summaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/3rdparty/3rdparty.pro10
-rw-r--r--src/libs/3rdparty/7zip/7zip.pri (renamed from src/libs/7zip/7zip.pri)0
-rw-r--r--src/libs/3rdparty/7zip/7zip.pro (renamed from src/libs/7zip/7zip.pro)2
-rw-r--r--src/libs/3rdparty/7zip/COPYING15
-rw-r--r--src/libs/3rdparty/7zip/installer_framework_changes.txt (renamed from src/libs/7zip/installer_framework_changes.txt)0
-rw-r--r--src/libs/3rdparty/7zip/qt_attribution.json13
-rw-r--r--src/libs/3rdparty/7zip/unix/C/7zCrc.c (renamed from src/libs/7zip/unix/C/7zCrc.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/7zCrc.h (renamed from src/libs/7zip/unix/C/7zCrc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/7zCrcOpt.c (renamed from src/libs/7zip/unix/C/7zCrcOpt.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/7zStream.c (renamed from src/libs/7zip/unix/C/7zStream.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/7zTypes.h (renamed from src/libs/7zip/unix/C/7zTypes.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/7zVersion.h (renamed from src/libs/7zip/unix/C/7zVersion.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Alloc.c (renamed from src/libs/7zip/unix/C/Alloc.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Alloc.h (renamed from src/libs/7zip/unix/C/Alloc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Bra.c (renamed from src/libs/7zip/unix/C/Bra.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Bra.h (renamed from src/libs/7zip/unix/C/Bra.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Bra86.c (renamed from src/libs/7zip/unix/C/Bra86.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/BraIA64.c (renamed from src/libs/7zip/unix/C/BraIA64.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/C.pri (renamed from src/libs/7zip/unix/C/C.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Compiler.h (renamed from src/libs/7zip/unix/C/Compiler.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/CpuArch.h (renamed from src/libs/7zip/unix/C/CpuArch.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Delta.c (renamed from src/libs/7zip/unix/C/Delta.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Delta.h (renamed from src/libs/7zip/unix/C/Delta.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzFind.c (renamed from src/libs/7zip/unix/C/LzFind.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzFind.h (renamed from src/libs/7zip/unix/C/LzFind.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzFindMt.c (renamed from src/libs/7zip/unix/C/LzFindMt.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzFindMt.h (renamed from src/libs/7zip/unix/C/LzFindMt.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzHash.h (renamed from src/libs/7zip/unix/C/LzHash.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Lzma2Dec.c (renamed from src/libs/7zip/unix/C/Lzma2Dec.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Lzma2Dec.h (renamed from src/libs/7zip/unix/C/Lzma2Dec.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Lzma2Enc.c (renamed from src/libs/7zip/unix/C/Lzma2Enc.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Lzma2Enc.h (renamed from src/libs/7zip/unix/C/Lzma2Enc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzmaDec.c (renamed from src/libs/7zip/unix/C/LzmaDec.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzmaDec.h (renamed from src/libs/7zip/unix/C/LzmaDec.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzmaEnc.c (renamed from src/libs/7zip/unix/C/LzmaEnc.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/LzmaEnc.h (renamed from src/libs/7zip/unix/C/LzmaEnc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/MtCoder.c (renamed from src/libs/7zip/unix/C/MtCoder.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/MtCoder.h (renamed from src/libs/7zip/unix/C/MtCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Precomp.h (renamed from src/libs/7zip/unix/C/Precomp.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/RotateDefs.h (renamed from src/libs/7zip/unix/C/RotateDefs.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Sha256.c (renamed from src/libs/7zip/unix/C/Sha256.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Sha256.h (renamed from src/libs/7zip/unix/C/Sha256.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Threads.c (renamed from src/libs/7zip/unix/C/Threads.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Threads.h (renamed from src/libs/7zip/unix/C/Threads.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Xz.c (renamed from src/libs/7zip/unix/C/Xz.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/Xz.h (renamed from src/libs/7zip/unix/C/Xz.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzCrc64.c (renamed from src/libs/7zip/unix/C/XzCrc64.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzCrc64.h (renamed from src/libs/7zip/unix/C/XzCrc64.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzCrc64Opt.c (renamed from src/libs/7zip/unix/C/XzCrc64Opt.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzDec.c (renamed from src/libs/7zip/unix/C/XzDec.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzEnc.c (renamed from src/libs/7zip/unix/C/XzEnc.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzEnc.h (renamed from src/libs/7zip/unix/C/XzEnc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/C/XzIn.c (renamed from src/libs/7zip/unix/C/XzIn.c)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/7zip.pri (renamed from src/libs/7zip/unix/CPP/7zip/7zip.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7z.pri (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7z.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zIn.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zItem.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zOut.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Archive.pri (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Archive.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/Common.pri (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/IArchive.h (renamed from src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/XzHandler.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CWrappers.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/CWrappers.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CWrappers.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/CWrappers.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/Common.pri (renamed from src/libs/7zip/unix/CPP/7zip/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CreateCoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CreateCoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FileStreams.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/FileStreams.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FileStreams.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/FileStreams.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilterCoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilterCoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InBuffer.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/InBuffer.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InBuffer.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/InBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LimitedStreams.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LockedStream.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/LockedStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LockedStream.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/LockedStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodId.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/MethodId.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodProps.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/MethodProps.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodProps.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/MethodProps.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/OutBuffer.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/OutBuffer.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/ProgressUtils.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/PropId.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/PropId.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/RegisterArc.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/RegisterCodec.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamBinder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamBinder.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamObjects.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamObjects.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamUtils.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamUtils.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/UniqBlocks.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/VirtThread.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Common/VirtThread.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Common/VirtThread.h (renamed from src/libs/7zip/unix/CPP/7zip/Common/VirtThread.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjCoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchCoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchMisc.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Compress.pri (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Compress.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyCoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp (renamed from src/libs/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/RangeCoder.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/RangeCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h (renamed from src/libs/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/Guid.txt (renamed from src/libs/7zip/unix/CPP/7zip/Guid.txt)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/ICoder.h (renamed from src/libs/7zip/unix/CPP/7zip/ICoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/IDecl.h (renamed from src/libs/7zip/unix/CPP/7zip/IDecl.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/IPassword.h (renamed from src/libs/7zip/unix/CPP/7zip/IPassword.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/IProgress.h (renamed from src/libs/7zip/unix/CPP/7zip/IProgress.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/IStream.h (renamed from src/libs/7zip/unix/CPP/7zip/IStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/PropID.h (renamed from src/libs/7zip/unix/CPP/7zip/PropID.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Common.pri (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DefaultName.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DirItem.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/DirItem.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Extract.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Extract.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/HashCalc.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Property.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/Property.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SetProperties.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SortUtils.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/TempFiles.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Update.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/Update.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Update.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/Update.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/Console.pri (renamed from src/libs/7zip/unix/CPP/7zip/UI/Console/Console.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp (renamed from src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h (renamed from src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/ComTry.h (renamed from src/libs/7zip/unix/CPP/Common/ComTry.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/CommandLineParser.cpp (renamed from src/libs/7zip/unix/CPP/Common/CommandLineParser.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/CommandLineParser.h (renamed from src/libs/7zip/unix/CPP/Common/CommandLineParser.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/Common.h (renamed from src/libs/7zip/unix/CPP/Common/Common.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/Common.pri (renamed from src/libs/7zip/unix/CPP/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/Defs.h (renamed from src/libs/7zip/unix/CPP/Common/Defs.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/IntToString.cpp (renamed from src/libs/7zip/unix/CPP/Common/IntToString.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/IntToString.h (renamed from src/libs/7zip/unix/CPP/Common/IntToString.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/ListFileUtils.cpp (renamed from src/libs/7zip/unix/CPP/Common/ListFileUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/ListFileUtils.h (renamed from src/libs/7zip/unix/CPP/Common/ListFileUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyBuffer.h (renamed from src/libs/7zip/unix/CPP/Common/MyBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyCom.h (renamed from src/libs/7zip/unix/CPP/Common/MyCom.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyException.h (renamed from src/libs/7zip/unix/CPP/Common/MyException.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyGuidDef.h (renamed from src/libs/7zip/unix/CPP/Common/MyGuidDef.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyInitGuid.h (renamed from src/libs/7zip/unix/CPP/Common/MyInitGuid.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyString.cpp (renamed from src/libs/7zip/unix/CPP/Common/MyString.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyString.h (renamed from src/libs/7zip/unix/CPP/Common/MyString.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyTypes.h (renamed from src/libs/7zip/unix/CPP/Common/MyTypes.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyUnknown.h (renamed from src/libs/7zip/unix/CPP/Common/MyUnknown.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyVector.h (renamed from src/libs/7zip/unix/CPP/Common/MyVector.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyWindows.cpp (renamed from src/libs/7zip/unix/CPP/Common/MyWindows.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/MyWindows.h (renamed from src/libs/7zip/unix/CPP/Common/MyWindows.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/NewHandler.cpp (renamed from src/libs/7zip/unix/CPP/Common/NewHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/NewHandler.h (renamed from src/libs/7zip/unix/CPP/Common/NewHandler.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/StdOutStream.cpp (renamed from src/libs/7zip/unix/CPP/Common/StdOutStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/StdOutStream.h (renamed from src/libs/7zip/unix/CPP/Common/StdOutStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/StringConvert.cpp (renamed from src/libs/7zip/unix/CPP/Common/StringConvert.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/StringConvert.h (renamed from src/libs/7zip/unix/CPP/Common/StringConvert.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/StringToInt.cpp (renamed from src/libs/7zip/unix/CPP/Common/StringToInt.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/StringToInt.h (renamed from src/libs/7zip/unix/CPP/Common/StringToInt.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/UTFConvert.cpp (renamed from src/libs/7zip/unix/CPP/Common/UTFConvert.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/UTFConvert.h (renamed from src/libs/7zip/unix/CPP/Common/UTFConvert.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/Wildcard.cpp (renamed from src/libs/7zip/unix/CPP/Common/Wildcard.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Common/Wildcard.h (renamed from src/libs/7zip/unix/CPP/Common/Wildcard.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/DLL.cpp (renamed from src/libs/7zip/unix/CPP/Windows/DLL.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/DLL.h (renamed from src/libs/7zip/unix/CPP/Windows/DLL.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/Defs.h (renamed from src/libs/7zip/unix/CPP/Windows/Defs.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileDir.cpp (renamed from src/libs/7zip/unix/CPP/Windows/FileDir.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileDir.h (renamed from src/libs/7zip/unix/CPP/Windows/FileDir.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileFind.cpp (renamed from src/libs/7zip/unix/CPP/Windows/FileFind.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileFind.h (renamed from src/libs/7zip/unix/CPP/Windows/FileFind.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileIO.cpp (renamed from src/libs/7zip/unix/CPP/Windows/FileIO.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileIO.h (renamed from src/libs/7zip/unix/CPP/Windows/FileIO.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileName.cpp (renamed from src/libs/7zip/unix/CPP/Windows/FileName.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/FileName.h (renamed from src/libs/7zip/unix/CPP/Windows/FileName.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariant.cpp (renamed from src/libs/7zip/unix/CPP/Windows/PropVariant.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariant.h (renamed from src/libs/7zip/unix/CPP/Windows/PropVariant.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariantConv.cpp (renamed from src/libs/7zip/unix/CPP/Windows/PropVariantConv.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariantConv.h (renamed from src/libs/7zip/unix/CPP/Windows/PropVariantConv.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization.cpp (renamed from src/libs/7zip/unix/CPP/Windows/Synchronization.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization.h (renamed from src/libs/7zip/unix/CPP/Windows/Synchronization.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization2.h (renamed from src/libs/7zip/unix/CPP/Windows/Synchronization2.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/System.cpp (renamed from src/libs/7zip/unix/CPP/Windows/System.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/System.h (renamed from src/libs/7zip/unix/CPP/Windows/System.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/Thread.h (renamed from src/libs/7zip/unix/CPP/Windows/Thread.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/TimeUtils.cpp (renamed from src/libs/7zip/unix/CPP/Windows/TimeUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/TimeUtils.h (renamed from src/libs/7zip/unix/CPP/Windows/TimeUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/Windows/Windows.pri (renamed from src/libs/7zip/unix/CPP/Windows/Windows.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/include_windows/basetyps.h (renamed from src/libs/7zip/unix/CPP/include_windows/basetyps.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/include_windows/include_windows.pri (renamed from src/libs/7zip/unix/CPP/include_windows/include_windows.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/include_windows/tchar.h (renamed from src/libs/7zip/unix/CPP/include_windows/tchar.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/include_windows/windows.h (renamed from src/libs/7zip/unix/CPP/include_windows/windows.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/StdAfx.h (renamed from src/libs/7zip/unix/CPP/myWindows/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/config.h (renamed from src/libs/7zip/unix/CPP/myWindows/config.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/initguid.h (renamed from src/libs/7zip/unix/CPP/myWindows/initguid.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp (renamed from src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp)32
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/myDateAndTime.cpp (renamed from src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/myPrivate.h (renamed from src/libs/7zip/unix/CPP/myWindows/myPrivate.h)0
-rw-r--r--src/libs/3rdparty/7zip/unix/CPP/myWindows/myWindows.pri (renamed from src/libs/7zip/unix/CPP/myWindows/myWindows.pri)0
-rw-r--r--src/libs/3rdparty/7zip/unix/unix.pri (renamed from src/libs/7zip/unix/unix.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/7zCrc.c (renamed from src/libs/7zip/win/C/7zCrc.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/7zCrc.h (renamed from src/libs/7zip/win/C/7zCrc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/7zCrcOpt.c (renamed from src/libs/7zip/win/C/7zCrcOpt.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/7zStream.c (renamed from src/libs/7zip/win/C/7zStream.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/7zTypes.h (renamed from src/libs/7zip/win/C/7zTypes.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/7zVersion.h (renamed from src/libs/7zip/win/C/7zVersion.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Alloc.c (renamed from src/libs/7zip/win/C/Alloc.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Alloc.h (renamed from src/libs/7zip/win/C/Alloc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Bra.c (renamed from src/libs/7zip/win/C/Bra.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Bra.h (renamed from src/libs/7zip/win/C/Bra.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Bra86.c (renamed from src/libs/7zip/win/C/Bra86.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/BraIA64.c (renamed from src/libs/7zip/win/C/BraIA64.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/C.pri (renamed from src/libs/7zip/win/C/C.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Compiler.h (renamed from src/libs/7zip/win/C/Compiler.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/CpuArch.c (renamed from src/libs/7zip/win/C/CpuArch.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/CpuArch.h (renamed from src/libs/7zip/win/C/CpuArch.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Delta.c (renamed from src/libs/7zip/win/C/Delta.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Delta.h (renamed from src/libs/7zip/win/C/Delta.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzFind.c (renamed from src/libs/7zip/win/C/LzFind.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzFind.h (renamed from src/libs/7zip/win/C/LzFind.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzFindMt.c (renamed from src/libs/7zip/win/C/LzFindMt.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzFindMt.h (renamed from src/libs/7zip/win/C/LzFindMt.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzHash.h (renamed from src/libs/7zip/win/C/LzHash.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Lzma2Dec.c (renamed from src/libs/7zip/win/C/Lzma2Dec.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Lzma2Dec.h (renamed from src/libs/7zip/win/C/Lzma2Dec.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Lzma2Enc.c (renamed from src/libs/7zip/win/C/Lzma2Enc.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Lzma2Enc.h (renamed from src/libs/7zip/win/C/Lzma2Enc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzmaDec.c (renamed from src/libs/7zip/win/C/LzmaDec.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzmaDec.h (renamed from src/libs/7zip/win/C/LzmaDec.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzmaEnc.c (renamed from src/libs/7zip/win/C/LzmaEnc.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/LzmaEnc.h (renamed from src/libs/7zip/win/C/LzmaEnc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/MtCoder.c (renamed from src/libs/7zip/win/C/MtCoder.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/MtCoder.h (renamed from src/libs/7zip/win/C/MtCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Precomp.h (renamed from src/libs/7zip/win/C/Precomp.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/RotateDefs.h (renamed from src/libs/7zip/win/C/RotateDefs.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Sha256.c (renamed from src/libs/7zip/win/C/Sha256.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Sha256.h (renamed from src/libs/7zip/win/C/Sha256.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Threads.c (renamed from src/libs/7zip/win/C/Threads.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Threads.h (renamed from src/libs/7zip/win/C/Threads.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Xz.c (renamed from src/libs/7zip/win/C/Xz.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/Xz.h (renamed from src/libs/7zip/win/C/Xz.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzCrc64.c (renamed from src/libs/7zip/win/C/XzCrc64.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzCrc64.h (renamed from src/libs/7zip/win/C/XzCrc64.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzCrc64Opt.c (renamed from src/libs/7zip/win/C/XzCrc64Opt.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzDec.c (renamed from src/libs/7zip/win/C/XzDec.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzEnc.c (renamed from src/libs/7zip/win/C/XzEnc.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzEnc.h (renamed from src/libs/7zip/win/C/XzEnc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/C/XzIn.c (renamed from src/libs/7zip/win/C/XzIn.c)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/7zip.pri (renamed from src/libs/7zip/win/CPP/7zip/7zip.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7z.pri (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7z.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zDecode.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zEncode.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandler.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHeader.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zIn.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zItem.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zItem.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zOut.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zProperties.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/7z/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Archive.pri (renamed from src/libs/7zip/win/CPP/7zip/Archive/Archive.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/Common.pri (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/MultiStream.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/Common/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/IArchive.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/IArchive.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/SplitHandler.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/SplitHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/Archive/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Archive/XzHandler.cpp (renamed from src/libs/7zip/win/CPP/7zip/Archive/XzHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/CWrappers.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/CWrappers.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/CWrappers.h (renamed from src/libs/7zip/win/CPP/7zip/Common/CWrappers.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/Common.pri (renamed from src/libs/7zip/win/CPP/7zip/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/CreateCoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/CreateCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/CreateCoder.h (renamed from src/libs/7zip/win/CPP/7zip/Common/CreateCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilePathAutoRename.h (renamed from src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/FileStreams.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/FileStreams.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/FileStreams.h (renamed from src/libs/7zip/win/CPP/7zip/Common/FileStreams.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilterCoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/FilterCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilterCoder.h (renamed from src/libs/7zip/win/CPP/7zip/Common/FilterCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/InBuffer.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/InBuffer.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/InBuffer.h (renamed from src/libs/7zip/win/CPP/7zip/Common/InBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/InOutTempBuffer.h (renamed from src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/LimitedStreams.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/LimitedStreams.h (renamed from src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/LockedStream.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/LockedStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/LockedStream.h (renamed from src/libs/7zip/win/CPP/7zip/Common/LockedStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodId.h (renamed from src/libs/7zip/win/CPP/7zip/Common/MethodId.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodProps.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/MethodProps.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodProps.h (renamed from src/libs/7zip/win/CPP/7zip/Common/MethodProps.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/OutBuffer.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/OutBuffer.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/OutBuffer.h (renamed from src/libs/7zip/win/CPP/7zip/Common/OutBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/ProgressUtils.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/ProgressUtils.h (renamed from src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/PropId.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/PropId.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/RegisterArc.h (renamed from src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/RegisterCodec.h (renamed from src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/Common/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamBinder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/StreamBinder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamBinder.h (renamed from src/libs/7zip/win/CPP/7zip/Common/StreamBinder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamObjects.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/StreamObjects.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamObjects.h (renamed from src/libs/7zip/win/CPP/7zip/Common/StreamObjects.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamUtils.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/StreamUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamUtils.h (renamed from src/libs/7zip/win/CPP/7zip/Common/StreamUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/UniqBlocks.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/UniqBlocks.h (renamed from src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/VirtThread.cpp (renamed from src/libs/7zip/win/CPP/7zip/Common/VirtThread.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Common/VirtThread.h (renamed from src/libs/7zip/win/CPP/7zip/Common/VirtThread.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Coder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjCoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjCoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjRegister.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/BcjRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchCoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchCoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchMisc.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchMisc.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchRegister.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/BranchRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/ByteSwap.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/ByteSwap.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Compress.pri (renamed from src/libs/7zip/win/CPP/7zip/Compress/Compress.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyCoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyCoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyRegister.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/CopyRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaDecoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaEncoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp (renamed from src/libs/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/RangeCoder.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/RangeCoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/RangeCoderBit.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/RangeCoderBit.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Compress/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/Compress/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/Guid.txt (renamed from src/libs/7zip/win/CPP/7zip/Guid.txt)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/ICoder.h (renamed from src/libs/7zip/win/CPP/7zip/ICoder.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/IDecl.h (renamed from src/libs/7zip/win/CPP/7zip/IDecl.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/IPassword.h (renamed from src/libs/7zip/win/CPP/7zip/IPassword.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/IProgress.h (renamed from src/libs/7zip/win/CPP/7zip/IProgress.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/IStream.h (renamed from src/libs/7zip/win/CPP/7zip/IStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/PropID.h (renamed from src/libs/7zip/win/CPP/7zip/PropID.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Common.pri (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DefaultName.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DirItem.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/DirItem.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Extract.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Extract.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/Extract.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractMode.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ExtractMode.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/HashCalc.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/OpenArchive.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Property.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/Property.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SetProperties.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SortUtils.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/TempFiles.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Update.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/Update.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Update.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/Update.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateAction.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdatePair.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/Console.pri (renamed from src/libs/7zip/win/CPP/7zip/UI/Console/Console.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp (renamed from src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/StdAfx.h (renamed from src/libs/7zip/win/CPP/7zip/UI/Console/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/ComTry.h (renamed from src/libs/7zip/win/CPP/Common/ComTry.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/CommandLineParser.cpp (renamed from src/libs/7zip/win/CPP/Common/CommandLineParser.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/CommandLineParser.h (renamed from src/libs/7zip/win/CPP/Common/CommandLineParser.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/Common.h (renamed from src/libs/7zip/win/CPP/Common/Common.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/Common.pri (renamed from src/libs/7zip/win/CPP/Common/Common.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/Defs.h (renamed from src/libs/7zip/win/CPP/Common/Defs.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/IntToString.cpp (renamed from src/libs/7zip/win/CPP/Common/IntToString.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/IntToString.h (renamed from src/libs/7zip/win/CPP/Common/IntToString.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/ListFileUtils.cpp (renamed from src/libs/7zip/win/CPP/Common/ListFileUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/ListFileUtils.h (renamed from src/libs/7zip/win/CPP/Common/ListFileUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyBuffer.h (renamed from src/libs/7zip/win/CPP/Common/MyBuffer.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyCom.h (renamed from src/libs/7zip/win/CPP/Common/MyCom.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyException.h (renamed from src/libs/7zip/win/CPP/Common/MyException.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyGuidDef.h (renamed from src/libs/7zip/win/CPP/Common/MyGuidDef.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyInitGuid.h (renamed from src/libs/7zip/win/CPP/Common/MyInitGuid.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyString.cpp (renamed from src/libs/7zip/win/CPP/Common/MyString.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyString.h (renamed from src/libs/7zip/win/CPP/Common/MyString.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyTypes.h (renamed from src/libs/7zip/win/CPP/Common/MyTypes.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyUnknown.h (renamed from src/libs/7zip/win/CPP/Common/MyUnknown.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyVector.h (renamed from src/libs/7zip/win/CPP/Common/MyVector.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyWindows.cpp (renamed from src/libs/7zip/win/CPP/Common/MyWindows.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/MyWindows.h (renamed from src/libs/7zip/win/CPP/Common/MyWindows.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/NewHandler.cpp (renamed from src/libs/7zip/win/CPP/Common/NewHandler.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/NewHandler.h (renamed from src/libs/7zip/win/CPP/Common/NewHandler.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StdAfx.h (renamed from src/libs/7zip/win/CPP/Common/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StdOutStream.cpp (renamed from src/libs/7zip/win/CPP/Common/StdOutStream.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StdOutStream.h (renamed from src/libs/7zip/win/CPP/Common/StdOutStream.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StringConvert.cpp (renamed from src/libs/7zip/win/CPP/Common/StringConvert.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StringConvert.h (renamed from src/libs/7zip/win/CPP/Common/StringConvert.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StringToInt.cpp (renamed from src/libs/7zip/win/CPP/Common/StringToInt.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/StringToInt.h (renamed from src/libs/7zip/win/CPP/Common/StringToInt.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/UTFConvert.cpp (renamed from src/libs/7zip/win/CPP/Common/UTFConvert.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/UTFConvert.h (renamed from src/libs/7zip/win/CPP/Common/UTFConvert.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/Wildcard.cpp (renamed from src/libs/7zip/win/CPP/Common/Wildcard.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Common/Wildcard.h (renamed from src/libs/7zip/win/CPP/Common/Wildcard.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/DLL.cpp (renamed from src/libs/7zip/win/CPP/Windows/DLL.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/DLL.h (renamed from src/libs/7zip/win/CPP/Windows/DLL.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/Defs.h (renamed from src/libs/7zip/win/CPP/Windows/Defs.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileDir.cpp (renamed from src/libs/7zip/win/CPP/Windows/FileDir.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileDir.h (renamed from src/libs/7zip/win/CPP/Windows/FileDir.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileFind.cpp (renamed from src/libs/7zip/win/CPP/Windows/FileFind.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileFind.h (renamed from src/libs/7zip/win/CPP/Windows/FileFind.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileIO.cpp (renamed from src/libs/7zip/win/CPP/Windows/FileIO.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileIO.h (renamed from src/libs/7zip/win/CPP/Windows/FileIO.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileLink.cpp (renamed from src/libs/7zip/win/CPP/Windows/FileLink.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileMapping.h (renamed from src/libs/7zip/win/CPP/Windows/FileMapping.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileName.cpp (renamed from src/libs/7zip/win/CPP/Windows/FileName.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/FileName.h (renamed from src/libs/7zip/win/CPP/Windows/FileName.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/Handle.h (renamed from src/libs/7zip/win/CPP/Windows/Handle.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/PropVariant.cpp (renamed from src/libs/7zip/win/CPP/Windows/PropVariant.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/PropVariant.h (renamed from src/libs/7zip/win/CPP/Windows/PropVariant.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/PropVariantConv.cpp (renamed from src/libs/7zip/win/CPP/Windows/PropVariantConv.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/PropVariantConv.h (renamed from src/libs/7zip/win/CPP/Windows/PropVariantConv.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/SecurityUtils.cpp (renamed from src/libs/7zip/win/CPP/Windows/SecurityUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/SecurityUtils.h (renamed from src/libs/7zip/win/CPP/Windows/SecurityUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/StdAfx.h (renamed from src/libs/7zip/win/CPP/Windows/StdAfx.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/Synchronization.cpp (renamed from src/libs/7zip/win/CPP/Windows/Synchronization.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/Synchronization.h (renamed from src/libs/7zip/win/CPP/Windows/Synchronization.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/System.cpp (renamed from src/libs/7zip/win/CPP/Windows/System.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/System.h (renamed from src/libs/7zip/win/CPP/Windows/System.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/Thread.h (renamed from src/libs/7zip/win/CPP/Windows/Thread.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/TimeUtils.cpp (renamed from src/libs/7zip/win/CPP/Windows/TimeUtils.cpp)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/TimeUtils.h (renamed from src/libs/7zip/win/CPP/Windows/TimeUtils.h)0
-rw-r--r--src/libs/3rdparty/7zip/win/CPP/Windows/Windows.pri (renamed from src/libs/7zip/win/CPP/Windows/Windows.pri)0
-rw-r--r--src/libs/3rdparty/7zip/win/win.pri (renamed from src/libs/7zip/win/win.pri)0
-rw-r--r--src/libs/3rdparty/libarchive/COPYING65
-rw-r--r--src/libs/3rdparty/libarchive/archive.h1212
-rw-r--r--src/libs/3rdparty/libarchive/archive_acl.c2097
-rw-r--r--src/libs/3rdparty/libarchive/archive_acl_private.h83
-rw-r--r--src/libs/3rdparty/libarchive/archive_blake2.h197
-rw-r--r--src/libs/3rdparty/libarchive/archive_blake2_impl.h161
-rw-r--r--src/libs/3rdparty/libarchive/archive_blake2s_ref.c369
-rw-r--r--src/libs/3rdparty/libarchive/archive_blake2sp_ref.c361
-rw-r--r--src/libs/3rdparty/libarchive/archive_check_magic.c175
-rw-r--r--src/libs/3rdparty/libarchive/archive_cmdline.c227
-rw-r--r--src/libs/3rdparty/libarchive/archive_cmdline_private.h47
-rw-r--r--src/libs/3rdparty/libarchive/archive_crc32.h83
-rw-r--r--src/libs/3rdparty/libarchive/archive_cryptor.c534
-rw-r--r--src/libs/3rdparty/libarchive/archive_cryptor_private.h188
-rw-r--r--src/libs/3rdparty/libarchive/archive_digest.c1565
-rw-r--r--src/libs/3rdparty/libarchive/archive_digest_private.h426
-rw-r--r--src/libs/3rdparty/libarchive/archive_disk_acl_darwin.c559
-rw-r--r--src/libs/3rdparty/libarchive/archive_disk_acl_freebsd.c712
-rw-r--r--src/libs/3rdparty/libarchive/archive_disk_acl_linux.c760
-rw-r--r--src/libs/3rdparty/libarchive/archive_disk_acl_sunos.c824
-rw-r--r--src/libs/3rdparty/libarchive/archive_endian.h195
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry.c2149
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry.h723
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_copy_bhfi.c75
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_copy_stat.c83
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_link_resolver.c447
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_locale.h92
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_private.h200
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_sparse.c156
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_stat.c118
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_strmode.c87
-rw-r--r--src/libs/3rdparty/libarchive/archive_entry_xattr.c156
-rw-r--r--src/libs/3rdparty/libarchive/archive_getdate.c1104
-rw-r--r--src/libs/3rdparty/libarchive/archive_getdate.h39
-rw-r--r--src/libs/3rdparty/libarchive/archive_hmac.c339
-rw-r--r--src/libs/3rdparty/libarchive/archive_hmac_private.h119
-rw-r--r--src/libs/3rdparty/libarchive/archive_match.c1875
-rw-r--r--src/libs/3rdparty/libarchive/archive_openssl_evp_private.h54
-rw-r--r--src/libs/3rdparty/libarchive/archive_openssl_hmac_private.h54
-rw-r--r--src/libs/3rdparty/libarchive/archive_options.c218
-rw-r--r--src/libs/3rdparty/libarchive/archive_options_private.h51
-rw-r--r--src/libs/3rdparty/libarchive/archive_pack_dev.c337
-rw-r--r--src/libs/3rdparty/libarchive/archive_pack_dev.h49
-rw-r--r--src/libs/3rdparty/libarchive/archive_pathmatch.c463
-rw-r--r--src/libs/3rdparty/libarchive/archive_pathmatch.h52
-rw-r--r--src/libs/3rdparty/libarchive/archive_platform.h233
-rw-r--r--src/libs/3rdparty/libarchive/archive_platform_acl.h55
-rw-r--r--src/libs/3rdparty/libarchive/archive_platform_xattr.h47
-rw-r--r--src/libs/3rdparty/libarchive/archive_ppmd7.c1168
-rw-r--r--src/libs/3rdparty/libarchive/archive_ppmd7_private.h119
-rw-r--r--src/libs/3rdparty/libarchive/archive_ppmd8.c1287
-rw-r--r--src/libs/3rdparty/libarchive/archive_ppmd8_private.h148
-rw-r--r--src/libs/3rdparty/libarchive/archive_ppmd_private.h151
-rw-r--r--src/libs/3rdparty/libarchive/archive_private.h180
-rw-r--r--src/libs/3rdparty/libarchive/archive_random.c301
-rw-r--r--src/libs/3rdparty/libarchive/archive_random_private.h36
-rw-r--r--src/libs/3rdparty/libarchive/archive_rb.c709
-rw-r--r--src/libs/3rdparty/libarchive/archive_rb.h113
-rw-r--r--src/libs/3rdparty/libarchive/archive_read.c1756
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_add_passphrase.c190
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_append_filter.c204
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_data_into_fd.c144
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_entry_from_file.c1086
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_posix.c2760
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_private.h98
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_set_standard_lookup.c313
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_disk_windows.c2547
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_extract.c60
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_extract2.c155
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_open_fd.c211
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_open_file.c180
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_open_filename.c586
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_open_memory.c186
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_private.h267
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_set_format.c117
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_set_options.c133
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_all.c85
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_by_code.c83
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c365
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_compress.c452
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_grzip.c112
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_gzip.c536
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lrzip.c122
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c742
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c499
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_none.c52
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_program.c496
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_rpm.c287
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_uu.c683
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c793
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c297
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c4074
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_all.c89
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_ar.c638
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_by_code.c92
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_cab.c3228
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c1104
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_empty.c96
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c3279
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_lha.c2919
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c2156
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_rar.c3788
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c4251
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_raw.c192
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_tar.c2946
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_warc.c848
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_xar.c3332
-rw-r--r--src/libs/3rdparty/libarchive/archive_read_support_format_zip.c4270
-rw-r--r--src/libs/3rdparty/libarchive/archive_string.c4244
-rw-r--r--src/libs/3rdparty/libarchive/archive_string.h243
-rw-r--r--src/libs/3rdparty/libarchive/archive_string_composition.h2292
-rw-r--r--src/libs/3rdparty/libarchive/archive_string_sprintf.c192
-rw-r--r--src/libs/3rdparty/libarchive/archive_util.c704
-rw-r--r--src/libs/3rdparty/libarchive/archive_version_details.c151
-rw-r--r--src/libs/3rdparty/libarchive/archive_virtual.c163
-rw-r--r--src/libs/3rdparty/libarchive/archive_windows.c938
-rw-r--r--src/libs/3rdparty/libarchive/archive_windows.h315
-rw-r--r--src/libs/3rdparty/libarchive/archive_write.c859
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter.c72
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_b64encode.c304
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_by_name.c77
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c401
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c447
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_grzip.c135
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_gzip.c442
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_lrzip.c197
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c700
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_lzop.c478
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_none.c43
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_program.c391
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_uuencode.c295
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_xz.c545
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c467
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_posix.c4759
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_private.h45
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_set_standard_lookup.c263
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_disk_windows.c2911
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_open_fd.c146
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_open_file.c111
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_open_filename.c270
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_open_memory.c115
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_private.h166
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format.c125
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c2322
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_ar.c570
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_by_name.c94
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_cpio.c11
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_cpio_binary.c610
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_cpio_newc.c457
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_cpio_odc.c500
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_filter_by_ext.c142
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_gnutar.c755
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c8161
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_mtree.c2217
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_pax.c2063
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_private.h42
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_raw.c125
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_shar.c641
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_ustar.c758
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_v7tar.c638
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_warc.c444
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_xar.c3255
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_format_zip.c1693
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_options.c130
-rw-r--r--src/libs/3rdparty/libarchive/archive_write_set_passphrase.c95
-rw-r--r--src/libs/3rdparty/libarchive/archive_xxhash.h48
-rw-r--r--src/libs/3rdparty/libarchive/config/linux/config.h1342
-rw-r--r--src/libs/3rdparty/libarchive/config/mac/config.h1342
-rw-r--r--src/libs/3rdparty/libarchive/config/win/config.h1342
-rw-r--r--src/libs/3rdparty/libarchive/config_freebsd.h276
-rw-r--r--src/libs/3rdparty/libarchive/filter_fork.h46
-rw-r--r--src/libs/3rdparty/libarchive/filter_fork_posix.c240
-rw-r--r--src/libs/3rdparty/libarchive/filter_fork_windows.c245
-rw-r--r--src/libs/3rdparty/libarchive/libarchive.pro197
-rw-r--r--src/libs/3rdparty/libarchive/qt_attribution.json13
-rw-r--r--src/libs/3rdparty/libarchive/xxhash.c529
-rw-r--r--src/libs/7zip/patches/0001-Adjust-7z-and-p7z.patch517
-rw-r--r--src/libs/ifwtools/binarycreator.cpp898
-rw-r--r--src/libs/ifwtools/binarycreator.h132
-rw-r--r--src/libs/ifwtools/ifwtools.pri19
-rw-r--r--src/libs/ifwtools/ifwtools_global.h44
-rw-r--r--src/libs/ifwtools/rcc/qcorecmdlineargs_p.h59
-rw-r--r--src/libs/ifwtools/rcc/rcc.cpp1038
-rw-r--r--src/libs/ifwtools/rcc/rcc.h140
-rw-r--r--src/libs/ifwtools/rcc/rccmain.cpp239
-rw-r--r--src/libs/ifwtools/repositorygen.cpp1211
-rw-r--r--src/libs/ifwtools/repositorygen.h108
-rw-r--r--src/libs/ifwtools/resources/copylibsintobundle.sh189
-rw-r--r--src/libs/ifwtools/resources/default_icon_mac.icnsbin0 -> 118992 bytes
-rw-r--r--src/libs/ifwtools/resources/ifwtools.qrc7
-rw-r--r--src/libs/installer/abstractarchive.cpp221
-rw-r--r--src/libs/installer/abstractarchive.h123
-rw-r--r--src/libs/installer/abstractfiletask.cpp34
-rw-r--r--src/libs/installer/abstracttask.cpp47
-rw-r--r--src/libs/installer/abstracttask.h8
-rw-r--r--src/libs/installer/adminauthorization_win.cpp11
-rw-r--r--src/libs/installer/adminauthorization_x11.cpp238
-rw-r--r--src/libs/installer/archivefactory.cpp151
-rw-r--r--src/libs/installer/archivefactory.h69
-rw-r--r--src/libs/installer/aspectratiolabel.cpp147
-rw-r--r--src/libs/installer/aspectratiolabel.h70
-rw-r--r--src/libs/installer/binarycontent.h9
-rw-r--r--src/libs/installer/binaryformat.cpp21
-rw-r--r--src/libs/installer/binaryformat.h18
-rw-r--r--src/libs/installer/binaryformatengine.cpp25
-rw-r--r--src/libs/installer/binaryformatengine.h31
-rw-r--r--src/libs/installer/calculatorbase.cpp76
-rw-r--r--src/libs/installer/calculatorbase.h85
-rw-r--r--src/libs/installer/commandlineparser.cpp343
-rw-r--r--src/libs/installer/commandlineparser.h71
-rw-r--r--src/libs/installer/commandlineparser_p.cpp33
-rw-r--r--src/libs/installer/commandlineparser_p.h51
-rw-r--r--src/libs/installer/component.cpp677
-rw-r--r--src/libs/installer/component.h51
-rw-r--r--src/libs/installer/component_p.cpp52
-rw-r--r--src/libs/installer/component_p.h11
-rw-r--r--src/libs/installer/componentalias.cpp672
-rw-r--r--src/libs/installer/componentalias.h164
-rw-r--r--src/libs/installer/componentchecker.cpp6
-rw-r--r--src/libs/installer/componentmodel.cpp136
-rw-r--r--src/libs/installer/componentmodel.h32
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp592
-rw-r--r--src/libs/installer/componentselectionpage_p.h49
-rw-r--r--src/libs/installer/componentsortfilterproxymodel.cpp152
-rw-r--r--src/libs/installer/componentsortfilterproxymodel.h63
-rw-r--r--src/libs/installer/concurrentoperationrunner.cpp279
-rw-r--r--src/libs/installer/concurrentoperationrunner.h88
-rw-r--r--src/libs/installer/constants.h235
-rw-r--r--src/libs/installer/consumeoutputoperation.cpp46
-rw-r--r--src/libs/installer/consumeoutputoperation.h10
-rw-r--r--src/libs/installer/copydirectoryoperation.cpp14
-rw-r--r--src/libs/installer/copydirectoryoperation.h10
-rw-r--r--src/libs/installer/copyfiletask.cpp10
-rw-r--r--src/libs/installer/copyfiletask.h4
-rw-r--r--src/libs/installer/createdesktopentryoperation.cpp44
-rw-r--r--src/libs/installer/createdesktopentryoperation.h10
-rw-r--r--src/libs/installer/createlinkoperation.cpp8
-rw-r--r--src/libs/installer/createlinkoperation.h10
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.cpp82
-rw-r--r--src/libs/installer/createlocalrepositoryoperation.h10
-rw-r--r--src/libs/installer/createshortcutoperation.cpp17
-rw-r--r--src/libs/installer/createshortcutoperation.h12
-rw-r--r--src/libs/installer/customcombobox.cpp54
-rw-r--r--src/libs/installer/customcombobox.h (renamed from src/libs/installer/lazyplaintextedit.h)32
-rw-r--r--src/libs/installer/directoryguard.cpp112
-rw-r--r--src/libs/installer/directoryguard.h55
-rw-r--r--src/libs/installer/downloadarchivesjob.cpp155
-rw-r--r--src/libs/installer/downloadarchivesjob.h31
-rw-r--r--src/libs/installer/downloadfiletask.cpp55
-rw-r--r--src/libs/installer/downloadfiletask.h8
-rw-r--r--src/libs/installer/downloadfiletask_p.h2
-rw-r--r--src/libs/installer/elevatedexecuteoperation.cpp156
-rw-r--r--src/libs/installer/elevatedexecuteoperation.h16
-rw-r--r--src/libs/installer/environmentvariablesoperation.cpp38
-rw-r--r--src/libs/installer/environmentvariablesoperation.h10
-rw-r--r--src/libs/installer/errors.cpp35
-rw-r--r--src/libs/installer/errors.h14
-rw-r--r--src/libs/installer/extractarchiveoperation.cpp297
-rw-r--r--src/libs/installer/extractarchiveoperation.h33
-rw-r--r--src/libs/installer/extractarchiveoperation_p.h182
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.cpp14
-rw-r--r--src/libs/installer/fakestopprocessforupdateoperation.h10
-rw-r--r--src/libs/installer/fileguard.cpp120
-rw-r--r--src/libs/installer/fileguard.h66
-rw-r--r--src/libs/installer/fileio.cpp51
-rw-r--r--src/libs/installer/fileutils.cpp375
-rw-r--r--src/libs/installer/fileutils.h53
-rw-r--r--src/libs/installer/fileutils_mac.mm75
-rw-r--r--src/libs/installer/genericdatacache.cpp695
-rw-r--r--src/libs/installer/genericdatacache.h122
-rw-r--r--src/libs/installer/globals.cpp148
-rw-r--r--src/libs/installer/globals.h31
-rw-r--r--src/libs/installer/globalsettingsoperation.cpp19
-rw-r--r--src/libs/installer/globalsettingsoperation.h10
-rw-r--r--src/libs/installer/graph.cpp35
-rw-r--r--src/libs/installer/graph.h2
-rw-r--r--src/libs/installer/init.cpp88
-rw-r--r--src/libs/installer/installer.pro88
-rw-r--r--src/libs/installer/installer_global.h8
-rw-r--r--src/libs/installer/installercalculator.cpp289
-rw-r--r--src/libs/installer/installercalculator.h59
-rw-r--r--src/libs/installer/installiconsoperation.cpp59
-rw-r--r--src/libs/installer/installiconsoperation.h11
-rw-r--r--src/libs/installer/keepaliveobject.cpp13
-rw-r--r--src/libs/installer/lazyplaintextedit.cpp101
-rw-r--r--src/libs/installer/lib7z_create.h21
-rw-r--r--src/libs/installer/lib7z_extract.h30
-rw-r--r--src/libs/installer/lib7z_facade.cpp298
-rw-r--r--src/libs/installer/lib7z_guid.h30
-rw-r--r--src/libs/installer/lib7z_list.h44
-rw-r--r--src/libs/installer/lib7zarchive.cpp242
-rw-r--r--src/libs/installer/lib7zarchive.h95
-rw-r--r--src/libs/installer/libarchivearchive.cpp1243
-rw-r--r--src/libs/installer/libarchivearchive.h201
-rw-r--r--src/libs/installer/libarchivewrapper.cpp192
-rw-r--r--src/libs/installer/libarchivewrapper.h72
-rw-r--r--src/libs/installer/libarchivewrapper_p.cpp405
-rw-r--r--src/libs/installer/libarchivewrapper_p.h95
-rw-r--r--src/libs/installer/licenseoperation.cpp27
-rw-r--r--src/libs/installer/licenseoperation.h10
-rw-r--r--src/libs/installer/linereplaceoperation.cpp14
-rw-r--r--src/libs/installer/linereplaceoperation.h10
-rw-r--r--src/libs/installer/link.cpp36
-rw-r--r--src/libs/installer/loggingutils.cpp567
-rw-r--r--src/libs/installer/loggingutils.h140
-rw-r--r--src/libs/installer/messageboxhandler.cpp116
-rw-r--r--src/libs/installer/messageboxhandler.h19
-rw-r--r--src/libs/installer/metadata.cpp410
-rw-r--r--src/libs/installer/metadata.h81
-rw-r--r--src/libs/installer/metadatacache.cpp67
-rw-r--r--src/libs/installer/metadatacache.h46
-rw-r--r--src/libs/installer/metadatajob.cpp942
-rw-r--r--src/libs/installer/metadatajob.h69
-rw-r--r--src/libs/installer/metadatajob_p.h155
-rw-r--r--src/libs/installer/minimumprogressoperation.cpp6
-rw-r--r--src/libs/installer/minimumprogressoperation.h10
-rw-r--r--src/libs/installer/observer.cpp18
-rw-r--r--src/libs/installer/observer.h10
-rw-r--r--src/libs/installer/operationtracer.cpp151
-rw-r--r--src/libs/installer/operationtracer.h68
-rw-r--r--src/libs/installer/packagemanagercore.cpp2958
-rw-r--r--src/libs/installer/packagemanagercore.h218
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp1873
-rw-r--r--src/libs/installer/packagemanagercore_p.h135
-rw-r--r--src/libs/installer/packagemanagercoredata.cpp147
-rw-r--r--src/libs/installer/packagemanagercoredata.h17
-rw-r--r--src/libs/installer/packagemanagergui.cpp1211
-rw-r--r--src/libs/installer/packagemanagergui.h118
-rw-r--r--src/libs/installer/packagemanagerpagefactory.cpp6
-rw-r--r--src/libs/installer/packagemanagerproxyfactory.cpp12
-rw-r--r--src/libs/installer/packagemanagerproxyfactory.h6
-rw-r--r--src/libs/installer/packagesource.cpp38
-rw-r--r--src/libs/installer/packagesource.h36
-rw-r--r--src/libs/installer/performinstallationform.cpp99
-rw-r--r--src/libs/installer/performinstallationform.h21
-rw-r--r--src/libs/installer/permissionsettings.cpp8
-rw-r--r--src/libs/installer/permissionsettings.h11
-rw-r--r--src/libs/installer/productkeycheck.cpp29
-rw-r--r--src/libs/installer/productkeycheck.h9
-rw-r--r--src/libs/installer/progresscoordinator.cpp123
-rw-r--r--src/libs/installer/progresscoordinator.h24
-rw-r--r--src/libs/installer/protocol.cpp22
-rw-r--r--src/libs/installer/protocol.h32
-rw-r--r--src/libs/installer/proxycredentialsdialog.cpp14
-rw-r--r--src/libs/installer/qinstallerglobal.cpp99
-rw-r--r--src/libs/installer/qinstallerglobal.h10
-rw-r--r--src/libs/installer/qprocesswrapper.cpp92
-rw-r--r--src/libs/installer/qprocesswrapper.h9
-rw-r--r--src/libs/installer/qsettingswrapper.cpp117
-rw-r--r--src/libs/installer/qsettingswrapper.h54
-rw-r--r--src/libs/installer/qtpatch.cpp41
-rw-r--r--src/libs/installer/registerfiletypeoperation.cpp18
-rw-r--r--src/libs/installer/registerfiletypeoperation.h10
-rw-r--r--src/libs/installer/remoteclient.cpp12
-rw-r--r--src/libs/installer/remoteclient_p.h35
-rw-r--r--src/libs/installer/remotefileengine.cpp61
-rw-r--r--src/libs/installer/remotefileengine.h86
-rw-r--r--src/libs/installer/remoteobject.cpp38
-rw-r--r--src/libs/installer/remoteobject.h110
-rw-r--r--src/libs/installer/remoteserver.cpp12
-rw-r--r--src/libs/installer/remoteserver_p.h4
-rw-r--r--src/libs/installer/remoteserverconnection.cpp380
-rw-r--r--src/libs/installer/remoteserverconnection.h45
-rw-r--r--src/libs/installer/remoteserverconnection_p.h69
-rw-r--r--src/libs/installer/replaceoperation.cpp6
-rw-r--r--src/libs/installer/replaceoperation.h10
-rw-r--r--src/libs/installer/repository.cpp84
-rw-r--r--src/libs/installer/repository.h15
-rw-r--r--src/libs/installer/repositorycategory.cpp32
-rw-r--r--src/libs/installer/repositorycategory.h17
-rw-r--r--src/libs/installer/resources/installer.qrc1
-rw-r--r--src/libs/installer/resources/qt/etc/qt.conf0
-rw-r--r--src/libs/installer/runextensions.h378
-rw-r--r--src/libs/installer/scriptengine.cpp198
-rw-r--r--src/libs/installer/scriptengine.h8
-rw-r--r--src/libs/installer/scriptengine_p.h80
-rw-r--r--src/libs/installer/selfrestartoperation.cpp8
-rw-r--r--src/libs/installer/selfrestartoperation.h10
-rw-r--r--src/libs/installer/serverauthenticationdialog.cpp6
-rw-r--r--src/libs/installer/settings.cpp251
-rw-r--r--src/libs/installer/settings.h25
-rw-r--r--src/libs/installer/settingsoperation.cpp46
-rw-r--r--src/libs/installer/settingsoperation.h10
-rw-r--r--src/libs/installer/simplemovefileoperation.cpp8
-rw-r--r--src/libs/installer/simplemovefileoperation.h10
-rw-r--r--src/libs/installer/sysinfo_win.cpp10
-rw-r--r--src/libs/installer/systeminfo.cpp30
-rw-r--r--src/libs/installer/systeminfo.h5
-rw-r--r--src/libs/installer/testrepository.cpp9
-rw-r--r--src/libs/installer/testrepository.h6
-rw-r--r--src/libs/installer/uninstallercalculator.cpp192
-rw-r--r--src/libs/installer/uninstallercalculator.h31
-rw-r--r--src/libs/installer/unziptask.cpp6
-rw-r--r--src/libs/installer/unziptask.h4
-rw-r--r--src/libs/installer/utils.cpp251
-rw-r--r--src/libs/installer/utils.h48
-rw-r--r--src/libs/kdtools/filedownloader.cpp175
-rw-r--r--src/libs/kdtools/filedownloader.h6
-rw-r--r--src/libs/kdtools/filedownloader_p.h69
-rw-r--r--src/libs/kdtools/filedownloaderfactory.cpp7
-rw-r--r--src/libs/kdtools/genericfactory.cpp45
-rw-r--r--src/libs/kdtools/genericfactory.h28
-rw-r--r--src/libs/kdtools/job.cpp4
-rw-r--r--src/libs/kdtools/kdsysinfo_win.cpp14
-rw-r--r--src/libs/kdtools/localpackagehub.cpp49
-rw-r--r--src/libs/kdtools/localpackagehub.h14
-rw-r--r--src/libs/kdtools/lockfile_win.cpp2
-rw-r--r--src/libs/kdtools/runoncechecker.cpp5
-rw-r--r--src/libs/kdtools/selfrestarter.cpp14
-rw-r--r--src/libs/kdtools/sysinfo.cpp4
-rw-r--r--src/libs/kdtools/sysinfo_x11.cpp27
-rw-r--r--src/libs/kdtools/task.cpp32
-rw-r--r--src/libs/kdtools/update.cpp7
-rw-r--r--src/libs/kdtools/updatefinder.cpp440
-rw-r--r--src/libs/kdtools/updatefinder.h94
-rw-r--r--src/libs/kdtools/updateoperation.cpp239
-rw-r--r--src/libs/kdtools/updateoperation.h28
-rw-r--r--src/libs/kdtools/updateoperations.cpp131
-rw-r--r--src/libs/kdtools/updateoperations.h63
-rw-r--r--src/libs/kdtools/updater.cpp40
-rw-r--r--src/libs/kdtools/updatesinfo.cpp233
-rw-r--r--src/libs/kdtools/updatesinfo_p.h6
-rw-r--r--src/libs/kdtools/updatesinfodata_p.h12
-rw-r--r--src/libs/libs.pro6
1013 files changed, 159347 insertions, 6254 deletions
diff --git a/src/libs/3rdparty/3rdparty.pro b/src/libs/3rdparty/3rdparty.pro
new file mode 100644
index 000000000..e69183197
--- /dev/null
+++ b/src/libs/3rdparty/3rdparty.pro
@@ -0,0 +1,10 @@
+TEMPLATE = subdirs
+
+include(../../../installerfw.pri)
+
+CONFIG(libarchive) {
+ SUBDIRS += libarchive
+}
+CONFIG(lzmasdk) {
+ SUBDIRS += 7zip
+}
diff --git a/src/libs/7zip/7zip.pri b/src/libs/3rdparty/7zip/7zip.pri
index 26c052fc7..26c052fc7 100644
--- a/src/libs/7zip/7zip.pri
+++ b/src/libs/3rdparty/7zip/7zip.pri
diff --git a/src/libs/7zip/7zip.pro b/src/libs/3rdparty/7zip/7zip.pro
index 17e64df8f..0c8ff3e1b 100644
--- a/src/libs/7zip/7zip.pro
+++ b/src/libs/3rdparty/7zip/7zip.pro
@@ -1,7 +1,7 @@
QT = core
TARGET = 7z
TEMPLATE = lib
-include(../../../installerfw.pri)
+include(../../../../installerfw.pri)
INCLUDEPATH += . ..
CONFIG += staticlib
DESTDIR = $$IFW_LIB_PATH
diff --git a/src/libs/3rdparty/7zip/COPYING b/src/libs/3rdparty/7zip/COPYING
new file mode 100644
index 000000000..2b197ecf1
--- /dev/null
+++ b/src/libs/3rdparty/7zip/COPYING
@@ -0,0 +1,15 @@
+LICENSE
+-------
+
+LZMA SDK is written and placed in the public domain by Igor Pavlov.
+
+Some code in LZMA SDK is based on public domain code from another developers:
+ 1) PPMd var.H (2001): Dmitry Shkarin
+ 2) SHA-256: Wei Dai (Crypto++ library)
+
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute the
+original LZMA SDK code, either in source code form or as a compiled binary, for
+any purpose, commercial or non-commercial, and by any means.
+
+LZMA SDK code is compatible with open source licenses, for example, you can
+include it to GNU GPL or GNU LGPL code.
diff --git a/src/libs/7zip/installer_framework_changes.txt b/src/libs/3rdparty/7zip/installer_framework_changes.txt
index 6b5839208..6b5839208 100644
--- a/src/libs/7zip/installer_framework_changes.txt
+++ b/src/libs/3rdparty/7zip/installer_framework_changes.txt
diff --git a/src/libs/3rdparty/7zip/qt_attribution.json b/src/libs/3rdparty/7zip/qt_attribution.json
new file mode 100644
index 000000000..1028b2bd4
--- /dev/null
+++ b/src/libs/3rdparty/7zip/qt_attribution.json
@@ -0,0 +1,13 @@
+{
+ "Id": "lzmasdk",
+ "Name": "LZMA SDK",
+ "QDocModule": "ifw",
+ "Description": "Tools to develop applications that use LZMA compression.",
+ "QtUsage": "Used for reading and writing 7z archives in Qt Installer Framework",
+ "Homepage": "https://www.7-zip.org/sdk.html",
+ "Version": "9.38.beta",
+ "License": "Public Domain",
+ "LicenseId": "public-domain",
+ "LicenseFile": "COPYING",
+ "Copyright": "Copyright (c) 2015 Igor Pavlov"
+}
diff --git a/src/libs/7zip/unix/C/7zCrc.c b/src/libs/3rdparty/7zip/unix/C/7zCrc.c
index ac33358f6..ac33358f6 100644
--- a/src/libs/7zip/unix/C/7zCrc.c
+++ b/src/libs/3rdparty/7zip/unix/C/7zCrc.c
diff --git a/src/libs/7zip/unix/C/7zCrc.h b/src/libs/3rdparty/7zip/unix/C/7zCrc.h
index 8fd579587..8fd579587 100644
--- a/src/libs/7zip/unix/C/7zCrc.h
+++ b/src/libs/3rdparty/7zip/unix/C/7zCrc.h
diff --git a/src/libs/7zip/unix/C/7zCrcOpt.c b/src/libs/3rdparty/7zip/unix/C/7zCrcOpt.c
index ce132b5d4..ce132b5d4 100644
--- a/src/libs/7zip/unix/C/7zCrcOpt.c
+++ b/src/libs/3rdparty/7zip/unix/C/7zCrcOpt.c
diff --git a/src/libs/7zip/unix/C/7zStream.c b/src/libs/3rdparty/7zip/unix/C/7zStream.c
index 88f9c42b1..88f9c42b1 100644
--- a/src/libs/7zip/unix/C/7zStream.c
+++ b/src/libs/3rdparty/7zip/unix/C/7zStream.c
diff --git a/src/libs/7zip/unix/C/7zTypes.h b/src/libs/3rdparty/7zip/unix/C/7zTypes.h
index 778413ef4..778413ef4 100644
--- a/src/libs/7zip/unix/C/7zTypes.h
+++ b/src/libs/3rdparty/7zip/unix/C/7zTypes.h
diff --git a/src/libs/7zip/unix/C/7zVersion.h b/src/libs/3rdparty/7zip/unix/C/7zVersion.h
index 518513534..518513534 100644
--- a/src/libs/7zip/unix/C/7zVersion.h
+++ b/src/libs/3rdparty/7zip/unix/C/7zVersion.h
diff --git a/src/libs/7zip/unix/C/Alloc.c b/src/libs/3rdparty/7zip/unix/C/Alloc.c
index 9c5fe00ca..9c5fe00ca 100644
--- a/src/libs/7zip/unix/C/Alloc.c
+++ b/src/libs/3rdparty/7zip/unix/C/Alloc.c
diff --git a/src/libs/7zip/unix/C/Alloc.h b/src/libs/3rdparty/7zip/unix/C/Alloc.h
index 7ef372e3a..7ef372e3a 100644
--- a/src/libs/7zip/unix/C/Alloc.h
+++ b/src/libs/3rdparty/7zip/unix/C/Alloc.h
diff --git a/src/libs/7zip/unix/C/Bra.c b/src/libs/3rdparty/7zip/unix/C/Bra.c
index 33f7a391c..33f7a391c 100644
--- a/src/libs/7zip/unix/C/Bra.c
+++ b/src/libs/3rdparty/7zip/unix/C/Bra.c
diff --git a/src/libs/7zip/unix/C/Bra.h b/src/libs/3rdparty/7zip/unix/C/Bra.h
index 184c291a7..184c291a7 100644
--- a/src/libs/7zip/unix/C/Bra.h
+++ b/src/libs/3rdparty/7zip/unix/C/Bra.h
diff --git a/src/libs/7zip/unix/C/Bra86.c b/src/libs/3rdparty/7zip/unix/C/Bra86.c
index 6db15e7ec..6db15e7ec 100644
--- a/src/libs/7zip/unix/C/Bra86.c
+++ b/src/libs/3rdparty/7zip/unix/C/Bra86.c
diff --git a/src/libs/7zip/unix/C/BraIA64.c b/src/libs/3rdparty/7zip/unix/C/BraIA64.c
index aa1a44e1e..aa1a44e1e 100644
--- a/src/libs/7zip/unix/C/BraIA64.c
+++ b/src/libs/3rdparty/7zip/unix/C/BraIA64.c
diff --git a/src/libs/7zip/unix/C/C.pri b/src/libs/3rdparty/7zip/unix/C/C.pri
index 6f0abb674..6f0abb674 100644
--- a/src/libs/7zip/unix/C/C.pri
+++ b/src/libs/3rdparty/7zip/unix/C/C.pri
diff --git a/src/libs/7zip/unix/C/Compiler.h b/src/libs/3rdparty/7zip/unix/C/Compiler.h
index 6e964897e..6e964897e 100644
--- a/src/libs/7zip/unix/C/Compiler.h
+++ b/src/libs/3rdparty/7zip/unix/C/Compiler.h
diff --git a/src/libs/7zip/unix/C/CpuArch.h b/src/libs/3rdparty/7zip/unix/C/CpuArch.h
index e3e5d8c99..e3e5d8c99 100644
--- a/src/libs/7zip/unix/C/CpuArch.h
+++ b/src/libs/3rdparty/7zip/unix/C/CpuArch.h
diff --git a/src/libs/7zip/unix/C/Delta.c b/src/libs/3rdparty/7zip/unix/C/Delta.c
index e3edd21ed..e3edd21ed 100644
--- a/src/libs/7zip/unix/C/Delta.c
+++ b/src/libs/3rdparty/7zip/unix/C/Delta.c
diff --git a/src/libs/7zip/unix/C/Delta.h b/src/libs/3rdparty/7zip/unix/C/Delta.h
index 2fa54ad67..2fa54ad67 100644
--- a/src/libs/7zip/unix/C/Delta.h
+++ b/src/libs/3rdparty/7zip/unix/C/Delta.h
diff --git a/src/libs/7zip/unix/C/LzFind.c b/src/libs/3rdparty/7zip/unix/C/LzFind.c
index 9a4d25b80..9a4d25b80 100644
--- a/src/libs/7zip/unix/C/LzFind.c
+++ b/src/libs/3rdparty/7zip/unix/C/LzFind.c
diff --git a/src/libs/7zip/unix/C/LzFind.h b/src/libs/3rdparty/7zip/unix/C/LzFind.h
index 706143d25..706143d25 100644
--- a/src/libs/7zip/unix/C/LzFind.h
+++ b/src/libs/3rdparty/7zip/unix/C/LzFind.h
diff --git a/src/libs/7zip/unix/C/LzFindMt.c b/src/libs/3rdparty/7zip/unix/C/LzFindMt.c
index 8be0adaaf..8be0adaaf 100644
--- a/src/libs/7zip/unix/C/LzFindMt.c
+++ b/src/libs/3rdparty/7zip/unix/C/LzFindMt.c
diff --git a/src/libs/7zip/unix/C/LzFindMt.h b/src/libs/3rdparty/7zip/unix/C/LzFindMt.h
index 65cc12783..65cc12783 100644
--- a/src/libs/7zip/unix/C/LzFindMt.h
+++ b/src/libs/3rdparty/7zip/unix/C/LzFindMt.h
diff --git a/src/libs/7zip/unix/C/LzHash.h b/src/libs/3rdparty/7zip/unix/C/LzHash.h
index f3e89966c..f3e89966c 100644
--- a/src/libs/7zip/unix/C/LzHash.h
+++ b/src/libs/3rdparty/7zip/unix/C/LzHash.h
diff --git a/src/libs/7zip/unix/C/Lzma2Dec.c b/src/libs/3rdparty/7zip/unix/C/Lzma2Dec.c
index e7dcc2725..e7dcc2725 100644
--- a/src/libs/7zip/unix/C/Lzma2Dec.c
+++ b/src/libs/3rdparty/7zip/unix/C/Lzma2Dec.c
diff --git a/src/libs/7zip/unix/C/Lzma2Dec.h b/src/libs/3rdparty/7zip/unix/C/Lzma2Dec.h
index 367daf6b3..367daf6b3 100644
--- a/src/libs/7zip/unix/C/Lzma2Dec.h
+++ b/src/libs/3rdparty/7zip/unix/C/Lzma2Dec.h
diff --git a/src/libs/7zip/unix/C/Lzma2Enc.c b/src/libs/3rdparty/7zip/unix/C/Lzma2Enc.c
index 5d67cc344..5d67cc344 100644
--- a/src/libs/7zip/unix/C/Lzma2Enc.c
+++ b/src/libs/3rdparty/7zip/unix/C/Lzma2Enc.c
diff --git a/src/libs/7zip/unix/C/Lzma2Enc.h b/src/libs/3rdparty/7zip/unix/C/Lzma2Enc.h
index f409f184c..f409f184c 100644
--- a/src/libs/7zip/unix/C/Lzma2Enc.h
+++ b/src/libs/3rdparty/7zip/unix/C/Lzma2Enc.h
diff --git a/src/libs/7zip/unix/C/LzmaDec.c b/src/libs/3rdparty/7zip/unix/C/LzmaDec.c
index b1a2ad150..b1a2ad150 100644
--- a/src/libs/7zip/unix/C/LzmaDec.c
+++ b/src/libs/3rdparty/7zip/unix/C/LzmaDec.c
diff --git a/src/libs/7zip/unix/C/LzmaDec.h b/src/libs/3rdparty/7zip/unix/C/LzmaDec.h
index 63efc351f..63efc351f 100644
--- a/src/libs/7zip/unix/C/LzmaDec.h
+++ b/src/libs/3rdparty/7zip/unix/C/LzmaDec.h
diff --git a/src/libs/7zip/unix/C/LzmaEnc.c b/src/libs/3rdparty/7zip/unix/C/LzmaEnc.c
index bf3cc2ddb..bf3cc2ddb 100644
--- a/src/libs/7zip/unix/C/LzmaEnc.c
+++ b/src/libs/3rdparty/7zip/unix/C/LzmaEnc.c
diff --git a/src/libs/7zip/unix/C/LzmaEnc.h b/src/libs/3rdparty/7zip/unix/C/LzmaEnc.h
index cffe220bb..cffe220bb 100644
--- a/src/libs/7zip/unix/C/LzmaEnc.h
+++ b/src/libs/3rdparty/7zip/unix/C/LzmaEnc.h
diff --git a/src/libs/7zip/unix/C/MtCoder.c b/src/libs/3rdparty/7zip/unix/C/MtCoder.c
index 3d4dd2d14..3d4dd2d14 100644
--- a/src/libs/7zip/unix/C/MtCoder.c
+++ b/src/libs/3rdparty/7zip/unix/C/MtCoder.c
diff --git a/src/libs/7zip/unix/C/MtCoder.h b/src/libs/3rdparty/7zip/unix/C/MtCoder.h
index e2cbdc3ab..e2cbdc3ab 100644
--- a/src/libs/7zip/unix/C/MtCoder.h
+++ b/src/libs/3rdparty/7zip/unix/C/MtCoder.h
diff --git a/src/libs/7zip/unix/C/Precomp.h b/src/libs/3rdparty/7zip/unix/C/Precomp.h
index e8ff8b40e..e8ff8b40e 100644
--- a/src/libs/7zip/unix/C/Precomp.h
+++ b/src/libs/3rdparty/7zip/unix/C/Precomp.h
diff --git a/src/libs/7zip/unix/C/RotateDefs.h b/src/libs/3rdparty/7zip/unix/C/RotateDefs.h
index 1b83e5ea1..1b83e5ea1 100644
--- a/src/libs/7zip/unix/C/RotateDefs.h
+++ b/src/libs/3rdparty/7zip/unix/C/RotateDefs.h
diff --git a/src/libs/7zip/unix/C/Sha256.c b/src/libs/3rdparty/7zip/unix/C/Sha256.c
index 10df0874f..10df0874f 100644
--- a/src/libs/7zip/unix/C/Sha256.c
+++ b/src/libs/3rdparty/7zip/unix/C/Sha256.c
diff --git a/src/libs/7zip/unix/C/Sha256.h b/src/libs/3rdparty/7zip/unix/C/Sha256.h
index 3f455dbc0..3f455dbc0 100644
--- a/src/libs/7zip/unix/C/Sha256.h
+++ b/src/libs/3rdparty/7zip/unix/C/Sha256.h
diff --git a/src/libs/7zip/unix/C/Threads.c b/src/libs/3rdparty/7zip/unix/C/Threads.c
index f3fdb24e1..f3fdb24e1 100644
--- a/src/libs/7zip/unix/C/Threads.c
+++ b/src/libs/3rdparty/7zip/unix/C/Threads.c
diff --git a/src/libs/7zip/unix/C/Threads.h b/src/libs/3rdparty/7zip/unix/C/Threads.h
index a285bc901..a285bc901 100644
--- a/src/libs/7zip/unix/C/Threads.h
+++ b/src/libs/3rdparty/7zip/unix/C/Threads.h
diff --git a/src/libs/7zip/unix/C/Xz.c b/src/libs/3rdparty/7zip/unix/C/Xz.c
index fbc732a8a..fbc732a8a 100644
--- a/src/libs/7zip/unix/C/Xz.c
+++ b/src/libs/3rdparty/7zip/unix/C/Xz.c
diff --git a/src/libs/7zip/unix/C/Xz.h b/src/libs/3rdparty/7zip/unix/C/Xz.h
index 9268d5bc6..9268d5bc6 100644
--- a/src/libs/7zip/unix/C/Xz.h
+++ b/src/libs/3rdparty/7zip/unix/C/Xz.h
diff --git a/src/libs/7zip/unix/C/XzCrc64.c b/src/libs/3rdparty/7zip/unix/C/XzCrc64.c
index 2c04c0af4..2c04c0af4 100644
--- a/src/libs/7zip/unix/C/XzCrc64.c
+++ b/src/libs/3rdparty/7zip/unix/C/XzCrc64.c
diff --git a/src/libs/7zip/unix/C/XzCrc64.h b/src/libs/3rdparty/7zip/unix/C/XzCrc64.h
index 08dbc330c..08dbc330c 100644
--- a/src/libs/7zip/unix/C/XzCrc64.h
+++ b/src/libs/3rdparty/7zip/unix/C/XzCrc64.h
diff --git a/src/libs/7zip/unix/C/XzCrc64Opt.c b/src/libs/3rdparty/7zip/unix/C/XzCrc64Opt.c
index dccae1c19..dccae1c19 100644
--- a/src/libs/7zip/unix/C/XzCrc64Opt.c
+++ b/src/libs/3rdparty/7zip/unix/C/XzCrc64Opt.c
diff --git a/src/libs/7zip/unix/C/XzDec.c b/src/libs/3rdparty/7zip/unix/C/XzDec.c
index 6eef587d3..6eef587d3 100644
--- a/src/libs/7zip/unix/C/XzDec.c
+++ b/src/libs/3rdparty/7zip/unix/C/XzDec.c
diff --git a/src/libs/7zip/unix/C/XzEnc.c b/src/libs/3rdparty/7zip/unix/C/XzEnc.c
index 56680fcd8..56680fcd8 100644
--- a/src/libs/7zip/unix/C/XzEnc.c
+++ b/src/libs/3rdparty/7zip/unix/C/XzEnc.c
diff --git a/src/libs/7zip/unix/C/XzEnc.h b/src/libs/3rdparty/7zip/unix/C/XzEnc.h
index c3c19eca0..c3c19eca0 100644
--- a/src/libs/7zip/unix/C/XzEnc.h
+++ b/src/libs/3rdparty/7zip/unix/C/XzEnc.h
diff --git a/src/libs/7zip/unix/C/XzIn.c b/src/libs/3rdparty/7zip/unix/C/XzIn.c
index ed9eac31a..ed9eac31a 100644
--- a/src/libs/7zip/unix/C/XzIn.c
+++ b/src/libs/3rdparty/7zip/unix/C/XzIn.c
diff --git a/src/libs/7zip/unix/CPP/7zip/7zip.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/7zip.pri
index a2b70a1f2..a2b70a1f2 100644
--- a/src/libs/7zip/unix/CPP/7zip/7zip.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/7zip.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7z.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7z.pri
index 60211faae..60211faae 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7z.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7z.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h
index 5cde97c38..5cde97c38 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zCompressionMode.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp
index 973966bd3..973966bd3 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zDecode.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h
index 54e9d2b52..54e9d2b52 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zDecode.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp
index 5f1436fc7..5f1436fc7 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zEncode.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h
index 8e20bdb5f..8e20bdb5f 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zEncode.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp
index bb350455c..bb350455c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zExtract.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp
index 3f420a513..3f420a513 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h
index 4ed4b2dd2..4ed4b2dd2 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderInStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
index 847f65bf2..847f65bf2 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h
index cc2d77343..cc2d77343 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zFolderOutStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp
index ed65dc20c..ed65dc20c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandler.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h
index 677a3e10a..677a3e10a 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandler.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp
index 7de5b8140..7de5b8140 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHandlerOut.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp
index acff2fdd8..acff2fdd8 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHeader.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h
index 61dad655d..61dad655d 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zHeader.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp
index 28ec5e083..28ec5e083 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zIn.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zIn.h
index 98f61c81e..98f61c81e 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zIn.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zIn.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zItem.h
index c112f83fd..c112f83fd 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zItem.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zItem.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp
index e20858ea7..e20858ea7 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zOut.h
index 391ca9d02..391ca9d02 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zOut.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp
index a29f8abe9..a29f8abe9 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zProperties.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h
index 661817954..661817954 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zProperties.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp
index 37ea29d30..37ea29d30 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zRegister.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp
index 8e45d9875..8e45d9875 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h
index 2e26efd5c..2e26efd5c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zSpecStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp
index c745e32f0..c745e32f0 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h
index aee2d5ed3..aee2d5ed3 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/7z/7zUpdate.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Archive.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Archive.pri
index fe77cb0d4..fe77cb0d4 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Archive.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Archive.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp
index e562fec58..e562fec58 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h
index 50e7077ae..50e7077ae 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
index 36b252600..36b252600 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h
index 2190cf867..2190cf867 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/CoderMixer2MT.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/Common.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/Common.pri
index 5443ba297..5443ba297 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/Common.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp
index 7c4f54879..7c4f54879 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h
index b5a51fc07..b5a51fc07 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/DummyOutStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp
index 7b875fbd0..7b875fbd0 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h
index eba2a19e1..eba2a19e1 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/HandlerOut.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
index a2d688328..a2d688328 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h
index 31b761e45..31b761e45 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/InStreamWithCRC.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp
index 7cd3037be..7cd3037be 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h
index d0dc76a41..d0dc76a41 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ItemNameUtils.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp
index 17f749058..17f749058 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/MultiStream.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h
index 93aff33bf..93aff33bf 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/MultiStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
index f955c2254..f955c2254 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h
index 09b899bbd..09b899bbd 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/OutStreamWithCRC.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h
index 1038a8c02..1038a8c02 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/Common/ParseProperties.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/IArchive.h
index 038e05ed2..038e05ed2 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/IArchive.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/IArchive.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp
index 279cdefb7..279cdefb7 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/LzmaHandler.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp
index db9f49aa0..db9f49aa0 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/SplitHandler.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/XzHandler.cpp
index 789f41a72..789f41a72 100644
--- a/src/libs/7zip/unix/CPP/7zip/Archive/XzHandler.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Archive/XzHandler.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/CWrappers.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CWrappers.cpp
index a15794e2a..a15794e2a 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/CWrappers.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CWrappers.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/CWrappers.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CWrappers.h
index 4fe7dea3e..4fe7dea3e 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/CWrappers.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CWrappers.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/Common.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/Common.pri
index a23ad30b1..a23ad30b1 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/Common.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CreateCoder.cpp
index 01ccbe12a..01ccbe12a 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CreateCoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CreateCoder.h
index fe1f6ccfe..fe1f6ccfe 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/CreateCoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/CreateCoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp
index 958360fac..958360fac 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilePathAutoRename.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h
index 7b576591c..7b576591c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilePathAutoRename.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/FileStreams.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FileStreams.cpp
index ee1cc54e2..ee1cc54e2 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/FileStreams.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FileStreams.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/FileStreams.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FileStreams.h
index a80cbad4d..a80cbad4d 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/FileStreams.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FileStreams.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilterCoder.cpp
index 3a2023b35..3a2023b35 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilterCoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilterCoder.h
index 2b8f142f5..2b8f142f5 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/FilterCoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/FilterCoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/InBuffer.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InBuffer.cpp
index 133d95b38..133d95b38 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/InBuffer.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InBuffer.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/InBuffer.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InBuffer.h
index dd3c66808..dd3c66808 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/InBuffer.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InBuffer.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp
index be65ba32f..be65ba32f 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InOutTempBuffer.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h
index 256d72420..256d72420 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/InOutTempBuffer.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp
index 5f20dcda4..5f20dcda4 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LimitedStreams.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LimitedStreams.h
index b14616f3b..b14616f3b 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/LimitedStreams.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LimitedStreams.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/LockedStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LockedStream.cpp
index f05601cb6..f05601cb6 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/LockedStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LockedStream.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/LockedStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LockedStream.h
index 486e4220b..486e4220b 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/LockedStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/LockedStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/MethodId.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodId.h
index 28b615fcd..28b615fcd 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/MethodId.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodId.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/MethodProps.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodProps.cpp
index ff61995b7..ff61995b7 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/MethodProps.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodProps.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/MethodProps.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodProps.h
index 39e2ee937..39e2ee937 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/MethodProps.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/MethodProps.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/OutBuffer.cpp
index 4ba34a053..4ba34a053 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/OutBuffer.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/OutBuffer.h
index 0baad3636..0baad3636 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/OutBuffer.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/OutBuffer.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp
index bac45c1c2..bac45c1c2 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/ProgressUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/ProgressUtils.h
index bae5395c1..bae5395c1 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/ProgressUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/ProgressUtils.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/PropId.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/PropId.cpp
index 10daef715..10daef715 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/PropId.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/PropId.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/RegisterArc.h
index 82bd09673..82bd09673 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/RegisterArc.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/RegisterCodec.h
index 0c6662a6c..0c6662a6c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/RegisterCodec.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamBinder.cpp
index 43feef1ba..43feef1ba 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamBinder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamBinder.h
index aba6b8e17..aba6b8e17 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/StreamBinder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamBinder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamObjects.cpp
index 7721c3a7e..7721c3a7e 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamObjects.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamObjects.h
index d0c86b566..d0c86b566 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/StreamObjects.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamObjects.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamUtils.cpp
index 1402f4205..1402f4205 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamUtils.h
index ae914c004..ae914c004 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/StreamUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/StreamUtils.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp
index 7fcc88f5e..7fcc88f5e 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/UniqBlocks.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/UniqBlocks.h
index 9c08b09f4..9c08b09f4 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/UniqBlocks.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/UniqBlocks.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/VirtThread.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/VirtThread.cpp
index 77e3c1acf..77e3c1acf 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/VirtThread.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/VirtThread.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Common/VirtThread.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/VirtThread.h
index ebee158ca..ebee158ca 100644
--- a/src/libs/7zip/unix/CPP/7zip/Common/VirtThread.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Common/VirtThread.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp
index 9da6b9c28..9da6b9c28 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Coder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h
index e7bd37951..e7bd37951 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Coder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp
index 8eb1e7360..8eb1e7360 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Bcj2Register.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp
index 0e34ef488..0e34ef488 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjCoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjCoder.h
index 0754bcd23..0754bcd23 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BcjCoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjCoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp
index 648ad8e03..648ad8e03 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BcjRegister.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp
index 431709526..431709526 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchCoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchCoder.h
index 0e3a5c4e1..0e3a5c4e1 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BranchCoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchCoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp
index 239f25138..239f25138 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchMisc.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchMisc.h
index 81198b21c..81198b21c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BranchMisc.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchMisc.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp
index 380828c6d..380828c6d 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/BranchRegister.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp
index 645b6ffcd..645b6ffcd 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/ByteSwap.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Compress.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Compress.pri
index c9a7a5045..c9a7a5045 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Compress.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Compress.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp
index f0863202a..f0863202a 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyCoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyCoder.h
index 5e0bb6436..5e0bb6436 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/CopyCoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyCoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp
index efb9b9e95..efb9b9e95 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/CopyRegister.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp
index d8378a60e..d8378a60e 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/DeltaFilter.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp
index b20ae5f5e..b20ae5f5e 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h
index fd7ca2f39..fd7ca2f39 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Decoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp
index f867881c0..f867881c0 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h
index 6a2318076..6a2318076 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Encoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp
index cace871ef..cace871ef 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/Lzma2Register.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp
index d378ba668..d378ba668 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaDecoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h
index 140c48b9c..140c48b9c 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaDecoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp
index 484d04523..484d04523 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaEncoder.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h
index 7e15a132d..7e15a132d 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaEncoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp
index 96ed0baed..96ed0baed 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/LzmaRegister.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/RangeCoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/RangeCoder.h
index 1555bd705..1555bd705 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/RangeCoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/RangeCoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h
index 0eddd5586..0eddd5586 100644
--- a/src/libs/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Compress/RangeCoderBit.h
diff --git a/src/libs/7zip/unix/CPP/7zip/Guid.txt b/src/libs/3rdparty/7zip/unix/CPP/7zip/Guid.txt
index c1e7446be..c1e7446be 100644
--- a/src/libs/7zip/unix/CPP/7zip/Guid.txt
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/Guid.txt
diff --git a/src/libs/7zip/unix/CPP/7zip/ICoder.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/ICoder.h
index 74ee0e453..74ee0e453 100644
--- a/src/libs/7zip/unix/CPP/7zip/ICoder.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/ICoder.h
diff --git a/src/libs/7zip/unix/CPP/7zip/IDecl.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/IDecl.h
index 8316eb3ac..8316eb3ac 100644
--- a/src/libs/7zip/unix/CPP/7zip/IDecl.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/IDecl.h
diff --git a/src/libs/7zip/unix/CPP/7zip/IPassword.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/IPassword.h
index 7ea45537e..7ea45537e 100644
--- a/src/libs/7zip/unix/CPP/7zip/IPassword.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/IPassword.h
diff --git a/src/libs/7zip/unix/CPP/7zip/IProgress.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/IProgress.h
index a270e693b..a270e693b 100644
--- a/src/libs/7zip/unix/CPP/7zip/IProgress.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/IProgress.h
diff --git a/src/libs/7zip/unix/CPP/7zip/IStream.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/IStream.h
index 48643e7b3..48643e7b3 100644
--- a/src/libs/7zip/unix/CPP/7zip/IStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/IStream.h
diff --git a/src/libs/7zip/unix/CPP/7zip/PropID.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/PropID.h
index 82a7462bb..82a7462bb 100644
--- a/src/libs/7zip/unix/CPP/7zip/PropID.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/PropID.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
index 48587264f..48587264f 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h
index 255e12c3c..255e12c3c 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveCommandLine.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
index 250f66797..250f66797 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h
index 374297651..374297651 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveExtractCallback.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
index 9eab39d31..9eab39d31 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h
index b72892c2f..b72892c2f 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ArchiveOpenCallback.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Common.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Common.pri
index a1d75b674..a1d75b674 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Common.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp
index ce0b327b5..ce0b327b5 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DefaultName.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DefaultName.h
index df1645602..df1645602 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/DefaultName.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DefaultName.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/DirItem.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DirItem.h
index 82203c03a..82203c03a 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/DirItem.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/DirItem.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp
index b94e6274c..b94e6274c 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h
index 803a64e7d..803a64e7d 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/EnumDirItems.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Extract.cpp
index 5f94254ef..5f94254ef 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Extract.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Extract.h
index 052b2f7d3..052b2f7d3 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Extract.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h
index a54376705..a54376705 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractMode.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp
index 852fd5adb..852fd5adb 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h
index 751248a97..751248a97 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/ExtractingFilePath.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp
index 2d13a2af1..2d13a2af1 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/HashCalc.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/HashCalc.h
index 68e2404cc..68e2404cc 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/HashCalc.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/HashCalc.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h
index 7bb852795..7bb852795 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/IFileExtractCallback.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp
index 09c79147d..09c79147d 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h
index d254ae659..d254ae659 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/LoadCodecs.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp
index 7c53bd99f..7c53bd99f 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/OpenArchive.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h
index f2a5ce9fe..f2a5ce9fe 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/OpenArchive.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp
index 5ba8c7a89..5ba8c7a89 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h
index 3ee2981de..3ee2981de 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/PropIDUtils.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Property.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Property.h
index 8b57a2a64..8b57a2a64 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Property.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Property.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp
index 64b9d92a6..64b9d92a6 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SetProperties.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SetProperties.h
index 892f1a210..892f1a210 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/SetProperties.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SetProperties.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp
index b7e422a29..b7e422a29 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SortUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SortUtils.h
index 8e42e0682..8e42e0682 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/SortUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/SortUtils.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp
index 0c13ae158..0c13ae158 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/TempFiles.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/TempFiles.h
index 4099e6558..4099e6558 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/TempFiles.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/TempFiles.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Update.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Update.cpp
index 4697dca46..4697dca46 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Update.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Update.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Update.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Update.h
index b2fdb4647..b2fdb4647 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Update.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/Update.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp
index a80db7212..a80db7212 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateAction.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h
index b6645cbdd..b6645cbdd 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateAction.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp
index 609ece856..609ece856 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h
index 81982e61d..81982e61d 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateCallback.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp
index 95afdd694..95afdd694 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdatePair.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h
index 296d3b097..296d3b097 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdatePair.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp
index 2c4c28583..2c4c28583 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h
index ef7b0f7a3..ef7b0f7a3 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Common/UpdateProduce.h
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Console/Console.pri b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/Console.pri
index cdb025475..cdb025475 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Console/Console.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/Console.pri
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp
index f2889957a..f2889957a 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.cpp
diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h
index 509bab5fc..509bab5fc 100644
--- a/src/libs/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/7zip/UI/Console/PercentPrinter.h
diff --git a/src/libs/7zip/unix/CPP/Common/ComTry.h b/src/libs/3rdparty/7zip/unix/CPP/Common/ComTry.h
index 04bfe6320..04bfe6320 100644
--- a/src/libs/7zip/unix/CPP/Common/ComTry.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/ComTry.h
diff --git a/src/libs/7zip/unix/CPP/Common/CommandLineParser.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/CommandLineParser.cpp
index 749c4a2ea..749c4a2ea 100644
--- a/src/libs/7zip/unix/CPP/Common/CommandLineParser.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/CommandLineParser.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/CommandLineParser.h b/src/libs/3rdparty/7zip/unix/CPP/Common/CommandLineParser.h
index e3e6e6b14..e3e6e6b14 100644
--- a/src/libs/7zip/unix/CPP/Common/CommandLineParser.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/CommandLineParser.h
diff --git a/src/libs/7zip/unix/CPP/Common/Common.h b/src/libs/3rdparty/7zip/unix/CPP/Common/Common.h
index 9dd30f4be..9dd30f4be 100644
--- a/src/libs/7zip/unix/CPP/Common/Common.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/Common.h
diff --git a/src/libs/7zip/unix/CPP/Common/Common.pri b/src/libs/3rdparty/7zip/unix/CPP/Common/Common.pri
index eec23a144..eec23a144 100644
--- a/src/libs/7zip/unix/CPP/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/Common.pri
diff --git a/src/libs/7zip/unix/CPP/Common/Defs.h b/src/libs/3rdparty/7zip/unix/CPP/Common/Defs.h
index dad3ae8f1..dad3ae8f1 100644
--- a/src/libs/7zip/unix/CPP/Common/Defs.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/Defs.h
diff --git a/src/libs/7zip/unix/CPP/Common/IntToString.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/IntToString.cpp
index ed217c72c..ed217c72c 100644
--- a/src/libs/7zip/unix/CPP/Common/IntToString.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/IntToString.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/IntToString.h b/src/libs/3rdparty/7zip/unix/CPP/Common/IntToString.h
index 69605ab76..69605ab76 100644
--- a/src/libs/7zip/unix/CPP/Common/IntToString.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/IntToString.h
diff --git a/src/libs/7zip/unix/CPP/Common/ListFileUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/ListFileUtils.cpp
index cede1ae54..cede1ae54 100644
--- a/src/libs/7zip/unix/CPP/Common/ListFileUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/ListFileUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/ListFileUtils.h b/src/libs/3rdparty/7zip/unix/CPP/Common/ListFileUtils.h
index e8d833fdb..e8d833fdb 100644
--- a/src/libs/7zip/unix/CPP/Common/ListFileUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/ListFileUtils.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyBuffer.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyBuffer.h
index 7bd79f6f4..7bd79f6f4 100644
--- a/src/libs/7zip/unix/CPP/Common/MyBuffer.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyBuffer.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyCom.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyCom.h
index 466407cde..466407cde 100644
--- a/src/libs/7zip/unix/CPP/Common/MyCom.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyCom.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyException.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyException.h
index f0ad11158..f0ad11158 100644
--- a/src/libs/7zip/unix/CPP/Common/MyException.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyException.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyGuidDef.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyGuidDef.h
index 68745870e..68745870e 100644
--- a/src/libs/7zip/unix/CPP/Common/MyGuidDef.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyGuidDef.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyInitGuid.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyInitGuid.h
index 279fba5d6..279fba5d6 100644
--- a/src/libs/7zip/unix/CPP/Common/MyInitGuid.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyInitGuid.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyString.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/MyString.cpp
index 6fbfa334b..6fbfa334b 100644
--- a/src/libs/7zip/unix/CPP/Common/MyString.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyString.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/MyString.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyString.h
index 638ceed9b..638ceed9b 100644
--- a/src/libs/7zip/unix/CPP/Common/MyString.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyString.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyTypes.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyTypes.h
index fe41188db..fe41188db 100644
--- a/src/libs/7zip/unix/CPP/Common/MyTypes.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyTypes.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyUnknown.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyUnknown.h
index 8b95afd38..8b95afd38 100644
--- a/src/libs/7zip/unix/CPP/Common/MyUnknown.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyUnknown.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyVector.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyVector.h
index 7e61dec31..7e61dec31 100644
--- a/src/libs/7zip/unix/CPP/Common/MyVector.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyVector.h
diff --git a/src/libs/7zip/unix/CPP/Common/MyWindows.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/MyWindows.cpp
index 9acddc974..9acddc974 100644
--- a/src/libs/7zip/unix/CPP/Common/MyWindows.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyWindows.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/MyWindows.h b/src/libs/3rdparty/7zip/unix/CPP/Common/MyWindows.h
index 83a896283..83a896283 100644
--- a/src/libs/7zip/unix/CPP/Common/MyWindows.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/MyWindows.h
diff --git a/src/libs/7zip/unix/CPP/Common/NewHandler.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/NewHandler.cpp
index e53f1d14d..e53f1d14d 100644
--- a/src/libs/7zip/unix/CPP/Common/NewHandler.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/NewHandler.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/NewHandler.h b/src/libs/3rdparty/7zip/unix/CPP/Common/NewHandler.h
index e3e7422c8..e3e7422c8 100644
--- a/src/libs/7zip/unix/CPP/Common/NewHandler.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/NewHandler.h
diff --git a/src/libs/7zip/unix/CPP/Common/StdOutStream.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/StdOutStream.cpp
index 6aed31a31..6aed31a31 100644
--- a/src/libs/7zip/unix/CPP/Common/StdOutStream.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/StdOutStream.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/StdOutStream.h b/src/libs/3rdparty/7zip/unix/CPP/Common/StdOutStream.h
index 0a8c0febb..0a8c0febb 100644
--- a/src/libs/7zip/unix/CPP/Common/StdOutStream.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/StdOutStream.h
diff --git a/src/libs/7zip/unix/CPP/Common/StringConvert.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/StringConvert.cpp
index 78f3d90ef..78f3d90ef 100644
--- a/src/libs/7zip/unix/CPP/Common/StringConvert.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/StringConvert.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/StringConvert.h b/src/libs/3rdparty/7zip/unix/CPP/Common/StringConvert.h
index 8eea72ef2..8eea72ef2 100644
--- a/src/libs/7zip/unix/CPP/Common/StringConvert.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/StringConvert.h
diff --git a/src/libs/7zip/unix/CPP/Common/StringToInt.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/StringToInt.cpp
index 2023fcc2c..2023fcc2c 100644
--- a/src/libs/7zip/unix/CPP/Common/StringToInt.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/StringToInt.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/StringToInt.h b/src/libs/3rdparty/7zip/unix/CPP/Common/StringToInt.h
index 5c5d7d7fe..5c5d7d7fe 100644
--- a/src/libs/7zip/unix/CPP/Common/StringToInt.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/StringToInt.h
diff --git a/src/libs/7zip/unix/CPP/Common/UTFConvert.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/UTFConvert.cpp
index 38bac3331..38bac3331 100644
--- a/src/libs/7zip/unix/CPP/Common/UTFConvert.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/UTFConvert.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/UTFConvert.h b/src/libs/3rdparty/7zip/unix/CPP/Common/UTFConvert.h
index 16b02fe45..16b02fe45 100644
--- a/src/libs/7zip/unix/CPP/Common/UTFConvert.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/UTFConvert.h
diff --git a/src/libs/7zip/unix/CPP/Common/Wildcard.cpp b/src/libs/3rdparty/7zip/unix/CPP/Common/Wildcard.cpp
index e88a1cf1c..e88a1cf1c 100644
--- a/src/libs/7zip/unix/CPP/Common/Wildcard.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/Wildcard.cpp
diff --git a/src/libs/7zip/unix/CPP/Common/Wildcard.h b/src/libs/3rdparty/7zip/unix/CPP/Common/Wildcard.h
index a645e28b5..a645e28b5 100644
--- a/src/libs/7zip/unix/CPP/Common/Wildcard.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Common/Wildcard.h
diff --git a/src/libs/7zip/unix/CPP/Windows/DLL.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/DLL.cpp
index 345153cb2..345153cb2 100644
--- a/src/libs/7zip/unix/CPP/Windows/DLL.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/DLL.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/DLL.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/DLL.h
index 2e044ec59..2e044ec59 100644
--- a/src/libs/7zip/unix/CPP/Windows/DLL.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/DLL.h
diff --git a/src/libs/7zip/unix/CPP/Windows/Defs.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/Defs.h
index b88df748d..b88df748d 100644
--- a/src/libs/7zip/unix/CPP/Windows/Defs.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/Defs.h
diff --git a/src/libs/7zip/unix/CPP/Windows/FileDir.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileDir.cpp
index 9915bd8b8..9915bd8b8 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileDir.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileDir.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/FileDir.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileDir.h
index 02d3e5a57..02d3e5a57 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileDir.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileDir.h
diff --git a/src/libs/7zip/unix/CPP/Windows/FileFind.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileFind.cpp
index eb2f93835..eb2f93835 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileFind.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileFind.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/FileFind.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileFind.h
index 06435bb3d..06435bb3d 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileFind.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileFind.h
diff --git a/src/libs/7zip/unix/CPP/Windows/FileIO.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileIO.cpp
index 5da074a0f..5da074a0f 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileIO.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileIO.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/FileIO.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileIO.h
index a262e5610..a262e5610 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileIO.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileIO.h
diff --git a/src/libs/7zip/unix/CPP/Windows/FileName.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileName.cpp
index b726976fe..b726976fe 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileName.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileName.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/FileName.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileName.h
index 19c585fc9..19c585fc9 100644
--- a/src/libs/7zip/unix/CPP/Windows/FileName.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/FileName.h
diff --git a/src/libs/7zip/unix/CPP/Windows/PropVariant.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariant.cpp
index d16f576cc..d16f576cc 100644
--- a/src/libs/7zip/unix/CPP/Windows/PropVariant.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariant.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/PropVariant.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariant.h
index d71b85e1d..d71b85e1d 100644
--- a/src/libs/7zip/unix/CPP/Windows/PropVariant.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariant.h
diff --git a/src/libs/7zip/unix/CPP/Windows/PropVariantConv.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariantConv.cpp
index dfb93d6d6..dfb93d6d6 100644
--- a/src/libs/7zip/unix/CPP/Windows/PropVariantConv.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariantConv.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/PropVariantConv.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariantConv.h
index 5d26357f0..5d26357f0 100644
--- a/src/libs/7zip/unix/CPP/Windows/PropVariantConv.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/PropVariantConv.h
diff --git a/src/libs/7zip/unix/CPP/Windows/Synchronization.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization.cpp
index 4be33e1e9..4be33e1e9 100644
--- a/src/libs/7zip/unix/CPP/Windows/Synchronization.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/Synchronization.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization.h
index 1c45a1b78..1c45a1b78 100644
--- a/src/libs/7zip/unix/CPP/Windows/Synchronization.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization.h
diff --git a/src/libs/7zip/unix/CPP/Windows/Synchronization2.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization2.h
index ff8f7a752..ff8f7a752 100644
--- a/src/libs/7zip/unix/CPP/Windows/Synchronization2.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/Synchronization2.h
diff --git a/src/libs/7zip/unix/CPP/Windows/System.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/System.cpp
index 55e723c40..55e723c40 100644
--- a/src/libs/7zip/unix/CPP/Windows/System.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/System.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/System.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/System.h
index 4133a7b30..4133a7b30 100644
--- a/src/libs/7zip/unix/CPP/Windows/System.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/System.h
diff --git a/src/libs/7zip/unix/CPP/Windows/Thread.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/Thread.h
index 944f19744..944f19744 100644
--- a/src/libs/7zip/unix/CPP/Windows/Thread.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/Thread.h
diff --git a/src/libs/7zip/unix/CPP/Windows/TimeUtils.cpp b/src/libs/3rdparty/7zip/unix/CPP/Windows/TimeUtils.cpp
index 7ef44d9cc..7ef44d9cc 100644
--- a/src/libs/7zip/unix/CPP/Windows/TimeUtils.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/TimeUtils.cpp
diff --git a/src/libs/7zip/unix/CPP/Windows/TimeUtils.h b/src/libs/3rdparty/7zip/unix/CPP/Windows/TimeUtils.h
index 2967214e6..2967214e6 100644
--- a/src/libs/7zip/unix/CPP/Windows/TimeUtils.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/TimeUtils.h
diff --git a/src/libs/7zip/unix/CPP/Windows/Windows.pri b/src/libs/3rdparty/7zip/unix/CPP/Windows/Windows.pri
index 117fb37b3..117fb37b3 100644
--- a/src/libs/7zip/unix/CPP/Windows/Windows.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/Windows/Windows.pri
diff --git a/src/libs/7zip/unix/CPP/include_windows/basetyps.h b/src/libs/3rdparty/7zip/unix/CPP/include_windows/basetyps.h
index 2200cb7c3..2200cb7c3 100644
--- a/src/libs/7zip/unix/CPP/include_windows/basetyps.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/include_windows/basetyps.h
diff --git a/src/libs/7zip/unix/CPP/include_windows/include_windows.pri b/src/libs/3rdparty/7zip/unix/CPP/include_windows/include_windows.pri
index 5ef72fddd..5ef72fddd 100644
--- a/src/libs/7zip/unix/CPP/include_windows/include_windows.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/include_windows/include_windows.pri
diff --git a/src/libs/7zip/unix/CPP/include_windows/tchar.h b/src/libs/3rdparty/7zip/unix/CPP/include_windows/tchar.h
index 5e89145af..5e89145af 100644
--- a/src/libs/7zip/unix/CPP/include_windows/tchar.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/include_windows/tchar.h
diff --git a/src/libs/7zip/unix/CPP/include_windows/windows.h b/src/libs/3rdparty/7zip/unix/CPP/include_windows/windows.h
index 59541864c..59541864c 100644
--- a/src/libs/7zip/unix/CPP/include_windows/windows.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/include_windows/windows.h
diff --git a/src/libs/7zip/unix/CPP/myWindows/StdAfx.h b/src/libs/3rdparty/7zip/unix/CPP/myWindows/StdAfx.h
index 86ce117a9..86ce117a9 100644
--- a/src/libs/7zip/unix/CPP/myWindows/StdAfx.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/StdAfx.h
diff --git a/src/libs/7zip/unix/CPP/myWindows/config.h b/src/libs/3rdparty/7zip/unix/CPP/myWindows/config.h
index ee7d11882..ee7d11882 100644
--- a/src/libs/7zip/unix/CPP/myWindows/config.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/config.h
diff --git a/src/libs/7zip/unix/CPP/myWindows/initguid.h b/src/libs/3rdparty/7zip/unix/CPP/myWindows/initguid.h
index f7580d571..f7580d571 100644
--- a/src/libs/7zip/unix/CPP/myWindows/initguid.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/initguid.h
diff --git a/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
index b4b444392..e101db855 100644
--- a/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
@@ -1,32 +1,26 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
@@ -43,7 +37,7 @@ void SplitCommandLine(const UString &s, UStringVector &parts)
parts.Clear();
const QString cmdLine = QString::fromStdWString(static_cast<const wchar_t*>(s));
- const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts);
+ const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts);
foreach (const QString &arg, args)
parts.Add(arg.toStdWString().c_str());
}
diff --git a/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myDateAndTime.cpp
index 0c8deef18..0c8deef18 100644
--- a/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myDateAndTime.cpp
diff --git a/src/libs/7zip/unix/CPP/myWindows/myPrivate.h b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myPrivate.h
index 73739d095..73739d095 100644
--- a/src/libs/7zip/unix/CPP/myWindows/myPrivate.h
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myPrivate.h
diff --git a/src/libs/7zip/unix/CPP/myWindows/myWindows.pri b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myWindows.pri
index 0875fdb1f..0875fdb1f 100644
--- a/src/libs/7zip/unix/CPP/myWindows/myWindows.pri
+++ b/src/libs/3rdparty/7zip/unix/CPP/myWindows/myWindows.pri
diff --git a/src/libs/7zip/unix/unix.pri b/src/libs/3rdparty/7zip/unix/unix.pri
index 6071add8f..6071add8f 100644
--- a/src/libs/7zip/unix/unix.pri
+++ b/src/libs/3rdparty/7zip/unix/unix.pri
diff --git a/src/libs/7zip/win/C/7zCrc.c b/src/libs/3rdparty/7zip/win/C/7zCrc.c
index 5f32380dd..5f32380dd 100644
--- a/src/libs/7zip/win/C/7zCrc.c
+++ b/src/libs/3rdparty/7zip/win/C/7zCrc.c
diff --git a/src/libs/7zip/win/C/7zCrc.h b/src/libs/3rdparty/7zip/win/C/7zCrc.h
index 8fd579587..8fd579587 100644
--- a/src/libs/7zip/win/C/7zCrc.h
+++ b/src/libs/3rdparty/7zip/win/C/7zCrc.h
diff --git a/src/libs/7zip/win/C/7zCrcOpt.c b/src/libs/3rdparty/7zip/win/C/7zCrcOpt.c
index ce132b5d4..ce132b5d4 100644
--- a/src/libs/7zip/win/C/7zCrcOpt.c
+++ b/src/libs/3rdparty/7zip/win/C/7zCrcOpt.c
diff --git a/src/libs/7zip/win/C/7zStream.c b/src/libs/3rdparty/7zip/win/C/7zStream.c
index 88f9c42b1..88f9c42b1 100644
--- a/src/libs/7zip/win/C/7zStream.c
+++ b/src/libs/3rdparty/7zip/win/C/7zStream.c
diff --git a/src/libs/7zip/win/C/7zTypes.h b/src/libs/3rdparty/7zip/win/C/7zTypes.h
index 778413ef4..778413ef4 100644
--- a/src/libs/7zip/win/C/7zTypes.h
+++ b/src/libs/3rdparty/7zip/win/C/7zTypes.h
diff --git a/src/libs/7zip/win/C/7zVersion.h b/src/libs/3rdparty/7zip/win/C/7zVersion.h
index 475918352..475918352 100644
--- a/src/libs/7zip/win/C/7zVersion.h
+++ b/src/libs/3rdparty/7zip/win/C/7zVersion.h
diff --git a/src/libs/7zip/win/C/Alloc.c b/src/libs/3rdparty/7zip/win/C/Alloc.c
index 1f3f6c661..1f3f6c661 100644
--- a/src/libs/7zip/win/C/Alloc.c
+++ b/src/libs/3rdparty/7zip/win/C/Alloc.c
diff --git a/src/libs/7zip/win/C/Alloc.h b/src/libs/3rdparty/7zip/win/C/Alloc.h
index b8e414367..b8e414367 100644
--- a/src/libs/7zip/win/C/Alloc.h
+++ b/src/libs/3rdparty/7zip/win/C/Alloc.h
diff --git a/src/libs/7zip/win/C/Bra.c b/src/libs/3rdparty/7zip/win/C/Bra.c
index 33f7a391c..33f7a391c 100644
--- a/src/libs/7zip/win/C/Bra.c
+++ b/src/libs/3rdparty/7zip/win/C/Bra.c
diff --git a/src/libs/7zip/win/C/Bra.h b/src/libs/3rdparty/7zip/win/C/Bra.h
index 184c291a7..184c291a7 100644
--- a/src/libs/7zip/win/C/Bra.h
+++ b/src/libs/3rdparty/7zip/win/C/Bra.h
diff --git a/src/libs/7zip/win/C/Bra86.c b/src/libs/3rdparty/7zip/win/C/Bra86.c
index 6db15e7ec..6db15e7ec 100644
--- a/src/libs/7zip/win/C/Bra86.c
+++ b/src/libs/3rdparty/7zip/win/C/Bra86.c
diff --git a/src/libs/7zip/win/C/BraIA64.c b/src/libs/3rdparty/7zip/win/C/BraIA64.c
index aa1a44e1e..aa1a44e1e 100644
--- a/src/libs/7zip/win/C/BraIA64.c
+++ b/src/libs/3rdparty/7zip/win/C/BraIA64.c
diff --git a/src/libs/7zip/win/C/C.pri b/src/libs/3rdparty/7zip/win/C/C.pri
index 23d688642..23d688642 100644
--- a/src/libs/7zip/win/C/C.pri
+++ b/src/libs/3rdparty/7zip/win/C/C.pri
diff --git a/src/libs/7zip/win/C/Compiler.h b/src/libs/3rdparty/7zip/win/C/Compiler.h
index 6e964897e..6e964897e 100644
--- a/src/libs/7zip/win/C/Compiler.h
+++ b/src/libs/3rdparty/7zip/win/C/Compiler.h
diff --git a/src/libs/7zip/win/C/CpuArch.c b/src/libs/3rdparty/7zip/win/C/CpuArch.c
index 4feb028a2..4feb028a2 100644
--- a/src/libs/7zip/win/C/CpuArch.c
+++ b/src/libs/3rdparty/7zip/win/C/CpuArch.c
diff --git a/src/libs/7zip/win/C/CpuArch.h b/src/libs/3rdparty/7zip/win/C/CpuArch.h
index 4fee00937..4fee00937 100644
--- a/src/libs/7zip/win/C/CpuArch.h
+++ b/src/libs/3rdparty/7zip/win/C/CpuArch.h
diff --git a/src/libs/7zip/win/C/Delta.c b/src/libs/3rdparty/7zip/win/C/Delta.c
index e3edd21ed..e3edd21ed 100644
--- a/src/libs/7zip/win/C/Delta.c
+++ b/src/libs/3rdparty/7zip/win/C/Delta.c
diff --git a/src/libs/7zip/win/C/Delta.h b/src/libs/3rdparty/7zip/win/C/Delta.h
index 2fa54ad67..2fa54ad67 100644
--- a/src/libs/7zip/win/C/Delta.h
+++ b/src/libs/3rdparty/7zip/win/C/Delta.h
diff --git a/src/libs/7zip/win/C/LzFind.c b/src/libs/3rdparty/7zip/win/C/LzFind.c
index 9a4d25b80..9a4d25b80 100644
--- a/src/libs/7zip/win/C/LzFind.c
+++ b/src/libs/3rdparty/7zip/win/C/LzFind.c
diff --git a/src/libs/7zip/win/C/LzFind.h b/src/libs/3rdparty/7zip/win/C/LzFind.h
index 706143d25..706143d25 100644
--- a/src/libs/7zip/win/C/LzFind.h
+++ b/src/libs/3rdparty/7zip/win/C/LzFind.h
diff --git a/src/libs/7zip/win/C/LzFindMt.c b/src/libs/3rdparty/7zip/win/C/LzFindMt.c
index 8be0adaaf..8be0adaaf 100644
--- a/src/libs/7zip/win/C/LzFindMt.c
+++ b/src/libs/3rdparty/7zip/win/C/LzFindMt.c
diff --git a/src/libs/7zip/win/C/LzFindMt.h b/src/libs/3rdparty/7zip/win/C/LzFindMt.h
index 65cc12783..65cc12783 100644
--- a/src/libs/7zip/win/C/LzFindMt.h
+++ b/src/libs/3rdparty/7zip/win/C/LzFindMt.h
diff --git a/src/libs/7zip/win/C/LzHash.h b/src/libs/3rdparty/7zip/win/C/LzHash.h
index f3e89966c..f3e89966c 100644
--- a/src/libs/7zip/win/C/LzHash.h
+++ b/src/libs/3rdparty/7zip/win/C/LzHash.h
diff --git a/src/libs/7zip/win/C/Lzma2Dec.c b/src/libs/3rdparty/7zip/win/C/Lzma2Dec.c
index e7dcc2725..e7dcc2725 100644
--- a/src/libs/7zip/win/C/Lzma2Dec.c
+++ b/src/libs/3rdparty/7zip/win/C/Lzma2Dec.c
diff --git a/src/libs/7zip/win/C/Lzma2Dec.h b/src/libs/3rdparty/7zip/win/C/Lzma2Dec.h
index 367daf6b3..367daf6b3 100644
--- a/src/libs/7zip/win/C/Lzma2Dec.h
+++ b/src/libs/3rdparty/7zip/win/C/Lzma2Dec.h
diff --git a/src/libs/7zip/win/C/Lzma2Enc.c b/src/libs/3rdparty/7zip/win/C/Lzma2Enc.c
index 5d67cc344..5d67cc344 100644
--- a/src/libs/7zip/win/C/Lzma2Enc.c
+++ b/src/libs/3rdparty/7zip/win/C/Lzma2Enc.c
diff --git a/src/libs/7zip/win/C/Lzma2Enc.h b/src/libs/3rdparty/7zip/win/C/Lzma2Enc.h
index f409f184c..f409f184c 100644
--- a/src/libs/7zip/win/C/Lzma2Enc.h
+++ b/src/libs/3rdparty/7zip/win/C/Lzma2Enc.h
diff --git a/src/libs/7zip/win/C/LzmaDec.c b/src/libs/3rdparty/7zip/win/C/LzmaDec.c
index b1a2ad150..b1a2ad150 100644
--- a/src/libs/7zip/win/C/LzmaDec.c
+++ b/src/libs/3rdparty/7zip/win/C/LzmaDec.c
diff --git a/src/libs/7zip/win/C/LzmaDec.h b/src/libs/3rdparty/7zip/win/C/LzmaDec.h
index 63efc351f..63efc351f 100644
--- a/src/libs/7zip/win/C/LzmaDec.h
+++ b/src/libs/3rdparty/7zip/win/C/LzmaDec.h
diff --git a/src/libs/7zip/win/C/LzmaEnc.c b/src/libs/3rdparty/7zip/win/C/LzmaEnc.c
index bf3cc2ddb..bf3cc2ddb 100644
--- a/src/libs/7zip/win/C/LzmaEnc.c
+++ b/src/libs/3rdparty/7zip/win/C/LzmaEnc.c
diff --git a/src/libs/7zip/win/C/LzmaEnc.h b/src/libs/3rdparty/7zip/win/C/LzmaEnc.h
index cffe220bb..cffe220bb 100644
--- a/src/libs/7zip/win/C/LzmaEnc.h
+++ b/src/libs/3rdparty/7zip/win/C/LzmaEnc.h
diff --git a/src/libs/7zip/win/C/MtCoder.c b/src/libs/3rdparty/7zip/win/C/MtCoder.c
index 3d4dd2d14..3d4dd2d14 100644
--- a/src/libs/7zip/win/C/MtCoder.c
+++ b/src/libs/3rdparty/7zip/win/C/MtCoder.c
diff --git a/src/libs/7zip/win/C/MtCoder.h b/src/libs/3rdparty/7zip/win/C/MtCoder.h
index e2cbdc3ab..e2cbdc3ab 100644
--- a/src/libs/7zip/win/C/MtCoder.h
+++ b/src/libs/3rdparty/7zip/win/C/MtCoder.h
diff --git a/src/libs/7zip/win/C/Precomp.h b/src/libs/3rdparty/7zip/win/C/Precomp.h
index e8ff8b40e..e8ff8b40e 100644
--- a/src/libs/7zip/win/C/Precomp.h
+++ b/src/libs/3rdparty/7zip/win/C/Precomp.h
diff --git a/src/libs/7zip/win/C/RotateDefs.h b/src/libs/3rdparty/7zip/win/C/RotateDefs.h
index 1b83e5ea1..1b83e5ea1 100644
--- a/src/libs/7zip/win/C/RotateDefs.h
+++ b/src/libs/3rdparty/7zip/win/C/RotateDefs.h
diff --git a/src/libs/7zip/win/C/Sha256.c b/src/libs/3rdparty/7zip/win/C/Sha256.c
index 10df0874f..10df0874f 100644
--- a/src/libs/7zip/win/C/Sha256.c
+++ b/src/libs/3rdparty/7zip/win/C/Sha256.c
diff --git a/src/libs/7zip/win/C/Sha256.h b/src/libs/3rdparty/7zip/win/C/Sha256.h
index 3f455dbc0..3f455dbc0 100644
--- a/src/libs/7zip/win/C/Sha256.h
+++ b/src/libs/3rdparty/7zip/win/C/Sha256.h
diff --git a/src/libs/7zip/win/C/Threads.c b/src/libs/3rdparty/7zip/win/C/Threads.c
index 5c67a1e26..5c67a1e26 100644
--- a/src/libs/7zip/win/C/Threads.c
+++ b/src/libs/3rdparty/7zip/win/C/Threads.c
diff --git a/src/libs/7zip/win/C/Threads.h b/src/libs/3rdparty/7zip/win/C/Threads.h
index 9b3e1c556..9b3e1c556 100644
--- a/src/libs/7zip/win/C/Threads.h
+++ b/src/libs/3rdparty/7zip/win/C/Threads.h
diff --git a/src/libs/7zip/win/C/Xz.c b/src/libs/3rdparty/7zip/win/C/Xz.c
index fbc732a8a..fbc732a8a 100644
--- a/src/libs/7zip/win/C/Xz.c
+++ b/src/libs/3rdparty/7zip/win/C/Xz.c
diff --git a/src/libs/7zip/win/C/Xz.h b/src/libs/3rdparty/7zip/win/C/Xz.h
index 9268d5bc6..9268d5bc6 100644
--- a/src/libs/7zip/win/C/Xz.h
+++ b/src/libs/3rdparty/7zip/win/C/Xz.h
diff --git a/src/libs/7zip/win/C/XzCrc64.c b/src/libs/3rdparty/7zip/win/C/XzCrc64.c
index 2c04c0af4..2c04c0af4 100644
--- a/src/libs/7zip/win/C/XzCrc64.c
+++ b/src/libs/3rdparty/7zip/win/C/XzCrc64.c
diff --git a/src/libs/7zip/win/C/XzCrc64.h b/src/libs/3rdparty/7zip/win/C/XzCrc64.h
index 08dbc330c..08dbc330c 100644
--- a/src/libs/7zip/win/C/XzCrc64.h
+++ b/src/libs/3rdparty/7zip/win/C/XzCrc64.h
diff --git a/src/libs/7zip/win/C/XzCrc64Opt.c b/src/libs/3rdparty/7zip/win/C/XzCrc64Opt.c
index dccae1c19..dccae1c19 100644
--- a/src/libs/7zip/win/C/XzCrc64Opt.c
+++ b/src/libs/3rdparty/7zip/win/C/XzCrc64Opt.c
diff --git a/src/libs/7zip/win/C/XzDec.c b/src/libs/3rdparty/7zip/win/C/XzDec.c
index 6eef587d3..6eef587d3 100644
--- a/src/libs/7zip/win/C/XzDec.c
+++ b/src/libs/3rdparty/7zip/win/C/XzDec.c
diff --git a/src/libs/7zip/win/C/XzEnc.c b/src/libs/3rdparty/7zip/win/C/XzEnc.c
index 56680fcd8..56680fcd8 100644
--- a/src/libs/7zip/win/C/XzEnc.c
+++ b/src/libs/3rdparty/7zip/win/C/XzEnc.c
diff --git a/src/libs/7zip/win/C/XzEnc.h b/src/libs/3rdparty/7zip/win/C/XzEnc.h
index c3c19eca0..c3c19eca0 100644
--- a/src/libs/7zip/win/C/XzEnc.h
+++ b/src/libs/3rdparty/7zip/win/C/XzEnc.h
diff --git a/src/libs/7zip/win/C/XzIn.c b/src/libs/3rdparty/7zip/win/C/XzIn.c
index ed9eac31a..ed9eac31a 100644
--- a/src/libs/7zip/win/C/XzIn.c
+++ b/src/libs/3rdparty/7zip/win/C/XzIn.c
diff --git a/src/libs/7zip/win/CPP/7zip/7zip.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/7zip.pri
index a2b70a1f2..a2b70a1f2 100644
--- a/src/libs/7zip/win/CPP/7zip/7zip.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/7zip.pri
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7z.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7z.pri
index 7763cf705..7763cf705 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7z.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7z.pri
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h
index 5cde97c38..5cde97c38 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zCompressionMode.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp
index 973966bd3..973966bd3 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zDecode.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zDecode.h
index 54e9d2b52..54e9d2b52 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zDecode.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zDecode.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp
index 5f1436fc7..5f1436fc7 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zEncode.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zEncode.h
index 8e20bdb5f..8e20bdb5f 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zEncode.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zEncode.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp
index bb350455c..bb350455c 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zExtract.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp
index 3f420a513..3f420a513 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h
index 4ed4b2dd2..4ed4b2dd2 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderInStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
index 847f65bf2..847f65bf2 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h
index cc2d77343..cc2d77343 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zFolderOutStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp
index ed65dc20c..ed65dc20c 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandler.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandler.h
index 677a3e10a..677a3e10a 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandler.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandler.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp
index 7de5b8140..7de5b8140 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHandlerOut.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp
index acff2fdd8..acff2fdd8 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHeader.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHeader.h
index 61dad655d..61dad655d 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zHeader.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zHeader.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp
index 28ec5e083..28ec5e083 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zIn.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zIn.h
index 98f61c81e..98f61c81e 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zIn.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zIn.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zItem.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zItem.h
index c112f83fd..c112f83fd 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zItem.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zItem.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp
index e20858ea7..e20858ea7 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zOut.h
index 391ca9d02..391ca9d02 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zOut.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp
index a29f8abe9..a29f8abe9 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zProperties.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zProperties.h
index 661817954..661817954 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zProperties.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zProperties.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp
index 37ea29d30..37ea29d30 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zRegister.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp
index 8e45d9875..8e45d9875 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h
index 2e26efd5c..2e26efd5c 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zSpecStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp
index 26faf2a18..26faf2a18 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zUpdate.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h
index aee2d5ed3..aee2d5ed3 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/7zUpdate.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/StdAfx.h
index 2854ff3e9..2854ff3e9 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/7z/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/7z/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Archive.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Archive.pri
index e8a1c7832..e8a1c7832 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Archive.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Archive.pri
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp
index e562fec58..e562fec58 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h
index 50e7077ae..50e7077ae 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
index 36b252600..36b252600 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h
index 2190cf867..2190cf867 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/CoderMixer2MT.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/Common.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/Common.pri
index e808619d3..e808619d3 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/Common.pri
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp
index 7c4f54879..7c4f54879 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h
index b5a51fc07..b5a51fc07 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/DummyOutStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp
index 7b875fbd0..7b875fbd0 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/HandlerOut.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h
index eba2a19e1..eba2a19e1 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/HandlerOut.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
index a2d688328..a2d688328 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h
index 31b761e45..31b761e45 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/InStreamWithCRC.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp
index 7cd3037be..7cd3037be 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h
index d0dc76a41..d0dc76a41 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ItemNameUtils.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp
index 17f749058..17f749058 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/MultiStream.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/MultiStream.h
index 93aff33bf..93aff33bf 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/MultiStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/MultiStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
index f955c2254..f955c2254 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h
index 09b899bbd..09b899bbd 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/OutStreamWithCRC.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h
index 1038a8c02..1038a8c02 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/ParseProperties.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/Common/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/StdAfx.h
index 2854ff3e9..2854ff3e9 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/Common/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/Common/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/IArchive.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/IArchive.h
index a15a97f16..a15a97f16 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/IArchive.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/IArchive.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp
index 279cdefb7..279cdefb7 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/LzmaHandler.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/SplitHandler.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/SplitHandler.cpp
index db9f49aa0..db9f49aa0 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/SplitHandler.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/SplitHandler.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/StdAfx.h
index 1cbd7feae..1cbd7feae 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/7zip/Archive/XzHandler.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/XzHandler.cpp
index 789f41a72..789f41a72 100644
--- a/src/libs/7zip/win/CPP/7zip/Archive/XzHandler.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Archive/XzHandler.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/CWrappers.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CWrappers.cpp
index a15794e2a..a15794e2a 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/CWrappers.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CWrappers.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/CWrappers.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CWrappers.h
index 4fe7dea3e..4fe7dea3e 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/CWrappers.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CWrappers.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/Common.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/Common.pri
index e6b054a24..e6b054a24 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/Common.pri
diff --git a/src/libs/7zip/win/CPP/7zip/Common/CreateCoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CreateCoder.cpp
index 01ccbe12a..01ccbe12a 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/CreateCoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CreateCoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/CreateCoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CreateCoder.h
index fe1f6ccfe..fe1f6ccfe 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/CreateCoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/CreateCoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp
index 958360fac..958360fac 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilePathAutoRename.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilePathAutoRename.h
index 7b576591c..7b576591c 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/FilePathAutoRename.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilePathAutoRename.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/FileStreams.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FileStreams.cpp
index 77e5463e6..77e5463e6 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/FileStreams.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FileStreams.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/FileStreams.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FileStreams.h
index cce71e582..cce71e582 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/FileStreams.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FileStreams.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/FilterCoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilterCoder.cpp
index 3a2023b35..3a2023b35 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/FilterCoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilterCoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/FilterCoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilterCoder.h
index 2b8f142f5..2b8f142f5 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/FilterCoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/FilterCoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/InBuffer.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InBuffer.cpp
index 133d95b38..133d95b38 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/InBuffer.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InBuffer.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/InBuffer.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InBuffer.h
index dd3c66808..dd3c66808 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/InBuffer.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InBuffer.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp
index be65ba32f..be65ba32f 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InOutTempBuffer.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InOutTempBuffer.h
index 256d72420..256d72420 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/InOutTempBuffer.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/InOutTempBuffer.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LimitedStreams.cpp
index 5f20dcda4..5f20dcda4 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LimitedStreams.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LimitedStreams.h
index b14616f3b..b14616f3b 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LimitedStreams.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/LockedStream.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LockedStream.cpp
index f05601cb6..f05601cb6 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/LockedStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LockedStream.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/LockedStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LockedStream.h
index 486e4220b..486e4220b 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/LockedStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/LockedStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/MethodId.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodId.h
index 28b615fcd..28b615fcd 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/MethodId.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodId.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/MethodProps.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodProps.cpp
index ff61995b7..ff61995b7 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/MethodProps.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodProps.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/MethodProps.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodProps.h
index 39e2ee937..39e2ee937 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/MethodProps.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/MethodProps.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/OutBuffer.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/OutBuffer.cpp
index 4ba34a053..4ba34a053 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/OutBuffer.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/OutBuffer.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/OutBuffer.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/OutBuffer.h
index 0baad3636..0baad3636 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/OutBuffer.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/OutBuffer.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/ProgressUtils.cpp
index bac45c1c2..bac45c1c2 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/ProgressUtils.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/ProgressUtils.h
index bae5395c1..bae5395c1 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/ProgressUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/ProgressUtils.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/PropId.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/PropId.cpp
index 10daef715..10daef715 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/PropId.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/PropId.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/RegisterArc.h
index 82bd09673..82bd09673 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/RegisterArc.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/RegisterCodec.h
index 0c6662a6c..0c6662a6c 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/RegisterCodec.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StdAfx.h
index 1cbd7feae..1cbd7feae 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StreamBinder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamBinder.cpp
index 7a4c0ed26..7a4c0ed26 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StreamBinder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamBinder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StreamBinder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamBinder.h
index f3fb53228..f3fb53228 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StreamBinder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamBinder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StreamObjects.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamObjects.cpp
index 7721c3a7e..7721c3a7e 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StreamObjects.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamObjects.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StreamObjects.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamObjects.h
index d0c86b566..d0c86b566 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StreamObjects.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamObjects.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StreamUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamUtils.cpp
index 1402f4205..1402f4205 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StreamUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamUtils.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/StreamUtils.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamUtils.h
index ae914c004..ae914c004 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/StreamUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/StreamUtils.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/UniqBlocks.cpp
index 7fcc88f5e..7fcc88f5e 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/UniqBlocks.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/UniqBlocks.h
index 9c08b09f4..9c08b09f4 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/UniqBlocks.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/UniqBlocks.h
diff --git a/src/libs/7zip/win/CPP/7zip/Common/VirtThread.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/VirtThread.cpp
index 77e3c1acf..77e3c1acf 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/VirtThread.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/VirtThread.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Common/VirtThread.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/VirtThread.h
index ebee158ca..ebee158ca 100644
--- a/src/libs/7zip/win/CPP/7zip/Common/VirtThread.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Common/VirtThread.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp
index 9da6b9c28..9da6b9c28 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Coder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Coder.h
index e7bd37951..e7bd37951 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Bcj2Coder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Coder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp
index 8eb1e7360..8eb1e7360 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Bcj2Register.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjCoder.cpp
index 0e34ef488..0e34ef488 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjCoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjCoder.h
index 0754bcd23..0754bcd23 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BcjCoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjCoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BcjRegister.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjRegister.cpp
index 648ad8e03..648ad8e03 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BcjRegister.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BcjRegister.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchCoder.cpp
index 431709526..431709526 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchCoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchCoder.h
index 0e3a5c4e1..0e3a5c4e1 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BranchCoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchCoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchMisc.cpp
index 239f25138..239f25138 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchMisc.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchMisc.h
index 81198b21c..81198b21c 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BranchMisc.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchMisc.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/BranchRegister.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchRegister.cpp
index 380828c6d..380828c6d 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/BranchRegister.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/BranchRegister.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/ByteSwap.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/ByteSwap.cpp
index 645b6ffcd..645b6ffcd 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/ByteSwap.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/ByteSwap.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Compress.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Compress.pri
index db923a868..db923a868 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Compress.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Compress.pri
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyCoder.cpp
index f0863202a..f0863202a 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyCoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyCoder.h
index 5e0bb6436..5e0bb6436 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/CopyCoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyCoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/CopyRegister.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyRegister.cpp
index efb9b9e95..efb9b9e95 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/CopyRegister.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/CopyRegister.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp
index d8378a60e..d8378a60e 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/DeltaFilter.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp
index b20ae5f5e..b20ae5f5e 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Decoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h
index fd7ca2f39..fd7ca2f39 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Decoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp
index f867881c0..f867881c0 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Encoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h
index 6a2318076..6a2318076 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Encoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp
index cace871ef..cace871ef 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/Lzma2Register.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp
index d378ba668..d378ba668 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaDecoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaDecoder.h
index 140c48b9c..140c48b9c 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/LzmaDecoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaDecoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp
index 484d04523..484d04523 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaEncoder.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaEncoder.h
index 7e15a132d..7e15a132d 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/LzmaEncoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaEncoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp
index 96ed0baed..96ed0baed 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/LzmaRegister.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/RangeCoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/RangeCoder.h
index 1555bd705..1555bd705 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/RangeCoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/RangeCoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/RangeCoderBit.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/RangeCoderBit.h
index 0eddd5586..0eddd5586 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/RangeCoderBit.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/RangeCoderBit.h
diff --git a/src/libs/7zip/win/CPP/7zip/Compress/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/StdAfx.h
index 1cbd7feae..1cbd7feae 100644
--- a/src/libs/7zip/win/CPP/7zip/Compress/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Compress/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/7zip/Guid.txt b/src/libs/3rdparty/7zip/win/CPP/7zip/Guid.txt
index c1e7446be..c1e7446be 100644
--- a/src/libs/7zip/win/CPP/7zip/Guid.txt
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/Guid.txt
diff --git a/src/libs/7zip/win/CPP/7zip/ICoder.h b/src/libs/3rdparty/7zip/win/CPP/7zip/ICoder.h
index 74ee0e453..74ee0e453 100644
--- a/src/libs/7zip/win/CPP/7zip/ICoder.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/ICoder.h
diff --git a/src/libs/7zip/win/CPP/7zip/IDecl.h b/src/libs/3rdparty/7zip/win/CPP/7zip/IDecl.h
index 8316eb3ac..8316eb3ac 100644
--- a/src/libs/7zip/win/CPP/7zip/IDecl.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/IDecl.h
diff --git a/src/libs/7zip/win/CPP/7zip/IPassword.h b/src/libs/3rdparty/7zip/win/CPP/7zip/IPassword.h
index 7ea45537e..7ea45537e 100644
--- a/src/libs/7zip/win/CPP/7zip/IPassword.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/IPassword.h
diff --git a/src/libs/7zip/win/CPP/7zip/IProgress.h b/src/libs/3rdparty/7zip/win/CPP/7zip/IProgress.h
index a270e693b..a270e693b 100644
--- a/src/libs/7zip/win/CPP/7zip/IProgress.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/IProgress.h
diff --git a/src/libs/7zip/win/CPP/7zip/IStream.h b/src/libs/3rdparty/7zip/win/CPP/7zip/IStream.h
index 48643e7b3..48643e7b3 100644
--- a/src/libs/7zip/win/CPP/7zip/IStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/IStream.h
diff --git a/src/libs/7zip/win/CPP/7zip/PropID.h b/src/libs/3rdparty/7zip/win/CPP/7zip/PropID.h
index 82a7462bb..82a7462bb 100644
--- a/src/libs/7zip/win/CPP/7zip/PropID.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/PropID.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
index 769d21604..769d21604 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h
index 87e6619e6..87e6619e6 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveCommandLine.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
index 250f66797..250f66797 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h
index fe7ef42bb..fe7ef42bb 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveExtractCallback.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
index 4157aa4f1..4157aa4f1 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h
index 3352eadef..3352eadef 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ArchiveOpenCallback.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Common.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Common.pri
index 9d829d2af..9d829d2af 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Common.pri
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp
index ce0b327b5..ce0b327b5 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DefaultName.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DefaultName.h
index df1645602..df1645602 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/DefaultName.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DefaultName.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/DirItem.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DirItem.h
index 82203c03a..82203c03a 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/DirItem.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/DirItem.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp
index 1e9562a0e..1e9562a0e 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/EnumDirItems.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h
index 803a64e7d..803a64e7d 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/EnumDirItems.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Extract.cpp
index 13d2ad29a..13d2ad29a 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Extract.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Extract.h
index 052b2f7d3..052b2f7d3 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Extract.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ExtractMode.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractMode.h
index a54376705..a54376705 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ExtractMode.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractMode.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp
index 852fd5adb..852fd5adb 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h
index 751248a97..751248a97 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/ExtractingFilePath.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp
index 2d13a2af1..2d13a2af1 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/HashCalc.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/HashCalc.h
index 68e2404cc..68e2404cc 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/HashCalc.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/HashCalc.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h
index 7bb852795..7bb852795 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/IFileExtractCallback.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp
index a1d801b04..a1d801b04 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/LoadCodecs.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h
index d254ae659..d254ae659 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/LoadCodecs.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp
index 4efbc9cc7..4efbc9cc7 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/OpenArchive.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/OpenArchive.h
index 2ee743adc..2ee743adc 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/OpenArchive.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/OpenArchive.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp
index 8f27cc0d1..8f27cc0d1 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/PropIDUtils.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h
index 3ee2981de..3ee2981de 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/PropIDUtils.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Property.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Property.h
index 8b57a2a64..8b57a2a64 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/Property.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Property.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp
index 64b9d92a6..64b9d92a6 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SetProperties.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SetProperties.h
index 892f1a210..892f1a210 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/SetProperties.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SetProperties.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp
index b7e422a29..b7e422a29 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SortUtils.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SortUtils.h
index 8e42e0682..8e42e0682 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/SortUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/SortUtils.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/StdAfx.h
index 2854ff3e9..2854ff3e9 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp
index 0c13ae158..0c13ae158 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/TempFiles.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/TempFiles.h
index 4099e6558..4099e6558 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/TempFiles.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/TempFiles.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Update.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Update.cpp
index e3d538f10..e3d538f10 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/Update.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Update.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Update.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Update.h
index ff53cd992..ff53cd992 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/Update.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/Update.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp
index a80db7212..a80db7212 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateAction.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateAction.h
index 8c7609fb4..8c7609fb4 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateAction.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateAction.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp
index e490cde24..e490cde24 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateCallback.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h
index 81982e61d..81982e61d 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateCallback.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp
index 95afdd694..95afdd694 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdatePair.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdatePair.h
index 296d3b097..296d3b097 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdatePair.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdatePair.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp
index 2c4c28583..2c4c28583 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateProduce.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h
index ef7b0f7a3..ef7b0f7a3 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Common/UpdateProduce.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Console/Console.pri b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/Console.pri
index 14668f8b3..14668f8b3 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Console/Console.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/Console.pri
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp
index f2889957a..f2889957a 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/PercentPrinter.cpp
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h
index 509bab5fc..509bab5fc 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/PercentPrinter.h
diff --git a/src/libs/7zip/win/CPP/7zip/UI/Console/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/StdAfx.h
index 2854ff3e9..2854ff3e9 100644
--- a/src/libs/7zip/win/CPP/7zip/UI/Console/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/7zip/UI/Console/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/Common/ComTry.h b/src/libs/3rdparty/7zip/win/CPP/Common/ComTry.h
index c8aa4aedc..c8aa4aedc 100644
--- a/src/libs/7zip/win/CPP/Common/ComTry.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/ComTry.h
diff --git a/src/libs/7zip/win/CPP/Common/CommandLineParser.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/CommandLineParser.cpp
index ac9ae1960..ac9ae1960 100644
--- a/src/libs/7zip/win/CPP/Common/CommandLineParser.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/CommandLineParser.cpp
diff --git a/src/libs/7zip/win/CPP/Common/CommandLineParser.h b/src/libs/3rdparty/7zip/win/CPP/Common/CommandLineParser.h
index e3e6e6b14..e3e6e6b14 100644
--- a/src/libs/7zip/win/CPP/Common/CommandLineParser.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/CommandLineParser.h
diff --git a/src/libs/7zip/win/CPP/Common/Common.h b/src/libs/3rdparty/7zip/win/CPP/Common/Common.h
index 9dd30f4be..9dd30f4be 100644
--- a/src/libs/7zip/win/CPP/Common/Common.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/Common.h
diff --git a/src/libs/7zip/win/CPP/Common/Common.pri b/src/libs/3rdparty/7zip/win/CPP/Common/Common.pri
index ecc997d89..ecc997d89 100644
--- a/src/libs/7zip/win/CPP/Common/Common.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/Common.pri
diff --git a/src/libs/7zip/win/CPP/Common/Defs.h b/src/libs/3rdparty/7zip/win/CPP/Common/Defs.h
index dad3ae8f1..dad3ae8f1 100644
--- a/src/libs/7zip/win/CPP/Common/Defs.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/Defs.h
diff --git a/src/libs/7zip/win/CPP/Common/IntToString.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/IntToString.cpp
index ed217c72c..ed217c72c 100644
--- a/src/libs/7zip/win/CPP/Common/IntToString.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/IntToString.cpp
diff --git a/src/libs/7zip/win/CPP/Common/IntToString.h b/src/libs/3rdparty/7zip/win/CPP/Common/IntToString.h
index 69605ab76..69605ab76 100644
--- a/src/libs/7zip/win/CPP/Common/IntToString.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/IntToString.h
diff --git a/src/libs/7zip/win/CPP/Common/ListFileUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/ListFileUtils.cpp
index 4d7faeca3..4d7faeca3 100644
--- a/src/libs/7zip/win/CPP/Common/ListFileUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/ListFileUtils.cpp
diff --git a/src/libs/7zip/win/CPP/Common/ListFileUtils.h b/src/libs/3rdparty/7zip/win/CPP/Common/ListFileUtils.h
index e8d833fdb..e8d833fdb 100644
--- a/src/libs/7zip/win/CPP/Common/ListFileUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/ListFileUtils.h
diff --git a/src/libs/7zip/win/CPP/Common/MyBuffer.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyBuffer.h
index 7bd79f6f4..7bd79f6f4 100644
--- a/src/libs/7zip/win/CPP/Common/MyBuffer.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyBuffer.h
diff --git a/src/libs/7zip/win/CPP/Common/MyCom.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyCom.h
index 466407cde..466407cde 100644
--- a/src/libs/7zip/win/CPP/Common/MyCom.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyCom.h
diff --git a/src/libs/7zip/win/CPP/Common/MyException.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyException.h
index f0ad11158..f0ad11158 100644
--- a/src/libs/7zip/win/CPP/Common/MyException.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyException.h
diff --git a/src/libs/7zip/win/CPP/Common/MyGuidDef.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyGuidDef.h
index 68745870e..68745870e 100644
--- a/src/libs/7zip/win/CPP/Common/MyGuidDef.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyGuidDef.h
diff --git a/src/libs/7zip/win/CPP/Common/MyInitGuid.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyInitGuid.h
index 279fba5d6..279fba5d6 100644
--- a/src/libs/7zip/win/CPP/Common/MyInitGuid.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyInitGuid.h
diff --git a/src/libs/7zip/win/CPP/Common/MyString.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/MyString.cpp
index 6fbfa334b..6fbfa334b 100644
--- a/src/libs/7zip/win/CPP/Common/MyString.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyString.cpp
diff --git a/src/libs/7zip/win/CPP/Common/MyString.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyString.h
index 8417815cf..8417815cf 100644
--- a/src/libs/7zip/win/CPP/Common/MyString.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyString.h
diff --git a/src/libs/7zip/win/CPP/Common/MyTypes.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyTypes.h
index d81788816..d81788816 100644
--- a/src/libs/7zip/win/CPP/Common/MyTypes.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyTypes.h
diff --git a/src/libs/7zip/win/CPP/Common/MyUnknown.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyUnknown.h
index 8b95afd38..8b95afd38 100644
--- a/src/libs/7zip/win/CPP/Common/MyUnknown.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyUnknown.h
diff --git a/src/libs/7zip/win/CPP/Common/MyVector.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyVector.h
index 7e61dec31..7e61dec31 100644
--- a/src/libs/7zip/win/CPP/Common/MyVector.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyVector.h
diff --git a/src/libs/7zip/win/CPP/Common/MyWindows.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/MyWindows.cpp
index 38c93fdb2..38c93fdb2 100644
--- a/src/libs/7zip/win/CPP/Common/MyWindows.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyWindows.cpp
diff --git a/src/libs/7zip/win/CPP/Common/MyWindows.h b/src/libs/3rdparty/7zip/win/CPP/Common/MyWindows.h
index 139a4e8b9..139a4e8b9 100644
--- a/src/libs/7zip/win/CPP/Common/MyWindows.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/MyWindows.h
diff --git a/src/libs/7zip/win/CPP/Common/NewHandler.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/NewHandler.cpp
index 9072376df..9072376df 100644
--- a/src/libs/7zip/win/CPP/Common/NewHandler.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/NewHandler.cpp
diff --git a/src/libs/7zip/win/CPP/Common/NewHandler.h b/src/libs/3rdparty/7zip/win/CPP/Common/NewHandler.h
index e3e7422c8..e3e7422c8 100644
--- a/src/libs/7zip/win/CPP/Common/NewHandler.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/NewHandler.h
diff --git a/src/libs/7zip/win/CPP/Common/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/Common/StdAfx.h
index 420f5c326..420f5c326 100644
--- a/src/libs/7zip/win/CPP/Common/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/Common/StdOutStream.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/StdOutStream.cpp
index 6aed31a31..6aed31a31 100644
--- a/src/libs/7zip/win/CPP/Common/StdOutStream.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StdOutStream.cpp
diff --git a/src/libs/7zip/win/CPP/Common/StdOutStream.h b/src/libs/3rdparty/7zip/win/CPP/Common/StdOutStream.h
index 0a8c0febb..0a8c0febb 100644
--- a/src/libs/7zip/win/CPP/Common/StdOutStream.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StdOutStream.h
diff --git a/src/libs/7zip/win/CPP/Common/StringConvert.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/StringConvert.cpp
index 0443a06ca..0443a06ca 100644
--- a/src/libs/7zip/win/CPP/Common/StringConvert.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StringConvert.cpp
diff --git a/src/libs/7zip/win/CPP/Common/StringConvert.h b/src/libs/3rdparty/7zip/win/CPP/Common/StringConvert.h
index 8eea72ef2..8eea72ef2 100644
--- a/src/libs/7zip/win/CPP/Common/StringConvert.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StringConvert.h
diff --git a/src/libs/7zip/win/CPP/Common/StringToInt.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/StringToInt.cpp
index 2023fcc2c..2023fcc2c 100644
--- a/src/libs/7zip/win/CPP/Common/StringToInt.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StringToInt.cpp
diff --git a/src/libs/7zip/win/CPP/Common/StringToInt.h b/src/libs/3rdparty/7zip/win/CPP/Common/StringToInt.h
index 5c5d7d7fe..5c5d7d7fe 100644
--- a/src/libs/7zip/win/CPP/Common/StringToInt.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/StringToInt.h
diff --git a/src/libs/7zip/win/CPP/Common/UTFConvert.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/UTFConvert.cpp
index 38bac3331..38bac3331 100644
--- a/src/libs/7zip/win/CPP/Common/UTFConvert.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/UTFConvert.cpp
diff --git a/src/libs/7zip/win/CPP/Common/UTFConvert.h b/src/libs/3rdparty/7zip/win/CPP/Common/UTFConvert.h
index 16b02fe45..16b02fe45 100644
--- a/src/libs/7zip/win/CPP/Common/UTFConvert.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/UTFConvert.h
diff --git a/src/libs/7zip/win/CPP/Common/Wildcard.cpp b/src/libs/3rdparty/7zip/win/CPP/Common/Wildcard.cpp
index e88a1cf1c..e88a1cf1c 100644
--- a/src/libs/7zip/win/CPP/Common/Wildcard.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/Wildcard.cpp
diff --git a/src/libs/7zip/win/CPP/Common/Wildcard.h b/src/libs/3rdparty/7zip/win/CPP/Common/Wildcard.h
index 137d71ced..137d71ced 100644
--- a/src/libs/7zip/win/CPP/Common/Wildcard.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Common/Wildcard.h
diff --git a/src/libs/7zip/win/CPP/Windows/DLL.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/DLL.cpp
index cf3dd1ceb..cf3dd1ceb 100644
--- a/src/libs/7zip/win/CPP/Windows/DLL.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/DLL.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/DLL.h b/src/libs/3rdparty/7zip/win/CPP/Windows/DLL.h
index d8848ce95..d8848ce95 100644
--- a/src/libs/7zip/win/CPP/Windows/DLL.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/DLL.h
diff --git a/src/libs/7zip/win/CPP/Windows/Defs.h b/src/libs/3rdparty/7zip/win/CPP/Windows/Defs.h
index 281c40c33..281c40c33 100644
--- a/src/libs/7zip/win/CPP/Windows/Defs.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/Defs.h
diff --git a/src/libs/7zip/win/CPP/Windows/FileDir.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/FileDir.cpp
index 097f81efb..097f81efb 100644
--- a/src/libs/7zip/win/CPP/Windows/FileDir.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileDir.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/FileDir.h b/src/libs/3rdparty/7zip/win/CPP/Windows/FileDir.h
index 02d3e5a57..02d3e5a57 100644
--- a/src/libs/7zip/win/CPP/Windows/FileDir.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileDir.h
diff --git a/src/libs/7zip/win/CPP/Windows/FileFind.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/FileFind.cpp
index 7f58288fe..7f58288fe 100644
--- a/src/libs/7zip/win/CPP/Windows/FileFind.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileFind.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/FileFind.h b/src/libs/3rdparty/7zip/win/CPP/Windows/FileFind.h
index aaa7499bd..aaa7499bd 100644
--- a/src/libs/7zip/win/CPP/Windows/FileFind.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileFind.h
diff --git a/src/libs/7zip/win/CPP/Windows/FileIO.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/FileIO.cpp
index fec859ed3..fec859ed3 100644
--- a/src/libs/7zip/win/CPP/Windows/FileIO.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileIO.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/FileIO.h b/src/libs/3rdparty/7zip/win/CPP/Windows/FileIO.h
index f595121ef..f595121ef 100644
--- a/src/libs/7zip/win/CPP/Windows/FileIO.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileIO.h
diff --git a/src/libs/7zip/win/CPP/Windows/FileLink.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/FileLink.cpp
index dc524700d..dc524700d 100644
--- a/src/libs/7zip/win/CPP/Windows/FileLink.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileLink.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/FileMapping.h b/src/libs/3rdparty/7zip/win/CPP/Windows/FileMapping.h
index f90c429f1..f90c429f1 100644
--- a/src/libs/7zip/win/CPP/Windows/FileMapping.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileMapping.h
diff --git a/src/libs/7zip/win/CPP/Windows/FileName.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/FileName.cpp
index 0a6aee100..0a6aee100 100644
--- a/src/libs/7zip/win/CPP/Windows/FileName.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileName.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/FileName.h b/src/libs/3rdparty/7zip/win/CPP/Windows/FileName.h
index 04fc79b2d..04fc79b2d 100644
--- a/src/libs/7zip/win/CPP/Windows/FileName.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/FileName.h
diff --git a/src/libs/7zip/win/CPP/Windows/Handle.h b/src/libs/3rdparty/7zip/win/CPP/Windows/Handle.h
index bb7cb705d..bb7cb705d 100644
--- a/src/libs/7zip/win/CPP/Windows/Handle.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/Handle.h
diff --git a/src/libs/7zip/win/CPP/Windows/PropVariant.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariant.cpp
index d16f576cc..d16f576cc 100644
--- a/src/libs/7zip/win/CPP/Windows/PropVariant.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariant.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/PropVariant.h b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariant.h
index d71b85e1d..d71b85e1d 100644
--- a/src/libs/7zip/win/CPP/Windows/PropVariant.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariant.h
diff --git a/src/libs/7zip/win/CPP/Windows/PropVariantConv.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariantConv.cpp
index dfb93d6d6..dfb93d6d6 100644
--- a/src/libs/7zip/win/CPP/Windows/PropVariantConv.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariantConv.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/PropVariantConv.h b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariantConv.h
index 5d26357f0..5d26357f0 100644
--- a/src/libs/7zip/win/CPP/Windows/PropVariantConv.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/PropVariantConv.h
diff --git a/src/libs/7zip/win/CPP/Windows/SecurityUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/SecurityUtils.cpp
index f997738ce..f997738ce 100644
--- a/src/libs/7zip/win/CPP/Windows/SecurityUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/SecurityUtils.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/SecurityUtils.h b/src/libs/3rdparty/7zip/win/CPP/Windows/SecurityUtils.h
index 715de2505..715de2505 100644
--- a/src/libs/7zip/win/CPP/Windows/SecurityUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/SecurityUtils.h
diff --git a/src/libs/7zip/win/CPP/Windows/StdAfx.h b/src/libs/3rdparty/7zip/win/CPP/Windows/StdAfx.h
index 1766dfa86..1766dfa86 100644
--- a/src/libs/7zip/win/CPP/Windows/StdAfx.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/StdAfx.h
diff --git a/src/libs/7zip/win/CPP/Windows/Synchronization.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/Synchronization.cpp
index 5f86d1eb1..5f86d1eb1 100644
--- a/src/libs/7zip/win/CPP/Windows/Synchronization.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/Synchronization.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/Synchronization.h b/src/libs/3rdparty/7zip/win/CPP/Windows/Synchronization.h
index dc695f6f3..dc695f6f3 100644
--- a/src/libs/7zip/win/CPP/Windows/Synchronization.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/Synchronization.h
diff --git a/src/libs/7zip/win/CPP/Windows/System.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/System.cpp
index 4bc8d2a3f..4bc8d2a3f 100644
--- a/src/libs/7zip/win/CPP/Windows/System.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/System.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/System.h b/src/libs/3rdparty/7zip/win/CPP/Windows/System.h
index 4133a7b30..4133a7b30 100644
--- a/src/libs/7zip/win/CPP/Windows/System.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/System.h
diff --git a/src/libs/7zip/win/CPP/Windows/Thread.h b/src/libs/3rdparty/7zip/win/CPP/Windows/Thread.h
index f9ecaed85..f9ecaed85 100644
--- a/src/libs/7zip/win/CPP/Windows/Thread.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/Thread.h
diff --git a/src/libs/7zip/win/CPP/Windows/TimeUtils.cpp b/src/libs/3rdparty/7zip/win/CPP/Windows/TimeUtils.cpp
index 7ef44d9cc..7ef44d9cc 100644
--- a/src/libs/7zip/win/CPP/Windows/TimeUtils.cpp
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/TimeUtils.cpp
diff --git a/src/libs/7zip/win/CPP/Windows/TimeUtils.h b/src/libs/3rdparty/7zip/win/CPP/Windows/TimeUtils.h
index 2967214e6..2967214e6 100644
--- a/src/libs/7zip/win/CPP/Windows/TimeUtils.h
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/TimeUtils.h
diff --git a/src/libs/7zip/win/CPP/Windows/Windows.pri b/src/libs/3rdparty/7zip/win/CPP/Windows/Windows.pri
index a69279060..a69279060 100644
--- a/src/libs/7zip/win/CPP/Windows/Windows.pri
+++ b/src/libs/3rdparty/7zip/win/CPP/Windows/Windows.pri
diff --git a/src/libs/7zip/win/win.pri b/src/libs/3rdparty/7zip/win/win.pri
index d29553cbd..d29553cbd 100644
--- a/src/libs/7zip/win/win.pri
+++ b/src/libs/3rdparty/7zip/win/win.pri
diff --git a/src/libs/3rdparty/libarchive/COPYING b/src/libs/3rdparty/libarchive/COPYING
new file mode 100644
index 000000000..1b9723574
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive.h b/src/libs/3rdparty/libarchive/archive.h
new file mode 100644
index 000000000..618b1a176
--- /dev/null
+++ b/src/libs/3rdparty/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 3007001
+
+#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__)
+#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.1"
+#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/src/libs/3rdparty/libarchive/archive_acl.c b/src/libs/3rdparty/libarchive/archive_acl.c
new file mode 100644
index 000000000..ead7e36e4
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_acl_private.h b/src/libs/3rdparty/libarchive/archive_acl_private.h
new file mode 100644
index 000000000..af108162c
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_blake2.h b/src/libs/3rdparty/libarchive/archive_blake2.h
new file mode 100644
index 000000000..8f6b5e922
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_blake2.h
@@ -0,0 +1,197 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
+ terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+ your option. The terms of these licenses can be found at:
+
+ - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+ - OpenSSL license : https://www.openssl.org/source/license.html
+ - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
+
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+
+#ifndef ARCHIVE_BLAKE2_H
+#define ARCHIVE_BLAKE2_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_MSC_VER)
+#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#elif defined(__GNUC__)
+#define BLAKE2_PACKED(x) x __attribute__((packed))
+#else
+#define BLAKE2_PACKED(x) _Pragma("pack 1") x _Pragma("pack 0")
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#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
+ };
+
+ typedef struct blake2s_state__
+ {
+ uint32_t h[8];
+ uint32_t t[2];
+ uint32_t f[2];
+ uint8_t buf[BLAKE2S_BLOCKBYTES];
+ size_t buflen;
+ size_t outlen;
+ uint8_t last_node;
+ } blake2s_state;
+
+ typedef struct blake2b_state__
+ {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[BLAKE2B_BLOCKBYTES];
+ size_t buflen;
+ size_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];
+ size_t buflen;
+ size_t outlen;
+ } blake2sp_state;
+
+ typedef struct blake2bp_state__
+ {
+ blake2b_state S[4][1];
+ blake2b_state R[1];
+ uint8_t buf[4 * BLAKE2B_BLOCKBYTES];
+ size_t buflen;
+ size_t outlen;
+ } blake2bp_state;
+
+ BLAKE2_PACKED(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 */
+ uint32_t node_offset; /* 12 */
+ uint16_t xof_length; /* 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 */
+ });
+
+ typedef struct blake2s_param__ blake2s_param;
+
+ BLAKE2_PACKED(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 */
+ uint32_t node_offset; /* 12 */
+ uint32_t xof_length; /* 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 */
+ });
+
+ typedef struct blake2b_param__ blake2b_param;
+
+ typedef struct blake2xs_state__
+ {
+ blake2s_state S[1];
+ blake2s_param P[1];
+ } blake2xs_state;
+
+ typedef struct blake2xb_state__
+ {
+ blake2b_state S[1];
+ blake2b_param P[1];
+ } blake2xb_state;
+
+ /* Padded structs result in a compile-time error */
+ enum {
+ BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),
+ BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)
+ };
+
+ /* Streaming API */
+ int blake2s_init( blake2s_state *S, size_t outlen );
+ int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+ int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
+ int blake2s_final( blake2s_state *S, void *out, size_t outlen );
+
+ int blake2b_init( blake2b_state *S, size_t outlen );
+ int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+ int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
+ int blake2b_final( blake2b_state *S, void *out, size_t outlen );
+
+ int blake2sp_init( blake2sp_state *S, size_t outlen );
+ int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
+ int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
+
+ int blake2bp_init( blake2bp_state *S, size_t outlen );
+ int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
+ int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
+ int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
+
+ /* Variable output length API */
+ int blake2xs_init( blake2xs_state *S, const size_t outlen );
+ int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
+ int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
+ int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
+
+ int blake2xb_init( blake2xb_state *S, const size_t outlen );
+ int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
+ int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
+ int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
+
+ /* Simple API */
+ int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+ int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+ int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+ int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+ int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+ int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+ /* This is simply an alias for blake2b */
+ int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/libs/3rdparty/libarchive/archive_blake2_impl.h b/src/libs/3rdparty/libarchive/archive_blake2_impl.h
new file mode 100644
index 000000000..eb8619ca7
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_blake2_impl.h
@@ -0,0 +1,161 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
+ terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+ your option. The terms of these licenses can be found at:
+
+ - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+ - OpenSSL license : https://www.openssl.org/source/license.html
+ - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
+
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+
+#ifndef ARCHIVE_BLAKE2_IMPL_H
+#define ARCHIVE_BLAKE2_IMPL_H
+
+#include <stdint.h>
+#include <string.h>
+
+#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
+ #if defined(_MSC_VER)
+ #define BLAKE2_INLINE __inline
+ #elif defined(__GNUC__)
+ #define BLAKE2_INLINE __inline__
+ #else
+ #define BLAKE2_INLINE
+ #endif
+#else
+ #define BLAKE2_INLINE inline
+#endif
+
+static BLAKE2_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 = ( const uint8_t * )src;
+ return (( uint32_t )( p[0] ) << 0) |
+ (( uint32_t )( p[1] ) << 8) |
+ (( uint32_t )( p[2] ) << 16) |
+ (( uint32_t )( p[3] ) << 24) ;
+#endif
+}
+
+static BLAKE2_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 = ( const uint8_t * )src;
+ return (( uint64_t )( p[0] ) << 0) |
+ (( uint64_t )( p[1] ) << 8) |
+ (( uint64_t )( p[2] ) << 16) |
+ (( uint64_t )( p[3] ) << 24) |
+ (( uint64_t )( p[4] ) << 32) |
+ (( uint64_t )( p[5] ) << 40) |
+ (( uint64_t )( p[6] ) << 48) |
+ (( uint64_t )( p[7] ) << 56) ;
+#endif
+}
+
+static BLAKE2_INLINE uint16_t load16( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint16_t w;
+ memcpy(&w, src, sizeof w);
+ return w;
+#else
+ const uint8_t *p = ( const uint8_t * )src;
+ return ( uint16_t )((( uint32_t )( p[0] ) << 0) |
+ (( uint32_t )( p[1] ) << 8));
+#endif
+}
+
+static BLAKE2_INLINE void store16( void *dst, uint16_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;
+#endif
+}
+
+static BLAKE2_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[0] = (uint8_t)(w >> 0);
+ p[1] = (uint8_t)(w >> 8);
+ p[2] = (uint8_t)(w >> 16);
+ p[3] = (uint8_t)(w >> 24);
+#endif
+}
+
+static BLAKE2_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[0] = (uint8_t)(w >> 0);
+ p[1] = (uint8_t)(w >> 8);
+ p[2] = (uint8_t)(w >> 16);
+ p[3] = (uint8_t)(w >> 24);
+ p[4] = (uint8_t)(w >> 32);
+ p[5] = (uint8_t)(w >> 40);
+ p[6] = (uint8_t)(w >> 48);
+ p[7] = (uint8_t)(w >> 56);
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load48( const void *src )
+{
+ const uint8_t *p = ( const uint8_t * )src;
+ return (( uint64_t )( p[0] ) << 0) |
+ (( uint64_t )( p[1] ) << 8) |
+ (( uint64_t )( p[2] ) << 16) |
+ (( uint64_t )( p[3] ) << 24) |
+ (( uint64_t )( p[4] ) << 32) |
+ (( uint64_t )( p[5] ) << 40) ;
+}
+
+static BLAKE2_INLINE void store48( void *dst, uint64_t w )
+{
+ uint8_t *p = ( uint8_t * )dst;
+ p[0] = (uint8_t)(w >> 0);
+ p[1] = (uint8_t)(w >> 8);
+ p[2] = (uint8_t)(w >> 16);
+ p[3] = (uint8_t)(w >> 24);
+ p[4] = (uint8_t)(w >> 32);
+ p[5] = (uint8_t)(w >> 40);
+}
+
+static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )
+{
+ return ( w >> c ) | ( w << ( 64 - c ) );
+}
+
+/* prevents compiler optimizing out memset() */
+static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)
+{
+ static void *(__LA_LIBC_CC *const volatile memset_v)(void *, int, size_t) = &memset;
+ memset_v(v, 0, n);
+}
+
+#endif
diff --git a/src/libs/3rdparty/libarchive/archive_blake2s_ref.c b/src/libs/3rdparty/libarchive/archive_blake2s_ref.c
new file mode 100644
index 000000000..93d328118
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_blake2s_ref.c
@@ -0,0 +1,369 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
+ terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+ your option. The terms of these licenses can be found at:
+
+ - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+ - OpenSSL license : https://www.openssl.org/source/license.html
+ - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
+
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+
+#include "archive_platform.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "archive_blake2.h"
+#include "archive_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 void blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = (uint32_t)-1;
+}
+
+/* Some helper functions, not necessarily useful */
+static int blake2s_is_lastblock( const blake2s_state *S )
+{
+ return S->f[0] != 0;
+}
+
+static void blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = (uint32_t)-1;
+}
+
+static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+}
+
+static void blake2s_init0( blake2s_state *S )
+{
+ size_t i;
+ memset( S, 0, sizeof( blake2s_state ) );
+
+ for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+}
+
+/* init2 xors IV with input parameter block */
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+ const unsigned char *p = ( const unsigned char * )( P );
+ size_t i;
+
+ blake2s_init0( S );
+
+ /* IV XOR ParamBlock */
+ for( i = 0; i < 8; ++i )
+ S->h[i] ^= load32( &p[i * 4] );
+
+ 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 );
+ store32( &P->node_offset, 0 );
+ store16( &P->xof_length, 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 );
+ store32( &P->node_offset, 0 );
+ store16( &P->xof_length, 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;
+}
+
+#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)
+
+static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] )
+{
+ uint32_t m[16];
+ uint32_t v[16];
+ size_t i;
+
+ for( i = 0; i < 16; ++i ) {
+ m[i] = load32( in + i * sizeof( m[i] ) );
+ }
+
+ for( 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];
+
+ ROUND( 0 );
+ ROUND( 1 );
+ ROUND( 2 );
+ ROUND( 3 );
+ ROUND( 4 );
+ ROUND( 5 );
+ ROUND( 6 );
+ ROUND( 7 );
+ ROUND( 8 );
+ ROUND( 9 );
+
+ for( i = 0; i < 8; ++i ) {
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+ }
+}
+
+#undef G
+#undef ROUND
+
+int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
+{
+ const unsigned char * in = (const unsigned char *)pin;
+ if( inlen > 0 )
+ {
+ size_t left = S->buflen;
+ size_t fill = BLAKE2S_BLOCKBYTES - left;
+ if( inlen > fill )
+ {
+ S->buflen = 0;
+ memcpy( S->buf + left, in, fill ); /* Fill buffer */
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf ); /* Compress */
+ in += fill; inlen -= fill;
+ while(inlen > BLAKE2S_BLOCKBYTES) {
+ blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES);
+ blake2s_compress( S, in );
+ in += BLAKE2S_BLOCKBYTES;
+ inlen -= BLAKE2S_BLOCKBYTES;
+ }
+ }
+ memcpy( S->buf + S->buflen, in, inlen );
+ S->buflen += inlen;
+ }
+ return 0;
+}
+
+int blake2s_final( blake2s_state *S, void *out, size_t outlen )
+{
+ uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
+ size_t i;
+
+ if( out == NULL || outlen < S->outlen )
+ return -1;
+
+ if( blake2s_is_lastblock( S ) )
+ return -1;
+
+ blake2s_increment_counter( S, ( uint32_t )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 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 );
+ secure_zero_memory(buffer, sizeof(buffer));
+ return 0;
+}
+
+int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, 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;
+ }
+
+ blake2s_update( S, ( const uint8_t * )in, inlen );
+ blake2s_final( S, out, outlen );
+ return 0;
+}
+
+#if defined(SUPERCOP)
+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
+{
+ return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );
+}
+#endif
+
+#if defined(BLAKE2S_SELFTEST)
+#include <string.h>
+#include "blake2-kat.h"
+int main( void )
+{
+ uint8_t key[BLAKE2S_KEYBYTES];
+ uint8_t buf[BLAKE2_KAT_LENGTH];
+ size_t i, step;
+
+ for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
+ key[i] = ( uint8_t )i;
+
+ for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+ buf[i] = ( uint8_t )i;
+
+ /* Test simple API */
+ for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+ {
+ uint8_t hash[BLAKE2S_OUTBYTES];
+ blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+
+ if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) )
+ {
+ goto fail;
+ }
+ }
+
+ /* Test streaming API */
+ for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
+ for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
+ uint8_t hash[BLAKE2S_OUTBYTES];
+ blake2s_state S;
+ uint8_t * p = buf;
+ size_t mlen = i;
+ int err = 0;
+
+ if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+ goto fail;
+ }
+
+ while (mlen >= step) {
+ if ( (err = blake2s_update(&S, p, step)) < 0 ) {
+ goto fail;
+ }
+ mlen -= step;
+ p += step;
+ }
+ if ( (err = blake2s_update(&S, p, mlen)) < 0) {
+ goto fail;
+ }
+ if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+ goto fail;
+ }
+
+ if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) {
+ goto fail;
+ }
+ }
+ }
+
+ puts( "ok" );
+ return 0;
+fail:
+ puts("error");
+ return -1;
+}
+#endif
diff --git a/src/libs/3rdparty/libarchive/archive_blake2sp_ref.c b/src/libs/3rdparty/libarchive/archive_blake2sp_ref.c
new file mode 100644
index 000000000..b913a4db6
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_blake2sp_ref.c
@@ -0,0 +1,361 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
+ terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+ your option. The terms of these licenses can be found at:
+
+ - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+ - OpenSSL license : https://www.openssl.org/source/license.html
+ - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
+
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+
+#include "archive_platform.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "archive_blake2.h"
+#include "archive_blake2_impl.h"
+
+#define PARALLELISM_DEGREE 8
+
+/*
+ blake2sp_init_param defaults to setting the expecting output length
+ from the digest_length parameter block field.
+
+ In some cases, however, we do not want this, as the output length
+ of these instances is given by inner_length instead.
+*/
+static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )
+{
+ int err = blake2s_init_param(S, P);
+ S->outlen = P->inner_length;
+ return err;
+}
+
+static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint32_t offset )
+{
+ blake2s_param P[1];
+ P->digest_length = (uint8_t)outlen;
+ P->key_length = (uint8_t)keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ store32( &P->leaf_length, 0 );
+ store32( &P->node_offset, offset );
+ store16( &P->xof_length, 0 );
+ P->node_depth = 0;
+ P->inner_length = BLAKE2S_OUTBYTES;
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ return blake2sp_init_leaf_param( S, P );
+}
+
+static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen )
+{
+ blake2s_param P[1];
+ P->digest_length = (uint8_t)outlen;
+ P->key_length = (uint8_t)keylen;
+ P->fanout = PARALLELISM_DEGREE;
+ P->depth = 2;
+ store32( &P->leaf_length, 0 );
+ store32( &P->node_offset, 0 );
+ store16( &P->xof_length, 0 );
+ P->node_depth = 1;
+ P->inner_length = BLAKE2S_OUTBYTES;
+ memset( P->salt, 0, sizeof( P->salt ) );
+ memset( P->personal, 0, sizeof( P->personal ) );
+ return blake2s_init_param( S, P );
+}
+
+
+int blake2sp_init( blake2sp_state *S, size_t outlen )
+{
+ size_t i;
+
+ if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+ S->outlen = outlen;
+
+ if( blake2sp_init_root( S->R, outlen, 0 ) < 0 )
+ return -1;
+
+ for( i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S->S[i], outlen, 0, (uint32_t)i ) < 0 ) return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ return 0;
+}
+
+int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
+{
+ size_t i;
+
+ 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;
+ S->outlen = outlen;
+
+ if( blake2sp_init_root( S->R, outlen, keylen ) < 0 )
+ return -1;
+
+ for( i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S->S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;
+
+ S->R->last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES];
+ memset( block, 0, BLAKE2S_BLOCKBYTES );
+ memcpy( block, key, keylen );
+
+ for( 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 void *pin, size_t inlen )
+{
+ const unsigned char * in = (const unsigned char *)pin;
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+ size_t i;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( 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)
+ #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
+#else
+ for( i = 0; i < PARALLELISM_DEGREE; ++i )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t i = omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const unsigned char *in__ = ( const unsigned char * )in;
+ in__ += i * BLAKE2S_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_update( S->S[i], 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 = left + inlen;
+ return 0;
+}
+
+
+int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+ size_t i;
+
+ if(out == NULL || outlen < S->outlen) {
+ return -1;
+ }
+
+ for( 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( i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+
+ return blake2s_final( S->R, out, S->outlen );
+}
+
+
+int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+{
+ uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+ blake2s_state S[PARALLELISM_DEGREE][1];
+ blake2s_state FS[1];
+ size_t i;
+
+ /* 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( i = 0; i < PARALLELISM_DEGREE; ++i )
+ if( blake2sp_init_leaf( S[i], outlen, keylen, (uint32_t)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( 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)
+ #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
+#else
+
+ for( i = 0; i < PARALLELISM_DEGREE; ++i )
+#endif
+ {
+#if defined(_OPENMP)
+ size_t i = omp_get_thread_num();
+#endif
+ size_t inlen__ = inlen;
+ const unsigned char *in__ = ( const unsigned char * )in;
+ in__ += i * BLAKE2S_BLOCKBYTES;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+
+ if( inlen__ > i * BLAKE2S_BLOCKBYTES )
+ {
+ const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;
+ const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
+ blake2s_update( S[i], in__, len );
+ }
+
+ blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
+ }
+
+ if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
+ return -1;
+
+ FS->last_node = 1;
+
+ for( i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+
+ return blake2s_final( FS, out, outlen );
+}
+
+
+
+#if defined(BLAKE2SP_SELFTEST)
+#include <string.h>
+#include "blake2-kat.h"
+int main( void )
+{
+ uint8_t key[BLAKE2S_KEYBYTES];
+ uint8_t buf[BLAKE2_KAT_LENGTH];
+ size_t i, step;
+
+ for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
+ key[i] = ( uint8_t )i;
+
+ for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+ buf[i] = ( uint8_t )i;
+
+ /* Test simple API */
+ for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+ {
+ uint8_t hash[BLAKE2S_OUTBYTES];
+ blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+
+ if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )
+ {
+ goto fail;
+ }
+ }
+
+ /* Test streaming API */
+ for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
+ for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
+ uint8_t hash[BLAKE2S_OUTBYTES];
+ blake2sp_state S;
+ uint8_t * p = buf;
+ size_t mlen = i;
+ int err = 0;
+
+ if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+ goto fail;
+ }
+
+ while (mlen >= step) {
+ if ( (err = blake2sp_update(&S, p, step)) < 0 ) {
+ goto fail;
+ }
+ mlen -= step;
+ p += step;
+ }
+ if ( (err = blake2sp_update(&S, p, mlen)) < 0) {
+ goto fail;
+ }
+ if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+ goto fail;
+ }
+
+ if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) {
+ goto fail;
+ }
+ }
+ }
+
+ puts( "ok" );
+ return 0;
+fail:
+ puts("error");
+ return -1;
+}
+#endif
diff --git a/src/libs/3rdparty/libarchive/archive_check_magic.c b/src/libs/3rdparty/libarchive/archive_check_magic.c
new file mode 100644
index 000000000..1f40072f8
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_cmdline.c b/src/libs/3rdparty/libarchive/archive_cmdline.c
new file mode 100644
index 000000000..5c519cd17
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_cmdline_private.h b/src/libs/3rdparty/libarchive/archive_cmdline_private.h
new file mode 100644
index 000000000..57a19494f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_crc32.h b/src/libs/3rdparty/libarchive/archive_crc32.h
new file mode 100644
index 000000000..4f1aed305
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_crc32.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2009 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/archive_crc32.h 201102 2009-12-28 03:11:36Z kientzle $
+ */
+
+#ifndef ARCHIVE_CRC32_H
+#define ARCHIVE_CRC32_H
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/*
+ * When zlib is unavailable, we should still be able to validate
+ * uncompressed zip archives. That requires us to be able to compute
+ * the CRC32 check value. This is a drop-in compatible replacement
+ * for crc32() from zlib. It's slower than the zlib implementation,
+ * but still pretty fast: This runs about 300MB/s on my 3GHz P4
+ * compared to about 800MB/s for the zlib implementation.
+ */
+static unsigned long
+crc32(unsigned long crc, const void *_p, size_t len)
+{
+ unsigned long crc2, b, i;
+ const unsigned char *p = _p;
+ static volatile int crc_tbl_inited = 0;
+ static unsigned long crc_tbl[256];
+
+ if (!crc_tbl_inited) {
+ for (b = 0; b < 256; ++b) {
+ crc2 = b;
+ for (i = 8; i > 0; --i) {
+ if (crc2 & 1)
+ crc2 = (crc2 >> 1) ^ 0xedb88320UL;
+ else
+ crc2 = (crc2 >> 1);
+ }
+ crc_tbl[b] = crc2;
+ }
+ crc_tbl_inited = 1;
+ }
+
+ crc = crc ^ 0xffffffffUL;
+ /* A use of this loop is about 20% - 30% faster than
+ * no use version in any optimization option of gcc. */
+ for (;len >= 8; len -= 8) {
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ }
+ while (len--)
+ crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ return (crc ^ 0xffffffffUL);
+}
+
+#endif
diff --git a/src/libs/3rdparty/libarchive/archive_cryptor.c b/src/libs/3rdparty/libarchive/archive_cryptor.c
new file mode 100644
index 000000000..112baf161
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_cryptor_private.h b/src/libs/3rdparty/libarchive/archive_cryptor_private.h
new file mode 100644
index 000000000..16b6d16ff
--- /dev/null
+++ b/src/libs/3rdparty/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)
+#include <mbedtls/aes.h>
+#include <mbedtls/md.h>
+#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)
+#include <nettle/pbkdf2.h>
+#endif
+#include <nettle/aes.h>
+#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/src/libs/3rdparty/libarchive/archive_digest.c b/src/libs/3rdparty/libarchive/archive_digest.c
new file mode 100644
index 000000000..08a9aeb02
--- /dev/null
+++ b/src/libs/3rdparty/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,
+ 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/src/libs/3rdparty/libarchive/archive_digest_private.h b/src/libs/3rdparty/libarchive/archive_digest_private.h
new file mode 100644
index 000000000..339b4edca
--- /dev/null
+++ b/src/libs/3rdparty/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)
+#include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+#include <rmd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+#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)
+#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)
+#include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+#include <ripemd.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+#include <sha.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+#include <sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#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)
+#include <mbedtls/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+#include <mbedtls/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+#include <mbedtls/sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+#include <mbedtls/sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+ defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+#include <mbedtls/sha512.h>
+#endif
+
+/* Nettle crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+#include <nettle/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+#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)
+#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/src/libs/3rdparty/libarchive/archive_disk_acl_darwin.c b/src/libs/3rdparty/libarchive/archive_disk_acl_darwin.c
new file mode 100644
index 000000000..48ad01651
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_disk_acl_darwin.c
@@ -0,0 +1,559 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+#if ARCHIVE_ACL_DARWIN
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_MEMBERSHIP_H
+#include <membership.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+ const int a_perm; /* Libarchive permission or flag */
+ const int p_perm; /* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+ {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+ {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+ {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+ {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+ {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+ {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+ {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},
+ {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},
+ {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},
+#if HAVE_DECL_ACL_SYNCHRONIZE
+ {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+#endif
+};
+
+static const int acl_nfs4_perm_map_size =
+ (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},
+ {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+ {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+ {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}
+};
+
+static const int acl_nfs4_flag_map_size =
+ (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+
+static int translate_guid(struct archive *a, acl_entry_t acl_entry,
+ int *ae_id, int *ae_tag, const char **ae_name)
+{
+ void *q;
+ uid_t ugid;
+ int r, idtype;
+
+ q = acl_get_qualifier(acl_entry);
+ if (q == NULL)
+ return (1);
+ r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);
+ if (r != 0) {
+ acl_free(q);
+ return (1);
+ }
+ if (idtype == ID_TYPE_UID) {
+ *ae_tag = ARCHIVE_ENTRY_ACL_USER;
+ *ae_id = ugid;
+ *ae_name = archive_read_disk_uname(a, *ae_id);
+ } else if (idtype == ID_TYPE_GID) {
+ *ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+ *ae_id = ugid;
+ *ae_name = archive_read_disk_gname(a, *ae_id);
+ } else
+ r = 1;
+
+ acl_free(q);
+ return (r);
+}
+
+static void
+add_trivial_nfs4_acl(struct archive_entry *entry)
+{
+ mode_t mode;
+ int i;
+ const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;
+ const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |
+ ARCHIVE_ENTRY_ACL_APPEND_DATA;
+ const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;
+ const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+ ARCHIVE_ENTRY_ACL_READ_ACL |
+ ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+ const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+ ARCHIVE_ENTRY_ACL_WRITE_ACL |
+ ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+
+ struct {
+ const int type;
+ const int tag;
+ int permset;
+ } tacl_entry[] = {
+ {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+ {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+ {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},
+ {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},
+ {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},
+ {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}
+ };
+
+ mode = archive_entry_mode(entry);
+
+ /* Permissions for everyone@ */
+ if (mode & 0004)
+ tacl_entry[5].permset |= rperm;
+ if (mode & 0002)
+ tacl_entry[5].permset |= wperm;
+ if (mode & 0001)
+ tacl_entry[5].permset |= eperm;
+
+ /* Permissions for group@ */
+ if (mode & 0040)
+ tacl_entry[4].permset |= rperm;
+ else if (mode & 0004)
+ tacl_entry[2].permset |= rperm;
+ if (mode & 0020)
+ tacl_entry[4].permset |= wperm;
+ else if (mode & 0002)
+ tacl_entry[2].permset |= wperm;
+ if (mode & 0010)
+ tacl_entry[4].permset |= eperm;
+ else if (mode & 0001)
+ tacl_entry[2].permset |= eperm;
+
+ /* Permissions for owner@ */
+ if (mode & 0400) {
+ tacl_entry[3].permset |= rperm;
+ if (!(mode & 0040) && (mode & 0004))
+ tacl_entry[0].permset |= rperm;
+ } else if ((mode & 0040) || (mode & 0004))
+ tacl_entry[1].permset |= rperm;
+ if (mode & 0200) {
+ tacl_entry[3].permset |= wperm;
+ if (!(mode & 0020) && (mode & 0002))
+ tacl_entry[0].permset |= wperm;
+ } else if ((mode & 0020) || (mode & 0002))
+ tacl_entry[1].permset |= wperm;
+ if (mode & 0100) {
+ tacl_entry[3].permset |= eperm;
+ if (!(mode & 0010) && (mode & 0001))
+ tacl_entry[0].permset |= eperm;
+ } else if ((mode & 0010) || (mode & 0001))
+ tacl_entry[1].permset |= eperm;
+
+ for (i = 0; i < 6; i++) {
+ if (tacl_entry[i].permset != 0) {
+ archive_entry_acl_add_entry(entry,
+ tacl_entry[i].type, tacl_entry[i].permset,
+ tacl_entry[i].tag, -1, NULL);
+ }
+ }
+
+ return;
+}
+
+static int
+translate_acl(struct archive_read_disk *a,
+ struct archive_entry *entry, acl_t acl)
+{
+ acl_tag_t acl_tag;
+ acl_flagset_t acl_flagset;
+ acl_entry_t acl_entry;
+ acl_permset_t acl_permset;
+ int i, entry_acl_type;
+ int r, s, ae_id, ae_tag, ae_perm;
+ const char *ae_name;
+
+ s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get first ACL entry");
+ return (ARCHIVE_WARN);
+ }
+
+ while (s == 0) {
+ ae_id = -1;
+ ae_name = NULL;
+ ae_perm = 0;
+
+ if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get ACL tag type");
+ return (ARCHIVE_WARN);
+ }
+ switch (acl_tag) {
+ case ACL_EXTENDED_ALLOW:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ r = translate_guid(&a->archive, acl_entry,
+ &ae_id, &ae_tag, &ae_name);
+ break;
+ case ACL_EXTENDED_DENY:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ r = translate_guid(&a->archive, acl_entry,
+ &ae_id, &ae_tag, &ae_name);
+ break;
+ default:
+ /* Skip types that libarchive can't support. */
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ continue;
+ }
+
+ /* Skip if translate_guid() above failed */
+ if (r != 0) {
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ continue;
+ }
+
+ /*
+ * Libarchive stores "flag" (NFSv4 inheritance bits)
+ * in the ae_perm bitmap.
+ *
+ * acl_get_flagset_np() fails with non-NFSv4 ACLs
+ */
+ if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get flagset from a NFSv4 ACL entry");
+ return (ARCHIVE_WARN);
+ }
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ r = acl_get_flag_np(acl_flagset,
+ acl_nfs4_flag_map[i].p_perm);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to check flag in a NFSv4 "
+ "ACL flagset");
+ return (ARCHIVE_WARN);
+ } else if (r)
+ ae_perm |= acl_nfs4_flag_map[i].a_perm;
+ }
+
+ if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get ACL permission set");
+ return (ARCHIVE_WARN);
+ }
+
+ for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+ /*
+ * acl_get_perm() is spelled differently on different
+ * platforms; see above.
+ */
+ r = acl_get_perm_np(acl_permset,
+ acl_nfs4_perm_map[i].p_perm);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to check permission in an ACL "
+ "permission set");
+ return (ARCHIVE_WARN);
+ } else if (r)
+ ae_perm |= acl_nfs4_perm_map[i].a_perm;
+ }
+
+#if !HAVE_DECL_ACL_SYNCHRONIZE
+ /* On Mac OS X without ACL_SYNCHRONIZE assume it is set */
+ ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+#endif
+
+ archive_entry_acl_add_entry(entry, entry_acl_type,
+ ae_perm, ae_tag,
+ ae_id, ae_name);
+
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl,
+ int ae_requested_type, const char *tname)
+{
+ acl_t acl;
+ acl_entry_t acl_entry;
+ acl_permset_t acl_permset;
+ acl_flagset_t acl_flagset;
+ int ret;
+ int ae_type, ae_permset, ae_tag, ae_id;
+ uuid_t ae_uuid;
+ uid_t ae_uid;
+ gid_t ae_gid;
+ const char *ae_name;
+ int entries;
+ int i;
+
+ ret = ARCHIVE_OK;
+ entries = archive_acl_reset(abstract_acl, ae_requested_type);
+ if (entries == 0)
+ return (ARCHIVE_OK);
+
+ if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ errno = ENOENT;
+ archive_set_error(a, errno, "Unsupported ACL type");
+ return (ARCHIVE_FAILED);
+ }
+
+ acl = acl_init(entries);
+ if (acl == (acl_t)NULL) {
+ archive_set_error(a, errno,
+ "Failed to initialize ACL working storage");
+ return (ARCHIVE_FAILED);
+ }
+
+ while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+ &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+ /*
+ * Mac OS doesn't support NFSv4 ACLs for
+ * owner@, group@ and everyone@.
+ * We skip any of these ACLs found.
+ */
+ if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||
+ ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||
+ ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)
+ continue;
+
+ if (acl_create_entry(&acl, &acl_entry) != 0) {
+ archive_set_error(a, errno,
+ "Failed to create a new ACL entry");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ switch (ae_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);
+ break;
+ default:
+ /* We don't support any other types on MacOS */
+ continue;
+ }
+
+ switch (ae_tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+ if (mbr_uid_to_uuid(ae_uid, ae_uuid) != 0)
+ continue;
+ if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+ continue;
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+ if (mbr_gid_to_uuid(ae_gid, ae_uuid) != 0)
+ continue;
+ if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+ continue;
+ break;
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL tag");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to get ACL permission set");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ if (acl_clear_perms(acl_permset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to clear ACL permissions");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+ if (ae_permset & acl_nfs4_perm_map[i].a_perm) {
+ if (acl_add_perm(acl_permset,
+ acl_nfs4_perm_map[i].p_perm) != 0) {
+ archive_set_error(a, errno,
+ "Failed to add ACL permission");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ }
+ }
+
+ /*
+ * acl_get_flagset_np() fails with non-NFSv4 ACLs
+ */
+ if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to get flagset from an NFSv4 ACL entry");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ if (acl_clear_flags_np(acl_flagset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to clear flags from an NFSv4 ACL flagset");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+ if (acl_add_flag_np(acl_flagset,
+ acl_nfs4_flag_map[i].p_perm) != 0) {
+ archive_set_error(a, errno,
+ "Failed to add flag to "
+ "NFSv4 ACL flagset");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ }
+ }
+ }
+
+ if (fd >= 0) {
+ if (acl_set_fd_np(fd, acl, ACL_TYPE_EXTENDED) == 0)
+ ret = ARCHIVE_OK;
+ else {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno,
+ "Failed to set acl on fd: %s", tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+ } else if (acl_set_link_np(name, ACL_TYPE_EXTENDED, acl) != 0) {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno, "Failed to set acl: %s",
+ tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+exit_free:
+ acl_free(acl);
+ return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ const char *accpath;
+ acl_t acl;
+ int r;
+
+ accpath = NULL;
+
+ if (*fd < 0) {
+ accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (accpath == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_acl_clear(entry);
+
+ acl = NULL;
+
+ if (*fd >= 0)
+ acl = acl_get_fd_np(*fd, ACL_TYPE_EXTENDED);
+ else if (!a->follow_symlinks)
+ acl = acl_get_link_np(accpath, ACL_TYPE_EXTENDED);
+ else
+ acl = acl_get_file(accpath, ACL_TYPE_EXTENDED);
+
+ if (acl != NULL) {
+ r = translate_acl(a, entry, acl);
+ acl_free(acl);
+ acl = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate NFSv4 ACLs");
+ }
+
+ /*
+ * Because Mac OS doesn't support owner@, group@ and everyone@
+ * ACLs we need to add NFSv4 ACLs mirroring the file mode to
+ * the archive entry. Otherwise extraction on non-Mac platforms
+ * would lead to an invalid file mode.
+ */
+ if ((archive_entry_acl_types(entry) &
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+ add_trivial_nfs4_acl(entry);
+
+ return (r);
+ }
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+ int ret = ARCHIVE_OK;
+
+ (void)mode; /* UNUSED */
+
+ if ((archive_acl_types(abstract_acl) &
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ ret = set_acl(a, fd, name, abstract_acl,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+ }
+ return (ret);
+}
+#endif /* ARCHIVE_ACL_DARWIN */
diff --git a/src/libs/3rdparty/libarchive/archive_disk_acl_freebsd.c b/src/libs/3rdparty/libarchive/archive_disk_acl_freebsd.c
new file mode 100644
index 000000000..ed4e7a789
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_disk_acl_freebsd.c
@@ -0,0 +1,712 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+#if ARCHIVE_ACL_FREEBSD
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+ const int a_perm; /* Libarchive permission or flag */
+ const int p_perm; /* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_posix_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+ {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+ {ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+};
+
+static const int acl_posix_perm_map_size =
+ (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+ {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+ {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+ {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+ {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+ {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
+ {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
+ {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+ {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+ {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
+ {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
+ {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
+ {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+ (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+ {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+ {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+ {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},
+ {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},
+ {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},
+#ifdef ACL_ENTRY_INHERITED
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
+#endif
+};
+
+static const int acl_nfs4_flag_map_size =
+ (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */
+
+static int
+translate_acl(struct archive_read_disk *a,
+ struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
+{
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ int brand;
+ acl_flagset_t acl_flagset;
+ acl_entry_type_t acl_type;
+#endif
+ acl_tag_t acl_tag;
+ acl_entry_t acl_entry;
+ acl_permset_t acl_permset;
+ int i, entry_acl_type, perm_map_size;
+ const acl_perm_map_t *perm_map;
+ int r, s, ae_id, ae_tag, ae_perm;
+ void *q;
+ const char *ae_name;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ // FreeBSD "brands" ACLs as POSIX.1e or NFSv4
+ // Make sure the "brand" on this ACL is consistent
+ // with the default_entry_acl_type bits provided.
+ if (acl_get_brand_np(acl, &brand) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to read ACL brand");
+ return (ARCHIVE_WARN);
+ }
+ switch (brand) {
+ case ACL_BRAND_POSIX:
+ switch (default_entry_acl_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid ACL entry type for POSIX.1e ACL");
+ return (ARCHIVE_WARN);
+ }
+ break;
+ case ACL_BRAND_NFS4:
+ if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Invalid ACL entry type for NFSv4 ACL");
+ return (ARCHIVE_WARN);
+ }
+ break;
+ default:
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Unknown ACL brand");
+ return (ARCHIVE_WARN);
+ }
+#endif
+
+ s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get first ACL entry");
+ return (ARCHIVE_WARN);
+ }
+
+ while (s == 1) {
+ ae_id = -1;
+ ae_name = NULL;
+ ae_perm = 0;
+
+ if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get ACL tag type");
+ return (ARCHIVE_WARN);
+ }
+ switch (acl_tag) {
+ case ACL_USER:
+ q = acl_get_qualifier(acl_entry);
+ if (q != NULL) {
+ ae_id = (int)*(uid_t *)q;
+ acl_free(q);
+ ae_name = archive_read_disk_uname(&a->archive,
+ ae_id);
+ }
+ ae_tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case ACL_GROUP:
+ q = acl_get_qualifier(acl_entry);
+ if (q != NULL) {
+ ae_id = (int)*(gid_t *)q;
+ acl_free(q);
+ ae_name = archive_read_disk_gname(&a->archive,
+ ae_id);
+ }
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case ACL_MASK:
+ ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ case ACL_USER_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case ACL_GROUP_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case ACL_OTHER:
+ ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ case ACL_EVERYONE:
+ ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ break;
+#endif
+ default:
+ /* Skip types that libarchive can't support. */
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ continue;
+ }
+
+ // XXX acl_type maps to allow/deny/audit/YYYY bits
+ entry_acl_type = default_entry_acl_type;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /*
+ * acl_get_entry_type_np() fails with non-NFSv4 ACLs
+ */
+ if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) {
+ archive_set_error(&a->archive, errno, "Failed "
+ "to get ACL type from a NFSv4 ACL entry");
+ return (ARCHIVE_WARN);
+ }
+ switch (acl_type) {
+ case ACL_ENTRY_TYPE_ALLOW:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ break;
+ case ACL_ENTRY_TYPE_DENY:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ break;
+ case ACL_ENTRY_TYPE_AUDIT:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+ break;
+ case ACL_ENTRY_TYPE_ALARM:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+ break;
+ default:
+ archive_set_error(&a->archive, errno,
+ "Invalid NFSv4 ACL entry type");
+ return (ARCHIVE_WARN);
+ }
+
+ /*
+ * Libarchive stores "flag" (NFSv4 inheritance bits)
+ * in the ae_perm bitmap.
+ *
+ * acl_get_flagset_np() fails with non-NFSv4 ACLs
+ */
+ if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get flagset from a NFSv4 "
+ "ACL entry");
+ return (ARCHIVE_WARN);
+ }
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ r = acl_get_flag_np(acl_flagset,
+ acl_nfs4_flag_map[i].p_perm);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to check flag in a NFSv4 "
+ "ACL flagset");
+ return (ARCHIVE_WARN);
+ } else if (r)
+ ae_perm |= acl_nfs4_flag_map[i].a_perm;
+ }
+ }
+#endif
+
+ if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get ACL permission set");
+ return (ARCHIVE_WARN);
+ }
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ perm_map_size = acl_nfs4_perm_map_size;
+ perm_map = acl_nfs4_perm_map;
+ } else {
+#endif
+ perm_map_size = acl_posix_perm_map_size;
+ perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ }
+#endif
+
+ for (i = 0; i < perm_map_size; ++i) {
+ r = acl_get_perm_np(acl_permset, perm_map[i].p_perm);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to check permission in an ACL "
+ "permission set");
+ return (ARCHIVE_WARN);
+ } else if (r)
+ ae_perm |= perm_map[i].a_perm;
+ }
+
+ archive_entry_acl_add_entry(entry, entry_acl_type,
+ ae_perm, ae_tag,
+ ae_id, ae_name);
+
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get next ACL entry");
+ return (ARCHIVE_WARN);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode,
+ int ae_requested_type, const char *tname)
+{
+ int acl_type = 0;
+ acl_t acl;
+ acl_entry_t acl_entry;
+ acl_permset_t acl_permset;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ acl_flagset_t acl_flagset;
+ int r;
+#endif
+ int ret;
+ int ae_type, ae_permset, ae_tag, ae_id;
+ int perm_map_size;
+ const acl_perm_map_t *perm_map;
+ uid_t ae_uid;
+ gid_t ae_gid;
+ const char *ae_name;
+ int entries;
+ int i;
+
+ ret = ARCHIVE_OK;
+ entries = archive_acl_reset(abstract_acl, ae_requested_type);
+ if (entries == 0)
+ return (ARCHIVE_OK);
+
+
+ switch (ae_requested_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ acl_type = ACL_TYPE_ACCESS;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ acl_type = ACL_TYPE_DEFAULT;
+ break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ acl_type = ACL_TYPE_NFS4;
+ break;
+#endif
+ default:
+ errno = ENOENT;
+ archive_set_error(a, errno, "Unsupported ACL type");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) {
+ errno = EINVAL;
+ archive_set_error(a, errno,
+ "Cannot set default ACL on non-directory");
+ return (ARCHIVE_WARN);
+ }
+
+ acl = acl_init(entries);
+ if (acl == (acl_t)NULL) {
+ archive_set_error(a, errno,
+ "Failed to initialize ACL working storage");
+ return (ARCHIVE_FAILED);
+ }
+
+ while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+ &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+ if (acl_create_entry(&acl, &acl_entry) != 0) {
+ archive_set_error(a, errno,
+ "Failed to create a new ACL entry");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ switch (ae_tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+ acl_set_tag_type(acl_entry, ACL_USER);
+ acl_set_qualifier(acl_entry, &ae_uid);
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+ acl_set_tag_type(acl_entry, ACL_GROUP);
+ acl_set_qualifier(acl_entry, &ae_gid);
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ acl_set_tag_type(acl_entry, ACL_MASK);
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ acl_set_tag_type(acl_entry, ACL_OTHER);
+ break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ acl_set_tag_type(acl_entry, ACL_EVERYONE);
+ break;
+#endif
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL tag");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ r = 0;
+ switch (ae_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ r = acl_set_entry_type_np(acl_entry,
+ ACL_ENTRY_TYPE_ALLOW);
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ r = acl_set_entry_type_np(acl_entry,
+ ACL_ENTRY_TYPE_DENY);
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ r = acl_set_entry_type_np(acl_entry,
+ ACL_ENTRY_TYPE_AUDIT);
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ r = acl_set_entry_type_np(acl_entry,
+ ACL_ENTRY_TYPE_ALARM);
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ // These don't translate directly into the system ACL.
+ break;
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL entry type");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ if (r != 0) {
+ archive_set_error(a, errno,
+ "Failed to set ACL entry type");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+#endif
+
+ if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to get ACL permission set");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ if (acl_clear_perms(acl_permset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to clear ACL permissions");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ perm_map_size = acl_nfs4_perm_map_size;
+ perm_map = acl_nfs4_perm_map;
+ } else {
+#endif
+ perm_map_size = acl_posix_perm_map_size;
+ perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ }
+#endif
+
+ for (i = 0; i < perm_map_size; ++i) {
+ if (ae_permset & perm_map[i].a_perm) {
+ if (acl_add_perm(acl_permset,
+ perm_map[i].p_perm) != 0) {
+ archive_set_error(a, errno,
+ "Failed to add ACL permission");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ }
+ }
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ /*
+ * acl_get_flagset_np() fails with non-NFSv4 ACLs
+ */
+ if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to get flagset from an NFSv4 "
+ "ACL entry");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ if (acl_clear_flags_np(acl_flagset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to clear flags from an NFSv4 "
+ "ACL flagset");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+ if (acl_add_flag_np(acl_flagset,
+ acl_nfs4_flag_map[i].p_perm) != 0) {
+ archive_set_error(a, errno,
+ "Failed to add flag to "
+ "NFSv4 ACL flagset");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ /* Try restoring the ACL through 'fd' if we can. */
+ if (fd >= 0) {
+ if (acl_set_fd_np(fd, acl, acl_type) == 0)
+ ret = ARCHIVE_OK;
+ else {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno,
+ "Failed to set acl on fd: %s", tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+ }
+#if HAVE_ACL_SET_LINK_NP
+ else if (acl_set_link_np(name, acl_type, acl) != 0)
+#else
+ /* FreeBSD older than 8.0 */
+ else if (S_ISLNK(mode)) {
+ /* acl_set_file() follows symbolic links, skip */
+ ret = ARCHIVE_OK;
+ } else if (acl_set_file(name, acl_type, acl) != 0)
+#endif
+ {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno, "Failed to set acl: %s",
+ tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+exit_free:
+ acl_free(acl);
+ return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ const char *accpath;
+ acl_t acl;
+ int r;
+
+ accpath = NULL;
+
+ if (*fd < 0) {
+ accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (accpath == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_acl_clear(entry);
+
+ acl = NULL;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ /* Try NFSv4 ACL first. */
+ if (*fd >= 0)
+ acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
+ else if (!a->follow_symlinks)
+ acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);
+ else
+ acl = acl_get_file(accpath, ACL_TYPE_NFS4);
+
+ /* Ignore "trivial" ACLs that just mirror the file mode. */
+ if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+ acl_free(acl);
+ acl = NULL;
+ return (ARCHIVE_OK);
+ }
+
+ if (acl != NULL) {
+ r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ acl_free(acl);
+ acl = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate NFSv4 ACLs");
+ }
+
+ return (r);
+ }
+#endif
+
+ /* Retrieve access ACL from file. */
+ if (*fd >= 0)
+ acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS);
+#if HAVE_ACL_GET_LINK_NP
+ else if (!a->follow_symlinks)
+ acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
+#else
+ else if ((!a->follow_symlinks)
+ && (archive_entry_filetype(entry) == AE_IFLNK))
+ /* We can't get the ACL of a symlink, so we assume it can't
+ have one. */
+ acl = NULL;
+#endif
+ else
+ acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+
+#if HAVE_ACL_IS_TRIVIAL_NP
+ /* Ignore "trivial" ACLs that just mirror the file mode. */
+ if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+ acl_free(acl);
+ acl = NULL;
+ }
+#endif
+
+ if (acl != NULL) {
+ r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ acl_free(acl);
+ acl = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate access ACLs");
+ return (r);
+ }
+ }
+
+ /* Only directories can have default ACLs. */
+ if (S_ISDIR(archive_entry_mode(entry))) {
+ if (*fd >= 0)
+ acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);
+ else
+ acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
+ if (acl != NULL) {
+ r = translate_acl(a, entry, acl,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+ acl_free(acl);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate default ACLs");
+ return (r);
+ }
+ }
+ }
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+ int ret = ARCHIVE_OK;
+
+ (void)mode; /* UNUSED */
+
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+
+ /* Simultaneous POSIX.1e and NFSv4 is not supported */
+ return (ret);
+ }
+#if ARCHIVE_ACL_FREEBSD_NFS4
+ else if ((archive_acl_types(abstract_acl) &
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+ }
+#endif
+ return (ret);
+}
+#endif /* ARCHIVE_ACL_FREEBSD */
diff --git a/src/libs/3rdparty/libarchive/archive_disk_acl_linux.c b/src/libs/3rdparty/libarchive/archive_disk_acl_linux.c
new file mode 100644
index 000000000..31d270535
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_disk_acl_linux.c
@@ -0,0 +1,760 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+#if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_RICHACL_H
+#include <sys/richacl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+ const int a_perm; /* Libarchive permission or flag */
+ const int p_perm; /* Platform permission or flag */
+} acl_perm_map_t;
+
+#if ARCHIVE_ACL_LIBACL
+static const acl_perm_map_t acl_posix_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+ {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+ {ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+};
+
+static const int acl_posix_perm_map_size =
+ (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+#endif /* ARCHIVE_ACL_LIBACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_EXECUTE, RICHACE_EXECUTE},
+ {ARCHIVE_ENTRY_ACL_READ_DATA, RICHACE_READ_DATA},
+ {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, RICHACE_LIST_DIRECTORY},
+ {ARCHIVE_ENTRY_ACL_WRITE_DATA, RICHACE_WRITE_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_FILE, RICHACE_ADD_FILE},
+ {ARCHIVE_ENTRY_ACL_APPEND_DATA, RICHACE_APPEND_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, RICHACE_ADD_SUBDIRECTORY},
+ {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, RICHACE_READ_NAMED_ATTRS},
+ {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, RICHACE_WRITE_NAMED_ATTRS},
+ {ARCHIVE_ENTRY_ACL_DELETE_CHILD, RICHACE_DELETE_CHILD},
+ {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, RICHACE_READ_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, RICHACE_WRITE_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_DELETE, RICHACE_DELETE},
+ {ARCHIVE_ENTRY_ACL_READ_ACL, RICHACE_READ_ACL},
+ {ARCHIVE_ENTRY_ACL_WRITE_ACL, RICHACE_WRITE_ACL},
+ {ARCHIVE_ENTRY_ACL_WRITE_OWNER, RICHACE_WRITE_OWNER},
+ {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, RICHACE_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+ (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+ {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, RICHACE_FILE_INHERIT_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, RICHACE_DIRECTORY_INHERIT_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, RICHACE_NO_PROPAGATE_INHERIT_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, RICHACE_INHERIT_ONLY_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, RICHACE_INHERITED_ACE}
+};
+
+static const int acl_nfs4_flag_map_size =
+ (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+/*
+ * Translate POSIX.1e ACLs into libarchive internal structure
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+ struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
+{
+ acl_tag_t acl_tag;
+ acl_entry_t acl_entry;
+ acl_permset_t acl_permset;
+ int i, entry_acl_type;
+ int r, s, ae_id, ae_tag, ae_perm;
+ void *q;
+ const char *ae_name;
+
+ s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get first ACL entry");
+ return (ARCHIVE_WARN);
+ }
+
+ while (s == 1) {
+ ae_id = -1;
+ ae_name = NULL;
+ ae_perm = 0;
+
+ if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get ACL tag type");
+ return (ARCHIVE_WARN);
+ }
+ switch (acl_tag) {
+ case ACL_USER:
+ q = acl_get_qualifier(acl_entry);
+ if (q != NULL) {
+ ae_id = (int)*(uid_t *)q;
+ acl_free(q);
+ ae_name = archive_read_disk_uname(&a->archive,
+ ae_id);
+ }
+ ae_tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case ACL_GROUP:
+ q = acl_get_qualifier(acl_entry);
+ if (q != NULL) {
+ ae_id = (int)*(gid_t *)q;
+ acl_free(q);
+ ae_name = archive_read_disk_gname(&a->archive,
+ ae_id);
+ }
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case ACL_MASK:
+ ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ case ACL_USER_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case ACL_GROUP_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case ACL_OTHER:
+ ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+ default:
+ /* Skip types that libarchive can't support. */
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ continue;
+ }
+
+ // XXX acl_type maps to allow/deny/audit/YYYY bits
+ entry_acl_type = default_entry_acl_type;
+
+ if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get ACL permission set");
+ return (ARCHIVE_WARN);
+ }
+
+ for (i = 0; i < acl_posix_perm_map_size; ++i) {
+ r = acl_get_perm(acl_permset,
+ acl_posix_perm_map[i].p_perm);
+ if (r == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to check permission in an ACL "
+ "permission set");
+ return (ARCHIVE_WARN);
+ } else if (r)
+ ae_perm |= acl_posix_perm_map[i].a_perm;
+ }
+
+ archive_entry_acl_add_entry(entry, entry_acl_type,
+ ae_perm, ae_tag,
+ ae_id, ae_name);
+
+ s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+ if (s == -1) {
+ archive_set_error(&a->archive, errno,
+ "Failed to get next ACL entry");
+ return (ARCHIVE_WARN);
+ }
+ }
+ return (ARCHIVE_OK);
+}
+#endif /* ARCHIVE_ACL_LIBACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+/*
+ * Translate RichACL into libarchive internal ACL
+ */
+static int
+translate_richacl(struct archive_read_disk *a, struct archive_entry *entry,
+ struct richacl *richacl)
+{
+ int ae_id, ae_tag, ae_perm;
+ int entry_acl_type, i;
+ const char *ae_name;
+
+ struct richace *richace;
+
+ richacl_for_each_entry(richace, richacl) {
+ ae_name = NULL;
+ ae_tag = 0;
+ ae_perm = 0;
+ ae_id = -1;
+
+ switch (richace->e_type) {
+ case RICHACE_ACCESS_ALLOWED_ACE_TYPE:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ break;
+ case RICHACE_ACCESS_DENIED_ACE_TYPE:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ break;
+ default: /* Unknown entry type, skip */
+ continue;
+ }
+
+ /* Unsupported */
+ if (richace->e_flags & RICHACE_UNMAPPED_WHO)
+ continue;
+
+ if (richace->e_flags & RICHACE_SPECIAL_WHO) {
+ switch (richace->e_id) {
+ case RICHACE_OWNER_SPECIAL_ID:
+ ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case RICHACE_GROUP_SPECIAL_ID:
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case RICHACE_EVERYONE_SPECIAL_ID:
+ ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ break;
+ default: /* Unknown special ID type */
+ continue;
+ }
+ } else {
+ ae_id = richace->e_id;
+ if (richace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+ ae_name = archive_read_disk_gname(&a->archive,
+ (gid_t)(richace->e_id));
+ } else {
+ ae_tag = ARCHIVE_ENTRY_ACL_USER;
+ ae_name = archive_read_disk_uname(&a->archive,
+ (uid_t)(richace->e_id));
+ }
+ }
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ if ((richace->e_flags &
+ acl_nfs4_flag_map[i].p_perm) != 0)
+ ae_perm |= acl_nfs4_flag_map[i].a_perm;
+ }
+ for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+ if ((richace->e_mask &
+ acl_nfs4_perm_map[i].p_perm) != 0)
+ ae_perm |=
+ acl_nfs4_perm_map[i].a_perm;
+ }
+
+ archive_entry_acl_add_entry(entry, entry_acl_type,
+ ae_perm, ae_tag, ae_id, ae_name);
+ }
+ return (ARCHIVE_OK);
+}
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static int
+_richacl_mode_to_mask(short mode)
+{
+ int mask = 0;
+
+ if (mode & S_IROTH)
+ mask |= RICHACE_POSIX_MODE_READ;
+ if (mode & S_IWOTH)
+ mask |= RICHACE_POSIX_MODE_WRITE;
+ if (mode & S_IXOTH)
+ mask |= RICHACE_POSIX_MODE_EXEC;
+
+ return (mask);
+}
+
+static void
+_richacl_mode_to_masks(struct richacl *richacl, __LA_MODE_T mode)
+{
+ richacl->a_owner_mask = _richacl_mode_to_mask((mode & 0700) >> 6);
+ richacl->a_group_mask = _richacl_mode_to_mask((mode & 0070) >> 3);
+ richacl->a_other_mask = _richacl_mode_to_mask(mode & 0007);
+}
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static int
+set_richacl(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode,
+ int ae_requested_type, const char *tname)
+{
+ int ae_type, ae_permset, ae_tag, ae_id;
+ uid_t ae_uid;
+ gid_t ae_gid;
+ const char *ae_name;
+ int entries;
+ int i;
+ int ret;
+ int e = 0;
+ struct richacl *richacl = NULL;
+ struct richace *richace;
+
+ ret = ARCHIVE_OK;
+ entries = archive_acl_reset(abstract_acl, ae_requested_type);
+ if (entries == 0)
+ return (ARCHIVE_OK);
+
+ if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ errno = ENOENT;
+ archive_set_error(a, errno, "Unsupported ACL type");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (S_ISLNK(mode)) {
+ /* Linux does not support RichACLs on symbolic links */
+ return (ARCHIVE_OK);
+ }
+
+ richacl = richacl_alloc(entries);
+ if (richacl == NULL) {
+ archive_set_error(a, errno,
+ "Failed to initialize RichACL working storage");
+ return (ARCHIVE_FAILED);
+ }
+
+ e = 0;
+
+ while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+ &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+ richace = &(richacl->a_entries[e]);
+
+ richace->e_flags = 0;
+ richace->e_mask = 0;
+
+ switch (ae_tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+ richace->e_id = ae_uid;
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+ richace->e_id = ae_gid;
+ richace->e_flags |= RICHACE_IDENTIFIER_GROUP;
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ richace->e_flags |= RICHACE_SPECIAL_WHO;
+ richace->e_id = RICHACE_OWNER_SPECIAL_ID;
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ richace->e_flags |= RICHACE_SPECIAL_WHO;
+ richace->e_id = RICHACE_GROUP_SPECIAL_ID;
+ break;
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ richace->e_flags |= RICHACE_SPECIAL_WHO;
+ richace->e_id = RICHACE_EVERYONE_SPECIAL_ID;
+ break;
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL tag");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ switch (ae_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ richace->e_type =
+ RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ richace->e_type =
+ RICHACE_ACCESS_DENIED_ACE_TYPE;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ break;
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL entry type");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+ if (ae_permset & acl_nfs4_perm_map[i].a_perm)
+ richace->e_mask |= acl_nfs4_perm_map[i].p_perm;
+ }
+
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ if (ae_permset &
+ acl_nfs4_flag_map[i].a_perm)
+ richace->e_flags |= acl_nfs4_flag_map[i].p_perm;
+ }
+ e++;
+ }
+
+ /* Fill RichACL masks */
+ _richacl_mode_to_masks(richacl, mode);
+
+ if (fd >= 0) {
+ if (richacl_set_fd(fd, richacl) == 0)
+ ret = ARCHIVE_OK;
+ else {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno,
+ "Failed to set richacl on fd: %s", tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+ } else if (richacl_set_file(name, richacl) != 0) {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno, "Failed to set richacl: %s",
+ tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+exit_free:
+ richacl_free(richacl);
+ return (ret);
+}
+#endif /* ARCHIVE_ACL_RICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+static int
+set_acl(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode,
+ int ae_requested_type, const char *tname)
+{
+ int acl_type = 0;
+ int ae_type, ae_permset, ae_tag, ae_id;
+ uid_t ae_uid;
+ gid_t ae_gid;
+ const char *ae_name;
+ int entries;
+ int i;
+ int ret;
+ acl_t acl = NULL;
+ acl_entry_t acl_entry;
+ acl_permset_t acl_permset;
+
+ ret = ARCHIVE_OK;
+ entries = archive_acl_reset(abstract_acl, ae_requested_type);
+ if (entries == 0)
+ return (ARCHIVE_OK);
+
+ switch (ae_requested_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ acl_type = ACL_TYPE_ACCESS;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ acl_type = ACL_TYPE_DEFAULT;
+ break;
+ default:
+ errno = ENOENT;
+ archive_set_error(a, errno, "Unsupported ACL type");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (S_ISLNK(mode)) {
+ /* Linux does not support ACLs on symbolic links */
+ return (ARCHIVE_OK);
+ }
+
+ if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) {
+ errno = EINVAL;
+ archive_set_error(a, errno,
+ "Cannot set default ACL on non-directory");
+ return (ARCHIVE_WARN);
+ }
+
+ acl = acl_init(entries);
+ if (acl == (acl_t)NULL) {
+ archive_set_error(a, errno,
+ "Failed to initialize ACL working storage");
+ return (ARCHIVE_FAILED);
+ }
+
+ while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+ &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+
+ if (acl_create_entry(&acl, &acl_entry) != 0) {
+ archive_set_error(a, errno,
+ "Failed to create a new ACL entry");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ switch (ae_tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+ acl_set_tag_type(acl_entry, ACL_USER);
+ acl_set_qualifier(acl_entry, &ae_uid);
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+ acl_set_tag_type(acl_entry, ACL_GROUP);
+ acl_set_qualifier(acl_entry, &ae_gid);
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ acl_set_tag_type(acl_entry, ACL_MASK);
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ acl_set_tag_type(acl_entry, ACL_OTHER);
+ break;
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL tag");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to get ACL permission set");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ if (acl_clear_perms(acl_permset) != 0) {
+ archive_set_error(a, errno,
+ "Failed to clear ACL permissions");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ for (i = 0; i < acl_posix_perm_map_size; ++i) {
+ if (ae_permset & acl_posix_perm_map[i].a_perm) {
+ if (acl_add_perm(acl_permset,
+ acl_posix_perm_map[i].p_perm) != 0) {
+ archive_set_error(a, errno,
+ "Failed to add ACL permission");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+ }
+ }
+
+ }
+
+ if (fd >= 0 && ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+ if (acl_set_fd(fd, acl) == 0)
+ ret = ARCHIVE_OK;
+ else {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno,
+ "Failed to set acl on fd: %s", tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+ } else if (acl_set_file(name, acl_type, acl) != 0) {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno, "Failed to set acl: %s",
+ tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+exit_free:
+ acl_free(acl);
+ return (ret);
+}
+#endif /* ARCHIVE_ACL_LIBACL */
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ const char *accpath;
+ int r;
+#if ARCHIVE_ACL_LIBACL
+ acl_t acl;
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+ struct richacl *richacl;
+ mode_t mode;
+#endif
+
+ accpath = NULL;
+ r = ARCHIVE_OK;
+
+ /* For default ACLs we need reachable accpath */
+ if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) {
+ accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (accpath == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_acl_clear(entry);
+
+#if ARCHIVE_ACL_LIBACL
+ acl = NULL;
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+ richacl = NULL;
+#endif
+
+#if ARCHIVE_ACL_LIBRICHACL
+ /* Try NFSv4 ACL first. */
+ if (*fd >= 0)
+ richacl = richacl_get_fd(*fd);
+ else if ((!a->follow_symlinks)
+ && (archive_entry_filetype(entry) == AE_IFLNK))
+ /* We can't get the ACL of a symlink, so we assume it can't
+ have one */
+ richacl = NULL;
+ else
+ richacl = richacl_get_file(accpath);
+
+ /* Ignore "trivial" ACLs that just mirror the file mode. */
+ if (richacl != NULL) {
+ mode = archive_entry_mode(entry);
+ if (richacl_equiv_mode(richacl, &mode) == 0) {
+ richacl_free(richacl);
+ richacl = NULL;
+ return (ARCHIVE_OK);
+ }
+ }
+
+ if (richacl != NULL) {
+ r = translate_richacl(a, entry, richacl);
+ richacl_free(richacl);
+ richacl = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate NFSv4 ACLs");
+ }
+
+ return (r);
+ }
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+ /* Retrieve access ACL from file. */
+ if (*fd >= 0)
+ acl = acl_get_fd(*fd);
+ else if ((!a->follow_symlinks)
+ && (archive_entry_filetype(entry) == AE_IFLNK))
+ /* We can't get the ACL of a symlink, so we assume it can't
+ have one. */
+ acl = NULL;
+ else
+ acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+
+ if (acl != NULL) {
+ r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ acl_free(acl);
+ acl = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate access ACLs");
+ return (r);
+ }
+ }
+
+ /* Only directories can have default ACLs. */
+ if (S_ISDIR(archive_entry_mode(entry))) {
+ acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
+ if (acl != NULL) {
+ r = translate_acl(a, entry, acl,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+ acl_free(acl);
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate default ACLs");
+ return (r);
+ }
+ }
+ }
+#endif /* ARCHIVE_ACL_LIBACL */
+ return (r);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+ int ret = ARCHIVE_OK;
+
+#if !ARCHIVE_ACL_LIBRICHACL
+ (void)mode; /* UNUSED */
+#endif
+
+#if ARCHIVE_ACL_LIBRICHACL
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ ret = set_richacl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+ }
+#if ARCHIVE_ACL_LIBACL
+ else
+#endif
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+#if ARCHIVE_ACL_LIBACL
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+ if (ret != ARCHIVE_OK)
+ return (ret);
+ }
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+ }
+#endif /* ARCHIVE_ACL_LIBACL */
+ return (ret);
+}
+#endif /* ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL */
diff --git a/src/libs/3rdparty/libarchive/archive_disk_acl_sunos.c b/src/libs/3rdparty/libarchive/archive_disk_acl_sunos.c
new file mode 100644
index 000000000..0ef3ad52e
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_disk_acl_sunos.c
@@ -0,0 +1,824 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+#if ARCHIVE_ACL_SUNOS
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+ const int a_perm; /* Libarchive permission or flag */
+ const int p_perm; /* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_posix_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_EXECUTE, S_IXOTH },
+ {ARCHIVE_ENTRY_ACL_WRITE, S_IWOTH },
+ {ARCHIVE_ENTRY_ACL_READ, S_IROTH }
+};
+
+static const int acl_posix_perm_map_size =
+ (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+ {ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE},
+ {ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA},
+ {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY},
+ {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE},
+ {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA},
+ {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY},
+ {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS},
+ {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS},
+ {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD},
+ {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES},
+ {ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE},
+ {ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL},
+ {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL},
+ {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER},
+ {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+ (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+ {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE},
+ {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
+ {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG},
+#ifdef ACE_INHERITED_ACE
+ {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
+#endif
+};
+
+const int acl_nfs4_flag_map_size =
+ (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+
+#endif /* ARCHIVE_ACL_SUNOS_NFS4 */
+
+static void *
+sunacl_get(int cmd, int *aclcnt, int fd, const char *path)
+{
+ int cnt, cntcmd;
+ size_t size;
+ void *aclp;
+
+ if (cmd == GETACL) {
+ cntcmd = GETACLCNT;
+ size = sizeof(aclent_t);
+ }
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else if (cmd == ACE_GETACL) {
+ cntcmd = ACE_GETACLCNT;
+ size = sizeof(ace_t);
+ }
+#endif
+ else {
+ errno = EINVAL;
+ *aclcnt = -1;
+ return (NULL);
+ }
+
+ aclp = NULL;
+ cnt = -2;
+
+ while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) {
+ if (path != NULL)
+ cnt = acl(path, cntcmd, 0, NULL);
+ else
+ cnt = facl(fd, cntcmd, 0, NULL);
+
+ if (cnt > 0) {
+ if (aclp == NULL)
+ aclp = malloc(cnt * size);
+ else
+ aclp = realloc(NULL, cnt * size);
+ if (aclp != NULL) {
+ if (path != NULL)
+ cnt = acl(path, cmd, cnt, aclp);
+ else
+ cnt = facl(fd, cmd, cnt, aclp);
+ }
+ } else {
+ free(aclp);
+ aclp = NULL;
+ break;
+ }
+ }
+
+ *aclcnt = cnt;
+ return (aclp);
+}
+
+/*
+ * Check if acl is trivial
+ * This is a FreeBSD acl_is_trivial_np() implementation for Solaris
+ */
+static int
+sun_acl_is_trivial(void *aclp, int aclcnt, mode_t mode, int is_nfs4,
+ int is_dir, int *trivialp)
+{
+#if ARCHIVE_ACL_SUNOS_NFS4
+ int i, p;
+ const uint32_t rperm = ACE_READ_DATA;
+ const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA;
+ const uint32_t eperm = ACE_EXECUTE;
+ const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+ ACE_READ_ACL | ACE_SYNCHRONIZE;
+ const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES |
+ ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER;
+
+ ace_t *ace;
+ ace_t tace[6];
+#endif
+
+ if (aclp == NULL || trivialp == NULL)
+ return (-1);
+
+ *trivialp = 0;
+
+ /*
+ * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with
+ * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries,
+ * including mask.
+ */
+ if (!is_nfs4) {
+ if (aclcnt == 4)
+ *trivialp = 1;
+ return (0);
+ }
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+ /*
+ * Continue with checking NFSv4 ACLs
+ *
+ * Create list of trivial ace's to be compared
+ */
+
+ /* owner@ allow pre */
+ tace[0].a_flags = ACE_OWNER;
+ tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ tace[0].a_access_mask = 0;
+
+ /* owner@ deny */
+ tace[1].a_flags = ACE_OWNER;
+ tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ tace[1].a_access_mask = 0;
+
+ /* group@ deny */
+ tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+ tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ tace[2].a_access_mask = 0;
+
+ /* owner@ allow */
+ tace[3].a_flags = ACE_OWNER;
+ tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ tace[3].a_access_mask = ownset;
+
+ /* group@ allow */
+ tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+ tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ tace[4].a_access_mask = pubset;
+
+ /* everyone@ allow */
+ tace[5].a_flags = ACE_EVERYONE;
+ tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ tace[5].a_access_mask = pubset;
+
+ /* Permissions for everyone@ */
+ if (mode & 0004)
+ tace[5].a_access_mask |= rperm;
+ if (mode & 0002)
+ tace[5].a_access_mask |= wperm;
+ if (mode & 0001)
+ tace[5].a_access_mask |= eperm;
+
+ /* Permissions for group@ */
+ if (mode & 0040)
+ tace[4].a_access_mask |= rperm;
+ else if (mode & 0004)
+ tace[2].a_access_mask |= rperm;
+ if (mode & 0020)
+ tace[4].a_access_mask |= wperm;
+ else if (mode & 0002)
+ tace[2].a_access_mask |= wperm;
+ if (mode & 0010)
+ tace[4].a_access_mask |= eperm;
+ else if (mode & 0001)
+ tace[2].a_access_mask |= eperm;
+
+ /* Permissions for owner@ */
+ if (mode & 0400) {
+ tace[3].a_access_mask |= rperm;
+ if (!(mode & 0040) && (mode & 0004))
+ tace[0].a_access_mask |= rperm;
+ } else if ((mode & 0040) || (mode & 0004))
+ tace[1].a_access_mask |= rperm;
+ if (mode & 0200) {
+ tace[3].a_access_mask |= wperm;
+ if (!(mode & 0020) && (mode & 0002))
+ tace[0].a_access_mask |= wperm;
+ } else if ((mode & 0020) || (mode & 0002))
+ tace[1].a_access_mask |= wperm;
+ if (mode & 0100) {
+ tace[3].a_access_mask |= eperm;
+ if (!(mode & 0010) && (mode & 0001))
+ tace[0].a_access_mask |= eperm;
+ } else if ((mode & 0010) || (mode & 0001))
+ tace[1].a_access_mask |= eperm;
+
+ /* Check if the acl count matches */
+ p = 3;
+ for (i = 0; i < 3; i++) {
+ if (tace[i].a_access_mask != 0)
+ p++;
+ }
+ if (aclcnt != p)
+ return (0);
+
+ p = 0;
+ for (i = 0; i < 6; i++) {
+ if (tace[i].a_access_mask != 0) {
+ ace = &((ace_t *)aclp)[p];
+ /*
+ * Illumos added ACE_DELETE_CHILD to write perms for
+ * directories. We have to check against that, too.
+ */
+ if (ace->a_flags != tace[i].a_flags ||
+ ace->a_type != tace[i].a_type ||
+ (ace->a_access_mask != tace[i].a_access_mask &&
+ (!is_dir || (tace[i].a_access_mask & wperm) == 0 ||
+ ace->a_access_mask !=
+ (tace[i].a_access_mask | ACE_DELETE_CHILD))))
+ return (0);
+ p++;
+ }
+ }
+
+ *trivialp = 1;
+#else /* !ARCHIVE_ACL_SUNOS_NFS4 */
+ (void)is_dir; /* UNUSED */
+ (void)aclp; /* UNUSED */
+#endif /* !ARCHIVE_ACL_SUNOS_NFS4 */
+ return (0);
+}
+
+/*
+ * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+ struct archive_entry *entry, void *aclp, int aclcnt,
+ int default_entry_acl_type)
+{
+ int e, i;
+ int ae_id, ae_tag, ae_perm;
+ int entry_acl_type;
+ const char *ae_name;
+ aclent_t *aclent;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ ace_t *ace;
+#endif
+
+ if (aclcnt <= 0)
+ return (ARCHIVE_OK);
+
+ for (e = 0; e < aclcnt; e++) {
+ ae_name = NULL;
+ ae_tag = 0;
+ ae_perm = 0;
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+ if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ ace = &((ace_t *)aclp)[e];
+ ae_id = ace->a_who;
+
+ switch(ace->a_type) {
+ case ACE_ACCESS_ALLOWED_ACE_TYPE:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+ break;
+ case ACE_ACCESS_DENIED_ACE_TYPE:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+ break;
+ case ACE_SYSTEM_AUDIT_ACE_TYPE:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ break;
+ case ACE_SYSTEM_ALARM_ACE_TYPE:
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+ break;
+ default:
+ /* Unknown entry type, skip */
+ continue;
+ }
+
+ if ((ace->a_flags & ACE_OWNER) != 0)
+ ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ else if ((ace->a_flags & ACE_GROUP) != 0)
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ else if ((ace->a_flags & ACE_EVERYONE) != 0)
+ ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+ else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) {
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+ ae_name = archive_read_disk_gname(&a->archive,
+ ae_id);
+ } else {
+ ae_tag = ARCHIVE_ENTRY_ACL_USER;
+ ae_name = archive_read_disk_uname(&a->archive,
+ ae_id);
+ }
+
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ if ((ace->a_flags &
+ acl_nfs4_flag_map[i].p_perm) != 0)
+ ae_perm |= acl_nfs4_flag_map[i].a_perm;
+ }
+
+ for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+ if ((ace->a_access_mask &
+ acl_nfs4_perm_map[i].p_perm) != 0)
+ ae_perm |= acl_nfs4_perm_map[i].a_perm;
+ }
+ } else
+#endif /* ARCHIVE_ACL_SUNOS_NFS4 */
+ if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+ aclent = &((aclent_t *)aclp)[e];
+ if ((aclent->a_type & ACL_DEFAULT) != 0)
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+ else
+ entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+ ae_id = aclent->a_id;
+
+ switch(aclent->a_type) {
+ case DEF_USER:
+ case USER:
+ ae_name = archive_read_disk_uname(&a->archive,
+ ae_id);
+ ae_tag = ARCHIVE_ENTRY_ACL_USER;
+ break;
+ case DEF_GROUP:
+ case GROUP:
+ ae_name = archive_read_disk_gname(&a->archive,
+ ae_id);
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+ break;
+ case DEF_CLASS_OBJ:
+ case CLASS_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+ break;
+ case DEF_USER_OBJ:
+ case USER_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+ break;
+ case DEF_GROUP_OBJ:
+ case GROUP_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+ break;
+ case DEF_OTHER_OBJ:
+ case OTHER_OBJ:
+ ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+ break;
+ default:
+ /* Unknown tag type, skip */
+ continue;
+ }
+
+ for (i = 0; i < acl_posix_perm_map_size; ++i) {
+ if ((aclent->a_perm &
+ acl_posix_perm_map[i].p_perm) != 0)
+ ae_perm |= acl_posix_perm_map[i].a_perm;
+ }
+ } else
+ return (ARCHIVE_WARN);
+
+ archive_entry_acl_add_entry(entry, entry_acl_type,
+ ae_perm, ae_tag, ae_id, ae_name);
+ }
+ return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode,
+ int ae_requested_type, const char *tname)
+{
+ aclent_t *aclent;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ ace_t *ace;
+#endif
+ int cmd, e, r;
+ void *aclp;
+ int ret;
+ int ae_type, ae_permset, ae_tag, ae_id;
+ int perm_map_size;
+ const acl_perm_map_t *perm_map;
+ uid_t ae_uid;
+ gid_t ae_gid;
+ const char *ae_name;
+ int entries;
+ int i;
+
+ ret = ARCHIVE_OK;
+ entries = archive_acl_reset(abstract_acl, ae_requested_type);
+ if (entries == 0)
+ return (ARCHIVE_OK);
+
+ switch (ae_requested_type) {
+ case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+ cmd = SETACL;
+ aclp = malloc(entries * sizeof(aclent_t));
+ break;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+ cmd = ACE_SETACL;
+ aclp = malloc(entries * sizeof(ace_t));
+
+ break;
+#endif
+ default:
+ errno = ENOENT;
+ archive_set_error(a, errno, "Unsupported ACL type");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (aclp == NULL) {
+ archive_set_error(a, errno,
+ "Can't allocate memory for acl buffer");
+ return (ARCHIVE_FAILED);
+ }
+
+ if (S_ISLNK(mode)) {
+ /* Skip ACLs on symbolic links */
+ ret = ARCHIVE_OK;
+ goto exit_free;
+ }
+
+ e = 0;
+
+ while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+ &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+ aclent = NULL;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ ace = NULL;
+#endif
+ if (cmd == SETACL) {
+ aclent = &((aclent_t *)aclp)[e];
+ aclent->a_id = -1;
+ aclent->a_type = 0;
+ aclent->a_perm = 0;
+ }
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else { /* cmd == ACE_SETACL */
+ ace = &((ace_t *)aclp)[e];
+ ace->a_who = -1;
+ ace->a_access_mask = 0;
+ ace->a_flags = 0;
+ }
+#endif /* ARCHIVE_ACL_SUNOS_NFS4 */
+
+ switch (ae_tag) {
+ case ARCHIVE_ENTRY_ACL_USER:
+ ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+ if (aclent != NULL) {
+ aclent->a_id = ae_uid;
+ aclent->a_type |= USER;
+ }
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else {
+ ace->a_who = ae_uid;
+ }
+#endif
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP:
+ ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+ if (aclent != NULL) {
+ aclent->a_id = ae_gid;
+ aclent->a_type |= GROUP;
+ }
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else {
+ ace->a_who = ae_gid;
+ ace->a_flags |= ACE_IDENTIFIER_GROUP;
+ }
+#endif
+ break;
+ case ARCHIVE_ENTRY_ACL_USER_OBJ:
+ if (aclent != NULL)
+ aclent->a_type |= USER_OBJ;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else {
+ ace->a_flags |= ACE_OWNER;
+ }
+#endif
+ break;
+ case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+ if (aclent != NULL)
+ aclent->a_type |= GROUP_OBJ;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else {
+ ace->a_flags |= ACE_GROUP;
+ ace->a_flags |= ACE_IDENTIFIER_GROUP;
+ }
+#endif
+ break;
+ case ARCHIVE_ENTRY_ACL_MASK:
+ if (aclent != NULL)
+ aclent->a_type |= CLASS_OBJ;
+ break;
+ case ARCHIVE_ENTRY_ACL_OTHER:
+ if (aclent != NULL)
+ aclent->a_type |= OTHER_OBJ;
+ break;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ case ARCHIVE_ENTRY_ACL_EVERYONE:
+ if (ace != NULL)
+ ace->a_flags |= ACE_EVERYONE;
+ break;
+#endif
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL tag");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ r = 0;
+ switch (ae_type) {
+#if ARCHIVE_ACL_SUNOS_NFS4
+ case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+ if (ace != NULL)
+ ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ else
+ r = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+ if (ace != NULL)
+ ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ else
+ r = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+ if (ace != NULL)
+ ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE;
+ else
+ r = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+ if (ace != NULL)
+ ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE;
+ else
+ r = -1;
+ break;
+#endif
+ case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+ if (aclent == NULL)
+ r = -1;
+ break;
+ case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+ if (aclent != NULL)
+ aclent->a_type |= ACL_DEFAULT;
+ else
+ r = -1;
+ break;
+ default:
+ archive_set_error(a, ARCHIVE_ERRNO_MISC,
+ "Unsupported ACL entry type");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+ if (r != 0) {
+ errno = EINVAL;
+ archive_set_error(a, errno,
+ "Failed to set ACL entry type");
+ ret = ARCHIVE_FAILED;
+ goto exit_free;
+ }
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+ if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ perm_map_size = acl_nfs4_perm_map_size;
+ perm_map = acl_nfs4_perm_map;
+ } else {
+#endif
+ perm_map_size = acl_posix_perm_map_size;
+ perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_SUNOS_NFS4
+ }
+#endif
+ for (i = 0; i < perm_map_size; ++i) {
+ if (ae_permset & perm_map[i].a_perm) {
+#if ARCHIVE_ACL_SUNOS_NFS4
+ if (ae_requested_type ==
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+ ace->a_access_mask |=
+ perm_map[i].p_perm;
+ else
+#endif
+ aclent->a_perm |= perm_map[i].p_perm;
+ }
+ }
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+ if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+ for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+ if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+ ace->a_flags |=
+ acl_nfs4_flag_map[i].p_perm;
+ }
+ }
+ }
+#endif
+ e++;
+ }
+
+ /* Try restoring the ACL through 'fd' if we can. */
+ if (fd >= 0) {
+ if (facl(fd, cmd, entries, aclp) == 0)
+ ret = ARCHIVE_OK;
+ else {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno,
+ "Failed to set acl on fd: %s", tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+ } else if (acl(name, cmd, entries, aclp) != 0) {
+ if (errno == EOPNOTSUPP) {
+ /* Filesystem doesn't support ACLs */
+ ret = ARCHIVE_OK;
+ } else {
+ archive_set_error(a, errno, "Failed to set acl: %s",
+ tname);
+ ret = ARCHIVE_WARN;
+ }
+ }
+exit_free:
+ free(aclp);
+ return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+ struct archive_entry *entry, int *fd)
+{
+ const char *accpath;
+ void *aclp;
+ int aclcnt;
+ int r;
+
+ accpath = NULL;
+
+ if (*fd < 0) {
+ accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+ if (accpath == NULL)
+ return (ARCHIVE_WARN);
+ }
+
+ archive_entry_acl_clear(entry);
+
+ aclp = NULL;
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+ if (*fd >= 0)
+ aclp = sunacl_get(ACE_GETACL, &aclcnt, *fd, NULL);
+ else if ((!a->follow_symlinks)
+ && (archive_entry_filetype(entry) == AE_IFLNK))
+ /* We can't get the ACL of a symlink, so we assume it can't
+ have one. */
+ aclp = NULL;
+ else
+ aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, accpath);
+
+ if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+ archive_entry_mode(entry), 1, S_ISDIR(archive_entry_mode(entry)),
+ &r) == 0 && r == 1) {
+ free(aclp);
+ aclp = NULL;
+ return (ARCHIVE_OK);
+ }
+
+ if (aclp != NULL) {
+ r = translate_acl(a, entry, aclp, aclcnt,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+ free(aclp);
+ aclp = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate NFSv4 ACLs");
+ }
+ return (r);
+ }
+#endif /* ARCHIVE_ACL_SUNOS_NFS4 */
+
+ /* Retrieve POSIX.1e ACLs from file. */
+ if (*fd >= 0)
+ aclp = sunacl_get(GETACL, &aclcnt, *fd, NULL);
+ else if ((!a->follow_symlinks)
+ && (archive_entry_filetype(entry) == AE_IFLNK))
+ /* We can't get the ACL of a symlink, so we assume it can't
+ have one. */
+ aclp = NULL;
+ else
+ aclp = sunacl_get(GETACL, &aclcnt, 0, accpath);
+
+ /* Ignore "trivial" ACLs that just mirror the file mode. */
+ if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+ archive_entry_mode(entry), 0, S_ISDIR(archive_entry_mode(entry)),
+ &r) == 0 && r == 1) {
+ free(aclp);
+ aclp = NULL;
+ }
+
+ if (aclp != NULL)
+ {
+ r = translate_acl(a, entry, aclp, aclcnt,
+ ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+ free(aclp);
+ aclp = NULL;
+
+ if (r != ARCHIVE_OK) {
+ archive_set_error(&a->archive, errno,
+ "Couldn't translate access ACLs");
+ return (r);
+ }
+ }
+
+ return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+ struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+ int ret = ARCHIVE_OK;
+
+ (void)mode; /* UNUSED */
+
+ if ((archive_acl_types(abstract_acl)
+ & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+ /* Solaris writes POSIX.1e access and default ACLs together */
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
+
+ /* Simultaneous POSIX.1e and NFSv4 is not supported */
+ return (ret);
+ }
+#if ARCHIVE_ACL_SUNOS_NFS4
+ else if ((archive_acl_types(abstract_acl) &
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+ ret = set_acl(a, fd, name, abstract_acl, mode,
+ ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+ }
+#endif
+ return (ret);
+}
+#endif /* ARCHIVE_ACL_SUNOS */
diff --git a/src/libs/3rdparty/libarchive/archive_endian.h b/src/libs/3rdparty/libarchive/archive_endian.h
new file mode 100644
index 000000000..e6d3f2ce5
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry.c b/src/libs/3rdparty/libarchive/archive_entry.c
new file mode 100644
index 000000000..ae6dc3336
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#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> */
+#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/src/libs/3rdparty/libarchive/archive_entry.h b/src/libs/3rdparty/libarchive/archive_entry.h
new file mode 100644
index 000000000..c15460b96
--- /dev/null
+++ b/src/libs/3rdparty/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 3007001
+
+/*
+ * 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__)
+#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/src/libs/3rdparty/libarchive/archive_entry_copy_bhfi.c b/src/libs/3rdparty/libarchive/archive_entry_copy_bhfi.c
new file mode 100644
index 000000000..77bf38e45
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_copy_stat.c b/src/libs/3rdparty/libarchive/archive_entry_copy_stat.c
new file mode 100644
index 000000000..ac83868e8
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_link_resolver.c b/src/libs/3rdparty/libarchive/archive_entry_link_resolver.c
new file mode 100644
index 000000000..c7d59497a
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_locale.h b/src/libs/3rdparty/libarchive/archive_entry_locale.h
new file mode 100644
index 000000000..803c0368b
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_private.h b/src/libs/3rdparty/libarchive/archive_entry_private.h
new file mode 100644
index 000000000..cf4deb24e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_sparse.c b/src/libs/3rdparty/libarchive/archive_entry_sparse.c
new file mode 100644
index 000000000..74917b37b
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_stat.c b/src/libs/3rdparty/libarchive/archive_entry_stat.c
new file mode 100644
index 000000000..71a407b1f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_strmode.c b/src/libs/3rdparty/libarchive/archive_entry_strmode.c
new file mode 100644
index 000000000..af2517a32
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_entry_xattr.c b/src/libs/3rdparty/libarchive/archive_entry_xattr.c
new file mode 100644
index 000000000..5fe726b99
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#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/src/libs/3rdparty/libarchive/archive_getdate.c b/src/libs/3rdparty/libarchive/archive_getdate.c
new file mode 100644
index 000000000..20ab1b158
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_getdate.h b/src/libs/3rdparty/libarchive/archive_getdate.h
new file mode 100644
index 000000000..900a8f692
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_hmac.c b/src/libs/3rdparty/libarchive/archive_hmac.c
new file mode 100644
index 000000000..edb3bf5ab
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_hmac_private.h b/src/libs/3rdparty/libarchive/archive_hmac_private.h
new file mode 100644
index 000000000..d0fda7f96
--- /dev/null
+++ b/src/libs/3rdparty/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)
+#include <mbedtls/md.h>
+
+typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+#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
+#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/src/libs/3rdparty/libarchive/archive_match.c b/src/libs/3rdparty/libarchive/archive_match.c
new file mode 100644
index 000000000..04747b1f6
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h b/src/libs/3rdparty/libarchive/archive_openssl_evp_private.h
new file mode 100644
index 000000000..8ac477280
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_openssl_hmac_private.h b/src/libs/3rdparty/libarchive/archive_openssl_hmac_private.h
new file mode 100644
index 000000000..25c8dda65
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_options.c b/src/libs/3rdparty/libarchive/archive_options.c
new file mode 100644
index 000000000..6496025a5
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_options_private.h b/src/libs/3rdparty/libarchive/archive_options_private.h
new file mode 100644
index 000000000..9a7f8080d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_pack_dev.c b/src/libs/3rdparty/libarchive/archive_pack_dev.c
new file mode 100644
index 000000000..d95444d97
--- /dev/null
+++ b/src/libs/3rdparty/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> */
+#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/src/libs/3rdparty/libarchive/archive_pack_dev.h b/src/libs/3rdparty/libarchive/archive_pack_dev.h
new file mode 100644
index 000000000..eaf23e388
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_pathmatch.c b/src/libs/3rdparty/libarchive/archive_pathmatch.c
new file mode 100644
index 000000000..0867a268e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_pathmatch.h b/src/libs/3rdparty/libarchive/archive_pathmatch.h
new file mode 100644
index 000000000..999514292
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_platform.h b/src/libs/3rdparty/libarchive/archive_platform.h
new file mode 100644
index 000000000..1038932ac
--- /dev/null
+++ b/src/libs/3rdparty/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. */
+#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/src/libs/3rdparty/libarchive/archive_platform_acl.h b/src/libs/3rdparty/libarchive/archive_platform_acl.h
new file mode 100644
index 000000000..264e6de37
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_platform_xattr.h b/src/libs/3rdparty/libarchive/archive_platform_xattr.h
new file mode 100644
index 000000000..ad4b90ab7
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_platform_xattr.h
@@ -0,0 +1,47 @@
+/*-
+ * 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_XATTR_H_INCLUDED
+#define ARCHIVE_PLATFORM_XATTR_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST_COMMON
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/*
+ * Determine if we support extended attributes
+ */
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_FREEBSD || \
+ ARCHIVE_XATTR_AIX
+#define ARCHIVE_XATTR_SUPPORT 1
+#endif
+
+#endif /* ARCHIVE_PLATFORM_XATTR_H_INCLUDED */
diff --git a/src/libs/3rdparty/libarchive/archive_ppmd7.c b/src/libs/3rdparty/libarchive/archive_ppmd7.c
new file mode 100644
index 000000000..cc3f77820
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_ppmd7_private.h b/src/libs/3rdparty/libarchive/archive_ppmd7_private.h
new file mode 100644
index 000000000..71b954458
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_ppmd8.c b/src/libs/3rdparty/libarchive/archive_ppmd8.c
new file mode 100644
index 000000000..d1779395d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_ppmd8_private.h b/src/libs/3rdparty/libarchive/archive_ppmd8_private.h
new file mode 100644
index 000000000..454b75f41
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_ppmd_private.h b/src/libs/3rdparty/libarchive/archive_ppmd_private.h
new file mode 100644
index 000000000..582803e5f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_private.h b/src/libs/3rdparty/libarchive/archive_private.h
new file mode 100644
index 000000000..b2a2cda25
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_random.c b/src/libs/3rdparty/libarchive/archive_random.c
new file mode 100644
index 000000000..301765acd
--- /dev/null
+++ b/src/libs/3rdparty/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, 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/src/libs/3rdparty/libarchive/archive_random_private.h b/src/libs/3rdparty/libarchive/archive_random_private.h
new file mode 100644
index 000000000..08b91b3b7
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_rb.c b/src/libs/3rdparty/libarchive/archive_rb.c
new file mode 100644
index 000000000..cf58ac335
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_rb.h b/src/libs/3rdparty/libarchive/archive_rb.h
new file mode 100644
index 000000000..8851f1081
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read.c b/src/libs/3rdparty/libarchive/archive_read.c
new file mode 100644
index 000000000..45a38aed0
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_add_passphrase.c b/src/libs/3rdparty/libarchive/archive_read_add_passphrase.c
new file mode 100644
index 000000000..f0b1ab933
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_append_filter.c b/src/libs/3rdparty/libarchive/archive_read_append_filter.c
new file mode 100644
index 000000000..25dc4b2a2
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c b/src/libs/3rdparty/libarchive/archive_read_data_into_fd.c
new file mode 100644
index 000000000..f16ca5c82
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_disk_entry_from_file.c b/src/libs/3rdparty/libarchive/archive_read_disk_entry_from_file.c
new file mode 100644
index 000000000..ab0270bc2
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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
+#include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#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/src/libs/3rdparty/libarchive/archive_read_disk_posix.c b/src/libs/3rdparty/libarchive/archive_read_disk_posix.c
new file mode 100644
index 000000000..8d5c32f03
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#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/src/libs/3rdparty/libarchive/archive_read_disk_private.h b/src/libs/3rdparty/libarchive/archive_read_disk_private.h
new file mode 100644
index 000000000..bc8abc15d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_disk_set_standard_lookup.c b/src/libs/3rdparty/libarchive/archive_read_disk_set_standard_lookup.c
new file mode 100644
index 000000000..c7fd2471e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_disk_windows.c b/src/libs/3rdparty/libarchive/archive_read_disk_windows.c
new file mode 100644
index 000000000..f92a78a21
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_extract.c b/src/libs/3rdparty/libarchive/archive_read_extract.c
new file mode 100644
index 000000000..b7973fa8e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_extract2.c b/src/libs/3rdparty/libarchive/archive_read_extract2.c
new file mode 100644
index 000000000..4febd8ce0
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_open_fd.c b/src/libs/3rdparty/libarchive/archive_read_open_fd.c
new file mode 100644
index 000000000..f59cd07fe
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_open_file.c b/src/libs/3rdparty/libarchive/archive_read_open_file.c
new file mode 100644
index 000000000..03719e8bf
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_open_filename.c b/src/libs/3rdparty/libarchive/archive_read_open_filename.c
new file mode 100644
index 000000000..561289b69
--- /dev/null
+++ b/src/libs/3rdparty/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>
+#include <sys/dkio.h>
+#elif defined(__DragonFly__)
+#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/src/libs/3rdparty/libarchive/archive_read_open_memory.c b/src/libs/3rdparty/libarchive/archive_read_open_memory.c
new file mode 100644
index 000000000..311be4704
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_private.h b/src/libs/3rdparty/libarchive/archive_read_private.h
new file mode 100644
index 000000000..383405d52
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_set_format.c b/src/libs/3rdparty/libarchive/archive_read_set_format.c
new file mode 100644
index 000000000..796dcdcce
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_set_options.c b/src/libs/3rdparty/libarchive/archive_read_set_options.c
new file mode 100644
index 000000000..2bd9b811e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_all.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_all.c
new file mode 100644
index 000000000..edb508c1d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_by_code.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_by_code.c
new file mode 100644
index 000000000..94c4af695
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_bzip2.c
new file mode 100644
index 000000000..9158e668e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_compress.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_compress.c
new file mode 100644
index 000000000..05b80a576
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_grzip.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_grzip.c
new file mode 100644
index 000000000..d4d1737cd
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_gzip.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_gzip.c
new file mode 100644
index 000000000..4135a6361
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_lrzip.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lrzip.c
new file mode 100644
index 000000000..a2389894f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lz4.c
new file mode 100644
index 000000000..d0fc1a83e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_lzop.c
new file mode 100644
index 000000000..4ebdd3bf3
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#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)
+#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/src/libs/3rdparty/libarchive/archive_read_support_filter_none.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_none.c
new file mode 100644
index 000000000..95e5cfdb1
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_program.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_program.c
new file mode 100644
index 000000000..885b2c205
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_rpm.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_rpm.c
new file mode 100644
index 000000000..67a979cd7
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_filter_uu.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_uu.c
new file mode 100644
index 000000000..209b2a159
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_read_support_filter_uu.c
@@ -0,0 +1,683 @@
+/*-
+ * 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_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
+};
+
+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 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,
+};
+
+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;
+ 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 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;
+
+ 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;
+ }
+ 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);
+
+ return (ARCHIVE_OK);
+}
+
diff --git a/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_xz.c
new file mode 100644
index 000000000..e313d39c0
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c b/src/libs/3rdparty/libarchive/archive_read_support_filter_zstd.c
new file mode 100644
index 000000000..1959b5ac3
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
new file mode 100644
index 000000000..b171bea01
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_read_support_format_7zip.c
@@ -0,0 +1,4074 @@
+/*-
+ * 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
+#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
+#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
+
+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);
+ }
+
+ /* 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_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].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 & 0x01)
+ 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/src/libs/3rdparty/libarchive/archive_read_support_format_all.c b/src/libs/3rdparty/libarchive/archive_read_support_format_all.c
new file mode 100644
index 000000000..dea558bbf
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_ar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_ar.c
new file mode 100644
index 000000000..296b7db04
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_by_code.c b/src/libs/3rdparty/libarchive/archive_read_support_format_by_code.c
new file mode 100644
index 000000000..89e96f1f5
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cab.c
new file mode 100644
index 000000000..3b552a84d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c b/src/libs/3rdparty/libarchive/archive_read_support_format_cpio.c
new file mode 100644
index 000000000..9adcfd335
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_empty.c b/src/libs/3rdparty/libarchive/archive_read_support_format_empty.c
new file mode 100644
index 000000000..53fb6cc47
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_read_support_format_iso9660.c
new file mode 100644
index 000000000..f5414be2a
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c b/src/libs/3rdparty/libarchive/archive_read_support_format_lha.c
new file mode 100644
index 000000000..1c64b2900
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c b/src/libs/3rdparty/libarchive/archive_read_support_format_mtree.c
new file mode 100644
index 000000000..a5fa30e3c
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar.c
new file mode 100644
index 000000000..16b6e6eed
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c b/src/libs/3rdparty/libarchive/archive_read_support_format_rar5.c
new file mode 100644
index 000000000..1f9099439
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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
+#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/src/libs/3rdparty/libarchive/archive_read_support_format_raw.c b/src/libs/3rdparty/libarchive/archive_read_support_format_raw.c
new file mode 100644
index 000000000..ec0520b60
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_tar.c
new file mode 100644
index 000000000..93c3fd585
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c b/src/libs/3rdparty/libarchive/archive_read_support_format_warc.c
new file mode 100644
index 000000000..61ab29ea1
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c b/src/libs/3rdparty/libarchive/archive_read_support_format_xar.c
new file mode 100644
index 000000000..ec9cb1981
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <libxml/xmlreader.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#include <expat.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#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/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c b/src/libs/3rdparty/libarchive/archive_read_support_format_zip.c
new file mode 100644
index 000000000..c3b9b5755
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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
+#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/src/libs/3rdparty/libarchive/archive_string.c b/src/libs/3rdparty/libarchive/archive_string.c
new file mode 100644
index 000000000..accf52631
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_string.h b/src/libs/3rdparty/libarchive/archive_string.h
new file mode 100644
index 000000000..49d7d3064
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_string_composition.h b/src/libs/3rdparty/libarchive/archive_string_composition.h
new file mode 100644
index 000000000..d0ac34096
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_string_sprintf.c b/src/libs/3rdparty/libarchive/archive_string_sprintf.c
new file mode 100644
index 000000000..969a5603a
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_util.c b/src/libs/3rdparty/libarchive/archive_util.c
new file mode 100644
index 000000000..40603c483
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_version_details.c b/src/libs/3rdparty/libarchive/archive_version_details.c
new file mode 100644
index 000000000..bfb20eab2
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_virtual.c b/src/libs/3rdparty/libarchive/archive_virtual.c
new file mode 100644
index 000000000..f509ee5c6
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_windows.c b/src/libs/3rdparty/libarchive/archive_windows.c
new file mode 100644
index 000000000..ebc5eefb8
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_windows.h b/src/libs/3rdparty/libarchive/archive_windows.h
new file mode 100644
index 000000000..47b7cb8e3
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write.c b/src/libs/3rdparty/libarchive/archive_write.c
new file mode 100644
index 000000000..ec3c95c56
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter.c b/src/libs/3rdparty/libarchive/archive_write_add_filter.c
new file mode 100644
index 000000000..203f4142b
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_b64encode.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_b64encode.c
new file mode 100644
index 000000000..87fdb73ec
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_by_name.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_by_name.c
new file mode 100644
index 000000000..ffa633c96
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_bzip2.c
new file mode 100644
index 000000000..3e5c0891a
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_compress.c
new file mode 100644
index 000000000..3ed269fce
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_grzip.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_grzip.c
new file mode 100644
index 000000000..371102d74
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_gzip.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_gzip.c
new file mode 100644
index 000000000..8670d5ca7
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_lrzip.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_lrzip.c
new file mode 100644
index 000000000..e215f8903
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_lz4.c
new file mode 100644
index 000000000..6ac450357
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_lzop.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_lzop.c
new file mode 100644
index 000000000..3bd9062e4
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#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/src/libs/3rdparty/libarchive/archive_write_add_filter_none.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_none.c
new file mode 100644
index 000000000..3c06c642e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_program.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_program.c
new file mode 100644
index 000000000..c096e7227
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_uuencode.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_uuencode.c
new file mode 100644
index 000000000..1ad458921
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_add_filter_xz.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_xz.c
new file mode 100644
index 000000000..04bee90ef
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c b/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c
new file mode 100644
index 000000000..584cfb668
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_write_add_filter_zstd.c
@@ -0,0 +1,467 @@
+/*-
+ * 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;
+#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 MINVER_NEGCLEVEL 10304
+#define MINVER_MINCLEVEL 10306
+
+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;
+#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
+ }
+
+ /* 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);
+
+ 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);
+ }
+
+ 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/src/libs/3rdparty/libarchive/archive_write_disk_posix.c b/src/libs/3rdparty/libarchive/archive_write_disk_posix.c
new file mode 100644
index 000000000..c8c2e1058
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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
+#include <linux/ext2_fs.h> /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#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 */
+#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
+#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/src/libs/3rdparty/libarchive/archive_write_disk_private.h b/src/libs/3rdparty/libarchive/archive_write_disk_private.h
new file mode 100644
index 000000000..557d7e2bf
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_disk_set_standard_lookup.c b/src/libs/3rdparty/libarchive/archive_write_disk_set_standard_lookup.c
new file mode 100644
index 000000000..5fccdb9dc
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_disk_windows.c b/src/libs/3rdparty/libarchive/archive_write_disk_windows.c
new file mode 100644
index 000000000..7b9ea7493
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_open_fd.c b/src/libs/3rdparty/libarchive/archive_write_open_fd.c
new file mode 100644
index 000000000..b8d491faa
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_open_file.c b/src/libs/3rdparty/libarchive/archive_write_open_file.c
new file mode 100644
index 000000000..bf5b55a67
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_open_filename.c b/src/libs/3rdparty/libarchive/archive_write_open_filename.c
new file mode 100644
index 000000000..9ceefb19b
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_open_memory.c b/src/libs/3rdparty/libarchive/archive_write_open_memory.c
new file mode 100644
index 000000000..a8a0b817f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_private.h b/src/libs/3rdparty/libarchive/archive_write_private.h
new file mode 100644
index 000000000..6522e6521
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format.c b/src/libs/3rdparty/libarchive/archive_write_set_format.c
new file mode 100644
index 000000000..1f65fa4a7
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c b/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c
new file mode 100644
index 000000000..1e40601c4
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_7zip.c
@@ -0,0 +1,2322 @@
+/*-
+ * 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
+#include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#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
+
+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;
+ if (file->dir)
+ attr = 0x8010;
+ else
+ attr = 0x8020;
+ if ((file->mode & 0222) == 0)
+ attr |= 1;/* Read Only. */
+ 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/src/libs/3rdparty/libarchive/archive_write_set_format_ar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_ar.c
new file mode 100644
index 000000000..fc0de1e9f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_by_name.c b/src/libs/3rdparty/libarchive/archive_write_set_format_by_name.c
new file mode 100644
index 000000000..bfb4b3545
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_cpio.c b/src/libs/3rdparty/libarchive/archive_write_set_format_cpio.c
new file mode 100644
index 000000000..47152cc6a
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_cpio_binary.c b/src/libs/3rdparty/libarchive/archive_write_set_format_cpio_binary.c
new file mode 100644
index 000000000..d6ce35a7b
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_cpio_newc.c b/src/libs/3rdparty/libarchive/archive_write_set_format_cpio_newc.c
new file mode 100644
index 000000000..f0f39809d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_cpio_odc.c b/src/libs/3rdparty/libarchive/archive_write_set_format_cpio_odc.c
new file mode 100644
index 000000000..091925a2f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_filter_by_ext.c b/src/libs/3rdparty/libarchive/archive_write_set_format_filter_by_ext.c
new file mode 100644
index 000000000..9fe21e454
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_gnutar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_gnutar.c
new file mode 100644
index 000000000..ec29c5c41
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c b/src/libs/3rdparty/libarchive/archive_write_set_format_iso9660.c
new file mode 100644
index 000000000..2a3ae07fa
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_mtree.c b/src/libs/3rdparty/libarchive/archive_write_set_format_mtree.c
new file mode 100644
index 000000000..619b7714e
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
new file mode 100644
index 000000000..c9c159164
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/archive_write_set_format_pax.c
@@ -0,0 +1,2063 @@
+/*-
+ * 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 (pax->flags & WRITE_LIBARCHIVE_XATTR) {
+ encoded_value = base64_encode((const char *)value, value_len);
+
+ if (encoded_name != NULL && 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) {
+ /* 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 (errno == ENOMEM) {
+ archive_set_error(&a->archive, ENOMEM,
+ "Can't allocate memory for Linkname");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ archive_write_pax_header_xattr(pax, encoded_name,
+ value, size);
+
+ }
+ return (ARCHIVE_OK);
+}
+
+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;
+ int out_len = 0;
+ char *out;
+
+ for (s = in; *s != '\0'; s++) {
+ if (*s < 33 || *s > 126 || *s == '%' || *s == '=')
+ out_len += 3;
+ else
+ 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/src/libs/3rdparty/libarchive/archive_write_set_format_private.h b/src/libs/3rdparty/libarchive/archive_write_set_format_private.h
new file mode 100644
index 000000000..e20022755
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_raw.c b/src/libs/3rdparty/libarchive/archive_write_set_format_raw.c
new file mode 100644
index 000000000..feff93697
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_shar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_shar.c
new file mode 100644
index 000000000..9e4931c95
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_ustar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_ustar.c
new file mode 100644
index 000000000..d1a06bc4f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_v7tar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_v7tar.c
new file mode 100644
index 000000000..599407144
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c b/src/libs/3rdparty/libarchive/archive_write_set_format_warc.c
new file mode 100644
index 000000000..0ef003e2f
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c b/src/libs/3rdparty/libarchive/archive_write_set_format_xar.c
new file mode 100644
index 000000000..7307757d3
--- /dev/null
+++ b/src/libs/3rdparty/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
+#include <libxml/xmlwriter.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#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/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c b/src/libs/3rdparty/libarchive/archive_write_set_format_zip.c
new file mode 100644
index 000000000..6821049c9
--- /dev/null
+++ b/src/libs/3rdparty/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
+#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/src/libs/3rdparty/libarchive/archive_write_set_options.c b/src/libs/3rdparty/libarchive/archive_write_set_options.c
new file mode 100644
index 000000000..962309ada
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_write_set_passphrase.c b/src/libs/3rdparty/libarchive/archive_write_set_passphrase.c
new file mode 100644
index 000000000..710ecba52
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/archive_xxhash.h b/src/libs/3rdparty/libarchive/archive_xxhash.h
new file mode 100644
index 000000000..1c7131ca1
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/config/linux/config.h b/src/libs/3rdparty/libarchive/config/linux/config.h
new file mode 100644
index 000000000..577704b50
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/config/linux/config.h
@@ -0,0 +1,1342 @@
+/* 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 SIZE_OF_SHORT 2
+#define SIZE_OF_INT 4
+#define SIZE_OF_LONG 8
+#define SIZE_OF_LONG_LONG 8
+#define SIZE_OF_UNSIGNED_SHORT 2
+#define SIZE_OF_UNSIGNED 4
+#define SIZE_OF_UNSIGNED_LONG 8
+#define SIZE_OF_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) && SIZE_OF_INT == 8
+typedef int int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8
+typedef long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZE_OF_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) && SIZE_OF_INT == 4
+typedef int int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T) && SIZE_OF_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) && SIZE_OF_INT == 2
+typedef int int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 8
+typedef unsigned uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8
+typedef unsigned long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 4
+typedef unsigned uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 2
+typedef unsigned uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T) && SIZE_OF_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_UINT16_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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */
+
+/* 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_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */
+
+/* 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 */
+#define ARCHIVE_XATTR_LINUX 1
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.5.1"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.5.1"
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.5.1"
+
+/* 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. */
+#define HAVE_EXPAT_H 1
+
+/* 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. */
+#define HAVE_FGETXATTR 1
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+#define HAVE_FLISTXATTR 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. */
+#define HAVE_FSETXATTR 1
+
+/* 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. */
+#define HAVE_FSTATVFS 1
+
+/* 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 `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. */
+#define HAVE_GETXATTR 1
+
+/* 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. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <iconv.h> header file. */
+#define HAVE_ICONV_H 1
+
+/* 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. */
+/* #undef HAVE_LCHMOD */
+
+/* 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. */
+#define HAVE_LGETXATTR 1
+
+/* 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). */
+/* #undef HAVE_LIBB2 */
+
+/* Define to 1 if you have the <blake2.h> header file. */
+/* #undef HAVE_BLAKE2_H */
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+#define HAVE_LIBEXPAT 1
+
+/* Define to 1 if you have the `gcc' library (-lgcc). */
+/* #undef HAVE_LIBGCC */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+#define HAVE_LIBLZMA 1
+
+/* 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). */
+/* #undef HAVE_LIBZSTD */
+
+/* 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 <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. */
+#define HAVE_LISTXATTR 1
+
+/* Define to 1 if you have the `llistea' function. */
+/* #undef HAVE_LLISTEA */
+
+/* Define to 1 if you have the `llistxattr' function. */
+#define HAVE_LLISTXATTR 1
+
+/* 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. */
+#define HAVE_LSETXATTR 1
+
+/* 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. */
+/* #undef HAVE_LZ4HC_H */
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* 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. */
+#define HAVE_LZMA_H 1
+
+/* 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 `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 <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. */
+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */
+
+/* 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 1
+
+/* 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. */
+#define HAVE_STATVFS 1
+
+/* 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 `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/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. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to 1 if you have the `_ctime64_s' function. */
+/* #undef HAVE__CTIME64_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 `_gmtime64_s' function. */
+/* #undef HAVE__GMTIME64_S */
+
+/* Define to 1 if you have the `_localtime64_s' function. */
+/* #undef HAVE__LOCALTIME64_S */
+
+/* Define to 1 if you have the `_mkgmtime64' function. */
+/* #undef HAVE__MKGMTIME64 */
+
+/* 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 "3005001"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.5.1"
+
+/* 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>. */
+#define TIME_WITH_SYS_TIME 1
+
+/*
+ * 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.5.1"
+
+/* 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 */
diff --git a/src/libs/3rdparty/libarchive/config/mac/config.h b/src/libs/3rdparty/libarchive/config/mac/config.h
new file mode 100644
index 000000000..d20c30769
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/config/mac/config.h
@@ -0,0 +1,1342 @@
+/* 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 SIZE_OF_SHORT 2
+#define SIZE_OF_INT 4
+#define SIZE_OF_LONG 8
+#define SIZE_OF_LONG_LONG 8
+#define SIZE_OF_UNSIGNED_SHORT 2
+#define SIZE_OF_UNSIGNED 4
+#define SIZE_OF_UNSIGNED_LONG 8
+#define SIZE_OF_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) && SIZE_OF_INT == 8
+typedef int int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8
+typedef long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZE_OF_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) && SIZE_OF_INT == 4
+typedef int int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T) && SIZE_OF_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) && SIZE_OF_INT == 2
+typedef int int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 8
+typedef unsigned uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8
+typedef unsigned long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 4
+typedef unsigned uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 2
+typedef unsigned uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T) && SIZE_OF_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_UINT16_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 */
+#define ARCHIVE_ACL_DARWIN 1
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */
+
+/* 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_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */
+
+/* AIX xattr support */
+/* #undef ARCHIVE_XATTR_AIX */
+
+/* Darwin xattr support */
+#define ARCHIVE_XATTR_DARWIN 1
+
+/* FreeBSD xattr support */
+/* #undef ARCHIVE_XATTR_FREEBSD */
+
+/* Linux xattr support */
+/* #undef ARCHIVE_XATTR_LINUX */
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.5.1"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.5.1"
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.5.1"
+
+/* Define to 1 if you have the `acl_create_entry' function. */
+#define HAVE_ACL_CREATE_ENTRY 1
+
+/* Define to 1 if you have the `acl_get_fd_np' function. */
+#define HAVE_ACL_GET_FD_NP 1
+
+/* 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. */
+#define HAVE_ACL_GET_LINK_NP 1
+
+/* 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. */
+#define HAVE_ACL_GET_PERM_NP 1
+
+/* Define to 1 if you have the `acl_init' function. */
+#define HAVE_ACL_INIT 1
+
+/* 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'. */
+#define HAVE_ACL_PERMSET_T 1
+
+/* Define to 1 if you have the `acl_set_fd' function. */
+#define HAVE_ACL_SET_FD 1
+
+/* Define to 1 if you have the `acl_set_fd_np' function. */
+#define HAVE_ACL_SET_FD_NP 1
+
+/* Define to 1 if you have the `acl_set_file' function. */
+#define HAVE_ACL_SET_FILE 1
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+#define HAVE_ARC4RANDOM_BUF 1
+
+/* 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. */
+#define HAVE_CHFLAGS 1
+
+/* 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. */
+#define HAVE_COPYFILE_H 1
+
+/* 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. */
+#define HAVE_DECL_ACL_SYNCHRONIZE 1
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+ you don't. */
+#define HAVE_DECL_ACL_TYPE_EXTENDED 1
+
+/* 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. */
+#define HAVE_DECL_XATTR_NOFOLLOW 1
+
+/* 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 */
+#define HAVE_D_MD_ORDER 1
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EFTYPE 1
+
+/* 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. */
+#define HAVE_EXPAT_H 1
+
+/* 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. */
+#define HAVE_FCHFLAGS 1
+
+/* 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. */
+#define HAVE_FGETXATTR 1
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+#define HAVE_FLISTXATTR 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. */
+#define HAVE_FSETXATTR 1
+
+/* 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. */
+#define HAVE_FSTATVFS 1
+
+/* 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. */
+/* #undef HAVE_FUTIMESAT */
+
+/* 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 `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. */
+#define HAVE_GETVFSBYNAME 1
+
+/* Define to 1 if you have the `getxattr' function. */
+#define HAVE_GETXATTR 1
+
+/* 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. */
+#define HAVE_ICONV 1
+
+/* Define to 1 if you have the <iconv.h> header file. */
+#define HAVE_ICONV_H 1
+
+/* 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. */
+#define HAVE_LCHFLAGS 1
+
+/* 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). */
+/* #undef HAVE_LIBB2 */
+
+/* Define to 1 if you have the <blake2.h> header file. */
+/* #undef HAVE_BLAKE2_H */
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+#define HAVE_LIBEXPAT 1
+
+/* Define to 1 if you have the `gcc' library (-lgcc). */
+/* #undef HAVE_LIBGCC */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+#define HAVE_LIBLZMA 1
+
+/* 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). */
+/* #undef HAVE_LIBZSTD */
+
+/* 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 <linux/fiemap.h> header file. */
+/* #undef HAVE_LINUX_FIEMAP_H */
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+/* #undef HAVE_LINUX_MAGIC_H */
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+/* #undef HAVE_LINUX_TYPES_H */
+
+/* Define to 1 if you have the `listea' function. */
+/* #undef HAVE_LISTEA */
+
+/* Define to 1 if you have the `listxattr' function. */
+#define HAVE_LISTXATTR 1
+
+/* 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. */
+#define HAVE_LOCALCHARSET_H 1
+
+/* Define to 1 if you have the `locale_charset' function. */
+#define HAVE_LOCALE_CHARSET 1
+
+/* 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. */
+/* #undef HAVE_LZ4HC_H */
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* 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. */
+#define HAVE_LZMA_H 1
+
+/* 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 `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the <membership.h> header file. */
+#define HAVE_MEMBERSHIP_H 1
+
+/* 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 <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. */
+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */
+
+/* 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 1
+
+/* 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. */
+#define HAVE_READPASSPHRASE 1
+
+/* Define to 1 if you have the <readpassphrase.h> header file. */
+#define HAVE_READPASSPHRASE_H 1
+
+/* 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. */
+#define HAVE_STATVFS 1
+
+/* 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 `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'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+
+/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+
+/* 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'. */
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+
+/* 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'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */
+
+/* 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'. */
+#define HAVE_STRUCT_VFSCONF 1
+
+/* 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. */
+#define HAVE_SYS_ACL_H 1
+
+/* 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. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* 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/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. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* 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. */
+/* #undef HAVE_SYS_SYSMACROS_H */
+
+/* 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. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* 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 */
+/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */
+
+/* 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. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to 1 if you have the `_ctime64_s' function. */
+/* #undef HAVE__CTIME64_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 `_gmtime64_s' function. */
+/* #undef HAVE__GMTIME64_S */
+
+/* Define to 1 if you have the `_localtime64_s' function. */
+/* #undef HAVE__LOCALTIME64_S */
+
+/* Define to 1 if you have the `_mkgmtime64' function. */
+/* #undef HAVE__MKGMTIME64 */
+
+/* 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 "3005001"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.5.1"
+
+/* 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>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* 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>. */
+#define TIME_WITH_SYS_TIME 1
+
+/*
+ * 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.5.1"
+
+/* 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 */
diff --git a/src/libs/3rdparty/libarchive/config/win/config.h b/src/libs/3rdparty/libarchive/config/win/config.h
new file mode 100644
index 000000000..51f2e917a
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/config/win/config.h
@@ -0,0 +1,1342 @@
+/* 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. */
+#define HAVE___INT64
+/* #undef HAVE_U_INT64_T */
+#define HAVE_UNSIGNED___INT64
+
+/* The sizes of various standard integer types. */
+#define SIZE_OF_SHORT 2
+#define SIZE_OF_INT 4
+#define SIZE_OF_LONG 4
+#define SIZE_OF_LONG_LONG 8
+#define SIZE_OF_UNSIGNED_SHORT 2
+#define SIZE_OF_UNSIGNED 4
+#define SIZE_OF_UNSIGNED_LONG 4
+#define SIZE_OF_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) && SIZE_OF_INT == 8
+typedef int int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZE_OF_LONG == 8
+typedef long int64_t;
+#define HAVE_INT64_T
+#endif
+
+#if !defined(HAVE_INT64_T) && SIZE_OF_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) && SIZE_OF_INT == 4
+typedef int int32_t;
+#define HAVE_INT32_T
+#endif
+
+#if !defined(HAVE_INT32_T) && SIZE_OF_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) && SIZE_OF_INT == 2
+typedef int int16_t;
+#define HAVE_INT16_T
+#endif
+
+#if !defined(HAVE_INT16_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 8
+typedef unsigned uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZE_OF_UNSIGNED_LONG == 8
+typedef unsigned long uint64_t;
+#define HAVE_UINT64_T
+#endif
+
+#if !defined(HAVE_UINT64_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 4
+typedef unsigned uint32_t;
+#define HAVE_UINT32_T
+#endif
+
+#if !defined(HAVE_UINT32_T) && SIZE_OF_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) && SIZE_OF_UNSIGNED == 2
+typedef unsigned uint16_t;
+#define HAVE_UINT16_T
+#endif
+
+#if !defined(HAVE_UINT16_T) && SIZE_OF_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_UINT16_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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */
+
+/* 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_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */
+
+/* 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_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */
+
+/* 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.5.1"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.5.1"
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.5.1"
+
+/* 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. */
+#define HAVE_BCRYPT_H 1
+
+/* 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. */
+/* #undef HAVE_CHOWN */
+
+/* Define to 1 if you have the `chroot' function. */
+/* #undef HAVE_CHROOT */
+
+/* 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. */
+/* #undef HAVE_CTIME_R */
+
+/* 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. */
+/* #undef HAVE_DECL_SSIZE_MAX */
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_STRERROR_R */
+
+/* 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. */
+#define HAVE_DIRECT_H 1
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_DIRENT_H */
+
+/* Define to 1 if you have the `dirfd' function. */
+/* #undef HAVE_DIRFD */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* 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. */
+/* #undef HAVE_FCHDIR */
+
+/* Define to 1 if you have the `fchflags' function. */
+/* #undef HAVE_FCHFLAGS */
+
+/* Define to 1 if you have the `fchmod' function. */
+/* #undef HAVE_FCHMOD */
+
+/* Define to 1 if you have the `fchown' function. */
+/* #undef HAVE_FCHOWN */
+
+/* Define to 1 if you have the `fcntl' function. */
+/* #undef HAVE_FCNTL */
+
+/* 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. */
+/* #undef HAVE_FDOPENDIR */
+
+/* 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 `fork' function. */
+/* #undef HAVE_FORK */
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+/* #undef HAVE_FSEEKO */
+
+/* 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. */
+/* #undef HAVE_FSTATAT */
+
+/* Define to 1 if you have the `fstatfs' function. */
+/* #undef HAVE_FSTATFS */
+
+/* Define to 1 if you have the `fstatvfs' function. */
+/* #undef HAVE_FSTATVFS */
+
+/* Define to 1 if you have the `ftruncate' function. */
+/* #undef HAVE_FTRUNCATE */
+
+/* Define to 1 if you have the `futimens' function. */
+/* #undef HAVE_FUTIMENS */
+
+/* Define to 1 if you have the `futimes' function. */
+/* #undef HAVE_FUTIMES */
+
+/* Define to 1 if you have the `futimesat' function. */
+/* #undef HAVE_FUTIMESAT */
+
+/* Define to 1 if you have the `getea' function. */
+/* #undef HAVE_GETEA */
+
+/* Define to 1 if you have the `geteuid' function. */
+/* #undef HAVE_GETEUID */
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+/* #undef HAVE_GETGRGID_R */
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+/* #undef HAVE_GETGRNAM_R */
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+/* #undef HAVE_GETPWNAM_R */
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+/* #undef HAVE_GETPWUID_R */
+
+/* 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. */
+/* #undef HAVE_GMTIME_R */
+
+/* Define to 1 if you have the <grp.h> header file. */
+/* #undef HAVE_GRP_H */
+
+/* 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. */
+#define HAVE_IO_H 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+/* #undef HAVE_LANGINFO_H */
+
+/* Define to 1 if you have the `lchflags' function. */
+/* #undef HAVE_LCHFLAGS */
+
+/* Define to 1 if you have the `lchmod' function. */
+/* #undef HAVE_LCHMOD */
+
+/* Define to 1 if you have the `lchown' function. */
+/* #undef HAVE_LCHOWN */
+
+/* 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). */
+/* #undef HAVE_LIBB2 */
+
+/* Define to 1 if you have the <blake2.h> header file. */
+/* #undef HAVE_BLAKE2_H */
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* 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). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+#define HAVE_LIBLZMA 1
+
+/* 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). */
+/* #undef HAVE_LIBZSTD */
+
+/* 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. */
+/* #undef HAVE_LINK */
+
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+/* #undef HAVE_LINUX_FIEMAP_H */
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+/* #undef HAVE_LINUX_MAGIC_H */
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+/* #undef HAVE_LINUX_TYPES_H */
+
+/* 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. */
+/* #undef HAVE_LOCALTIME_R */
+
+/* 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. */
+/* #undef HAVE_LSTAT */
+
+/* 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. */
+/* #undef HAVE_LUTIMES */
+
+/* Define to 1 if you have the <lz4hc.h> header file. */
+/* #undef HAVE_LZ4HC_H */
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* 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. */
+#define HAVE_LZMA_H 1
+
+/* 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 `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. */
+/* #undef HAVE_MKFIFO */
+
+/* Define to 1 if you have the `mknod' function. */
+/* #undef HAVE_MKNOD */
+
+/* Define to 1 if you have the `mkstemp' function. */
+/* #undef HAVE_MKSTEMP */
+
+/* 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. */
+/* #undef HAVE_NL_LANGINFO */
+
+/* Define to 1 if you have the `openat' function. */
+/* #undef HAVE_OPENAT */
+
+/* Define to 1 if you have the <paths.h> header file. */
+/* #undef HAVE_PATHS_H */
+
+/* Define to 1 if you have the <pcreposix.h> header file. */
+/* #undef HAVE_PCREPOSIX_H */
+
+/* Define to 1 if you have the `pipe' function. */
+/* #undef HAVE_PIPE */
+
+/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */
+
+/* Define to 1 if you have the `poll' function. */
+/* #undef HAVE_POLL */
+
+/* Define to 1 if you have the <poll.h> header file. */
+/* #undef HAVE_POLL_H */
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+/* #undef HAVE_POSIX_SPAWNP */
+
+/* Define to 1 if you have the <process.h> header file. */
+#define HAVE_PROCESS_H 1
+
+/* Define to 1 if you have the <pthread.h> header file. */
+/* #undef HAVE_PTHREAD_H */
+
+/* Define to 1 if you have the <pwd.h> header file. */
+/* #undef HAVE_PWD_H */
+
+/* Define to 1 if you have the `readdir_r' function. */
+/* #undef HAVE_READDIR_R */
+
+/* Define to 1 if you have the `readlink' function. */
+/* #undef HAVE_READLINK */
+
+/* Define to 1 if you have the `readlinkat' function. */
+/* #undef HAVE_READLINKAT */
+
+/* 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. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if you have the `select' function. */
+/* #undef HAVE_SELECT */
+
+/* Define to 1 if you have the `setenv' function. */
+/* #undef HAVE_SETENV */
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `sigaction' function. */
+/* #undef HAVE_SIGACTION */
+
+/* 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. */
+/* #undef HAVE_SPAWN_H */
+
+/* Define to 1 if you have the `statfs' function. */
+/* #undef HAVE_STATFS */
+
+/* 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. */
+/* #undef HAVE_STRERROR_R */
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* 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. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* 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'. */
+/* #undef HAVE_STRUCT_STAT_ST_BLKSIZE */
+
+/* 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'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */
+
+/* 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'. */
+/* #undef HAVE_STRUCT_TM_TM_GMTOFF */
+
+/* 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. */
+/* #undef HAVE_SYMLINK */
+
+/* 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. */
+/* #undef HAVE_SYS_CDEFS_H */
+
+/* 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. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* 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. */
+/* #undef HAVE_SYS_PARAM_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* 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. */
+/* #undef HAVE_SYS_SELECT_H */
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+/* #undef HAVE_SYS_STATVFS_H */
+
+/* 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. */
+/* #undef HAVE_SYS_SYSMACROS_H */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+/* #undef HAVE_SYS_TIME_H */
+
+/* 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. */
+#define HAVE_SYS_UTIME_H 1
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+/* #undef HAVE_SYS_UTSNAME_H */
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+/* #undef HAVE_SYS_WAIT_H */
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+/* #undef HAVE_SYS_XATTR_H */
+
+/* Define to 1 if you have the `timegm' function. */
+/* #undef HAVE_TIMEGM */
+
+/* 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. */
+/* #undef HAVE_UNISTD_H */
+
+/* Define to 1 if you have the `unlinkat' function. */
+/* #undef HAVE_UNLINKAT */
+
+/* Define to 1 if you have the `unsetenv' function. */
+/* #undef HAVE_UNSETENV */
+
+/* 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. */
+/* #undef HAVE_UTIMENSAT */
+
+/* Define to 1 if you have the `utimes' function. */
+/* #undef HAVE_UTIMES */
+
+/* Define to 1 if you have the <utime.h> header file. */
+/* #undef HAVE_UTIME_H */
+
+/* Define to 1 if you have the `vfork' function. */
+/* #undef HAVE_VFORK */
+
+/* Define to 1 if you have the `vprintf' function. */
+/* #undef HAVE_VPRINTF */
+
+/* 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. */
+#define HAVE_WINCRYPT_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+#define HAVE_WINDOWS_H 1
+
+/* Define to 1 if you have the <winioctl.h> header file. */
+#define HAVE_WINIOCTL_H 1
+
+/* Define to 1 if you have _CrtSetReportMode in <crtdbg.h> */
+#define HAVE__CrtSetReportMode 1
+
+/* Define to 1 if you have the `wmemcmp' function. */
+/* #undef HAVE_WMEMCMP */
+
+/* Define to 1 if you have the `wmemcpy' function. */
+/* #undef HAVE_WMEMCPY */
+
+/* Define to 1 if you have the `wmemmove' function. */
+/* #undef HAVE_WMEMMOVE */
+
+/* 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 */
+/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */
+
+/* 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. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to 1 if you have the `_ctime64_s' function. */
+#define HAVE__CTIME64_S 1
+
+/* Define to 1 if you have the `_fseeki64' function. */
+#define HAVE__FSEEKI64 1
+
+/* Define to 1 if you have the `_get_timezone' function. */
+#define HAVE__GET_TIMEZONE 1
+
+/* Define to 1 if you have the `_gmtime64_s' function. */
+#define HAVE__GMTIME64_S 1
+
+/* Define to 1 if you have the `_localtime64_s' function. */
+#define HAVE__LOCALTIME64_S 1
+
+/* Define to 1 if you have the `_mkgmtime64' function. */
+#define HAVE__MKGMTIME64 1
+
+/* 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 "3005001"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.5.1"
+
+/* 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>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* 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 2
+
+/* 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.5.1"
+
+/* 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
+#define NTDDI_VERSION 0x06010000
+#endif // NTDDI_VERSION
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601
+#endif // _WIN32_WINNT
+
+#ifndef WINVER
+#define WINVER 0x0601
+#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. */
+#define gid_t short
+
+/* Define to `unsigned long' if <sys/types.h> does not define. */
+#define id_t short
+
+/* Define to `int' if <sys/types.h> does not define. */
+#define mode_t unsigned short
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#define pid_t int
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+#define ssize_t int64_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#define uid_t short
+
+/* 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 */
diff --git a/src/libs/3rdparty/libarchive/config_freebsd.h b/src/libs/3rdparty/libarchive/config_freebsd.h
new file mode 100644
index 000000000..669f27246
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/config_freebsd.h
@@ -0,0 +1,276 @@
+/*-
+ * 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$
+ */
+#define __LIBARCHIVE_CONFIG_H_INCLUDED 1
+
+#include <osreldate.h>
+
+/* FreeBSD 5.0 and later has ACL and extattr support. */
+#if __FreeBSD__ > 4
+#define ARCHIVE_ACL_FREEBSD 1
+#define HAVE_ACL_GET_PERM_NP 1
+#define HAVE_ARC4RANDOM_BUF 1
+#define HAVE_EXTATTR_GET_FILE 1
+#define HAVE_EXTATTR_LIST_FILE 1
+#define HAVE_EXTATTR_SET_FD 1
+#define HAVE_EXTATTR_SET_FILE 1
+#define HAVE_STRUCT_XVFSCONF 1
+#define HAVE_SYS_ACL_H 1
+#define HAVE_SYS_EXTATTR_H 1
+#if __FreeBSD__ > 7
+/* FreeBSD 8.0 and later has NFSv4 ACL support */
+#define ARCHIVE_ACL_FREEBSD_NFS4 1
+#define HAVE_ACL_GET_LINK_NP 1
+#define HAVE_ACL_IS_TRIVIAL_NP 1
+#define HAVE_ACL_SET_LINK_NP 1
+#endif /* __FreeBSD__ > 7 */
+#endif /* __FreeBSD__ > 4 */
+
+#ifdef WITH_OPENSSL
+#define HAVE_LIBCRYPTO 1
+#define HAVE_OPENSSL_EVP_H 1
+#define HAVE_OPENSSL_MD5_H 1
+#define HAVE_OPENSSL_RIPEMD_H 1
+#define HAVE_OPENSSL_SHA_H 1
+#define HAVE_OPENSSL_SHA256_INIT 1
+#define HAVE_OPENSSL_SHA384_INIT 1
+#define HAVE_OPENSSL_SHA512_INIT 1
+#define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1
+#define HAVE_SHA256 1
+#define HAVE_SHA384 1
+#define HAVE_SHA512 1
+#else
+#define HAVE_LIBMD 1
+#define HAVE_MD5_H 1
+#define HAVE_MD5INIT 1
+#define HAVE_RIPEMD_H 1
+#define HAVE_SHA_H 1
+#define HAVE_SHA1 1
+#define HAVE_SHA1_INIT 1
+#define HAVE_SHA256 1
+#define HAVE_SHA256_H 1
+#define HAVE_SHA256_INIT 1
+#define HAVE_SHA512 1
+#define HAVE_SHA512_H 1
+#define HAVE_SHA512_INIT 1
+#endif
+
+#define HAVE_BSDXML_H 1
+#define HAVE_BZLIB_H 1
+#define HAVE_CHFLAGS 1
+#define HAVE_CHOWN 1
+#define HAVE_CHROOT 1
+#define HAVE_CTIME_R 1
+#define HAVE_CTYPE_H 1
+#define HAVE_DECL_EXTATTR_NAMESPACE_USER 1
+#define HAVE_DECL_INT32_MAX 1
+#define HAVE_DECL_INT32_MIN 1
+#define HAVE_DECL_INT64_MAX 1
+#define HAVE_DECL_INT64_MIN 1
+#define HAVE_DECL_INTMAX_MAX 1
+#define HAVE_DECL_INTMAX_MIN 1
+#define HAVE_DECL_SIZE_MAX 1
+#define HAVE_DECL_SSIZE_MAX 1
+#define HAVE_DECL_STRERROR_R 1
+#define HAVE_DECL_UINT32_MAX 1
+#define HAVE_DECL_UINT64_MAX 1
+#define HAVE_DECL_UINTMAX_MAX 1
+#define HAVE_DIRENT_H 1
+#define HAVE_DLFCN_H 1
+#define HAVE_D_MD_ORDER 1
+#define HAVE_EFTYPE 1
+#define HAVE_EILSEQ 1
+#define HAVE_ERRNO_H 1
+#define HAVE_FCHDIR 1
+#define HAVE_FCHFLAGS 1
+#define HAVE_FCHMOD 1
+#define HAVE_FCHOWN 1
+#define HAVE_FCNTL 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FDOPENDIR 1
+#define HAVE_FNMATCH 1
+#define HAVE_FNMATCH_H 1
+#define HAVE_FORK 1
+#define HAVE_FSEEKO 1
+#define HAVE_FSTAT 1
+#define HAVE_FSTATAT 1
+#define HAVE_FSTATFS 1
+#define HAVE_FSTATVFS 1
+#define HAVE_FTRUNCATE 1
+#define HAVE_FUTIMES 1
+#define HAVE_FUTIMESAT 1
+#define HAVE_GETEUID 1
+#define HAVE_GETGRGID_R 1
+#define HAVE_GETGRNAM_R 1
+#define HAVE_GETLINE 1
+#define HAVE_GETOPT_OPTRESET 1
+#define HAVE_GETPID 1
+#define HAVE_GETPWNAM_R 1
+#define HAVE_GETPWUID_R 1
+#define HAVE_GETVFSBYNAME 1
+#define HAVE_GMTIME_R 1
+#define HAVE_GRP_H 1
+#define HAVE_INTMAX_T 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LANGINFO_H 1
+#define HAVE_LCHFLAGS 1
+#define HAVE_LCHMOD 1
+#define HAVE_LCHOWN 1
+#define HAVE_LIBZ 1
+#define HAVE_LIMITS_H 1
+#define HAVE_LINK 1
+#define HAVE_LINKAT 1
+#define HAVE_LOCALE_H 1
+#define HAVE_LOCALTIME_R 1
+#define HAVE_LONG_LONG_INT 1
+#define HAVE_LSTAT 1
+#define HAVE_LUTIMES 1
+#define HAVE_MBRTOWC 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MEMORY_H 1
+#define HAVE_MEMSET 1
+#define HAVE_MKDIR 1
+#define HAVE_MKFIFO 1
+#define HAVE_MKNOD 1
+#define HAVE_MKSTEMP 1
+#define HAVE_NL_LANGINFO 1
+#define HAVE_OPENAT 1
+#define HAVE_PATHS_H 1
+#define HAVE_PIPE 1
+#define HAVE_POLL 1
+#define HAVE_POLL_H 1
+#define HAVE_POSIX_SPAWNP 1
+#define HAVE_PTHREAD_H 1
+#define HAVE_PWD_H 1
+#define HAVE_READDIR_R 1
+#define HAVE_READLINK 1
+#define HAVE_READLINKAT 1
+#define HAVE_READPASSPHRASE 1
+#define HAVE_READPASSPHRASE_H 1
+#define HAVE_REGEX_H 1
+#define HAVE_SELECT 1
+#define HAVE_SETENV 1
+#define HAVE_SETLOCALE 1
+#define HAVE_SIGACTION 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SPAWN_H 1
+#define HAVE_STATFS 1
+#define HAVE_STATVFS 1
+#define HAVE_STDARG_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRCHR 1
+#define HAVE_STRDUP 1
+#define HAVE_STRERROR 1
+#define HAVE_STRERROR_R 1
+#define HAVE_STRFTIME 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRNLEN 1
+#define HAVE_STRRCHR 1
+#define HAVE_STRUCT_STATFS_F_NAMEMAX 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+#define HAVE_SYMLINK 1
+#define HAVE_SYS_CDEFS_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_POLL_H 1
+#define HAVE_SYS_QUEUE_H 1
+#define HAVE_SYS_SELECT_H 1
+#define HAVE_SYS_STATVFS_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_UTSNAME_H 1
+#define HAVE_SYS_WAIT_H 1
+#define HAVE_TIMEGM 1
+#define HAVE_TIME_H 1
+#define HAVE_TZSET 1
+#define HAVE_UINTMAX_T 1
+#define HAVE_UNISTD_H 1
+#define HAVE_UNLINKAT 1
+#define HAVE_UNSETENV 1
+#define HAVE_UNSIGNED_LONG_LONG 1
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+#define HAVE_UTIME 1
+#define HAVE_UTIMES 1
+#define HAVE_UTIME_H 1
+#define HAVE_VFORK 1
+#define HAVE_VPRINTF 1
+#define HAVE_WCHAR_H 1
+#define HAVE_WCHAR_T 1
+#define HAVE_WCRTOMB 1
+#define HAVE_WCSCMP 1
+#define HAVE_WCSCPY 1
+#define HAVE_WCSLEN 1
+#define HAVE_WCTOMB 1
+#define HAVE_WCTYPE_H 1
+#define HAVE_WMEMCMP 1
+#define HAVE_WMEMCPY 1
+#define HAVE_WMEMMOVE 1
+#define HAVE_ZLIB_H 1
+#define HAVE_SYS_TIME_H 1
+
+#if __FreeBSD_version >= 800505
+#define HAVE_LIBLZMA 1
+#define HAVE_LZMA_H 1
+#if __FreeBSD_version >= 1002504
+#define HAVE_LZMA_STREAM_ENCODER_MT 1
+#endif
+#endif
+
+#if __FreeBSD_version >= 1100056
+#define HAVE_FUTIMENS 1
+#define HAVE_UTIMENSAT 1
+#endif
+
+/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */
+#if __FreeBSD__ < 5
+#define intmax_t int64_t
+#define uintmax_t uint64_t
+#endif
+
+/* FreeBSD defines for archive_hash.h */
+#ifdef WITH_OPENSSL
+#define ARCHIVE_CRYPTO_MD5_OPENSSL 1
+#define ARCHIVE_CRYPTO_RMD160_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA1_OPENSSL
+#define ARCHIVE_CRYPTO_SHA256_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA384_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA512_OPENSSL 1
+#else
+#define ARCHIVE_CRYPTO_MD5_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA1_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA256_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA512_LIBMD 1
+#endif
diff --git a/src/libs/3rdparty/libarchive/filter_fork.h b/src/libs/3rdparty/libarchive/filter_fork.h
new file mode 100644
index 000000000..2bf290c4d
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/filter_fork_posix.c b/src/libs/3rdparty/libarchive/filter_fork_posix.c
new file mode 100644
index 000000000..62085a709
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/filter_fork_windows.c b/src/libs/3rdparty/libarchive/filter_fork_windows.c
new file mode 100644
index 000000000..9e49c5655
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/3rdparty/libarchive/libarchive.pro b/src/libs/3rdparty/libarchive/libarchive.pro
new file mode 100644
index 000000000..0cf6f3097
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/libarchive.pro
@@ -0,0 +1,197 @@
+TEMPLATE = lib
+TARGET = libarchive
+INCLUDEPATH += . ..
+
+CONFIG += staticlib
+
+include(../../../../installerfw.pri)
+
+DESTDIR = $$IFW_LIB_PATH
+
+DEFINES += HAVE_CONFIG_H
+
+HEADERS += $$PWD/archive.h \
+ $$PWD/archive_entry.h \
+ $$PWD/archive_acl_private.h \
+ $$PWD/archive_cmdline_private.h \
+ $$PWD/archive_crc32.h \
+ $$PWD/archive_cryptor_private.h \
+ $$PWD/archive_digest_private.h \
+ $$PWD/archive_endian.h \
+ $$PWD/archive_entry_locale.h \
+ $$PWD/archive_entry_private.h \
+ $$PWD/archive_getdate.h \
+ $$PWD/archive_hmac_private.h \
+ $$PWD/archive_openssl_evp_private.h \
+ $$PWD/archive_openssl_hmac_private.h \
+ $$PWD/archive_options_private.h \
+ $$PWD/archive_pack_dev.h \
+ $$PWD/archive_pathmatch.h \
+ $$PWD/archive_platform.h \
+ $$PWD/archive_platform_acl.h \
+ $$PWD/archive_platform_xattr.h \
+ $$PWD/archive_ppmd_private.h \
+ $$PWD/archive_ppmd8_private.h \
+ $$PWD/archive_ppmd7_private.h \
+ $$PWD/archive_private.h \
+ $$PWD/archive_random_private.h \
+ $$PWD/archive_rb.h \
+ $$PWD/archive_read_disk_private.h \
+ $$PWD/archive_read_private.h \
+ $$PWD/archive_string.h \
+ $$PWD/archive_string_composition.h \
+ $$PWD/archive_write_disk_private.h \
+ $$PWD/archive_write_private.h \
+ $$PWD/archive_write_set_format_private.h \
+ $$PWD/archive_xxhash.h \
+ $$PWD/filter_fork.h
+
+SOURCES += $$PWD/archive_acl.c \
+ $$PWD/archive_check_magic.c \
+ $$PWD/archive_cmdline.c \
+ $$PWD/archive_cryptor.c \
+ $$PWD/archive_digest.c \
+ $$PWD/archive_entry.c \
+ $$PWD/archive_entry_copy_stat.c \
+ $$PWD/archive_entry_link_resolver.c \
+ $$PWD/archive_entry_sparse.c \
+ $$PWD/archive_entry_stat.c \
+ $$PWD/archive_entry_strmode.c \
+ $$PWD/archive_entry_xattr.c \
+ $$PWD/archive_getdate.c \
+ $$PWD/archive_hmac.c \
+ $$PWD/archive_match.c \
+ $$PWD/archive_options.c \
+ $$PWD/archive_pack_dev.c \
+ $$PWD/archive_pathmatch.c \
+ $$PWD/archive_ppmd8.c \
+ $$PWD/archive_ppmd7.c \
+ $$PWD/archive_random.c \
+ $$PWD/archive_rb.c \
+ $$PWD/archive_read.c \
+ $$PWD/archive_read_add_passphrase.c \
+ $$PWD/archive_read_append_filter.c \
+ $$PWD/archive_read_data_into_fd.c \
+ $$PWD/archive_read_disk_entry_from_file.c \
+ $$PWD/archive_read_disk_posix.c \
+ $$PWD/archive_read_disk_set_standard_lookup.c \
+ $$PWD/archive_read_extract.c \
+ $$PWD/archive_read_extract2.c \
+ $$PWD/archive_read_open_fd.c \
+ $$PWD/archive_read_open_file.c \
+ $$PWD/archive_read_open_filename.c \
+ $$PWD/archive_read_open_memory.c \
+ $$PWD/archive_read_set_format.c \
+ $$PWD/archive_read_set_options.c \
+ $$PWD/archive_read_support_filter_all.c \
+ $$PWD/archive_read_support_filter_bzip2.c \
+ $$PWD/archive_read_support_filter_compress.c \
+ $$PWD/archive_read_support_filter_gzip.c \
+ $$PWD/archive_read_support_filter_grzip.c \
+ $$PWD/archive_read_support_filter_lrzip.c \
+ $$PWD/archive_read_support_filter_lz4.c \
+ $$PWD/archive_read_support_filter_lzop.c \
+ $$PWD/archive_read_support_filter_none.c \
+ $$PWD/archive_read_support_filter_program.c \
+ $$PWD/archive_read_support_filter_rpm.c \
+ $$PWD/archive_read_support_filter_uu.c \
+ $$PWD/archive_read_support_filter_xz.c \
+ $$PWD/archive_read_support_filter_zstd.c \
+ $$PWD/archive_read_support_format_7zip.c \
+ $$PWD/archive_read_support_format_all.c \
+ $$PWD/archive_read_support_format_ar.c \
+ $$PWD/archive_read_support_format_by_code.c \
+ $$PWD/archive_read_support_format_cab.c \
+ $$PWD/archive_read_support_format_cpio.c \
+ $$PWD/archive_read_support_format_empty.c \
+ $$PWD/archive_read_support_format_iso9660.c \
+ $$PWD/archive_read_support_format_lha.c \
+ $$PWD/archive_read_support_format_mtree.c \
+ $$PWD/archive_read_support_format_rar.c \
+ $$PWD/archive_read_support_format_rar5.c \
+ $$PWD/archive_read_support_format_raw.c \
+ $$PWD/archive_read_support_format_tar.c \
+ $$PWD/archive_read_support_format_warc.c \
+ $$PWD/archive_read_support_format_xar.c \
+ $$PWD/archive_read_support_format_zip.c \
+ $$PWD/archive_string.c \
+ $$PWD/archive_string_sprintf.c \
+ $$PWD/archive_util.c \
+ $$PWD/archive_version_details.c \
+ $$PWD/archive_virtual.c \
+ $$PWD/archive_write.c \
+ $$PWD/archive_write_disk_posix.c \
+ $$PWD/archive_write_disk_set_standard_lookup.c \
+ $$PWD/archive_write_open_fd.c \
+ $$PWD/archive_write_open_file.c \
+ $$PWD/archive_write_open_filename.c \
+ $$PWD/archive_write_open_memory.c \
+ $$PWD/archive_write_add_filter.c \
+ $$PWD/archive_write_add_filter_b64encode.c \
+ $$PWD/archive_write_add_filter_by_name.c \
+ $$PWD/archive_write_add_filter_bzip2.c \
+ $$PWD/archive_write_add_filter_compress.c \
+ $$PWD/archive_write_add_filter_grzip.c \
+ $$PWD/archive_write_add_filter_gzip.c \
+ $$PWD/archive_write_add_filter_lrzip.c \
+ $$PWD/archive_write_add_filter_lz4.c \
+ $$PWD/archive_write_add_filter_lzop.c \
+ $$PWD/archive_write_add_filter_none.c \
+ $$PWD/archive_write_add_filter_program.c \
+ $$PWD/archive_write_add_filter_uuencode.c \
+ $$PWD/archive_write_add_filter_xz.c \
+ $$PWD/archive_write_add_filter_zstd.c \
+ $$PWD/archive_write_set_format.c \
+ $$PWD/archive_write_set_format_7zip.c \
+ $$PWD/archive_write_set_format_ar.c \
+ $$PWD/archive_write_set_format_by_name.c \
+ $$PWD/archive_write_set_format_cpio.c \
+ $$PWD/archive_write_set_format_cpio_binary.c \
+ $$PWD/archive_write_set_format_cpio_newc.c \
+ $$PWD/archive_write_set_format_cpio_odc.c \
+ $$PWD/archive_write_set_format_filter_by_ext.c \
+ $$PWD/archive_write_set_format_gnutar.c \
+ $$PWD/archive_write_set_format_iso9660.c \
+ $$PWD/archive_write_set_format_mtree.c \
+ $$PWD/archive_write_set_format_pax.c \
+ $$PWD/archive_write_set_format_raw.c \
+ $$PWD/archive_write_set_format_shar.c \
+ $$PWD/archive_write_set_format_ustar.c \
+ $$PWD/archive_write_set_format_v7tar.c \
+ $$PWD/archive_write_set_format_warc.c \
+ $$PWD/archive_write_set_format_xar.c \
+ $$PWD/archive_write_set_format_zip.c \
+ $$PWD/archive_write_set_options.c \
+ $$PWD/archive_write_set_passphrase.c \
+ $$PWD/filter_fork_posix.c \
+ $$PWD/xxhash.c
+
+if (isEmpty(IFW_ZLIB_LIBRARY):contains(QT_MODULES, zlib)) {
+ INCLUDEPATH += $$[QT_INSTALL_HEADERS]/QtZlib
+}
+
+linux {
+ INCLUDEPATH += ./config/linux
+ HEADERS += $$PWD/config/linux/config.h
+ SOURCES += $$PWD/archive_disk_acl_linux.c
+}
+
+macx {
+ INCLUDEPATH += ./config/mac
+ HEADERS += $$PWD/config/mac/config.h
+ SOURCES += $$PWD/archive_disk_acl_darwin.c
+}
+
+win32:!cygwin-g++ {
+ INCLUDEPATH += ./config/win
+ HEADERS += $$PWD/config/win/config.h \
+ $$PWD/archive_windows.h
+ SOURCES += $$PWD/archive_entry_copy_bhfi.c \
+ $$PWD/archive_read_disk_windows.c \
+ $$PWD/archive_windows.c \
+ $$PWD/archive_write_disk_windows.c \
+ $$PWD/filter_fork_windows.c
+}
+
+target.path = $$[QT_INSTALL_LIBS]
+INSTALLS += target
diff --git a/src/libs/3rdparty/libarchive/qt_attribution.json b/src/libs/3rdparty/libarchive/qt_attribution.json
new file mode 100644
index 000000000..76db5e26a
--- /dev/null
+++ b/src/libs/3rdparty/libarchive/qt_attribution.json
@@ -0,0 +1,13 @@
+{
+ "Id": "libarchive",
+ "Name": "libarchive",
+ "QDocModule": "ifw",
+ "Description": "Multi-format archive and compression library.",
+ "QtUsage": "Used for reading and writing archive files in Qt Installer Framework",
+ "Homepage": "https://www.libarchive.org",
+ "Version": "3.7.1",
+ "License": "BSD 2-clause \"Simplified\" License",
+ "LicenseId": "BSD-2-Clause",
+ "LicenseFile": "COPYING",
+ "Copyright": "Copyright (c) 2003-2018 Tim Kientzle"
+}
diff --git a/src/libs/3rdparty/libarchive/xxhash.c b/src/libs/3rdparty/libarchive/xxhash.c
new file mode 100644
index 000000000..beacd2391
--- /dev/null
+++ b/src/libs/3rdparty/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/src/libs/7zip/patches/0001-Adjust-7z-and-p7z.patch b/src/libs/7zip/patches/0001-Adjust-7z-and-p7z.patch
deleted file mode 100644
index 822deb07f..000000000
--- a/src/libs/7zip/patches/0001-Adjust-7z-and-p7z.patch
+++ /dev/null
@@ -1,517 +0,0 @@
-From f643c01e4e8534f26a5a2d260caa566d23cdcb13 Mon Sep 17 00:00:00 2001
-From: Karsten Heimrich <karsten.heimrich@theqtcompany.com>
-Date: Thu, 4 Jun 2015 15:41:51 +0200
-Subject: [PATCH 1/1] Adjust 7z and p7z.
-
-Change-Id: I3b96d2b02e5a0908fb4cf5b4262cb33516a10098
----
- src/libs/7zip/7zip.pri | 11 ++++-
- src/libs/7zip/7zip.pro | 14 +-----
- src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp | 2 -
- src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h | 32 ++++++++++---
- src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h | 29 ++++++++---
- src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp | 2 -
- .../unix/CPP/include_windows/include_windows.pri | 3 ++
- .../unix/CPP/myWindows/myCommandLineParser.cpp | 56 ++++++++++++++++++++++
- src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp | 42 ++++++++--------
- src/libs/7zip/unix/CPP/myWindows/myWindows.pri | 7 +++
- src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp | 2 -
- src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h | 32 ++++++++++---
- src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h | 29 ++++++++---
- src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp | 2 -
- 14 files changed, 194 insertions(+), 69 deletions(-)
- create mode 100644 src/libs/7zip/unix/CPP/include_windows/include_windows.pri
- create mode 100644 src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
- create mode 100644 src/libs/7zip/unix/CPP/myWindows/myWindows.pri
-
-diff --git a/src/libs/7zip/7zip.pri b/src/libs/7zip/7zip.pri
-index 823e3ab..85574ce 100644
---- a/src/libs/7zip/7zip.pri
-+++ b/src/libs/7zip/7zip.pri
-@@ -1,7 +1,11 @@
-+DEFINES += _UNICODE _NO_CRYPTO
-+
- win32 {
- 7ZIP_BASE=$$PWD/win
- INCLUDEPATH += $$7ZIP_BASE/C $$7ZIP_BASE/CPP
-- DEFINES += WIN_LONG_PATH _UNICODE _NO_CRYPTO
-+ DEFINES += WIN_LONG_PATH _CRT_SECURE_NO_WARNINGS
-+ win32-g++*:QMAKE_CXXFLAGS += -w -fvisibility=hidden
-+
- QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings
- QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
- }
-@@ -14,6 +18,9 @@ unix {
- $$7ZIP_BASE/CPP/myWindows \
- $$7ZIP_BASE/CPP/include_windows
-
-+ QMAKE_CFLAGS += -w
-+ QMAKE_CXXFLAGS += -fvisibility=hidden -w
-+
- macx:DEFINES += ENV_MACOSX
-- DEFINES += _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE NDEBUG _REENTRANT ENV_UNIX UNICODE _UNICODE _NO_CRYPTO
-+ DEFINES += _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE NDEBUG _REENTRANT ENV_UNIX UNICODE
- }
-diff --git a/src/libs/7zip/7zip.pro b/src/libs/7zip/7zip.pro
-index 01b69da..70a51c0 100644
---- a/src/libs/7zip/7zip.pro
-+++ b/src/libs/7zip/7zip.pro
-@@ -7,15 +7,5 @@ CONFIG += staticlib
- DESTDIR = $$IFW_LIB_PATH
-
- include(7zip.pri)
--win32 {
-- DEFINES += _CRT_SECURE_NO_WARNINGS
-- win32-g++*:QMAKE_CXXFLAGS += -w -fvisibility=hidden
-- CONFIG += no_batch # this is needed because we have a same named *.c and *.cpp file -> 7in
-- include($$7ZIP_BASE/win.pri) #this is 7zip
--}
--
--unix {
-- QMAKE_CFLAGS += -w
-- QMAKE_CXXFLAGS += -fvisibility=hidden -w
-- include($$7ZIP_BASE/unix.pri) #this is p7zip
--}
-+win32:include($$7ZIP_BASE/win.pri) #7zip
-+unix:include($$7ZIP_BASE/unix.pri) #p7zip
-diff --git a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp
-index 8af28b9..e20858e 100644
---- a/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp
-+++ b/src/libs/7zip/unix/CPP/7zip/Archive/7z/7zOut.cpp
-@@ -4,8 +4,6 @@
-
- #include "../../../../C/7zCrc.h"
-
--#include "../../../Common/AutoPtr.h"
--
- #include "../../Common/StreamObjects.h"
-
- #include "7zOut.h"
-diff --git a/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h b/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h
-index 1e9bf14..82bd096 100644
---- a/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h
-+++ b/src/libs/7zip/unix/CPP/7zip/Common/RegisterArc.h
-@@ -5,6 +5,8 @@
-
- #include "../Archive/IArchive.h"
-
-+#include <mutex>
-+
- struct CArcInfo
- {
- const char *Name;
-@@ -24,19 +26,35 @@ struct CArcInfo
- Func_IsArc IsArc;
-
- bool IsMultiSignature() const { return (Flags & NArcInfoFlags::kMultiSignature) != 0; }
-+
-+ std::once_flag once;
- };
-
- void RegisterArc(const CArcInfo *arcInfo) throw();
-
- #define REGISTER_ARC_NAME(x) CRegister ## x
-
--#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \
-- REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \
-- static REGISTER_ARC_NAME(x) g_RegisterArc;
--
--#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) { \
-- REGISTER_ARC_NAME(x)() { g_ArcInfo.Signature[0]--; RegisterArc(&g_ArcInfo); }}; \
-- static REGISTER_ARC_NAME(x) g_RegisterArc;
-+#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) \
-+ { \
-+ REGISTER_ARC_NAME(x)() \
-+ { \
-+ std::call_once(g_ArcInfo.once, [] { RegisterArc(&g_ArcInfo); }); \
-+ } \
-+ }; \
-+ static REGISTER_ARC_NAME(x) g_RegisterArc; \
-+ void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; }
-+
-+#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) \
-+ { \
-+ REGISTER_ARC_NAME(x)() { \
-+ std::call_once(g_ArcInfo.once, [] { \
-+ g_ArcInfo.Signature[0]--; \
-+ RegisterArc(&g_ArcInfo); \
-+ }); \
-+ } \
-+ }; \
-+ static REGISTER_ARC_NAME(x) g_RegisterArc; \
-+ void registerArcDec##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; }
-
-
- #define IMP_CreateArcIn_2(c) \
-diff --git a/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h b/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h
-index 4222a30..0c6662a 100644
---- a/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h
-+++ b/src/libs/7zip/unix/CPP/7zip/Common/RegisterCodec.h
-@@ -6,6 +6,8 @@
- #include "../Common/MethodId.h"
- #include "../ICoder.h"
-
-+#include <mutex>
-+
- typedef void * (*CreateCodecP)();
- struct CCodecInfo
- {
-@@ -15,21 +17,34 @@ struct CCodecInfo
- const wchar_t *Name;
- UInt32 NumInStreams;
- bool IsFilter;
-+ std::once_flag once;
- };
-
- void RegisterCodec(const CCodecInfo *codecInfo) throw();
-
- #define REGISTER_CODEC_NAME(x) CRegisterCodec ## x
-
--#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \
-- REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \
-- static REGISTER_CODEC_NAME(x) g_RegisterCodec;
-+#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) \
-+ { \
-+ REGISTER_CODEC_NAME(x)() \
-+ { \
-+ std::call_once(g_CodecInfo.once, [] { RegisterCodec(&g_CodecInfo); }); \
-+ } \
-+ }; \
-+ static REGISTER_CODEC_NAME(x) g_RegisterCodec; \
-+ void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; }
-
- #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x
--#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \
-- REGISTER_CODECS_NAME(x)() { for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \
-- RegisterCodec(&g_CodecsInfo[i]); }}; \
-- static REGISTER_CODECS_NAME(x) g_RegisterCodecs;
-+#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) \
-+ { \
-+ REGISTER_CODECS_NAME(x)() \
-+ { \
-+ for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \
-+ std::call_once(g_CodecsInfo[i].once, [&i] { RegisterCodec(&g_CodecsInfo[i]); }); \
-+ } \
-+ }; \
-+ static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \
-+ void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; }
-
-
- struct CHasherInfo
-diff --git a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp b/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp
-index 03f31fa..5f94254 100644
---- a/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp
-+++ b/src/libs/7zip/unix/CPP/7zip/UI/Common/Extract.cpp
-@@ -2,8 +2,6 @@
-
- #include "StdAfx.h"
-
--#include "../../../../C/Sort.h"
--
- #include "../../../Common/StringConvert.h"
-
- #include "../../../Windows/FileDir.h"
-diff --git a/src/libs/7zip/unix/CPP/include_windows/include_windows.pri b/src/libs/7zip/unix/CPP/include_windows/include_windows.pri
-new file mode 100644
-index 0000000..5ef72fd
---- /dev/null
-+++ b/src/libs/7zip/unix/CPP/include_windows/include_windows.pri
-@@ -0,0 +1,3 @@
-+HEADERS += $$7ZIP_BASE/CPP/include_windows/basetyps.h \
-+ $$7ZIP_BASE/CPP/include_windows/tchar.h \
-+ $$7ZIP_BASE/CPP/include_windows/windows.h
-diff --git a/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp b/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
-new file mode 100644
-index 0000000..5d7f6fd
---- /dev/null
-+++ b/src/libs/7zip/unix/CPP/myWindows/myCommandLineParser.cpp
-@@ -0,0 +1,56 @@
-+/**************************************************************************
-+**
-+** Copyright (C) 2015 The Qt Company Ltd.
-+** Contact: http://www.qt.io/licensing/
-+**
-+** This file is part of the Qt Installer Framework.
-+**
-+** $QT_BEGIN_LICENSE:LGPL$
-+** Commercial License Usage
-+** Licensees holding valid commercial Qt licenses may use this file in
-+** accordance with the commercial license agreement provided with the
-+** Software or, alternatively, in accordance with the terms contained in
-+** a written agreement between you and The Qt Company. For licensing terms
-+** and conditions see http://qt.io/terms-conditions. For further
-+** information use the contact form at http://www.qt.io/contact-us.
-+**
-+** GNU Lesser General Public License Usage
-+** Alternatively, this file may be used under the terms of the GNU Lesser
-+** General Public License version 2.1 or version 3 as published by the Free
-+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-+** following information to ensure the GNU Lesser General Public License
-+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-+**
-+** As a special exception, The Qt Company gives you certain additional
-+** rights. These rights are described in The Qt Company LGPL Exception
-+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-+**
-+**
-+** $QT_END_LICENSE$
-+**
-+**************************************************************************/
-+
-+#include "../CPP/Common/MyString.h"
-+
-+#include <QStringList>
-+
-+namespace NCommandLineParser {
-+
-+void SplitCommandLine(const UString &s, UStringVector &parts)
-+{
-+ parts.Clear();
-+
-+ const QString cmdLine = QString::fromStdWString(static_cast<const wchar_t*>(s));
-+ const QStringList args = cmdLine.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts);
-+ foreach (QString arg, args) {
-+ if (arg.startsWith(QLatin1Char('\"')))
-+ arg = arg.mid(1);
-+ if (arg.endsWith(QLatin1Char('\"')))
-+ arg = arg.mid(1);
-+ parts.Add(arg.toStdWString().c_str());
-+ }
-+}
-+
-+} // namespace NCommandLineParser
-diff --git a/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp b/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp
-index 96554c9..9ebfe37 100644
---- a/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp
-+++ b/src/libs/7zip/unix/CPP/myWindows/myDateAndTime.cpp
-@@ -32,19 +32,18 @@
- **
- **************************************************************************/
-
--#include <QDebug>
--#include <QDateTime>
- #include "windows.h"
-
-+#include <QDateTime>
-+
- void FileTimeToDateTime(const FILETIME *source, QDateTime *target)
- {
- ULARGE_INTEGER store;
-- QDateTime tempDateTime(QDate(1601, 1, 1));
--
- store.QuadPart = source->dwHighDateTime;
- store.QuadPart = store.QuadPart << 32;
- store.QuadPart += source->dwLowDateTime;
-
-+ const QDateTime tempDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 0), Qt::UTC);
- *target = tempDateTime.addMSecs(store.QuadPart / 10000);
- }
-
-@@ -60,6 +59,13 @@ void DateTimeToSystemTime(const QDateTime *source, SYSTEMTIME *target)
- target->wMilliseconds = source->time().msec();
- }
-
-+void DateTimeToFileTime(const QDateTime &dateTime, FILETIME *target)
-+{
-+ const qint64 nsecs = QDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 0), Qt::UTC)
-+ .msecsTo(dateTime) * 10000;
-+ target->dwLowDateTime = nsecs;
-+ target->dwHighDateTime = nsecs >> 32;
-+}
-
- BOOL WINAPI FileTimeToSystemTime(CONST FILETIME *source,SYSTEMTIME *target)
- {
-@@ -70,21 +76,6 @@ BOOL WINAPI FileTimeToSystemTime(CONST FILETIME *source,SYSTEMTIME *target)
- return TRUE;
- }
-
--BOOL WINAPI SystemTimeToFileTime(const SYSTEMTIME *source,FILETIME *target)
--{
-- // TODO: Implementation!
-- // This doesn't seem to be called at all
--
-- qDebug() << "SystemTimeToFileTime";
--
-- target->dwHighDateTime = 0;
-- target->dwLowDateTime = 0;
--
-- qWarning() << Q_FUNC_INFO;
--
-- return TRUE;
--}
--
- BOOL WINAPI FileTimeToLocalFileTime(CONST FILETIME *source,FILETIME *target)
- {
- target->dwHighDateTime = source->dwHighDateTime;
-@@ -137,3 +128,16 @@ VOID WINAPI GetSystemTime(SYSTEMTIME *st)
- QDateTime nowDateTime = QDateTime::currentDateTimeUtc();
- DateTimeToSystemTime(&nowDateTime, st);
- }
-+
-+VOID WINAPI GetSystemTimeAsFileTime(FILETIME *time)
-+{
-+ DateTimeToFileTime(QDateTime::currentDateTimeUtc(), time);
-+}
-+
-+DWORD WINAPI GetTickCount()
-+{
-+ struct timespec ts;
-+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
-+ return DWORD(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
-+ return DWORD(QDateTime::currentMSecsSinceEpoch());
-+}
-diff --git a/src/libs/7zip/unix/CPP/myWindows/myWindows.pri b/src/libs/7zip/unix/CPP/myWindows/myWindows.pri
-new file mode 100644
-index 0000000..0875fdb
---- /dev/null
-+++ b/src/libs/7zip/unix/CPP/myWindows/myWindows.pri
-@@ -0,0 +1,7 @@
-+HEADERS += $$7ZIP_BASE/CPP/myWindows/StdAfx.h \
-+ $$7ZIP_BASE/CPP/myWindows/config.h \
-+ $$7ZIP_BASE/CPP/myWindows/initguid.h \
-+ $$7ZIP_BASE/CPP/myWindows/myPrivate.h
-+
-+SOURCES += $$7ZIP_BASE/CPP/myWindows/myDateAndTime.cpp \
-+ $$7ZIP_BASE/CPP/myWindows/myCommandLineParser.cpp
-diff --git a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp b/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp
-index 8af28b9..e20858e 100644
---- a/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp
-+++ b/src/libs/7zip/win/CPP/7zip/Archive/7z/7zOut.cpp
-@@ -4,8 +4,6 @@
-
- #include "../../../../C/7zCrc.h"
-
--#include "../../../Common/AutoPtr.h"
--
- #include "../../Common/StreamObjects.h"
-
- #include "7zOut.h"
-diff --git a/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h b/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h
-index 1e9bf14..82bd096 100644
---- a/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h
-+++ b/src/libs/7zip/win/CPP/7zip/Common/RegisterArc.h
-@@ -5,6 +5,8 @@
-
- #include "../Archive/IArchive.h"
-
-+#include <mutex>
-+
- struct CArcInfo
- {
- const char *Name;
-@@ -24,19 +26,35 @@ struct CArcInfo
- Func_IsArc IsArc;
-
- bool IsMultiSignature() const { return (Flags & NArcInfoFlags::kMultiSignature) != 0; }
-+
-+ std::once_flag once;
- };
-
- void RegisterArc(const CArcInfo *arcInfo) throw();
-
- #define REGISTER_ARC_NAME(x) CRegister ## x
-
--#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \
-- REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \
-- static REGISTER_ARC_NAME(x) g_RegisterArc;
--
--#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) { \
-- REGISTER_ARC_NAME(x)() { g_ArcInfo.Signature[0]--; RegisterArc(&g_ArcInfo); }}; \
-- static REGISTER_ARC_NAME(x) g_RegisterArc;
-+#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) \
-+ { \
-+ REGISTER_ARC_NAME(x)() \
-+ { \
-+ std::call_once(g_ArcInfo.once, [] { RegisterArc(&g_ArcInfo); }); \
-+ } \
-+ }; \
-+ static REGISTER_ARC_NAME(x) g_RegisterArc; \
-+ void registerArc##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; }
-+
-+#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) \
-+ { \
-+ REGISTER_ARC_NAME(x)() { \
-+ std::call_once(g_ArcInfo.once, [] { \
-+ g_ArcInfo.Signature[0]--; \
-+ RegisterArc(&g_ArcInfo); \
-+ }); \
-+ } \
-+ }; \
-+ static REGISTER_ARC_NAME(x) g_RegisterArc; \
-+ void registerArcDec##x() { static REGISTER_ARC_NAME(x) g_RegisterArc; }
-
-
- #define IMP_CreateArcIn_2(c) \
-diff --git a/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h b/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h
-index 4222a30..0c6662a 100644
---- a/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h
-+++ b/src/libs/7zip/win/CPP/7zip/Common/RegisterCodec.h
-@@ -6,6 +6,8 @@
- #include "../Common/MethodId.h"
- #include "../ICoder.h"
-
-+#include <mutex>
-+
- typedef void * (*CreateCodecP)();
- struct CCodecInfo
- {
-@@ -15,21 +17,34 @@ struct CCodecInfo
- const wchar_t *Name;
- UInt32 NumInStreams;
- bool IsFilter;
-+ std::once_flag once;
- };
-
- void RegisterCodec(const CCodecInfo *codecInfo) throw();
-
- #define REGISTER_CODEC_NAME(x) CRegisterCodec ## x
-
--#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \
-- REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \
-- static REGISTER_CODEC_NAME(x) g_RegisterCodec;
-+#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) \
-+ { \
-+ REGISTER_CODEC_NAME(x)() \
-+ { \
-+ std::call_once(g_CodecInfo.once, [] { RegisterCodec(&g_CodecInfo); }); \
-+ } \
-+ }; \
-+ static REGISTER_CODEC_NAME(x) g_RegisterCodec; \
-+ void registerCodec##x() { static REGISTER_CODEC_NAME(x) g_RegisterCodecs; }
-
- #define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x
--#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \
-- REGISTER_CODECS_NAME(x)() { for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \
-- RegisterCodec(&g_CodecsInfo[i]); }}; \
-- static REGISTER_CODECS_NAME(x) g_RegisterCodecs;
-+#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) \
-+ { \
-+ REGISTER_CODECS_NAME(x)() \
-+ { \
-+ for (unsigned i = 0; i < ARRAY_SIZE(g_CodecsInfo); i++) \
-+ std::call_once(g_CodecsInfo[i].once, [&i] { RegisterCodec(&g_CodecsInfo[i]); }); \
-+ } \
-+ }; \
-+ static REGISTER_CODECS_NAME(x) g_RegisterCodecs; \
-+ void registerCodec##x() { static REGISTER_CODECS_NAME(x) g_RegisterCodecs; }
-
-
- struct CHasherInfo
-diff --git a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp b/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp
-index df86620..13d2ad2 100644
---- a/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp
-+++ b/src/libs/7zip/win/CPP/7zip/UI/Common/Extract.cpp
-@@ -2,8 +2,6 @@
-
- #include "StdAfx.h"
-
--#include "../../../../C/Sort.h"
--
- #include "../../../Common/StringConvert.h"
-
- #include "../../../Windows/FileDir.h"
---
-2.3.7.windows.1
-
diff --git a/src/libs/ifwtools/binarycreator.cpp b/src/libs/ifwtools/binarycreator.cpp
new file mode 100644
index 000000000..341052650
--- /dev/null
+++ b/src/libs/ifwtools/binarycreator.cpp
@@ -0,0 +1,898 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "binarycreator.h"
+
+#include "qtpatch.h"
+#include "repositorygen.h"
+#include "binarycontent.h"
+#include "errors.h"
+#include "fileio.h"
+#include "init.h"
+#include "repository.h"
+#include "settings.h"
+#include "utils.h"
+#include "fileutils.h"
+
+#include <QDateTime>
+#include <QDirIterator>
+#include <QDomDocument>
+#include <QProcess>
+#include <QRegularExpression>
+#include <QSettings>
+#include <QTemporaryFile>
+#include <QTemporaryDir>
+
+#include <iostream>
+
+#ifdef Q_OS_MACOS
+#include <QtCore/QtEndian>
+#include <QtCore/QFile>
+#include <QtCore/QVersionNumber>
+
+#include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#endif
+
+using namespace QInstaller;
+using namespace QInstallerTools;
+
+#ifndef Q_OS_WIN
+static void chmod755(const QString &absolutFilePath)
+{
+ QFile::setPermissions(absolutFilePath, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
+ | QFile::ReadGroup | QFile::ExeGroup | QFile::ReadOther | QFile::ExeOther);
+}
+#endif
+
+#ifdef Q_OS_MACOS
+template <typename T = uint32_t> T readInt(QIODevice *ioDevice, bool *ok,
+ bool swap, bool peek = false) {
+ const auto bytes = peek
+ ? ioDevice->peek(sizeof(T))
+ : ioDevice->read(sizeof(T));
+ if (bytes.size() != sizeof(T)) {
+ if (ok)
+ *ok = false;
+ return T();
+ }
+ if (ok)
+ *ok = true;
+ T n = *reinterpret_cast<const T *>(bytes.constData());
+ return swap ? qbswap(n) : n;
+}
+
+static QVersionNumber readMachOMinimumSystemVersion(QIODevice *device)
+{
+ bool ok;
+ std::vector<qint64> machoOffsets;
+
+ qint64 pos = device->pos();
+ uint32_t magic = readInt(device, &ok, false);
+ if (magic == FAT_MAGIC || magic == FAT_CIGAM ||
+ magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64) {
+ bool swap = magic == FAT_CIGAM || magic == FAT_CIGAM_64;
+ uint32_t nfat = readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ for (uint32_t n = 0; n < nfat; ++n) {
+ const bool is64bit = magic == FAT_MAGIC_64 || magic == FAT_CIGAM_64;
+ fat_arch_64 fat_arch;
+ fat_arch.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap));
+ if (!ok)
+ return QVersionNumber();
+
+ fat_arch.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap));
+ if (!ok)
+ return QVersionNumber();
+
+ fat_arch.offset = is64bit
+ ? readInt<uint64_t>(device, &ok, swap) : readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ fat_arch.size = is64bit
+ ? readInt<uint64_t>(device, &ok, swap) : readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ fat_arch.align = readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ fat_arch.reserved = is64bit ? readInt(device, &ok, swap) : 0;
+ if (!ok)
+ return QVersionNumber();
+
+ machoOffsets.push_back(static_cast<qint64>(fat_arch.offset));
+ }
+ } else if (!ok) {
+ return QVersionNumber();
+ }
+
+ // Wasn't a fat file, so we just read a thin Mach-O from the original offset
+ if (machoOffsets.empty())
+ machoOffsets.push_back(pos);
+
+ std::vector<QVersionNumber> versions;
+
+ for (const auto &offset : machoOffsets) {
+ if (!device->seek(offset))
+ return QVersionNumber();
+
+ bool swap = false;
+ mach_header_64 header;
+ header.magic = readInt(device, nullptr, swap);
+ switch (header.magic) {
+ case MH_CIGAM:
+ case MH_CIGAM_64:
+ swap = true;
+ break;
+ case MH_MAGIC:
+ case MH_MAGIC_64:
+ break;
+ default:
+ return QVersionNumber();
+ }
+
+ header.cputype = static_cast<cpu_type_t>(readInt(device, &ok, swap));
+ if (!ok)
+ return QVersionNumber();
+
+ header.cpusubtype = static_cast<cpu_subtype_t>(readInt(device, &ok, swap));
+ if (!ok)
+ return QVersionNumber();
+
+ header.filetype = readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ header.ncmds = readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ header.sizeofcmds = readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ header.flags = readInt(device, &ok, swap);
+ if (!ok)
+ return QVersionNumber();
+
+ header.reserved = header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64
+ ? readInt(device, &ok, swap) : 0;
+ if (!ok)
+ return QVersionNumber();
+
+ for (uint32_t i = 0; i < header.ncmds; ++i) {
+ const uint32_t cmd = readInt(device, nullptr, swap);
+ const uint32_t cmdsize = readInt(device, nullptr, swap);
+ if (cmd == 0 || cmdsize == 0)
+ return QVersionNumber();
+
+ switch (cmd) {
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_WATCHOS:
+ const uint32_t version = readInt(device, &ok, swap, true);
+ if (!ok)
+ return QVersionNumber();
+
+ versions.push_back(QVersionNumber(
+ static_cast<int>(version >> 16),
+ static_cast<int>((version >> 8) & 0xff),
+ static_cast<int>(version & 0xff)));
+ break;
+ }
+
+ const qint64 remaining = static_cast<qint64>(cmdsize - sizeof(cmd) - sizeof(cmdsize));
+ if (device->read(remaining).size() != remaining)
+ return QVersionNumber();
+ }
+ }
+
+ std::sort(versions.begin(), versions.end());
+ return !versions.empty() ? versions.front() : QVersionNumber();
+}
+#endif
+
+static int assemble(Input input, const QInstaller::Settings &settings, const BinaryCreatorArgs &args)
+{
+#ifdef Q_OS_MACOS
+ if (QInstaller::isInBundle(input.installerExePath)) {
+ const QString bundle = input.installerExePath;
+ // if the input file was a bundle
+ const QSettings s(QString::fromLatin1("%1/Contents/Info.plist").arg(input.installerExePath),
+ QSettings::NativeFormat);
+ input.installerExePath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(bundle)
+ .arg(s.value(QLatin1String("CFBundleExecutable"),
+ QFileInfo(input.installerExePath).completeBaseName()).toString());
+ }
+
+ const bool createDMG = input.outputPath.endsWith(QLatin1String(".dmg"));
+ if (createDMG)
+ input.outputPath.replace(input.outputPath.length() - 4, 4, QLatin1String(".app"));
+
+ const bool isBundle = input.outputPath.endsWith(QLatin1String(".app"));
+ const QString bundle = isBundle ? input.outputPath : QString();
+ const BundleBackup bundleBackup(bundle);
+
+ if (isBundle) {
+ // output should be a bundle
+ const QFileInfo fi(input.outputPath);
+
+ QString minimumSystemVersion;
+ QFile file(input.installerExePath);
+ if (file.open(QIODevice::ReadOnly))
+ minimumSystemVersion = readMachOMinimumSystemVersion(&file).normalized().toString();
+
+ const QString contentsResourcesPath = fi.filePath() + QLatin1String("/Contents/Resources/");
+
+ QInstaller::mkpath(fi.filePath() + QLatin1String("/Contents/MacOS"));
+ QInstaller::mkpath(contentsResourcesPath);
+
+ {
+ QFile pkgInfo(fi.filePath() + QLatin1String("/Contents/PkgInfo"));
+ pkgInfo.open(QIODevice::WriteOnly);
+ QTextStream pkgInfoStream(&pkgInfo);
+ pkgInfoStream << QLatin1String("APPL????") << Qt::endl;
+ }
+
+ QString iconFile;
+ if (QFile::exists(settings.installerApplicationIcon())) {
+ iconFile = settings.installerApplicationIcon();
+ } else {
+ iconFile = QString::fromLatin1(":/resources/default_icon_mac.icns");
+ }
+
+ const QString iconTargetFile = fi.completeBaseName() + QLatin1String(".icns");
+ QFile::copy(iconFile, contentsResourcesPath + iconTargetFile);
+ if (QDir(qApp->applicationDirPath() + QLatin1String("/qt_menu.nib")).exists()) {
+ copyDirectoryContents(qApp->applicationDirPath() + QLatin1String("/qt_menu.nib"),
+ contentsResourcesPath + QLatin1String("/qt_menu.nib"));
+ }
+
+ QFile infoPList(fi.filePath() + QLatin1String("/Contents/Info.plist"));
+ infoPList.open(QIODevice::WriteOnly);
+ QTextStream plistStream(&infoPList);
+ plistStream << QLatin1String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") << Qt::endl;
+ plistStream << QLatin1String("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
+ "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">") << Qt::endl;
+ plistStream << QLatin1String("<plist version=\"1.0\">") << Qt::endl;
+ plistStream << QLatin1String("<dict>") << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleIconFile</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>") << iconTargetFile << QLatin1String("</string>")
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundlePackageType</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>APPL</string>") << Qt::endl;
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+ plistStream << QLatin1String("\t<key>CFBundleShortVersionString</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>")
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleVersion</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>") << QLatin1String(QUOTE(IFW_VERSION_STR)) << ("</string>")
+ << Qt::endl;
+#undef QUOTE
+#undef QUOTE_
+ plistStream << QLatin1String("\t<key>CFBundleSignature</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>\?\?\?\?</string>") << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleExecutable</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>") << fi.completeBaseName() << QLatin1String("</string>")
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>CFBundleIdentifier</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>com.yourcompany.installerbase</string>") << Qt::endl;
+ plistStream << QLatin1String("\t<key>NOTE</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>This file was generated by Qt Installer Framework.</string>")
+ << Qt::endl;
+ plistStream << QLatin1String("\t<key>NSPrincipalClass</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>NSApplication</string>") << Qt::endl;
+ if (!minimumSystemVersion.isEmpty()) {
+ plistStream << QLatin1String("\t<key>LSMinimumSystemVersion</key>") << Qt::endl;
+ plistStream << QLatin1String("\t<string>") << minimumSystemVersion << QLatin1String("</string>") << Qt::endl;
+ }
+ plistStream << QLatin1String("</dict>") << Qt::endl;
+ plistStream << QLatin1String("</plist>") << Qt::endl;
+
+ input.outputPath = QString::fromLatin1("%1/Contents/MacOS/%2").arg(input.outputPath)
+ .arg(fi.completeBaseName());
+ }
+#elif defined(Q_OS_LINUX)
+ Q_UNUSED(settings)
+#endif
+
+ QTemporaryFile file(input.outputPath);
+ if (!file.open()) {
+ throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(input.installerExePath,
+ input.outputPath, file.errorString()));
+ }
+
+ const QString tempFile = file.fileName();
+ file.close();
+ file.remove();
+
+ QFile instExe(input.installerExePath);
+ if (!instExe.copy(tempFile)) {
+ throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(instExe.fileName(),
+ tempFile, instExe.errorString()));
+ }
+
+ QtPatch::patchBinaryFile(tempFile, QByteArray("MY_InstallerCreateDateTime_MY"),
+ QDateTime::currentDateTime().toString(QLatin1String("yyyy-MM-dd - HH:mm:ss")).toLatin1());
+
+
+ input.installerExePath = tempFile;
+
+#if defined(Q_OS_WIN)
+ // setting the windows icon must happen before we append our binary data - otherwise they get lost :-/
+ if (QFile::exists(settings.installerApplicationIcon())) {
+ // no error handling as this is not fatal
+ setApplicationIcon(tempFile, settings.installerApplicationIcon());
+ }
+#elif defined(Q_OS_MACOS)
+ if (isBundle) {
+ // no error handling as this is not fatal
+ const QString copyscript = QDir::temp().absoluteFilePath(QLatin1String("copylibsintobundle.sh"));
+ QFile::copy(QLatin1String(":/resources/copylibsintobundle.sh"), copyscript);
+ QFile::rename(tempFile, input.outputPath);
+ chmod755(copyscript);
+ QProcess p;
+ p.start(copyscript, QStringList() << bundle);
+ p.waitForFinished(-1);
+ QFile::rename(input.outputPath, tempFile);
+ QFile::remove(copyscript);
+ }
+#endif
+
+ QFile out(generateTemporaryFileName());
+
+ QString targetName = input.outputPath;
+#ifdef Q_OS_MACOS
+ QDir resourcePath(QFileInfo(input.outputPath).dir());
+ resourcePath.cdUp();
+ resourcePath.cd(QLatin1String("Resources"));
+ targetName = resourcePath.filePath(QLatin1String("installer.dat"));
+#endif
+
+ {
+ QFile target(targetName);
+ if (target.exists() && !target.remove()) {
+ qCritical("Cannot remove target %s: %s", qPrintable(target.fileName()),
+ qPrintable(target.errorString()));
+ QFile::remove(tempFile);
+ return EXIT_FAILURE;
+ }
+ }
+
+ try {
+ QInstaller::openForWrite(&out);
+ QFile exe(input.installerExePath);
+
+#ifdef Q_OS_MACOS
+ if (!exe.copy(input.outputPath)) {
+ throw Error(QString::fromLatin1("Cannot copy %1 to %2: %3").arg(exe.fileName(),
+ input.outputPath, exe.errorString()));
+ }
+#else
+ QInstaller::openForRead(&exe);
+ QInstaller::appendData(&out, &exe, exe.size());
+#endif
+
+ if (!args.createMaintenanceTool) {
+ foreach (const QInstallerTools::PackageInfo &info, input.packages) {
+ QInstaller::ResourceCollection collection;
+ collection.setName(info.name.toUtf8());
+ qDebug() << "Creating resource archive for" << info.name;
+ foreach (const QString &copiedFile, info.copiedFiles) {
+ const QSharedPointer<Resource> resource(new Resource(copiedFile));
+ qDebug().nospace() << "Appending " << copiedFile << " (" << humanReadableSize(resource->size()) << ")";
+ collection.appendResource(resource);
+ }
+ input.manager.insertCollection(collection);
+ }
+
+ const QList<QInstaller::OperationBlob> operations;
+ BinaryContent::writeBinaryContent(&out, operations, input.manager,
+ BinaryContent::MagicInstallerMarker, BinaryContent::MagicCookie);
+ } else {
+ createMTDatFile(out);
+ }
+
+ } catch (const Error &e) {
+ qCritical("Error occurred while assembling the installer: %s", qPrintable(e.message()));
+ QFile::remove(tempFile);
+ return EXIT_FAILURE;
+ }
+
+ if (!out.rename(targetName)) {
+ qCritical("Cannot write installer to %s: %s", targetName.toUtf8().constData(),
+ out.errorString().toUtf8().constData());
+ QFile::remove(tempFile);
+ return EXIT_FAILURE;
+ }
+
+#ifdef Q_OS_MACOS
+ // installer executable
+ chmod755(input.outputPath);
+#endif
+#ifndef Q_OS_WIN
+ // installer executable on linux, installer.dat on macOS
+ chmod755(targetName);
+#endif
+ QFile::remove(tempFile);
+
+#ifdef Q_OS_MACOS
+ if (isBundle && !args.signingIdentity.isEmpty()) {
+ qDebug() << "Signing .app bundle...";
+
+ QProcess p;
+ p.start(QLatin1String("codesign"),
+ QStringList() << QLatin1String("--force")
+ << QLatin1String("--deep")
+ << QLatin1String("--sign") << args.signingIdentity
+ << bundle);
+
+ if (!p.waitForFinished(-1)) {
+ qCritical("Failed to sign app bundle: error while running '%s %s': %s",
+ p.program().toUtf8().constData(),
+ p.arguments().join(QLatin1Char(' ')).toUtf8().constData(),
+ p.errorString().toUtf8().constData());
+ return EXIT_FAILURE;
+ }
+
+ if (p.exitStatus() == QProcess::NormalExit) {
+ if (p.exitCode() != 0) {
+ qCritical("Failed to sign app bundle: running codesign failed "
+ "with exit code %d: %s", p.exitCode(),
+ p.readAllStandardError().constData());
+ return EXIT_FAILURE;
+ }
+ }
+
+ qDebug() << "done.";
+ }
+
+ bundleBackup.release();
+
+ if (createDMG) {
+ qDebug() << "creating a DMG disk image...";
+
+ const QString volumeName = QFileInfo(input.outputPath).fileName();
+ const QString imagePath = QString::fromLatin1("%1/%2.dmg")
+ .arg(QFileInfo(bundle).path())
+ .arg(volumeName);
+
+ // no error handling as this is not fatal
+ QProcess p;
+ p.start(QLatin1String("/usr/bin/hdiutil"),
+ QStringList() << QLatin1String("create")
+ << imagePath
+ << QLatin1String("-srcfolder")
+ << bundle
+ << QLatin1String("-ov")
+ << QLatin1String("-volname")
+ << volumeName
+ << QLatin1String("-fs")
+ << QLatin1String("HFS+"));
+ qDebug() << "running " << p.program() << p.arguments();
+ p.waitForFinished(-1);
+ qDebug() << "removing" << bundle;
+ QDir(bundle).removeRecursively();
+ qDebug() << "done.";
+ }
+#endif
+ return EXIT_SUCCESS;
+}
+
+QT_BEGIN_NAMESPACE
+int runRcc(int argc, char *argv[]);
+QT_END_NAMESPACE
+
+static int runRcc(const QStringList &args)
+{
+ const int argc = args.count();
+ QVector<char*> argv(argc, nullptr);
+ for (int i = 0; i < argc; ++i)
+ argv[i] = qstrdup(qPrintable(args[i]));
+
+ // Note: this does not run the rcc provided by Qt, this one is using the compiled in binarycreator
+ // version. If it happens that resource mapping fails, we might need to adapt the code here...
+ const int result = runRcc(argc, argv.data());
+
+ foreach (char *arg, argv)
+ delete [] arg;
+
+ return result;
+}
+
+static QSharedPointer<QInstaller::Resource> createDefaultResourceFile(const QString &directory,
+ const QString &binaryName)
+{
+ QTemporaryFile projectFile(directory + QLatin1String("/rccprojectXXXXXX.qrc"));
+ if (!projectFile.open())
+ throw Error(QString::fromLatin1("Cannot create temporary file for generated rcc project file"));
+ projectFile.close();
+
+ const WorkingDirectoryChange wd(directory);
+ const QString projectFileName = QFileInfo(projectFile.fileName()).absoluteFilePath();
+
+ // 1. create the .qrc file
+ if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-project") << QLatin1String("-o")
+ << projectFileName) != EXIT_SUCCESS) {
+ throw Error(QString::fromLatin1("Cannot create rcc project file."));
+ }
+
+ // 2. create the binary resource file from the .qrc file
+ if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary") << QLatin1String("-o")
+ << binaryName << projectFileName) != EXIT_SUCCESS) {
+ throw Error(QString::fromLatin1("Cannot compile rcc project file."));
+ }
+
+ return QSharedPointer<QInstaller::Resource>(new QInstaller::Resource(binaryName, binaryName
+ .toUtf8()));
+}
+
+static
+QList<QSharedPointer<QInstaller::Resource> > createBinaryResourceFiles(const QStringList &resources)
+{
+ QList<QSharedPointer<QInstaller::Resource> > result;
+ foreach (const QString &resource, resources) {
+ QFile file(resource);
+ if (file.exists()) {
+ const QString binaryName = generateTemporaryFileName();
+ const QString fileName = QFileInfo(file.fileName()).absoluteFilePath();
+ const int status = runRcc(QStringList() << QLatin1String("rcc")
+ << QLatin1String("-binary") << QLatin1String("-o") << binaryName << fileName);
+ if (status != EXIT_SUCCESS)
+ continue;
+
+ result.append(QSharedPointer<QInstaller::Resource> (new QInstaller::Resource(binaryName,
+ binaryName.toUtf8())));
+ }
+ }
+ return result;
+}
+
+void QInstallerTools::copyConfigData(const QString &configFile, const QString &targetDir)
+{
+ qDebug() << "Begin to copy configuration file and data.";
+
+ const QString sourceConfigFile = QFileInfo(configFile).absoluteFilePath();
+ const QString targetConfigFile = targetDir + QLatin1String("/config.xml");
+ QInstallerTools::copyWithException(sourceConfigFile, targetConfigFile, QLatin1String("configuration"));
+
+ // Permissions might be set to bogus values
+ QInstaller::setDefaultFilePermissions(targetConfigFile, DefaultFilePermissions::NonExecutable);
+
+ QFile configXml(targetConfigFile);
+ QInstaller::openForRead(&configXml);
+
+ QDomDocument dom;
+ dom.setContent(&configXml);
+ configXml.close();
+
+ // iterate over all child elements, searching for relative file names
+ const QDomNodeList children = dom.documentElement().childNodes();
+ const QString sourceConfigFilePath = QFileInfo(sourceConfigFile).absolutePath();
+ for (int i = 0; i < children.count(); ++i) {
+ QDomElement domElement = children.at(i).toElement();
+ if (domElement.isNull())
+ continue;
+
+ const QString tagName = domElement.tagName();
+ const QString elementText = domElement.text();
+ qDebug().noquote() << QString::fromLatin1("Read dom element: <%1>%2</%1>.").arg(tagName, elementText);
+
+ if (tagName == QLatin1String("ProductImages")) {
+ const QDomNodeList productImageNode = domElement.childNodes();
+ for (int j = 0; j < productImageNode.count(); ++j) {
+ QDomElement productImagesElement = productImageNode.at(j).toElement();
+ if (productImagesElement.isNull())
+ continue;
+ const QString childName = productImagesElement.tagName();
+ if (childName != QLatin1String("ProductImage"))
+ continue;
+ const QDomNodeList imageNode = productImagesElement.childNodes();
+ for (int k = 0; k < imageNode.count(); ++k) {
+ QDomElement productImageElement = imageNode.at(k).toElement();
+ if (productImageElement.isNull())
+ continue;
+ const QString imageChildName = productImageElement.tagName();
+ if (imageChildName != QLatin1String("Image"))
+ continue;
+ const QString targetFile = targetDir + QLatin1Char('/') + productImageElement.text();
+ const QFileInfo childFileInfo = QFileInfo(sourceConfigFilePath, productImageElement.text());
+ QInstallerTools::copyWithException(childFileInfo.absoluteFilePath(), targetFile, imageChildName);
+ copyHighDPIImage(childFileInfo, imageChildName, targetFile);
+ }
+
+ }
+ continue;
+ }
+
+ static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:"));
+ QString newName = domElement.text().replace(regex, QLatin1String("_"));
+
+ QString targetFile;
+ QFileInfo elementFileInfo;
+ if (tagName == QLatin1String("InstallerApplicationIcon")) {
+#if defined(Q_OS_MACOS)
+ const QString suffix = QLatin1String(".icns");
+#elif defined(Q_OS_WIN)
+ const QString suffix = QLatin1String(".ico");
+#else
+ const QString suffix = QLatin1String(".png");
+#endif
+ elementFileInfo = QFileInfo(sourceConfigFilePath, elementText + suffix);
+ targetFile = targetDir + QLatin1Char('/') + newName + suffix;
+ } else {
+ elementFileInfo = QFileInfo(sourceConfigFilePath, elementText);
+ const QString suffix = elementFileInfo.completeSuffix();
+ if (!suffix.isEmpty())
+ newName.append(QLatin1Char('.') + suffix);
+ targetFile = targetDir + QLatin1Char('/') + newName;
+ }
+ if (!elementFileInfo.exists() || elementFileInfo.isDir())
+ continue;
+
+ domElement.replaceChild(dom.createTextNode(newName), domElement.firstChild());
+ QInstallerTools::copyWithException(elementFileInfo.absoluteFilePath(), targetFile, tagName);
+ copyHighDPIImage(elementFileInfo, tagName, targetFile);
+ }
+
+ QInstaller::openForWrite(&configXml);
+ QTextStream stream(&configXml);
+ dom.save(stream, 4);
+
+ qDebug() << "done.\n";
+}
+
+void QInstallerTools::copyHighDPIImage(const QFileInfo &childFileInfo,
+ const QString &childName, const QString &targetFile)
+{
+ //Copy also highdpi image if present
+ const QFileInfo childFileInfoHighDpi = QFileInfo(childFileInfo.absolutePath(), childFileInfo.baseName() + scHighDpi + childFileInfo.suffix());
+ if (childFileInfoHighDpi.exists()) {
+ const QFileInfo tf(targetFile);
+ const QString highDpiTarget = tf.absolutePath() + QLatin1Char('/') + tf.baseName() + scHighDpi + tf.suffix();
+ QInstallerTools::copyWithException(childFileInfoHighDpi.absoluteFilePath(), highDpiTarget, childName);
+ }
+}
+
+int QInstallerTools::createBinary(BinaryCreatorArgs args, QString &argumentError)
+{
+ // increase maximum numbers of file descriptors
+#if defined (Q_OS_MACOS)
+ struct rlimit rl;
+ getrlimit(RLIMIT_NOFILE, &rl);
+ rl.rlim_cur = qMin(static_cast<rlim_t>(OPEN_MAX), rl.rlim_max);
+ setrlimit(RLIMIT_NOFILE, &rl);
+#endif
+ QString suffix;
+#ifdef Q_OS_WIN
+ suffix = QLatin1String(".exe");
+ if (!args.target.endsWith(suffix))
+ args.target = args.target + suffix;
+#endif
+
+ // Begin check arguments
+ foreach (const QString &packageDir, args.packagesDirectories) {
+ if (!QFileInfo::exists(packageDir)) {
+ argumentError = QString::fromLatin1("Error: Package directory not found at the specified location.");
+ return EXIT_FAILURE;
+ }
+ }
+ foreach (const QString &repositoryDir, args.repositoryDirectories) {
+ if (!QFileInfo::exists(repositoryDir)) {
+ argumentError = QString::fromLatin1("Error: Only local filesystem repositories now supported.");
+ return EXIT_FAILURE;
+ }
+ }
+ if (!args.filteredPackages.isEmpty() && args.onlineOnly) {
+ argumentError = QString::fromLatin1("Error: 'online-only' option cannot be used "
+ "in conjunction with the 'include' or 'exclude' option. An 'online-only' installer will never "
+ "contain any components apart from the root component.");
+ return EXIT_FAILURE;
+ }
+ if (!QFileInfo::exists(args.templateBinary)) {
+#ifdef Q_OS_WIN
+ if (!args.templateBinary.endsWith(suffix))
+ args.templateBinary = args.templateBinary + suffix;
+ // Try again with added executable suffix
+ if (!QFileInfo::exists(args.templateBinary)) {
+ argumentError = QString::fromLatin1("Error: Template base binary not found at the specified location.");
+ return EXIT_FAILURE;
+ }
+#else
+ argumentError = QString::fromLatin1("Error: Template not found at the specified location.");
+ return EXIT_FAILURE;
+#endif
+ }
+ const QFileInfo fi(args.configFile);
+ if (!fi.exists()) {
+ argumentError = QString::fromLatin1("Error: Config file %1 not found at the "
+ "specified location.").arg(fi.absoluteFilePath());
+ return EXIT_FAILURE;
+ }
+ if (!fi.isFile()) {
+ argumentError = QString::fromLatin1("Error: Configuration %1 is not a file.")
+ .arg(fi.absoluteFilePath());
+ return EXIT_FAILURE;
+ }
+ if (!fi.isReadable()) {
+ argumentError = QString::fromLatin1("Error: Config file %1 is not readable.")
+ .arg(fi.absoluteFilePath());
+ return EXIT_FAILURE;
+ }
+ if (args.onlineOnly && args.offlineOnly) {
+ argumentError = QString::fromLatin1("You cannot use --online-only and "
+ "--offline-only at the same time.");
+ return EXIT_FAILURE;
+ }
+ if (args.target.isEmpty() && !args.compileResource && !args.createMaintenanceTool) {
+ argumentError = QString::fromLatin1("Error: Target parameter missing.");
+ return EXIT_FAILURE;
+ }
+ if (args.configFile.isEmpty()) {
+ argumentError = QString::fromLatin1("Error: No configuration file selected.");
+ return EXIT_FAILURE;
+ }
+ if (args.packagesDirectories.isEmpty() && args.repositoryDirectories.isEmpty()
+ && !args.compileResource
+ && !args.createMaintenanceTool) {
+ argumentError = QString::fromLatin1("Error: Both Package directory and Repository parameters missing.");
+ return EXIT_FAILURE;
+ }
+ if (args.onlineOnly) {
+ args.filteredPackages.append(QLatin1String("X_fake_filter_component_for_online_only_installer_X"));
+ args.ftype = QInstallerTools::Include;
+ }
+ // End check arguments
+ qDebug() << "Parsed arguments, ok.";
+
+ Input input;
+ int exitCode = EXIT_FAILURE;
+ QTemporaryDir tmp;
+ tmp.setAutoRemove(false);
+ const QString tmpMetaDir = tmp.path();
+ QTemporaryDir tmp2;
+ tmp2.setAutoRemove(false);
+ const QString tmpRepoDir = tmp2.path();
+ try {
+ const Settings settings = Settings::fromFileAndPrefix(args.configFile, QFileInfo(args.configFile)
+ .absolutePath());
+
+ // Note: the order here is important
+
+ PackageInfoVector packages;
+ QStringList unite7zFiles;
+
+ // 1; update the list of available compressed packages
+ if (!args.repositoryDirectories.isEmpty()) {
+ // 1.1; search packages
+ PackageInfoVector precompressedPackages = createListOfRepositoryPackages(args.repositoryDirectories,
+ &args.filteredPackages, args.ftype);
+ // 1.2; add to common vector
+ packages.append(precompressedPackages);
+ // 1.3; create list of unified metadata archives
+ foreach (const QString &dir, args.repositoryDirectories) {
+ QDirIterator it(dir, QStringList(QLatin1String("*_meta.7z")), QDir::Files | QDir::CaseSensitive);
+ while (it.hasNext()) {
+ it.next();
+ unite7zFiles.append(it.fileInfo().absoluteFilePath());
+ }
+ }
+ }
+
+ // 2; update the list of available prepared packages
+ if (!args.packagesDirectories.isEmpty()) {
+ // 2.1; search packages
+ PackageInfoVector preparedPackages = createListOfPackages(args.packagesDirectories,
+ &args.filteredPackages, args.ftype);
+ // 2.2; copy the packages data and setup the packages vector with the files we copied,
+ // must happen before copying meta data because files will be compressed if
+ // needed and meta data generation relies on this
+ copyComponentData(args.packagesDirectories, tmpRepoDir, &preparedPackages,
+ args.archiveSuffix, args.compression);
+ // 2.3; add to common vector
+ packages.append(preparedPackages);
+ }
+
+ // 3; copy the meta data of the available packages, generate Updates.xml
+ copyMetaData(tmpMetaDir, tmpRepoDir, packages, settings
+ .applicationName(), settings.version(), unite7zFiles);
+
+ // 4; copy the configuration file and and icons etc.
+ copyConfigData(args.configFile, tmpMetaDir + QLatin1String("/installer-config"));
+ {
+ QSettings confInternal(tmpMetaDir + QLatin1String("/config/config-internal.ini")
+ , QSettings::IniFormat);
+ // assume offline installer if there are no repositories and no
+ //--online-only not set
+ args.offlineOnly = args.offlineOnly | settings.repositories().isEmpty();
+ if (args.onlineOnly)
+ args.offlineOnly = !args.onlineOnly;
+ confInternal.setValue(QLatin1String("offlineOnly"), args.offlineOnly);
+ }
+
+ if (!args.compileResource) {
+ // 5; put the copied resources into a resource file
+ ResourceCollection metaCollection("QResources");
+ metaCollection.appendResource(createDefaultResourceFile(tmpMetaDir,
+ generateTemporaryFileName()));
+ metaCollection.appendResources(createBinaryResourceFiles(args.resources));
+ input.manager.insertCollection(metaCollection);
+
+ input.packages = packages;
+ if (args.createMaintenanceTool)
+ input.outputPath = settings.maintenanceToolName();
+ else
+ input.outputPath = args.target;
+ input.installerExePath = args.templateBinary;
+
+#ifdef Q_OS_MACOS
+ // on mac, we enforce building a bundle
+ if (!input.outputPath.endsWith(QLatin1String(".app")) && !input.outputPath.endsWith(QLatin1String(".dmg")))
+ input.outputPath += QLatin1String(".app");
+#endif
+
+ qDebug() << "Creating the binary";
+ exitCode = assemble(input, settings, args);
+ } else {
+ createDefaultResourceFile(tmpMetaDir, QDir::currentPath() + QLatin1String("/update.rcc"));
+ exitCode = EXIT_SUCCESS;
+ }
+ } catch (const Error &e) {
+ QFile::remove(input.outputPath);
+ std::cerr << "Caught exception: " << e.message() << std::endl;
+ } catch (...) {
+ QFile::remove(input.outputPath);
+ std::cerr << "Unknown exception caught" << std::endl;
+ }
+
+ qDebug() << "Cleaning up...";
+ const ResourceCollection collection = input.manager.collectionByName("QResources");
+ foreach (const QSharedPointer<QInstaller::Resource> &resource, collection.resources())
+ QFile::remove(QString::fromUtf8(resource->name()));
+ QInstaller::removeDirectory(tmpMetaDir, true);
+ QInstaller::removeDirectory(tmpRepoDir, true);
+
+ return exitCode;
+}
+
+void QInstallerTools::createMTDatFile(QFile &datFile)
+{
+ QInstaller::appendInt64(&datFile, 0); // operations start
+ QInstaller::appendInt64(&datFile, 0); // operations end
+ QInstaller::appendInt64(&datFile, 0); // resource count
+ QInstaller::appendInt64(&datFile, 4 * sizeof(qint64)); // data block size
+ QInstaller::appendInt64(&datFile, BinaryContent::MagicUninstallerMarker);
+ QInstaller::appendInt64(&datFile, BinaryContent::MagicCookie);
+}
diff --git a/src/libs/ifwtools/binarycreator.h b/src/libs/ifwtools/binarycreator.h
new file mode 100644
index 000000000..387195742
--- /dev/null
+++ b/src/libs/ifwtools/binarycreator.h
@@ -0,0 +1,132 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef BINARYCREATOR_H
+#define BINARYCREATOR_H
+
+#include "ifwtools_global.h"
+
+#include "repositorygen.h"
+#include "fileutils.h"
+#include "binaryformat.h"
+
+#include <abstractarchive.h>
+
+#include <QtCore/QString>
+#include <QtCore/QFile>
+
+namespace QInstallerTools {
+
+struct Input
+{
+ QString outputPath;
+ QString installerExePath;
+ QInstallerTools::PackageInfoVector packages;
+ QInstaller::ResourceCollectionManager manager;
+};
+
+typedef QInstaller::AbstractArchive::CompressionLevel Compression;
+
+struct IFWTOOLS_EXPORT BinaryCreatorArgs
+{
+ QString target;
+ QString configFile;
+ QString templateBinary;
+ QStringList packagesDirectories;
+ QStringList repositoryDirectories;
+ QString archiveSuffix = QLatin1String("7z");
+ Compression compression = Compression::Normal;
+ bool onlineOnly = false;
+ bool offlineOnly = false;
+ QStringList resources;
+ QStringList filteredPackages;
+ FilterType ftype = QInstallerTools::Exclude;
+ bool compileResource = false;
+ QString signingIdentity;
+ bool createMaintenanceTool = false;
+};
+
+class BundleBackup
+{
+public:
+ explicit BundleBackup(const QString &bundle = QString())
+ : bundle(bundle)
+ {
+ if (!bundle.isEmpty() && QFileInfo::exists(bundle)) {
+ backup = QInstaller::generateTemporaryFileName(bundle);
+ QFile::rename(bundle, backup);
+ }
+ }
+
+ ~BundleBackup()
+ {
+ if (!backup.isEmpty()) {
+ QInstaller::removeDirectory(bundle);
+ QFile::rename(backup, bundle);
+ }
+ }
+
+ void release() const
+ {
+ if (!backup.isEmpty())
+ QInstaller::removeDirectory(backup);
+ backup.clear();
+ }
+
+private:
+ const QString bundle;
+ mutable QString backup;
+};
+
+class WorkingDirectoryChange
+{
+public:
+ explicit WorkingDirectoryChange(const QString &path)
+ : oldPath(QDir::currentPath())
+ {
+ QDir::setCurrent(path);
+ }
+
+ virtual ~WorkingDirectoryChange()
+ {
+ QDir::setCurrent(oldPath);
+ }
+
+private:
+ const QString oldPath;
+};
+
+void copyConfigData(const QString &configFile, const QString &targetDir);
+void copyHighDPIImage(const QFileInfo &childFileInfo, const QString &childName, const QString &targetFile);
+
+int IFWTOOLS_EXPORT createBinary(BinaryCreatorArgs args, QString &argumentError);
+void IFWTOOLS_EXPORT createMTDatFile(QFile &datFile);
+
+} // namespace QInstallerTools
+
+#endif // BINARYCREATOR_H
diff --git a/src/libs/ifwtools/ifwtools.pri b/src/libs/ifwtools/ifwtools.pri
new file mode 100644
index 000000000..0ce7b45fc
--- /dev/null
+++ b/src/libs/ifwtools/ifwtools.pri
@@ -0,0 +1,19 @@
+INCLUDEPATH += $$PWD $$PWD/rcc
+
+DEFINES += BUILD_SHARED_IFWTOOLS
+
+HEADERS += $$PWD/rcc/rcc.h \
+ $$PWD/rcc/qcorecmdlineargs_p.h
+
+SOURCES += $$PWD/rcc/rcc.cpp \
+ $$PWD/rcc/rccmain.cpp
+
+HEADERS += $$PWD/ifwtools_global.h \
+ $$PWD/repositorygen.h \
+ $$PWD/binarycreator.h
+
+SOURCES += $$PWD/repositorygen.cpp \
+ $$PWD/binarycreator.cpp
+
+RESOURCES += $$PWD/resources/ifwtools.qrc
+
diff --git a/src/libs/ifwtools/ifwtools_global.h b/src/libs/ifwtools/ifwtools_global.h
new file mode 100644
index 000000000..ff04efce8
--- /dev/null
+++ b/src/libs/ifwtools/ifwtools_global.h
@@ -0,0 +1,44 @@
+/**************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef IFWTOOLS_GLOBAL_H
+#define IFWTOOLS_GLOBAL_H
+
+#include <QtCore/QtGlobal>
+
+#ifndef QT_STATIC
+# ifdef BUILD_SHARED_IFWTOOLS
+# define IFWTOOLS_EXPORT Q_DECL_EXPORT
+# else
+# define IFWTOOLS_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define IFWTOOLS_EXPORT
+#endif
+
+#endif //IFWTOOLS_GLOBAL_H
diff --git a/src/libs/ifwtools/rcc/qcorecmdlineargs_p.h b/src/libs/ifwtools/rcc/qcorecmdlineargs_p.h
new file mode 100644
index 000000000..9c55a43cd
--- /dev/null
+++ b/src/libs/ifwtools/rcc/qcorecmdlineargs_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCORECMDLINEARGS_P_H
+#define QCORECMDLINEARGS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+
+QT_BEGIN_NAMESPACE
+
+static inline QStringList qCmdLineArgs(int argc, char *argv[])
+{
+ QStringList args;
+ for (int i = 0; i != argc; ++i)
+ args += QString::fromLocal8Bit(argv[i]);
+ return args;
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QCORECMDLINEARGS_WIN_P_H
diff --git a/src/libs/ifwtools/rcc/rcc.cpp b/src/libs/ifwtools/rcc/rcc.cpp
new file mode 100644
index 000000000..caef84433
--- /dev/null
+++ b/src/libs/ifwtools/rcc/rcc.cpp
@@ -0,0 +1,1038 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "rcc.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+#include <QtCore/QFile>
+#include <QtCore/QIODevice>
+#include <QtCore/QLocale>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QStack>
+
+#include <QXmlStreamReader>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ CONSTANT_USENAMESPACE = 1,
+ CONSTANT_COMPRESSLEVEL_DEFAULT = -1,
+ CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70
+};
+
+
+#define writeString(s) write(s, sizeof(s))
+
+void RCCResourceLibrary::write(const char *str, int len)
+{
+ --len; // trailing \0 on string literals...
+ int n = m_out.size();
+ m_out.resize(n + len);
+ memcpy(m_out.data() + n, str, len);
+}
+
+void RCCResourceLibrary::writeByteArray(const QByteArray &other)
+{
+ m_out.append(other);
+}
+
+static inline QString msgOpenReadFailed(const QString &fname, const QString &why)
+{
+ return QString::fromUtf8("Unable to open %1 for reading: %2\n").arg(fname).arg(why);
+}
+
+
+///////////////////////////////////////////////////////////
+//
+// RCCFileInfo
+//
+///////////////////////////////////////////////////////////
+
+class RCCFileInfo
+{
+public:
+ enum Flags
+ {
+ NoFlags = 0x00,
+ Compressed = 0x01,
+ Directory = 0x02
+ };
+
+ RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(),
+ QLocale::Language language = QLocale::C,
+ QLocale::Country country = QLocale::AnyCountry,
+ uint flags = NoFlags,
+ int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT,
+ int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT);
+ ~RCCFileInfo();
+
+ QString resourceName() const;
+
+public:
+ qint64 writeDataBlob(RCCResourceLibrary &lib, qint64 offset, QString *errorMessage);
+ qint64 writeDataName(RCCResourceLibrary &, qint64 offset);
+ void writeDataInfo(RCCResourceLibrary &lib);
+
+ int m_flags;
+ QString m_name;
+ QLocale::Language m_language;
+ QLocale::Country m_country;
+ QFileInfo m_fileInfo;
+ RCCFileInfo *m_parent;
+ QMultiHash<QString, RCCFileInfo*> m_children;
+ int m_compressLevel;
+ int m_compressThreshold;
+
+ qint64 m_nameOffset;
+ qint64 m_dataOffset;
+ qint64 m_childOffset;
+};
+
+RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo,
+ QLocale::Language language, QLocale::Country country, uint flags,
+ int compressLevel, int compressThreshold)
+ : m_flags(flags)
+ , m_name(name)
+ , m_language(language)
+ , m_country(country)
+ , m_fileInfo(fileInfo)
+ , m_parent(nullptr)
+ , m_compressLevel(compressLevel)
+ , m_compressThreshold(compressThreshold)
+ , m_nameOffset(0)
+ , m_dataOffset(0)
+ , m_childOffset(0)
+{
+}
+
+RCCFileInfo::~RCCFileInfo()
+{
+ qDeleteAll(m_children);
+}
+
+QString RCCFileInfo::resourceName() const
+{
+ QString resource = m_name;
+ for (RCCFileInfo *p = m_parent; p; p = p->m_parent)
+ resource = resource.prepend(p->m_name + QLatin1Char('/'));
+ return QLatin1Char(':') + resource;
+}
+
+void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib)
+{
+ const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
+ //some info
+ if (text) {
+ if (m_language != QLocale::C) {
+ lib.writeString(" // ");
+ lib.writeByteArray(resourceName().toLocal8Bit());
+ lib.writeString(" [");
+ lib.writeByteArray(QByteArray::number(m_country));
+ lib.writeString("::");
+ lib.writeByteArray(QByteArray::number(m_language));
+ lib.writeString("[\n ");
+ } else {
+ lib.writeString(" // ");
+ lib.writeByteArray(resourceName().toLocal8Bit());
+ lib.writeString("\n ");
+ }
+ }
+
+ //pointer data
+ if (m_flags & RCCFileInfo::Directory) {
+ // name offset
+ lib.writeNumber4(m_nameOffset);
+
+ // flags
+ lib.writeNumber2(m_flags);
+
+ // child count
+ lib.writeNumber4(m_children.size());
+
+ // first child offset
+ lib.writeNumber4(m_childOffset);
+ } else {
+ // name offset
+ lib.writeNumber4(m_nameOffset);
+
+ // flags
+ lib.writeNumber2(m_flags);
+
+ // locale
+ lib.writeNumber2(m_country);
+ lib.writeNumber2(m_language);
+
+ //data offset
+ lib.writeNumber4(m_dataOffset);
+ }
+ if (text)
+ lib.writeChar('\n');
+}
+
+qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
+ QString *errorMessage)
+{
+ const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
+
+ //capture the offset
+ m_dataOffset = offset;
+
+ //find the data to be written
+ QFile file(m_fileInfo.absoluteFilePath());
+ if (!file.open(QFile::ReadOnly)) {
+ *errorMessage = msgOpenReadFailed(m_fileInfo.absoluteFilePath(), file.errorString());
+ return 0;
+ }
+ QByteArray data = file.readAll();
+
+#ifndef QT_NO_COMPRESS
+ // Check if compression is useful for this file
+ if (m_compressLevel != 0 && data.size() != 0) {
+ QByteArray compressed =
+ qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel);
+
+ int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size());
+ if (compressRatio >= m_compressThreshold) {
+ data = compressed;
+ m_flags |= Compressed;
+ }
+ }
+#endif // QT_NO_COMPRESS
+
+ // some info
+ if (text) {
+ lib.writeString(" // ");
+ lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
+ lib.writeString("\n ");
+ }
+
+ // write the length
+
+ lib.writeNumber4(data.size());
+ if (text)
+ lib.writeString("\n ");
+ offset += 4;
+
+ // write the payload
+ const char *p = data.constData();
+ if (text) {
+ for (int i = data.size(), j = 0; --i >= 0; --j) {
+ lib.writeHex(*p++);
+ if (j == 0) {
+ lib.writeString("\n ");
+ j = 16;
+ }
+ }
+ } else {
+ for (int i = data.size(); --i >= 0; )
+ lib.writeChar(*p++);
+ }
+ offset += data.size();
+
+ // done
+ if (text)
+ lib.writeString("\n ");
+ return offset;
+}
+
+qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset)
+{
+ const bool text = (lib.m_format == RCCResourceLibrary::C_Code);
+
+ // capture the offset
+ m_nameOffset = offset;
+
+ // some info
+ if (text) {
+ lib.writeString(" // ");
+ lib.writeByteArray(m_name.toLocal8Bit());
+ lib.writeString("\n ");
+ }
+
+ // write the length
+ lib.writeNumber2(m_name.length());
+ if (text)
+ lib.writeString("\n ");
+ offset += 2;
+
+ // write the hash
+ lib.writeNumber4(qt_hash(m_name));
+ if (text)
+ lib.writeString("\n ");
+ offset += 4;
+
+ // write the m_name
+ const QChar *unicode = m_name.unicode();
+ for (int i = 0; i < m_name.length(); ++i) {
+ lib.writeNumber2(unicode[i].unicode());
+ if (text && i % 16 == 0)
+ lib.writeString("\n ");
+ }
+ offset += m_name.length()*2;
+
+ // done
+ if (text)
+ lib.writeString("\n ");
+ return offset;
+}
+
+
+///////////////////////////////////////////////////////////
+//
+// RCCResourceLibrary
+//
+///////////////////////////////////////////////////////////
+
+RCCResourceLibrary::Strings::Strings() :
+ TAG_RCC(QLatin1String("RCC")),
+ TAG_RESOURCE(QLatin1String("qresource")),
+ TAG_FILE(QLatin1String("file")),
+ ATTRIBUTE_LANG(QLatin1String("lang")),
+ ATTRIBUTE_PREFIX(QLatin1String("prefix")),
+ ATTRIBUTE_ALIAS(QLatin1String("alias")),
+ ATTRIBUTE_THRESHOLD(QLatin1String("threshold")),
+ ATTRIBUTE_COMPRESS(QLatin1String("compress"))
+{
+}
+
+RCCResourceLibrary::RCCResourceLibrary()
+ : m_root(nullptr),
+ m_format(C_Code),
+ m_verbose(false),
+ m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT),
+ m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT),
+ m_treeOffset(0),
+ m_namesOffset(0),
+ m_dataOffset(0),
+ m_useNameSpace(CONSTANT_USENAMESPACE),
+ m_errorDevice(nullptr)
+{
+ m_out.reserve(30 * 1000 * 1000);
+}
+
+RCCResourceLibrary::~RCCResourceLibrary()
+{
+ delete m_root;
+}
+
+enum RCCXmlTag {
+ RccTag,
+ ResourceTag,
+ FileTag
+};
+
+bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
+ const QString &fname, QString currentPath, bool ignoreErrors)
+{
+ Q_ASSERT(m_errorDevice);
+ const QChar slash = QLatin1Char('/');
+ if (!currentPath.isEmpty() && !currentPath.endsWith(slash))
+ currentPath += slash;
+
+ QXmlStreamReader reader(inputDevice);
+ QStack<RCCXmlTag> tokens;
+
+ QString prefix;
+ QLocale::Language language = QLocale::c().language();
+ QLocale::Country country = QLocale::c().country();
+ QString alias;
+ int compressLevel = m_compressLevel;
+ int compressThreshold = m_compressThreshold;
+
+ while (!reader.atEnd()) {
+ QXmlStreamReader::TokenType t = reader.readNext();
+ switch (t) {
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == m_strings.TAG_RCC) {
+ if (!tokens.isEmpty())
+ reader.raiseError(QLatin1String("expected <RCC> tag"));
+ else
+ tokens.push(RccTag);
+ } else if (reader.name() == m_strings.TAG_RESOURCE) {
+ if (tokens.isEmpty() || tokens.top() != RccTag) {
+ reader.raiseError(QLatin1String("unexpected <RESOURCE> tag"));
+ } else {
+ tokens.push(ResourceTag);
+
+ QXmlStreamAttributes attributes = reader.attributes();
+ language = QLocale::c().language();
+ country = QLocale::c().country();
+
+ if (attributes.hasAttribute(m_strings.ATTRIBUTE_LANG)) {
+ QString attribute = attributes.value(m_strings.ATTRIBUTE_LANG).toString();
+ QLocale lang = QLocale(attribute);
+ language = lang.language();
+ if (2 == attribute.length()) {
+ // Language only
+ country = QLocale::AnyCountry;
+ } else {
+ country = lang.country();
+ }
+ }
+
+ prefix.clear();
+ if (attributes.hasAttribute(m_strings.ATTRIBUTE_PREFIX))
+ prefix = attributes.value(m_strings.ATTRIBUTE_PREFIX).toString();
+ if (!prefix.startsWith(slash))
+ prefix.prepend(slash);
+ if (!prefix.endsWith(slash))
+ prefix += slash;
+ }
+ } else if (reader.name() == m_strings.TAG_FILE) {
+ if (tokens.isEmpty() || tokens.top() != ResourceTag) {
+ reader.raiseError(QLatin1String("unexpected <FILE> tag"));
+ } else {
+ tokens.push(FileTag);
+
+ QXmlStreamAttributes attributes = reader.attributes();
+ alias.clear();
+ if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS))
+ alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString();
+
+ compressLevel = m_compressLevel;
+ if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS))
+ compressLevel = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString().toInt();
+
+ compressThreshold = m_compressThreshold;
+ if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD))
+ compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt();
+
+ // Special case for -no-compress. Overrides all other settings.
+ if (m_compressLevel == -2)
+ compressLevel = 0;
+ }
+ } else {
+ reader.raiseError(QString(QLatin1String("unexpected tag: %1")).arg(reader.name().toString()));
+ }
+ break;
+
+ case QXmlStreamReader::EndElement:
+ if (reader.name() == m_strings.TAG_RCC) {
+ if (!tokens.isEmpty() && tokens.top() == RccTag)
+ tokens.pop();
+ else
+ reader.raiseError(QLatin1String("unexpected closing tag"));
+ } else if (reader.name() == m_strings.TAG_RESOURCE) {
+ if (!tokens.isEmpty() && tokens.top() == ResourceTag)
+ tokens.pop();
+ else
+ reader.raiseError(QLatin1String("unexpected closing tag"));
+ } else if (reader.name() == m_strings.TAG_FILE) {
+ if (!tokens.isEmpty() && tokens.top() == FileTag)
+ tokens.pop();
+ else
+ reader.raiseError(QLatin1String("unexpected closing tag"));
+ }
+ break;
+
+ case QXmlStreamReader::Characters:
+ if (reader.isWhitespace())
+ break;
+ if (tokens.isEmpty() || tokens.top() != FileTag) {
+ reader.raiseError(QLatin1String("unexpected text"));
+ } else {
+ QString fileName = reader.text().toString();
+ if (fileName.isEmpty()) {
+ const QString msg = QString::fromLatin1("RCC: Warning: Null node in XML of '%1'\n").arg(fname);
+ m_errorDevice->write(msg.toUtf8());
+ }
+
+ if (alias.isNull())
+ alias = fileName;
+
+ alias = QDir::cleanPath(alias);
+ while (alias.startsWith(QLatin1String("../")))
+ alias.remove(0, 3);
+ alias = QDir::cleanPath(m_resourceRoot) + prefix + alias;
+
+ QString absFileName = fileName;
+ if (QDir::isRelativePath(absFileName))
+ absFileName.prepend(currentPath);
+ QFileInfo file(absFileName);
+ if (!file.exists()) {
+ m_failedResources.push_back(absFileName);
+ const QString msg = QString::fromLatin1("RCC: Error in '%1': Cannot find file '%2'\n").arg(fname).arg(fileName);
+ m_errorDevice->write(msg.toUtf8());
+ if (ignoreErrors)
+ continue;
+ else
+ return false;
+ } else if (file.isFile()) {
+ const bool arc =
+ addFile(alias,
+ RCCFileInfo(alias.section(slash, -1),
+ file,
+ language,
+ country,
+ RCCFileInfo::NoFlags,
+ compressLevel,
+ compressThreshold)
+ );
+ if (!arc)
+ m_failedResources.push_back(absFileName);
+ } else {
+ QDir dir;
+ if (file.isDir()) {
+ dir.setPath(file.filePath());
+ } else {
+ dir.setPath(file.path());
+ dir.setNameFilters(QStringList(file.fileName()));
+ if (alias.endsWith(file.fileName()))
+ alias = alias.left(alias.length()-file.fileName().length());
+ }
+ if (!alias.endsWith(slash))
+ alias += slash;
+ QDirIterator it(dir, QDirIterator::FollowSymlinks|QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ QFileInfo child(it.fileInfo());
+ if (child.fileName() != QLatin1String(".") && child.fileName() != QLatin1String("..")) {
+ const bool arc =
+ addFile(alias + child.fileName(),
+ RCCFileInfo(child.fileName(),
+ child,
+ language,
+ country,
+ child.isDir() ? RCCFileInfo::Directory : RCCFileInfo::NoFlags,
+ compressLevel,
+ compressThreshold)
+ );
+ if (!arc)
+ m_failedResources.push_back(child.fileName());
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (reader.hasError()) {
+ if (ignoreErrors)
+ return true;
+ int errorLine = reader.lineNumber();
+ int errorColumn = reader.columnNumber();
+ QString errorMessage = reader.errorString();
+ QString msg = QString::fromLatin1("RCC Parse Error: '%1' Line: %2 Column: %3 [%4]\n").arg(fname).arg(errorLine).arg(errorColumn).arg(errorMessage);
+ m_errorDevice->write(msg.toUtf8());
+ return false;
+ }
+
+ if (m_root == nullptr) {
+ const QString msg = QString::fromUtf8("RCC: Warning: No resources in '%1'.\n").arg(fname);
+ m_errorDevice->write(msg.toUtf8());
+ if (!ignoreErrors && m_format == Binary) {
+ // create dummy entry, otherwise loading with QResource will crash
+ m_root = new RCCFileInfo(QString(), QFileInfo(),
+ QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
+ }
+ }
+
+ return true;
+}
+
+bool RCCResourceLibrary::addFile(const QString &alias, const RCCFileInfo &file)
+{
+ Q_ASSERT(m_errorDevice);
+ if (file.m_fileInfo.size() > 0xffffffff) {
+ const QString msg = QString::fromUtf8("File too big: %1\n").arg(file.m_fileInfo.absoluteFilePath());
+ m_errorDevice->write(msg.toUtf8());
+ return false;
+ }
+ if (!m_root)
+ m_root = new RCCFileInfo(QString(), QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
+
+ RCCFileInfo *parent = m_root;
+ const QStringList nodes = alias.split(QLatin1Char('/'));
+ for (int i = 1; i < nodes.size()-1; ++i) {
+ const QString node = nodes.at(i);
+ if (node.isEmpty())
+ continue;
+ if (!parent->m_children.contains(node)) {
+ RCCFileInfo *s = new RCCFileInfo(node, QFileInfo(), QLocale::C, QLocale::AnyCountry, RCCFileInfo::Directory);
+ s->m_parent = parent;
+ parent->m_children.replace(node, s);
+ parent = s;
+ } else {
+ parent = parent->m_children.value(node);
+ }
+ }
+
+ const QString filename = nodes.at(nodes.size()-1);
+ RCCFileInfo *s = new RCCFileInfo(file);
+ s->m_parent = parent;
+ if (parent->m_children.contains(filename)) {
+ foreach (const QString &fileName, m_fileNames)
+ qWarning("%s: Warning: potential duplicate alias detected: '%s'",
+ qPrintable(fileName), qPrintable(filename));
+ }
+ parent->m_children.insert(filename, s);
+ return true;
+}
+
+void RCCResourceLibrary::reset()
+{
+ if (m_root) {
+ delete m_root;
+ m_root = nullptr;
+ }
+ m_errorDevice = nullptr;
+ m_failedResources.clear();
+}
+
+
+bool RCCResourceLibrary::readFiles(bool ignoreErrors, QIODevice &errorDevice)
+{
+ reset();
+ m_errorDevice = &errorDevice;
+ //read in data
+ if (m_verbose) {
+ const QString msg = QString::fromUtf8("Processing %1 files [%2]\n")
+ .arg(m_fileNames.size()).arg(static_cast<int>(ignoreErrors));
+ m_errorDevice->write(msg.toUtf8());
+ }
+ for (int i = 0; i < m_fileNames.size(); ++i) {
+ QFile fileIn;
+ QString fname = m_fileNames.at(i);
+ QString pwd;
+ if (fname == QLatin1String("-")) {
+ fname = QLatin1String("(stdin)");
+ pwd = QDir::currentPath();
+ fileIn.setFileName(fname);
+ if (!fileIn.open(stdin, QIODevice::ReadOnly)) {
+ m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
+ return false;
+ }
+ } else {
+ pwd = QFileInfo(fname).path();
+ fileIn.setFileName(fname);
+ if (!fileIn.open(QIODevice::ReadOnly)) {
+ m_errorDevice->write(msgOpenReadFailed(fname, fileIn.errorString()).toUtf8());
+ return false;
+ }
+ }
+ if (m_verbose) {
+ const QString msg = QString::fromUtf8("Interpreting %1\n").arg(fname);
+ m_errorDevice->write(msg.toUtf8());
+ }
+
+ if (!interpretResourceFile(&fileIn, fname, pwd, ignoreErrors))
+ return false;
+ }
+ return true;
+}
+
+QStringList RCCResourceLibrary::dataFiles() const
+{
+ QStringList ret;
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return ret;
+ pending.push(m_root);
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ it != file->m_children.end(); ++it) {
+ RCCFileInfo *child = it.value();
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ ret.append(child->m_fileInfo.filePath());
+ }
+ }
+ return ret;
+}
+
+// Determine map of resource identifier (':/newPrefix/images/p1.png') to file via recursion
+static void resourceDataFileMapRecursion(const RCCFileInfo *m_root, const QString &path, RCCResourceLibrary::ResourceDataFileMap &m)
+{
+ typedef QMultiHash<QString, RCCFileInfo*>::const_iterator ChildConstIterator;
+ const QChar slash = QLatin1Char('/');
+ const ChildConstIterator cend = m_root->m_children.constEnd();
+ for (ChildConstIterator it = m_root->m_children.constBegin(); it != cend; ++it) {
+ const RCCFileInfo *child = it.value();
+ QString childName = path;
+ childName += slash;
+ childName += child->m_name;
+ if (child->m_flags & RCCFileInfo::Directory) {
+ resourceDataFileMapRecursion(child, childName, m);
+ } else {
+ m.insert(childName, child->m_fileInfo.filePath());
+ }
+ }
+}
+
+RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap() const
+{
+ ResourceDataFileMap rc;
+ if (m_root)
+ resourceDataFileMapRecursion(m_root, QString(QLatin1Char(':')), rc);
+ return rc;
+}
+
+bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &errorDevice)
+{
+ m_errorDevice = &errorDevice;
+ //write out
+ if (m_verbose)
+ m_errorDevice->write("Outputting code\n");
+ if (!writeHeader()) {
+ m_errorDevice->write("Cannot write header\n");
+ return false;
+ }
+ if (m_root) {
+ if (!writeDataBlobs()) {
+ m_errorDevice->write("Cannot write data blobs.\n");
+ return false;
+ }
+ if (!writeDataNames()) {
+ m_errorDevice->write("Cannot write file names\n");
+ return false;
+ }
+ if (!writeDataStructure()) {
+ m_errorDevice->write("Cannot write data tree\n");
+ return false;
+ }
+ }
+ if (!writeInitializer()) {
+ m_errorDevice->write("Cannot write footer\n");
+ return false;
+ }
+ outDevice.write(m_out.constData(), m_out.size());
+ return true;
+}
+
+void RCCResourceLibrary::writeHex(quint8 tmp)
+{
+ const char digits[] = "0123456789abcdef";
+ writeChar('0');
+ writeChar('x');
+ if (tmp < 16) {
+ writeChar(digits[tmp]);
+ } else {
+ writeChar(digits[tmp >> 4]);
+ writeChar(digits[tmp & 0xf]);
+ }
+ writeChar(',');
+}
+
+void RCCResourceLibrary::writeNumber2(quint16 number)
+{
+ if (m_format == RCCResourceLibrary::Binary) {
+ writeChar(number >> 8);
+ writeChar(number);
+ } else {
+ writeHex(number >> 8);
+ writeHex(number);
+ }
+}
+
+void RCCResourceLibrary::writeNumber4(quint32 number)
+{
+ if (m_format == RCCResourceLibrary::Binary) {
+ writeChar(number >> 24);
+ writeChar(number >> 16);
+ writeChar(number >> 8);
+ writeChar(number);
+ } else {
+ writeHex(number >> 24);
+ writeHex(number >> 16);
+ writeHex(number >> 8);
+ writeHex(number);
+ }
+}
+
+bool RCCResourceLibrary::writeHeader()
+{
+ if (m_format == C_Code) {
+ writeString("/****************************************************************************\n");
+ writeString("** Resource object code\n");
+ writeString("**\n");
+ writeString("** Created: ");
+ writeByteArray(QDateTime::currentDateTime().toString().toLatin1());
+ writeString("\n** by: The Resource Compiler for Qt version ");
+ writeByteArray(QT_VERSION_STR);
+ writeString("\n**\n");
+ writeString("** WARNING! All changes made in this file will be lost!\n");
+ writeString( "*****************************************************************************/\n\n");
+ writeString("#include <QtCore/qglobal.h>\n\n");
+ } else if (m_format == Binary) {
+ writeString("qres");
+ writeNumber4(0);
+ writeNumber4(0);
+ writeNumber4(0);
+ writeNumber4(0);
+ }
+ return true;
+}
+
+bool RCCResourceLibrary::writeDataBlobs()
+{
+ Q_ASSERT(m_errorDevice);
+ if (m_format == C_Code)
+ writeString("static const unsigned char qt_resource_data[] = {\n");
+ else if (m_format == Binary)
+ m_dataOffset = m_out.size();
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return false;
+
+ pending.push(m_root);
+ qint64 offset = 0;
+ QString errorMessage;
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ it != file->m_children.end(); ++it) {
+ RCCFileInfo *child = it.value();
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ else {
+ offset = child->writeDataBlob(*this, offset, &errorMessage);
+ if (offset == 0) {
+ m_errorDevice->write(errorMessage.toUtf8());
+ return false;
+ }
+ }
+ }
+ }
+ if (m_format == C_Code)
+ writeString("\n};\n\n");
+ return true;
+}
+
+bool RCCResourceLibrary::writeDataNames()
+{
+ if (m_format == C_Code)
+ writeString("static const unsigned char qt_resource_name[] = {\n");
+ else if (m_format == Binary)
+ m_namesOffset = m_out.size();
+
+ QHash<QString, int> names;
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return false;
+
+ pending.push(m_root);
+ qint64 offset = 0;
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ for (QMultiHash<QString, RCCFileInfo*>::iterator it = file->m_children.begin();
+ it != file->m_children.end(); ++it) {
+ RCCFileInfo *child = it.value();
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ if (names.contains(child->m_name)) {
+ child->m_nameOffset = names.value(child->m_name);
+ } else {
+ names.insert(child->m_name, offset);
+ offset = child->writeDataName(*this, offset);
+ }
+ }
+ }
+ if (m_format == C_Code)
+ writeString("\n};\n\n");
+ return true;
+}
+
+static bool qt_rcc_compare_hash(const RCCFileInfo *left, const RCCFileInfo *right)
+{
+ return qt_hash(left->m_name) < qt_hash(right->m_name);
+}
+
+bool RCCResourceLibrary::writeDataStructure()
+{
+ if (m_format == C_Code)
+ writeString("static const unsigned char qt_resource_struct[] = {\n");
+ else if (m_format == Binary)
+ m_treeOffset = m_out.size();
+ QStack<RCCFileInfo*> pending;
+
+ if (!m_root)
+ return false;
+
+ //calculate the child offsets (flat)
+ pending.push(m_root);
+ int offset = 1;
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+ file->m_childOffset = offset;
+
+ //sort by hash value for binary lookup
+ QList<RCCFileInfo*> m_children = file->m_children.values();
+ std::sort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
+
+ //write out the actual data now
+ for (int i = 0; i < m_children.size(); ++i) {
+ RCCFileInfo *child = m_children.at(i);
+ ++offset;
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ }
+ }
+
+ //write out the structure (ie iterate again!)
+ pending.push(m_root);
+ m_root->writeDataInfo(*this);
+ while (!pending.isEmpty()) {
+ RCCFileInfo *file = pending.pop();
+
+ //sort by hash value for binary lookup
+ QList<RCCFileInfo*> m_children = file->m_children.values();
+ std::sort(m_children.begin(), m_children.end(), qt_rcc_compare_hash);
+
+ //write out the actual data now
+ for (int i = 0; i < m_children.size(); ++i) {
+ RCCFileInfo *child = m_children.at(i);
+ child->writeDataInfo(*this);
+ if (child->m_flags & RCCFileInfo::Directory)
+ pending.push(child);
+ }
+ }
+ if (m_format == C_Code)
+ writeString("\n};\n\n");
+
+ return true;
+}
+
+void RCCResourceLibrary::writeMangleNamespaceFunction(const QByteArray &name)
+{
+ if (m_useNameSpace) {
+ writeString("QT_MANGLE_NAMESPACE(");
+ writeByteArray(name);
+ writeChar(')');
+ } else {
+ writeByteArray(name);
+ }
+}
+
+void RCCResourceLibrary::writeAddNamespaceFunction(const QByteArray &name)
+{
+ if (m_useNameSpace) {
+ writeString("QT_PREPEND_NAMESPACE(");
+ writeByteArray(name);
+ writeChar(')');
+ } else {
+ writeByteArray(name);
+ }
+}
+
+bool RCCResourceLibrary::writeInitializer()
+{
+ if (m_format == C_Code) {
+ //write("\nQT_BEGIN_NAMESPACE\n");
+ QString initName = m_initName;
+ if (!initName.isEmpty()) {
+ initName.prepend(QLatin1Char('_'));
+ initName.replace(QRegularExpression(QLatin1String("[^a-zA-Z0-9_]")), QLatin1String("_"));
+ }
+
+ //init
+ if (m_useNameSpace)
+ writeString("QT_BEGIN_NAMESPACE\n\n");
+ if (m_root) {
+ writeString("extern Q_CORE_EXPORT bool qRegisterResourceData\n "
+ "(int, const unsigned char *, "
+ "const unsigned char *, const unsigned char *);\n\n");
+ writeString("extern Q_CORE_EXPORT bool qUnregisterResourceData\n "
+ "(int, const unsigned char *, "
+ "const unsigned char *, const unsigned char *);\n\n");
+ }
+ if (m_useNameSpace)
+ writeString("QT_END_NAMESPACE\n\n\n");
+ QString initResources = QLatin1String("qInitResources");
+ initResources += initName;
+ writeString("int ");
+ writeMangleNamespaceFunction(initResources.toLatin1());
+ writeString("()\n{\n");
+
+ if (m_root) {
+ writeString(" ");
+ writeAddNamespaceFunction("qRegisterResourceData");
+ writeString("\n (0x01, qt_resource_struct, "
+ "qt_resource_name, qt_resource_data);\n");
+ }
+ writeString(" return 1;\n");
+ writeString("}\n\n");
+ writeString("Q_CONSTRUCTOR_FUNCTION(");
+ writeMangleNamespaceFunction(initResources.toLatin1());
+ writeString(")\n\n");
+
+ //cleanup
+ QString cleanResources = QLatin1String("qCleanupResources");
+ cleanResources += initName;
+ writeString("int ");
+ writeMangleNamespaceFunction(cleanResources.toLatin1());
+ writeString("()\n{\n");
+ if (m_root) {
+ writeString(" ");
+ writeAddNamespaceFunction("qUnregisterResourceData");
+ writeString("\n (0x01, qt_resource_struct, "
+ "qt_resource_name, qt_resource_data);\n");
+ }
+ writeString(" return 1;\n");
+ writeString("}\n\n");
+ writeString("Q_DESTRUCTOR_FUNCTION(");
+ writeMangleNamespaceFunction(cleanResources.toLatin1());
+ writeString(")\n\n");
+ } else if (m_format == Binary) {
+ int i = 4;
+ char *p = m_out.data();
+ p[i++] = 0; // 0x01
+ p[i++] = 0;
+ p[i++] = 0;
+ p[i++] = 1;
+
+ p[i++] = (m_treeOffset >> 24) & 0xff;
+ p[i++] = (m_treeOffset >> 16) & 0xff;
+ p[i++] = (m_treeOffset >> 8) & 0xff;
+ p[i++] = (m_treeOffset >> 0) & 0xff;
+
+ p[i++] = (m_dataOffset >> 24) & 0xff;
+ p[i++] = (m_dataOffset >> 16) & 0xff;
+ p[i++] = (m_dataOffset >> 8) & 0xff;
+ p[i++] = (m_dataOffset >> 0) & 0xff;
+
+ p[i++] = (m_namesOffset >> 24) & 0xff;
+ p[i++] = (m_namesOffset >> 16) & 0xff;
+ p[i++] = (m_namesOffset >> 8) & 0xff;
+ p[i++] = (m_namesOffset >> 0) & 0xff;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/libs/ifwtools/rcc/rcc.h b/src/libs/ifwtools/rcc/rcc.h
new file mode 100644
index 000000000..ecf408dbc
--- /dev/null
+++ b/src/libs/ifwtools/rcc/rcc.h
@@ -0,0 +1,140 @@
+/**************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef RCC_H
+#define RCC_H
+
+#include <QtCore/QStringList>
+#include <QtCore/QHash>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+class RCCFileInfo;
+class QIODevice;
+class QTextStream;
+
+
+class RCCResourceLibrary
+{
+ RCCResourceLibrary(const RCCResourceLibrary &);
+ RCCResourceLibrary &operator=(const RCCResourceLibrary &);
+
+public:
+ RCCResourceLibrary();
+ ~RCCResourceLibrary();
+
+ bool output(QIODevice &out, QIODevice &errorDevice);
+
+ bool readFiles(bool ignoreErrors, QIODevice &errorDevice);
+
+ enum Format { Binary, C_Code };
+ void setFormat(Format f) { m_format = f; }
+ Format format() const { return m_format; }
+
+ void setInputFiles(const QStringList &files) { m_fileNames = files; }
+ QStringList inputFiles() const { return m_fileNames; }
+
+ QStringList dataFiles() const;
+
+ // Return a map of resource identifier (':/newPrefix/images/p1.png') to file.
+ typedef QHash<QString, QString> ResourceDataFileMap;
+ ResourceDataFileMap resourceDataFileMap() const;
+
+ void setVerbose(bool b) { m_verbose = b; }
+ bool verbose() const { return m_verbose; }
+
+ void setInitName(const QString &name) { m_initName = name; }
+ QString initName() const { return m_initName; }
+
+ void setCompressLevel(int c) { m_compressLevel = c; }
+ int compressLevel() const { return m_compressLevel; }
+
+ void setCompressThreshold(int t) { m_compressThreshold = t; }
+ int compressThreshold() const { return m_compressThreshold; }
+
+ void setResourceRoot(const QString &root) { m_resourceRoot = root; }
+ QString resourceRoot() const { return m_resourceRoot; }
+
+ void setUseNameSpace(bool v) { m_useNameSpace = v; }
+ bool useNameSpace() const { return m_useNameSpace; }
+
+ QStringList failedResources() const { return m_failedResources; }
+
+private:
+ struct Strings {
+ Strings();
+ const QString TAG_RCC;
+ const QString TAG_RESOURCE;
+ const QString TAG_FILE;
+ const QString ATTRIBUTE_LANG;
+ const QString ATTRIBUTE_PREFIX;
+ const QString ATTRIBUTE_ALIAS;
+ const QString ATTRIBUTE_THRESHOLD;
+ const QString ATTRIBUTE_COMPRESS;
+ };
+ friend class RCCFileInfo;
+ void reset();
+ bool addFile(const QString &alias, const RCCFileInfo &file);
+ bool interpretResourceFile(QIODevice *inputDevice, const QString &file,
+ QString currentPath = QString(), bool ignoreErrors = false);
+ bool writeHeader();
+ bool writeDataBlobs();
+ bool writeDataNames();
+ bool writeDataStructure();
+ bool writeInitializer();
+ void writeMangleNamespaceFunction(const QByteArray &name);
+ void writeAddNamespaceFunction(const QByteArray &name);
+ void writeHex(quint8 number);
+ void writeNumber2(quint16 number);
+ void writeNumber4(quint32 number);
+ void writeChar(char c) { m_out.append(c); }
+ void writeByteArray(const QByteArray &);
+ void write(const char *, int len);
+
+ const Strings m_strings;
+ RCCFileInfo *m_root;
+ QStringList m_fileNames;
+ QString m_resourceRoot;
+ QString m_initName;
+ Format m_format;
+ bool m_verbose;
+ int m_compressLevel;
+ int m_compressThreshold;
+ int m_treeOffset;
+ int m_namesOffset;
+ int m_dataOffset;
+ bool m_useNameSpace;
+ QStringList m_failedResources;
+ QIODevice *m_errorDevice;
+ QByteArray m_out;
+};
+
+QT_END_NAMESPACE
+
+#endif // RCC_H
diff --git a/src/libs/ifwtools/rcc/rccmain.cpp b/src/libs/ifwtools/rcc/rccmain.cpp
new file mode 100644
index 000000000..47d8fa8de
--- /dev/null
+++ b/src/libs/ifwtools/rcc/rccmain.cpp
@@ -0,0 +1,239 @@
+/**************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "rcc.h"
+#include "qcorecmdlineargs_p.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+
+QT_BEGIN_NAMESPACE
+
+void showHelp(const QString &argv0, const QString &error)
+{
+ fprintf(stderr, "Qt resource compiler\n");
+ if (!error.isEmpty())
+ fprintf(stderr, "%s: %s\n", qPrintable(argv0), qPrintable(error));
+ fprintf(stderr, "Usage: %s [options] <inputs>\n\n"
+ "Options:\n"
+ " -o file write output to file rather than stdout\n"
+ " -name name create an external initialization function with name\n"
+ " -threshold level threshold to consider compressing files\n"
+ " -compress level compress input files by level\n"
+ " -root path prefix resource access path with root path\n"
+ " -no-compress disable all compression\n"
+ " -binary output a binary file for use as a dynamic resource\n"
+ " -namespace turn off namespace macros\n"
+ " -project Output a resource file containing all\n"
+ " files from the current directory\n"
+ " -version display version\n"
+ " -help display this information\n",
+ qPrintable(argv0));
+}
+
+void dumpRecursive(const QDir &dir, QTextStream &out)
+{
+ QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
+ | QDir::NoSymLinks);
+ foreach (const QFileInfo &entry, entries) {
+ if (entry.isDir()) {
+ dumpRecursive(entry.filePath(), out);
+ } else {
+ out << QLatin1String("<file>")
+ << entry.filePath()
+ << QLatin1String("</file>\n");
+ }
+ }
+}
+
+int createProject(const QString &outFileName)
+{
+ QFile file;
+ bool isOk = false;
+ if (outFileName.isEmpty()) {
+ isOk = file.open(stdout, QFile::WriteOnly | QFile::Text);
+ } else {
+ file.setFileName(outFileName);
+ isOk = file.open(QFile::WriteOnly | QFile::Text);
+ }
+ if (!isOk) {
+ fprintf(stderr, "Unable to open %s: %s\n",
+ outFileName.isEmpty() ? qPrintable(outFileName) : "standard output",
+ qPrintable(file.errorString()));
+ return 1;
+ }
+
+ QTextStream out(&file);
+ out << QLatin1String("<!DOCTYPE RCC><RCC version=\"1.0\">\n"
+ "<qresource>\n");
+
+ // use "." as dir to get relative file paths
+ dumpRecursive(QDir(QLatin1String(".")), out);
+
+ out << QLatin1String("</qresource>\n"
+ "</RCC>\n");
+
+ return 0;
+}
+
+int runRcc(int argc, char *argv[])
+{
+ QString outFilename;
+ bool helpRequested = false;
+ bool list = false;
+ bool projectRequested = false;
+ QStringList filenamesIn;
+
+ QStringList args = qCmdLineArgs(argc, argv);
+
+ RCCResourceLibrary library;
+
+ //parse options
+ QString errorMsg;
+ for (int i = 1; i < args.count() && errorMsg.isEmpty(); i++) {
+ if (args[i].isEmpty())
+ continue;
+ if (args[i][0] == QLatin1Char('-')) { // option
+ QString opt = args[i];
+ if (opt == QLatin1String("-o")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing output name");
+ break;
+ }
+ outFilename = args[++i];
+ } else if (opt == QLatin1String("-name")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing target name");
+ break;
+ }
+ library.setInitName(args[++i]);
+ } else if (opt == QLatin1String("-root")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing root path");
+ break;
+ }
+ library.setResourceRoot(QDir::cleanPath(args[++i]));
+ if (library.resourceRoot().isEmpty()
+ || library.resourceRoot().at(0) != QLatin1Char('/'))
+ errorMsg = QLatin1String("Root must start with a /");
+ } else if (opt == QLatin1String("-compress")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing compression level");
+ break;
+ }
+ library.setCompressLevel(args[++i].toInt());
+ } else if (opt == QLatin1String("-threshold")) {
+ if (!(i < argc-1)) {
+ errorMsg = QLatin1String("Missing compression threshold");
+ break;
+ }
+ library.setCompressThreshold(args[++i].toInt());
+ } else if (opt == QLatin1String("-binary")) {
+ library.setFormat(RCCResourceLibrary::Binary);
+ } else if (opt == QLatin1String("-namespace")) {
+ library.setUseNameSpace(!library.useNameSpace());
+ } else if (opt == QLatin1String("-verbose")) {
+ library.setVerbose(true);
+ } else if (opt == QLatin1String("-list")) {
+ list = true;
+ } else if (opt == QLatin1String("-version") || opt == QLatin1String("-v")) {
+ fprintf(stderr, "Qt Resource Compiler version %s\n", QT_VERSION_STR);
+ return 1;
+ } else if (opt == QLatin1String("-help") || opt == QLatin1String("-h")) {
+ helpRequested = true;
+ } else if (opt == QLatin1String("-no-compress")) {
+ library.setCompressLevel(-2);
+ } else if (opt == QLatin1String("-project")) {
+ projectRequested = true;
+ } else {
+ errorMsg = QString::fromLatin1("Unknown option: '%1'").arg(args[i]);
+ }
+ } else {
+ if (!QFile::exists(args[i])) {
+ qWarning("%s: File does not exist '%s'",
+ qPrintable(args[0]), qPrintable(args[i]));
+ return 1;
+ }
+ filenamesIn.append(args[i]);
+ }
+ }
+
+ if (projectRequested && !helpRequested) {
+ return createProject(outFilename);
+ }
+
+ if (!filenamesIn.size() || !errorMsg.isEmpty() || helpRequested) {
+ showHelp(args[0], errorMsg);
+ return 1;
+ }
+ QFile errorDevice;
+ errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text);
+
+ if (library.verbose())
+ errorDevice.write("Qt resource compiler\n");
+
+ library.setInputFiles(filenamesIn);
+
+ if (!library.readFiles(list, errorDevice))
+ return 1;
+
+ // open output
+ QFile out;
+ QIODevice::OpenMode mode = QIODevice::WriteOnly;
+ if (library.format() == RCCResourceLibrary::C_Code)
+ mode |= QIODevice::Text;
+
+ if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
+ // using this overload close() only flushes.
+ out.open(stdout, mode);
+ } else {
+ out.setFileName(outFilename);
+ if (!out.open(mode)) {
+ const QString msg = QString::fromUtf8("Unable to open %1 for writing: %2\n").arg(outFilename).arg(out.errorString());
+ errorDevice.write(msg.toUtf8());
+ return 1;
+ }
+ }
+
+ // do the task
+ if (list) {
+ const QStringList data = library.dataFiles();
+ for (int i = 0; i < data.size(); ++i) {
+ out.write(qPrintable(QDir::cleanPath(data.at(i))));
+ out.write("\n");
+ }
+ return 0;
+ }
+
+ return library.output(out, errorDevice) ? 0 : 1;
+}
+
+QT_END_NAMESPACE
diff --git a/src/libs/ifwtools/repositorygen.cpp b/src/libs/ifwtools/repositorygen.cpp
new file mode 100644
index 000000000..9232a02d4
--- /dev/null
+++ b/src/libs/ifwtools/repositorygen.cpp
@@ -0,0 +1,1211 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "repositorygen.h"
+
+#include "constants.h"
+#include "fileio.h"
+#include "fileutils.h"
+#include "errors.h"
+#include "globals.h"
+#include "archivefactory.h"
+#include "metadata.h"
+#include "settings.h"
+#include "qinstallerglobal.h"
+#include "utils.h"
+#include "scriptengine.h"
+
+#include "updater.h"
+
+#include <QtCore/QDirIterator>
+#include <QtCore/QRegularExpression>
+
+#include <QtXml/QDomDocument>
+#include <QTemporaryDir>
+
+#include <iostream>
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+using namespace QInstaller;
+using namespace QInstallerTools;
+
+void QInstallerTools::printRepositoryGenOptions()
+{
+ std::cout << " -p|--packages dir The directory containing the available packages." << std::endl;
+ std::cout << " This entry can be given multiple times." << std::endl;
+ std::cout << " --repository dir The directory containing the available repository." << std::endl;
+ std::cout << " This entry can be given multiple times." << std::endl;
+
+ std::cout << " -e|--exclude p1,...,pn Exclude the given packages." << std::endl;
+ std::cout << " -i|--include p1,...,pn Include the given packages and their dependencies" << std::endl;
+ std::cout << " from the repository." << std::endl;
+
+ std::cout << " --ignore-translations Do not use any translation" << std::endl;
+ std::cout << " --ignore-invalid-packages Ignore all invalid packages instead of aborting." << std::endl;
+ std::cout << " --ignore-invalid-repositories Ignore all invalid repositories instead of aborting." << std::endl;
+ std::cout << " -s|--sha-update p1,...,pn List of packages which are updated using" <<std::endl;
+ std::cout << " content sha1 instead of version number." << std::endl;
+}
+
+QString QInstallerTools::makePathAbsolute(const QString &path)
+{
+ if (QFileInfo(path).isRelative())
+ return QDir::current().absoluteFilePath(path);
+ return path;
+}
+
+void QInstallerTools::copyWithException(const QString &source, const QString &target, const QString &kind)
+{
+ qDebug() << "Copying associated" << kind << "file" << source;
+
+ const QFileInfo targetFileInfo(target);
+ if (!targetFileInfo.dir().exists())
+ QInstaller::mkpath(targetFileInfo.absolutePath());
+
+ QFile sourceFile(source);
+ if (!sourceFile.copy(target)) {
+ qDebug() << "failed!\n";
+ throw QInstaller::Error(QString::fromLatin1("Cannot copy the %1 file from \"%2\" to \"%3\": "
+ "%4").arg(kind, QDir::toNativeSeparators(source), QDir::toNativeSeparators(target),
+ /* in case of an existing target the error String does not show the file */
+ (targetFileInfo.exists() ? QLatin1String("Target already exist.") : sourceFile.errorString())));
+ }
+
+ qDebug() << "done.";
+}
+
+static QStringList copyFilesFromNode(const QString &parentNode, const QString &childNode, const QString &attr,
+ const QString &kind, const QDomNode &package, const PackageInfo &info, const QString &targetDir)
+{
+ QStringList copiedFiles;
+ const QDomNodeList nodes = package.firstChildElement(parentNode).childNodes();
+ for (int i = 0; i < nodes.count(); ++i) {
+ const QDomNode node = nodes.at(i);
+ if (node.nodeName() != childNode)
+ continue;
+
+ const QDir dir(QString::fromLatin1("%1/meta").arg(info.directory));
+ const QString filter = attr.isEmpty() ? node.toElement().text() : node.toElement().attribute(attr);
+ const QStringList files = dir.entryList(QStringList(filter), QDir::Files);
+ if (files.isEmpty()) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot find any %1 matching \"%2\" "
+ "while copying %1 of \"%3\".").arg(kind, filter, info.name));
+ }
+
+ foreach (const QString &file, files) {
+ const QString source(QString::fromLatin1("%1/meta/%2").arg(info.directory, file));
+ const QString target(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, file));
+ copyWithException(source, target, kind);
+ copiedFiles.append(file);
+ }
+ }
+ return copiedFiles;
+}
+
+/*
+ Returns \c true if the \a file is an archive or an SHA1 checksum
+ file for an archive, /c false otherwise.
+*/
+static bool isArchiveOrChecksum(const QString &file)
+{
+ if (file.endsWith(QLatin1String(".sha1")))
+ return true;
+
+ for (auto &supportedSuffix : ArchiveFactory::supportedTypes()) {
+ if (file.endsWith(supportedSuffix))
+ return true;
+ }
+ return false;
+}
+
+/*
+ Fills the package \a info with the name of the metadata archive when applicable. Returns
+ \c true if the component has metadata compressed in an archive or uncompressed to cache, or
+ if the metadata archive is redundant. Returns \c false if the component should have metadata
+ but none was found.
+*/
+static bool findMetaFile(const QString &repositoryDir, const QDomElement &packageUpdate, PackageInfo &info)
+{
+ // Note: the order here is important, when updating from an existing
+ // repository we shouldn't drop the empty metadata archives.
+
+ // 1. First, try with normal repository structure
+ QString metaFile = QString::fromLatin1("%1/%3%2").arg(info.directory,
+ QString::fromLatin1("meta.7z"), info.version);
+
+ if (QFileInfo::exists(metaFile)) {
+ info.metaFile = metaFile;
+ return true;
+ }
+
+ // 2. If that does not work, check for fetched temporary repository structure
+ metaFile = QString::fromLatin1("%1/%2-%3-%4").arg(repositoryDir,
+ info.name, info.version, QString::fromLatin1("meta.7z"));
+
+ if (QFileInfo::exists(metaFile)) {
+ info.metaFile = metaFile;
+ return true;
+ }
+
+ // 3. Try with the cached metadata directory structure
+ const QDir packageDir(info.directory);
+ const QStringList cachedMetaFiles = packageDir.entryList(QDir::Files);
+ for (auto &file : cachedMetaFiles) {
+ if (!isArchiveOrChecksum(file))
+ return true; // Return for first non-archive file
+ }
+
+ // 4. The meta archive may be redundant, skip in that case (cached item from a
+ // repository that has empty meta archive)
+ bool metaElementFound = false;
+ const QDomNodeList c1 = packageUpdate.childNodes();
+ for (int i = 0; i < c1.count(); ++i) {
+ const QDomElement e1 = c1.at(i).toElement();
+ for (const QString &meta : scMetaElements) {
+ if (e1.tagName() == meta) {
+ metaElementFound = true;
+ break;
+ }
+ }
+ }
+ return !metaElementFound;
+}
+
+void QInstallerTools::copyMetaData(const QString &_targetDir, const QString &metaDataDir,
+ const PackageInfoVector &packages, const QString &appName, const QString &appVersion,
+ const QStringList &uniteMetadatas)
+{
+ const QString targetDir = makePathAbsolute(_targetDir);
+ if (!QFile::exists(targetDir))
+ QInstaller::mkpath(targetDir);
+
+ QDomDocument doc;
+ QDomElement root;
+ QFile existingUpdatesXml(QFileInfo(metaDataDir, QLatin1String("Updates.xml")).absoluteFilePath());
+ if (existingUpdatesXml.open(QIODevice::ReadOnly) && doc.setContent(&existingUpdatesXml)) {
+ root = doc.documentElement();
+ // remove entry for this component from existing Updates.xml, if found
+ foreach (const PackageInfo &info, packages) {
+ const QDomNodeList packageNodes = root.childNodes();
+ for (int i = packageNodes.count() - 1; i >= 0; --i) {
+ const QDomNode node = packageNodes.at(i);
+ if (node.nodeName() != QLatin1String("PackageUpdate"))
+ continue;
+ if (node.firstChildElement(QLatin1String("Name")).text() != info.name)
+ continue;
+ root.removeChild(node);
+ }
+ }
+ existingUpdatesXml.close();
+ } else {
+ root = doc.createElement(QLatin1String("Updates"));
+ root.appendChild(doc.createElement(QLatin1String("ApplicationName"))).appendChild(doc
+ .createTextNode(appName));
+ root.appendChild(doc.createElement(QLatin1String("ApplicationVersion"))).appendChild(doc
+ .createTextNode(appVersion));
+ root.appendChild(doc.createElement(QLatin1String("Checksum"))).appendChild(doc
+ .createTextNode(QLatin1String("true")));
+ }
+
+ foreach (const PackageInfo &info, packages) {
+ if (info.metaFile.isEmpty() && info.metaNode.isEmpty()) {
+ if (!QDir(targetDir).mkpath(info.name))
+ throw QInstaller::Error(QString::fromLatin1("Cannot create directory \"%1\".").arg(info.name));
+
+ const QString packageXmlPath = QString::fromLatin1("%1/meta/package.xml").arg(info.directory);
+ qDebug() << "Copy meta data for package" << info.name << "using" << packageXmlPath;
+
+ QFile file(packageXmlPath);
+ QInstaller::openForRead(&file);
+
+ QString errMsg;
+ int line = 0;
+ int column = 0;
+ QDomDocument packageXml;
+ if (!packageXml.setContent(&file, &errMsg, &line, &column)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot parse \"%1\": line: %2, column: %3: %4 (%5)")
+ .arg(QDir::toNativeSeparators(packageXmlPath)).arg(line).arg(column).arg(errMsg, info.name));
+ }
+
+ QDomElement update = doc.createElement(QLatin1String("PackageUpdate"));
+ QDomNode nameElement = update.appendChild(doc.createElement(QLatin1String("Name")));
+ nameElement.appendChild(doc.createTextNode(info.name));
+
+ // list of current unused or later transformed tags
+ QStringList blackList;
+ blackList << QLatin1String("UserInterfaces") << QLatin1String("Translations") <<
+ QLatin1String("Licenses") << QLatin1String("Name") << QLatin1String("Operations");
+
+ bool foundDefault = false;
+ bool foundVirtual = false;
+ bool foundDisplayName = false;
+ bool foundDownloadableArchives = false;
+ bool foundCheckable = false;
+ const QDomNode package = packageXml.firstChildElement(QLatin1String("Package"));
+ const QDomNodeList childNodes = package.childNodes();
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomNode node = childNodes.at(i);
+ const QString key = node.nodeName();
+
+ if (key == QLatin1String("Default"))
+ foundDefault = true;
+ if (key == QLatin1String("Virtual"))
+ foundVirtual = true;
+ if (key == QLatin1String("DisplayName"))
+ foundDisplayName = true;
+ if (key == QLatin1String("DownloadableArchives"))
+ foundDownloadableArchives = true;
+ if (key == QLatin1String("Checkable"))
+ foundCheckable = true;
+ if (node.isComment() || blackList.contains(key))
+ continue; // just skip comments and some tags...
+
+ QDomElement element = doc.createElement(key);
+ for (int j = 0; j < node.attributes().size(); ++j) {
+ element.setAttribute(node.attributes().item(j).toAttr().name(),
+ node.attributes().item(j).toAttr().value());
+ }
+ update.appendChild(element).appendChild(doc.createTextNode(node.toElement().text()));
+ }
+
+ if (foundDefault && foundVirtual) {
+ throw QInstaller::Error(QString::fromLatin1("Error: <Default> and <Virtual> elements are "
+ "mutually exclusive in file \"%1\".").arg(QDir::toNativeSeparators(packageXmlPath)));
+ }
+
+ if (foundDefault && foundCheckable) {
+ throw QInstaller::Error(QString::fromLatin1("Error: <Default> and <Checkable>"
+ "elements are mutually exclusive in file \"%1\".")
+ .arg(QDir::toNativeSeparators(packageXmlPath)));
+ }
+
+ if (!foundDisplayName) {
+ qWarning() << "No DisplayName tag found at" << info.name << ", using component Name instead.";
+ QDomElement displayNameElement = doc.createElement(QLatin1String("DisplayName"));
+ update.appendChild(displayNameElement).appendChild(doc.createTextNode(info.name));
+ }
+
+ // get the size of the data
+ quint64 componentSize = 0;
+ quint64 compressedComponentSize = 0;
+
+ const QDir::Filters filters = QDir::Files | QDir::NoDotAndDotDot;
+ const QDir dataDir = QString::fromLatin1("%1/%2/data").arg(metaDataDir, info.name);
+ const QFileInfoList entries = dataDir.exists() ? dataDir.entryInfoList(filters | QDir::Dirs)
+ : QDir(QString::fromLatin1("%1/%2").arg(metaDataDir, info.name)).entryInfoList(filters);
+ qDebug() << "calculate size of directory" << dataDir.absolutePath();
+ foreach (const QFileInfo &fi, entries) {
+ try {
+ QScopedPointer<AbstractArchive> archive(ArchiveFactory::instance().create(fi.filePath()));
+ if (fi.isDir()) {
+ QDirIterator recursDirIt(fi.filePath(), QDirIterator::Subdirectories);
+ while (recursDirIt.hasNext()) {
+ recursDirIt.next();
+ const quint64 size = QInstaller::fileSize(recursDirIt.fileInfo());
+ componentSize += size;
+ compressedComponentSize += size;
+ }
+ } else if (archive && archive->open(QIODevice::ReadOnly) && archive->isSupported()) {
+ // if it's an archive already, list its files and sum the uncompressed sizes
+ compressedComponentSize += fi.size();
+
+ QVector<ArchiveEntry>::const_iterator fileIt;
+ const QVector<ArchiveEntry> files = archive->list();
+ for (fileIt = files.begin(); fileIt != files.end(); ++fileIt)
+ componentSize += fileIt->uncompressedSize;
+ } else {
+ // otherwise just add its size
+ const quint64 size = QInstaller::fileSize(fi);
+ componentSize += size;
+ compressedComponentSize += size;
+ }
+ } catch (const QInstaller::Error &error) {
+ qDebug().noquote() << error.message();
+ } catch(...) {
+ // ignore, that's just about the sizes - and size doesn't matter, you know?
+ }
+ }
+
+ QDomElement fileElement = doc.createElement(QLatin1String("UpdateFile"));
+ fileElement.setAttribute(QLatin1String("UncompressedSize"), componentSize);
+ fileElement.setAttribute(QLatin1String("CompressedSize"), compressedComponentSize);
+ // adding the OS attribute to be compatible with old sdks
+ fileElement.setAttribute(QLatin1String("OS"), QLatin1String("Any"));
+ update.appendChild(fileElement);
+
+ if (info.createContentSha1Node) {
+ QDomNode contentSha1Element = update.appendChild(doc.createElement(QLatin1String("ContentSha1")));
+ contentSha1Element.appendChild(doc.createTextNode(info.contentSha1));
+ }
+
+ root.appendChild(update);
+
+ // copy script files
+ copyScriptFiles(childNodes, info, foundDownloadableArchives, targetDir);
+
+ // write DownloadableArchives tag if that is missed by the user
+ if (!foundDownloadableArchives && !info.copiedFiles.isEmpty()) {
+ QStringList realContentFiles;
+ foreach (const QString &filePath, info.copiedFiles) {
+ if (!filePath.endsWith(QLatin1String(".sha1"), Qt::CaseInsensitive)) {
+ const QString fileName = QFileInfo(filePath).fileName();
+ // remove unnecessary version string from filename and add it to the list
+ realContentFiles.append(fileName.mid(info.version.size()));
+ }
+ }
+
+ update.appendChild(doc.createElement(QLatin1String("DownloadableArchives"))).appendChild(doc
+ .createTextNode(realContentFiles.join(QChar::fromLatin1(','))));
+ }
+
+ // copy user interfaces
+ const QStringList uiFiles = copyFilesFromNode(QLatin1String("UserInterfaces"),
+ QLatin1String("UserInterface"), QString(), QLatin1String("user interface"), package, info,
+ targetDir);
+ if (!uiFiles.isEmpty()) {
+ update.appendChild(doc.createElement(QLatin1String("UserInterfaces"))).appendChild(doc
+ .createTextNode(uiFiles.join(QChar::fromLatin1(','))));
+ }
+
+ // copy translations
+ QStringList trFiles;
+ if (!qApp->arguments().contains(QString::fromLatin1("--ignore-translations"))) {
+ trFiles = copyFilesFromNode(QLatin1String("Translations"), QLatin1String("Translation"),
+ QString(), QLatin1String("translation"), package, info, targetDir);
+ if (!trFiles.isEmpty()) {
+ update.appendChild(doc.createElement(QLatin1String("Translations"))).appendChild(doc
+ .createTextNode(trFiles.join(QChar::fromLatin1(','))));
+ }
+ }
+
+ // copy license files
+ const QStringList licenses = copyFilesFromNode(QLatin1String("Licenses"), QLatin1String("License"),
+ QLatin1String("file"), QLatin1String("license"), package, info, targetDir);
+ if (!licenses.isEmpty()) {
+ foreach (const QString &trFile, trFiles) {
+ // Copy translated license file based on the assumption that it will have the same base name
+ // as the original license plus the file name of an existing translation file without suffix.
+ foreach (const QString &license, licenses) {
+ const QFileInfo untranslated(license);
+ const QString translatedLicense = QString::fromLatin1("%2_%3.%4").arg(untranslated
+ .baseName(), QFileInfo(trFile).baseName(), untranslated.completeSuffix());
+ // ignore copy failure, that's just about the translations
+ QFile::copy(QString::fromLatin1("%1/meta/%2").arg(info.directory).arg(translatedLicense),
+ QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, translatedLicense));
+ }
+ }
+ update.appendChild(package.firstChildElement(QLatin1String("Licenses")).cloneNode());
+ }
+ // operations
+ update.appendChild(package.firstChildElement(QLatin1String("Operations")).cloneNode());
+ } else {
+ // Extract metadata from archive
+ if (!info.metaFile.isEmpty()){
+ QScopedPointer<AbstractArchive> metaFile(ArchiveFactory::instance().create(info.metaFile));
+ if (!metaFile) {
+ throw QInstaller::Error(QString::fromLatin1("Could not create handler "
+ "object for archive \"%1\": \"%2\".").arg(info.metaFile, QLatin1String(Q_FUNC_INFO)));
+ }
+ if (!(metaFile->open(QIODevice::ReadOnly) && metaFile->extract(targetDir))) {
+ throw Error(QString::fromLatin1("Could not extract archive \"%1\": %2").arg(
+ QDir::toNativeSeparators(info.metaFile), metaFile->errorString()));
+ }
+ } else {
+ // The metadata may have been already extracted, i.e. when reading from a
+ // local repository cache.
+ const QDir packageDir(info.directory);
+ const QStringList metaFiles = packageDir.entryList(QDir::Files);
+ for (auto &file : metaFiles) {
+ if (isArchiveOrChecksum(file))
+ continue; // Skip data archives
+
+ const QString source(QString::fromLatin1("%1/%2").arg(info.directory, file));
+ const QString target(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, file));
+ copyWithException(source, target, QLatin1String("cached metadata"));
+ }
+ }
+
+ // Restore "PackageUpdate" node;
+ QDomDocument update;
+ if (!update.setContent(info.metaNode)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot restore \"PackageUpdate\" description for node %1").arg(info.name));
+ }
+
+ root.appendChild(update.documentElement());
+ }
+ }
+
+ // Packages can be in repositories using different meta formats,
+ // always extract unified meta if given as argument.
+ foreach (const QString uniteMetadata, uniteMetadatas) {
+ const QString metaFilePath = QFileInfo(metaDataDir, uniteMetadata).absoluteFilePath();
+ QScopedPointer<AbstractArchive> metaFile(ArchiveFactory::instance().create(metaFilePath));
+ if (!metaFile) {
+ throw QInstaller::Error(QString::fromLatin1("Could not create handler "
+ "object for archive \"%1\": \"%2\".").arg(metaFilePath, QLatin1String(Q_FUNC_INFO)));
+ }
+ if (!(metaFile->open(QIODevice::ReadOnly) && metaFile->extract(targetDir))) {
+ throw Error(QString::fromLatin1("Could not extract archive \"%1\": %2").arg(
+ QDir::toNativeSeparators(metaFilePath), metaFile->errorString()));
+ }
+ }
+
+ doc.appendChild(root);
+
+ QFile targetUpdatesXml(targetDir + QLatin1String("/Updates.xml"));
+ QInstaller::openForWrite(&targetUpdatesXml);
+ QInstaller::blockingWrite(&targetUpdatesXml, doc.toByteArray());
+}
+
+PackageInfoVector QInstallerTools::createListOfPackages(const QStringList &packagesDirectories,
+ QStringList *packagesToFilter, FilterType filterType, QStringList packagesUpdatedWithSha)
+{
+ qDebug() << "Collecting information about available packages...";
+
+ bool ignoreInvalidPackages = qApp->arguments().contains(QString::fromLatin1("--ignore-invalid-packages"));
+
+ PackageInfoVector dict;
+ QFileInfoList entries;
+ foreach (const QString &packagesDirectory, packagesDirectories)
+ entries.append(QDir(packagesDirectory).entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot));
+ for (QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it) {
+ if (filterType == Exclude) {
+ // Check for current file in exclude list, if found, skip it and remove it from exclude list
+ if (packagesToFilter->contains(it->fileName())) {
+ packagesToFilter->removeAll(it->fileName());
+ continue;
+ }
+ } else {
+ // Check for current file in include list, if not found, skip it; if found, remove it from include list
+ if (!packagesToFilter->contains(it->fileName()))
+ continue;
+ packagesToFilter->removeAll(it->fileName());
+ }
+ qDebug() << "Found subdirectory" << it->fileName();
+ // because the filter is QDir::Dirs - filename means the name of the subdirectory
+ if (it->fileName().contains(QLatin1Char('-'))) {
+ qDebug("When using the component \"%s\" as a dependency, "
+ "to ensure backward compatibility, you must add a colon symbol at the end, "
+ "even if you do not specify a version.",
+ qUtf8Printable(it->fileName()));
+ }
+
+ QFile file(QString::fromLatin1("%1/meta/package.xml").arg(it->filePath()));
+ QFileInfo fileInfo(file);
+ if (!fileInfo.exists()) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QString::fromLatin1("Component \"%1\" does not contain a package "
+ "description (meta/package.xml is missing).").arg(QDir::toNativeSeparators(it->fileName())));
+ }
+
+ file.open(QIODevice::ReadOnly);
+
+ QDomDocument doc;
+ QString error;
+ int errorLine = 0;
+ int errorColumn = 0;
+ if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QString::fromLatin1("Component package description in \"%1\" is invalid. "
+ "Error at line: %2, column: %3 -> %4").arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()),
+ QString::number(errorLine),
+ QString::number(errorColumn), error));
+ }
+
+ const QDomElement packageElement = doc.firstChildElement(QLatin1String("Package"));
+ const QString name = packageElement.firstChildElement(QLatin1String("Name")).text();
+ if (!name.isEmpty() && name != it->fileName()) {
+ qWarning().nospace() << "The <Name> tag in the file " << fileInfo.absoluteFilePath()
+ << " is ignored - the installer uses the path element right before the 'meta'"
+ << " (" << it->fileName() << ")";
+ }
+
+ QString releaseDate = packageElement.firstChildElement(QLatin1String("ReleaseDate")).text();
+ if (releaseDate.isEmpty()) {
+ qWarning("Release date for \"%s\" is empty! Using the current date instead.",
+ qPrintable(fileInfo.absoluteFilePath()));
+ releaseDate = QDate::currentDate().toString(Qt::ISODate);
+ }
+
+ if (!QDate::fromString(releaseDate, Qt::ISODate).isValid()) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QString::fromLatin1("Release date for \"%1\" is invalid! <ReleaseDate>%2"
+ "</ReleaseDate>. Supported format: YYYY-MM-DD").arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()),
+ releaseDate));
+ }
+
+ PackageInfo info;
+ info.name = it->fileName();
+ info.version = packageElement.firstChildElement(QLatin1String("Version")).text();
+ // Version cannot start with comparison characters, be an empty string
+ // or have whitespaces at the beginning or at the end
+ static const QRegularExpression regex(QLatin1String("^(?![<=>\\s]+)(.+)$"));
+ if (!regex.match(info.version).hasMatch() || (info.version != info.version.trimmed())) {
+ if (ignoreInvalidPackages)
+ continue;
+ throw QInstaller::Error(QString::fromLatin1("Component version for \"%1\" is invalid! <Version>%2</Version>")
+ .arg(QDir::toNativeSeparators(fileInfo.absoluteFilePath()), info.version));
+ }
+ info.dependencies = packageElement.firstChildElement(QLatin1String("Dependencies")).text()
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ info.directory = it->filePath();
+ if (packagesUpdatedWithSha.contains(info.name)) {
+ info.createContentSha1Node = true;
+ packagesUpdatedWithSha.removeOne(info.name);
+ } else {
+ info.createContentSha1Node = false;
+ }
+
+ dict.push_back(info);
+
+ qDebug() << "- it provides the package" << info.name << " - " << info.version;
+ }
+
+ if (!packagesToFilter->isEmpty() && packagesToFilter->at(0) != QString::fromLatin1(
+ "X_fake_filter_component_for_online_only_installer_X")) {
+ qWarning() << "The following explicitly given packages could not be found\n in package directory:" << *packagesToFilter;
+ }
+
+ if (dict.isEmpty())
+ qDebug() << "No available packages found at the specified location.";
+
+ if (!packagesUpdatedWithSha.isEmpty()) {
+ throw QInstaller::Error(QString::fromLatin1("The following packages could not be found in "
+ "package directory: %1").arg(packagesUpdatedWithSha.join(QLatin1String(", "))));
+ }
+
+ return dict;
+}
+
+PackageInfoVector QInstallerTools::createListOfRepositoryPackages(const QStringList &repositoryDirectories,
+ QStringList *packagesToFilter, FilterType filterType)
+{
+ qDebug() << "Collecting information about available repository packages...";
+
+ bool ignoreInvalidRepositories = qApp->arguments().contains(QString::fromLatin1("--ignore-invalid-repositories"));
+
+ PackageInfoVector dict;
+ QFileInfoList entries;
+ foreach (const QString &repositoryDirectory, repositoryDirectories)
+ entries.append(QFileInfo(repositoryDirectory));
+ for (QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it) {
+
+ qDebug() << "Process repository" << it->fileName();
+
+ QFile file(QString::fromLatin1("%1/Updates.xml").arg(it->filePath()));
+
+ QFileInfo fileInfo(file);
+ if (!fileInfo.exists()) {
+ if (ignoreInvalidRepositories) {
+ qDebug() << "- skip invalid repository";
+ continue;
+ }
+ throw QInstaller::Error(QString::fromLatin1("Repository \"%1\" does not contain a update "
+ "description (Updates.xml is missing).").arg(QDir::toNativeSeparators(it->fileName())));
+ }
+ if (!file.open(QIODevice::ReadOnly)) {
+ qDebug() << "Cannot open Updates.xml for reading:" << file.errorString();
+ continue;
+ }
+
+ QString error;
+ QDomDocument doc;
+ if (!doc.setContent(&file, &error)) {
+ qDebug().nospace() << "Cannot fetch a valid version of Updates.xml from repository "
+ << it->fileName() << ": " << error;
+ continue;
+ }
+ file.close();
+
+ const QDomElement root = doc.documentElement();
+ if (root.tagName() != QLatin1String("Updates")) {
+ throw QInstaller::Error(QCoreApplication::translate("QInstaller",
+ "Invalid content in \"%1\".").arg(QDir::toNativeSeparators(file.fileName())));
+ }
+
+ bool hasUnifiedMetaFile = false;
+ const QDomElement unifiedSha1 = root.firstChildElement(scSHA1);
+ const QDomElement unifiedMetaName = root.firstChildElement(QLatin1String("MetadataName"));
+
+ // Unified metadata takes priority over component metadata
+ if (!unifiedSha1.isNull() && !unifiedMetaName.isNull())
+ hasUnifiedMetaFile = true;
+
+ const QDomNodeList children = root.childNodes();
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if ((!el.isNull()) && (el.tagName() == QLatin1String("PackageUpdate"))) {
+ QInstallerTools::PackageInfo info;
+
+ QDomElement c1 = el.firstChildElement(QInstaller::scName);
+ if (!c1.isNull())
+ info.name = c1.text();
+ else
+ continue;
+ if (filterType == Exclude) {
+ // Check for current package in exclude list, if found, skip it
+ if (packagesToFilter->contains(info.name)) {
+ continue;
+ }
+ } else {
+ // Check for current package in include list, if not found, skip it
+ if (!packagesToFilter->contains(info.name))
+ continue;
+ }
+ c1 = el.firstChildElement(QInstaller::scVersion);
+ if (!c1.isNull())
+ info.version = c1.text();
+ else
+ continue;
+
+ info.directory = QString::fromLatin1("%1/%2").arg(it->filePath(), info.name);
+ if (!hasUnifiedMetaFile) {
+ const QDomElement sha1 = el.firstChildElement(QInstaller::scSHA1);
+ if (!sha1.isNull() && !findMetaFile(it->filePath(), el, info)) {
+ throw QInstaller::Error(QString::fromLatin1("Could not find metadata archive for component "
+ "%1 %2 in repository %3.").arg(info.name, info.version, it->filePath()));
+ }
+ }
+
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ const QDomElement c2Element = c2.at(j).toElement();
+ if (c2Element.tagName() == QInstaller::scDependencies)
+ info.dependencies = c2Element.text()
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ else if (c2Element.tagName() == QInstaller::scDownloadableArchives) {
+ QStringList names = c2Element.text()
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ foreach (const QString &name, names) {
+ info.copiedFiles.append(QString::fromLatin1("%1/%3%2").arg(info.directory,
+ name, info.version));
+ info.copiedFiles.append(QString::fromLatin1("%1/%3%2.sha1").arg(info.directory,
+ name, info.version));
+ }
+ }
+ }
+ QString metaString;
+ {
+ QTextStream metaStream(&metaString);
+ el.save(metaStream, 0);
+ }
+ info.metaNode = metaString;
+
+ bool pushToDict = true;
+ bool replacement = false;
+ // Check whether this package already exists in vector:
+ for (int i = 0; i < dict.size(); ++i) {
+ const QInstallerTools::PackageInfo oldInfo = dict.at(i);
+ if (oldInfo.name != info.name)
+ continue;
+
+ if (KDUpdater::compareVersion(info.version, oldInfo.version) > 0) {
+ // A package with newer version, it will replace the existing one.
+ dict.remove(i);
+ replacement = true;
+ } else {
+ // A package with older or same version, do not add it again.
+ pushToDict = false;
+ }
+ break;
+ }
+
+ if (pushToDict) {
+ replacement ? qDebug() << "- it provides a new version of the package" << info.name << " - " << info.version << "- replaced"
+ : qDebug() << "- it provides the package" << info.name << " - " << info.version;
+ dict.push_back(info);
+ } else {
+ qDebug() << "- it provides an old version of the package" << info.name << " - " << info.version << "- ignored";
+ }
+ }
+ }
+ }
+
+ return dict;
+}
+
+QHash<QString, QString> QInstallerTools::buildPathToVersionMapping(const PackageInfoVector &info)
+{
+ QHash<QString, QString> map;
+ foreach (const PackageInfo &inf, info)
+ map[inf.name] = inf.version;
+ return map;
+}
+
+static void writeSHA1ToNodeWithName(QDomDocument &doc, QDomNodeList &list, const QByteArray &sha1sum,
+ const QString &nodename = QString())
+{
+ if (nodename.isEmpty())
+ qDebug() << "Writing sha1sum node.";
+ else
+ qDebug() << "Searching sha1sum node for" << nodename;
+ QString sha1Value = QString::fromLatin1(sha1sum.toHex().constData());
+ for (int i = 0; i < list.size(); ++i) {
+ QDomNode curNode = list.at(i);
+ QDomNode nameTag = curNode.firstChildElement(scName);
+ if ((!nameTag.isNull() && nameTag.toElement().text() == nodename) || nodename.isEmpty()) {
+ QDomNode sha1Node = curNode.firstChildElement(scSHA1);
+ QDomNode newSha1Node = doc.createElement(scSHA1);
+ newSha1Node.appendChild(doc.createTextNode(sha1Value));
+
+ if (!sha1Node.isNull() && sha1Node.hasChildNodes()) {
+ QDomNode sha1NodeChild = sha1Node.firstChild();
+ QString sha1OldValue = sha1NodeChild.nodeValue();
+ if (sha1Value == sha1OldValue) {
+ qDebug() << "- keeping the existing sha1sum" << sha1OldValue;
+ continue;
+ } else {
+ qDebug() << "- clearing the old sha1sum" << sha1OldValue;
+ sha1Node.removeChild(sha1NodeChild);
+ }
+ }
+ if (sha1Node.isNull())
+ curNode.appendChild(newSha1Node);
+ else
+ curNode.replaceChild(newSha1Node, sha1Node);
+ qDebug() << "- writing the sha1sum" << sha1Value;
+ }
+ }
+}
+
+void QInstallerTools::createArchive(const QString &filename, const QStringList &data, Compression compression)
+{
+ QScopedPointer<AbstractArchive> targetArchive(ArchiveFactory::instance().create(filename));
+ if (!targetArchive) {
+ throw QInstaller::Error(QString::fromLatin1("Could not create handler "
+ "object for archive \"%1\": \"%2\".").arg(filename, QLatin1String(Q_FUNC_INFO)));
+ }
+ targetArchive->setCompressionLevel(compression);
+ if (!(targetArchive->open(QIODevice::WriteOnly) && targetArchive->create(data))) {
+ throw Error(QString::fromLatin1("Could not create archive \"%1\": %2").arg(
+ QDir::toNativeSeparators(filename), targetArchive->errorString()));
+ }
+}
+
+void QInstallerTools::compressMetaDirectories(const QString &repoDir, const QString &existingUnite7zUrl,
+ const QHash<QString, QString> &versionMapping, bool createSplitMetadata, bool createUnifiedMetadata)
+{
+ QDomDocument doc;
+ // use existing Updates.xml, if any
+ QFile existingUpdatesXml(QFileInfo(QDir(repoDir), QLatin1String("Updates.xml")).absoluteFilePath());
+ if (!existingUpdatesXml.open(QIODevice::ReadOnly) || !doc.setContent(&existingUpdatesXml)) {
+ qDebug() << "Cannot find Updates.xml";
+ }
+ existingUpdatesXml.close();
+
+ QDir dir(repoDir);
+ const QStringList entryList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+
+ QStringList absPaths;
+ if (createUnifiedMetadata) {
+ absPaths = unifyMetadata(repoDir, existingUnite7zUrl, doc);
+ }
+
+ if (createSplitMetadata) {
+ splitMetadata(entryList, repoDir, doc, versionMapping);
+ } else {
+ // remove the files that got compressed
+ foreach (const QString path, absPaths)
+ QInstaller::removeFiles(path, true);
+ }
+
+ QInstaller::openForWrite(&existingUpdatesXml);
+ QInstaller::blockingWrite(&existingUpdatesXml, doc.toByteArray());
+ existingUpdatesXml.close();
+}
+
+QStringList QInstallerTools::unifyMetadata(const QString &repoDir, const QString &existingRepoDir, QDomDocument doc)
+{
+ QStringList absPaths;
+ QDir dir(repoDir);
+ const QStringList entryList = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+
+ foreach (const QString &i, entryList) {
+ dir.cd(i);
+ const QString absPath = dir.absolutePath();
+ absPaths.append(absPath);
+ dir.cdUp();
+ }
+
+ QTemporaryDir existingRepoTempDir;
+ QString existingRepoTemp = existingRepoTempDir.path();
+ if (!existingRepoDir.isEmpty()) {
+ existingRepoTempDir.setAutoRemove(false);
+ QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(existingRepoDir));
+ if (!archiveFile) {
+ throw QInstaller::Error(QString::fromLatin1("Could not create handler "
+ "object for archive \"%1\": \"%2\".").arg(existingRepoDir, QLatin1String(Q_FUNC_INFO)));
+ }
+ if (!(archiveFile->open(QIODevice::ReadOnly) && archiveFile->extract(existingRepoTemp))) {
+ throw Error(QString::fromLatin1("Could not extract archive \"%1\": %2").arg(
+ QDir::toNativeSeparators(existingRepoDir), archiveFile->errorString()));
+ }
+ QDir dir2(existingRepoTemp);
+ QStringList existingRepoEntries = dir2.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString existingRepoEntry, existingRepoEntries) {
+ if (entryList.contains(existingRepoEntry)) {
+ continue;
+ } else {
+ dir2.cd(existingRepoEntry);
+ const QString absPath = dir2.absolutePath();
+ absPaths.append(absPath);
+ dir2.cdUp();
+ }
+ }
+ }
+
+ // Compress all metadata from repository to one single 7z
+ const QString metadataFilename = QDateTime::currentDateTime().
+ toString(QLatin1String("yyyy-MM-dd-hhmm")) + QLatin1String("_meta.7z");
+ const QString tmpTarget = repoDir + QDir::separator() + metadataFilename;
+ createArchive(tmpTarget, absPaths);
+
+ QFile tmp(tmpTarget);
+ tmp.open(QFile::ReadOnly);
+ const QByteArray sha1Sum = QInstaller::calculateHash(&tmp, QCryptographicHash::Sha1);
+ QDomNodeList elements = doc.elementsByTagName(QLatin1String("Updates"));
+ writeSHA1ToNodeWithName(doc, elements, sha1Sum, QString());
+
+ qDebug() << "Updating the metadata node with name " << metadataFilename;
+ if (elements.count() > 0) {
+ QDomNode node = elements.at(0);
+ QDomNode nameTag = node.firstChildElement(QLatin1String("MetadataName"));
+
+ QDomNode newNodeTag = doc.createElement(QLatin1String("MetadataName"));
+ newNodeTag.appendChild(doc.createTextNode(metadataFilename));
+
+ if (nameTag.isNull())
+ node.appendChild(newNodeTag);
+ else
+ node.replaceChild(newNodeTag, nameTag);
+ }
+ QInstaller::removeDirectory(existingRepoTemp, true);
+ return absPaths;
+}
+
+void QInstallerTools::splitMetadata(const QStringList &entryList, const QString &repoDir,
+ QDomDocument doc, const QHash<QString, QString> &versionMapping)
+{
+ QStringList absPaths;
+ QDomNodeList elements = doc.elementsByTagName(QLatin1String("PackageUpdate"));
+ QDir dir(repoDir);
+ foreach (const QString &i, entryList) {
+ dir.cd(i);
+ const QString absPath = dir.absolutePath();
+ const QString path = QString(i).remove(repoDir);
+ if (path.isNull())
+ continue;
+ const QString versionPrefix = versionMapping[path];
+ const QString fn = QLatin1String(versionPrefix.toLatin1() + "meta.7z");
+ const QString tmpTarget = repoDir + QLatin1String("/") + fn;
+
+ createArchive(tmpTarget, QStringList() << absPath);
+
+ // remove the files that got compressed
+ QInstaller::removeFiles(absPath, true);
+ QFile tmp(tmpTarget);
+ tmp.open(QFile::ReadOnly);
+ const QByteArray sha1Sum = QInstaller::calculateHash(&tmp, QCryptographicHash::Sha1);
+ writeSHA1ToNodeWithName(doc, elements, sha1Sum, path);
+ const QString finalTarget = absPath + QLatin1String("/") + fn;
+ if (!tmp.rename(finalTarget)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot move file \"%1\" to \"%2\".").arg(
+ QDir::toNativeSeparators(tmpTarget), QDir::toNativeSeparators(finalTarget)));
+ }
+ dir.cdUp();
+ }
+}
+
+void QInstallerTools::copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir)
+{
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomNode node = childNodes.at(i);
+ const QString key = node.nodeName();
+
+ if (key != QLatin1String("Script"))
+ continue;
+ const QString script = node.toElement().text();
+ if (script.isEmpty())
+ continue;
+
+ QFile scriptFile(QString::fromLatin1("%1/meta/%2").arg(info.directory, script));
+ if (!scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot open component script at \"%1\".")
+ .arg(QDir::toNativeSeparators(scriptFile.fileName())));
+ }
+
+ const QString scriptContent = QLatin1String("(function() {")
+ + QString::fromUtf8(scriptFile.readAll())
+ + QLatin1String(";"
+ " if (typeof Component == \"undefined\")"
+ " throw \"Missing Component constructor. Please check your script.\";"
+ "})();");
+
+ // if the user isn't aware of the downloadable archives value we will add it automatically later
+ foundDownloadableArchives |= scriptContent.contains(QLatin1String("addDownloadableArchive"))
+ || scriptContent.contains(QLatin1String("removeDownloadableArchive"));
+
+ static QInstaller::ScriptEngine testScriptEngine;
+ const QJSValue value = testScriptEngine.evaluate(scriptContent, scriptFile.fileName());
+ if (value.isError()) {
+ throw QInstaller::Error(QString::fromLatin1("Exception while loading component "
+ "script at \"%1\": %2").arg(QDir::toNativeSeparators(scriptFile.fileName()),
+ value.toString().isEmpty() ? QString::fromLatin1("Unknown error.") :
+ value.toString() + QStringLiteral(" on line number: ") +
+ value.property(QStringLiteral("lineNumber")).toString()));
+ }
+
+ const QString toLocation(QString::fromLatin1("%1/%2/%3").arg(targetDir, info.name, script));
+ copyWithException(scriptFile.fileName(), toLocation, QInstaller::scScript);
+ }
+}
+
+void QInstallerTools::copyComponentData(const QStringList &packageDirs, const QString &repoDir,
+ PackageInfoVector *const infos, const QString &archiveSuffix, Compression compression)
+{
+ for (int i = 0; i < infos->count(); ++i) {
+ const PackageInfo info = infos->at(i);
+ const QString name = info.name;
+ qDebug() << "Copying component data for" << name;
+
+ const QString namedRepoDir = QString::fromLatin1("%1/%2").arg(repoDir, name);
+ if (!QDir().mkpath(namedRepoDir)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot create repository directory for component \"%1\".")
+ .arg(name));
+ }
+
+ if (info.copiedFiles.isEmpty()) {
+ QStringList compressedFiles;
+ QStringList filesToCompress;
+ foreach (const QString &packageDir, packageDirs) {
+ const QDir dataDir(QString::fromLatin1("%1/%2/data").arg(packageDir, name));
+ foreach (const QString &entry, dataDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files)) {
+ QFileInfo fileInfo(dataDir.absoluteFilePath(entry));
+ if (fileInfo.isFile() && !fileInfo.isSymLink()) {
+ const QString absoluteEntryFilePath = dataDir.absoluteFilePath(entry);
+ QScopedPointer<AbstractArchive> archive(ArchiveFactory::instance()
+ .create(absoluteEntryFilePath));
+ if (archive && archive->open(QIODevice::ReadOnly) && archive->isSupported()) {
+ QFile tmp(absoluteEntryFilePath);
+ QString target = QString::fromLatin1("%1/%3%2").arg(namedRepoDir, entry, info.version);
+ qDebug() << "Copying archive from" << tmp.fileName() << "to" << target;
+ if (!tmp.copy(target)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot copy file \"%1\" to \"%2\": %3")
+ .arg(QDir::toNativeSeparators(tmp.fileName()), QDir::toNativeSeparators(target), tmp.errorString()));
+ }
+ compressedFiles.append(target);
+ } else {
+ filesToCompress.append(absoluteEntryFilePath);
+ }
+ } else if (fileInfo.isDir()) {
+ qDebug() << "Compressing data directory" << entry;
+ QString target = QString::fromLatin1("%1/%3%2.%4").arg(namedRepoDir, entry, info.version, archiveSuffix);
+ createArchive(target, QStringList() << dataDir.absoluteFilePath(entry), compression);
+ compressedFiles.append(target);
+ } else if (fileInfo.isSymLink()) {
+ filesToCompress.append(dataDir.absoluteFilePath(entry));
+ }
+ }
+ }
+
+ if (!filesToCompress.isEmpty()) {
+ qDebug() << "Compressing files found in data directory:" << filesToCompress;
+ QString target = QString::fromLatin1("%1/%2content.%3").arg(namedRepoDir, info.version, archiveSuffix);
+ createArchive(target, filesToCompress, compression);
+ compressedFiles.append(target);
+ }
+
+ foreach (const QString &target, compressedFiles) {
+ (*infos)[i].copiedFiles.append(target);
+
+ QFile archiveFile(target);
+ QFile archiveHashFile(archiveFile.fileName() + QLatin1String(".sha1"));
+
+ qDebug() << "Hash is stored in" << archiveHashFile.fileName();
+ qDebug() << "Creating hash of archive" << archiveFile.fileName();
+
+ try {
+ QInstaller::openForRead(&archiveFile);
+ const QByteArray hashOfArchiveData = QInstaller::calculateHash(&archiveFile,
+ QCryptographicHash::Sha1).toHex();
+ archiveFile.close();
+
+ QInstaller::openForWrite(&archiveHashFile);
+ archiveHashFile.write(hashOfArchiveData);
+ qDebug() << "Generated sha1 hash:" << hashOfArchiveData;
+ (*infos)[i].copiedFiles.append(archiveHashFile.fileName());
+ if ((*infos)[i].createContentSha1Node)
+ (*infos)[i].contentSha1 = QLatin1String(hashOfArchiveData);
+ archiveHashFile.close();
+ } catch (const QInstaller::Error &/*e*/) {
+ archiveFile.close();
+ archiveHashFile.close();
+ throw;
+ }
+ }
+ } else {
+ foreach (const QString &file, (*infos)[i].copiedFiles) {
+ QFileInfo fromInfo(file);
+ QFile from(file);
+ QString target = QString::fromLatin1("%1/%2").arg(namedRepoDir, fromInfo.fileName());
+ qDebug() << "Copying file from" << from.fileName() << "to" << target;
+ if (!from.copy(target)) {
+ throw QInstaller::Error(QString::fromLatin1("Cannot copy file \"%1\" to \"%2\": %3")
+ .arg(QDir::toNativeSeparators(from.fileName()), QDir::toNativeSeparators(target), from.errorString()));
+ }
+ }
+ }
+ }
+}
+
+void QInstallerTools::filterNewComponents(const QString &repositoryDir, QInstallerTools::PackageInfoVector &packages)
+{
+ QDomDocument doc;
+ QFile file(repositoryDir + QLatin1String("/Updates.xml"));
+ if (file.open(QFile::ReadOnly) && doc.setContent(&file)) {
+ const QDomElement root = doc.documentElement();
+ if (root.tagName() != QLatin1String("Updates")) {
+ throw QInstaller::Error(QCoreApplication::translate("QInstaller",
+ "Invalid content in \"%1\".").arg(QDir::toNativeSeparators(file.fileName())));
+ }
+ file.close(); // close the file, we read the content already
+
+ // read the already existing updates xml content
+ const QDomNodeList children = root.childNodes();
+ QHash <QString, QInstallerTools::PackageInfo> hash;
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if ((!el.isNull()) && (el.tagName() == QLatin1String("PackageUpdate"))) {
+ QInstallerTools::PackageInfo info;
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ const QDomElement c2Element = c2.at(j).toElement();
+ if (c2Element.tagName() == scName)
+ info.name = c2Element.text();
+ else if (c2Element.tagName() == scVersion)
+ info.version = c2Element.text();
+ }
+ hash.insert(info.name, info);
+ }
+ }
+
+ // remove all components that have no update (decision based on the version tag)
+ for (int i = packages.count() - 1; i >= 0; --i) {
+ const QInstallerTools::PackageInfo info = packages.at(i);
+
+ // check if component already exists & version did not change
+ if (hash.contains(info.name) && KDUpdater::compareVersion(info.version, hash.value(info.name).version) < 1) {
+ packages.remove(i); // the version did not change, no need to update the component
+ continue;
+ }
+ qDebug() << "Update component" << info.name << "in"<< repositoryDir << ".";
+ }
+ }
+}
+
+QString QInstallerTools::existingUniteMeta7z(const QString &repositoryDir)
+{
+ QString uniteMeta7z = QString();
+ QFile file(repositoryDir + QLatin1String("/Updates.xml"));
+ QDomDocument doc;
+ if (file.open(QFile::ReadOnly) && doc.setContent(&file)) {
+ QDomNodeList elements = doc.elementsByTagName(QLatin1String("MetadataName"));
+ if (elements.count() > 0 && elements.at(0).isElement()) {
+ uniteMeta7z = elements.at(0).toElement().text();
+ QFile metaFile(repositoryDir + QDir::separator() + uniteMeta7z);
+ if (!metaFile.exists()) {
+ throw QInstaller::Error(QString::fromLatin1("Unite meta7z \"%1\" does not exist in repository \"%2\"")
+ .arg(QDir::toNativeSeparators(metaFile.fileName()), repositoryDir));
+ }
+ }
+ }
+ return uniteMeta7z;
+}
+
+PackageInfoVector QInstallerTools::collectPackages(RepositoryInfo info, QStringList *filteredPackages, FilterType filterType, bool updateNewComponents, QStringList packagesUpdatedWithSha)
+{
+ PackageInfoVector packages;
+ PackageInfoVector precompressedPackages = QInstallerTools::createListOfRepositoryPackages(info.repositoryPackages,
+ filteredPackages, filterType);
+ packages.append(precompressedPackages);
+
+ PackageInfoVector preparedPackages = QInstallerTools::createListOfPackages(info.packages,
+ filteredPackages, filterType, packagesUpdatedWithSha);
+ packages.append(preparedPackages);
+ if (updateNewComponents) {
+ filterNewComponents(info.repositoryDir, packages);
+ }
+ foreach (const QInstallerTools::PackageInfo &package, packages) {
+ const QFileInfo fi(info.repositoryDir, package.name);
+ if (fi.exists())
+ removeDirectory(fi.absoluteFilePath());
+ }
+ return packages;
+}
+
+void QInstallerTools::createRepository(RepositoryInfo info, PackageInfoVector *packages,
+ const QString &tmpMetaDir, bool createComponentMetadata, bool createUnifiedMetadata,
+ const QString &archiveSuffix, Compression compression)
+{
+ QHash<QString, QString> pathToVersionMapping = QInstallerTools::buildPathToVersionMapping(*packages);
+
+ QStringList directories;
+ directories.append(info.packages);
+ directories.append(info.repositoryPackages);
+ QStringList unite7zFiles;
+ foreach (const QString &repositoryDirectory, info.repositoryPackages) {
+ QDirIterator it(repositoryDirectory, QStringList(QLatin1String("*_meta.7z"))
+ , QDir::Files | QDir::CaseSensitive);
+ while (it.hasNext()) {
+ it.next();
+ unite7zFiles.append(it.fileInfo().absoluteFilePath());
+ }
+ }
+ QInstallerTools::copyComponentData(directories, info.repositoryDir, packages, archiveSuffix, compression);
+ QInstallerTools::copyMetaData(tmpMetaDir, info.repositoryDir, *packages, QLatin1String("{AnyApplication}"),
+ QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)), unite7zFiles);
+
+ QString existing7z = QInstallerTools::existingUniteMeta7z(info.repositoryDir);
+ if (!existing7z.isEmpty())
+ existing7z = info.repositoryDir + QDir::separator() + existing7z;
+ QInstallerTools::compressMetaDirectories(tmpMetaDir, existing7z, pathToVersionMapping,
+ createComponentMetadata, createUnifiedMetadata);
+
+ QDirIterator it(info.repositoryDir, QStringList(QLatin1String("Updates*.xml"))
+ << QLatin1String("*_meta.7z"), QDir::Files | QDir::CaseSensitive);
+ while (it.hasNext()) {
+ it.next();
+ QFile::remove(it.fileInfo().absoluteFilePath());
+ }
+ QInstaller::moveDirectoryContents(tmpMetaDir, info.repositoryDir);
+}
diff --git a/src/libs/ifwtools/repositorygen.h b/src/libs/ifwtools/repositorygen.h
new file mode 100644
index 000000000..7ad3dd073
--- /dev/null
+++ b/src/libs/ifwtools/repositorygen.h
@@ -0,0 +1,108 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef REPOSITORYGEN_H
+#define REPOSITORYGEN_H
+
+#include "ifwtools_global.h"
+
+#include <abstractarchive.h>
+
+#include <QHash>
+#include <QString>
+#include <QStringList>
+#include <QVector>
+#include <QDomDocument>
+
+namespace QInstallerTools {
+
+struct IFWTOOLS_EXPORT PackageInfo
+{
+ QString name;
+ QString version;
+ QString directory;
+ QStringList dependencies;
+ QStringList copiedFiles;
+ QString metaFile;
+ QString metaNode;
+ QString contentSha1;
+ bool createContentSha1Node;
+};
+typedef QVector<PackageInfo> PackageInfoVector;
+typedef QInstaller::AbstractArchive::CompressionLevel Compression;
+
+enum IFWTOOLS_EXPORT FilterType {
+ Include,
+ Exclude
+};
+
+struct IFWTOOLS_EXPORT RepositoryInfo
+{
+ QStringList packages;
+ QStringList repositoryPackages;
+ QString repositoryDir;
+};
+
+void IFWTOOLS_EXPORT printRepositoryGenOptions();
+QString IFWTOOLS_EXPORT makePathAbsolute(const QString &path);
+void IFWTOOLS_EXPORT copyWithException(const QString &source, const QString &target, const QString &kind = QString());
+
+PackageInfoVector IFWTOOLS_EXPORT createListOfPackages(const QStringList &packagesDirectories, QStringList *packagesToFilter,
+ FilterType ftype, QStringList packagesUpdatedWithSha = QStringList());
+
+PackageInfoVector IFWTOOLS_EXPORT createListOfRepositoryPackages(const QStringList &repositoryDirectories, QStringList *packagesToFilter,
+ FilterType filterType);
+
+QHash<QString, QString> IFWTOOLS_EXPORT buildPathToVersionMapping(const PackageInfoVector &info);
+
+void IFWTOOLS_EXPORT createArchive(const QString &filename, const QStringList &data, Compression compression = Compression::Normal);
+
+void IFWTOOLS_EXPORT compressMetaDirectories(const QString &repoDir, const QString &existingUnite7zUrl,
+ const QHash<QString, QString> &versionMapping, bool createSplitMetadata, bool createUnifiedMetadata);
+
+QStringList unifyMetadata(const QString &repoDir, const QString &existingRepoDir, QDomDocument doc);
+void splitMetadata(const QStringList &entryList, const QString &repoDir, QDomDocument doc,
+ const QHash<QString, QString> &versionMapping);
+void copyScriptFiles(const QDomNodeList &childNodes, const PackageInfo &info, bool &foundDownloadableArchives, const QString &targetDir);
+
+void IFWTOOLS_EXPORT copyMetaData(const QString &outDir, const QString &dataDir, const PackageInfoVector &packages,
+ const QString &appName, const QString& appVersion, const QStringList &uniteMetadatas);
+void IFWTOOLS_EXPORT copyComponentData(const QStringList &packageDir, const QString &repoDir,
+ PackageInfoVector *const infos, const QString &archiveSuffix,
+ Compression compression = Compression::Normal);
+
+void IFWTOOLS_EXPORT filterNewComponents(const QString &repositoryDir, QInstallerTools::PackageInfoVector &packages);
+
+QString IFWTOOLS_EXPORT existingUniteMeta7z(const QString &repositoryDir);
+PackageInfoVector IFWTOOLS_EXPORT collectPackages(RepositoryInfo info, QStringList *filteredPackages, FilterType filterType, bool updateNewComponents, QStringList packagesUpdatedWithSha);
+void IFWTOOLS_EXPORT createRepository(RepositoryInfo info, PackageInfoVector *packages, const QString &tmpMetaDir,
+ bool createComponentMetadata, bool createUnifiedMetadata, const QString &archiveSuffix,
+ Compression compression = Compression::Normal);
+} // namespace QInstallerTools
+
+#endif // REPOSITORYGEN_H
diff --git a/src/libs/ifwtools/resources/copylibsintobundle.sh b/src/libs/ifwtools/resources/copylibsintobundle.sh
new file mode 100644
index 000000000..1ad5da4db
--- /dev/null
+++ b/src/libs/ifwtools/resources/copylibsintobundle.sh
@@ -0,0 +1,189 @@
+#!/bin/sh
+#############################################################################
+##
+## Copyright (C) 2017 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the Qt Installer Framework.
+##
+## $QT_BEGIN_LICENSE:GPL-EXCEPT$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 as published by the Free Software
+## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+
+# this script puts all libs directly needed by the bundle into it
+
+QTDIR=""
+IS_DEBUG=0
+HAVE_CORE=0
+HAVE_SVG=0
+HAVE_PHONON=0
+HAVE_SCRIPT=0
+HAVE_SQL=0
+HAVE_WEBKIT=0
+
+function handleFile()
+{
+ local FILE=$1
+ local BUNDLE=$2
+
+ # all dynamic libs directly needed by the bundle, which are not in /System/Library or in /usr/lib (which are system default libs, which we don't want)
+ local LIBS=`xcrun otool -L $FILE | grep -v 'executable_path' | grep -v '/System/Library' | grep -v '/usr/lib' | grep '/' | sed -ne 's,^ *\(.*\) (.*,\1,p'`
+
+ local lib
+ for lib in $LIBS; do
+ local NAME=`basename $lib`
+
+ if echo $NAME | grep 'QtCore' >/dev/null; then
+ HAVE_CORE=1
+ QTDIR=`echo $lib | sed -ne 's,^\(.*\)/lib/[^/]*QtCore.*$,\1,p'`
+ if echo $NAME | grep 'debug' >/dev/null; then
+ IS_DEBUG=1
+ fi
+ elif echo $NAME | grep 'QtSvg' >/dev/null; then
+ HAVE_SVG=1
+ elif echo $NAME | grep 'phonon' >/dev/null; then
+ HAVE_PHONON=1
+ elif echo $NAME | grep 'QtScript' >/dev/null; then
+ HAVE_SCRIPT=1
+ elif echo $NAME | grep 'QtSql' >/dev/null; then
+ HAVE_SQL=1
+ elif echo $NAME | grep 'QtWebKit' >/dev/null; then
+ HAVE_WEBKIT=1
+ fi
+
+ if [ `basename $FILE` != $NAME ]; then
+
+ # this part handles libraries which are macOS frameworks
+ if echo $lib | grep '\.framework' >/dev/null; then
+ local FRAMEWORKPATH=`echo $lib | sed -ne 's,\(.*\.framework\).*,\1,p'`
+ local FRAMEWORKNAME=`basename $FRAMEWORKPATH`
+ local NEWFRAMEWORKPATH=`echo $lib | sed -ne "s,.*\($FRAMEWORKNAME\),\1,p"`
+
+ # Qt installed via the precompled binaries...
+ if [ $FRAMEWORKPATH = $FRAMEWORKNAME ]; then
+ FRAMEWORKPATH="/Library/Frameworks/$FRAMEWORKNAME"
+ if [ ! -e "$FRAMEWORKPATH" ]; then
+ echo "Framework $FRAMEWORKNAME not found."
+ exit 1
+ fi
+ fi
+
+ if [ ! -e "$BUNDLE/Contents/Frameworks/$NEWFRAMEWORKPATH" ]; then
+ echo Embedding framework $FRAMEWORKNAME
+
+
+ # copy the framework into the bundle
+ cp -R $FRAMEWORKPATH $BUNDLE/Contents/Frameworks
+ # remove debug libs we've copied
+ find $BUNDLE/Contents/Frameworks/$FRAMEWORKNAME -regex '.*_debug\(\.dSYM\)*' | xargs rm -rf
+
+ handleFile "$BUNDLE/Contents/Frameworks/$NEWFRAMEWORKPATH" "$BUNDLE"
+ fi
+ # and inform the dynamic linker about this
+ xcrun install_name_tool -change $lib @executable_path/../Frameworks/$NEWFRAMEWORKPATH $FILE
+
+
+ # this part handles 'normal' dynamic libraries (.dylib)
+ else
+ if [ ! -e "$BUNDLE/Contents/Frameworks/$NAME" ]; then
+ echo Embedding library $NAME
+
+ # Qt installed via the precompled binaries...
+ if [ $lib = $NAME ]; then
+ lib="/Library/Frameworks/$NAME"
+ if [ ! -e "$lib" ]; then
+ lib="/usr/lib/$NAME"
+ fi
+ if [ ! -e "$lib" ]; then
+ echo "Library $NAME not found."
+ exit 1
+ fi
+ fi
+
+ # copy the lib into the bundle
+ cp $lib $BUNDLE/Contents/Frameworks
+ handleFile "$BUNDLE/Contents/Frameworks/$NAME" "$BUNDLE"
+ fi
+
+ # and inform the dynamic linker about this
+ xcrun install_name_tool -change $lib @executable_path/../Frameworks/$NAME $FILE
+ fi
+
+ fi
+ done
+}
+
+function handleQtPlugins()
+{
+ local PLUGINPATH=$QTDIR/plugins
+
+ # QTDIR was not found, then we're using /Developer/Applications/Qt
+ if [ "$PLUGINPATH" = "/plugins" ]; then
+ PLUGINPATH="/Developer/Applications/Qt/plugins"
+ fi
+
+ CLASS=$1
+ EXECUTABLE=$2
+ BUNDLE=$3
+ mkdir -p $BUNDLE/Contents/plugins/$CLASS
+ echo Add $CLASS plugins
+ for plugin in `ls $PLUGINPATH/$CLASS/*`; do
+ plugin=`basename $plugin`
+ if echo $plugin | grep 'debug' >/dev/null; then
+ #if [ $IS_DEBUG -eq 1 ]; then
+ cp "$PLUGINPATH/$CLASS/$plugin" $BUNDLE/Contents/plugins/$CLASS
+ xcrun install_name_tool -change $plugin @executable_path/../plugins/$CLASS/$plugin $EXECUTABLE
+ handleFile $BUNDLE/Contents/plugins/$CLASS/$plugin $BUNDLE
+ #fi
+ else
+ #if [ $IS_DEBUG -eq 0 ]; then
+ cp "$PLUGINPATH/$CLASS/$plugin" $BUNDLE/Contents/plugins/$CLASS
+ xcrun install_name_tool -change $plugin @executable_path/../plugins/$CLASS/$plugin $EXECUTABLE
+ handleFile $BUNDLE/Contents/plugins/$CLASS/$plugin $BUNDLE
+ #fi
+ fi
+ done
+}
+
+# the app bundle we're working with
+BUNDLE=$1
+# the executable inside of the bundle
+EXECUTABLE=$BUNDLE/Contents/MacOS/`xargs < $BUNDLE/Contents/Info.plist | sed -ne 's,.*<key>CFBundleExecutable</key> <string>\([^<]*\)</string>.*,\1,p'`
+
+mkdir -p $BUNDLE/Contents/Frameworks
+
+handleFile $EXECUTABLE $BUNDLE
+
+if [ $HAVE_CORE -eq 1 ]; then
+ handleQtPlugins "imageformats" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_SVG -eq 1 ]; then
+ handleQtPlugins "iconengines" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_PHONON -eq 1 ]; then
+ handleQtPlugins "phonon_backend" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_SQL -eq 1 ]; then
+ handleQtPlugins "sqldrivers" "$EXECUTABLE" "$BUNDLE"
+fi
+if [ $HAVE_WEBKIT -eq 1 ]; then
+ handleQtPlugins "codecs" "$EXECUTABLE" "$BUNDLE"
+fi
diff --git a/src/libs/ifwtools/resources/default_icon_mac.icns b/src/libs/ifwtools/resources/default_icon_mac.icns
new file mode 100644
index 000000000..8d870d649
--- /dev/null
+++ b/src/libs/ifwtools/resources/default_icon_mac.icns
Binary files differ
diff --git a/src/libs/ifwtools/resources/ifwtools.qrc b/src/libs/ifwtools/resources/ifwtools.qrc
new file mode 100644
index 000000000..8b73a8442
--- /dev/null
+++ b/src/libs/ifwtools/resources/ifwtools.qrc
@@ -0,0 +1,7 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>default_icon_mac.icns</file>
+ <file>copylibsintobundle.sh</file>
+ <file alias="resources/installerbase.ico">../../../sdk/installerbase.ico</file>
+</qresource>
+</RCC>
diff --git a/src/libs/installer/abstractarchive.cpp b/src/libs/installer/abstractarchive.cpp
new file mode 100644
index 000000000..f2b4300c4
--- /dev/null
+++ b/src/libs/installer/abstractarchive.cpp
@@ -0,0 +1,221 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "abstractarchive.h"
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ArchiveEntry
+ \brief The ArchiveEntry struct represents an entry in an archive file,
+ which can be for example a file or a directory.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AbstractArchive
+ \brief The AbstractArchive class is the base class for classes representing
+ different archive files. It cannot be instantiated on its own but
+ defines the API and provides common functionality when subclassed.
+*/
+
+/*!
+ \enum AbstractArchive::CompressionLevel
+ This enum holds the possible values for archive compression level.
+
+ \value Non
+ \value Fastest
+ \value Fast
+ \value Normal
+ \value Maximum
+ \value Ultra
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::currentEntryChanged(const QString &filename)
+
+ Current entry changed to \a filename. Subclasses should emit this signal whenever
+ the entry to process is changed.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::completedChanged(const quint64 completed, const quint64 total)
+
+ The ratio of \a completed entries from \a total changed. Subclasses should emit
+ this whenever the progress changes.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::cancel()
+
+ Cancels current operation. A subclass should implement this slot.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::close()
+
+ Closes the archive. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::create(const QStringList &data)
+
+ Creates an archive from \a data. Returns \c true on success;
+ \c false otherwise. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::extract(const QString &dirPath)
+
+ Extracts the archive to \a dirPath. Returns \c true on success;
+ \c false otherwise. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::extract(const QString &dirPath, const quint64 totalFiles)
+
+ Extracts the contents of an archive to \a dirPath with precalculated
+ count of \a totalFiles. Returns \c true on success; \c false otherwise.
+ A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::isSupported()
+
+ Returns \c true if the archive is supported; \c false otherwise.
+ A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::list()
+
+ Returns a list of entries in this archive. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::open(QIODevice::OpenMode mode)
+
+ Opens the file device for an archive in \a mode. Returns \c true on success;
+ \c false otherwise. A subclass should implement this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractArchive::setFilename(const QString &filename)
+
+ Sets the \a filename for the archive. A subclass should implement this method.
+*/
+
+/*!
+ Constructs a new archive object with \a parent as parent. Cannot be
+ called directly but instead from subclass constructors.
+*/
+AbstractArchive::AbstractArchive(QObject *parent)
+ : QObject(parent)
+ , m_compressionLevel(CompressionLevel::Normal)
+{
+}
+
+/*!
+ Virtual destructor for \c AbstractArchive.
+*/
+AbstractArchive::~AbstractArchive()
+{
+}
+
+/*!
+ Returns a human-readable description of the last error that occurred.
+*/
+QString AbstractArchive::errorString() const
+{
+ return m_error;
+}
+
+/*!
+ Sets the compression level for new archives to \a level.
+*/
+void AbstractArchive::setCompressionLevel(const CompressionLevel level)
+{
+ m_compressionLevel = level;
+}
+
+/*!
+ Sets a human-readable description of the current \a error.
+*/
+void AbstractArchive::setErrorString(const QString &error)
+{
+ m_error = error;
+}
+
+/*!
+ Returns the current compression level.
+*/
+AbstractArchive::CompressionLevel AbstractArchive::compressionLevel() const
+{
+ return m_compressionLevel;
+}
+
+/*!
+ Reads an \a entry from the specified \a istream. Returns a reference to \a istream.
+*/
+QDataStream &operator>>(QDataStream &istream, ArchiveEntry &entry)
+{
+ istream >> entry.path >> entry.utcTime >> entry.isDirectory
+ >> entry.uncompressedSize >> entry.permissions_mode >> entry.permissions_enum;
+
+ return istream;
+}
+
+/*!
+ Writes an \a entry to the specified \a ostream. Returns a reference to \a ostream.
+*/
+QDataStream &operator<<(QDataStream &ostream, const ArchiveEntry &entry)
+{
+ ostream << entry.path << entry.utcTime << entry.isDirectory
+ << entry.uncompressedSize << entry.permissions_mode << entry.permissions_enum;
+
+ return ostream;
+}
+
+/*!
+ Returns \c true if left-hand-side entry \a lhs is equal to right-hand-size entry \a rhs.
+*/
+bool operator==(const ArchiveEntry &lhs, const ArchiveEntry &rhs)
+{
+ return lhs.path == rhs.path
+ && lhs.utcTime == rhs.utcTime
+ && lhs.isDirectory == rhs.isDirectory
+ && lhs.compressedSize == rhs.compressedSize
+ && lhs.uncompressedSize == rhs.uncompressedSize
+ && lhs.permissions_mode == rhs.permissions_mode
+ && (lhs.permissions_enum == rhs.permissions_enum // ignore invalid permissions
+ || lhs.permissions_enum == static_cast<QFile::Permissions>(-1)
+ || rhs.permissions_enum == static_cast<QFile::Permissions>(-1));
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/abstractarchive.h b/src/libs/installer/abstractarchive.h
new file mode 100644
index 000000000..86b4b4ffa
--- /dev/null
+++ b/src/libs/installer/abstractarchive.h
@@ -0,0 +1,123 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef ABSTRACTARCHIVE_H
+#define ABSTRACTARCHIVE_H
+
+#include "installer_global.h"
+
+#include <QFile>
+#include <QDateTime>
+#include <QDataStream>
+#include <QPoint>
+
+#ifdef Q_OS_WIN
+typedef int mode_t;
+#endif
+
+namespace QInstaller {
+
+struct INSTALLER_EXPORT ArchiveEntry
+{
+ ArchiveEntry()
+ : isDirectory(false)
+ , isSymbolicLink(false)
+ , compressedSize(0)
+ , uncompressedSize(0)
+ , permissions_mode(0)
+ {}
+
+ ArchiveEntry(const ArchiveEntry &) = default;
+
+ QString path;
+ QDateTime utcTime;
+ QPoint archiveIndex;
+ bool isDirectory;
+ bool isSymbolicLink;
+ quint64 compressedSize;
+ quint64 uncompressedSize;
+ mode_t permissions_mode;
+ QFile::Permissions permissions_enum;
+};
+
+class INSTALLER_EXPORT AbstractArchive : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AbstractArchive)
+
+public:
+ enum CompressionLevel {
+ Non = 0,
+ Fastest = 1,
+ Fast = 3,
+ Normal = 5,
+ Maximum = 7,
+ Ultra = 9
+ };
+ Q_ENUM(CompressionLevel)
+
+ explicit AbstractArchive(QObject *parent = nullptr);
+ virtual ~AbstractArchive() = 0;
+
+ virtual bool open(QIODevice::OpenMode mode) = 0;
+ virtual void close() = 0;
+ virtual void setFilename(const QString &filename) = 0;
+
+ virtual QString errorString() const;
+
+ virtual bool extract(const QString &dirPath) = 0;
+ virtual bool extract(const QString &dirPath, const quint64 totalFiles) = 0;
+ virtual bool create(const QStringList &data) = 0;
+ virtual QVector<ArchiveEntry> list() = 0;
+ virtual bool isSupported() = 0;
+
+ virtual void setCompressionLevel(const CompressionLevel level);
+
+Q_SIGNALS:
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(const quint64 completed, const quint64 total);
+
+public Q_SLOTS:
+ virtual void cancel() = 0;
+
+protected:
+ void setErrorString(const QString &error);
+ CompressionLevel compressionLevel() const;
+
+private:
+ QString m_error;
+ CompressionLevel m_compressionLevel;
+};
+
+INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, ArchiveEntry &entry);
+INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const ArchiveEntry &entry);
+INSTALLER_EXPORT bool operator==(const ArchiveEntry &lhs, const ArchiveEntry &rhs);
+
+} // namespace QInstaller
+
+#endif // ABSTRACTARCHIVE_H
diff --git a/src/libs/installer/abstractfiletask.cpp b/src/libs/installer/abstractfiletask.cpp
index ada54e598..424529fe4 100644
--- a/src/libs/installer/abstractfiletask.cpp
+++ b/src/libs/installer/abstractfiletask.cpp
@@ -50,6 +50,8 @@ namespace QInstaller {
\value TaskItem
\value SourceFile
\value TargetFile
+ \value Name
+ \value ChecksumMismatch
\value UserRole The first role that can be used for user-specific purposes.
*/
@@ -60,32 +62,32 @@ namespace QInstaller {
*/
/*!
- \fn FileTaskItem::FileTaskItem()
+ \fn QInstaller::FileTaskItem::FileTaskItem()
Creates a file task item.
*/
/*!
- \fn FileTaskItem::FileTaskItem(const QString &s)
+ \fn QInstaller::FileTaskItem::FileTaskItem(const QString &s)
Creates a file task item using the source specified by \a s.
*/
/*!
- \fn FileTaskItem::FileTaskItem(const QString &s, const QString &t)
+ \fn QInstaller::FileTaskItem::FileTaskItem(const QString &s, const QString &t)
Creates a file task item using the source specified by \a s and target
specified by \a t.
*/
/*!
- \fn FileTaskItem::source() const
+ \fn QInstaller::FileTaskItem::source() const
Returns the source file of the file task item.
*/
/*!
- \fn FileTaskItem::target() const
+ \fn QInstaller::FileTaskItem::target() const
Returns the target file of the file task item.
*/
@@ -97,37 +99,45 @@ namespace QInstaller {
*/
/*!
- \fn FileTaskResult::FileTaskResult()
+ \fn QInstaller::FileTaskResult::FileTaskResult()
Creates file task results.
*/
/*!
- \fn FileTaskResult::FileTaskResult(const QString &t, const QByteArray &c, const FileTaskItem &i)
+ \fn QInstaller::FileTaskResult::FileTaskResult(const QString &t, const QByteArray &c,
+ const FileTaskItem &i, bool checksumMismatch)
Creates file task results using the target file specified by \a t, checksum
- specified by \a c, and file task item specified by \a i.
+ specified by \a c, file task item specified by \a i, and checksum mismatch
+ specified by \a checksumMismatch.
*/
/*!
- \fn FileTaskResult::target() const
+ \fn QInstaller::FileTaskResult::target() const
Returns the target file of the task result.
*/
/*!
- \fn FileTaskResult::checkSum() const
+ \fn QInstaller::FileTaskResult::checkSum() const
Returns the checksum of the task result.
*/
/*!
- \fn FileTaskResult::taskItem() const
+ \fn QInstaller::FileTaskResult::taskItem() const
Returns file task items.
*/
/*!
+ \fn QInstaller::FileTaskResult::checksumMismatch() const
+
+ Returns \c true if checksum mismatch is detected otherwise returns \c false.
+*/
+
+/*!
Constructs an empty abstract file task object.
*/
AbstractFileTask::AbstractFileTask()
@@ -136,7 +146,7 @@ AbstractFileTask::AbstractFileTask()
}
/*!
- \fn AbstractFileTask::~AbstractFileTask()
+ \fn QInstaller::AbstractFileTask::~AbstractFileTask()
Destroys the abstract file task object.
*/
diff --git a/src/libs/installer/abstracttask.cpp b/src/libs/installer/abstracttask.cpp
new file mode 100644
index 000000000..31092dada
--- /dev/null
+++ b/src/libs/installer/abstracttask.cpp
@@ -0,0 +1,47 @@
+/**************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "abstracttask.h"
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AbstractTask
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AbstractTaskData
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::TaskException
+ \internal
+*/
diff --git a/src/libs/installer/abstracttask.h b/src/libs/installer/abstracttask.h
index 9a44c50a9..5ee23bef5 100644
--- a/src/libs/installer/abstracttask.h
+++ b/src/libs/installer/abstracttask.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,7 +31,9 @@
#include "runextensions.h"
+#include <QException>
#include <QFutureInterface>
+#include <QVariant>
namespace QInstaller {
@@ -58,9 +60,9 @@ public:
: m_message(message)
{}
- void raise() const { throw *this; }
+ void raise() const override { throw *this; }
QString message() const { return m_message; }
- TaskException *clone() const { return new TaskException(*this); }
+ TaskException *clone() const override { return new TaskException(*this); }
private:
QString m_message;
diff --git a/src/libs/installer/adminauthorization_win.cpp b/src/libs/installer/adminauthorization_win.cpp
index 3ad193b5e..6fdef741c 100644
--- a/src/libs/installer/adminauthorization_win.cpp
+++ b/src/libs/installer/adminauthorization_win.cpp
@@ -35,6 +35,8 @@
#include <qt_windows.h>
+#include "globals.h"
+
#ifdef Q_CC_MINGW
# ifndef SEE_MASK_NOASYNC
# define SEE_MASK_NOASYNC 0x00000100
@@ -106,14 +108,15 @@ bool AdminAuthorization::execute(QWidget *, const QString &program, const QStrin
shellExecuteInfo.lpParameters = (wchar_t *)args.utf16();
shellExecuteInfo.fMask = SEE_MASK_NOASYNC;
- qDebug() << "Starting elevated process" << file << "with arguments" << args;
+ qCDebug(QInstaller::lcServer) << "Starting elevated process" << file
+ << "with arguments" << args;
if (ShellExecuteExW(&shellExecuteInfo)) {
- qDebug() << "Finished starting elevated process.";
+ qCDebug(QInstaller::lcServer) << "Finished starting elevated process.";
return true;
} else {
- qWarning() << "Error while starting elevated process" << program
- << ":" << QInstaller::windowsErrorString(GetLastError());
+ qCWarning(QInstaller::lcServer) << "Error while starting elevated process" << program
+ << ":" << QInstaller::windowsErrorString(GetLastError());
}
return false;
}
diff --git a/src/libs/installer/adminauthorization_x11.cpp b/src/libs/installer/adminauthorization_x11.cpp
index 7068bb2c6..f4951d523 100644
--- a/src/libs/installer/adminauthorization_x11.cpp
+++ b/src/libs/installer/adminauthorization_x11.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,59 +28,26 @@
#include "adminauthorization.h"
-#include <QtCore/QFile>
-#include <QtCore/QRegExp>
-#include <QDebug>
+#include "globals.h"
+#include <QDebug>
#include <QApplication>
-#include <QInputDialog>
#include <QMessageBox>
+#include <QProcess>
-#include <cstdlib>
-#include <sys/resource.h>
#include <unistd.h>
-#include <fcntl.h>
-
-#ifdef Q_OS_LINUX
-#include <linux/limits.h>
-#include <pty.h>
-#else
-#ifdef Q_OS_FREEBSD
-#include <libutil.h>
-#include <signal.h>
-#else
-#include <util.h>
-#endif
-#endif
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
#include <iostream>
-#define SU_COMMAND "/usr/bin/sudo"
-//#define SU_COMMAND "/bin/echo"
+#define PKEXEC_COMMAND "/usr/bin/pkexec"
namespace QInstaller {
-static QString getPassword(QWidget *parent)
-{
- if (qobject_cast<QApplication*> (qApp) != 0) {
- bool ok = false;
- const QString result = QInputDialog::getText(parent, QObject::tr("Authorization required"),
- QObject::tr("Enter your password to authorize for sudo:"),
- QLineEdit::Password, QString(), &ok);
- return ok ? result : QString();
- } else {
- std::cout << QObject::tr("Authorization required").toStdString() << std::endl;
- std::cout << QObject::tr("Enter your password to authorize for sudo:").toStdString()
- << std::endl;
- std::string password;
- std::cin >> password;
- return QString::fromStdString(password);
- }
-}
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AdminAuthorization
+ \internal
+*/
static void printError(QWidget *parent, const QString &value)
{
@@ -95,186 +62,23 @@ static void printError(QWidget *parent, const QString &value)
bool AdminAuthorization::execute(QWidget *parent, const QString &program, const QStringList &arguments)
{
const QString fallback = program + QLatin1String(" ") + arguments.join(QLatin1String(" "));
- qDebug() << "Fallback:" << fallback;
-
- // as we cannot pipe the password to su in QProcess, we need to setup a pseudo-terminal for it
- int masterFD = -1;
- int slaveFD = -1;
- char ptsn[ PATH_MAX ];
-
- if (::openpty(&masterFD, &slaveFD, ptsn, 0, 0))
- return false;
+ qCDebug(QInstaller::lcServer) << "Fallback:" << fallback;
- masterFD = ::posix_openpt(O_RDWR | O_NOCTTY);
- if (masterFD < 0)
- return false;
-
- const QByteArray ttyName = ::ptsname(masterFD);
-
- if (::grantpt(masterFD)) {
- ::close(masterFD);
- return false;
- }
-
- ::revoke(ttyName);
- ::unlockpt(masterFD);
+ QProcess process;
+ process.setProcessChannelMode(QProcess::MergedChannels);
+ process.start(QLatin1String(PKEXEC_COMMAND), QStringList() << program << arguments, QIODevice::ReadOnly);
- slaveFD = ::open(ttyName, O_RDWR | O_NOCTTY);
- if (slaveFD < 0) {
- ::close(masterFD);
+ if (!process.waitForStarted() || !process.waitForFinished(-1)) {
+ printError(parent, process.errorString());
+ if (process.state() > QProcess::NotRunning)
+ process.kill();
return false;
}
-
- ::fcntl(masterFD, F_SETFD, FD_CLOEXEC);
- ::fcntl(slaveFD, F_SETFD, FD_CLOEXEC);
- int pipedData[2];
- if (pipe(pipedData) != 0)
- return false;
-
- int flags = ::fcntl(pipedData[0], F_GETFL);
- if (flags != -1)
- ::fcntl(pipedData[0], F_SETFL, flags | O_NONBLOCK);
-
- flags = ::fcntl(masterFD, F_GETFL);
- if (flags != -1)
- ::fcntl(masterFD, F_SETFL, flags | O_NONBLOCK);
-
- pid_t child = fork();
-
- if (child < -1) {
- ::close(masterFD);
- ::close(slaveFD);
- ::close(pipedData[0]);
- ::close(pipedData[1]);
- return false;
- }
-
- // parent process
- else if (child > 0) {
- ::close(slaveFD);
- //close writing end of pipe
- ::close(pipedData[1]);
-
- QRegExp re(QLatin1String("[Pp]assword.*:"));
- QByteArray data;
- QByteArray errData;
- int bytes = 0;
- int errBytes = 0;
- char buf[1024];
- char errBuf[1024];
- int status;
- bool statusValid = false;
- while (bytes >= 0) {
- const pid_t waitResult = ::waitpid(child, &status, WNOHANG);
- if (waitResult == -1) {
- break;
- }
- if (waitResult == child) {
- statusValid = true;
- break;
- }
- bytes = ::read(masterFD, buf, 1023);
- if (bytes == -1 && errno == EAGAIN)
- bytes = 0;
- else if (bytes > 0)
- data.append(buf, bytes);
- errBytes = ::read(pipedData[0], errBuf, 1023);
- if (errBytes > 0)
- {
- errData.append(errBuf, errBytes);
- errBytes=0;
- }
- if (bytes > 0) {
- const QString line = QString::fromLatin1(data);
- if (re.indexIn(line) != -1) {
- const QString password = getPassword(parent);
- if (password.isEmpty()) {
- QByteArray pwd = password.toLatin1();
- for (int i = 0; i < 3; ++i) {
- ::write(masterFD, pwd.data(), pwd.length());
- ::write(masterFD, "\n", 1);
- }
- return false;
- }
- QByteArray pwd = password.toLatin1();
- ::write(masterFD, pwd.data(), pwd.length());
- ::write(masterFD, "\n", 1);
- ::read(masterFD, buf, pwd.length() + 1);
- }
- }
- if (bytes == 0)
- ::usleep(100000);
- }
-
- while (true) {
- errBytes = ::read(pipedData[0], errBuf, 1023);
- if (errBytes == -1 && errno == EAGAIN) {
- ::usleep(100000);
- continue;
- }
-
- if (errBytes <= 0)
- break;
-
- errData.append(errBuf, errBytes);
- }
-
- const bool success = statusValid && WIFEXITED(status) && WEXITSTATUS(status) == 0;
-
- if (!success && !errData.isEmpty()) {
- printError(parent, QString::fromLocal8Bit(errData.constData()));
- }
-
- ::close(pipedData[0]);
- return success;
- }
-
- // child process
- else {
- ::close(pipedData[0]);
- // Reset signal handlers
- for (int sig = 1; sig < NSIG; ++sig)
- signal(sig, SIG_DFL);
- signal(SIGHUP, SIG_IGN);
-
- ::setsid();
-
- ::ioctl(slaveFD, TIOCSCTTY, 1);
- int pgrp = ::getpid();
- ::tcsetpgrp(slaveFD, pgrp);
-
- ::dup2(slaveFD, 0);
- ::dup2(slaveFD, 1);
- ::dup2(pipedData[1], 2);
-
- // close all file descriptors
- struct rlimit rlp;
- getrlimit(RLIMIT_NOFILE, &rlp);
- for (int i = 3; i < static_cast<int>(rlp.rlim_cur); ++i)
- ::close(i);
-
- char **argp = (char **) ::malloc((arguments.count() + 4) * sizeof(char *));
- QList<QByteArray> args;
- args.push_back(SU_COMMAND);
- args.push_back("-b");
- args.push_back(program.toLocal8Bit());
- for (QStringList::const_iterator it = arguments.begin(); it != arguments.end(); ++it)
- args.push_back(it->toLocal8Bit());
-
- int i = 0;
- for (QList<QByteArray>::iterator it = args.begin(); it != args.end(); ++it, ++i)
- argp[i] = it->data();
- argp[i] = 0;
-
- ::unsetenv("LANG");
- ::unsetenv("LC_ALL");
-
- int exitStatus = 0;
- if (::execv(SU_COMMAND, argp) == -1)
- exitStatus = -errno;
- _exit(exitStatus);
+ if (process.exitCode() != EXIT_SUCCESS) {
+ printError(parent, QLatin1String(process.readAll()));
return false;
}
+ return true;
}
// has no guarantee to work
diff --git a/src/libs/installer/archivefactory.cpp b/src/libs/installer/archivefactory.cpp
new file mode 100644
index 000000000..56811d8e3
--- /dev/null
+++ b/src/libs/installer/archivefactory.cpp
@@ -0,0 +1,151 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "archivefactory.h"
+#ifdef IFW_LIBARCHIVE
+#include "libarchivewrapper.h"
+#elif defined(IFW_LIB7Z)
+#include "lib7zarchive.h"
+#endif
+
+#include <QFileInfo>
+
+using namespace QInstaller;
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ArchiveFactory
+ \brief The ArchiveFactory class is used to create archive objects
+ based on the suffix of a given filename.
+
+ This class acts as a factory for \c QInstaller::AbstractArchive. You can
+ register one or more archive handlers with this factory and create
+ registered objects based on the file suffix.
+
+ This class follows the singleton design pattern. Only one instance
+ of this class can be created and its reference can be fetched from
+ the \c {instance()} method.
+
+ Depending of the configuration features set at build time, one of the
+ following archive handlers is registered by default:
+ \list
+ \li Lib7z
+ \li LibArchive
+ \endlist
+*/
+
+/*!
+ \fn void QInstaller::ArchiveFactory::registerArchive(const QString &name, const QStringList &types)
+
+ Registers a new archive handler with the factory based on \a name and list
+ of supported file suffix \a types.
+*/
+
+
+/*!
+ Returns the only instance of this class.
+*/
+ArchiveFactory &ArchiveFactory::instance()
+{
+ static ArchiveFactory instance;
+ return instance;
+}
+
+/*!
+ Constructs and returns a pointer to an archive object with \a filename and \a parent.
+ If the archive type referenced by \a filename is not registered, a null pointer is
+ returned instead.
+*/
+AbstractArchive *ArchiveFactory::create(const QString &filename, QObject *parent) const
+{
+ const QString suffix = QFileInfo(filename).completeSuffix();
+ QString name;
+ for (auto &types : m_supportedTypesHash) {
+ QStringList::const_iterator it;
+ for (it = types.constBegin(); it != types.constEnd(); ++it) {
+ if (suffix.endsWith(*it, Qt::CaseInsensitive)) {
+ name = m_supportedTypesHash.key(types);
+ break;
+ }
+ }
+ }
+ if (name.isEmpty())
+ return nullptr;
+
+ AbstractArchive *archive = GenericFactory<AbstractArchive, QString, QString, QObject *>
+ ::create(name, filename, parent);
+
+ return archive;
+}
+
+/*!
+ Returns a list of supported archive types.
+*/
+QStringList ArchiveFactory::supportedTypes()
+{
+ QStringList types;
+ QHash<QString, QStringList> *const typesHash = &instance().m_supportedTypesHash;
+ for (auto &value : *typesHash)
+ types.append(value);
+
+ return types;
+}
+
+/*!
+ Returns \c true if the archive type from \a filename is registered with
+ an archive handler.
+*/
+bool ArchiveFactory::isSupportedType(const QString &filename)
+{
+ const QString suffix = QFileInfo(filename).completeSuffix();
+ QHash<QString, QStringList> *const typesHash = &instance().m_supportedTypesHash;
+ for (auto &types : *typesHash) {
+ QStringList::const_iterator it;
+ for (it = types.constBegin(); it != types.constEnd(); ++it) {
+ if (suffix.endsWith(*it, Qt::CaseInsensitive))
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ Private constructor for ArchiveFactory. Registers default archive handlers.
+*/
+ArchiveFactory::ArchiveFactory()
+{
+#ifdef IFW_LIBARCHIVE
+ registerArchive<LibArchiveWrapper>(QLatin1String("LibArchive"), QStringList()
+ << QLatin1String("tar") << QLatin1String("tar.gz") << QLatin1String("tar.bz2")
+ << QLatin1String("tar.xz") << QLatin1String("zip") << QLatin1String("7z")
+ << QLatin1String("qbsp"));
+#elif defined(IFW_LIB7Z)
+ registerArchive<Lib7zArchive>(QLatin1String("Lib7z"), QStringList()
+ << QLatin1String("7z") << QLatin1String("qbsp"));
+#endif
+}
diff --git a/src/libs/installer/archivefactory.h b/src/libs/installer/archivefactory.h
new file mode 100644
index 000000000..6f545eefa
--- /dev/null
+++ b/src/libs/installer/archivefactory.h
@@ -0,0 +1,69 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef ARCHIVEFACTORY_H
+#define ARCHIVEFACTORY_H
+
+#include "installer_global.h"
+#include "genericfactory.h"
+#include "abstractarchive.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ArchiveFactory
+ : public GenericFactory<AbstractArchive, QString, QString, QObject *>
+{
+ Q_DISABLE_COPY(ArchiveFactory)
+
+public:
+ static ArchiveFactory &instance();
+
+ template <typename T>
+ void registerArchive(const QString &name, const QStringList &types)
+ {
+ if (containsProduct(name))
+ m_supportedTypesHash.remove(name);
+
+ registerProduct<T>(name);
+ m_supportedTypesHash.insert(name, types);
+ }
+ AbstractArchive *create(const QString &filename, QObject *parent = nullptr) const;
+
+ static QStringList supportedTypes();
+ static bool isSupportedType(const QString &filename);
+
+private:
+ ArchiveFactory();
+
+private:
+ QHash<QString, QStringList> m_supportedTypesHash;
+};
+
+} // namespace QInstaller
+
+#endif // ARCHIVEFACTORY_H
diff --git a/src/libs/installer/aspectratiolabel.cpp b/src/libs/installer/aspectratiolabel.cpp
new file mode 100644
index 000000000..b5a21424d
--- /dev/null
+++ b/src/libs/installer/aspectratiolabel.cpp
@@ -0,0 +1,147 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "aspectratiolabel.h"
+
+#include <QDesktopServices>
+#include <QTimer>
+
+using namespace QInstaller;
+
+/*!
+ \class QInstaller::AspectRatioLabel
+ \inmodule QtInstallerFramework
+ \brief The AspectRatioLabel class provides a label for displaying
+ a pixmap that maintains its original aspect ratio when resized.
+*/
+
+/*!
+ Constructs the label with \a parent as parent.
+*/
+AspectRatioLabel::AspectRatioLabel(QWidget *parent)
+ : QLabel(parent)
+ , m_discardMousePress(false)
+{
+ setMinimumSize(1, 1);
+ setScaledContents(false);
+ setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+}
+
+/*!
+ Sets the \a pixmap shown on the label. Setting a new pixmap clears
+ the previous content.
+
+ \note This redefines the non-virtual slot of the same signature from the
+ QLabel base class, which results in non polymorphic behavior when
+ called via a base class pointer.
+*/
+void AspectRatioLabel::setPixmap(const QPixmap &pixmap)
+{
+ setPixmapAndUrl(pixmap, QString());
+}
+
+/*!
+ Sets the \a pixmap shown on the label and an \a url. Setting a new
+ pixmap clears the previous content. When clicking the \a pixmap, \a url
+ is opened in a browser. If the \a url is a reference to a file, it will
+ be opened with a suitable application instead of a Web browser.
+*/
+void AspectRatioLabel::setPixmapAndUrl(const QPixmap &pixmap, const QString &url)
+{
+ m_clickableUrl = url;
+ m_pixmap = pixmap;
+ QLabel::setPixmap(scaledPixmap());
+}
+
+/*!
+ \reimp
+*/
+int AspectRatioLabel::heightForWidth(int w) const
+{
+ return m_pixmap.isNull()
+ ? height()
+ : m_pixmap.height() * w / m_pixmap.width();
+}
+
+/*!
+ \reimp
+*/
+QSize AspectRatioLabel::sizeHint() const
+{
+ const int w = width();
+ return QSize(w, heightForWidth(w));
+}
+
+/*!
+ Returns the currently set pixmap scaled to the label size,
+ while preserving the original aspect ratio.
+*/
+QPixmap AspectRatioLabel::scaledPixmap() const
+{
+ if (m_pixmap.isNull())
+ return QPixmap();
+
+ return m_pixmap.scaled(size() * m_pixmap.devicePixelRatio(),
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
+}
+
+/*!
+ \reimp
+*/
+void AspectRatioLabel::resizeEvent(QResizeEvent *event)
+{
+ Q_UNUSED(event);
+
+ if (!m_pixmap.isNull())
+ QLabel::setPixmap(scaledPixmap());
+}
+
+/*!
+ \reimp
+*/
+void AspectRatioLabel::mousePressEvent(QMouseEvent* event)
+{
+ Q_UNUSED(event)
+ if (!m_clickableUrl.isEmpty() && !m_discardMousePress)
+ QDesktopServices::openUrl(m_clickableUrl);
+}
+
+/*!
+ \reimp
+*/
+bool AspectRatioLabel::event(QEvent *e)
+{
+ if (e->type() == QEvent::WindowActivate) {
+ QTimer::singleShot(100, [&]() {
+ m_discardMousePress = false;
+ });
+ } else if (e->type() == QEvent::WindowDeactivate) {
+ m_discardMousePress = true;
+ }
+ return QLabel::event(e);
+}
diff --git a/src/libs/installer/aspectratiolabel.h b/src/libs/installer/aspectratiolabel.h
new file mode 100644
index 000000000..bbe54d3a1
--- /dev/null
+++ b/src/libs/installer/aspectratiolabel.h
@@ -0,0 +1,70 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef ASPECTRATIOLABEL_H
+#define ASPECTRATIOLABEL_H
+
+#include "installer_global.h"
+
+#include <QLabel>
+#include <QPixmap>
+#include <QResizeEvent>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT AspectRatioLabel : public QLabel
+{
+ Q_OBJECT
+
+public:
+ explicit AspectRatioLabel(QWidget *parent = nullptr);
+
+ int heightForWidth(int w) const override;
+ QSize sizeHint() const override;
+
+public slots:
+ void setPixmap(const QPixmap &pixmap);
+ void setPixmapAndUrl (const QPixmap &pixmap, const QString &url);
+ void resizeEvent(QResizeEvent *event) override;
+
+protected:
+ void mousePressEvent(QMouseEvent* event) override;
+ bool event(QEvent *e) override;
+
+private:
+ QPixmap scaledPixmap() const;
+
+private:
+ QPixmap m_pixmap;
+ QString m_clickableUrl;
+ bool m_discardMousePress;
+};
+
+} // namespace QInstaller
+
+#endif // ASPECTRATIOLABEL_H
diff --git a/src/libs/installer/binarycontent.h b/src/libs/installer/binarycontent.h
index a21cd69e7..7d50c0e28 100644
--- a/src/libs/installer/binarycontent.h
+++ b/src/libs/installer/binarycontent.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,6 +48,13 @@ public:
static const qint64 MagicUpdaterMarker = 0x12023235UL;
static const qint64 MagicPackageManagerMarker = 0x12023236UL;
+ // additional distinguishers only used at runtime, not written to the binary itself
+ enum MagicMarkerSupplement {
+ Default = 0x0,
+ OfflineGenerator = 0x1,
+ PackageViewer = 0x2
+ };
+
// the cookie put at the end of the file
static const quint64 MagicCookie = 0xc2630a1c99d668f8LL; // binary
static const quint64 MagicCookieDat = 0xc2630a1c99d668f9LL; // data
diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp
index 3e7dd5a2a..54af0c310 100644
--- a/src/libs/installer/binaryformat.cpp
+++ b/src/libs/installer/binaryformat.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -45,7 +45,7 @@ namespace QInstaller {
*/
/*!
- \fn OperationBlob::OperationBlob(const QString &n, const QString &x)
+ \fn QInstaller::OperationBlob::OperationBlob(const QString &n, const QString &x)
Constructs the operation blob with the given arguments, while \a n stands for the name part and
\a x for the XML representation of the operation.
@@ -75,13 +75,13 @@ namespace QInstaller {
*/
/*!
- \fn Range<qint64> Resource::segment() const
+ \fn Range<qint64> QInstaller::Resource::segment() const
Returns the range inside the file this resource represents.
*/
/*!
- \fn void Resource::setSegment(const Range<qint64> &segment)
+ \fn void QInstaller::Resource::setSegment(const Range<qint64> &segment)
Sets the range to the \a segment of the file that this resource represents.
*/
@@ -151,14 +151,21 @@ void Resource::setName(const QByteArray &name)
/*!
Opens a resource in QIODevice::ReadOnly mode. The function returns \c true
- if successful.
+ if successful. Optionally, \a permissions can be given.
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool Resource::open()
+#else
+bool Resource::open(std::optional<QFile::Permissions> permissions)
+#endif
{
if (isOpen())
return false;
-
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
+#else
+ if (!m_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered, permissions)) {
+#endif
setErrorString(m_file.errorString());
return false;
}
@@ -215,7 +222,7 @@ qint64 Resource::writeData(const char* data, qint64 maxSize)
}
/*!
- \fn void Resource::copyData(QFileDevice *out)
+ \fn void QInstaller::Resource::copyData(QFileDevice *out)
Copies the resource data to a file called \a out. Throws Error on failure.
*/
diff --git a/src/libs/installer/binaryformat.h b/src/libs/installer/binaryformat.h
index 3bd8a6aa3..e7505a341 100644
--- a/src/libs/installer/binaryformat.h
+++ b/src/libs/installer/binaryformat.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,11 +58,15 @@ public:
Resource(const QString &path, const Range<qint64> &segment);
~Resource();
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool open();
- void close();
+#else
+ bool open(std::optional<QFile::Permissions> permissions = std::nullopt);
+#endif
+ void close() override;
- bool seek(qint64 pos);
- qint64 size() const;
+ bool seek(qint64 pos) override;
+ qint64 size() const override;
QByteArray name() const;
void setName(const QByteArray &name);
@@ -74,10 +78,10 @@ public:
static void copyData(Resource *archive, QFileDevice *out);
private:
- qint64 readData(char *data, qint64 maxSize);
- qint64 writeData(const char *data, qint64 maxSize);
+ qint64 readData(char *data, qint64 maxSize) override;
+ qint64 writeData(const char *data, qint64 maxSize) override;
- bool open(OpenMode mode) { return QIODevice::open(mode); }
+ bool open(OpenMode mode) override { return QIODevice::open(mode); }
void setOpenMode(OpenMode mode) { QIODevice::setOpenMode(mode); }
private:
diff --git a/src/libs/installer/binaryformatengine.cpp b/src/libs/installer/binaryformatengine.cpp
index ec6926031..7f00c8d47 100644
--- a/src/libs/installer/binaryformatengine.cpp
+++ b/src/libs/installer/binaryformatengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,7 +28,7 @@
#include "binaryformatengine.h"
-#include <QRegExp>
+#include <QRegularExpression>
namespace {
@@ -127,9 +127,16 @@ bool BinaryFormatEngine::close()
/*!
\internal
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool BinaryFormatEngine::open(QIODevice::OpenMode mode)
+#else
+bool BinaryFormatEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions)
+#endif
{
Q_UNUSED(mode)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
+ Q_UNUSED(permissions)
+#endif
return m_resource.isNull() ? false : m_resource->open();
}
@@ -263,15 +270,17 @@ QStringList BinaryFormatEngine::entryList(QDir::Filters filters, const QStringLi
if (filterNames.isEmpty())
return result;
- QList<QRegExp> regexps;
- foreach (const QString &i, filterNames)
- regexps.append(QRegExp(i, Qt::CaseInsensitive, QRegExp::Wildcard));
+ QList<QRegularExpression> regexps;
+ for (const QString &i : filterNames) {
+ regexps.append(QRegularExpression(QRegularExpression::wildcardToRegularExpression(i),
+ QRegularExpression::CaseInsensitiveOption));
+ }
QStringList entries;
- foreach (const QString &i, result) {
+ for (const QString &i : qAsConst(result)) {
bool matched = false;
- foreach (const QRegExp &reg, regexps) {
- matched = reg.exactMatch(i);
+ for (const QRegularExpression &reg : qAsConst(regexps)) {
+ matched = reg.match(i).hasMatch();
if (matched)
break;
}
diff --git a/src/libs/installer/binaryformatengine.h b/src/libs/installer/binaryformatengine.h
index 64d0e95f7..9321e9d9c 100644
--- a/src/libs/installer/binaryformatengine.h
+++ b/src/libs/installer/binaryformatengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -43,21 +43,26 @@ public:
BinaryFormatEngine(const QHash<QByteArray, ResourceCollection> &collections,
const QString &fileName);
- void setFileName(const QString &file);
+ void setFileName(const QString &file) override;
- bool copy(const QString &newName);
- bool close();
- bool open(QIODevice::OpenMode mode);
- qint64 pos() const;
- qint64 read(char *data, qint64 maxlen);
- bool seek(qint64 offset);
- qint64 size() const;
+ bool copy(const QString &newName) override;
+ bool close() override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ bool open(QIODevice::OpenMode mode) override;
+#else
+ bool open(QIODevice::OpenMode mode,
+ std::optional<QFile::Permissions> permissions = std::nullopt) override;
+#endif
+ qint64 pos() const override;
+ qint64 read(char *data, qint64 maxlen) override;
+ bool seek(qint64 offset) override;
+ qint64 size() const override;
- QString fileName(FileName file = DefaultName) const;
- FileFlags fileFlags(FileFlags type = FileInfoAll) const;
+ QString fileName(FileName file = DefaultName) const override;
+ FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
- Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames);
- QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override;
private:
QString m_fileNamePath;
diff --git a/src/libs/installer/calculatorbase.cpp b/src/libs/installer/calculatorbase.cpp
new file mode 100644
index 000000000..4f1732677
--- /dev/null
+++ b/src/libs/installer/calculatorbase.cpp
@@ -0,0 +1,76 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "calculatorbase.h"
+
+#include "component.h"
+
+namespace QInstaller {
+
+CalculatorBase::CalculatorBase(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+CalculatorBase::~CalculatorBase()
+{
+}
+
+void CalculatorBase::insertResolution(Component *component, const Resolution resolutionType
+ , const QString &referencedComponent)
+{
+ // Keep the first reason
+ if (m_componentNameResolutionHash.contains(component->name()))
+ return;
+
+ m_componentNameResolutionHash.insert(component->name(),
+ QPair<Resolution, QString>(resolutionType, referencedComponent));
+}
+
+QList<Component *> CalculatorBase::resolvedComponents() const
+{
+ return m_resolvedComponents;
+}
+
+CalculatorBase::Resolution CalculatorBase::resolutionType(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).first;
+}
+
+QString CalculatorBase::error() const
+{
+ return m_errorString;
+}
+
+QString CalculatorBase::referencedComponent(Component *component) const
+{
+ return m_componentNameResolutionHash.value(component->name()).second;
+}
+
+} // namespace QInstaller
+
diff --git a/src/libs/installer/calculatorbase.h b/src/libs/installer/calculatorbase.h
new file mode 100644
index 000000000..351658f99
--- /dev/null
+++ b/src/libs/installer/calculatorbase.h
@@ -0,0 +1,85 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef CALCULATORBASE_H
+#define CALCULATORBASE_H
+
+#include "installer_global.h"
+
+#include <QList>
+#include <QString>
+#include <QMetaEnum>
+
+namespace QInstaller {
+
+class Component;
+class PackageManagerCore;
+
+class INSTALLER_EXPORT CalculatorBase
+{
+public:
+ enum class Resolution {
+ Selected = 0, // "Selected Component(s) without Dependencies" / "Deselected Component(s)"
+ Replaced, // "Component(s) replaced by other components"
+ VirtualDependent, // "No dependencies to virtual component"
+ Dependent, // "Added as dependency for %1." / "Removed as dependency component is removed"
+ Automatic, // "Component(s) added as automatic dependencies" / "Removed as autodependency component is removed"
+ Resolved, // "Component(s) that have resolved Dependencies"
+ Alias // "Components added from selected alias"
+ };
+
+ CalculatorBase(PackageManagerCore *core);
+ virtual ~CalculatorBase() = 0;
+
+ virtual bool solve(const QList<Component *> &components) = 0;
+ void insertResolution(Component *component, const Resolution resolutionType,
+ const QString &referencedComponent = QString());
+
+ QList<Component *> resolvedComponents() const;
+ virtual QString resolutionText(Component *component) const = 0;
+ Resolution resolutionType(Component *component) const;
+
+ QString error() const;
+
+protected:
+ virtual bool solveComponent(Component *component, const QString &version = QString()) = 0;
+ QString referencedComponent(Component *component) const;
+
+protected:
+ PackageManagerCore *m_core;
+ QString m_errorString;
+
+ QList<Component *> m_resolvedComponents;
+ QHash<QString, QPair<Resolution, QString> > m_componentNameResolutionHash;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::CalculatorBase::Resolution)
+
+#endif // CALCULATORBASE_H
diff --git a/src/libs/installer/commandlineparser.cpp b/src/libs/installer/commandlineparser.cpp
new file mode 100644
index 000000000..f9e1f663e
--- /dev/null
+++ b/src/libs/installer/commandlineparser.cpp
@@ -0,0 +1,343 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "commandlineparser.h"
+
+#include "commandlineparser_p.h"
+#include "constants.h"
+#include "globals.h"
+
+namespace CommandLineOptions {
+
+static const QLatin1String scCommand("Command");
+static const QLatin1String scArguments("Args");
+static const QLatin1String scInstallerValue("InstallerValue");
+
+} // namespace CommandLineOptions
+
+CommandLineParser::CommandLineParser()
+ : d(new CommandLineParserPrivate(this))
+{
+ static const QLatin1String indent(" ");
+ static const QString preformatted = QLatin1String("\nQt Installer Framework supports both GUI and "
+ "headless mode. The installation operations can be invoked with the following commands and "
+ "options. Note that the options marked with \"CLI\" are available in the headless mode only.\n")
+ + QLatin1String("\nCommands:\n")
+ + indent + QString::fromLatin1("%1, %2 - install default or selected packages and aliases - <pkg|alias ...>\n")
+ .arg(CommandLineOptions::scInstallShort, CommandLineOptions::scInstallLong)
+ + indent + QString::fromLatin1("%1, %2 - show available updates information on maintenance tool\n")
+ .arg(CommandLineOptions::scCheckUpdatesShort, CommandLineOptions::scCheckUpdatesLong)
+ + indent + QString::fromLatin1("%1, %2 - update all or selected packages - <pkg ...>\n")
+ .arg(CommandLineOptions::scUpdateShort, CommandLineOptions::scUpdateLong)
+ + indent + QString::fromLatin1("%1, %2 - uninstall packages and their child components - <pkg ...>\n")
+ .arg(CommandLineOptions::scRemoveShort, CommandLineOptions::scRemoveLong)
+ + indent + QString::fromLatin1("%1, %2 - list currently installed packages - <regexp for pkg>\n")
+ .arg(CommandLineOptions::scListShort, CommandLineOptions::scListLong)
+ + indent + QString::fromLatin1("%1, %2 - search available aliases or packages - <regexp for pkg|alias>\n")
+ .arg(CommandLineOptions::scSearchShort, CommandLineOptions::scSearchLong)
+ + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
+ .arg(CommandLineOptions::scFilterPackagesLong)
+ + indent + indent + QLatin1String("additional filters for the search operation\n")
+ + indent + indent + QString::fromLatin1("Note: The --%1 option can be used to specify\n")
+ .arg(CommandLineOptions::scTypeLong)
+ + indent + indent + QLatin1String("the content type to search\n")
+ + indent + QString::fromLatin1("%1, %2 - create offline installer from selected packages - <pkg ...>\n")
+ .arg(CommandLineOptions::scCreateOfflineShort, CommandLineOptions::scCreateOfflineLong)
+ + indent + QString::fromLatin1("%1, %2 - clear contents of the local metadata cache\n")
+ .arg(CommandLineOptions::scClearCacheShort, CommandLineOptions::scClearCacheLong)
+ + indent + QString::fromLatin1("%1, %2 - uninstall all packages and remove entire program directory")
+ .arg(CommandLineOptions::scPurgeShort, CommandLineOptions::scPurgeLong);
+
+ m_parser.setApplicationDescription(preformatted);
+
+ // Help & version information
+ m_parser.addHelpOption();
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scVersionShort << CommandLineOptions::scVersionLong,
+ QLatin1String("Displays version information.")));
+
+ // Output related options
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scVerboseShort << CommandLineOptions::scVerboseLong,
+ QString::fromLatin1("Verbose mode. Prints out more information. Adding -%1 or --%2 more "
+ "than once increases verbosity.").arg(CommandLineOptions::scVerboseShort,
+ CommandLineOptions::scVerboseLong)));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scLoggingRulesShort << CommandLineOptions::scLoggingRulesLong,
+ QLatin1String("Enables logging according to passed rules. Comma separated logging rules "
+ "have the following syntax: loggingCategory=true/false. Passing empty logging "
+ "rules enables all logging categories. The following rules enable a single "
+ "category: ifw.*=false, ifw.category=true. The following logging categories "
+ "are available:\n") + QInstaller::loggingCategories().join(QLatin1Char('\n')),
+ QLatin1String("rules")));
+
+ // Repository management options
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scAddRepositoryShort << CommandLineOptions::scAddRepositoryLong,
+ QLatin1String("Add a local or remote repository to the list of user defined repositories."),
+ QLatin1String("URI,...")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scAddTmpRepositoryShort << CommandLineOptions::scAddTmpRepositoryLong,
+ QLatin1String("Add a local or remote repository to the list of temporary available repositories."),
+ QLatin1String("URI,...")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scSetTmpRepositoryShort << CommandLineOptions::scSetTmpRepositoryLong,
+ QLatin1String("Set a local or remote repository as temporary repository, it is the only "
+ "one used during fetch.\nNote: URI must be prefixed with the protocol, i.e. "
+ "file:///, https://, http:// or ftp://."),
+ QLatin1String("URI,...")));
+
+ // Proxy options
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scSystemProxyShort << CommandLineOptions::scSystemProxyLong,
+ QLatin1String("Use system proxy on Windows and Linux. This option has no effect on macOS. (Default)")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scNoProxyShort << CommandLineOptions::scNoProxyLong,
+ QLatin1String("Do not use system proxy.")));
+
+ // Starting mode options
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scStartUpdaterShort << CommandLineOptions::scStartUpdaterLong,
+ QLatin1String("Start application in updater mode. This will override the internal "
+ "marker that is used to distinguish which kind of binary is currently running.")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scStartPackageManagerShort << CommandLineOptions::scStartPackageManagerLong,
+ QLatin1String("Start application in package manager mode. This will override the internal "
+ "marker that is used to distinguish which kind of binary is currently running.")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scStartUninstallerShort << CommandLineOptions::scStartUninstallerLong,
+ QLatin1String("Start application in uninstaller mode. This will override the internal "
+ "marker that is used to distinguish which kind of binary is currently running.")));
+
+ // Misc installation options
+ addOptionWithContext(QCommandLineOption(QStringList()
+ << CommandLineOptions::scRootShort << CommandLineOptions::scRootLong,
+ QLatin1String("[CLI] Set installation root directory."),
+ QLatin1String("directory")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList()
+ << CommandLineOptions::scOfflineInstallerNameShort << CommandLineOptions::scOfflineInstallerNameLong,
+ QLatin1String("[CLI] Set custom filename for the generated offline installer. Without this "
+ "the original filename is used with an added \"_offline-yyyy-MM-dd\" suffix."),
+ QLatin1String("filename")), CommandLineOnly);
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scPlatformShort << CommandLineOptions::scPlatformLong,
+ QLatin1String("Use the specified platform plugin."),
+ QLatin1String("plugin")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scNoForceInstallationShort << CommandLineOptions::scNoForceInstallationLong,
+ QLatin1String("Allow deselecting components that are marked as forced.")));
+ addOption(QCommandLineOption(QStringList() << CommandLineOptions::scNoDefaultInstallationShort
+ << CommandLineOptions::scNoDefaultInstallationLong,
+ QLatin1String("Deselects components that are marked as default.")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scNoSizeCheckingShort << CommandLineOptions::scNoSizeCheckingLong,
+ QLatin1String("Disable checking of free space for installation target.")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scShowVirtualComponentsShort << CommandLineOptions::scShowVirtualComponentsLong,
+ QLatin1String("Show virtual components in installer and package manager.")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scInstallCompressedRepositoryShort
+ << CommandLineOptions::scInstallCompressedRepositoryLong,
+ QLatin1String("Installs QBSP or 7z file. The QBSP (Board Support Package) file must be a .7z "
+ "file which contains a valid repository."),
+ QLatin1String("URI,...")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scCreateLocalRepositoryShort << CommandLineOptions::scCreateLocalRepositoryLong,
+ QLatin1String("Create a local repository inside the installation directory. This option "
+ "has no effect on online installers.")));
+ addOptionWithContext(QCommandLineOption(QStringList()
+ << CommandLineOptions::scFilterPackagesShort << CommandLineOptions::scFilterPackagesLong,
+ QLatin1String("[CLI] Comma separated list of additional key-value pair filters used to query packages with the "
+ "search command. The keys can be any of the possible package information elements, like "
+ "\"DisplayName\" and \"Description\"."),
+ QLatin1String("element=regex,...")), CommandLineOnly);
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scLocalCachePathShort << CommandLineOptions::scLocalCachePathLong,
+ QLatin1String("Sets the path used for local metadata cache. The path must be writable by the current user."),
+ QLatin1String("path")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scTypeLong,
+ QLatin1String("[CLI] Sets the type of the given arguments for commands supporting multiple argument types, "
+ "like \"search\". By default aliases are searched first, and if no matching aliases are found, "
+ "then packages are searched with the same search pattern."),
+ QLatin1String("package|alias")));
+
+ // Message query options
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptMessageQueryShort
+ << CommandLineOptions::scAcceptMessageQueryLong,
+ QLatin1String("[CLI] Accepts all message queries without user input.")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scRejectMessageQueryShort
+ << CommandLineOptions::scRejectMessageQueryLong,
+ QLatin1String("[CLI] Rejects all message queries without user input.")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scMessageAutomaticAnswerShort
+ << CommandLineOptions::scMessageAutomaticAnswerLong,
+ QLatin1String("[CLI] Automatically answers the message queries with the message identifier and button value. "
+ "Several identifier=value pairs can be given separated with comma, "
+ "for example --auto-answer message.id=Ok,message.id2=Cancel."),
+ QLatin1String("identifier=value")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scMessageDefaultAnswerShort
+ << CommandLineOptions::scMessageDefaultAnswerLong,
+ QLatin1String("[CLI] Automatically answers to message queries with their default values.")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scAcceptLicensesShort
+ << CommandLineOptions::scAcceptLicensesLong,
+ QLatin1String("[CLI] Accepts all licenses without user input.")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scFileDialogAutomaticAnswer,
+ QLatin1String("[CLI] Automatically sets the QFileDialog values getExistingDirectory() or getOpenFileName() "
+ "requested by install script. "
+ "Several identifier=value pairs can be given separated with comma, "
+ "for example --file-query filedialog.id=C:/Temp,filedialog.id2=C:/Temp2"),
+ QLatin1String("identifier=value")), CommandLineOnly);
+ addOptionWithContext(QCommandLineOption(QStringList() << CommandLineOptions::scConfirmCommandShort
+ << CommandLineOptions::scConfirmCommandLong, QLatin1String("[CLI] Confirms starting of "
+ "installation, update or removal of components without user input.")), CommandLineOnly);
+
+ // Developer options
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scScriptShort << CommandLineOptions::scScriptLong,
+ QLatin1String("Execute the script given as argument."),
+ QLatin1String("file")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scStartServerShort << CommandLineOptions::scStartServerLong,
+ QLatin1String("Starts the application as headless process waiting for commands to execute. Mode "
+ "can be DEBUG or PRODUCTION. In DEBUG mode, the option values can be omitted. Note: "
+ "The server will not shutdown on his own, you need to quit the process by hand."),
+ QLatin1String("mode, socketname, key")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scStartClientShort << CommandLineOptions::scStartClientLong,
+ QLatin1String("Starts the application to debug the client-server communication. If a value is "
+ "omitted, the client will use a default instead. Note: The server process is not "
+ "started by the client application in that case, you need to start it on your own."),
+ QLatin1String("socketname, key")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scSquishPortShort << CommandLineOptions::scSquishPortLong,
+ QLatin1String("Give a port where Squish can connect to. If no port is given, attach to squish "
+ "not done. Note: To enable Squish support you first need to build IFW with SQUISH_PATH "
+ "parameter where SQUISH_PATH is pointing to your Squish installation folder: "
+ "<path_to_qt>/bin/qmake -r SQUISH_PATH=<pat_to_squish>"),
+ QLatin1String("port number")));
+ addOption(QCommandLineOption(QStringList()
+ << CommandLineOptions::scMaxConcurrentOperationsShort << CommandLineOptions::scMaxConcurrentOperationsLong,
+ QLatin1String("Specifies the maximum number of threads used to perform concurrent operations "
+ "in the unpacking phase of components. Set to a positive number, or 0 (default) "
+ "to let the application determine the ideal thread count from the amount of logical "
+ "processor cores in the system."),
+ QLatin1String("threads")));
+
+ QCommandLineOption cleanupUpdate(CommandLineOptions::scCleanupUpdate);
+ cleanupUpdate.setValueName(QLatin1String("path"));
+ cleanupUpdate.setFlags(QCommandLineOption::HiddenFromHelp);
+ addOption(cleanupUpdate);
+
+ QCommandLineOption cleanupUpdateOnly(CommandLineOptions::scCleanupUpdateOnly);
+ cleanupUpdateOnly.setValueName(QLatin1String("path"));
+ cleanupUpdateOnly.setFlags(QCommandLineOption::HiddenFromHelp);
+ addOption(cleanupUpdateOnly);
+
+ // Deprecated options
+ QCommandLineOption deprecatedUpdater(CommandLineOptions::scDeprecatedUpdater);
+ deprecatedUpdater.setFlags(QCommandLineOption::HiddenFromHelp);
+ addOption(deprecatedUpdater);
+
+ QCommandLineOption deprecatedCheckUpdates(CommandLineOptions::scDeprecatedCheckUpdates);
+ deprecatedCheckUpdates.setFlags(QCommandLineOption::HiddenFromHelp);
+ addOption(deprecatedCheckUpdates); // Behaves like check-updates but does not default to verbose output
+
+ // Custom extension options
+ d->addExtensionsOptions();
+
+ // Positional arguments
+ m_parser.addPositionalArgument(CommandLineOptions::scCommand,
+ QLatin1String("Command to be run by installer. Note that this feature may be disabled by vendor."),
+ QLatin1String("command"));
+ m_parser.addPositionalArgument(CommandLineOptions::scArguments,
+ QLatin1String("Extra arguments for command, each separated by space."),
+ QLatin1String("<args>"));
+ m_parser.addPositionalArgument(CommandLineOptions::scInstallerValue,
+ QLatin1String("Key-value pair to be set internally by the framework."),
+ QLatin1String("<key=value>"));
+}
+
+CommandLineParser::~CommandLineParser()
+{
+ delete d;
+}
+
+bool CommandLineParser::addOptionWithContext(const QCommandLineOption &option, CommandLineParser::OptionContextFlags flags)
+{
+ if (!m_parser.addOption(option))
+ return false;
+
+ for (auto &name : option.names())
+ m_optionContextFlagsNameHash.insert(name, flags);
+
+ return true;
+}
+
+CommandLineParser::OptionContextFlags CommandLineParser::optionContextFlags(const QString &option) const
+{
+ return m_optionContextFlagsNameHash.value(option);
+}
+
+/*
+ Returns the command line arguments of the application. The returned list
+ is context-aware, i.e. options that are set on the parser with
+ \c OptionContextFlag::NoEchoValue are returned with their value hidden.
+*/
+QStringList CommandLineParser::arguments() const
+{
+ const QStringList arguments = QCoreApplication::arguments();
+ QStringList returnArguments;
+ bool skipNext = false;
+ for (const QString &arg : arguments) {
+ if (skipNext) {
+ skipNext = false;
+ continue;
+ }
+ returnArguments << arg;
+ // Append positional arguments as-is
+ if (!arg.startsWith(QLatin1String("--")) && !arg.startsWith(QLatin1Char('-')))
+ continue;
+
+ QString normalizedOption = arg;
+ while (normalizedOption.startsWith(QLatin1Char('-')))
+ normalizedOption.remove(QLatin1Char('-'));
+
+ const OptionContextFlags flags = optionContextFlags(normalizedOption);
+ if (!flags.testFlag(OptionContextFlag::NoEchoValue))
+ continue;
+
+ QString nextArg = arguments.value(arguments.indexOf(arg) + 1);
+ if (!nextArg.isEmpty() && !nextArg.startsWith(QLatin1String("--"))
+ && !nextArg.startsWith(QLatin1Char('-'))) {
+ nextArg = QLatin1String("******");
+ returnArguments << nextArg;
+ skipNext = true;
+ }
+ }
+ return returnArguments;
+}
diff --git a/src/libs/installer/commandlineparser.h b/src/libs/installer/commandlineparser.h
new file mode 100644
index 000000000..3c14d9f45
--- /dev/null
+++ b/src/libs/installer/commandlineparser.h
@@ -0,0 +1,71 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMMANDLINEPARSER_H
+#define COMMANDLINEPARSER_H
+
+#include <QCommandLineParser>
+
+class CommandLineParserPrivate;
+
+class CommandLineParser
+{
+public:
+ enum OptionContextFlag {
+ CommandLineOnly = 0x1,
+ NoEchoValue = 0x2
+ };
+ Q_DECLARE_FLAGS(OptionContextFlags, OptionContextFlag)
+
+ CommandLineParser();
+ ~CommandLineParser();
+
+ bool addOption(const QCommandLineOption &option) { return m_parser.addOption(option); }
+ bool addOptionWithContext(const QCommandLineOption &option, OptionContextFlags flags);
+
+ QString helpText() const { return m_parser.helpText(); }
+ bool isSet(const QString &option) const { return m_parser.isSet(option); }
+ QStringList unknownOptionNames() const { return m_parser.unknownOptionNames(); }
+ QStringList positionalArguments() const { return m_parser.positionalArguments(); }
+ bool parse(const QStringList &argumens) { return m_parser.parse(argumens); }
+ QString value(const QString &option) const { return m_parser.value(option); }
+ QStringList optionNames() const { return m_parser.optionNames(); }
+
+ OptionContextFlags optionContextFlags(const QString &option) const;
+ QStringList arguments() const;
+
+private:
+ QCommandLineParser m_parser;
+ CommandLineParserPrivate *const d;
+
+ QHash<QString, OptionContextFlags> m_optionContextFlagsNameHash;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(CommandLineParser::OptionContextFlags)
+
+#endif // COMMANDLINEPARSER_H
diff --git a/src/libs/installer/commandlineparser_p.cpp b/src/libs/installer/commandlineparser_p.cpp
new file mode 100644
index 000000000..a6b2a4a50
--- /dev/null
+++ b/src/libs/installer/commandlineparser_p.cpp
@@ -0,0 +1,33 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "commandlineparser_p.h"
+
+void CommandLineParserPrivate::addExtensionsOptions() const
+{
+}
diff --git a/src/libs/installer/commandlineparser_p.h b/src/libs/installer/commandlineparser_p.h
new file mode 100644
index 000000000..3c0016b96
--- /dev/null
+++ b/src/libs/installer/commandlineparser_p.h
@@ -0,0 +1,51 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMMANDLINEPARSER_P_H
+#define COMMANDLINEPARSER_P_H
+
+#include "commandlineparser.h"
+
+#include <QCommandLineParser>
+
+class CommandLineParser;
+
+class CommandLineParserPrivate
+{
+ Q_DISABLE_COPY(CommandLineParserPrivate)
+
+public:
+ explicit CommandLineParserPrivate(CommandLineParser *parser) : q(parser) {};
+
+ void addExtensionsOptions() const;
+
+private:
+ CommandLineParser *const q;
+};
+
+#endif // COMMANDLINEPARSER_P_H
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp
index 5a508f118..ce76a2927 100644
--- a/src/libs/installer/component.cpp
+++ b/src/libs/installer/component.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,47 +31,69 @@
#include "errors.h"
#include "fileutils.h"
#include "globals.h"
-#include "lib7z_facade.h"
+#include "archivefactory.h"
#include "messageboxhandler.h"
#include "packagemanagercore.h"
#include "remoteclient.h"
#include "settings.h"
#include "utils.h"
+#include "constants.h"
#include "updateoperationfactory.h"
#include <productkeycheck.h>
#include <QtCore/QDirIterator>
-#include <QtCore/QRegExp>
#include <QtCore/QTranslator>
+#include <QtCore/QRegularExpression>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore/QTextCodec>
+#endif
#include <QApplication>
+#include <QtConcurrentFilter>
#include <QtUiTools/QUiLoader>
+#if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
+#include <private/qv4engine_p.h>
+#else
#include <private/qv8engine_p.h>
+#endif
#include <private/qv4scopedvalue_p.h>
#include <private/qv4object_p.h>
#include <algorithm>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+#include <QJSEngine>
+#else
+#include <QQmlEngine>
+#endif
using namespace QInstaller;
-static const QLatin1String scScriptTag("Script");
-static const QLatin1String scVirtual("Virtual");
-static const QLatin1String scInstalled("Installed");
-static const QLatin1String scUpdateText("UpdateText");
-static const QLatin1String scUninstalled("Uninstalled");
-static const QLatin1String scCurrentState("CurrentState");
-static const QLatin1String scForcedInstallation("ForcedInstallation");
-static const QLatin1String scCheckable("Checkable");
-static const QLatin1String scExpandedByDefault("ExpandedByDefault");
-static const QLatin1String scUnstable("Unstable");
+/*!
+ \enum QInstaller::Component::UnstableError
+
+ This enum type holds the component unstable error reason:
+
+ \value DepencyToUnstable
+ Component has a dependency to an unstable component.
+ \value ShaMismatch
+ Component has packages with non-matching SHA values.
+ \value ScriptLoadingFailed
+ Component script has errors or loading fails.
+ \value MissingDependency
+ Component has dependencies to missing components.
+ \value InvalidTreeName
+ Component has an invalid tree name.
+ \value DescendantOfUnstable
+ Component is descendant of an unstable component.
+*/
/*!
\inmodule QtInstallerFramework
- \class Component::SortingPriorityLessThan
+ \class QInstaller::Component::SortingPriorityLessThan
\brief The SortingPriorityLessThan class sets an increasing sorting order for child components.
If the component contains several children and has this sorting priority set, the child list
@@ -79,14 +101,14 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \fn Component::SortingPriorityLessThan::operator() (const Component *lhs, const Component *rhs) const
+ \fn QInstaller::Component::SortingPriorityLessThan::operator() (const Component *lhs, const Component *rhs) const
Returns \c true if \a lhs is less than \a rhs; otherwise returns \c false.
*/
/*!
\inmodule QtInstallerFramework
- \class Component::SortingPriorityGreaterThan
+ \class QInstaller::Component::SortingPriorityGreaterThan
\brief The SortingPriorityGreaterThan class sets a decreasing sorting priority for child
components.
@@ -95,7 +117,7 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \fn Component::SortingPriorityGreaterThan::operator() (const Component *lhs, const Component *rhs) const
+ \fn QInstaller::Component::SortingPriorityGreaterThan::operator() (const Component *lhs, const Component *rhs) const
Returns \c true if \a lhs is greater than \a rhs; otherwise returns \c false.
*/
@@ -107,20 +129,20 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \property Component::name
+ \property QInstaller::Component::name
\brief The name of the component as set in the \c <Name> tag of the package
information file.
*/
/*!
- \property Component::displayName
+ \property QInstaller::Component::displayName
\brief The name of the component as shown in the user interface.
*/
/*!
- \property Component::autoCreateOperations
+ \property QInstaller::Component::autoCreateOperations
\brief Whether some standard operations for the component should be
automatically created when the installation starts.
@@ -129,7 +151,7 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \property Component::archives
+ \property QInstaller::Component::archives
\brief The list of archive URLs registered for the component.
@@ -139,25 +161,25 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \property Component::dependencies
+ \property QInstaller::Component::dependencies
\brief The components this component depends on. This is a read-only property.
*/
/*!
- \property Component::autoDependencies
+ \property QInstaller::Component::autoDependencies
\brief The value of the \c <AutoDependOn> element in the package information file.
*/
/*!
- \property Component::fromOnlineRepository
+ \property QInstaller::Component::fromOnlineRepository
\brief Whether this component has been loaded from an online repository.
*/
/*!
- \property Component::repositoryUrl
+ \property QInstaller::Component::repositoryUrl
\brief The repository URL the component is downloaded from.
@@ -165,7 +187,7 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \property Component::default
+ \property QInstaller::Component::default
\brief Whether the component is a default one. This is a read-only property.
@@ -173,25 +195,43 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \property Component::installed
+ \property QInstaller::Component::installed
\brief Whether the component is installed. This is a read-only property.
*/
/*!
- \property Component::enabled
+ \property QInstaller::Component::enabled
\brief Whether the component is currently enabled. The property is both readable and writable.
*/
/*!
- \fn Component::loaded()
+ \property QInstaller::Component::unstable
+
+ \brief Whether the component is unstable. This is a read-only property.
+
+ \note Unstable components cannot be selected for installation.
+*/
+
+/*!
+ \property QInstaller::Component::treeName
+
+ \brief The tree name of the component. Specifies the location of the component in the install tree view.
+
+ \note If the tree name is not set, the tree view is organized based on the component name.
+
+ \note Must be unique, the tree name must not conflict with other component's names or tree names.
+*/
+
+/*!
+ \fn QInstaller::Component::loaded()
\sa {component::loaded}{component.loaded}
*/
/*!
- \fn Component::valueChanged(const QString &key, const QString &value)
+ \fn QInstaller::Component::valueChanged(const QString &key, const QString &value)
Emitted when the value of the variable with the name \a key changes to \a value.
@@ -199,7 +239,7 @@ static const QLatin1String scUnstable("Unstable");
*/
/*!
- \fn Component::virtualStateChanged()
+ \fn QInstaller::Component::virtualStateChanged()
\sa {component::virtualStateChanged}{component.virtualStateChanged}
*/
@@ -210,7 +250,13 @@ static const QLatin1String scUnstable("Unstable");
*/
Component::Component(PackageManagerCore *core)
: d(new ComponentPrivate(core, this))
+ , m_defaultArchivePath(scTargetDirPlaceholder)
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
+ QJSEngine::setObjectOwnership(this, QJSEngine::CppOwnership);
+#else
+ QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
+#endif
setPrivate(d);
connect(this, &Component::valueChanged, this, &Component::updateModelData);
@@ -250,26 +296,33 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package)
setValue(scVersion, package.version);
setValue(scInheritVersion, package.inheritVersionFrom);
setValue(scInstalledVersion, package.version);
- setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString());
- setValue(QLatin1String("InstallDate"), package.installDate.toString());
+ setValue(scLastUpdateDate, package.lastUpdateDate.toString());
+ setValue(scInstallDate, package.installDate.toString());
setValue(scUncompressedSize, QString::number(package.uncompressedSize));
- setValue(scDependencies, package.dependencies.join(QLatin1String(",")));
- setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(",")));
+ setValue(scDependencies, package.dependencies.join(scCommaWithSpace));
+ setValue(scAutoDependOn, package.autoDependencies.join(scCommaWithSpace));
+ setValue(scSortingPriority, QString::number(package.sortingPriority));
setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse);
- if (package.forcedInstallation & !PackageManagerCore::noForceInstallation()) {
- setCheckable(false);
- setCheckState(Qt::Checked);
- }
setValue(scVirtual, package.virtualComp ? scTrue : scFalse);
setValue(scCurrentState, scInstalled);
setValue(scCheckable, package.checkable ? scTrue : scFalse);
setValue(scExpandedByDefault, package.expandedByDefault ? scTrue : scFalse);
+ setValue(scContentSha1, package.contentSha1);
+
+ setValue(scTreeName, package.treeName.first);
+ d->m_treeNameMoveChildren = package.treeName.second;
+
+ // scDependencies might be updated from repository later,
+ // keep the local dependencies as well.
+ setValue(scLocalDependencies, value(scDependencies));
}
/*!
Sets variables according to the values set in the package.xml file of \a package.
Also loads UI files, licenses and translations if they are referenced in the package.xml.
+ If the \c PackageManagerCore object of this component is run as package viewer, then
+ only sets the variables without loading referenced files.
*/
void Component::loadDataFromPackage(const Package &package)
{
@@ -290,11 +343,11 @@ void Component::loadDataFromPackage(const Package &package)
setValue(scSortingPriority, package.data(scSortingPriority).toString());
setValue(scEssential, package.data(scEssential).toString());
+ setValue(scForcedUpdate, package.data(scForcedUpdate).toString());
setValue(scUpdateText, package.data(scUpdateText).toString());
setValue(scNewComponent, package.data(scNewComponent).toString());
setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString());
-
- setValue(scScriptTag, package.data(scScriptTag).toString());
+ d->m_scriptHash = package.data(scScriptTag).toHash();
setValue(scReplaces, package.data(scReplaces).toString());
setValue(scReleaseDate, package.data(scReleaseDate).toString());
setValue(scCheckable, package.data(scCheckable).toString());
@@ -304,25 +357,33 @@ void Component::loadDataFromPackage(const Package &package)
if (PackageManagerCore::noForceInstallation())
forced = scFalse;
setValue(scForcedInstallation, forced);
- if (forced == scTrue) {
- setCheckable(false);
- setCheckState(Qt::Checked);
- }
+ setValue(scContentSha1, package.data(scContentSha1).toString());
+ setValue(scCheckSha1CheckSum, package.data(scCheckSha1CheckSum, scTrue).toString().toLower());
+
+ const auto treeNamePair = package.data(scTreeName).value<QPair<QString, bool>>();
+ setValue(scTreeName, treeNamePair.first);
+ d->m_treeNameMoveChildren = treeNamePair.second;
+
+ if (d->m_core->isPackageViewer())
+ return;
setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url));
- const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString()
- .split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
- if (!uis.isEmpty())
- loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis);
- const QStringList qms = package.data(QLatin1String("Translations")).toString()
- .split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
- if (!qms.isEmpty())
- loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms);
+ const QStringList uiList = QInstaller::splitStringWithComma(package.data(scUserInterfaces).toString());
+ if (!uiList.isEmpty())
+ loadUserInterfaces(QDir(scTwoArgs.arg(localTempPath(), name())), uiList);
- QHash<QString, QVariant> licenseHash = package.data(QLatin1String("Licenses")).toHash();
+#ifndef IFW_DISABLE_TRANSLATIONS
+ const QStringList qms = QInstaller::splitStringWithComma(package.data(scTranslations).toString());
+ if (!qms.isEmpty())
+ loadTranslations(QDir(scTwoArgs.arg(localTempPath(), name())), qms);
+#endif
+ QHash<QString, QVariant> licenseHash = package.data(scLicenses).toHash();
if (!licenseHash.isEmpty())
- loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash);
+ loadLicenses(scTwoArgs.arg(localTempPath(), name()), licenseHash);
+ QVariant operationsVariant = package.data(scOperations);
+ if (operationsVariant.canConvert<QList<QPair<QString, QVariant>>>())
+ m_operationsList = operationsVariant.value<QList<QPair<QString, QVariant>>>();
}
/*!
@@ -332,16 +393,20 @@ quint64 Component::updateUncompressedSize()
{
quint64 size = 0;
- if (installAction() == ComponentModelHelper::Install
- || installAction() == ComponentModelHelper::KeepInstalled) {
+ const bool installOrKeepInstalled = (installAction() == ComponentModelHelper::Install
+ || installAction() == ComponentModelHelper::KeepInstalled);
+
+ if (installOrKeepInstalled)
size = d->m_vars.value(scUncompressedSize).toLongLong();
- }
foreach (Component* comp, d->m_allChildComponents)
size += comp->updateUncompressedSize();
setValue(scUncompressedSizeSum, QString::number(size));
- setData(humanReadableSize(size), UncompressedSize);
+ if (size == 0 && !installOrKeepInstalled)
+ setData(QVariant(), UncompressedSize);
+ else
+ setData(humanReadableSize(size), UncompressedSize);
return size;
}
@@ -377,6 +442,16 @@ QString Component::value(const QString &key, const QString &defaultValue) const
}
/*!
+ Removes all the values that have the \a key from the variables set for this component.
+ Returns the number of values removed which is 1 if the key exists in the variables,
+ and 0 otherwise.
+*/
+int Component::removeValue(const QString &key)
+{
+ return d->m_vars.remove(key);
+}
+
+/*!
Sets the value of the variable with \a key to \a value.
\sa {component::setValue}{component.setValue}
@@ -387,13 +462,27 @@ void Component::setValue(const QString &key, const QString &value)
if (d->m_vars.value(key) == normalizedValue)
return;
+ if (key == scDefault && d->m_core->noDefaultInstallation())
+ normalizedValue = scFalse;
if (key == scName)
d->m_componentName = normalizedValue;
- if (key == scCheckable)
- this->setCheckable(normalizedValue.toLower() == scTrue);
+ if (key == scCheckable) // Non-checkable components can still be toggled in updater
+ this->setCheckable(normalizedValue.toLower() == scTrue || d->m_core->isUpdater());
if (key == scExpandedByDefault)
this->setExpandedByDefault(normalizedValue.toLower() == scTrue);
+ if (key == scForcedInstallation) {
+ if (value == scTrue && !d->m_core->isUpdater() && !PackageManagerCore::noForceInstallation()) {
+ // Forced installation components can still be toggled in updater or when
+ // core is set to ignore forced installations.
+ setCheckable(false);
+ setCheckState(Qt::Checked);
+ }
+ }
+ if (key == scAutoDependOn)
+ packageManagerCore()->createAutoDependencyHash(name(), d->m_vars[key], normalizedValue);
+ if (key == scLocalDependencies)
+ packageManagerCore()->createLocalDependencyHash(name(), normalizedValue);
d->m_vars[key] = normalizedValue;
emit valueChanged(key, normalizedValue);
@@ -491,49 +580,66 @@ QString Component::displayName() const
}
/*!
- Loads the component script into the script engine.
+ Returns this component's location in the tree view. If the tree name is not
+ set, returns the component name. The tree name must be unique, it must not
+ conflict with other tree names or component names.
*/
-void Component::loadComponentScript()
+QString Component::treeName() const
{
- const QString script = d->m_vars.value(scScriptTag);
- if (!localTempPath().isEmpty() && !script.isEmpty())
- loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script));
+ const QString defaultValue = d->m_vars.value(scAutoTreeName, name());
+ return d->m_vars.value(scTreeName, defaultValue);
}
/*!
- Loads the script at \a fileName into the script engine. The installer and all its
- components as well as other useful things are being exported into the script.
- For more information, see \l{Component Scripting}.
+ Returns \c true if descendants of this component should have automatically
+ created tree names in relation to the parent component's modified location,
+ \c false otherwise.
+*/
+bool Component::treeNameMoveChildren() const
+{
+ return d->m_treeNameMoveChildren;
+}
+
+/*!
+ Loads the component script into the script engine. Call this method with
+ \a postLoad \c true to a list of components that are updated or installed
+ to improve performance if the amount of components is huge and there are no script
+ functions that need to be called before the installation starts.
+*/
+void Component::loadComponentScript(const bool postLoad)
+{
+ const QString installScript(!postLoad ? d->m_scriptHash.value(scInstallScript).toString()
+ : d->m_scriptHash.value(scPostLoadScript).toString());
+
+ if (!localTempPath().isEmpty() && !installScript.isEmpty()) {
+ evaluateComponentScript(scThreeArgs.arg(localTempPath(), name()
+ , installScript), postLoad);
+ }
+}
- Throws an error when either the script at \a fileName could not be opened, or QScriptEngine
- could not evaluate the script.
+/*!
+ \internal
*/
-void Component::loadComponentScript(const QString &fileName)
+void Component::evaluateComponentScript(const QString &fileName, const bool postScriptContent)
{
// introduce the component object as javascript value and call the name to check that it
// was successful
try {
- d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName,
- QString::fromLatin1("var component = installer.componentByName('%1'); component.name;")
- .arg(name()));
- if (packageManagerCore()->settings().allowUnstableComponents()) {
- // Check if component has dependency to a broken component. Dependencies to broken
- // components are checked if error is thrown but if dependency to a broken
- // component is added in script, the script might not be loaded yet
- foreach (QString dependency, dependencies()) {
- Component *dependencyComponent = packageManagerCore()->componentByName
- (PackageManagerCore::checkableName(dependency));
- if (dependencyComponent && dependencyComponent->isUnstable())
- setUnstable(PackageManagerCore::UnstableError::DepencyToUnstable, QLatin1String("Dependent on unstable component"));
- }
+ if (postScriptContent) {
+ d->m_postScriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
+ } else {
+ d->m_scriptContext = d->scriptEngine()->loadInContext(scComponent, fileName,
+ scComponentScriptTest.arg(name()));
}
} catch (const Error &error) {
- if (packageManagerCore()->settings().allowUnstableComponents()) {
- setUnstable(PackageManagerCore::UnstableError::ScriptLoadingFailed, error.message());
- qWarning() << error.message();
- } else {
+ qCWarning(QInstaller::lcDeveloperBuild) << error.message();
+ setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message());
+ // evaluateComponentScript is called with postScriptContent after we have selected components
+ // and are about to install. Do not allow install if unstable components are allowed
+ // as we then end up installing a component which has invalid script.
+ if (!packageManagerCore()->settings().allowUnstableComponents() || postScriptContent)
throw error;
- }
}
emit loaded();
@@ -547,7 +653,7 @@ void Component::loadComponentScript(const QString &fileName)
*/
void Component::languageChanged()
{
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("retranslateUi"));
+ callScriptMethod(scRetranslateUi);
}
/*!
@@ -559,26 +665,26 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms)
{
QDirIterator it(directory.path(), qms, QDir::Files);
const QStringList translations = d->m_core->settings().translations();
- const QString uiLanguage = QLocale().uiLanguages().value(0, QLatin1String("en"));
+ const QString uiLanguage = QLocale().uiLanguages().value(0, scEn);
while (it.hasNext()) {
const QString filename = it.next();
const QString basename = QFileInfo(filename).baseName();
- if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive))
- continue; // do not load the file if it does not match the UI language
if (!translations.isEmpty()) {
bool found = false;
foreach (const QString &translation, translations)
- found |= translation.startsWith(basename, Qt::CaseInsensitive);
+ found |= translation.startsWith(scIfw_ + basename, Qt::CaseInsensitive);
if (!found) // don't load the file if it does match the UI language but is not allowed to be used
continue;
+ } else if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive)) {
+ continue; // do not load the file if it does not match the UI language
}
- QScopedPointer<QTranslator> translator(new QTranslator(this));
+ std::unique_ptr<QTranslator> translator(new QTranslator(this));
if (translator->load(filename)) {
// Do not throw if translator returns false as it may just be an intentionally
// empty file. See also QTBUG-31031
- qApp->installTranslator(translator.take());
+ qApp->installTranslator(translator.release());
}
}
}
@@ -596,17 +702,17 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
while (it.hasNext()) {
QFile file(it.next());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg(
- it.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
- static QUiLoader loader;
- loader.setTranslationEnabled(true);
- loader.setLanguageChangeEnabled(true);
- QWidget *const widget = loader.load(&file, 0);
+ QUiLoader *const loader = ProductKeyCheck::instance()->uiLoader();
+ loader->setTranslationEnabled(true);
+ loader->setLanguageChangeEnabled(true);
+ QWidget *const widget = loader->load(&file, 0);
if (!widget) {
- throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg(
- it.fileName(), loader.errorString()));
+ throw Error(tr("Cannot load the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ it.fileName(), loader->errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
d->scriptEngine()->newQObject(widget);
d->m_userInterfaces.insert(widget->objectName(), widget);
@@ -615,13 +721,14 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis
/*!
Loads the text of the licenses contained in \a licenseHash from \a directory.
- This is saved into a new hash containing the filename and the text of that file.
+ This is saved into a new hash containing the filename, the text and the priority of that file.
*/
void Component::loadLicenses(const QString &directory, const QHash<QString, QVariant> &licenseHash)
{
QHash<QString, QVariant>::const_iterator it;
for (it = licenseHash.begin(); it != licenseHash.end(); ++it) {
- const QString &fileName = it.value().toString();
+ QVariantMap license = it.value().toMap();
+ const QString &fileName = license.value(scFile).toString();
if (!ProductKeyCheck::instance()->isValidLicenseTextFile(fileName))
continue;
@@ -632,8 +739,8 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
break;
QList<QFileInfo> fileCandidates;
- foreach (const QString &locale, QInstaller::localeCandidates(lang.toLower())) {
- fileCandidates << QFileInfo(QString::fromLatin1("%1%2_%3.%4").arg(
+ foreach (const QString &locale, QInstaller::localeCandidates(lang)) {
+ fileCandidates << QFileInfo(scLocalesArgs.arg(
directory, fileInfo.baseName(), locale,
fileInfo.completeSuffix()));
}
@@ -650,18 +757,54 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar
QFile file(fileInfo.filePath());
if (!file.open(QIODevice::ReadOnly)) {
- throw Error(tr("Cannot open the requested license file \"%1\": %2").arg(
- file.fileName(), file.errorString()));
+ throw Error(tr("Cannot open the requested license file \"%1\": %2.\n\n%3 \"%4\"").arg(
+ file.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath()));
}
QTextStream stream(&file);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
stream.setCodec("UTF-8");
- d->m_licenses.insert(it.key(), qMakePair(fileName, stream.readAll()));
+#endif
+ license.insert(scContent, stream.readAll());
+ d->m_licenses.insert(it.key(), license);
}
}
+/*!
+ Loads all operations defined in the component.xml except Extract operation.
+ Operations are added to the list of operations needed to install this component.
+*/
+void Component::loadXMLOperations()
+{
+ for (auto operation: qAsConst(m_operationsList)) {
+ if (operation.first != scExtract)
+ addOperation(operation.first, operation.second.toStringList());
+ }
+}
/*!
- \property Component::userInterfaces
+ Loads all Extract operations defined in the component.xml.
+ Operations are overwriting the default implementation of Extract operation.
+*/
+void Component::loadXMLExtractOperations()
+{
+ for (auto &operation: qAsConst(m_operationsList)) {
+ if (operation.first == scExtract) {
+ // Create hash for Extract operations. Operation has a mandatory extract folder as
+ // first argument and optional archive name as second argument.
+ const QStringList &operationArgs = operation.second.toStringList();
+ if (operationArgs.count() == 2) {
+ const QString archiveName = value(scVersion) + operationArgs.at(1);
+ const QString archivePath = scInstallerPrefixWithTwoArgs.arg(name()).arg(archiveName);
+ m_archivesHash.insert(archivePath, operationArgs.at(0));
+ } else if (operationArgs.count() == 1) {
+ m_defaultArchivePath = operationArgs.at(0);
+ }
+ }
+ }
+}
+
+/*!
+ \property QInstaller::Component::userInterfaces
\brief A list of all user interface class names known to this component.
*/
@@ -671,9 +814,9 @@ QStringList Component::userInterfaces() const
}
/*!
- Returns a hash that contains the file names and text of license files for the component.
+ Returns a hash that contains the file names, text and priorities of license files for the component.
*/
-QHash<QString, QPair<QString, QString> > Component::licenses() const
+QHash<QString, QVariantMap> Component::licenses() const
{
return d->m_licenses;
}
@@ -709,25 +852,23 @@ void Component::createOperationsForPath(const QString &path)
const QFileInfo fi(path);
// don't copy over a checksum file
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(scCreateOperationsForPath, QJSValueList() << path).isUndefined())
+ return;
QString target;
- static const QString prefix = QString::fromLatin1("installer://");
- target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length()));
+ static const QString prefix = scInstallerPrefix;
+ target = scTargetDirPlaceholderWithArg.arg(path.mid(prefix.length() + name().length()));
if (fi.isFile()) {
- static const QString copy = QString::fromLatin1("Copy");
+ static const QString copy = scCopy;
addOperation(copy, QStringList() << fi.filePath() << target);
} else if (fi.isDir()) {
qApp->processEvents();
- static const QString mkdir = QString::fromLatin1("Mkdir");
+ static const QString mkdir = scMkdir;
addOperation(mkdir, QStringList(target));
QDirIterator it(fi.filePath());
@@ -755,20 +896,22 @@ void Component::createOperationsForArchive(const QString &archive)
const QFileInfo fi(archive);
// don't do anything with sha1 files
- if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
+ if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists())
return;
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined()) {
- return;
- }
+ if (!callScriptMethod(scCreateOperationsForArchive, QJSValueList() << archive).isUndefined())
+ return;
- const bool isZip = Lib7z::isSupportedArchive(archive);
+ QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive));
+ const bool isZip = (archiveFile && archiveFile->open(QIODevice::ReadOnly) && archiveFile->isSupported());
if (isZip) {
- // archives get completely extracted per default (if the script isn't doing other stuff)
- addOperation(QLatin1String("Extract"), QStringList() << archive << QLatin1String("@TargetDir@"));
+ // component.xml can override this value
+ if (m_archivesHash.contains(archive))
+ addOperation(scExtract, QStringList() << archive << m_archivesHash.value(archive));
+ else
+ addOperation(scExtract, QStringList() << archive << m_defaultArchivePath);
} else {
createOperationsForPath(archive);
}
@@ -780,7 +923,7 @@ void Component::createOperationsForArchive(const QString &archive)
void Component::beginInstallation()
{
// the script can override this method
- d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("beginInstallation"));
+ callScriptMethod(scBeginInstallation);
}
/*!
@@ -790,15 +933,15 @@ void Component::beginInstallation()
void Component::createOperations()
{
// the script can override this method
- if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperations"))
- .isUndefined()) {
- d->m_operationsCreated = true;
- return;
+ if (!callScriptMethod(scCreateOperations).isUndefined()) {
+ d->m_operationsCreated = true;
+ return;
}
-
+ loadXMLExtractOperations();
foreach (const QString &archive, archives())
createOperationsForArchive(archive);
+ loadXMLOperations();
d->m_operationsCreated = true;
}
@@ -829,10 +972,17 @@ QList<QPair<QString, bool> > Component::pathsForUninstallation() const
*/
QStringList Component::archives() const
{
- QString pathString = QString::fromLatin1("installer://%1/").arg(name());
+ static const QRegularExpression regExp(scCaretSymbol);
+ QString pathString = scInstallerPrefixWithOneArgs.arg(name());
QStringList archivesNameList = QDir(pathString).entryList();
+
+ // In resources we may have older version of archives, this can happen
+ // when there is offline installer with same component with lower version
+ // number and newer version is available online
+ archivesNameList = archivesNameList.filter(value(scVersion));
+
//RegExp "^" means line beginning
- archivesNameList.replaceInStrings(QRegExp(QLatin1String("^")), pathString);
+ archivesNameList.replaceInStrings(regExp, pathString);
return archivesNameList;
}
@@ -847,12 +997,20 @@ QStringList Component::archives() const
void Component::addDownloadableArchive(const QString &path)
{
Q_ASSERT(isFromOnlineRepository());
-
- qDebug() << "addDownloadable" << path;
+ qCDebug(QInstaller::lcDeveloperBuild) << "addDownloadable" << path;
d->m_downloadableArchives.append(d->m_vars.value(scVersion) + path);
}
/*!
+ \internal
+*/
+void Component::addDownloadableArchives(const QString& archives)
+{
+ Q_ASSERT(isFromOnlineRepository());
+ d->m_downloadableArchivesVariable = archives;
+}
+
+/*!
Removes the archive \a path previously added via addDownloadableArchive() from this component.
This can only be called if this component was downloaded from an online repository.
@@ -867,9 +1025,15 @@ void Component::removeDownloadableArchive(const QString &path)
/*!
Returns the archives to be downloaded from the online repository before installation.
+ Should be called only once when the installation starts.
*/
-QStringList Component::downloadableArchives() const
+QStringList Component::downloadableArchives()
{
+ const QStringList downloadableArchives = d->m_downloadableArchivesVariable
+ .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ foreach (const QString downloadableArchive, downloadableArchives)
+ addDownloadableArchive(downloadableArchive);
+
return d->m_downloadableArchives;
}
@@ -920,33 +1084,43 @@ QStringList Component::stopProcessForUpdateRequests() const
/*!
Returns the operations needed to install this component. If autoCreateOperations() is \c true,
createOperations() is called if no operations have been automatically created yet.
+
+ The \a mask parameter filters the returned operations by their group.
*/
-OperationList Component::operations() const
+OperationList Component::operations(const Operation::OperationGroups &mask) const
{
if (d->m_autoCreateOperations && !d->m_operationsCreated) {
const_cast<Component*>(this)->createOperations();
if (!d->m_minimumProgressOperation) {
d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("MinimumProgress"), d->m_core);
- d->m_minimumProgressOperation->setValue(QLatin1String("component"), name());
+ .create(scMinimumProgress, d->m_core);
+ d->m_minimumProgressOperation->setValue(scComponentSmall, name());
d->m_operations.append(d->m_minimumProgressOperation);
}
if (!d->m_licenses.isEmpty()) {
d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance()
- .create(QLatin1String("License"), d->m_core);
- d->m_licenseOperation->setValue(QLatin1String("component"), name());
+ .create(scLicense, d->m_core);
+ d->m_licenseOperation->setValue(scComponentSmall, name());
QVariantMap licenses;
- const QList<QPair<QString, QString> > values = d->m_licenses.values();
- for (int i = 0; i < values.count(); ++i)
- licenses.insert(values.at(i).first, values.at(i).second);
- d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses);
+ const QList<QVariantMap> values = d->m_licenses.values();
+ for (int i = 0; i < values.count(); ++i) {
+ licenses.insert(values.at(i).value(scFile).toString(),
+ values.at(i).value(scContent));
+ }
+ d->m_licenseOperation->setValue(scLicensesValue, licenses);
d->m_operations.append(d->m_licenseOperation);
}
}
- return d->m_operations;
+ OperationList operations;
+ std::copy_if(d->m_operations.begin(), d->m_operations.end(), std::back_inserter(operations),
+ [&](const Operation *op) {
+ return mask.testFlag(op->group());
+ }
+ );
+ return operations;
}
/*!
@@ -956,7 +1130,7 @@ void Component::addOperation(Operation *operation)
{
d->m_operations.append(operation);
if (RemoteClient::instance().isActive())
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -966,7 +1140,7 @@ void Component::addOperation(Operation *operation)
void Component::addElevatedOperation(Operation *operation)
{
addOperation(operation);
- operation->setValue(QLatin1String("admin"), true);
+ operation->setValue(scAdmin, true);
}
/*!
@@ -1015,21 +1189,24 @@ Operation *Component::createOperation(const QString &operationName, const QStrin
const QMessageBox::StandardButton button =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("OperationDoesNotExistError"), tr("Error"), tr("Error: Operation %1 does not exist.")
- .arg(operationName), QMessageBox::Abort | QMessageBox::Ignore);
+ .arg(operationName), QMessageBox::Abort | QMessageBox::Ignore, QMessageBox::Abort);
if (button == QMessageBox::Abort)
d->m_operationsCreatedSuccessfully = false;
return operation;
}
- if (operation->name() == QLatin1String("Delete"))
- operation->setValue(QLatin1String("performUndo"), false);
+ // Operation can contain variables which are resolved when performing the operation
+ if (operation->requiresUnreplacedVariables())
+ operation->setArguments(parameters);
+ else
+ operation->setArguments(d->m_core->replaceVariables(parameters));
+
- operation->setArguments(d->m_core->replaceVariables(parameters));
- operation->setValue(QLatin1String("component"), name());
+ operation->setValue(scComponentSmall, name());
return operation;
}
-void Component::markComponentUnstable()
+void Component::markComponentUnstable(Component::UnstableError error, const QString &errorMessage)
{
setValue(scDefault, scFalse);
// Mark unstable component unchecked if:
@@ -1042,6 +1219,23 @@ void Component::markComponentUnstable()
if (d->m_core->isInstaller() || !isInstalled() || d->m_core->isUpdater())
setCheckState(Qt::Unchecked);
setValue(scUnstable, scTrue);
+ QMetaEnum metaEnum = QMetaEnum::fromType<Component::UnstableError>();
+ emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name());
+
+ // Update the description and tooltip texts to contain
+ // information about the unstable error.
+ updateModelData(scDescription, QString());
+}
+
+QJSValue Component::callScriptMethod(const QString &methodName, const QJSValueList &arguments) const
+{
+ QJSValue scriptContext;
+ if (!d->m_postScriptContext.isUndefined() && d->m_postScriptContext.property(methodName).isCallable())
+ scriptContext = d->m_postScriptContext;
+ else
+ scriptContext = d->m_scriptContext;
+ return d->scriptEngine()->callScriptMethod(scriptContext,
+ methodName, arguments);
}
namespace {
@@ -1063,7 +1257,7 @@ inline bool convert(QQmlV4Function *func, QStringList *toArgs)
QV4::Object *array = val->as<QV4::Object>();
uint length = array->getLength();
for (uint ii = 0; ii < length; ++ii) {
- valtmp = array->getIndexed(ii);
+ valtmp = array->get(ii);
*toArgs << valtmp->toQStringNoThrow();
}
} else {
@@ -1090,6 +1284,8 @@ bool Component::addOperation(QQmlV4Function *func)
The variables that the parameters contain, such as \c @TargetDir@, are replaced with their
values.
+ Returns \c true if the operation is created; otherwise returns \c false.
+
\sa {component::addOperation}{component.addOperation}
*/
bool Component::addOperation(const QString &operation, const QStringList &parameters)
@@ -1118,6 +1314,8 @@ bool Component::addElevatedOperation(QQmlV4Function *func)
The variables that the parameters contain, such as \c @TargetDir@, are replaced with their
values. The operation is executed with elevated rights.
+ Returns \c true if the operation is created; otherwise returns \c false.
+
\sa {component::addElevatedOperation}{component.addElevatedOperation}
*/
bool Component::addElevatedOperation(const QString &operation, const QStringList &parameters)
@@ -1135,6 +1333,8 @@ bool Component::addElevatedOperation(const QString &operation, const QStringList
would be done by calling createOperations(). If you set this to \c false, it is completely up
to the component's script to create all operations.
+ Returns \c false when component's script will create all operations; otherwise returns \c true.
+
\sa {component::autoCreateOperations}{component.autoCreateOperations}
*/
bool Component::autoCreateOperations() const
@@ -1172,6 +1372,15 @@ bool Component::forcedInstallation() const
}
/*!
+ Returns whether this component is essential. Essential components
+ are always installed, and updated before other components.
+*/
+bool Component::isEssential() const
+{
+ return d->m_vars.value(scEssential, scFalse).toLower() == scTrue;
+}
+
+/*!
Sets the validator callback name to \a name.
*/
void Component::setValidatorCallbackName(const QString &name)
@@ -1186,12 +1395,14 @@ void Component::setValidatorCallbackName(const QString &name)
bool Component::validatePage()
{
if (!validatorCallbackName.isEmpty())
- return d->scriptEngine()->callScriptMethod(d->m_scriptContext, validatorCallbackName).toBool();
+ return callScriptMethod(validatorCallbackName).toBool();
return true;
}
/*!
Adds the component specified by \a newDependency to the list of dependencies.
+ Alternatively, multiple components can be specified by separating each with
+ a comma.
\sa {component::addDependency}{component.addDependency}
\sa dependencies
@@ -1203,19 +1414,32 @@ void Component::addDependency(const QString &newDependency)
if (oldDependencies.isEmpty())
setValue(scDependencies, newDependency);
else
- setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency);
+ setValue(scDependencies, oldDependencies + scCommaWithSpace + newDependency);
}
+/*!
+ Returns a list of dependencies defined in the the repository or in the package.xml.
+*/
QStringList Component::dependencies() const
{
- return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
+ return QInstaller::splitStringWithComma(d->m_vars.value(scDependencies));
+}
+
+/*!
+ Returns a list of installed components dependencies defined in the components.xml.
+*/
+QStringList Component::localDependencies() const
+{
+ return QInstaller::splitStringWithComma(d->m_vars.value(scLocalDependencies));
}
/*!
Adds the component specified by \a newDependOn to the automatic depend-on list.
+ Alternatively, multiple components can be specified by separating each with
+ a comma.
\sa {component::addAutoDependOn}{component.addAutoDependOn}
- \sa dependencies
+ \sa autoDependencies
*/
void Component::addAutoDependOn(const QString &newDependOn)
@@ -1224,12 +1448,30 @@ void Component::addAutoDependOn(const QString &newDependOn)
if (oldDependOn.isEmpty())
setValue(scAutoDependOn, newDependOn);
else
- setValue(scAutoDependOn, oldDependOn + QLatin1String(", ") + newDependOn);
+ setValue(scAutoDependOn, oldDependOn + scCommaWithSpace + newDependOn);
}
QStringList Component::autoDependencies() const
{
- return d->m_vars.value(scAutoDependOn).split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
+ return d->m_vars.value(scAutoDependOn).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+}
+
+/*!
+ Returns a list of dependencies that the component currently has. The
+ dependencies can vary when component is already installed with different
+ dependency list than what is introduced in the repository. If component is
+ not installed, or update is requested to an installed component,
+ current dependencies are read from repository so that correct dependencies
+ are calculated for the component when it is installed or updated.
+*/
+QStringList Component::currentDependencies() const
+{
+ QStringList dependenciesList;
+ if (isInstalled() && !updateRequested())
+ dependenciesList = localDependencies();
+ else
+ dependenciesList = dependencies();
+ return dependenciesList;
}
/*!
@@ -1255,6 +1497,23 @@ bool Component::isAutoDependOn(const QSet<QString> &componentsToInstall) const
if (autoDependOnList.isEmpty())
return false;
+ // If there is an essential update and autodepend on is not for essential
+ // update component, do not add the autodependency to an installed component as
+ // essential updates needs to be installed first, otherwise non-essential components
+ // will be installed
+ if (packageManagerCore()->foundEssentialUpdate()) {
+ const QSet<QString> autoDependOnSet(autoDependOnList.begin(), autoDependOnList.end());
+ if (componentsToInstall.contains(autoDependOnSet)) {
+ foreach (const QString &autoDep, autoDependOnSet) {
+ Component *component = packageManagerCore()->componentByName(autoDep);
+ if ((component->value(scEssential, scFalse).toLower() == scTrue)
+ || component->isForcedUpdate()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
QSet<QString> components = componentsToInstall;
const QStringList installedPackages = d->m_core->localInstalledPackages().keys();
foreach (const QString &name, installedPackages)
@@ -1281,8 +1540,7 @@ bool Component::isDefault() const
if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) {
QJSValue valueFromScript;
try {
- valueFromScript = d->scriptEngine()->callScriptMethod(d->m_scriptContext,
- QLatin1String("isDefault"));
+ valueFromScript = callScriptMethod(scIsDefault);
} catch (const Error &error) {
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()),
@@ -1291,7 +1549,8 @@ bool Component::isDefault() const
}
if (!valueFromScript.isError())
return valueFromScript.toBool();
- qDebug() << "Value from script is not valid." << (valueFromScript.toString().isEmpty()
+ qCWarning(QInstaller::lcDeveloperBuild) << "Value from script is not valid."
+ << (valueFromScript.toString().isEmpty()
? QString::fromLatin1("Unknown error.") : valueFromScript.toString());
return false;
}
@@ -1299,7 +1558,7 @@ bool Component::isDefault() const
return d->m_vars.value(scDefault).compare(scTrue, Qt::CaseInsensitive) == 0;
}
-bool Component::isInstalled(const QString version) const
+bool Component::isInstalled(const QString &version) const
{
if (version.isEmpty()) {
return scInstalled == d->m_vars.value(scCurrentState);
@@ -1337,11 +1596,21 @@ void Component::setUpdateAvailable(bool isUpdateAvailable)
}
/*!
+ Returns whether update is available for this component.
+
+ \sa {component::isUpdateAvailable}{component.isUpdateAvailable}
+*/
+bool Component::isUpdateAvailable() const
+{
+ return d->m_updateIsAvailable && !isUnstable();
+}
+
+/*!
Returns whether the user wants to install the update for this component.
\sa {component::updateRequested}{component.updateRequested}
*/
-bool Component::updateRequested()
+bool Component::updateRequested() const
{
return d->m_updateIsAvailable && isSelected() && !isUnstable();
}
@@ -1356,6 +1625,17 @@ bool Component::componentChangeRequested()
return updateRequested() || isSelectedForInstallation() || uninstallationRequested();
}
+/*!
+ Returns \c true if the component is installed and has a \c ForcedUpdate flag set.
+ ForcedUpdate components will be updated together with essential components before
+ any other component can be updated or installed.
+
+ \sa {component::isForcedUpdate}{component.isForcedUpdate}
+*/
+bool Component::isForcedUpdate()
+{
+ return isInstalled() && (value(scForcedUpdate, scFalse).toLower() == scTrue);
+}
/*!
\sa {component::setUninstalled}{component.setUninstalled}
@@ -1375,31 +1655,40 @@ bool Component::isUninstalled() const
return scUninstalled == d->m_vars.value(scCurrentState);
}
+/*!
+ Returns \c true if this component is unstable.
+*/
+
bool Component::isUnstable() const
{
return scTrue == d->m_vars.value(scUnstable);
}
-void Component::setUnstable(PackageManagerCore::UnstableError error, const QString &errorMessage)
+/*!
+ Sets this component, all its child components and all the components depending on
+ this component unstable with the error code \a error and message \a errorMessage.
+*/
+void Component::setUnstable(Component::UnstableError error, const QString &errorMessage)
{
QList<Component*> dependencies = d->m_core->dependees(this);
// Mark this component unstable
- markComponentUnstable();
+ markComponentUnstable(error, errorMessage);
// Marks all components unstable that depend on the unstable component
foreach (Component *dependency, dependencies) {
- dependency->markComponentUnstable();
+ dependency->markComponentUnstable(UnstableError::DepencyToUnstable,
+ QLatin1String("Dependent on unstable component"));
foreach (Component *descendant, dependency->descendantComponents()) {
- descendant->markComponentUnstable();
+ descendant->markComponentUnstable(UnstableError::DescendantOfUnstable,
+ QLatin1String("Descendant of unstable component"));
}
}
// Marks all child components unstable
foreach (Component *descendant, this->descendantComponents()) {
- descendant->markComponentUnstable();
+ descendant->markComponentUnstable(UnstableError::DescendantOfUnstable,
+ QLatin1String("Descendant of unstable component"));
}
- QMetaEnum metaEnum = QMetaEnum::fromType<PackageManagerCore::UnstableError>();
- emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name());
}
/*!
@@ -1485,23 +1774,23 @@ void Component::updateModelData(const QString &key, const QString &data)
setData(humanReadableSize(size), UncompressedSize);
}
- const QString &updateInfo = d->m_vars.value(scUpdateText);
- if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
- QString tooltipText
- = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
- if (isUnstable()) {
- tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
- "This component can not be installed.");
+ if (key == scUpdateText || key == scDescription) {
+ QString tooltipText;
+ const QString &updateInfo = d->m_vars.value(scUpdateText);
+ if (!d->m_core->isUpdater() || updateInfo.isEmpty()) {
+ tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription));
+ } else {
+ tooltipText = d->m_vars.value(scDescription) + scBr + scBr
+ + tr("Update Info: ") + updateInfo;
}
- setData(tooltipText, Qt::ToolTipRole);
- } else {
- QString tooltipText
- = d->m_vars.value(scDescription) + QLatin1String("<br><br>")
- + tr("Update Info: ") + updateInfo;
if (isUnstable()) {
- tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. "
- "This component can not be updated.");
+ tooltipText += scBr + tr("There was an error loading the selected component. "
+ "This component cannot be installed.");
}
+ static const QRegularExpression externalLinkRegexp(QLatin1String("{external-link}='(.*?)'"));
+ static const QLatin1String externalLinkElement(QLatin1String("<a href=\"\\1\">\\1</a>"));
+ // replace {external-link}='' fields in component description with proper link tags
+ tooltipText.replace(externalLinkRegexp, externalLinkElement);
setData(tooltipText, Qt::ToolTipRole);
}
diff --git a/src/libs/installer/component.h b/src/libs/installer/component.h
index e6bc4874d..8f17b2d98 100644
--- a/src/libs/installer/component.h
+++ b/src/libs/installer/component.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,7 +32,6 @@
#include "constants.h"
#include "component_p.h"
#include "qinstallerglobal.h"
-#include "packagemanagercore.h"
#include <QtCore/QDir>
#include <QtCore/QMetaType>
@@ -44,6 +43,8 @@ QT_FORWARD_DECLARE_CLASS(QQmlV4Function)
namespace QInstaller {
+class PackageManagerCore;
+
class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper
{
Q_OBJECT
@@ -62,11 +63,22 @@ class INSTALLER_EXPORT Component : public QObject, public ComponentModelHelper
Q_PROPERTY(bool installed READ isInstalled)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(bool unstable READ isUnstable)
+ Q_PROPERTY(QString treeName READ treeName)
public:
explicit Component(PackageManagerCore *core);
~Component();
+ enum UnstableError {
+ DepencyToUnstable = 0,
+ ShaMismatch,
+ ScriptLoadingFailed,
+ MissingDependency,
+ InvalidTreeName,
+ DescendantOfUnstable
+ };
+ Q_ENUM(UnstableError)
+
struct SortingPriorityLessThan
{
bool operator() (const Component *lhs, const Component *rhs) const
@@ -97,6 +109,7 @@ public:
QHash<QString, QString> variables() const;
Q_INVOKABLE void setValue(const QString &key, const QString &value);
Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const;
+ int removeValue(const QString &key);
QStringList archives() const;
PackageManagerCore *packageManagerCore() const;
@@ -106,17 +119,18 @@ public:
void removeComponent(Component *component);
QList<Component*> descendantComponents() const;
- void loadComponentScript();
+ void loadComponentScript(const bool postLoad = false);
+ void evaluateComponentScript(const QString &fileName, const bool postScriptContext = false);
- //move this to private
- void loadComponentScript(const QString &fileName);
void loadTranslations(const QDir &directory, const QStringList &qms);
void loadUserInterfaces(const QDir &directory, const QStringList &uis);
void loadLicenses(const QString &directory, const QHash<QString, QVariant> &hash);
+ void loadXMLOperations();
+ void loadXMLExtractOperations();
void markAsPerformedInstallation();
QStringList userInterfaces() const;
- QHash<QString, QPair<QString, QString> > licenses() const;
+ QHash<QString, QVariantMap> licenses() const;
Q_INVOKABLE QWidget *userInterface(const QString &name) const;
Q_INVOKABLE virtual void beginInstallation();
Q_INVOKABLE virtual void createOperations();
@@ -126,7 +140,7 @@ public:
QList<QPair<QString, bool> > pathsForUninstallation() const;
Q_INVOKABLE void registerPathForUninstallation(const QString &path, bool wipe = false);
- OperationList operations() const;
+ OperationList operations(const Operation::OperationGroups &mask = Operation::All) const;
void addOperation(Operation *operation);
Q_INVOKABLE bool addOperation(QQmlV4Function *args);
@@ -136,9 +150,10 @@ public:
Q_INVOKABLE bool addElevatedOperation(QQmlV4Function *args);
bool addElevatedOperation(const QString &operation, const QStringList &parameters);
- QStringList downloadableArchives() const;
+ QStringList downloadableArchives();
Q_INVOKABLE void addDownloadableArchive(const QString &path);
Q_INVOKABLE void removeDownloadableArchive(const QString &path);
+ void addDownloadableArchives(const QString& archives);
QStringList stopProcessForUpdateRequests() const;
Q_INVOKABLE void addStopProcessForUpdateRequest(const QString &process);
@@ -147,6 +162,8 @@ public:
QString name() const;
QString displayName() const;
+ QString treeName() const;
+ bool treeNameMoveChildren() const;
quint64 updateUncompressedSize();
QUrl repositoryUrl() const;
@@ -154,8 +171,10 @@ public:
Q_INVOKABLE void addDependency(const QString &newDependency);
QStringList dependencies() const;
+ QStringList localDependencies() const;
Q_INVOKABLE void addAutoDependOn(const QString &newDependOn);
QStringList autoDependencies() const;
+ QStringList currentDependencies() const;
void languageChanged();
QString localTempPath() const;
@@ -167,7 +186,7 @@ public:
Q_INVOKABLE bool isAutoDependOn(const QSet<QString> &componentsToInstall) const;
Q_INVOKABLE void setInstalled();
- Q_INVOKABLE bool isInstalled(const QString version = QString()) const;
+ Q_INVOKABLE bool isInstalled(const QString &version = QString()) const;
Q_INVOKABLE bool installationRequested() const;
bool isSelectedForInstallation() const;
@@ -178,16 +197,19 @@ public:
Q_INVOKABLE bool isFromOnlineRepository() const;
Q_INVOKABLE void setUpdateAvailable(bool isUpdateAvailable);
- Q_INVOKABLE bool updateRequested();
+ Q_INVOKABLE bool isUpdateAvailable() const;
+ Q_INVOKABLE bool updateRequested() const;
Q_INVOKABLE bool componentChangeRequested();
+ Q_INVOKABLE bool isForcedUpdate();
bool isUnstable() const;
- void setUnstable(PackageManagerCore::UnstableError error, const QString &errorMessage = QString());
+ void setUnstable(Component::UnstableError error, const QString &errorMessage = QString());
bool isVirtual() const;
bool isSelected() const;
bool forcedInstallation() const;
+ bool isEssential() const;
void setValidatorCallbackName(const QString &name);
@@ -214,11 +236,16 @@ private:
const QString &parameter8 = QString(), const QString &parameter9 = QString(),
const QString &parameter10 = QString());
Operation *createOperation(const QString &operationName, const QStringList &parameters);
- void markComponentUnstable();
+ void markComponentUnstable(const Component::UnstableError error, const QString &errorMessage);
+
+ QJSValue callScriptMethod(const QString &methodName, const QJSValueList &arguments = QJSValueList()) const;
private:
QString validatorCallbackName;
ComponentPrivate *d;
+ QList<QPair<QString, QVariant>> m_operationsList;
+ QHash<QString, QString> m_archivesHash;
+ QString m_defaultArchivePath;
};
QDebug operator<<(QDebug dbg, Component *component);
diff --git a/src/libs/installer/component_p.cpp b/src/libs/installer/component_p.cpp
index 5a5284a4b..bf3941274 100644
--- a/src/libs/installer/component_p.cpp
+++ b/src/libs/installer/component_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,6 +35,11 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ComponentPrivate
+ \internal
+*/
// -- ComponentPrivate
@@ -49,6 +54,10 @@ ComponentPrivate::ComponentPrivate(PackageManagerCore *core, Component *qq)
, m_autoCreateOperations(true)
, m_operationsCreatedSuccessfully(true)
, m_updateIsAvailable(false)
+ , m_treeNameMoveChildren(false)
+ , m_postLoadScript(false)
+ , m_scriptContext(QJSValue::UndefinedValue)
+ , m_postScriptContext(QJSValue::UndefinedValue)
{
}
@@ -92,8 +101,8 @@ int ComponentModelHelper::childCount() const
}
/*!
- Returns the component at index position in the list. Index must be a valid position in
- the list (i.e., index >= 0 && index < childCount()). Otherwise it returns 0.
+ Returns the component at \a index position in the list. Index must be a valid position in
+ the list, for example \c{index >= 0 && index < childCount()}. Otherwise it returns 0.
*/
Component *ComponentModelHelper::childAt(int index) const
{
@@ -123,7 +132,7 @@ QList<Component*> ComponentModelHelper::childItems() const
}
/*!
- Determines if the installation status of the component can be changed. The default value is true.
+ Determines if the installation status of the component can be changed. The default return value is \c true.
*/
bool ComponentModelHelper::isEnabled() const
{
@@ -131,7 +140,7 @@ bool ComponentModelHelper::isEnabled() const
}
/*!
- Enables or disables the ability to change the installation status of the components.
+ Enables or disables the ability to change the installation status of the components based on \a enabled.
*/
void ComponentModelHelper::setEnabled(bool enabled)
{
@@ -140,26 +149,26 @@ void ComponentModelHelper::setEnabled(bool enabled)
/*!
Returns whether the component is tri-state; that is, if it's checkable with three separate states.
- The default value is false.
+ The default value is \c false.
*/
bool ComponentModelHelper::isTristate() const
{
- return (flags() & Qt::ItemIsTristate) != 0;
+ return (flags() & Qt::ItemIsAutoTristate) != 0;
}
/*!
- Sets whether the component is tri-state. If tri-state is true, the component is checkable with three
+ Sets whether the component is tri-state based on \a tristate . If tri-state is \c true, the component is checkable with three
separate states; otherwise, the component is checkable with two states.
Note: this also requires that the component is checkable. \sa isCheckable()
*/
void ComponentModelHelper::setTristate(bool tristate)
{
- changeFlags(tristate, Qt::ItemIsTristate);
+ changeFlags(tristate, Qt::ItemIsAutoTristate);
}
/*!
- Returns whether the component is user-checkable. The default value is true.
+ Returns whether the component is user-checkable. The default value is \c true.
*/
bool ComponentModelHelper::isCheckable() const
{
@@ -167,7 +176,7 @@ bool ComponentModelHelper::isCheckable() const
}
/*!
- Sets whether the component is user-checkable. If checkable is true, the component can be checked by the
+ Sets whether the component is user-checkable. If \a checkable is \c true, the component can be checked by the
user; otherwise, the user cannot check the component. The delegate will render a checkable component
with a check box next to the component's text.
*/
@@ -179,18 +188,11 @@ void ComponentModelHelper::setCheckable(bool checkable)
setData(Qt::Unchecked, Qt::CheckStateRole);
}
changeFlags(checkable, Qt::ItemIsUserCheckable);
+ m_componentPrivate->m_vars[scCheckable] = checkable ? scTrue : scFalse;
}
/*!
- Returns whether the component is selectable by the user. The default value is true.
-*/
-bool ComponentModelHelper::isSelectable() const
-{
- return (flags() & Qt::ItemIsSelectable) != 0;
-}
-
-/*!
- Sets whether the component is selectable. If selectable is true, the component can be selected by the
+ Sets whether the component is selectable. If \a selectable is \c true, the component can be selected by the
user; otherwise, the user cannot select the component.
*/
void ComponentModelHelper::setSelectable(bool selectable)
@@ -207,7 +209,7 @@ bool ComponentModelHelper::isExpandedByDefault() const
}
/*!
- Sets whether the component is expanded by default. The default value is \c false.
+ Sets whether the component is expanded by default based on \a expandedByDefault. The default value is \c false.
*/
void ComponentModelHelper::setExpandedByDefault(bool expandedByDefault)
{
@@ -237,7 +239,7 @@ Qt::ItemFlags ComponentModelHelper::flags() const
}
/*!
- Sets the item flags for the component to flags. The item flags determine how the user can interact with
+ Sets the item flags for the component to \a flags. The item flags determine how the user can interact with
the component. This is often used to disable an component.
*/
void ComponentModelHelper::setFlags(Qt::ItemFlags flags)
@@ -254,7 +256,7 @@ Qt::CheckState ComponentModelHelper::checkState() const
}
/*!
- Sets the check state of the component to be state.
+ Sets the check state of the component to be \a state.
*/
void ComponentModelHelper::setCheckState(Qt::CheckState state)
{
@@ -262,7 +264,7 @@ void ComponentModelHelper::setCheckState(Qt::CheckState state)
}
/*!
- Returns the component's data for the given role, or an invalid QVariant if there is no data for role.
+ Returns the component's data for the given \a role, or an invalid QVariant if there is no data for role.
*/
QVariant ComponentModelHelper::data(int role) const
{
@@ -270,7 +272,7 @@ QVariant ComponentModelHelper::data(int role) const
}
/*!
- Sets the component's data for the given role to the specified value.
+ Sets the component's data for the given \a role to the specified \a value.
*/
void ComponentModelHelper::setData(const QVariant &value, int role)
{
diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h
index 05b169d4b..dea2d4935 100644
--- a/src/libs/installer/component_p.h
+++ b/src/libs/installer/component_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -63,21 +63,25 @@ public:
bool m_autoCreateOperations;
bool m_operationsCreatedSuccessfully;
bool m_updateIsAvailable;
- bool m_unstable;
+ bool m_treeNameMoveChildren;
+ bool m_postLoadScript;
QString m_componentName;
QUrl m_repositoryUrl;
QString m_localTempPath;
QJSValue m_scriptContext;
+ QJSValue m_postScriptContext;
QHash<QString, QString> m_vars;
QList<Component*> m_childComponents;
QList<Component*> m_allChildComponents;
QStringList m_downloadableArchives;
+ QString m_downloadableArchivesVariable;
QStringList m_stopProcessForUpdateRequests;
QHash<QString, QPointer<QWidget> > m_userInterfaces;
+ QHash<QString, QVariant> m_scriptHash;
// < display name, < file name, file content > >
- QHash<QString, QPair<QString, QString> > m_licenses;
+ QHash<QString, QVariantMap> m_licenses;
QList<QPair<QString, bool> > m_pathsForUninstallation;
};
@@ -129,7 +133,6 @@ public:
bool isCheckable() const;
void setCheckable(bool checkable);
- bool isSelectable() const;
void setSelectable(bool selectable);
bool isExpandedByDefault() const;
diff --git a/src/libs/installer/componentalias.cpp b/src/libs/installer/componentalias.cpp
new file mode 100644
index 000000000..311ca4475
--- /dev/null
+++ b/src/libs/installer/componentalias.cpp
@@ -0,0 +1,672 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "componentalias.h"
+
+#include "constants.h"
+#include "globals.h"
+#include "packagemanagercore.h"
+#include "updater.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+namespace QInstaller {
+
+static const QStringList scPossibleElements {
+ scName,
+ scDisplayName,
+ scDescription,
+ scVersion,
+ scVirtual,
+ scRequiredComponents,
+ scRequiredAliases,
+ scOptionalComponents,
+ scOptionalAliases,
+ scReleaseDate
+};
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AliasSource
+ \brief Describes a source for alias declarations.
+*/
+
+/*!
+ \enum QInstaller::AliasSource::SourceFileFormat
+
+ This enum type holds the possible file formats for alias source:
+
+ \value Unknown
+ Invalid or unknown file format.
+ \value Xml
+ XML file format.
+ \value Json
+ JSON file format.
+*/
+
+/*!
+ Constructs an alias source with empty information.
+*/
+AliasSource::AliasSource()
+ : priority(-1)
+{}
+
+/*!
+ Constructs an alias source with source file format \a aFormat, filename \a aFilename, and priority \a aPriority.
+*/
+AliasSource::AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority)
+ : format(aFormat)
+ , filename(aFilename)
+ , priority(aPriority)
+{}
+
+/*!
+ Copy-constructs an alias source from \a other.
+*/
+AliasSource::AliasSource(const AliasSource &other)
+{
+ format = other.format;
+ filename = other.filename;
+ priority = other.priority;
+}
+
+/*!
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+hashValue qHash(const AliasSource &key, hashValue seed)
+{
+ return qHash(key.filename, seed) ^ key.priority;
+}
+
+/*!
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
+*/
+bool operator==(const AliasSource &lhs, const AliasSource &rhs)
+{
+ return lhs.filename == rhs.filename && lhs.priority == rhs.priority && lhs.format == rhs.format;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AliasFinder
+ \brief Creates component alias objects from parsed alias source files, based
+ on version and source priorities.
+*/
+
+/*!
+ Constructs a new alias finder with \a core as the package manager instance.
+*/
+AliasFinder::AliasFinder(PackageManagerCore *core)
+ : m_core(core)
+{
+}
+
+/*!
+ Destroys the finder and cleans unreleased results.
+*/
+AliasFinder::~AliasFinder()
+{
+ clear();
+}
+
+/*!
+ Runs the finder. Parses the alias source files and creates component alias
+ objects based on the parsed data. Same alias may be declared in multiple source
+ files, thus source priority and version information is used to decide which
+ source is used for creating the alias object.
+
+ Any previous results are cleared when calling this.
+
+ Returns \c true if at least one alias was found, \c false otherwise.
+*/
+bool AliasFinder::run()
+{
+ clear();
+
+ if (m_sources.isEmpty())
+ return false;
+
+ // 1. Parse source files
+ for (auto &source : qAsConst(m_sources)) {
+ if (source.format == AliasSource::SourceFileFormat::Unknown) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unknown alias source format for file:" << source.filename;
+ continue;
+ }
+ if (source.format == AliasSource::SourceFileFormat::Xml)
+ parseXml(source);
+ else if (source.format == AliasSource::SourceFileFormat::Json)
+ parseJson(source);
+ }
+
+ // 2. Create aliases based on priority & version
+ for (auto &data : qAsConst(m_aliasData)) {
+ const QString name = data.value(scName).toString();
+ const Resolution resolution = checkPriorityAndVersion(data);
+ if (resolution == Resolution::KeepExisting)
+ continue;
+
+ if (resolution == Resolution::RemoveExisting)
+ delete m_aliases.take(name);
+
+ ComponentAlias *alias = new ComponentAlias(m_core);
+ AliasData::const_iterator it;
+ for (it = data.cbegin(); it != data.cend(); ++it) {
+ if (it.value().canConvert<QString>())
+ alias->setValue(it.key(), it.value().toString());
+ }
+ m_aliases.insert(name, alias);
+ }
+
+ return !m_aliases.isEmpty();
+}
+
+/*!
+ Returns a list of the found aliases.
+*/
+QList<ComponentAlias *> AliasFinder::aliases() const
+{
+ return m_aliases.values();
+}
+
+/*!
+ Sets the alias sources to look alias information from to \a sources.
+*/
+void AliasFinder::setAliasSources(const QSet<AliasSource> &sources)
+{
+ clear();
+ m_sources = sources;
+}
+
+/*!
+ Clears the results of the finder.
+*/
+void AliasFinder::clear()
+{
+ qDeleteAll(m_aliases);
+
+ m_aliases.clear();
+ m_aliasData.clear();
+}
+
+/*!
+ Reads an XML file specified by \a filename, and constructs a variant map of
+ the data for each alias.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool AliasFinder::parseXml(AliasSource source)
+{
+ QFile file(source.filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open alias definition for reading:" << file.errorString();
+ return false;
+ }
+
+ QString error;
+ int errorLine;
+ int errorColumn;
+
+ QDomDocument doc;
+ if (!doc.setContent(&file, &error, &errorLine, &errorColumn)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot read alias definition document:" << error
+ << "line:" << errorLine << "column:" << errorColumn;
+ return false;
+ }
+ file.close();
+
+ const QDomElement root = doc.documentElement();
+ const QDomNodeList children = root.childNodes();
+
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ const QString tag = el.tagName();
+ if (el.isNull() || tag != scAlias) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag;
+ continue;
+ }
+
+ AliasData data;
+ data.insert(QLatin1String("source"), QVariant::fromValue(source));
+
+ const QDomNodeList c2 = el.childNodes();
+ for (int j = 0; j < c2.count(); ++j) {
+ const QDomElement el2 = c2.at(j).toElement();
+ const QString tag2 = el2.tagName();
+ if (!scPossibleElements.contains(tag2)) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << tag2;
+ continue;
+ }
+ data.insert(tag2, el2.text());
+ }
+
+ m_aliasData.insert(data.value(scName).toString(), data);
+ }
+
+ return true;
+}
+
+/*!
+ Reads a JSON file specified by \a source, and constructs a variant map of
+ the data for each alias.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool AliasFinder::parseJson(AliasSource source)
+{
+ QFile file(source.filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open alias definition for reading:" << file.errorString();
+ return false;
+ }
+
+ const QByteArray jsonData = file.readAll();
+ const QJsonDocument doc(QJsonDocument::fromJson(jsonData));
+ const QJsonObject docJsonObject = doc.object();
+
+ const QJsonArray aliases = docJsonObject.value(QLatin1String("alias-packages")).toArray();
+ for (auto &it : aliases) {
+ AliasData data;
+ data.insert(QLatin1String("source"), QVariant::fromValue(source));
+
+ QJsonObject aliasObj = it.toObject();
+ for (const auto &key : aliasObj.keys()) {
+ if (!scPossibleElements.contains(key)) {
+ qCWarning(lcInstallerInstallLog) << "Unexpected element name:" << key;
+ continue;
+ }
+
+ const QJsonValue jsonValue = aliasObj.value(key);
+ if (key == scRequiredComponents || key == scRequiredAliases
+ || key == scOptionalComponents || key == scOptionalAliases) {
+ const QJsonArray requirements = jsonValue.toArray();
+ QString requiresString;
+
+ for (const auto &it2 : requirements) {
+ requiresString.append(it2.toString());
+ if (it2 != requirements.last())
+ requiresString.append(QLatin1Char(','));
+ }
+
+ data.insert(key, requiresString);
+ } else if (key == scVirtual) {
+ data.insert(key, QVariant(jsonValue.toBool()))->toString();
+ } else {
+ data.insert(key, jsonValue.toString());
+ }
+ }
+
+ m_aliasData.insert(data.value(scName).toString(), data);
+ }
+
+ return true;
+}
+
+/*!
+ Checks whether \a data should be used for creating a new alias object,
+ based on version and source priority.
+
+ If an alias of the same name exists, always use the one with the higher
+ version. If the new alias has the same version but a higher
+ priority, use the new new alias. Otherwise keep the already existing alias.
+
+ Returns the resolution of the check.
+*/
+AliasFinder::Resolution AliasFinder::checkPriorityAndVersion(const AliasData &data) const
+{
+ for (const auto &existingData : m_aliasData.values(data.value(scName).toString())) {
+ if (existingData == data)
+ continue;
+
+ const int versionMatch = KDUpdater::compareVersion(data.value(scVersion).toString(),
+ existingData.value(scVersion).toString());
+
+ const AliasSource newSource = data.value(QLatin1String("source")).value<AliasSource>();
+ const AliasSource oldSource = existingData.value(QLatin1String("source")).value<AliasSource>();
+
+ if (versionMatch > 0) {
+ // new alias has higher version, use
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: "
+ << data.value(scName).toString() << ", Version: " << existingData.value(scVersion).toString()
+ << ", Source: " << oldSource.filename
+ << "' found an alias with higher version 'Name: "
+ << data.value(scName).toString() << ", Version: " << data.value(scVersion).toString()
+ << ", Source: " << newSource.filename << "'";
+
+ return Resolution::RemoveExisting;
+ }
+
+ if ((versionMatch == 0) && (newSource.priority > oldSource.priority)) {
+ // new alias version equals but priority is higher, use
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Alias 'Name: "
+ << data.value(scName).toString() << ", Priority: " << oldSource.priority
+ << ", Source: " << oldSource.filename
+ << "' found an alias with higher priority 'Name: "
+ << data.value(scName).toString() << ", Priority: " << newSource.priority
+ << ", Source: " << newSource.filename << "'";
+
+ return Resolution::RemoveExisting;
+ }
+
+ return Resolution::KeepExisting; // otherwise keep existing
+ }
+
+ return Resolution::AddNew;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ComponentAlias
+ \brief The ComponentAlias class represents an alias for single or multiple components.
+*/
+
+/*!
+ \enum QInstaller::ComponentAlias::UnstableError
+
+ This enum type holds the possible reasons for marking an alias unstable:
+
+ \value ReferenceToUnstable
+ Alias requires another alias that is marked unstable.
+ \value MissingComponent
+ Alias requires a component that is missing.
+ \value UnselectableComponent
+ Alias requires a component that cannot be selected.
+ \value MissingAlias
+ Alias requires another alias that is missing.
+ \value ComponentNameConfict
+ Alias has a name that conflicts with a name of a component
+*/
+
+/*!
+ Constructs a new component alias with \a core as the package manager instance.
+*/
+ComponentAlias::ComponentAlias(PackageManagerCore *core)
+ : m_core(core)
+ , m_selected(false)
+ , m_unstable(false)
+ , m_missingOptionalComponents (false)
+{
+}
+
+/*!
+ Destructs the alias.
+*/
+ComponentAlias::~ComponentAlias()
+{
+}
+
+/*!
+ Returns the name of the alias.
+*/
+QString ComponentAlias::name() const
+{
+ return m_variables.value(scName);
+}
+
+/*!
+ Returns the display name of the alias.
+*/
+QString ComponentAlias::displayName() const
+{
+ return m_variables.value(scDisplayName);
+}
+
+/*!
+ Returns the description text of the alias.
+*/
+QString ComponentAlias::description() const
+{
+ return m_variables.value(scDescription);
+}
+
+/*!
+ Returns the version of the alias.
+*/
+QString ComponentAlias::version() const
+{
+ return m_variables.value(scVersion);
+}
+
+/*!
+ Returns \c true if the alias is virtual, \c false otherwise.
+
+ Virtual aliases are aliases that cannot be selected by the
+ user, and are invisible. They can be required by other aliases however.
+*/
+bool ComponentAlias::isVirtual() const
+{
+ return m_variables.value(scVirtual, scFalse).toLower() == scTrue;
+}
+
+/*!
+ Returns \c true if the alias is selected for installation, \c false otherwise.
+*/
+bool ComponentAlias::isSelected() const
+{
+ return m_selected;
+}
+
+/*!
+ Sets the selection state of the alias to \a selected. The selection
+ does not have an effect if the alias is unselectable.
+*/
+void ComponentAlias::setSelected(bool selected)
+{
+ if (selected && (isUnstable() || isVirtual()))
+ return;
+
+ m_selected = selected;
+}
+
+/*!
+ Returns the list of components required by this alias, or an
+ empty list if this alias does not require any components.
+*/
+QList<Component *> ComponentAlias::components()
+{
+ if (m_components.isEmpty()) {
+ m_componentErrorMessages.clear();
+ m_missingOptionalComponents = false;
+ const QStringList componentList = QInstaller::splitStringWithComma(
+ m_variables.value(scRequiredComponents));
+
+ const QStringList optionalComponentList = QInstaller::splitStringWithComma(
+ m_variables.value(scOptionalComponents));
+
+ addRequiredComponents(componentList, false);
+ addRequiredComponents(optionalComponentList, true);
+ }
+
+ return m_components;
+}
+
+/*!
+ Returns the list of other aliases required by this alias, or an
+ empty list if this alias does not require any other aliases.
+*/
+QList<ComponentAlias *> ComponentAlias::aliases()
+{
+ if (m_aliases.isEmpty()) {
+ const QStringList aliasList = QInstaller::splitStringWithComma(
+ m_variables.value(scRequiredAliases));
+
+ const QStringList optionalAliasList = QInstaller::splitStringWithComma(
+ m_variables.value(scOptionalAliases));
+
+ addRequiredAliases(aliasList, false);
+ addRequiredAliases(optionalAliasList, true);
+ }
+
+ return m_aliases;
+}
+
+/*!
+ Returns the value specified by \a key, with an optional default value \a defaultValue.
+*/
+QString ComponentAlias::value(const QString &key, const QString &defaultValue) const
+{
+ return m_variables.value(key, defaultValue);
+}
+
+/*!
+ Sets the value specified by \a key to \a value. If the value exists already,
+ it is replaced with the new value.
+*/
+void ComponentAlias::setValue(const QString &key, const QString &value)
+{
+ const QString normalizedValue = m_core->replaceVariables(value);
+ if (m_variables.value(key) == normalizedValue)
+ return;
+
+ m_variables[key] = normalizedValue;
+}
+
+/*!
+ Returns all keys for the component alias values.
+*/
+QStringList ComponentAlias::keys() const
+{
+ return m_variables.keys();
+}
+
+/*!
+ Returns \c true if the alias is marked unstable, \c false otherwise.
+*/
+bool ComponentAlias::isUnstable() const
+{
+ return m_unstable;
+}
+
+/*!
+ Sets the alias unstable with \a error, and a \a message describing the error.
+*/
+void ComponentAlias::setUnstable(UnstableError error, const QString &message)
+{
+ setSelected(false);
+ m_unstable = true;
+
+ const QMetaEnum metaEnum = QMetaEnum::fromType<ComponentAlias::UnstableError>();
+ emit m_core->unstableComponentFound(
+ QLatin1String(metaEnum.valueToKey(error)), message, name());
+}
+
+QString ComponentAlias::componentErrorMessage() const
+{
+ return m_componentErrorMessages;
+}
+
+bool ComponentAlias::missingOptionalComponents() const
+{
+ return m_missingOptionalComponents;
+}
+
+/*!
+ \internal
+
+ Adds the \a aliases to the list of required aliases by this alias. If \a optional
+ is \c true, missing alias references are ignored.
+*/
+void ComponentAlias::addRequiredAliases(const QStringList &aliases, const bool optional)
+{
+ for (const auto &aliasName : aliases) {
+ ComponentAlias *alias = m_core->aliasByName(aliasName);
+ if (!alias) {
+ if (optional)
+ continue;
+
+ const QString error = QLatin1String("No required alias found by name: ") + aliasName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::MissingAlias, error);
+ continue;
+ }
+
+ if (alias->isUnstable()) {
+ if (optional)
+ continue;
+ const QString error = QLatin1String("Alias requires another alias "
+ "that is marked unstable: ") + aliasName;
+ qCWarning(lcInstallerInstallLog) << error;
+
+ setUnstable(UnstableError::ReferenceToUnstable, error);
+ continue;
+ }
+
+ m_aliases.append(alias);
+ }
+}
+
+/*!
+ \internal
+
+ Adds the \a components to the list of required components by this alias. If \a optional
+ is \c true, missing component references are ignored.
+*/
+void ComponentAlias::addRequiredComponents(const QStringList &components, const bool optional)
+{
+ for (const auto &componentName : components) {
+ Component *component = m_core->componentByName(componentName);
+ if (!component) {
+ if (optional) {
+ m_missingOptionalComponents = true;
+ continue;
+ }
+
+ const QString error = QLatin1String("No required component found by name: ")
+ + componentName;
+ if (!m_componentErrorMessages.isEmpty())
+ m_componentErrorMessages.append(QLatin1String("\n"));
+ m_componentErrorMessages.append(error);
+
+ setUnstable(UnstableError::MissingComponent, error);
+ continue;
+ }
+
+ if (component->isUnstable() || !component->isCheckable()) {
+ if (optional)
+ continue;
+ const QString error = QLatin1String("Alias requires component that is uncheckable or unstable: ")
+ + componentName;
+ if (!m_componentErrorMessages.isEmpty())
+ m_componentErrorMessages.append(QLatin1String("\n"));
+ m_componentErrorMessages.append(error);
+
+ setUnstable(UnstableError::UnselectableComponent, error);
+ continue;
+ }
+
+ m_components.append(component);
+ }
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/componentalias.h b/src/libs/installer/componentalias.h
new file mode 100644
index 000000000..3083260cd
--- /dev/null
+++ b/src/libs/installer/componentalias.h
@@ -0,0 +1,164 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMPONENTALIAS_H
+#define COMPONENTALIAS_H
+
+#include "installer_global.h"
+
+#include <QHash>
+#include <QMetaEnum>
+#include <QString>
+#include <QSet>
+
+namespace QInstaller {
+
+class Component;
+class ComponentAlias;
+class PackageManagerCore;
+
+struct INSTALLER_EXPORT AliasSource
+{
+ enum class SourceFileFormat {
+ Unknown = -1,
+ Xml = 0,
+ Json
+ };
+
+ AliasSource();
+ AliasSource(SourceFileFormat aFormat, const QString &aFilename, int aPriority);
+ AliasSource(const AliasSource &other);
+
+ SourceFileFormat format;
+ QString filename;
+ int priority;
+};
+
+INSTALLER_EXPORT hashValue qHash(const AliasSource &key, hashValue seed);
+INSTALLER_EXPORT bool operator==(const AliasSource &lhs, const AliasSource &rhs);
+
+class INSTALLER_EXPORT AliasFinder
+{
+public:
+ using AliasData = QVariantMap;
+ using AliasDataHash = QMultiHash<QString, AliasData>;
+
+ enum struct Resolution {
+ AddNew,
+ KeepExisting,
+ RemoveExisting
+ };
+
+ explicit AliasFinder(PackageManagerCore *core);
+ ~AliasFinder();
+
+ bool run();
+ QList<ComponentAlias *> aliases() const;
+
+ void setAliasSources(const QSet<AliasSource> &sources);
+
+private:
+ void clear();
+ Resolution checkPriorityAndVersion(const AliasData &data) const;
+
+ bool parseXml(AliasSource source);
+ bool parseJson(AliasSource source);
+
+private:
+ PackageManagerCore *const m_core;
+
+ QSet<AliasSource> m_sources;
+ AliasDataHash m_aliasData;
+ QHash<QString, ComponentAlias *> m_aliases;
+};
+
+class INSTALLER_EXPORT ComponentAlias : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(ComponentAlias)
+
+public:
+ enum UnstableError {
+ ReferenceToUnstable = 0,
+ MissingComponent,
+ UnselectableComponent,
+ MissingAlias,
+ ComponentNameConfict
+ };
+ Q_ENUM(UnstableError)
+
+ ComponentAlias(PackageManagerCore *core);
+ ~ComponentAlias();
+
+ QString name() const;
+ QString displayName() const;
+ QString description() const;
+
+ QString version() const;
+
+ bool isVirtual() const;
+
+ bool isSelected() const;
+ void setSelected(bool selected);
+
+ QList<Component *> components();
+ QList<ComponentAlias *> aliases();
+
+ QString value(const QString &key, const QString &defaultValue = QString()) const;
+ void setValue(const QString &key, const QString &value);
+ QStringList keys() const;
+
+ bool isUnstable() const;
+ void setUnstable(UnstableError error, const QString &message = QString());
+ QString componentErrorMessage() const;
+ bool missingOptionalComponents() const;
+
+private:
+ void addRequiredAliases(const QStringList &aliases, const bool optional);
+ void addRequiredComponents(const QStringList &components, const bool optional);
+
+private:
+ PackageManagerCore *const m_core;
+
+ QHash<QString, QString> m_variables;
+
+ bool m_selected;
+ bool m_unstable;
+
+ QList<Component *> m_components;
+ bool m_missingOptionalComponents;
+ QList<ComponentAlias *> m_aliases;
+ QString m_componentErrorMessages;
+};
+
+} // namespace QInstaller
+
+Q_DECLARE_METATYPE(QInstaller::ComponentAlias *)
+Q_DECLARE_METATYPE(QInstaller::AliasSource);
+
+#endif // COMPONENTALIAS_H
diff --git a/src/libs/installer/componentchecker.cpp b/src/libs/installer/componentchecker.cpp
index de8626329..b755d1260 100644
--- a/src/libs/installer/componentchecker.cpp
+++ b/src/libs/installer/componentchecker.cpp
@@ -35,6 +35,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ComponentChecker
+ \internal
+*/
+
QStringList ComponentChecker::checkComponent(Component *component)
{
QStringList checkResult;
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 69c709060..1e8dd1ff7 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,6 +47,8 @@ namespace QInstaller {
This enum value holds the checked state of the components available for
installation.
+ \value Empty
+ The model does not contain any components.
\value AllChecked
All components are checked.
\value AllUnchecked
@@ -58,14 +60,7 @@ namespace QInstaller {
*/
/*!
- \fn void ComponentModel::checkStateChanged(const QModelIndex &index)
-
- This signal is emitted whenever the checked state of a component is changed. The \a index value
- indicates the QModelIndex representation of the component as seen from the model.
-*/
-
-/*!
- \fn void ComponentModel::checkStateChanged(QInstaller::ComponentModel::ModelState state)
+ \fn void QInstaller::ComponentModel::checkStateChanged(QInstaller::ComponentModel::ModelState state)
This signal is emitted whenever the checked state of a model is changed after all state
calculations have taken place. The \a state is a combination of \c ModelStateFlag values
@@ -101,7 +96,6 @@ ComponentModel::ComponentModel(int columns, PackageManagerCore *core)
, m_modelState(DefaultChecked)
{
m_headerData.insert(0, columns, QVariant());
- connect(this, &QAbstractItemModel::modelReset, this, &ComponentModel::slotModelReset);
}
/*!
@@ -159,7 +153,7 @@ QModelIndex ComponentModel::parent(const QModelIndex &child) const
if (Component *childComponent = componentFromIndex(child)) {
if (Component *parent = childComponent->parentComponent())
- return indexFromComponentName(parent->name());
+ return indexFromComponentName(parent->treeName());
}
return QModelIndex();
}
@@ -222,7 +216,10 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
return component->data(Qt::UserRole + index.column());
}
if (role == Qt::CheckStateRole) {
- if (!component->isCheckable() || !component->autoDependencies().isEmpty() || component->isUnstable())
+ if (!component->isCheckable() || component->isUnstable())
+ return QVariant();
+
+ if (!m_core->isUpdater() && !component->autoDependencies().isEmpty())
return QVariant();
}
if (role == ComponentModelHelper::ExpandedByDefault) {
@@ -239,7 +236,6 @@ QVariant ComponentModel::data(const QModelIndex &index, int role) const
/*!
Sets the \a role data for the item at \a index to \a value. Returns true if successful;
otherwise returns false. The dataChanged() signal is emitted if the data was successfully set.
- The checkStateChanged() signals are emitted in addition if the checked state of the item is set.
*/
bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
@@ -253,17 +249,17 @@ bool ComponentModel::setData(const QModelIndex &index, const QVariant &value, in
if (role == Qt::CheckStateRole) {
if (index.column() != 0)
return false;
- ComponentSet nodes = component->childItems().toSet();
+
+ const QList<Component*> childItems = component->childItems();
+ ComponentSet nodes(childItems.begin(), childItems.end());
Qt::CheckState newValue = Qt::CheckState(value.toInt());
if (newValue == Qt::PartiallyChecked) {
const Qt::CheckState oldValue = component->checkState();
newValue = (oldValue == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
}
- QSet<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
- foreach (const QModelIndex &index, changed) {
- emit dataChanged(index, index);
- emit checkStateChanged(index);
- }
+ const QSet<QModelIndex> changed = updateCheckedState(nodes << component, newValue);
+ foreach (const QModelIndex &changedIndex, changed)
+ emit dataChanged(changedIndex, changedIndex);
updateAndEmitModelState(); // update the internal state
} else {
component->setData(value, role);
@@ -339,6 +335,22 @@ QSet<Component *> ComponentModel::uncheckable() const
return m_uncheckable;
}
+bool ComponentModel::componentsSelected() const
+{
+ if (m_core->isInstaller() || m_core->isUpdater())
+ return checked().count();
+
+ if (checkedState().testFlag(ComponentModel::DefaultChecked) == false)
+ return true;
+
+ const QSet<Component *> uncheckables = uncheckable();
+ for (auto &component : uncheckables) {
+ if (component->forcedInstallation() && !component->isInstalled())
+ return true; // allow installation for new forced components
+ }
+ return false;
+}
+
/*!
Returns a pointer to the PackageManagerCore this model belongs to.
*/
@@ -385,20 +397,20 @@ Component *ComponentModel::componentFromIndex(const QModelIndex &index) const
// -- public slots
/*!
- Sets \a rootComponents to be the list of currently shown components.
+ Resets model and sets \a rootComponents to be the list of currently shown components.
The model is repopulated and the individual component's checked state is used to show the check
mark in front of the visual component representation. The modelAboutToBeReset() and
modelReset() signals are emitted.
*/
-void ComponentModel::setRootComponents(QList<QInstaller::Component*> rootComponents)
+void ComponentModel::reset(QList<Component *> rootComponents)
{
beginResetModel();
m_uncheckable.clear();
m_indexByNameCache.clear();
m_rootComponentList.clear();
- m_modelState = DefaultChecked;
+ m_modelState = !rootComponents.isEmpty() ? DefaultChecked : Empty;
// Initialize these with an empty set for every possible state, cause we compare the hashes later in
// updateAndEmitModelState(). The comparison than might lead to wrong results if one of the checked
@@ -417,49 +429,49 @@ void ComponentModel::setRootComponents(QList<QInstaller::Component*> rootCompone
m_rootComponentList.append(component);
}
endResetModel();
+ postModelReset();
}
/*!
Sets the checked state of every component in the model to be \a state.
The ComponentModel::PartiallyChecked flag is ignored by this function. Note that components
- are not changed if they are not checkable. The dataChanged() and checkStateChanged() signals
- are emitted.
+ are not changed if they are not checkable. The modelCheckStateChanged() signal
+ is emitted.
*/
void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag state)
{
- QSet<QModelIndex> changed;
switch (state) {
case AllChecked:
- changed = updateCheckedState(m_currentCheckedState[Qt::Unchecked], Qt::Checked);
+ updateCheckedState(m_currentCheckedState[Qt::Unchecked], Qt::Checked);
break;
case AllUnchecked:
- changed = updateCheckedState(m_currentCheckedState[Qt::Checked], Qt::Unchecked);
+ updateCheckedState(m_currentCheckedState[Qt::Checked], Qt::Unchecked);
break;
case DefaultChecked:
// record all changes, to be able to update the UI properly
- changed = updateCheckedState(m_currentCheckedState[Qt::Checked], Qt::Unchecked);
- changed += updateCheckedState(m_initialCheckedState[Qt::Checked], Qt::Checked);
+ updateCheckedState(m_currentCheckedState[Qt::Checked], Qt::Unchecked);
+ updateCheckedState(m_initialCheckedState[Qt::Checked], Qt::Checked);
break;
default:
break;
}
-
- if (changed.isEmpty())
- return;
-
- // notify about changes done to the model
- foreach (const QModelIndex &index, changed) {
- emit dataChanged(index, index);
- emit checkStateChanged(index);
- }
updateAndEmitModelState(); // update the internal state
}
// -- private slots
-void ComponentModel::slotModelReset()
+void ComponentModel::onVirtualStateChanged()
+{
+ // If the virtual state of a component changes, force a reset of the component model.
+ reset(m_core->components(PackageManagerCore::ComponentType::Root));
+}
+
+
+// -- private
+
+void ComponentModel::postModelReset()
{
ComponentList components = m_rootComponentList;
if (!m_core->isUpdater()) {
@@ -485,17 +497,12 @@ void ComponentModel::slotModelReset()
updateAndEmitModelState(); // update the internal state
}
-void ComponentModel::onVirtualStateChanged()
-{
- // If the virtual state of a component changes, force a reset of the component model.
- setRootComponents(m_core->components(PackageManagerCore::ComponentType::Root));
-}
-
-
-// -- private
-
void ComponentModel::updateAndEmitModelState()
{
+ if (m_rootComponentList.isEmpty()) {
+ m_modelState = ComponentModel::Empty;
+ return;
+ }
m_modelState = ComponentModel::DefaultChecked;
if (m_initialCheckedState != m_currentCheckedState)
m_modelState = ComponentModel::PartiallyChecked;
@@ -511,20 +518,11 @@ void ComponentModel::updateAndEmitModelState()
}
emit checkStateChanged(m_modelState);
-
- foreach (const Component *component, m_rootComponentList) {
- emit dataChanged(indexFromComponentName(component->name()),
- indexFromComponentName(component->name()));
- QList<Component *> children = component->childItems();
- foreach (const Component *child, children)
- emit dataChanged(indexFromComponentName(child->name()),
- indexFromComponentName(child->name()));
- }
}
void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const
{
- m_indexByNameCache.insert(component->name(), parent);
+ m_indexByNameCache.insert(component->treeName(), parent);
for (int i = 0; i < component->childCount(); ++i)
collectComponents(component->childAt(i), index(i, 0, parent));
}
@@ -563,13 +561,13 @@ static Qt::CheckState verifyPartiallyChecked(Component *component)
} // namespace ComponentModelPrivate
-QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, Qt::CheckState state)
+QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &components, const Qt::CheckState state)
{
// get all parent nodes for the components we're going to update
- QMap<QString, Component *> sortedNodesMap;
+ QMultiMap<QString, Component *> sortedNodesMap;
foreach (Component *component, components) {
- while (component && !sortedNodesMap.values(component->name()).contains(component)) {
- sortedNodesMap.insertMulti(component->name(), component);
+ while (component && !sortedNodesMap.values(component->treeName()).contains(component)) {
+ sortedNodesMap.insert(component->treeName(), component);
component = component->parentComponent();
}
}
@@ -580,14 +578,16 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone
for (int i = sortedNodes.count(); i > 0; i--) {
Component * const node = sortedNodes.at(i - 1);
- bool checkable = true;
- if (node->value(scCheckable, scTrue).toLower() == scFalse) {
- checkable = false;
- }
+ if (!node->isEnabled() || node->isUnstable())
+ continue;
- if ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty() || node->isUnstable())
+ //Do not let forced installations to be uninstalled
+ if (!m_core->isUpdater() && node->forcedInstallation() && (node->checkState() != Qt::Unchecked))
continue;
+ if (!m_core->isUpdater() && !node->autoDependencies().isEmpty())
+ continue;
+
Qt::CheckState newState = state;
const Qt::CheckState recentState = node->checkState();
if (node->isTristate())
@@ -596,7 +596,7 @@ QSet<QModelIndex> ComponentModel::updateCheckedState(const ComponentSet &compone
continue;
node->setCheckState(newState);
- changed.insert(indexFromComponentName(node->name()));
+ changed.insert(indexFromComponentName(node->treeName()));
m_currentCheckedState[Qt::Checked].remove(node);
m_currentCheckedState[Qt::Unchecked].remove(node);
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index 8a9fbf884..c93dd60ae 100644
--- a/src/libs/installer/componentmodel.h
+++ b/src/libs/installer/componentmodel.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,8 @@
#include "qinstallerglobal.h"
+#include "component.h"
+
#include <QtCore/QAbstractItemModel>
#include <QtCore/QList>
#include <QtCore/QSet>
@@ -38,7 +40,6 @@
namespace QInstaller {
-class Component;
class PackageManagerCore;
class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
@@ -49,6 +50,7 @@ class INSTALLER_EXPORT ComponentModel : public QAbstractItemModel
public:
enum ModelStateFlag {
+ Empty = -0x01,
AllChecked = 0x01,
AllUnchecked = 0x02,
DefaultChecked = 0x04,
@@ -59,25 +61,26 @@ public:
explicit ComponentModel(int columns, PackageManagerCore *core = 0);
~ComponentModel();
- Qt::ItemFlags flags(const QModelIndex &index) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
- QModelIndex parent(const QModelIndex &child) const;
- QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &child) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value,
- int role = Qt::EditRole);
+ int role = Qt::EditRole) override;
QSet<Component *> checked() const;
QSet<Component *> partially() const;
QSet<Component *> unchecked() const;
QSet<Component *> uncheckable() const;
+ bool componentsSelected() const;
PackageManagerCore *core() const;
ComponentModel::ModelState checkedState() const;
@@ -86,21 +89,20 @@ public:
Component* componentFromIndex(const QModelIndex &index) const;
public Q_SLOTS:
- void setRootComponents(QList<QInstaller::Component*> rootComponents);
+ void reset(QList<Component *> rootComponents = QList<Component *>());
void setCheckedState(QInstaller::ComponentModel::ModelStateFlag state);
Q_SIGNALS:
- void checkStateChanged(const QModelIndex &index);
void checkStateChanged(QInstaller::ComponentModel::ModelState state);
private Q_SLOTS:
- void slotModelReset();
void onVirtualStateChanged();
private:
+ void postModelReset();
void updateAndEmitModelState();
void collectComponents(Component *const component, const QModelIndex &parent) const;
- QSet<QModelIndex> updateCheckedState(const ComponentSet &components, Qt::CheckState state);
+ QSet<QModelIndex> updateCheckedState(const ComponentSet &components, const Qt::CheckState state);
private:
PackageManagerCore *m_core;
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index 84349e4ad..b68eebf06 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,12 +28,14 @@
#include "componentselectionpage_p.h"
+#include "globals.h"
#include "packagemanagergui.h"
#include "componentmodel.h"
#include "settings.h"
#include "component.h"
#include "fileutils.h"
#include "messageboxhandler.h"
+#include "customcombobox.h"
#include <QTreeView>
#include <QLabel>
@@ -44,120 +46,194 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QCheckBox>
-#include <QHeaderView>
#include <QStandardPaths>
#include <QFileDialog>
+#include <QStackedLayout>
+#include <QStackedWidget>
+#include <QLineEdit>
+#include <QStandardItemModel>
+#include <QStyledItemDelegate>
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ComponentSelectionPagePrivate
+ \internal
+*/
+
+constexpr int scNoCheckSelectionIndex = -1;
+constexpr int scCheckDefaultIndex = 0;
+constexpr int scCheckAllIndex = 1;
+constexpr int scUncheckAllIndex = 2;
+
ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core)
: q(qq)
, m_core(core)
, m_treeView(new QTreeView(q))
+ , m_tabWidget(nullptr)
+ , m_descriptionBaseWidget(nullptr)
+ , m_categoryWidget(Q_NULLPTR)
+ , m_allowCreateOfflineInstaller(false)
+ , m_categoryLayoutVisible(false)
, m_allModel(m_core->defaultComponentModel())
, m_updaterModel(m_core->updaterComponentModel())
, m_currentModel(m_allModel)
- , m_allowCompressedRepositoryInstall(false)
- , m_categoryWidget(Q_NULLPTR)
+ , m_proxyModel(m_core->componentSortFilterProxyModel())
+ , m_componentsResolved(false)
+ , m_headerStretchLastSection(false)
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
+ m_treeView->setUniformRowHeights(true);
+
+ m_descriptionBaseWidget = new QWidget(q);
+ m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
+
+ QVBoxLayout *descriptionVLayout = new QVBoxLayout(m_descriptionBaseWidget);
+ descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
+ descriptionVLayout->setContentsMargins(0, 0, 0, 0);
- connect(m_allModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this,
- SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState)));
- connect(m_updaterModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)),
- this, SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState)));
+ m_tabWidget = new QTabWidget(q);
+ m_tabWidget->setObjectName(QLatin1String("ComponentSelectionTabWidget"));
+ m_tabWidget->tabBar()->setObjectName(QLatin1String("ComponentSelectionTabBar"));
+ m_tabWidget->hide();
- m_descriptionVLayout = new QVBoxLayout;
- m_descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
+ m_rightSideVLayout = new QVBoxLayout;
- m_descriptionScrollArea = new QScrollArea(q);
- m_descriptionScrollArea->setWidgetResizable(true);
- m_descriptionScrollArea->setFrameShape(QFrame::NoFrame);
- m_descriptionScrollArea->setObjectName(QLatin1String("DescriptionScrollArea"));
+ QScrollArea *descriptionScrollArea = new QScrollArea(q);
+ descriptionScrollArea->setWidgetResizable(true);
+ descriptionScrollArea->setFrameShape(QFrame::NoFrame);
+ descriptionScrollArea->setObjectName(QLatin1String("DescriptionScrollArea"));
- m_descriptionLabel = new QLabel(q);
+ m_descriptionLabel = new QLabel(m_descriptionBaseWidget);
m_descriptionLabel->setWordWrap(true);
m_descriptionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
m_descriptionLabel->setOpenExternalLinks(true);
m_descriptionLabel->setObjectName(QLatin1String("ComponentDescriptionLabel"));
m_descriptionLabel->setAlignment(Qt::AlignTop);
- m_descriptionScrollArea->setWidget(m_descriptionLabel);
- m_descriptionVLayout->addWidget(m_descriptionScrollArea);
+ descriptionScrollArea->setWidget(m_descriptionLabel);
+ descriptionVLayout->addWidget(descriptionScrollArea);
- m_sizeLabel = new QLabel(q);
+ m_sizeLabel = new QLabel(m_descriptionBaseWidget);
m_sizeLabel->setWordWrap(true);
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
- m_descriptionVLayout->addWidget(m_sizeLabel);
+ descriptionVLayout->addWidget(m_sizeLabel);
+
+ m_createOfflinePushButton = new QPushButton(q);
+ m_createOfflinePushButton->setVisible(false);
+ m_createOfflinePushButton->setText(ComponentSelectionPage::tr("Create Offline Installer"));
+ m_createOfflinePushButton->setToolTip(
+ ComponentSelectionPage::tr("Create offline installer from selected components, instead "
+ "of installing now."));
+
+ connect(m_createOfflinePushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::createOfflineButtonClicked);
+ connect(q, &ComponentSelectionPage::completeChanged,
+ this, [&]() { m_createOfflinePushButton->setEnabled(q->isComplete()); });
+
+ m_qbspPushButton = new QPushButton(q);
+ m_qbspPushButton->setVisible(false);
+ m_qbspPushButton->setText(ComponentSelectionPage::tr("Browse &QBSP files"));
+ m_qbspPushButton->setToolTip(
+ ComponentSelectionPage::tr("Select a Qt Board Support Package file to install "
+ "additional content that is not directly available from the online repositories."));
+
+ connect(m_qbspPushButton, &QPushButton::clicked,
+ this, &ComponentSelectionPagePrivate::qbspButtonClicked);
+
+ m_rightSideVLayout->addWidget(m_descriptionBaseWidget);
+ m_rightSideVLayout->addWidget(m_createOfflinePushButton);
+ m_rightSideVLayout->addWidget(m_qbspPushButton);
+
+ QHBoxLayout *topHLayout = new QHBoxLayout;
+
+ // Using custom combobox to workaround QTBUG-90595
+ m_checkStateComboBox = new CustomComboBox(q);
+#ifdef Q_OS_MACOS
+ QStyledItemDelegate *delegate = new QStyledItemDelegate(this);
+ m_checkStateComboBox->setItemDelegate(delegate);
+#endif
+ m_checkStateComboBox->setObjectName(QLatin1String("CheckStateComboBox"));
+ topHLayout->addWidget(m_checkStateComboBox);
+
+ connect(m_checkStateComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
+ this, &ComponentSelectionPagePrivate::updateAllCheckStates);
- m_treeViewVLayout = new QVBoxLayout;
- m_treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
+ // Workaround invisible placeholder text
+ QPalette palette = m_checkStateComboBox->palette();
+ palette.setColor(QPalette::PlaceholderText, palette.color(QPalette::Text));
+ m_checkStateComboBox->setPalette(palette);
- QHBoxLayout *buttonHLayout = new QHBoxLayout;
- m_checkDefault = new QPushButton;
- connect(m_checkDefault, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectDefault);
+ m_checkStateComboBox->setPlaceholderText(ComponentSelectionPage::tr("Select"));
if (m_core->isInstaller()) {
- m_checkDefault->setObjectName(QLatin1String("SelectDefaultComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+A",
- "select default components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("Def&ault"));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Default"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Select default components in the tree view."), Qt::ToolTipRole);
} else {
- m_checkDefault->setEnabled(false);
- m_checkDefault->setObjectName(QLatin1String("ResetComponentsButton"));
- m_checkDefault->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+R",
- "reset to already installed components")));
- m_checkDefault->setText(ComponentSelectionPage::tr("&Reset"));
+ m_checkStateComboBox->insertItem(scCheckDefaultIndex, ComponentSelectionPage::tr("Reset"));
+ m_checkStateComboBox->setItemData(scCheckDefaultIndex,
+ ComponentSelectionPage::tr("Reset all components to their original selection state in the tree view."), Qt::ToolTipRole);
}
- buttonHLayout->addWidget(m_checkDefault);
-
- m_checkAll = new QPushButton;
- connect(m_checkAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::selectAll);
- m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton"));
- m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S",
- "select all components")));
- m_checkAll->setText(ComponentSelectionPage::tr("&Select All"));
- buttonHLayout->addWidget(m_checkAll);
-
- m_uncheckAll = new QPushButton;
- connect(m_uncheckAll, &QAbstractButton::clicked,
- this, &ComponentSelectionPagePrivate::deselectAll);
- m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton"));
- m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D",
- "deselect all components")));
- m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All"));
- buttonHLayout->addWidget(m_uncheckAll);
-
- m_treeViewVLayout->addLayout(buttonHLayout);
-
- m_metadataProgressLabel = new QLabel();
- m_metadataProgressLabel->hide();
- m_treeViewVLayout->addWidget(m_metadataProgressLabel);
-
- m_progressBar = new QProgressBar();
+ m_checkStateComboBox->insertItem(scCheckAllIndex, ComponentSelectionPage::tr("Select All"));
+ m_checkStateComboBox->setItemData(scCheckAllIndex,
+ ComponentSelectionPage::tr("Select all components in the tree view."), Qt::ToolTipRole);
+ m_checkStateComboBox->insertItem(scUncheckAllIndex, ComponentSelectionPage::tr("Deselect All"));
+ m_checkStateComboBox->setItemData(scUncheckAllIndex,
+ ComponentSelectionPage::tr("Deselect all components in the tree view."), Qt::ToolTipRole);
+
+ QWidget *progressStackedWidget = new QWidget();
+ QVBoxLayout *metaLayout = new QVBoxLayout(progressStackedWidget);
+ m_metadataProgressLabel = new QLabel(progressStackedWidget);
+ m_progressBar = new QProgressBar(progressStackedWidget);
m_progressBar->setRange(0, 0);
- m_progressBar->hide();
m_progressBar->setObjectName(QLatin1String("CompressedInstallProgressBar"));
- m_treeViewVLayout->addWidget(m_progressBar);
+ metaLayout->addSpacing(20);
+ metaLayout->addWidget(m_metadataProgressLabel);
+ metaLayout->addWidget(m_progressBar);
+ metaLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
+
+ m_searchLineEdit = new QLineEdit(q);
+ m_searchLineEdit->setObjectName(QLatin1String("SearchLineEdit"));
+ m_searchLineEdit->setPlaceholderText(ComponentSelectionPage::tr("Search"));
+ m_searchLineEdit->setClearButtonEnabled(true);
+ connect(m_searchLineEdit, &QLineEdit::textChanged,
+ this, &ComponentSelectionPagePrivate::setSearchPattern);
+ connect(q, &ComponentSelectionPage::entered, m_searchLineEdit, &QLineEdit::clear);
+ topHLayout->addWidget(m_searchLineEdit);
+
+ QVBoxLayout *treeViewVLayout = new QVBoxLayout;
+ treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
+ treeViewVLayout->addWidget(m_treeView, 3);
+
+ QWidget *mainStackedWidget = new QWidget();
+ m_mainGLayout = new QGridLayout(mainStackedWidget);
+ {
+ int left = 0;
+ int top = 0;
+ int bottom = 0;
+ m_mainGLayout->getContentsMargins(&left, &top, nullptr, &bottom);
+ m_mainGLayout->setContentsMargins(left, top, 0, bottom);
+ }
+ m_mainGLayout->addLayout(topHLayout, 0, 0);
+ m_mainGLayout->addLayout(treeViewVLayout, 1, 0);
+ m_mainGLayout->addLayout(m_rightSideVLayout, 0, 1, 0, -1);
+ m_mainGLayout->setColumnStretch(0, 3);
+ m_mainGLayout->setColumnStretch(1, 2);
+
+ m_stackedLayout = new QStackedLayout(q);
+ m_stackedLayout->addWidget(mainStackedWidget);
+ m_stackedLayout->addWidget(progressStackedWidget);
+ m_stackedLayout->setCurrentIndex(0);
+
+ connect(m_allModel, &ComponentModel::checkStateChanged,
+ this, &ComponentSelectionPagePrivate::onModelStateChanged);
+ connect(m_updaterModel, &ComponentModel::checkStateChanged,
+ this, &ComponentSelectionPagePrivate::onModelStateChanged);
connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int)));
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
connect(m_core, &PackageManagerCore::metaJobTotalProgress, this,
&ComponentSelectionPagePrivate::setTotalProgress);
-
- m_treeViewVLayout->addWidget(m_treeView, 3);
-
- // force a recalculation of components to install to keep the state correct
- connect(q, &ComponentSelectionPage::left,
- m_core, &PackageManagerCore::clearComponentsToInstallCalculated);
-
- m_mainHLayout = new QHBoxLayout(q);
- m_mainHLayout->addLayout(m_treeViewVLayout, 3);
- m_mainHLayout->addLayout(m_descriptionVLayout, 2);
-
-#ifdef INSTALLCOMPRESSED
- allowCompressedRepositoryInstall();
-#endif
}
ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
@@ -165,33 +241,28 @@ ComponentSelectionPagePrivate::~ComponentSelectionPagePrivate()
}
-void ComponentSelectionPagePrivate::allowCompressedRepositoryInstall()
+void ComponentSelectionPagePrivate::setAllowCreateOfflineInstaller(bool allow)
{
- m_allowCompressedRepositoryInstall = true;
+ m_allowCreateOfflineInstaller = allow;
}
void ComponentSelectionPagePrivate::showCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && !(wizard->options() & QWizard::HaveCustomButton2) && m_allowCompressedRepositoryInstall) {
- wizard->setOption(QWizard::HaveCustomButton2, true);
- wizard->setButtonText(QWizard::CustomButton2,
- ComponentSelectionPage::tr("&Browse QBSP files"));
- connect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ if (m_core->allowCompressedRepositoryInstall())
+ m_qbspPushButton->setVisible(true);
}
void ComponentSelectionPagePrivate::hideCompressedRepositoryButton()
{
- QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
- if (wizard && (wizard->options() & QWizard::HaveCustomButton2)) {
- wizard->setOption(QWizard::HaveCustomButton2, false);
- disconnect(wizard, &QWizard::customButtonClicked,
- this, &ComponentSelectionPagePrivate::customButtonClicked);
- q->gui()->updateButtonLayout();
- }
+ m_qbspPushButton->setVisible(false);
+}
+
+void ComponentSelectionPagePrivate::showCreateOfflineInstallerButton(bool show)
+{
+ if (show && m_allowCreateOfflineInstaller)
+ m_createOfflinePushButton->setVisible(m_core->isInstaller() && !m_core->isOfflineOnly());
+ else
+ m_createOfflinePushButton->setVisible(false);
}
void ComponentSelectionPagePrivate::setupCategoryLayout()
@@ -200,13 +271,16 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
return;
m_categoryWidget = new QWidget();
QVBoxLayout *vLayout = new QVBoxLayout;
+ vLayout->setContentsMargins(0, 0, 0, 0);
m_categoryWidget->setLayout(vLayout);
m_categoryGroupBox = new QGroupBox(q);
- m_categoryGroupBox->setTitle(m_core->settings().repositoryCategoryDisplayName());
m_categoryGroupBox->setObjectName(QLatin1String("CategoryGroupBox"));
QVBoxLayout *categoryLayout = new QVBoxLayout(m_categoryGroupBox);
QPushButton *fetchCategoryButton = new QPushButton(tr("Filter"));
fetchCategoryButton->setObjectName(QLatin1String("FetchCategoryButton"));
+ fetchCategoryButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ fetchCategoryButton->setToolTip(
+ ComponentSelectionPage::tr("Filter the enabled repository categories"));
connect(fetchCategoryButton, &QPushButton::clicked, this,
&ComponentSelectionPagePrivate::fetchRepositoryCategories);
@@ -214,8 +288,6 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
QCheckBox *checkBox = new QCheckBox;
checkBox->setObjectName(repository.displayname());
checkBox->setChecked(repository.isEnabled());
- connect(checkBox, &QCheckBox::stateChanged, this,
- &ComponentSelectionPagePrivate::checkboxStateChanged);
checkBox->setText(repository.displayname());
checkBox->setToolTip(repository.tooltip());
categoryLayout->addWidget(checkBox);
@@ -224,35 +296,44 @@ void ComponentSelectionPagePrivate::setupCategoryLayout()
vLayout->addWidget(m_categoryGroupBox);
vLayout->addStretch();
- m_mainHLayout->insertWidget(0, m_categoryWidget);
+ m_tabWidget->insertTab(1, m_categoryWidget, m_core->settings().repositoryCategoryDisplayName());
}
void ComponentSelectionPagePrivate::showCategoryLayout(bool show)
{
+ if (!show && !m_categoryWidget)
+ return;
+
+ if (show == m_categoryLayoutVisible)
+ return;
+
+ setupCategoryLayout();
if (show) {
- setupCategoryLayout();
+ m_rightSideVLayout->removeWidget(m_descriptionBaseWidget);
+ m_tabWidget->insertTab(0, m_descriptionBaseWidget, tr("Information"));
+ m_rightSideVLayout->insertWidget(0, m_tabWidget);
+ } else {
+ m_tabWidget->removeTab(0);
+ m_rightSideVLayout->removeWidget(m_tabWidget);
+ m_rightSideVLayout->insertWidget(0, m_descriptionBaseWidget);
+ m_descriptionBaseWidget->setVisible(true);
}
- if (m_categoryWidget)
- m_categoryWidget->setVisible(show);
+ m_tabWidget->setVisible(show);
+ m_categoryLayoutVisible = show;
}
void ComponentSelectionPagePrivate::updateTreeView()
{
- m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager());
+ setComboBoxItemEnabled(scCheckDefaultIndex, m_core->isInstaller() || m_core->isPackageManager());
if (m_treeView->selectionModel()) {
disconnect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
}
m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel;
- m_treeView->setModel(m_currentModel);
- m_treeView->setExpanded(m_currentModel->index(0, 0), true);
- foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::All)) {
- if (component->isExpandedByDefault()) {
- const QModelIndex index = m_currentModel->indexFromComponentName(component->name());
- m_treeView->setExpanded(index, true);
- }
- }
+ m_proxyModel->setSourceModel(m_currentModel);
+ m_treeView->setModel(m_proxyModel);
+ expandDefault();
const bool installActionColumnVisible = m_core->settings().installActionColumnVisible();
if (!installActionColumnVisible)
@@ -293,7 +374,57 @@ void ComponentSelectionPagePrivate::updateTreeView()
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
- m_treeView->setCurrentIndex(m_currentModel->index(0, 0));
+ m_treeView->setCurrentIndex(m_proxyModel->index(0, 0));
+}
+
+/*!
+ Expands components that should be expanded by default.
+*/
+void ComponentSelectionPagePrivate::expandDefault()
+{
+ m_treeView->setExpanded(m_proxyModel->index(0, 0), true);
+ foreach (auto *component, m_core->components(PackageManagerCore::ComponentType::All)) {
+ if (component->isExpandedByDefault()) {
+ const QModelIndex index = m_proxyModel->mapFromSource(
+ m_currentModel->indexFromComponentName(component->treeName()));
+ m_treeView->setExpanded(index, true);
+ }
+ }
+}
+
+/*!
+ Expands components that were accepted by proxy models filter.
+*/
+void ComponentSelectionPagePrivate::expandSearchResults()
+{
+ // Avoid resizing the sections after each expand of a node
+ storeHeaderResizeModes();
+
+ // Expand parents of root indexes accepted by filter
+ const QVector<QModelIndex> acceptedIndexes = m_proxyModel->directlyAcceptedIndexes();
+ for (auto proxyModelIndex : acceptedIndexes) {
+ if (!proxyModelIndex.isValid())
+ continue;
+
+ QModelIndex index = proxyModelIndex.parent();
+ while (index.isValid()) {
+ if (m_treeView->isExpanded(index))
+ break; // Multiple direct matches in a branch, can be skipped
+
+ m_treeView->expand(index);
+ index = index.parent();
+ }
+ }
+ restoreHeaderResizeModes();
+}
+
+/*!
+ Returns \c true if the components to install and uninstall are calculated
+ successfully, \c false otherwise.
+*/
+bool ComponentSelectionPagePrivate::componentsResolved() const
+{
+ return m_componentsResolved;
}
void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &current)
@@ -303,16 +434,12 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu
m_sizeLabel->setText(QString());
- QString description = m_currentModel->data(m_currentModel->index(current.row(),
+ QString description = m_proxyModel->data(m_proxyModel->index(current.row(),
ComponentModelHelper::NameColumn, current.parent()), Qt::ToolTipRole).toString();
- // replace {external-link}='' fields in component description with proper link tags
- description.replace(QRegularExpression(QLatin1String("{external-link}='(.*?)'")),
- QLatin1String("<a href=\"\\1\"><span style=\"color:#17a81a;\">\\1</span></a>"));
-
m_descriptionLabel->setText(description);
- Component *component = m_currentModel->componentFromIndex(current);
+ Component *component = m_currentModel->componentFromIndex(m_proxyModel->mapToSource(current));
if ((m_core->isUninstaller()) || (!component))
return;
@@ -323,6 +450,32 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu
}
}
+/*!
+ Updates the checkstate of the components based on the value of \c which.
+*/
+void ComponentSelectionPagePrivate::updateAllCheckStates(int which)
+{
+ switch (which) {
+ case scNoCheckSelectionIndex:
+ // A 'helper' text index, no selection
+ return;
+ case scCheckDefaultIndex:
+ selectDefault();
+ break;
+ case scCheckAllIndex:
+ selectAll();
+ break;
+ case scUncheckAllIndex:
+ deselectAll();
+ break;
+ default:
+ qCWarning(lcInstallerInstallLog) << "Invalid index for check state selection!";
+ break;
+ }
+ // Reset back to 'helper' text index
+ m_checkStateComboBox->setCurrentIndex(scNoCheckSelectionIndex);
+}
+
void ComponentSelectionPagePrivate::selectAll()
{
m_currentModel->setCheckedState(ComponentModel::AllChecked);
@@ -333,81 +486,34 @@ void ComponentSelectionPagePrivate::deselectAll()
m_currentModel->setCheckedState(ComponentModel::AllUnchecked);
}
-void ComponentSelectionPagePrivate::checkboxStateChanged()
-{
- QList<QCheckBox*> checkboxes = m_categoryGroupBox->findChildren<QCheckBox *>();
- bool enableFetchButton = false;
- foreach (QCheckBox *checkbox, checkboxes) {
- if (checkbox->isChecked()) {
- enableFetchButton = true;
- break;
- }
- }
-}
-
-void ComponentSelectionPagePrivate::enableRepositoryCategory(const QString &repositoryName, bool enable)
+void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
{
- QMap<QString, RepositoryCategory> organizedRepositoryCategories = m_core->settings().organizedRepositoryCategories();
+ if (show)
+ m_stackedLayout->setCurrentIndex(1);
+ else
+ m_stackedLayout->setCurrentIndex(0);
- QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName);
- RepositoryCategory repoCategory;
- while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) {
- repoCategory = i.value();
- i++;
- }
+ m_qbspPushButton->setEnabled(!show);
- RepositoryCategory replacement = repoCategory;
- replacement.setEnabled(enable);
- QSet<RepositoryCategory> tmpRepoCategories = m_core->settings().repositoryCategories();
- if (tmpRepoCategories.contains(repoCategory)) {
- tmpRepoCategories.remove(repoCategory);
- tmpRepoCategories.insert(replacement);
- m_core->settings().addRepositoryCategories(tmpRepoCategories);
- }
-}
-
-void ComponentSelectionPagePrivate::updateWidgetVisibility(bool show)
-{
if (show) {
- QSpacerItem *verticalSpacer2 = new QSpacerItem(0, 0, QSizePolicy::Minimum,
- QSizePolicy::Expanding);
- m_treeViewVLayout->addSpacerItem(verticalSpacer2);
- m_mainHLayout->removeItem(m_descriptionVLayout);
- //Hide next button during category fetch
- QPushButton *const b = qobject_cast<QPushButton *>(q->gui()->button(QWizard::NextButton));
- b->setEnabled(!show);
- } else {
- QSpacerItem *item = m_treeViewVLayout->spacerItem();
- m_treeViewVLayout->removeItem(item);
- m_mainHLayout->addLayout(m_descriptionVLayout, 2);
- //Call completeChanged() to determine if NextButton should be shown or not after category fetch.
- q->completeChanged();
+ q->gui()->button(QWizard::NextButton)->setEnabled(false);
+ q->gui()->button(QWizard::BackButton)->setEnabled(false);
}
- if (m_categoryWidget)
- m_categoryWidget->setDisabled(show);
- m_progressBar->setVisible(show);
- m_metadataProgressLabel->setVisible(show);
-
- m_treeView->setVisible(!show);
- m_checkDefault->setVisible(!show);
- m_checkAll->setVisible(!show);
- m_uncheckAll->setVisible(!show);
- m_descriptionLabel->setVisible(!show);
- m_sizeLabel->setVisible(!show);
-
- if (QAbstractButton *bspButton = q->gui()->button(QWizard::CustomButton2))
- bspButton->setEnabled(!show);
+
+ // In macOS 10.12 the widgets are not hidden if those are not updated immediately
+#ifdef Q_OS_MACOS
+ q->repaint();
+#endif
}
void ComponentSelectionPagePrivate::fetchRepositoryCategories()
{
updateWidgetVisibility(true);
- QCheckBox *checkbox;
QList<QCheckBox*> checkboxes = m_categoryGroupBox->findChildren<QCheckBox *>();
for (int i = 0; i < checkboxes.count(); i++) {
- checkbox = checkboxes.at(i);
- enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
+ QCheckBox *checkbox = checkboxes.at(i);
+ m_core->enableRepositoryCategory(checkbox->objectName(), checkbox->isChecked());
}
if (!m_core->fetchRemotePackagesTree()) {
@@ -415,33 +521,31 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
}
updateWidgetVisibility(false);
+ m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults();
}
-void ComponentSelectionPagePrivate::customButtonClicked(int which)
+void ComponentSelectionPagePrivate::createOfflineButtonClicked()
{
- if (QWizard::WizardButton(which) == QWizard::CustomButton2) {
- QString defaultDownloadDirectory =
- QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
- QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
- ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
- QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
-
- QSet<Repository> set;
- foreach (QString fileName, fileNames) {
- Repository repository = Repository::fromUserInput(fileName, true);
- repository.setEnabled(true);
- set.insert(repository);
- }
- if (set.count() > 0) {
- updateWidgetVisibility(true);
- m_core->settings().addTemporaryRepositories(set, false);
- if (!m_core->fetchCompressedPackagesTree()) {
- MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
- QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
- }
+ m_core->setOfflineGenerator();
+ q->gui()->button(QWizard::NextButton)->click();
+}
+
+void ComponentSelectionPagePrivate::qbspButtonClicked()
+{
+ QString defaultDownloadDirectory =
+ QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ QStringList fileNames = QFileDialog::getOpenFileNames(nullptr,
+ ComponentSelectionPage::tr("Open File"),defaultDownloadDirectory,
+ QLatin1String("QBSP or 7z Files (*.qbsp *.7z)"));
+
+ if (m_core->addQBspRepositories(fileNames)) {
+ updateWidgetVisibility(true);
+ if (!m_core->fetchCompressedPackagesTree()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
}
- updateWidgetVisibility(false);
}
+ updateWidgetVisibility(false);
}
/*!
@@ -475,6 +579,21 @@ void ComponentSelectionPagePrivate::selectDefault()
void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentModel::ModelState state)
{
+ if (state.testFlag(ComponentModel::Empty)) {
+ setComboBoxItemEnabled(scCheckAllIndex, false);
+ setComboBoxItemEnabled(scUncheckAllIndex, false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, false);
+ return;
+ }
+
+ m_componentsResolved = m_core->recalculateAllComponents();
+ if (!m_componentsResolved) {
+ const QString error = !m_core->componentsToInstallError().isEmpty()
+ ? m_core->componentsToInstallError() : m_core->componentsToUninstallError();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("CalculateComponentsError"), tr("Error"), error);
+ }
+
q->setModified(state.testFlag(ComponentModel::DefaultChecked) == false);
// If all components in the checked list are only checkable when run without forced
// installation, set ComponentModel::AllUnchecked as well, as we cannot uncheck anything.
@@ -484,13 +603,76 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod
state |= ComponentModel::AllUnchecked;
}
// enable the button if the corresponding flag is not set
- m_checkAll->setEnabled(state.testFlag(ComponentModel::AllChecked) == false);
- m_uncheckAll->setEnabled(state.testFlag(ComponentModel::AllUnchecked) == false);
- m_checkDefault->setEnabled(state.testFlag(ComponentModel::DefaultChecked) == false);
+ setComboBoxItemEnabled(scCheckAllIndex, state.testFlag(ComponentModel::AllChecked) == false);
+ setComboBoxItemEnabled(scUncheckAllIndex, state.testFlag(ComponentModel::AllUnchecked) == false);
+ setComboBoxItemEnabled(scCheckDefaultIndex, state.testFlag(ComponentModel::DefaultChecked) == false);
// update the current selected node (important to reflect possible sub-node changes)
if (m_treeView->selectionModel())
currentSelectedChanged(m_treeView->selectionModel()->currentIndex());
}
+/*!
+ Sets the new filter pattern to \a text and expands the tree nodes.
+*/
+void ComponentSelectionPagePrivate::setSearchPattern(const QString &text)
+{
+ m_proxyModel->setFilterWildcard(text);
+
+ m_treeView->collapseAll();
+ if (text.isEmpty()) {
+ // Expand user selection and default expanded, ensure selected is visible
+ QModelIndex index = m_treeView->selectionModel()->currentIndex();
+ while (index.isValid()) {
+ m_treeView->expand(index);
+ index = index.parent();
+ }
+ expandDefault();
+ m_treeView->scrollTo(m_treeView->selectionModel()->currentIndex());
+ } else {
+ expandSearchResults();
+ }
+}
+
+/*!
+ Stores the current resize modes of the tree view header's columns, and sets
+ the new resize modes to \c QHeaderView::Fixed.
+*/
+void ComponentSelectionPagePrivate::storeHeaderResizeModes()
+{
+ m_headerStretchLastSection = m_treeView->header()->stretchLastSection();
+ for (int i = 0; i < ComponentModelHelper::LastColumn; ++i)
+ m_headerResizeModes.insert(i, m_treeView->header()->sectionResizeMode(i));
+
+ m_treeView->header()->setStretchLastSection(false);
+ m_treeView->header()->setSectionResizeMode(QHeaderView::Fixed);
+}
+
+/*!
+ Restores the resize modes of the tree view header's columns, that were
+ stored when calling \l storeHeaderResizeModes().
+*/
+void ComponentSelectionPagePrivate::restoreHeaderResizeModes()
+{
+ m_treeView->header()->setStretchLastSection(m_headerStretchLastSection);
+ for (int i = 0; i < ComponentModelHelper::LastColumn; ++i)
+ m_treeView->header()->setSectionResizeMode(i, m_headerResizeModes.value(i));
+}
+
+/*!
+ Sets the enabled state of the combo box item in \a index to \a enabled.
+*/
+void ComponentSelectionPagePrivate::setComboBoxItemEnabled(int index, bool enabled)
+{
+ auto *model = qobject_cast<QStandardItemModel *>(m_checkStateComboBox->model());
+ if (!model)
+ return;
+
+ QStandardItem *item = model->item(index);
+ if (!item)
+ return;
+
+ item->setEnabled(enabled);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index 6169a06f5..187fce61d 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,9 +31,11 @@
#include <QObject>
#include <QWidget>
+#include <QHeaderView>
#include "componentmodel.h"
#include "packagemanagergui.h"
+#include "componentsortfilterproxymodel.h"
class QTreeView;
class QLabel;
@@ -44,12 +46,15 @@ class QListWidgetItem;
class QProgressBar;
class QVBoxLayout;
class QHBoxLayout;
+class QGridLayout;
+class QStackedLayout;
namespace QInstaller {
class PackageManagerCore;
class ComponentModel;
class ComponentSelectionPage;
+class CustomComboBox;
class ComponentSelectionPagePrivate : public QObject
{
@@ -61,49 +66,67 @@ public:
explicit ComponentSelectionPagePrivate(ComponentSelectionPage *qq, PackageManagerCore *core);
~ComponentSelectionPagePrivate();
- void allowCompressedRepositoryInstall();
+ void setAllowCreateOfflineInstaller(bool allow);
void showCompressedRepositoryButton();
void hideCompressedRepositoryButton();
+ void showCreateOfflineInstallerButton(bool show);
void setupCategoryLayout();
void showCategoryLayout(bool show);
void updateTreeView();
+ void expandDefault();
+ void expandSearchResults();
+ bool componentsResolved() const;
public slots:
void currentSelectedChanged(const QModelIndex &current);
+ void updateAllCheckStates(int which);
void selectAll();
void deselectAll();
- void checkboxStateChanged();
- void enableRepositoryCategory(const QString &repositoryName, bool enable);
void updateWidgetVisibility(bool show);
void fetchRepositoryCategories();
- void customButtonClicked(int which);
+ void createOfflineButtonClicked();
+ void qbspButtonClicked();
void onProgressChanged(int progress);
void setMessage(const QString &msg);
void setTotalProgress(int totalProgress);
void selectDefault();
void onModelStateChanged(QInstaller::ComponentModel::ModelState state);
+ void setSearchPattern(const QString &text);
+
+private:
+ void storeHeaderResizeModes();
+ void restoreHeaderResizeModes();
+ void setComboBoxItemEnabled(int index, bool enabled);
private:
ComponentSelectionPage *q;
PackageManagerCore *m_core;
QTreeView *m_treeView;
+ QTabWidget *m_tabWidget;
+ QWidget *m_descriptionBaseWidget;
QLabel *m_sizeLabel;
- QScrollArea *m_descriptionScrollArea;
QLabel *m_descriptionLabel;
- QVBoxLayout *m_descriptionVLayout;
- QPushButton *m_checkAll;
- QPushButton *m_uncheckAll;
- QPushButton *m_checkDefault;
+ QPushButton *m_createOfflinePushButton;
+ QPushButton *m_qbspPushButton;
+ CustomComboBox *m_checkStateComboBox;
QWidget *m_categoryWidget;
QGroupBox *m_categoryGroupBox;
QLabel *m_metadataProgressLabel;
QProgressBar *m_progressBar;
- QHBoxLayout *m_mainHLayout;
- QVBoxLayout *m_treeViewVLayout;
- bool m_allowCompressedRepositoryInstall;
+ QGridLayout *m_mainGLayout;
+ QVBoxLayout *m_rightSideVLayout;
+ bool m_allowCreateOfflineInstaller;
+ bool m_categoryLayoutVisible;
ComponentModel *m_allModel;
ComponentModel *m_updaterModel;
ComponentModel *m_currentModel;
+ QStackedLayout *m_stackedLayout;
+ ComponentSortFilterProxyModel *m_proxyModel;
+ QLineEdit *m_searchLineEdit;
+ bool m_componentsResolved;
+
+ bool m_headerStretchLastSection;
+ QHash<int, QHeaderView::ResizeMode> m_headerResizeModes;
};
} // namespace QInstaller
diff --git a/src/libs/installer/componentsortfilterproxymodel.cpp b/src/libs/installer/componentsortfilterproxymodel.cpp
new file mode 100644
index 000000000..9308d2356
--- /dev/null
+++ b/src/libs/installer/componentsortfilterproxymodel.cpp
@@ -0,0 +1,152 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "componentsortfilterproxymodel.h"
+
+namespace QInstaller {
+
+/*!
+ \class QInstaller::ComponentSortFilterProxyModel
+ \inmodule QtInstallerFramework
+ \brief The ComponentSortFilterProxyModel provides support for sorting and
+ filtering data passed between another model and a view.
+
+ The class subclasses QSortFilterProxyModel. Compared to the base class,
+ filters affect also child indexes in the base model, meaning if a
+ certain row has a parent that is accepted by filter, it is also accepted.
+ A distinction is made betweed directly and indirectly accepted indexes.
+*/
+
+/*!
+ \enum ComponentSortFilterProxyModel::AcceptType
+
+ This enum holds the possible values for filter acception type for model indexes.
+
+ \value Direct
+ Index was accepted directly by filter.
+ \value Descendant
+ Index is a descendant of an accepted index.
+ \value Rejected
+ Index was not accepted by filter.
+*/
+
+/*!
+ Constructs object with \a parent.
+*/
+ComponentSortFilterProxyModel::ComponentSortFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+}
+
+/*!
+ Returns a list of source model indexes that were accepted directly by the filter.
+*/
+QVector<QModelIndex> ComponentSortFilterProxyModel::directlyAcceptedIndexes() const
+{
+ QVector<QModelIndex> indexes;
+ for (int i = 0; i < rowCount(); i++) {
+ QModelIndex childIndex = index(i, 0, QModelIndex());
+ findDirectlyAcceptedIndexes(childIndex, indexes);
+ }
+ return indexes;
+}
+
+/*!
+ Returns \c true if the item in the row indicated by the given \a sourceRow and
+ \a sourceParent should be included in the model; otherwise returns \c false.
+*/
+bool ComponentSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ return acceptsRow(sourceRow, sourceParent);
+}
+
+/*!
+ Returns \c true if the item in the row indicated by the given \a sourceRow and
+ \a sourceParent should be included in the model; otherwise returns \c false. The
+ acception type can be retrieved with \a type.
+*/
+bool ComponentSortFilterProxyModel::acceptsRow(int sourceRow, const QModelIndex &sourceParent,
+ AcceptType *type) const
+{
+ if (type)
+ *type = AcceptType::Rejected;
+
+ if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
+ if (type)
+ *type = AcceptType::Direct;
+ return true;
+ }
+
+ if (!isRecursiveFilteringEnabled()) // filter not applied for children
+ return false;
+
+ if (!sourceParent.isValid()) // already root
+ return false;
+
+ int currentRow = sourceParent.row();
+ QModelIndex currentParent = sourceParent.parent();
+ // Ascend and check if any parent is accepted
+ forever {
+ if (QSortFilterProxyModel::filterAcceptsRow(currentRow, currentParent)) {
+ if (type)
+ *type = AcceptType::Descendant;
+ return true;
+ }
+ if (!currentParent.isValid()) // we hit a root node
+ break;
+
+ currentRow = currentParent.row();
+ currentParent = currentParent.parent();
+ }
+ return false;
+}
+
+/*!
+ Finds directly accepted child \a indexes for parent index \a in. Returns \c true
+ if at least one accepted index was found, \c false otherwise.
+*/
+bool ComponentSortFilterProxyModel::findDirectlyAcceptedIndexes(const QModelIndex &in, QVector<QModelIndex> &indexes) const
+{
+ bool found = false;
+ for (int i = 0; i < rowCount(in); i++) {
+ if (findDirectlyAcceptedIndexes(index(i, 0, in), indexes))
+ found = true;
+ }
+ if (!hasChildren(in) || !found) { // No need to check current if any child matched
+ AcceptType acceptType;
+ const QModelIndex sourceIndex = mapToSource(in);
+ acceptsRow(sourceIndex.row(), sourceIndex.parent(), &acceptType);
+ if (acceptType == AcceptType::Direct) {
+ indexes.append(in);
+ found = true;
+ }
+ }
+ return found;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/componentsortfilterproxymodel.h b/src/libs/installer/componentsortfilterproxymodel.h
new file mode 100644
index 000000000..12fd0a627
--- /dev/null
+++ b/src/libs/installer/componentsortfilterproxymodel.h
@@ -0,0 +1,63 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMPONENTSORTFILTERPROXYMODEL_H
+#define COMPONENTSORTFILTERPROXYMODEL_H
+
+#include "installer_global.h"
+
+#include <QSortFilterProxyModel>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ComponentSortFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ enum AcceptType {
+ Direct,
+ Descendant,
+ Rejected
+ };
+
+ explicit ComponentSortFilterProxyModel(QObject *parent = nullptr);
+
+ QVector<QModelIndex> directlyAcceptedIndexes() const;
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+
+private:
+ bool acceptsRow(int sourceRow, const QModelIndex &sourceParent, AcceptType *type = nullptr) const;
+ bool findDirectlyAcceptedIndexes(const QModelIndex &in, QVector<QModelIndex> &indexes) const;
+};
+
+} // namespace QInstaller
+
+#endif // COMPONENTSORTFILTERPROXYMODEL_H
diff --git a/src/libs/installer/concurrentoperationrunner.cpp b/src/libs/installer/concurrentoperationrunner.cpp
new file mode 100644
index 000000000..b0eb5f582
--- /dev/null
+++ b/src/libs/installer/concurrentoperationrunner.cpp
@@ -0,0 +1,279 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "concurrentoperationrunner.h"
+
+#include "errors.h"
+#include "operationtracer.h"
+
+#include <QtConcurrent>
+
+using namespace QInstaller;
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ConcurrentOperationRunner
+ \brief The ConcurrentOperationRunner class can be used to perform installer
+ operations concurrently.
+
+ The class accepts an operation list of any registered operation type. It can be
+ used to execute the \c Backup, \c Perform, or \c Undo steps of the operations. The
+ operations are run in a separate thread pool of this class, which by default limits
+ the maximum number of threads to the ideal number of logical processor cores in the
+ system.
+*/
+
+/*!
+ \fn QInstaller::ConcurrentOperationRunner::operationStarted(QInstaller::Operation *operation)
+
+ Emitted when the execution of \a operation is started.
+*/
+
+/*!
+ \fn QInstaller::ConcurrentOperationRunner::finished()
+
+ Emitted when the execution of all pooled operations is finished.
+*/
+
+/*!
+ \fn QInstaller::ConcurrentOperationRunner::progressChanged(const int completed, const int total)
+
+ Emitted when the count of \a completed of the \a total operations changes.
+*/
+
+/*!
+ Constructs an operation runner with \a parent as the parent object.
+*/
+ConcurrentOperationRunner::ConcurrentOperationRunner(QObject *parent)
+ : QObject(parent)
+ , m_completedOperations(0)
+ , m_totalOperations(0)
+ , m_operations(nullptr)
+ , m_type(Operation::OperationType::Perform)
+ , m_threadPool(new QThreadPool(this))
+{
+ connect(this, &ConcurrentOperationRunner::operationStarted,
+ this, &ConcurrentOperationRunner::onOperationStarted);
+}
+
+/*!
+ Constructs an operation runner with \a operations of type \a type to be performed,
+ and \a parent as the parent object.
+*/
+ConcurrentOperationRunner::ConcurrentOperationRunner(OperationList *operations,
+ const Operation::OperationType type, QObject *parent)
+ : QObject(parent)
+ , m_completedOperations(0)
+ , m_totalOperations(0)
+ , m_operations(operations)
+ , m_type(type)
+ , m_threadPool(new QThreadPool(this))
+{
+ m_totalOperations = m_operations->size();
+
+ connect(this, &ConcurrentOperationRunner::operationStarted,
+ this, &ConcurrentOperationRunner::onOperationStarted);
+}
+
+/*!
+ Destroys the instance and releases resources.
+*/
+ConcurrentOperationRunner::~ConcurrentOperationRunner()
+{
+ qDeleteAll(m_operationWatchers);
+}
+
+/*!
+ Sets the list of operations to be performed to \a operations.
+*/
+void ConcurrentOperationRunner::setOperations(OperationList *operations)
+{
+ m_operations = operations;
+ m_totalOperations = m_operations->size();
+}
+
+/*!
+ Sets \a type of operations to be performed. This can be either
+ \c Backup, \c Perform, or \c Undo.
+*/
+void ConcurrentOperationRunner::setType(const Operation::OperationType type)
+{
+ m_type = type;
+}
+
+/*!
+ Sets the maximum \a count of threads used by the thread pool of this class.
+ A value of \c 0 sets the count automatically to ideal number of threads.
+*/
+void ConcurrentOperationRunner::setMaxThreadCount(int count)
+{
+ if (count == 0) {
+ m_threadPool->setMaxThreadCount(QThread::idealThreadCount());
+ return;
+ }
+ m_threadPool->setMaxThreadCount(count);
+}
+
+/*!
+ Performs the current operations. Returns a hash of pointers to the performed operation
+ objects and their results. The result is a boolean value.
+*/
+QHash<Operation *, bool> ConcurrentOperationRunner::run()
+{
+ reset();
+
+ QEventLoop loop;
+ for (auto &operation : qAsConst(*m_operations)) {
+ auto futureWatcher = new QFutureWatcher<bool>();
+ m_operationWatchers.insert(operation, futureWatcher);
+
+ connect(futureWatcher, &QFutureWatcher<bool>::finished,
+ this, &ConcurrentOperationRunner::onOperationfinished);
+
+ futureWatcher->setFuture(QtConcurrent::run(m_threadPool,
+ [this, operation] { return runOperation(operation); }));
+ }
+
+ if (!m_operationWatchers.isEmpty()) {
+ connect(this, &ConcurrentOperationRunner::finished, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+
+ return m_results;
+}
+
+/*!
+ Cancels operations pending for an asynchronous run.
+
+ \note This does not stop already running operations, which
+ should provide a separate mechanism for canceling.
+*/
+void ConcurrentOperationRunner::cancel()
+{
+ for (auto &watcher : m_operationWatchers)
+ watcher->cancel();
+}
+
+/*!
+ \internal
+
+ Invoked when the execution of the \a operation has started. Adds console
+ output trace for the operation.
+*/
+void ConcurrentOperationRunner::onOperationStarted(Operation *operation)
+{
+ ConcurrentOperationTracer tracer(operation);
+
+ switch (m_type) {
+ case Operation::Backup:
+ tracer.trace(QLatin1String("backup"));
+ break;
+ case Operation::Perform:
+ tracer.trace(QLatin1String("perform"));
+ break;
+ case Operation::Undo:
+ tracer.trace(QLatin1String("undo"));
+ break;
+ default:
+ Q_ASSERT(!"Unexpected operation type");
+ }
+}
+
+/*!
+ \internal
+
+ Invoked when the execution of a single operation finishes. Adds the result
+ of the operation to the return hash of \c ConcurrentOperationRunner::run().
+*/
+void ConcurrentOperationRunner::onOperationfinished()
+{
+ auto watcher = static_cast<QFutureWatcher<bool> *>(sender());
+
+ Operation *op = m_operationWatchers.key(watcher);
+ if (!watcher->isCanceled()) {
+ try {
+ // Catch transferred exceptions
+ m_results.insert(op, watcher->future().result());
+ } catch (const Error &e) {
+ qCritical() << "Caught exception:" << e.message();
+ m_results.insert(op, false);
+ } catch (const QUnhandledException &) {
+ qCritical() << "Caught unhandled exception in:" << Q_FUNC_INFO;
+ m_results.insert(op, false);
+ }
+ ++m_completedOperations;
+ emit progressChanged(m_completedOperations, m_totalOperations);
+
+ } else {
+ // Remember also operations canceled before execution
+ m_results.insert(op, false);
+ }
+
+ delete m_operationWatchers.take(op);
+
+ // All finished
+ if (m_operationWatchers.isEmpty())
+ emit finished();
+}
+
+/*!
+ \internal
+
+ Runs \a operation. Returns \c true on success, \c false otherwise.
+*/
+bool ConcurrentOperationRunner::runOperation(Operation *const operation)
+{
+ emit operationStarted(operation);
+
+ switch (m_type) {
+ case Operation::Backup:
+ operation->backup();
+ return true;
+ case Operation::Perform:
+ return operation->performOperation();
+ case Operation::Undo:
+ return operation->undoOperation();
+ default:
+ Q_ASSERT(!"Unexpected operation type");
+ }
+ return false;
+}
+
+/*!
+ \internal
+
+ Clears previous results and deletes remaining operation watchers.
+*/
+void ConcurrentOperationRunner::reset()
+{
+ qDeleteAll(m_operationWatchers);
+ m_operationWatchers.clear();
+ m_results.clear();
+
+ m_completedOperations = 0;
+}
diff --git a/src/libs/installer/concurrentoperationrunner.h b/src/libs/installer/concurrentoperationrunner.h
new file mode 100644
index 000000000..436a2baef
--- /dev/null
+++ b/src/libs/installer/concurrentoperationrunner.h
@@ -0,0 +1,88 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef CONCURRENTOPERATIONRUNNER_H
+#define CONCURRENTOPERATIONRUNNER_H
+
+#include "qinstallerglobal.h"
+
+#include <QObject>
+#include <QHash>
+#include <QFutureWatcher>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ConcurrentOperationRunner : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ConcurrentOperationRunner)
+
+public:
+ explicit ConcurrentOperationRunner(QObject *parent = nullptr);
+ explicit ConcurrentOperationRunner(OperationList *operations,
+ const Operation::OperationType type, QObject *parent = nullptr);
+ ~ConcurrentOperationRunner();
+
+ void setOperations(OperationList *operations);
+ void setType(const Operation::OperationType type);
+ void setMaxThreadCount(int count);
+
+ QHash<Operation *, bool> run();
+
+signals:
+ void operationStarted(QInstaller::Operation *operation);
+ void progressChanged(const int completed, const int total);
+ void finished();
+
+public slots:
+ void cancel();
+
+private slots:
+ void onOperationStarted(QInstaller::Operation *operation);
+ void onOperationfinished();
+
+private:
+ bool runOperation(Operation *const operation);
+ void reset();
+
+private:
+ int m_completedOperations;
+ int m_totalOperations;
+
+ QHash<Operation *, QFutureWatcher<bool> *> m_operationWatchers;
+ QHash<Operation *, bool> m_results;
+
+ OperationList *m_operations;
+ Operation::OperationType m_type;
+
+ QThreadPool *const m_threadPool;
+};
+
+} // namespace QInstaller
+
+#endif // CONCURRENTOPERATIONRUNNER_H
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 26c2a7dfe..7bf816b5f 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#define CONSTANTS_H
#include <QtCore/QString>
+#include <QtCore/QStringList>
namespace QInstaller {
@@ -37,6 +38,10 @@ namespace QInstaller {
static const QLatin1String scTrue("true");
static const QLatin1String scFalse("false");
static const QLatin1String scScript("script");
+static const QLatin1String scAllUsersStartMenuProgramsPath("AllUsersStartMenuProgramsPath");
+static const QLatin1String scUserStartMenuProgramsPath("UserStartMenuProgramsPath");
+static const QLatin1String scUILanguage("UILanguage");
+static const QLatin1String scUpdatesXML("Updates.xml");
static const QLatin1String scName("Name");
static const QLatin1String scVersion("Version");
@@ -47,11 +52,20 @@ static const QLatin1String scInheritVersion("inheritVersionFrom");
static const QLatin1String scReplaces("Replaces");
static const QLatin1String scDownloadableArchives("DownloadableArchives");
static const QLatin1String scEssential("Essential");
+static const QLatin1String scForcedUpdate("ForcedUpdate");
static const QLatin1String scTargetDir("TargetDir");
static const QLatin1String scReleaseDate("ReleaseDate");
static const QLatin1String scDescription("Description");
static const QLatin1String scDisplayName("DisplayName");
+static const QLatin1String scTreeName("TreeName");
+static const QLatin1String scAutoTreeName("AutoTreeName");
static const QLatin1String scDependencies("Dependencies");
+static const QLatin1String scAlias("Alias");
+static const QLatin1String scRequiredAliases("RequiredAliases");
+static const QLatin1String scRequiredComponents("RequiredComponents");
+static const QLatin1String scOptionalAliases("OptionalAliases");
+static const QLatin1String scOptionalComponents("OptionalComponents");
+static const QLatin1String scLocalDependencies("LocalDependencies");
static const QLatin1String scAutoDependOn("AutoDependOn");
static const QLatin1String scNewComponent("NewComponent");
static const QLatin1String scRepositories("Repositories");
@@ -60,12 +74,70 @@ static const QLatin1String scInstalledVersion("InstalledVersion");
static const QLatin1String scUncompressedSize("UncompressedSize");
static const QLatin1String scUncompressedSizeSum("UncompressedSizeSum");
static const QLatin1String scRequiresAdminRights("RequiresAdminRights");
+static const QLatin1String scOfflineBinaryName("OfflineBinaryName");
static const QLatin1String scSHA1("SHA1");
+static const QLatin1String scMetadataName("MetadataName");
+static const QLatin1String scContentSha1("ContentSha1");
+static const QLatin1String scCheckSha1CheckSum("CheckSha1CheckSum");
+
+static const char * const scClearCacheHint = QT_TR_NOOP(
+ "This may be solved by restarting the application after clearing the cache from:");
+
+// symbols
+static const QLatin1String scCaretSymbol("^");
+static const QLatin1String scCommaWithSpace(", ");
+static const QLatin1String scBr("<br>");
// constants used throughout the components class
static const QLatin1String scVirtual("Virtual");
static const QLatin1String scSortingPriority("SortingPriority");
static const QLatin1String scCheckable("Checkable");
+static const QLatin1String scScriptTag("Script");
+static const QLatin1String scInstalled("Installed");
+static const QLatin1String scUpdateText("UpdateText");
+static const QLatin1String scUninstalled("Uninstalled");
+static const QLatin1String scCurrentState("CurrentState");
+static const QLatin1String scForcedInstallation("ForcedInstallation");
+static const QLatin1String scExpandedByDefault("ExpandedByDefault");
+static const QLatin1String scUnstable("Unstable");
+static const QLatin1String scTargetDirPlaceholder("@TargetDir@");
+static const QLatin1String scTargetDirPlaceholderWithArg("@TargetDir@%1");
+static const QLatin1String scLastUpdateDate("LastUpdateDate");
+static const QLatin1String scInstallDate("InstallDate");
+static const QLatin1String scUserInterfaces("UserInterfaces");
+static const QLatin1String scTranslations("Translations");
+static const QLatin1String scLicenses("Licenses");
+static const QLatin1String scLicensesValue("licenses");
+static const QLatin1String scLicense("License");
+static const QLatin1String scOperations("Operations");
+static const QLatin1String scInstallScript("installScript");
+static const QLatin1String scPostLoadScript("postLoadScript");
+static const QLatin1String scComponent("Component");
+static const QLatin1String scComponentSmall("component");
+static const QLatin1String scRetranslateUi("retranslateUi");
+static const QLatin1String scEn("en");
+static const QLatin1String scIfw_("ifw_");
+static const QLatin1String scFile("file");
+static const QLatin1String scContent("content");
+static const QLatin1String scExtract("Extract");
+static const QLatin1String scSha1("sha1");
+static const QLatin1String scCreateOperationsForPath("createOperationsForPath");
+static const QLatin1String scCreateOperationsForArchive("createOperationsForArchive");
+static const QLatin1String scCreateOperations("createOperations");
+static const QLatin1String scBeginInstallation("beginInstallation");
+static const QLatin1String scMinimumProgress("MinimumProgress");
+static const QLatin1String scDelete("Delete");
+static const QLatin1String scCopy("Copy");
+static const QLatin1String scMkdir("Mkdir");
+static const QLatin1String scIsDefault("isDefault");
+static const QLatin1String scAdmin("admin");
+static const QLatin1String scTwoArgs("%1/%2/");
+static const QLatin1String scThreeArgs("%1/%2/%3");
+static const QLatin1String scComponentScriptTest("var component = installer.componentByName('%1'); component.name;");
+static const QLatin1String scInstallerPrefix("installer://");
+static const QLatin1String scInstallerPrefixWithOneArgs("installer://%1/");
+static const QLatin1String scInstallerPrefixWithTwoArgs("installer://%1/%2");
+static const QLatin1String scLocalesArgs("%1%2_%3.%4");
// constants used throughout the settings and package manager core class
static const QLatin1String scTitle("Title");
@@ -74,17 +146,27 @@ static const QLatin1String scRunProgram("RunProgram");
static const QLatin1String scRunProgramArguments("RunProgramArguments");
static const QLatin1String scStartMenuDir("StartMenuDir");
static const QLatin1String scRemoveTargetDir("RemoveTargetDir");
+static const QLatin1String scLocalCacheDir("LocalCacheDir");
+static const QLatin1String scPersistentLocalCache("PersistentLocalCache");
static const QLatin1String scRunProgramDescription("RunProgramDescription");
static const QLatin1String scTargetConfigurationFile("TargetConfigurationFile");
static const QLatin1String scAllowNonAsciiCharacters("AllowNonAsciiCharacters");
static const QLatin1String scDisableAuthorizationFallback("DisableAuthorizationFallback");
+static const QLatin1String scDisableCommandLineInterface("DisableCommandLineInterface");
+static const QLatin1String scRemoteRepositories("RemoteRepositories");
+static const QLatin1String scRepositoryCategories("RepositoryCategories");
static const QLatin1String scRepositorySettingsPageVisible("RepositorySettingsPageVisible");
static const QLatin1String scAllowSpaceInPath("AllowSpaceInPath");
+static const QLatin1String scAllowRepositoriesForOfflineInstaller("AllowRepositoriesForOfflineInstaller");
static const QLatin1String scWizardStyle("WizardStyle");
static const QLatin1String scStyleSheet("StyleSheet");
static const QLatin1String scTitleColor("TitleColor");
static const QLatin1String scWizardDefaultWidth("WizardDefaultWidth");
static const QLatin1String scWizardDefaultHeight("WizardDefaultHeight");
+static const QLatin1String scWizardMinimumWidth("WizardMinimumWidth");
+static const QLatin1String scWizardMinimumHeight("WizardMinimumHeight");
+static const QLatin1String scWizardShowPageList("WizardShowPageList");
+static const QLatin1String scProductImages("ProductImages");
static const QLatin1String scUrlQueryString("UrlQueryString");
static const QLatin1String scProductUUID("ProductUUID");
static const QLatin1String scAllUsers("AllUsers");
@@ -92,9 +174,158 @@ static const QLatin1String scSupportsModify("SupportsModify");
static const QLatin1String scAllowUnstableComponents("AllowUnstableComponents");
static const QLatin1String scSaveDefaultRepositories("SaveDefaultRepositories");
static const QLatin1String scRepositoryCategoryDisplayName("RepositoryCategoryDisplayName");
-
+static const QLatin1String scHighDpi("@2x.");
+static const QLatin1String scWatermark("Watermark");
+static const QLatin1String scBanner("Banner");
+static const QLatin1String scLogo("Logo");
+static const QLatin1String scBackground("Background");
+static const QLatin1String scPageListPixmap("PageListPixmap");
+static const QLatin1String scAliasDefinitionsFile("AliasDefinitionsFile");
const char scRelocatable[] = "@RELOCATABLE_PATH@";
+static const QStringList scMetaElements = {
+ QLatin1String("Script"),
+ QLatin1String("Licenses"),
+ QLatin1String("UserInterfaces"),
+ QLatin1String("Translations")
+};
}
+namespace CommandLineOptions {
+
+// Help & version information
+static const QLatin1String scHelpShort("h");
+static const QLatin1String scHelpLong("help");
+static const QLatin1String scVersionShort("v");
+static const QLatin1String scVersionLong("version");
+
+// Output related options
+static const QLatin1String scVerboseShort("d");
+static const QLatin1String scVerboseLong("verbose");
+static const QLatin1String scLoggingRulesShort("g");
+static const QLatin1String scLoggingRulesLong("logging-rules");
+
+// Consumer commands
+static const QLatin1String scInstallShort("in");
+static const QLatin1String scInstallLong("install");
+static const QLatin1String scCheckUpdatesShort("ch");
+static const QLatin1String scCheckUpdatesLong("check-updates");
+static const QLatin1String scUpdateShort("up");
+static const QLatin1String scUpdateLong("update");
+static const QLatin1String scRemoveShort("rm");
+static const QLatin1String scRemoveLong("remove");
+static const QLatin1String scListShort("li");
+static const QLatin1String scListLong("list");
+static const QLatin1String scSearchShort("se");
+static const QLatin1String scSearchLong("search");
+static const QLatin1String scCreateOfflineShort("co");
+static const QLatin1String scCreateOfflineLong("create-offline");
+static const QLatin1String scClearCacheShort("cc");
+static const QLatin1String scClearCacheLong("clear-cache");
+static const QLatin1String scPurgeShort("pr");
+static const QLatin1String scPurgeLong("purge");
+
+// Repository management options
+static const QLatin1String scAddRepositoryShort("ar");
+static const QLatin1String scAddRepositoryLong("add-repository");
+static const QLatin1String scAddTmpRepositoryShort("at");
+static const QLatin1String scAddTmpRepositoryLong("add-temp-repository");
+static const QLatin1String scSetTmpRepositoryShort("st");
+static const QLatin1String scSetTmpRepositoryLong("set-temp-repository");
+
+// Proxy options
+static const QLatin1String scSystemProxyShort("sp");
+static const QLatin1String scSystemProxyLong("system-proxy");
+static const QLatin1String scNoProxyShort("np");
+static const QLatin1String scNoProxyLong("no-proxy");
+
+// Starting mode options
+static const QLatin1String scStartUpdaterShort("su");
+static const QLatin1String scStartUpdaterLong("start-updater");
+static const QLatin1String scStartPackageManagerShort("sm");
+static const QLatin1String scStartPackageManagerLong("start-package-manager");
+static const QLatin1String scStartUninstallerShort("sr");
+static const QLatin1String scStartUninstallerLong("start-uninstaller");
+
+// Message acceptance options
+static const QLatin1String scAcceptMessageQueryShort("am");
+static const QLatin1String scAcceptMessageQueryLong("accept-messages");
+static const QLatin1String scRejectMessageQueryShort("rm");
+static const QLatin1String scRejectMessageQueryLong("reject-messages");
+static const QLatin1String scMessageAutomaticAnswerShort("aa");
+static const QLatin1String scMessageAutomaticAnswerLong("auto-answer");
+static const QLatin1String scMessageDefaultAnswerShort("da");
+static const QLatin1String scMessageDefaultAnswerLong("default-answer");
+static const QLatin1String scAcceptLicensesShort("al");
+static const QLatin1String scAcceptLicensesLong("accept-licenses");
+static const QLatin1String scFileDialogAutomaticAnswer("file-query");
+static const QLatin1String scConfirmCommandShort("c");
+static const QLatin1String scConfirmCommandLong("confirm-command");
+
+// Misc installation options
+static const QLatin1String scRootShort("t");
+static const QLatin1String scRootLong("root");
+static const QLatin1String scOfflineInstallerNameShort("oi");
+static const QLatin1String scOfflineInstallerNameLong("offline-installer-name");
+static const QLatin1String scPlatformShort("p");
+static const QLatin1String scPlatformLong("platform");
+static const QLatin1String scNoForceInstallationShort("nf");
+static const QLatin1String scNoForceInstallationLong("no-force-installations");
+static const QLatin1String scNoSizeCheckingShort("ns");
+static const QLatin1String scNoSizeCheckingLong("no-size-checking");
+static const QLatin1String scShowVirtualComponentsShort("sv");
+static const QLatin1String scShowVirtualComponentsLong("show-virtual-components");
+static const QLatin1String scInstallCompressedRepositoryShort("i");
+static const QLatin1String scInstallCompressedRepositoryLong("install-compressed-repository");
+static const QLatin1String scCreateLocalRepositoryShort("cl");
+static const QLatin1String scCreateLocalRepositoryLong("create-local-repository");
+static const QLatin1String scNoDefaultInstallationShort("nd");
+static const QLatin1String scNoDefaultInstallationLong("no-default-installations");
+static const QLatin1String scFilterPackagesShort("fp");
+static const QLatin1String scFilterPackagesLong("filter-packages");
+static const QLatin1String scLocalCachePathShort("cp");
+static const QLatin1String scLocalCachePathLong("cache-path");
+static const QLatin1String scTypeLong("type");
+
+// Developer options
+static const QLatin1String scScriptShort("s");
+static const QLatin1String scScriptLong("script");
+static const QLatin1String scStartServerShort("ss");
+static const QLatin1String scStartServerLong("start-server");
+static const QLatin1String scStartClientShort("sc");
+static const QLatin1String scStartClientLong("start-client");
+static const QLatin1String scSquishPortShort("q");
+static const QLatin1String scSquishPortLong("squish-port");
+static const QLatin1String scMaxConcurrentOperationsShort("mco");
+static const QLatin1String scMaxConcurrentOperationsLong("max-concurrent-operations");
+static const QLatin1String scCleanupUpdate("cleanup-update");
+static const QLatin1String scCleanupUpdateOnly("cleanup-update-only");
+
+// Deprecated options, provided only for backward compatibility
+static const QLatin1String scDeprecatedUpdater("updater");
+static const QLatin1String scDeprecatedCheckUpdates("checkupdates");
+
+// Options supposed to be used without graphical interface
+static const QStringList scCommandLineInterfaceOptions = {
+ scInstallShort,
+ scInstallLong,
+ scCheckUpdatesShort,
+ scCheckUpdatesLong,
+ scUpdateShort,
+ scUpdateLong,
+ scRemoveShort,
+ scRemoveLong,
+ scListShort,
+ scListLong,
+ scSearchShort,
+ scSearchLong,
+ scCreateOfflineShort,
+ scCreateOfflineLong,
+ scPurgeShort,
+ scPurgeLong,
+ scClearCacheShort,
+ scClearCacheLong
+};
+
+} // namespace CommandLineOptions
#endif // CONSTANTS_H
diff --git a/src/libs/installer/consumeoutputoperation.cpp b/src/libs/installer/consumeoutputoperation.cpp
index 43c78193c..e9b8d1295 100644
--- a/src/libs/installer/consumeoutputoperation.cpp
+++ b/src/libs/installer/consumeoutputoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,7 @@
#include "consumeoutputoperation.h"
#include "packagemanagercore.h"
#include "utils.h"
+#include "globals.h"
#include <QFile>
#include <QDir>
@@ -37,6 +38,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ConsumeOutputOperation
+ \internal
+*/
+
ConsumeOutputOperation::ConsumeOutputOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -74,19 +81,7 @@ bool ConsumeOutputOperation::performOperation()
return false;
}
- QString executablePath = arguments().at(1);
- QFileInfo executable(executablePath);
-#ifdef Q_OS_WIN
- if (!executable.exists() && executable.suffix().isEmpty())
- executable = QFileInfo(executablePath + QLatin1String(".exe"));
-#endif
-
- if (!executable.exists() || !executable.isExecutable()) {
- setError(UserDefinedError);
- setErrorString(tr("File \"%1\" does not exist or is not an executable binary.").arg(
- QDir::toNativeSeparators(executable.absoluteFilePath())));
- return false;
- }
+ QString executable = arguments().at(1);
QByteArray executableOutput;
@@ -96,16 +91,17 @@ bool ConsumeOutputOperation::performOperation()
int waitCount = 0;
while (executableOutput.isEmpty() && waitCount < 3) {
QProcess process;
- process.start(executable.absoluteFilePath(), processArguments, QIODevice::ReadOnly);
+ process.start(executable, processArguments, QIODevice::ReadOnly);
if (process.waitForFinished(10000)) {
if (process.exitStatus() == QProcess::CrashExit) {
- qWarning() << executable.absoluteFilePath() << processArguments
- << "crashed with exit code" << process.exitCode()
- << "standard output: " << process.readAllStandardOutput()
- << "error output: " << process.readAllStandardError();
+ qCWarning(QInstaller::lcInstallerInstallLog) << executable
+ << processArguments << "crashed with exit code"
+ << process.exitCode() << "standard output: "
+ << process.readAllStandardOutput() << "error output: "
+ << process.readAllStandardError();
setError(UserDefinedError);
- setErrorString(tr("Running \"%1\" resulted in a crash.").arg(
- QDir::toNativeSeparators(executable.absoluteFilePath())));
+ setErrorString(tr("Failed to run command: \"%1\": %2").arg(
+ QDir::toNativeSeparators(executable), process.errorString()));
return false;
}
executableOutput.append(process.readAllStandardOutput());
@@ -116,15 +112,17 @@ bool ConsumeOutputOperation::performOperation()
uiDetachedWait(waitTimeInMilliSeconds);
}
if (process.state() > QProcess::NotRunning ) {
- qWarning() << executable.absoluteFilePath() << "process is still running, need to kill it.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << executable
+ << "process is still running, need to kill it.";
process.kill();
}
}
if (executableOutput.isEmpty()) {
- qWarning() << "Cannot get any query output from executable" << executable.absoluteFilePath();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot get any query output from executable"
+ << executable;
}
- core->setValue(installerKeyName, QString::fromLocal8Bit(executableOutput));
+ core->setValue(installerKeyName, QString::fromLocal8Bit(executableOutput.trimmed()));
return true;
}
diff --git a/src/libs/installer/consumeoutputoperation.h b/src/libs/installer/consumeoutputoperation.h
index a1f8a09ff..05f6425f3 100644
--- a/src/libs/installer/consumeoutputoperation.h
+++ b/src/libs/installer/consumeoutputoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT ConsumeOutputOperation : public Operation
public:
explicit ConsumeOutputOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
private:
};
diff --git a/src/libs/installer/copydirectoryoperation.cpp b/src/libs/installer/copydirectoryoperation.cpp
index e83af07e1..c0fec0649 100644
--- a/src/libs/installer/copydirectoryoperation.cpp
+++ b/src/libs/installer/copydirectoryoperation.cpp
@@ -34,6 +34,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CopyDirectoryOperation
+ \internal
+*/
+
class AutoPush
{
public:
@@ -61,7 +67,7 @@ bool CopyDirectoryOperation::performOperation()
if (!checkArgumentCount(2, 3, tr("<source> <target> [\"forceOverwrite\"]")))
return false;
- const QStringList args = arguments();
+ const QStringList args = parsePerformOperationArguments();
const QString sourcePath = args.at(0);
const QString targetPath = args.at(1);
bool overwrite = false;
@@ -147,7 +153,11 @@ bool CopyDirectoryOperation::performOperation()
bool CopyDirectoryOperation::undoOperation()
{
- Q_ASSERT(arguments().count() == 2);
+ if (skipUndoOperation())
+ return true;
+
+ if (!checkArgumentCount(2))
+ return false;
QDir dir;
const QStringList files = value(QLatin1String("files")).toStringList();
diff --git a/src/libs/installer/copydirectoryoperation.h b/src/libs/installer/copydirectoryoperation.h
index f934f8b91..2e96144d1 100644
--- a/src/libs/installer/copydirectoryoperation.h
+++ b/src/libs/installer/copydirectoryoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,10 +42,10 @@ class INSTALLER_EXPORT CopyDirectoryOperation : public QObject, public Operation
public:
explicit CopyDirectoryOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
Q_SIGNALS:
void outputTextChanged(const QString &progress);
diff --git a/src/libs/installer/copyfiletask.cpp b/src/libs/installer/copyfiletask.cpp
index 50626b682..856feda01 100644
--- a/src/libs/installer/copyfiletask.cpp
+++ b/src/libs/installer/copyfiletask.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CopyFileTask
+ \internal
+*/
+
CopyFileTask::CopyFileTask(const FileTaskItem &item)
: AbstractFileTask(item)
{
@@ -106,7 +112,7 @@ void CopyFileTask::doTask(QFutureInterface<FileTaskResult> &fi)
observer.addSample(read);
observer.timerEvent(nullptr);
observer.addBytesTransfered(read);
- observer.addCheckSumData(buffer.data(), read);
+ observer.addCheckSumData(buffer.left(read));
fi.setProgressValueAndText(observer.progressValue(), observer.progressText());
}
diff --git a/src/libs/installer/copyfiletask.h b/src/libs/installer/copyfiletask.h
index cad3b6fcd..0f06ba871 100644
--- a/src/libs/installer/copyfiletask.h
+++ b/src/libs/installer/copyfiletask.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -45,7 +45,7 @@ public:
explicit CopyFileTask(const QString &source);
CopyFileTask(const QString &source, const QString &target);
- void doTask(QFutureInterface<FileTaskResult> &fi);
+ void doTask(QFutureInterface<FileTaskResult> &fi) override;
};
} // namespace QInstaller
diff --git a/src/libs/installer/createdesktopentryoperation.cpp b/src/libs/installer/createdesktopentryoperation.cpp
index 17e165777..a19fd773a 100644
--- a/src/libs/installer/createdesktopentryoperation.cpp
+++ b/src/libs/installer/createdesktopentryoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,10 @@
#include "errors.h"
#include "fileutils.h"
+#include "globals.h"
+#include "adminauthorization.h"
+#include "remoteclient.h"
+#include "packagemanagercore.h"
#include <QDir>
#include <QFile>
@@ -37,6 +41,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CreateDesktopEntryOperation
+ \internal
+*/
+
QString CreateDesktopEntryOperation::absoluteFileName()
{
const QString filename = arguments().first();
@@ -51,9 +61,12 @@ QString CreateDesktopEntryOperation::absoluteFileName()
QStringList XDG_DATA_HOME = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"))
.split(QLatin1Char(':'),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
+
+ XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path
- XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default path
+ if (packageManager() && packageManager()->hasAdminRights())
+ XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path
const QStringList directories = XDG_DATA_HOME;
QString directory;
@@ -137,18 +150,18 @@ bool CreateDesktopEntryOperation::performOperation()
return false;
}
- QFile::setPermissions(filename, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::ReadGroup
- | QFile::ReadOther | QFile::ExeOwner | QFile::ExeGroup | QFile::ExeOther);
+ setDefaultFilePermissions(filename, DefaultFilePermissions::Executable);
- QTextStream stream(&file);
- stream.setCodec("UTF-8");
- stream << QLatin1String("[Desktop Entry]") << endl;
+ QString outString;
+ QTextStream stream(&outString);
+ stream << QLatin1String("[Desktop Entry]") << Qt::endl;
// Type=Application\nExec=qtcreator\nPath=...
const QStringList pairs = values.split(QLatin1Char('\n'));
for (QStringList::const_iterator it = pairs.begin(); it != pairs.end(); ++it)
- stream << *it << endl;
+ stream << *it << Qt::endl;
+ file.write(outString.toUtf8());
return true;
}
@@ -159,7 +172,8 @@ bool CreateDesktopEntryOperation::undoOperation()
// first remove the link
QFile file(filename);
if (file.exists() && !file.remove()) {
- qWarning() << "Cannot delete file" << filename << ":" << file.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot delete file" << filename
+ << ":" << file.errorString();
return true;
}
@@ -169,13 +183,15 @@ bool CreateDesktopEntryOperation::undoOperation()
QFile backupFile(value(QLatin1String("backupOfExistingDesktopEntry")).toString());
if (!backupFile.exists()) {
// do not treat this as a real error: The backup file might have been just nuked by the user.
- qWarning() << "Cannot restore original desktop entry at" << filename
- << ": Backup file" << backupFile.fileName() << "does not exist anymore.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot restore original desktop entry at" << filename
+ << ": Backup file" << backupFile.fileName() << "does not exist anymore.";
return true;
}
- if (!backupFile.rename(filename))
- qWarning() << "Cannot restore the file" << filename << ":" << backupFile.errorString();
+ if (!backupFile.rename(filename)) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot restore the file" << filename
+ << ":" << backupFile.errorString();
+ }
return true;
}
diff --git a/src/libs/installer/createdesktopentryoperation.h b/src/libs/installer/createdesktopentryoperation.h
index 793d1db16..be3b088b2 100644
--- a/src/libs/installer/createdesktopentryoperation.h
+++ b/src/libs/installer/createdesktopentryoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,10 +40,10 @@ public:
explicit CreateDesktopEntryOperation(PackageManagerCore *core);
~CreateDesktopEntryOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
QString absoluteFileName();
};
diff --git a/src/libs/installer/createlinkoperation.cpp b/src/libs/installer/createlinkoperation.cpp
index 851290ba8..65ff8fc5d 100644
--- a/src/libs/installer/createlinkoperation.cpp
+++ b/src/libs/installer/createlinkoperation.cpp
@@ -34,6 +34,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CreateLinkOperation
+ \internal
+*/
+
CreateLinkOperation::CreateLinkOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -82,7 +88,7 @@ bool CreateLinkOperation::undoOperation()
return false;
}
- return !QFileInfo(linkPath).exists();
+ return !QFileInfo::exists(linkPath);
}
bool CreateLinkOperation::testOperation()
diff --git a/src/libs/installer/createlinkoperation.h b/src/libs/installer/createlinkoperation.h
index 2e4ece9aa..b864f0e34 100644
--- a/src/libs/installer/createlinkoperation.h
+++ b/src/libs/installer/createlinkoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT CreateLinkOperation : public Operation
public:
explicit CreateLinkOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
}
diff --git a/src/libs/installer/createlocalrepositoryoperation.cpp b/src/libs/installer/createlocalrepositoryoperation.cpp
index 0eabc8c35..286cc9b5b 100644
--- a/src/libs/installer/createlocalrepositoryoperation.cpp
+++ b/src/libs/installer/createlocalrepositoryoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,11 +34,11 @@
#include "fileio.h"
#include "fileutils.h"
#include "copydirectoryoperation.h"
-#include "lib7z_create.h"
-#include "lib7z_facade.h"
+#include "archivefactory.h"
#include "packagemanagercore.h"
#include "productkeycheck.h"
#include "constants.h"
+#include "remoteclient.h"
#include "updateoperations.h"
@@ -49,6 +49,11 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CreateLocalRepositoryOperation
+ \internal
+*/
// -- AutoHelper
@@ -82,8 +87,7 @@ static void fixPermissions(const QString &repoPath)
if (!it.fileInfo().isFile())
continue;
- if (!QFile::setPermissions(it.filePath(), QFile::ReadOwner | QFile::WriteOwner
- | QFile::ReadUser | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther)) {
+ if (!setDefaultFilePermissions(it.filePath(), DefaultFilePermissions::NonExecutable)) {
throw Error(CreateLocalRepositoryOperation::tr("Cannot set permissions for file \"%1\".")
.arg(QDir::toNativeSeparators(it.filePath())));
}
@@ -119,8 +123,17 @@ static QString createArchive(const QString repoPath, const QString &sourceDir, c
const QString fileName = QString::fromLatin1("/%1meta.7z").arg(version);
QFile archive(repoPath + fileName);
- QInstaller::openForWrite(&archive);
- Lib7z::createArchive(&archive, QStringList() << sourceDir);
+
+ QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive.fileName()));
+ if (!archiveFile) {
+ throw Error(CreateLocalRepositoryOperation::tr("Unsupported archive \"%1\": no handler "
+ "registered for file suffix \"%2\".").arg(archive.fileName(), QFileInfo(archive.fileName()).suffix()));
+ }
+ if (!(archiveFile->open(QIODevice::WriteOnly) && archiveFile->create(QStringList() << sourceDir))) {
+ throw Error(CreateLocalRepositoryOperation::tr("Cannot create archive \"%1\": %2")
+ .arg(QDir::toNativeSeparators(archive.fileName()), archiveFile->errorString()));
+ }
+ archiveFile->close();
removeFiles(sourceDir, helper); // cleanup the files we compressed
if (!archive.rename(sourceDir + fileName)) {
throw Error(CreateLocalRepositoryOperation::tr("Cannot move file \"%1\" to \"%2\": %3")
@@ -184,7 +197,6 @@ bool CreateLocalRepositoryOperation::performOperation()
}
setValue(QLatin1String("createddir"), mkDirOp.value(QLatin1String("createddir")));
-#if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
// Internal changes to QTemporaryFile break copying Qt resources through
// QInstaller::RemoteFileEngine. We do not register resources to be handled by
// RemoteFileEngine, instead copying using 5.9 succeeded because QFile::copy()
@@ -193,22 +205,50 @@ bool CreateLocalRepositoryOperation::performOperation()
// This will not work with Qt 5.10 and above as QTemporaryFile introduced a new
// rename() implementation that explicitly uses its own QTemporaryFileEngine.
//
- // Fail and return early if we are working on an elevated permission directory.
- if (core && !core->directoryWritable(repoPath)) {
- setError(UserDefinedError);
- setErrorString(tr("Creating local repository into elevated permissions "
- "directory: %1 is not supported.").arg(repoPath));
- return false;
+ // During elevated installation, first dump the resources to a writable directory
+ // as a normal user, then do the real copy work. We will drop admin rights temporarily.
+
+ QString metaSource = QLatin1String(":/metadata/");
+ QDir metaSourceDir;
+ bool createdMetaDir = false;
+
+ if (RemoteClient::instance().isActive()) {
+ core->dropAdminRights();
+
+ metaSource = generateTemporaryFileName() + QDir::separator();
+ metaSourceDir.setPath(metaSource);
+ if (!metaSourceDir.mkpath(metaSource)) {
+ setError(UserDefinedError);
+ setErrorString(tr("Cannot create path \"%1\".")
+ .arg(QDir::toNativeSeparators(metaSource)));
+ return false;
+ }
+ createdMetaDir = true;
+
+ CopyDirectoryOperation copyMetadata(nullptr);
+ copyMetadata.setArguments(QStringList() << QLatin1String(":/metadata/") << metaSource);
+ if (!copyMetadata.performOperation()) {
+ setError(copyMetadata.error());
+ setErrorString(copyMetadata.errorString());
+ return false;
+ }
+ core->gainAdminRights();
}
-#endif
// copy the whole meta data into local repository
CopyDirectoryOperation copyDirOp(core);
- copyDirOp.setArguments(QStringList() << QLatin1String(":/metadata/") << repoPath);
+ copyDirOp.setArguments(QStringList() << metaSource << repoPath);
connect(&copyDirOp, &CopyDirectoryOperation::outputTextChanged,
this, &CreateLocalRepositoryOperation::outputTextChanged);
const bool success = copyDirOp.performOperation();
helper.m_files = copyDirOp.value(QLatin1String("files")).toStringList();
+
+ if (createdMetaDir && !metaSourceDir.removeRecursively()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Cannot remove directory \"%1\".")
+ .arg(QDir::toNativeSeparators(metaSourceDir.absolutePath())));
+ return false;
+ }
if (!success) {
setError(copyDirOp.error());
setErrorString(copyDirOp.errorString());
@@ -324,10 +364,6 @@ bool CreateLocalRepositoryOperation::performOperation()
}
} catch (...) {}
setValue(QLatin1String("local-repo"), repoPath);
- } catch (const Lib7z::SevenZipException &e) {
- setError(UserDefinedError);
- setErrorString(e.message());
- return false;
} catch (const QInstaller::Error &e) {
setError(UserDefinedError);
setErrorString(e.message());
@@ -342,7 +378,11 @@ bool CreateLocalRepositoryOperation::performOperation()
bool CreateLocalRepositoryOperation::undoOperation()
{
- Q_ASSERT(arguments().count() == 2);
+ if (skipUndoOperation())
+ return true;
+
+ if (!checkArgumentCount(2))
+ return false;
AutoHelper _(this);
emit progressChanged(0.0);
diff --git a/src/libs/installer/createlocalrepositoryoperation.h b/src/libs/installer/createlocalrepositoryoperation.h
index 275d7a409..c4b9264bc 100644
--- a/src/libs/installer/createlocalrepositoryoperation.h
+++ b/src/libs/installer/createlocalrepositoryoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -43,10 +43,10 @@ class INSTALLER_EXPORT CreateLocalRepositoryOperation : public QObject, public O
public:
explicit CreateLocalRepositoryOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
signals:
void progressChanged(double progress);
diff --git a/src/libs/installer/createshortcutoperation.cpp b/src/libs/installer/createshortcutoperation.cpp
index bbe3feba1..894b5843b 100644
--- a/src/libs/installer/createshortcutoperation.cpp
+++ b/src/libs/installer/createshortcutoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,7 @@
#include "fileutils.h"
#include "utils.h"
+#include "globals.h"
#include <QDebug>
#include <QDir>
@@ -38,6 +39,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CreateShortcutOperation
+ \internal
+*/
+
#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <shlobj.h>
@@ -80,7 +87,7 @@ static QString parentDirectory(const QString &current)
return current.mid(0, current.lastIndexOf(QLatin1Char('/')));
}
-static QString takeArgument(const QString argument, QStringList *arguments)
+static QString takeArgument(const QString &argument, QStringList *arguments)
{
// if the arguments contain an option in the form "argument=...", find it and consume it
QStringList::iterator it = std::find_if(arguments->begin(), arguments->end(), StartsWith(argument));
@@ -92,7 +99,7 @@ static QString takeArgument(const QString argument, QStringList *arguments)
return value;
}
-static bool createLink(const QString &fileName, const QString &linkName, QString workingDir,
+static bool createLink(const QString &fileName, const QString &linkName, QString &workingDir,
const QString &arguments = QString(), const QString &iconPath = QString(),
const QString &iconId = QString(), const QString &description = QString())
{
@@ -103,6 +110,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString
IUnknown *iunkn = nullptr;
if (fileName.toLower().startsWith(QLatin1String("http:"))
+ || fileName.toLower().startsWith(QLatin1String("https:"))
|| fileName.toLower().startsWith(QLatin1String("ftp:"))) {
IUniformResourceLocator *iurl = nullptr;
if (FAILED(CoCreateInstance(CLSID_InternetShortcut, nullptr, CLSCTX_INPROC_SERVER,
@@ -169,6 +177,7 @@ static bool createLink(const QString &fileName, const QString &linkName, QString
Q_UNUSED(linkName)
Q_UNUSED(iconPath)
Q_UNUSED(iconId)
+ Q_UNUSED(description)
return true;
#endif
}
@@ -276,7 +285,7 @@ bool CreateShortcutOperation::undoOperation()
const QString &linkLocation = arguments().at(1);
if (!deleteFileNowOrLater(linkLocation) )
- qDebug() << "Cannot delete:" << linkLocation;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot delete:" << linkLocation;
QDir dir; // remove all directories we created
const QStringList directoriesToDelete = value(QLatin1String("createddirs")).toStringList();
diff --git a/src/libs/installer/createshortcutoperation.h b/src/libs/installer/createshortcutoperation.h
index dc2ac6ddb..e8276e976 100644
--- a/src/libs/installer/createshortcutoperation.h
+++ b/src/libs/installer/createshortcutoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,12 +37,12 @@ class INSTALLER_EXPORT CreateShortcutOperation : public Operation
{
Q_DECLARE_TR_FUNCTIONS(QInstaller::CreateShortcutOperation)
public:
- explicit CreateShortcutOperation(PackageManagerCore *core);
+ explicit CreateShortcutOperation(PackageManagerCore *core = nullptr);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
private:
void ensureOptionalArgumentsRead();
diff --git a/src/libs/installer/customcombobox.cpp b/src/libs/installer/customcombobox.cpp
new file mode 100644
index 000000000..998364fe4
--- /dev/null
+++ b/src/libs/installer/customcombobox.cpp
@@ -0,0 +1,54 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "customcombobox.h"
+
+#include <QStylePainter>
+
+using namespace QInstaller;
+
+CustomComboBox::CustomComboBox(QWidget *parent)
+ : QComboBox(parent)
+{
+}
+
+void CustomComboBox::paintEvent(QPaintEvent *e)
+{
+ if (currentIndex() < 0 && !placeholderText().isEmpty()) {
+ QStylePainter painter(this);
+ painter.setPen(palette().color(QPalette::Text));
+ QStyleOptionComboBox opt;
+ initStyleOption(&opt);
+ painter.drawComplexControl(QStyle::CC_ComboBox, opt);
+ opt.palette.setBrush(QPalette::ButtonText, opt.palette.placeholderText());
+ opt.currentText = placeholderText();
+ painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
+ } else {
+ QComboBox::paintEvent(e);
+ }
+}
diff --git a/src/libs/installer/lazyplaintextedit.h b/src/libs/installer/customcombobox.h
index 445fa752d..e022da5a8 100644
--- a/src/libs/installer/lazyplaintextedit.h
+++ b/src/libs/installer/customcombobox.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -26,31 +26,23 @@
**
**************************************************************************/
-#ifndef LAZYPLAINTEXTEDIT_H
-#define LAZYPLAINTEXTEDIT_H
+#ifndef CUSTOMCOMBOBOX_H
+#define CUSTOMCOMBOBOX_H
-#include <QPlainTextEdit>
+#include <QComboBox>
-class LazyPlainTextEdit : public QPlainTextEdit
+namespace QInstaller {
+
+class CustomComboBox : public QComboBox
{
Q_OBJECT
public:
- enum struct TextCursorPosition {
- Keep,
- ForceEnd
- };
- explicit LazyPlainTextEdit(QWidget *parent = 0);
- void updateCursor(TextCursorPosition position);
+ CustomComboBox(QWidget *parent = nullptr);
-public slots:
- void append(const QString &text);
- virtual void clear();
- virtual void setVisible ( bool visible );
protected:
- void timerEvent(QTimerEvent *event);
-private:
- int m_timerId;
- QString m_cachedOutput;
+ void paintEvent(QPaintEvent *e) override;
};
-#endif // LAZYPLAINTEXTEDIT_H
+} // namespace QInstaller
+
+#endif // CUSTOMCOMBOBOX_H
diff --git a/src/libs/installer/directoryguard.cpp b/src/libs/installer/directoryguard.cpp
new file mode 100644
index 000000000..014d213d7
--- /dev/null
+++ b/src/libs/installer/directoryguard.cpp
@@ -0,0 +1,112 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "directoryguard.h"
+
+#include "fileutils.h"
+#include "globals.h"
+#include "errors.h"
+
+#include <QCoreApplication>
+#include <QDir>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::DirectoryGuard
+ \brief RAII class to create a directory and delete it on destruction unless released.
+*/
+
+/*!
+ Constructs a new guard object for \a path.
+*/
+DirectoryGuard::DirectoryGuard(const QString &path)
+ : m_path(path)
+ , m_created(false)
+ , m_released(false)
+{
+ m_path.replace(QLatin1Char('\\'), QLatin1Char('/'));
+}
+
+/*!
+ Destroys the directory guard instance and removes the
+ guarded directory unless released.
+*/
+DirectoryGuard::~DirectoryGuard()
+{
+ if (!m_created || m_released)
+ return;
+ QDir dir(m_path);
+ if (!dir.rmdir(m_path))
+ qCWarning(lcInstallerInstallLog) << "Cannot delete directory" << m_path;
+}
+
+/*!
+ Tries to create the directory structure.
+ Returns a list of every directory created.
+*/
+QStringList DirectoryGuard::tryCreate()
+{
+ if (m_path.isEmpty())
+ return QStringList();
+
+ const QFileInfo fi(m_path);
+ if (fi.exists() && fi.isDir())
+ return QStringList();
+ if (fi.exists() && !fi.isDir()) {
+ throw Error(QCoreApplication::translate("DirectoryGuard",
+ "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path)));
+ }
+ QStringList created;
+
+ QDir toCreate(m_path);
+ while (!toCreate.exists()) {
+ QString p = toCreate.absolutePath();
+ created.push_front(p);
+ p = p.section(QLatin1Char('/'), 0, -2);
+ toCreate = QDir(p);
+ }
+
+ m_created = QInstaller::createDirectoryWithParents(m_path);
+ if (!m_created) {
+ throw Error(QCoreApplication::translate("DirectoryGuard",
+ "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path)));
+ }
+ return created;
+}
+
+/*!
+ Marks the directory as released.
+*/
+void DirectoryGuard::release()
+{
+ m_released = true;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/directoryguard.h b/src/libs/installer/directoryguard.h
new file mode 100644
index 000000000..c18402557
--- /dev/null
+++ b/src/libs/installer/directoryguard.h
@@ -0,0 +1,55 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef DIRECTORYGUARD_H
+#define DIRECTORYGUARD_H
+
+#include "installer_global.h"
+
+#include <QString>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT DirectoryGuard
+{
+public:
+ explicit DirectoryGuard(const QString &path);
+ ~DirectoryGuard();
+
+ QStringList tryCreate();
+ void release();
+
+private:
+ QString m_path;
+ bool m_created;
+ bool m_released;
+};
+
+} // namespace QInstaller
+
+#endif // DIRECTORYGUARD_H
diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp
index 102783133..65eead1f9 100644
--- a/src/libs/installer/downloadarchivesjob.cpp
+++ b/src/libs/installer/downloadarchivesjob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "messageboxhandler.h"
#include "packagemanagercore.h"
#include "utils.h"
+#include "fileutils.h"
#include "filedownloader.h"
#include "filedownloaderfactory.h"
@@ -43,10 +44,12 @@ using namespace QInstaller;
using namespace KDUpdater;
+static constexpr uint scMaxRetries = 5;
+
/*!
- Creates a new DownloadArchivesJob with \a parent.
+ Creates a new DownloadArchivesJob with parent \a core.
*/
-DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
+DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core, const QString &objectName)
: Job(core)
, m_core(core)
, m_downloader(nullptr)
@@ -55,8 +58,12 @@ DownloadArchivesJob::DownloadArchivesJob(PackageManagerCore *core)
, m_canceled(false)
, m_lastFileProgress(0)
, m_progressChangedTimerId(0)
+ , m_totalSizeToDownload(0)
+ , m_totalSizeDownloaded(0)
+ , m_retryCount(scMaxRetries)
{
setCapabilities(Cancelable);
+ setObjectName(objectName);
}
/*!
@@ -69,20 +76,29 @@ DownloadArchivesJob::~DownloadArchivesJob()
}
/*!
- Sets the archives to download. The first value of each pair contains the file name to register
+ Sets the \a archives to download. The first value of each pair contains the file name to register
the file in the installer's internal file system, the second one the source url.
*/
-void DownloadArchivesJob::setArchivesToDownload(const QList<QPair<QString, QString> > &archives)
+void DownloadArchivesJob::setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives)
{
m_archivesToDownload = archives;
m_archivesToDownloadCount = archives.count();
}
/*!
+ Sets the expected total size of archives to download to \a total.
+*/
+void DownloadArchivesJob::setExpectedTotalSize(quint64 total)
+{
+ m_totalSizeToDownload = total;
+}
+
+/*!
\reimp
*/
void DownloadArchivesJob::doStart()
{
+ m_totalDownloadSpeedTimer.start();
m_archivesDownloaded = 0;
fetchNextArchiveHash();
}
@@ -99,17 +115,17 @@ void DownloadArchivesJob::doCancel()
void DownloadArchivesJob::fetchNextArchiveHash()
{
- if (m_core->testChecksum()) {
+ if (m_archivesToDownload.isEmpty()) {
+ emitFinished();
+ return;
+ }
+
+ if (m_archivesToDownload.first().checkSha1CheckSum) {
if (m_canceled) {
finishWithError(tr("Canceled"));
return;
}
- if (m_archivesToDownload.isEmpty()) {
- emitFinished();
- return;
- }
-
if (m_downloader)
m_downloader->deleteLater();
@@ -134,6 +150,7 @@ void DownloadArchivesJob::finishedHashDownload()
QFile sha1HashFile(m_downloader->downloadedFileName());
if (sha1HashFile.open(QFile::ReadOnly)) {
+ emit hashDownloadReady(m_downloader->downloadedFileName());
m_currentHash = sha1HashFile.readAll();
fetchNextArchive();
} else {
@@ -175,7 +192,7 @@ void DownloadArchivesJob::fetchNextArchive()
}
/*!
- Emits the global download progress during a single download in a lazy way (uses a timer to reduce to
+ Emits the global download \a progress during a single download in a lazy way (uses a timer to reduce to
much processChanged).
*/
void DownloadArchivesJob::emitDownloadProgress(double progress)
@@ -186,7 +203,7 @@ void DownloadArchivesJob::emitDownloadProgress(double progress)
}
/*!
- This is used to reduce the progressChanged signals.
+ This is used to reduce the \c progressChanged signals for \a event.
*/
void DownloadArchivesJob::timerEvent(QTimerEvent *event)
{
@@ -198,38 +215,115 @@ void DownloadArchivesJob::timerEvent(QTimerEvent *event)
}
/*!
+ Builds a textual representation of the total download \a status and
+ emits the \c {downloadStatusChanged()} signal.
+*/
+void DownloadArchivesJob::onDownloadStatusChanged(const QString &status)
+{
+ if (!m_downloader || m_canceled) {
+ emit downloadStatusChanged(status);
+ return;
+ }
+
+ QString extendedStatus;
+ quint64 currentDownloaded = m_totalSizeDownloaded + m_downloader->getBytesReceived();
+ if (m_totalSizeToDownload > 0) {
+ QString bytesReceived = humanReadableSize(currentDownloaded);
+ const QString bytesToReceive = humanReadableSize(m_totalSizeToDownload);
+
+ // remove the unit from the bytesReceived value if bytesToReceive has the same
+ const QString tmp = bytesToReceive.mid(bytesToReceive.indexOf(QLatin1Char(' ')));
+ if (bytesReceived.endsWith(tmp))
+ bytesReceived.chop(tmp.length());
+
+ extendedStatus = tr("%1 of %2").arg(bytesReceived, bytesToReceive);
+ } else if (currentDownloaded > 0) {
+ extendedStatus = tr("%1 downloaded.").arg(humanReadableSize(currentDownloaded));
+ }
+
+ const quint64 totalDownloadSpeed = currentDownloaded
+ / double(m_totalDownloadSpeedTimer.elapsed() / 1000);
+
+ if (m_totalSizeToDownload > 0 && totalDownloadSpeed > 0) {
+ const qint64 time = (m_totalSizeToDownload - currentDownloaded) / totalDownloadSpeed;
+
+ int s = time % 60;
+ const int d = time / 86400;
+ const int h = (time / 3600) - (d * 24);
+ const int m = (time / 60) - (d * 1440) - (h * 60);
+
+ QString days;
+ if (d > 0)
+ days = tr("%n day(s), ", "", d);
+
+ QString hours;
+ if (h > 0)
+ hours = tr("%n hour(s), ", "", h);
+
+ QString minutes;
+ if (m > 0)
+ minutes = tr("%n minute(s)", "", m);
+
+ QString seconds;
+ if (s >= 0 && minutes.isEmpty()) {
+ s = (s <= 0 ? 1 : s);
+ seconds = tr("%n second(s)", "", s);
+ }
+ extendedStatus += tr(" - %1%2%3%4 remaining.").arg(days, hours, minutes, seconds);
+ } else {
+ extendedStatus += tr(" - unknown time remaining.");
+ }
+
+ emit downloadStatusChanged(tr("Archive: ") + status
+ + QLatin1String("<br>") + tr("Total: ")+ extendedStatus);
+}
+
+/*!
Registers the just downloaded file in the installer's file system.
*/
void DownloadArchivesJob::registerFile()
{
Q_ASSERT(m_downloader != nullptr);
- if (m_canceled)
+ if (m_canceled || m_archivesToDownload.isEmpty())
return;
- if (m_core->testChecksum() && m_currentHash != m_downloader->sha1Sum().toHex()) {
+ if (m_archivesToDownload.first().checkSha1CheckSum && m_currentHash != m_downloader->sha1Sum().toHex()) {
//TODO: Maybe we should try to download the file again automatically
const QMessageBox::Button res =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("DownloadError"), tr("Download Error"), tr("Hash verification while "
- "downloading failed. This is a temporary error, please retry."),
- QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel);
+ "downloading failed. This is a temporary error, please retry.\n\n"
+ "Expected: %1 \nDownloaded: %2").arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())),
+ QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Retry);
if (res == QMessageBox::Cancel) {
- finishWithError(tr("Cannot verify Hash"));
+ finishWithError(tr("Cannot verify Hash\nExpected: %1 \nDownloaded: %2")
+ .arg(QString::fromLatin1(m_currentHash), QString::fromLatin1(m_downloader->sha1Sum().toHex())));
return;
}
+ // When using command line instance, only retry a number of times to avoid
+ // infinite loop in case the automatic answer for the messagebox is "Retry"
+ if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) {
+ finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries));
+ return;
+ }
} else {
+ m_retryCount = scMaxRetries;
+
++m_archivesDownloaded;
+ m_totalSizeDownloaded += QFile(m_downloader->downloadedFileName()).size();
if (m_progressChangedTimerId) {
killTimer(m_progressChangedTimerId);
m_progressChangedTimerId = 0;
emit progressChanged(double(m_archivesDownloaded) / m_archivesToDownloadCount);
}
- const QPair<QString, QString> pair = m_archivesToDownload.takeFirst();
- BinaryFormatEngineHandler::instance()->registerResource(pair.first,
+ const PackageManagerCore::DownloadItem item = m_archivesToDownload.takeFirst();
+ BinaryFormatEngineHandler::instance()->registerResource(item.fileName,
m_downloader->downloadedFileName());
+
+ emit fileDownloadReady(m_downloader->downloadedFileName());
}
fetchNextArchiveHash();
}
@@ -247,12 +341,21 @@ void DownloadArchivesJob::downloadFailed(const QString &error)
const QMessageBox::StandardButton b =
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("archiveDownloadError"), tr("Download Error"), tr("Cannot download archive %1: %2")
- .arg(m_archivesToDownload.first().second, error), QMessageBox::Retry | QMessageBox::Cancel);
+ .arg(m_archivesToDownload.first().sourceUrl, error), QMessageBox::Retry | QMessageBox::Cancel,
+ QMessageBox::Retry);
+
+ if (b == QMessageBox::Retry) {
+ // When using command line instance, only retry a number of times to avoid
+ // infinite loop in case the automatic answer for the messagebox is "Retry"
+ if (m_core->isCommandLineInstance() && (--m_retryCount == 0)) {
+ finishWithError(tr("Retry count (%1) exceeded").arg(scMaxRetries));
+ return;
+ }
- if (b == QMessageBox::Retry)
QMetaObject::invokeMethod(this, "fetchNextArchiveHash", Qt::QueuedConnection);
- else
+ } else {
downloadCanceled();
+ }
}
void DownloadArchivesJob::finishWithError(const QString &error)
@@ -268,13 +371,13 @@ void DownloadArchivesJob::finishWithError(const QString &error)
KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix, const QString &queryString)
{
KDUpdater::FileDownloader *downloader = nullptr;
- const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first);
+ const QFileInfo fi = QFileInfo(m_archivesToDownload.first().fileName);
const Component *const component = m_core->componentByName(PackageManagerCore::checkableName(QFileInfo(fi.path()).fileName()));
if (component) {
QString fullQueryString;
if (!queryString.isEmpty())
fullQueryString = QLatin1String("?") + queryString;
- const QUrl url(m_archivesToDownload.first().second + suffix + fullQueryString);
+ const QUrl url(m_archivesToDownload.first().sourceUrl + suffix + fullQueryString);
const QString &scheme = url.scheme();
downloader = FileDownloaderFactory::instance().create(scheme, this);
@@ -290,7 +393,7 @@ KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &s
connect(downloader, &FileDownloader::downloadCanceled, this, &DownloadArchivesJob::downloadCanceled);
connect(downloader, &FileDownloader::downloadAborted, this, &DownloadArchivesJob::downloadFailed,
Qt::QueuedConnection);
- connect(downloader, &FileDownloader::downloadStatus, this, &DownloadArchivesJob::downloadStatusChanged);
+ connect(downloader, &FileDownloader::downloadStatus, this, &DownloadArchivesJob::onDownloadStatusChanged);
if (FileDownloaderFactory::isSupportedScheme(scheme)) {
downloader->setDownloadedFileName(component->localTempPath() + QLatin1Char('/')
diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h
index 8f2392064..5155c881a 100644
--- a/src/libs/installer/downloadarchivesjob.h
+++ b/src/libs/installer/downloadarchivesjob.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,8 +30,9 @@
#define DOWNLOADARCHIVESJOB_H
#include "job.h"
-
+#include "packagemanagercore.h"
#include <QtCore/QPair>
+#include <QtCore/QElapsedTimer>
QT_BEGIN_NAMESPACE
class QTimerEvent;
@@ -44,28 +45,34 @@ namespace KDUpdater {
namespace QInstaller {
class MessageBoxHandler;
-class PackageManagerCore;
class DownloadArchivesJob : public Job
{
Q_OBJECT
public:
- explicit DownloadArchivesJob(PackageManagerCore *core);
+ explicit DownloadArchivesJob(PackageManagerCore *core, const QString &objectName);
~DownloadArchivesJob();
int numberOfDownloads() const { return m_archivesDownloaded; }
- void setArchivesToDownload(const QList<QPair<QString, QString> > &archives);
+ void setArchivesToDownload(const QList<PackageManagerCore::DownloadItem> &archives);
+ void setExpectedTotalSize(quint64 total);
Q_SIGNALS:
void progressChanged(double progress);
void outputTextChanged(const QString &progress);
void downloadStatusChanged(const QString &status);
+ void hashDownloadReady(const QString &localPath);
+ void fileDownloadReady(const QString &localPath);
+
protected:
- void doStart();
- void doCancel();
- void timerEvent(QTimerEvent *event);
+ void doStart() override;
+ void doCancel() override;
+ void timerEvent(QTimerEvent *event) override;
+
+public Q_SLOTS:
+ void onDownloadStatusChanged(const QString &status);
protected Q_SLOTS:
void registerFile();
@@ -86,12 +93,18 @@ private:
int m_archivesDownloaded;
int m_archivesToDownloadCount;
- QList<QPair<QString, QString> > m_archivesToDownload;
+ QList<PackageManagerCore::DownloadItem> m_archivesToDownload;
bool m_canceled;
QByteArray m_currentHash;
double m_lastFileProgress;
int m_progressChangedTimerId;
+
+ quint64 m_totalSizeToDownload;
+ quint64 m_totalSizeDownloaded;
+ QElapsedTimer m_totalDownloadSpeedTimer;
+
+ uint m_retryCount;
};
} // namespace QInstaller
diff --git a/src/libs/installer/downloadfiletask.cpp b/src/libs/installer/downloadfiletask.cpp
index a5d47ad3b..a959677a9 100644
--- a/src/libs/installer/downloadfiletask.cpp
+++ b/src/libs/installer/downloadfiletask.cpp
@@ -1,7 +1,7 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#include "downloadfiletask.h"
#include "downloadfiletask_p.h"
+#include "globals.h"
+#include "productkeycheck.h"
#include <QCoreApplication>
#include <QDir>
@@ -40,6 +42,30 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::DownloadFileTask
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Downloader
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AuthenticationRequiredException
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Data
+ \internal
+*/
+
AuthenticationRequiredException::AuthenticationRequiredException(Type type, const QString &message)
: TaskException(message)
, m_type(type)
@@ -172,7 +198,7 @@ void Downloader::onReadyRead()
data.observer->addSample(read);
data.observer->addBytesTransfered(read);
- data.observer->addCheckSumData(buffer.data(), read);
+ data.observer->addCheckSumData(buffer.left(read));
int progress = m_finished * 100;
for (const auto &pair : m_downloads)
@@ -202,8 +228,8 @@ void Downloader::onFinished(QNetworkReply *reply)
QNetworkReply *const redirectReply = startDownload(taskItem);
foreach (const QUrl &redirect, redirects)
- m_redirects.insertMulti(redirectReply, redirect);
- m_redirects.insertMulti(redirectReply, url);
+ m_redirects.insert(redirectReply, redirect);
+ m_redirects.insert(redirectReply, url);
m_downloads.erase(reply);
m_redirects.remove(reply);
@@ -221,7 +247,7 @@ void Downloader::onFinished(QNetworkReply *reply)
if (!ba.isEmpty()) {
data.observer->addSample(ba.size());
data.observer->addBytesTransfered(ba.size());
- data.observer->addCheckSumData(ba.data(), ba.size());
+ data.observer->addCheckSumData(ba);
}
const QByteArray expectedCheckSum = data.taskItem.value(TaskRole::Checksum).toByteArray();
@@ -244,7 +270,7 @@ void Downloader::onFinished(QNetworkReply *reply)
}
}
-void Downloader::onError(QNetworkReply::NetworkError error)
+void Downloader::errorOccurred(QNetworkReply::NetworkError error)
{
QNetworkReply *const reply = qobject_cast<QNetworkReply *>(sender());
@@ -259,8 +285,12 @@ void Downloader::onError(QNetworkReply::NetworkError error)
//with RepositoryUpdate in Updates.xml later.
//: %2 is a sentence describing the error
if (data.taskItem.source().contains(QLatin1String("Updates.xml"), Qt::CaseInsensitive)) {
- qDebug() << QString::fromLatin1("Network error while downloading '%1': %2.").arg(
+ qCWarning(QInstaller::lcServer) << QString::fromLatin1("Network error while downloading '%1': %2.").arg(
data.taskItem.source(), reply->errorString());
+ } else if (data.taskItem.source().contains(QLatin1String("_meta"), Qt::CaseInsensitive)) {
+ QString errorString = tr("Network error while downloading '%1': %2.").arg(data.taskItem.source(), reply->errorString());
+ errorString.append(ProductKeyCheck::instance()->additionalMetaDownloadWarning());
+ m_futureInterface->reportException(TaskException(errorString));
} else {
m_futureInterface->reportException(
TaskException(tr("Network error while downloading '%1': %2.").arg(
@@ -279,7 +309,7 @@ void Downloader::onSslErrors(const QList<QSslError> &sslErrors)
Q_UNUSED(sslErrors);
#else
foreach (const QSslError &error, sslErrors)
- qDebug() << "SSL error:" << error.errorString();
+ qCWarning(QInstaller::lcServer) << "SSL error:" << error.errorString();
#endif
}
@@ -370,14 +400,17 @@ QNetworkReply *Downloader::startDownload(const FileTaskItem &item)
.arg(source.toString(), source.errorString())));
return 0;
}
+ QNetworkRequest request(source);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
- QNetworkReply *reply = m_nam.get(QNetworkRequest(source));
+ QNetworkReply *reply = m_nam.get(request);
std::unique_ptr<Data> data(new Data(item));
m_downloads[reply] = std::move(data);
connect(reply, &QIODevice::readyRead, this, &Downloader::onReadyRead);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
- SLOT(onError(QNetworkReply::NetworkError)));
+ connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this,
+ SLOT(errorOccurred(QNetworkReply::NetworkError)));
#ifndef QT_NO_SSL
connect(reply, &QNetworkReply::sslErrors, this, &Downloader::onSslErrors);
#endif
diff --git a/src/libs/installer/downloadfiletask.h b/src/libs/installer/downloadfiletask.h
index 21908549d..f50d4c0ff 100644
--- a/src/libs/installer/downloadfiletask.h
+++ b/src/libs/installer/downloadfiletask.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -63,8 +63,8 @@ public:
FileTaskItem taskItem() const { return m_fileTaskItem; }
void setFileTaskItem(const FileTaskItem &item) { m_fileTaskItem = item; }
- void raise() const { throw *this; }
- AuthenticationRequiredException *clone() const {
+ void raise() const override { throw *this; }
+ AuthenticationRequiredException *clone() const override {
return new AuthenticationRequiredException(*this); }
private:
@@ -98,7 +98,7 @@ public:
void setAuthenticator(const QAuthenticator &authenticator);
void setProxyFactory(KDUpdater::FileDownloaderProxyFactory *factory);
- void doTask(QFutureInterface<FileTaskResult> &fi);
+ void doTask(QFutureInterface<FileTaskResult> &fi) override;
private:
friend class Downloader;
diff --git a/src/libs/installer/downloadfiletask_p.h b/src/libs/installer/downloadfiletask_p.h
index 3dfce27b4..4750d5134 100644
--- a/src/libs/installer/downloadfiletask_p.h
+++ b/src/libs/installer/downloadfiletask_p.h
@@ -86,7 +86,7 @@ private slots:
void doDownload();
void onReadyRead();
void onFinished(QNetworkReply *reply);
- void onError(QNetworkReply::NetworkError error);
+ void errorOccurred(QNetworkReply::NetworkError error);
void onSslErrors(const QList<QSslError> &sslErrors);
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
diff --git a/src/libs/installer/elevatedexecuteoperation.cpp b/src/libs/installer/elevatedexecuteoperation.cpp
index aa88f0398..87810211e 100644
--- a/src/libs/installer/elevatedexecuteoperation.cpp
+++ b/src/libs/installer/elevatedexecuteoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,14 +30,22 @@
#include "environment.h"
#include "qprocesswrapper.h"
+#include "globals.h"
+#include "packagemanagercore.h"
#include <QtCore/QDebug>
#include <QtCore/QProcessEnvironment>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
#include <QtCore/QThread>
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ElevatedExecuteOperation
+ \internal
+*/
+
class ElevatedExecuteOperation::Private
{
public:
@@ -53,10 +61,16 @@ private:
public:
void readProcessOutput();
- bool run(const QStringList &arguments);
+ int run(QStringList &arguments, const OperationType type);
+
+private:
+ bool needsRerunWithReplacedVariables(QStringList &arguments, const OperationType type);
+ void setErrorMessage(const QString &message);
+private:
QProcessWrapper *process;
bool showStandardError;
+ QString m_customErrorMessage;
};
ElevatedExecuteOperation::ElevatedExecuteOperation(PackageManagerCore *core)
@@ -65,6 +79,7 @@ ElevatedExecuteOperation::ElevatedExecuteOperation(PackageManagerCore *core)
{
// this operation has to "overwrite" the Execute operation from KDUpdater
setName(QLatin1String("Execute"));
+ setRequiresUnreplacedVariables(true);
}
ElevatedExecuteOperation::~ElevatedExecuteOperation()
@@ -87,10 +102,14 @@ bool ElevatedExecuteOperation::performOperation()
break; //we don't need the UNDOEXECUTE args here
}
- return d->run(args);
+ if (requiresUnreplacedVariables()) {
+ PackageManagerCore *const core = packageManager();
+ args = core->replaceVariables(args);
+ }
+ return d->run(args, Operation::Perform) ? false : true;
}
-bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
+int ElevatedExecuteOperation::Private::run(QStringList &arguments, const OperationType type)
{
QStringList args = arguments;
QString workingDirectory;
@@ -103,13 +122,12 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
args.removeAll(workingDirectoryArgument);
}
- QString customErrorMessage;
QStringList filteredCustomErrorMessage = args.filter(QLatin1String("errormessage="),
Qt::CaseInsensitive);
if (!filteredCustomErrorMessage.isEmpty()) {
QString customErrorMessageArgument = filteredCustomErrorMessage.at(0);
- customErrorMessage = customErrorMessageArgument;
- customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive);
+ m_customErrorMessage = customErrorMessageArgument;
+ m_customErrorMessage.replace(QLatin1String("errormessage="), QString(), Qt::CaseInsensitive);
args.removeAll(customErrorMessageArgument);
}
@@ -120,9 +138,10 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
QList< int > allowedExitCodes;
- QRegExp re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
- if (re.exactMatch(args.first())) {
- const QStringList numbers = re.cap(1).split(QLatin1Char(','));
+ static const QRegularExpression re(QLatin1String("^\\{((-?\\d+,)*-?\\d+)\\}$"));
+ const QRegularExpressionMatch match = re.match(args.first());
+ if (match.hasMatch()) {
+ const QStringList numbers = match.captured(1).split(QLatin1Char(','));
for(QStringList::const_iterator it = numbers.constBegin(); it != numbers.constEnd(); ++it)
allowedExitCodes.push_back(it->toInt());
args.pop_front();
@@ -134,22 +153,26 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
// unix style: when there's an ampersand after the command, it's started detached
if (args.count() >= 2 && args.last() == QLatin1String("&")) {
+ int returnValue = NoError;
args.pop_back();
const bool success = QProcessWrapper::startDetached(args.front(), args.mid(1));
if (!success) {
q->setError(UserDefinedError);
- q->setErrorString(tr("Cannot start detached: \"%1\"").arg(callstr));
+ setErrorMessage(tr("Cannot start detached: \"%1\"").arg(callstr));
+
+ returnValue = Error;
}
- return success;
+ return returnValue;
}
process = new QProcessWrapper();
if (!workingDirectory.isEmpty()) {
process->setWorkingDirectory(workingDirectory);
- qDebug() << "ElevatedExecuteOperation setWorkingDirectory:" << workingDirectory;
+ qCDebug(QInstaller::lcInstallerInstallLog) << "ElevatedExecuteOperation setWorkingDirectory:"
+ << workingDirectory;
}
- QProcessEnvironment penv;
+ QProcessEnvironment penv = QProcessEnvironment::systemEnvironment();
// there is no way to serialize a QProcessEnvironment properly other than per mangled QStringList:
// (i.e. no other way to list all keys)
process->setEnvironment(KDUpdater::Environment::instance().applyTo(penv).toStringList());
@@ -167,7 +190,8 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
//readProcessOutput should only called from this current Thread -> Qt::DirectConnection
QObject::connect(process, SIGNAL(readyRead()), q, SLOT(readProcessOutput()), Qt::DirectConnection);
process->start(args.front(), args.mid(1));
- qDebug() << args.front() << "started, arguments:" << QStringList(args.mid(1)).join(QLatin1String(" "));
+ qCDebug(QInstaller::lcInstallerInstallLog) << args.front() << "started, arguments:"
+ << QStringList(args.mid(1)).join(QLatin1String(" "));
bool success = false;
//we still like the none blocking possibility to perform this operation without threads
@@ -177,13 +201,16 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
success = process->waitForFinished(-1);
}
- bool returnValue = true;
+ int returnValue = NoError;
if (!success) {
q->setError(UserDefinedError);
- //TODO: pass errorString() through the wrapper */
- q->setErrorString(tr("Cannot start: \"%1\": %2").arg(callstr,
+ setErrorMessage(tr("Cannot start: \"%1\": %2").arg(callstr,
process->errorString()));
- returnValue = false;
+ if (!needsRerunWithReplacedVariables(arguments, type)) {
+ returnValue = Error;
+ } else {
+ returnValue = NeedsRerun;
+ }
}
if (QThread::currentThread() == qApp->thread()) {
@@ -195,29 +222,27 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
q->setValue(QLatin1String("ExitCode"), process->exitCode());
- if (process->exitStatus() == QProcessWrapper::CrashExit) {
- q->setError(UserDefinedError);
- q->setErrorString(tr("Program crashed: \"%1\"").arg(callstr));
- returnValue = false;
- }
-
- if (!allowedExitCodes.contains(process->exitCode())) {
- q->setError(UserDefinedError);
- if (customErrorMessage.isEmpty()) {
- q->setErrorString(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
- .arg(QString::number(process->exitCode()), callstr));
- } else {
- q->setErrorString(customErrorMessage);
- }
-
- QByteArray standardErrorOutput = process->readAllStandardError();
+ if (success) {
+ const QByteArray standardErrorOutput = process->readAllStandardError();
// in error case it would be useful to see something in verbose output
if (!standardErrorOutput.isEmpty())
- qWarning().noquote() << standardErrorOutput;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << standardErrorOutput;
- returnValue = false;
+ if (process->exitStatus() == QProcessWrapper::CrashExit) {
+ q->setError(UserDefinedError);
+ setErrorMessage(tr("Program crashed: \"%1\"").arg(callstr));
+ returnValue = Error;
+ } else if (!allowedExitCodes.contains(process->exitCode()) && returnValue != NeedsRerun) {
+ if (!needsRerunWithReplacedVariables(arguments, type)) {
+ q->setError(UserDefinedError);
+ setErrorMessage(tr("Execution failed (Unexpected exit code: %1): \"%2\"")
+ .arg(QString::number(process->exitCode()), callstr));
+ returnValue = Error;
+ } else {
+ returnValue = NeedsRerun;
+ }
+ }
}
-
Q_ASSERT(process);
Q_ASSERT(process->state() == QProcessWrapper::NotRunning);
delete process;
@@ -226,6 +251,35 @@ bool ElevatedExecuteOperation::Private::run(const QStringList &arguments)
return returnValue;
}
+bool ElevatedExecuteOperation::Private::needsRerunWithReplacedVariables(QStringList &arguments, const OperationType type)
+{
+ if (type != Operation::Undo)
+ return false;
+ bool rerun = false;
+ PackageManagerCore *const core = q->packageManager();
+ for (int i = 0; i < arguments.count(); i++) {
+ QString key = core->key(arguments.at(i));
+ if (!key.isEmpty() && key.endsWith(QLatin1String("_OLD"))) {
+ key.remove(key.length() - 4, 4);
+ if (core->containsValue(key)) {
+ key.prepend(QLatin1String("@"));
+ key.append(QLatin1String("@"));
+ QString value = core->replaceVariables(key);
+ arguments.replace(i, value);
+ rerun = true;
+ }
+ }
+ }
+ return rerun;
+}
+
+void ElevatedExecuteOperation::Private::setErrorMessage(const QString &message)
+{
+ if (m_customErrorMessage.isEmpty())
+ q->setErrorString(message);
+ else
+ q->setErrorString(m_customErrorMessage);
+}
/*!
Cancels the ElevatedExecuteOperation. This methods tries to terminate the process
gracefully by calling QProcessWrapper::terminate. After 10 seconds, the process gets killed.
@@ -240,19 +294,19 @@ void ElevatedExecuteOperation::Private::readProcessOutput()
Q_ASSERT(process);
Q_ASSERT(QThread::currentThread() == process->thread());
if (QThread::currentThread() != process->thread()) {
- qDebug() << Q_FUNC_INFO << "can only be called from the same thread as the process is.";
+ qCDebug(QInstaller::lcInstallerInstallLog) << Q_FUNC_INFO << "can only be called from the "
+ "same thread as the process is.";
}
const QByteArray output = process->readAll();
if (!output.isEmpty()) {
if (q->error() == UserDefinedError)
- qWarning() << output;
+ qCWarning(QInstaller::lcInstallerInstallLog)<< output;
else
- qDebug() << output;
+ qCDebug(QInstaller::lcInstallerInstallLog) << output;
emit q->outputTextChanged(QString::fromLocal8Bit(output));
}
}
-
bool ElevatedExecuteOperation::undoOperation()
{
QStringList args;
@@ -266,7 +320,21 @@ bool ElevatedExecuteOperation::undoOperation()
if (args.isEmpty())
return true;
- return d->run(args);
+ if (requiresUnreplacedVariables()) {
+ PackageManagerCore *const core = packageManager();
+ args = core->replaceVariables(args);
+ }
+
+ int returnValue = d->run(args, Operation::Undo);
+ if (returnValue == NeedsRerun) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("Failed to run "
+ "undo operation \"%1\" for component %2. Trying again with arguments %3").arg(name(),
+ value(QLatin1String("component")).toString(), args.join(QLatin1String(", ")));
+ setError(NoError);
+ setErrorString(QString());
+ returnValue = d->run(args, Operation::Undo);
+ }
+ return returnValue ? false : true;
}
bool ElevatedExecuteOperation::testOperation()
diff --git a/src/libs/installer/elevatedexecuteoperation.h b/src/libs/installer/elevatedexecuteoperation.h
index 5585e77a8..69224f99f 100644
--- a/src/libs/installer/elevatedexecuteoperation.h
+++ b/src/libs/installer/elevatedexecuteoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -38,13 +38,19 @@ class INSTALLER_EXPORT ElevatedExecuteOperation : public QObject, public Operati
Q_OBJECT
public:
+ enum Error {
+ NoError = 0,
+ Error,
+ NeedsRerun
+ };
+
explicit ElevatedExecuteOperation(PackageManagerCore *core);
~ElevatedExecuteOperation();
- void backup() Q_DECL_OVERRIDE;
- bool performOperation() Q_DECL_OVERRIDE;
- bool undoOperation() Q_DECL_OVERRIDE;
- bool testOperation() Q_DECL_OVERRIDE;
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
Q_SIGNALS:
void cancelProcess();
diff --git a/src/libs/installer/environmentvariablesoperation.cpp b/src/libs/installer/environmentvariablesoperation.cpp
index 006ea3762..94cd1e36f 100644
--- a/src/libs/installer/environmentvariablesoperation.cpp
+++ b/src/libs/installer/environmentvariablesoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,8 +30,10 @@
#include "qsettingswrapper.h"
#include <stdlib.h>
+#include <QDir>
#include "environment.h"
+#include "globals.h"
#ifdef Q_OS_WIN
# include <windows.h>
@@ -40,6 +42,12 @@
using namespace QInstaller;
using namespace KDUpdater;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::EnvironmentVariableOperation
+ \internal
+*/
+
EnvironmentVariableOperation::EnvironmentVariableOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -59,7 +67,7 @@ static void broadcastEnvironmentChange()
LRESULT sendresult = SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE,
0, (LPARAM) L"Environment", SMTO_BLOCK | SMTO_ABORTIFHUNG, 5000, &aResult);
if (sendresult == 0 || aResult != 0)
- qWarning("Failed to broadcast the WM_SETTINGCHANGE message.");
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to broadcast the WM_SETTINGCHANGE message.";
}
#endif
@@ -103,6 +111,12 @@ bool handleRegExpandSz(const QString &regPath, const QString &name,
}
}
}
+#else
+ Q_UNUSED(regPath)
+ Q_UNUSED(name)
+ Q_UNUSED(value)
+ Q_UNUSED(errorString)
+ Q_UNUSED(error)
#endif
return setAsExpandSZ;
}
@@ -115,7 +129,7 @@ UpdateOperation::Error writeSetting(const QString &regPath,
QString *oldValue)
{
oldValue->clear();
- SettingsType registry(regPath, QSettingsWrapper::NativeFormat);
+ SettingsType registry(regPath, QSettings::NativeFormat);
if (!registry.isWritable()) {
*errorString = UpdateOperation::tr("Registry path %1 is not writable.").arg(regPath);
return UpdateOperation::UserDefinedError;
@@ -149,11 +163,21 @@ UpdateOperation::Error undoSetting(const QString &regPath,
{
QString actual;
{
- SettingsType registry(regPath, QSettingsWrapper::NativeFormat);
+ SettingsType registry(regPath, QSettings::NativeFormat);
actual = registry.value(name).toString();
}
- if (actual != value) //key changed, don't undo
- return UpdateOperation::UserDefinedError;
+
+ if (actual != value)
+ {
+ //Ignore the separators
+ static const QRegularExpression regex(QLatin1String("(\\\\|/)"));
+ QString tempValue = value;
+ QString fixedValue = tempValue.replace(regex, QDir::separator());
+ actual = actual.replace(regex, QDir::separator());
+
+ if (actual != fixedValue) //key changed, don't undo
+ return UpdateOperation::UserDefinedError;
+ }
bool error = false;
if (handleRegExpandSz(regPath, name, oldValue, errorString, &error))
@@ -228,7 +252,7 @@ bool EnvironmentVariableOperation::undoOperation()
const bool doUndo = actual == value;
if (doUndo)
Environment::instance().setTemporaryValue(name, oldvalue);
- return doUndo;
+ return true;
}
#ifdef Q_OS_WIN
diff --git a/src/libs/installer/environmentvariablesoperation.h b/src/libs/installer/environmentvariablesoperation.h
index 3c5252bc6..dea2d40c7 100644
--- a/src/libs/installer/environmentvariablesoperation.h
+++ b/src/libs/installer/environmentvariablesoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT EnvironmentVariableOperation : public Operation
public:
explicit EnvironmentVariableOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
}
diff --git a/src/libs/installer/errors.cpp b/src/libs/installer/errors.cpp
new file mode 100644
index 000000000..cbffadc5b
--- /dev/null
+++ b/src/libs/installer/errors.cpp
@@ -0,0 +1,35 @@
+/**************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "errors.h"
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Error
+ \internal
+*/
diff --git a/src/libs/installer/errors.h b/src/libs/installer/errors.h
index 117eb5119..e8f2ee914 100644
--- a/src/libs/installer/errors.h
+++ b/src/libs/installer/errors.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,23 +31,21 @@
#include <QtCore/QDebug>
#include <QtCore/QString>
-
-#include <stdexcept>
+#include <QtCore/QException>
namespace QInstaller {
-class Error : public std::exception
+class Error : public QException
{
public:
- Error()
- {}
+ Error() = default;
explicit Error(const QString &message)
: m_message(message)
{}
- virtual ~Error() throw()
- {}
+ void raise() const override { throw *this; }
+ Error *clone() const override { return new Error(*this); }
QString message() const { return m_message; }
diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp
index 12608a0d1..b00a67190 100644
--- a/src/libs/installer/extractarchiveoperation.cpp
+++ b/src/libs/installer/extractarchiveoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,21 +28,102 @@
#include "extractarchiveoperation_p.h"
+#include "constants.h"
+#include "globals.h"
+#include "fileguard.h"
+
#include <QEventLoop>
#include <QThreadPool>
#include <QFileInfo>
+#include <QDataStream>
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ExtractArchiveOperation
+ \internal
+*/
+
+/*!
+ \typedef QInstaller::ExtractArchiveOperation::Backup
+
+ Synonym for QPair<QString, QString>. Contains a pair
+ of an original and a generated backup filename for a file.
+*/
+
+/*!
+ \typedef QInstaller::ExtractArchiveOperation::BackupFiles
+
+ Synonym for QVector<Backup>.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::WorkerThread
+ \internal
+*/
+
ExtractArchiveOperation::ExtractArchiveOperation(PackageManagerCore *core)
: UpdateOperation(core)
+ , m_totalEntries(0)
{
setName(QLatin1String("Extract"));
+ setGroup(OperationGroup::Unpack);
}
void ExtractArchiveOperation::backup()
{
- // we need to backup on the fly...
+ if (!checkArgumentCount(2))
+ return;
+
+ const QStringList args = arguments();
+ const QString archivePath = args.at(0);
+ const QString targetDir = args.at(1);
+
+ QScopedPointer<AbstractArchive> archive(ArchiveFactory::instance().create(archivePath));
+ if (!archive) {
+ setError(UserDefinedError);
+ setErrorString(tr("Unsupported archive \"%1\": no handler registered for file suffix \"%2\".")
+ .arg(archivePath, QFileInfo(archivePath).suffix()));
+ return;
+ }
+
+ if (!(archive->open(QIODevice::ReadOnly) && archive->isSupported())) {
+ setError(UserDefinedError);
+ setErrorString(tr("Cannot open archive \"%1\" for reading: %2")
+ .arg(archivePath, archive->errorString()));
+ return;
+ }
+ const QVector<ArchiveEntry> entries = archive->list();
+ if (entries.isEmpty()) {
+ setError(UserDefinedError);
+ setErrorString(tr("Error while reading contents of archive \"%1\": %2")
+ .arg(archivePath, archive->errorString()));
+ return;
+ }
+
+ const bool hasAdminRights = (packageManager() && packageManager()->hasAdminRights());
+ const bool canCreateSymLinks = QInstaller::canCreateSymbolicLinks();
+ bool needsAdminRights = false;
+
+ for (auto &entry : entries) {
+ const QString completeFilePath = targetDir + QDir::separator() + entry.path;
+ if (!entry.isDirectory) {
+ // Ignore failed backups, existing files are overwritten when extracting.
+ // Should the backups be used on rollback too, this may not be the
+ // desired behavior anymore.
+ prepareForFile(completeFilePath);
+ }
+ if (!hasAdminRights && !canCreateSymLinks && entry.isSymbolicLink)
+ needsAdminRights = true;
+ }
+ m_totalEntries = entries.size();
+ if (needsAdminRights)
+ setValue(QLatin1String("admin"), true);
+
+ // Show something was done
+ emit progressChanged(scBackupProgressPart);
}
bool ExtractArchiveOperation::performOperation()
@@ -57,38 +138,95 @@ bool ExtractArchiveOperation::performOperation()
Receiver receiver;
Callback callback;
- connect(&callback, &Callback::currentFileChanged, this, &ExtractArchiveOperation::fileFinished);
connect(&callback, &Callback::progressChanged, this, &ExtractArchiveOperation::progressChanged);
- if (PackageManagerCore *core = packageManager()) {
- connect(core, &PackageManagerCore::statusChanged, &callback, &Callback::statusChanged);
- }
-
- Runnable *runnable = new Runnable(archivePath, targetDir, &callback);
- connect(runnable, &Runnable::finished, &receiver, &Receiver::runnableFinished,
+ Worker *worker = new Worker(archivePath, targetDir, m_totalEntries, &callback);
+ connect(worker, &Worker::finished, &receiver, &Receiver::workerFinished,
Qt::QueuedConnection);
- m_files.clear();
+ if (PackageManagerCore *core = packageManager())
+ connect(core, &PackageManagerCore::statusChanged, worker, &Worker::onStatusChanged);
QFileInfo fileInfo(archivePath);
emit outputTextChanged(tr("Extracting \"%1\"").arg(fileInfo.fileName()));
+ {
+ QEventLoop loop;
+ QThread workerThread;
+ worker->moveToThread(&workerThread);
- QEventLoop loop;
- connect(&receiver, &Receiver::finished, &loop, &QEventLoop::quit);
- if (QThreadPool::globalInstance()->tryStart(runnable)) {
+ connect(&workerThread, &QThread::started, worker, &Worker::run);
+ connect(&receiver, &Receiver::finished, &workerThread, &QThread::quit);
+ connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
+ connect(&workerThread, &QThread::finished, &loop, &QEventLoop::quit);
+
+ workerThread.start();
loop.exec();
- } else {
- // HACK: In case there is no availabe thread we should call it directly.
- runnable->run();
- receiver.runnableFinished(true, QString());
}
+ // Write all file names which belongs to a package to a separate file and only the separate
+ // filename to a .dat file. There can be enormous amount of files in a package, which makes
+ // the dat file very slow to read and write. The .dat file is read into memory in startup,
+ // writing the file names to a separate file we don't need to load all the file names into
+ // memory as we need those only in uninstall. This will save a lot of memory.
+ // Parse a file and directorory structure using archivepath syntax
+ // installer://<component_name>/<filename>.7z Resulting structure is:
+ // -installerResources (dir)
+ // -<component_name> (dir)
+ // -<filename>.txt (file)
+
+ QStringList files = callback.extractedFiles();
+
+ QString installDir = targetDir;
+ // If we have package manager in use (normal installer run) then use
+ // TargetDir for saving filenames, otherwise those would be saved to
+ // extracted folder. Also initialize installerbasebinary which we use later
+ // to check if the extracted file in question is the maintenancetool itself.
+ QString installerBaseBinary;
+ if (PackageManagerCore *core = packageManager()) {
+ installDir = core->value(scTargetDir);
+ installerBaseBinary = core->toNativeSeparators(core->replaceVariables(core->installerBaseBinary()));
+ }
+ const QString resourcesPath = installDir + QLatin1Char('/') + QLatin1String("installerResources");
+
+ QString fileDirectory = resourcesPath + QLatin1Char('/') + archivePath.section(QLatin1Char('/'), 1, 1,
+ QString::SectionSkipEmpty) + QLatin1Char('/');
+ QString archiveFileName = archivePath.section(QLatin1Char('/'), 2, 2, QString::SectionSkipEmpty);
+ QFileInfo fileInfo2(archiveFileName);
+ QString suffix = fileInfo2.suffix();
+ archiveFileName.chop(suffix.length() + 1); // removes suffix (e.g. '.7z') from archive filename
+ QString fileName = archiveFileName + QLatin1String(".txt");
+
+ QFileInfo targetDirectoryInfo(fileDirectory);
- setValue(QLatin1String("files"), m_files);
+ QInstaller::createDirectoryWithParents(targetDirectoryInfo.absolutePath());
+ setDefaultFilePermissions(resourcesPath, DefaultFilePermissions::Executable);
+ setDefaultFilePermissions(targetDirectoryInfo.absolutePath(), DefaultFilePermissions::Executable);
+
+ QFile file(targetDirectoryInfo.absolutePath() + QLatin1Char('/') + fileName);
+ if (file.open(QIODevice::WriteOnly)) {
+ setDefaultFilePermissions(file.fileName(), DefaultFilePermissions::NonExecutable);
+ QDataStream out (&file);
+ for (int i = 0; i < files.count(); ++i) {
+ if (!installerBaseBinary.isEmpty() && files[i].startsWith(installerBaseBinary)) {
+ // Do not write installerbase binary filename to extracted files. Installer binary
+ // is maintenance tool program, the binary is removed elsewhere
+ // when we do full uninstall.
+ files.clear();
+ break;
+ }
+ files[i] = replacePath(files.at(i), installDir, QLatin1String(scRelocatable));
+ }
+ if (!files.isEmpty())
+ out << files;
+ setValue(QLatin1String("files"), file.fileName());
+ file.close();
+ } else {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open file for writing " << file.fileName() << ":" << file.errorString();
+ }
// TODO: Use backups for rollback, too? Doesn't work for uninstallation though.
// delete all backups we can delete right now, remember the rest
- foreach (const Backup &i, callback.backupFiles())
+ foreach (const Backup &i, m_backupFiles)
deleteFileNowOrLater(i.second);
if (!receiver.success()) {
@@ -102,19 +240,92 @@ bool ExtractArchiveOperation::performOperation()
bool ExtractArchiveOperation::undoOperation()
{
Q_ASSERT(arguments().count() == 2);
- const QStringList files = value(QLatin1String("files")).toStringList();
+ // For backward compatibility, check if "files" can be converted to QStringList.
+ // If yes, files are listed in .dat instead of in a separate file.
+ bool useStringListType(value(QLatin1String("files")).type() == QVariant::StringList);
+ QString targetDir = arguments().at(1);
+ if (packageManager())
+ targetDir = packageManager()->value(scTargetDir);
+ QStringList files;
+ if (useStringListType) {
+ files = value(QLatin1String("files")).toStringList();
+ } else {
+ if (!readDataFileContents(targetDir, &files))
+ return false;
+ }
+ if (!files.isEmpty())
+ startUndoProcess(files);
+ if (!useStringListType)
+ deleteDataFile(m_relocatedDataFileName);
+
+ // Remove the installerResources directory if it is empty.
+ QDir(targetDir).rmdir(QLatin1String("installerResources"));
+
+ return true;
+}
+
+void ExtractArchiveOperation::startUndoProcess(const QStringList &files)
+{
WorkerThread *const thread = new WorkerThread(this, files);
connect(thread, &WorkerThread::currentFileChanged, this,
&ExtractArchiveOperation::outputTextChanged);
connect(thread, &WorkerThread::progressChanged, this,
&ExtractArchiveOperation::progressChanged);
+ const QFileInfo archive(arguments().at(0));
+ emit outputTextChanged(tr("Removing files extracted from \"%1\"").arg(archive.fileName()));
+
QEventLoop loop;
connect(thread, &QThread::finished, &loop, &QEventLoop::quit, Qt::QueuedConnection);
thread->start();
loop.exec();
thread->deleteLater();
+}
+
+void ExtractArchiveOperation::deleteDataFile(const QString &fileName)
+{
+ if (fileName.isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << Q_FUNC_INFO << "data file name cannot be empty.";
+ return;
+ }
+ QFile file(fileName);
+ QFileInfo fileInfo(file);
+ if (file.remove()) {
+ QDir directory(fileInfo.absoluteDir());
+ if (directory.exists() && directory.isEmpty())
+ directory.rmdir(directory.path());
+ } else {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot remove data file" << file.fileName();
+ }
+}
+
+QString ExtractArchiveOperation::generateBackupName(const QString &fn)
+{
+ const QString bfn = fn + QLatin1String(".tmpUpdate");
+ QString res = bfn;
+ int i = 0;
+ while (QFile::exists(res))
+ res = bfn + QString::fromLatin1(".%1").arg(i++);
+ return res;
+}
+
+bool ExtractArchiveOperation::prepareForFile(const QString &filename)
+{
+ if (!QFile::exists(filename))
+ return true;
+
+ FileGuardLocker locker(filename, FileGuard::globalObject());
+
+ const QString backup = generateBackupName(filename);
+ QFile f(filename);
+ const bool renamed = f.rename(backup);
+ if (f.exists() && !renamed) {
+ qCritical("Cannot rename %s to %s: %s", qPrintable(filename), qPrintable(backup),
+ qPrintable(f.errorString()));
+ return false;
+ }
+ m_backupFiles.append(qMakePair(filename, backup));
return true;
}
@@ -123,13 +334,47 @@ bool ExtractArchiveOperation::testOperation()
return true;
}
-/*!
- This slot is direct connected to the caller so please don't call it from another thread in the
- same time.
-*/
-void ExtractArchiveOperation::fileFinished(const QString &filename)
+quint64 ExtractArchiveOperation::sizeHint()
{
- m_files.prepend(filename);
+ if (!checkArgumentCount(2))
+ return UpdateOperation::sizeHint();
+
+ if (hasValue(QLatin1String("sizeHint")))
+ return value(QLatin1String("sizeHint")).toULongLong();
+
+ const QString archivePath = arguments().at(0);
+ const quint64 compressedSize = QFileInfo(archivePath).size();
+
+ setValue(QLatin1String("sizeHint"), QString::number(compressedSize));
+
+ // A rough estimate of how much time it takes to extract this archive. Other
+ // affecting parameters are the archive format, compression filter and -level.
+ return compressedSize;
+}
+
+bool ExtractArchiveOperation::readDataFileContents(QString &targetDir, QStringList *resultList)
+{
+ const QString filePath = value(QLatin1String("files")).toString();
+ // Does not change target on non macOS platforms.
+ if (QInstaller::isInBundle(targetDir, &targetDir))
+ targetDir = QDir::cleanPath(targetDir + QLatin1String("/.."));
+ m_relocatedDataFileName = replacePath(filePath, QLatin1String(scRelocatable), targetDir);
+ QFile file(m_relocatedDataFileName);
+
+ if (file.open(QIODevice::ReadOnly)) {
+ QDataStream in(&file);
+ in >> *resultList;
+ for (int i = 0; i < resultList->count(); ++i)
+ resultList->replace(i, replacePath(resultList->at(i), QLatin1String(scRelocatable), targetDir));
+
+ } else {
+ // We should not be here. Either user has manually deleted the installer related
+ // files or same component is installed several times.
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open file " << file.fileName() << " for reading:"
+ << file.errorString() << ". Component is already uninstalled "
+ << "or file is manually deleted.";
+ }
+ return true;
}
} // namespace QInstaller
diff --git a/src/libs/installer/extractarchiveoperation.h b/src/libs/installer/extractarchiveoperation.h
index 3e75a9bb9..b1a696788 100644
--- a/src/libs/installer/extractarchiveoperation.h
+++ b/src/libs/installer/extractarchiveoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -43,23 +43,38 @@ class INSTALLER_EXPORT ExtractArchiveOperation : public QObject, public Operatio
public:
explicit ExtractArchiveOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
+
+ quint64 sizeHint() override;
+
+ bool readDataFileContents(QString &targetDir, QStringList *resultList);
Q_SIGNALS:
void outputTextChanged(const QString &progress);
void progressChanged(double);
-private Q_SLOTS:
- void fileFinished(const QString &progress);
+private:
+ void startUndoProcess(const QStringList &files);
+ void deleteDataFile(const QString &fileName);
+
+ QString generateBackupName(const QString &fn);
+ bool prepareForFile(const QString &filename);
private:
- QStringList m_files;
+ typedef QPair<QString, QString> Backup;
+ typedef QVector<Backup> BackupFiles;
+
class Callback;
- class Runnable;
+ class Worker;
class Receiver;
+
+private:
+ QString m_relocatedDataFileName;
+ BackupFiles m_backupFiles;
+ quint64 m_totalEntries;
};
}
diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h
index f333da366..24614de39 100644
--- a/src/libs/installer/extractarchiveoperation_p.h
+++ b/src/libs/installer/extractarchiveoperation_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,15 +31,22 @@
#include "extractarchiveoperation.h"
#include "fileutils.h"
-#include "lib7z_extract.h"
-#include "lib7z_facade.h"
+#include "archivefactory.h"
#include "packagemanagercore.h"
+#include "remoteclient.h"
+#include "adminauthorization.h"
+#include "utils.h"
+#include "errors.h"
+#include "loggingutils.h"
#include <QRunnable>
#include <QThread>
namespace QInstaller {
+constexpr double scBackupProgressPart = 0.1;
+constexpr double scPerformProgressPart = (1 - scBackupProgressPart);
+
class WorkerThread : public QThread
{
Q_OBJECT
@@ -53,24 +60,45 @@ public:
setObjectName(QLatin1String("ExtractArchive"));
}
- void run()
+ void run() override
{
Q_ASSERT(m_op != 0);
+ QStringList directories;
+
int removedCounter = 0;
foreach (const QString &file, m_files) {
removedCounter++;
const QFileInfo fi(file);
- emit currentFileChanged(QDir::toNativeSeparators(file));
+ if (LoggingHandler::instance().verboseLevel() == LoggingHandler::Detailed)
+ emit currentFileChanged(QDir::toNativeSeparators(file));
emit progressChanged(double(removedCounter) / m_files.count());
if (fi.isFile() || fi.isSymLink()) {
m_op->deleteFileNowOrLater(fi.absoluteFilePath());
} else if (fi.isDir()) {
- removeSystemGeneratedFiles(file);
- fi.dir().rmdir(file); // directory may not exist
+ directories.append(file);
}
}
+
+ std::sort(directories.begin(), directories.end(), [](const QString &lhs, const QString &rhs) {
+ // Doesn't match the original creation order, nor will the sorted list be a logical
+ // directory tree. Only requirement is that subdirectories get removed first.
+ const int lhsParts = QDir::fromNativeSeparators(lhs)
+ .split(QLatin1Char('/'), Qt::SkipEmptyParts).size();
+ const int rhsParts = QDir::fromNativeSeparators(rhs)
+ .split(QLatin1Char('/'), Qt::SkipEmptyParts).size();
+
+ if (lhsParts == rhsParts)
+ return lhs < rhs;
+
+ return lhsParts > rhsParts;
+ });
+
+ for (auto &directory : qAsConst(directories)) {
+ removeSystemGeneratedFiles(directory);
+ QDir(directory).rmdir(directory); // directory may not exist
+ }
}
signals:
@@ -82,124 +110,114 @@ private:
ExtractArchiveOperation *m_op;
};
-typedef QPair<QString, QString> Backup;
-typedef QVector<Backup> BackupFiles;
-
-class ExtractArchiveOperation::Callback : public QObject, public Lib7z::ExtractCallback
+class ExtractArchiveOperation::Callback : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(Callback)
public:
- Callback() = default;
-
- BackupFiles backupFiles() const {
- return m_backupFiles;
- }
+ Callback()
+ : m_lastProgressPercentage(0)
+ {}
-public slots:
- void statusChanged(QInstaller::PackageManagerCore::Status status)
+ QStringList extractedFiles() const
{
- switch(status) {
- case PackageManagerCore::Canceled:
- m_state = E_ABORT;
- break;
- case PackageManagerCore::Failure:
- m_state = E_FAIL;
- break;
- default: // ignore all other status values
- break;
- }
+ return m_extractedFiles;
}
-signals:
- void currentFileChanged(const QString &filename);
+Q_SIGNALS:
void progressChanged(double progress);
-private:
- void setCurrentFile(const QString &filename) Q_DECL_OVERRIDE
- {
- emit currentFileChanged(QDir::toNativeSeparators(filename));
- }
-
- static QString generateBackupName(const QString &fn)
+public Q_SLOTS:
+ void onCurrentEntryChanged(const QString &filename)
{
- const QString bfn = fn + QLatin1String(".tmpUpdate");
- QString res = bfn;
- int i = 0;
- while (QFile::exists(res))
- res = bfn + QString::fromLatin1(".%1").arg(i++);
- return res;
+ m_extractedFiles.prepend(QDir::toNativeSeparators(filename));
}
- bool prepareForFile(const QString &filename) Q_DECL_OVERRIDE
+ void onCompletedChanged(quint64 completed, quint64 total)
{
- if (!QFile::exists(filename))
- return true;
- const QString backup = generateBackupName(filename);
- QFile f(filename);
- const bool renamed = f.rename(backup);
- if (f.exists() && !renamed) {
- qCritical("Cannot rename %s to %s: %s", qPrintable(filename), qPrintable(backup),
- qPrintable(f.errorString()));
- return false;
+ const double currentProgress = double(completed) / total
+ * scPerformProgressPart + scBackupProgressPart;
+
+ const int currentProgressPercentage = qRound(currentProgress * 100);
+ if (currentProgressPercentage > m_lastProgressPercentage) {
+ // Emit only full percentage changes
+ m_lastProgressPercentage = currentProgressPercentage;
+ emit progressChanged(currentProgress);
}
- m_backupFiles.append(qMakePair(filename, backup));
- return true;
- }
-
- HRESULT setCompleted(quint64 completed, quint64 total) Q_DECL_OVERRIDE
- {
- emit progressChanged(double(completed) / total);
- return m_state;
}
private:
- HRESULT m_state = S_OK;
- BackupFiles m_backupFiles;
+ QStringList m_extractedFiles;
+ int m_lastProgressPercentage;
};
-class ExtractArchiveOperation::Runnable : public QObject, public QRunnable
+class ExtractArchiveOperation::Worker : public QObject
{
Q_OBJECT
- Q_DISABLE_COPY(Runnable)
+ Q_DISABLE_COPY(Worker)
public:
- Runnable(const QString &archivePath, const QString &targetDir,
- ExtractArchiveOperation::Callback *callback)
+ Worker(const QString &archivePath, const QString &targetDir, quint64 totalEntries, Callback *callback)
: m_archivePath(archivePath)
, m_targetDir(targetDir)
+ , m_totalEntries(totalEntries)
, m_callback(callback)
{}
+Q_SIGNALS:
+ void finished(bool success, const QString &errorString);
+
+public Q_SLOTS:
void run()
{
- QFile archive(m_archivePath);
- if (!archive.open(QIODevice::ReadOnly)) {
+ m_archive.reset(ArchiveFactory::instance().create(m_archivePath));
+ if (!m_archive) {
+ emit finished(false, tr("Could not create handler object for archive \"%1\": \"%2\".")
+ .arg(m_archivePath, QLatin1String(Q_FUNC_INFO)));
+ return;
+ }
+
+ connect(m_archive.get(), &AbstractArchive::currentEntryChanged, m_callback, &Callback::onCurrentEntryChanged);
+ connect(m_archive.get(), &AbstractArchive::completedChanged, m_callback, &Callback::onCompletedChanged);
+
+ if (!m_archive->open(QIODevice::ReadOnly)) {
emit finished(false, tr("Cannot open archive \"%1\" for reading: %2").arg(m_archivePath,
- archive.errorString()));
+ m_archive->errorString()));
return;
}
- try {
- Lib7z::extractArchive(&archive, m_targetDir, m_callback);
- emit finished(true, QString());
- } catch (const Lib7z::SevenZipException& e) {
+ if (!m_archive->extract(m_targetDir, m_totalEntries)) {
emit finished(false, tr("Error while extracting archive \"%1\": %2").arg(m_archivePath,
- e.message()));
- } catch (...) {
- emit finished(false, tr("Unknown exception caught while extracting \"%1\".")
- .arg(m_archivePath));
+ m_archive->errorString()));
+ } else {
+ emit finished(true, QString());
}
}
-signals:
- void finished(bool success, const QString &errorString);
+ void onStatusChanged(PackageManagerCore::Status status)
+ {
+ if (!m_archive)
+ return;
+
+ switch (status) {
+ case PackageManagerCore::Canceled:
+ m_archive->cancel();
+ break;
+ case PackageManagerCore::Failure:
+ m_archive->cancel();
+ break;
+ default: // ignore all other status values
+ break;
+ }
+ }
private:
QString m_archivePath;
QString m_targetDir;
- ExtractArchiveOperation::Callback *m_callback;
+ quint64 m_totalEntries;
+ QScopedPointer<AbstractArchive> m_archive;
+ Callback *m_callback;
};
class ExtractArchiveOperation::Receiver : public QObject
@@ -219,7 +237,7 @@ public:
}
public slots:
- void runnableFinished(bool ok, const QString &msg)
+ void workerFinished(bool ok, const QString &msg)
{
m_success = ok;
m_errorString = msg;
diff --git a/src/libs/installer/fakestopprocessforupdateoperation.cpp b/src/libs/installer/fakestopprocessforupdateoperation.cpp
index 69870fcab..bdd8625eb 100644
--- a/src/libs/installer/fakestopprocessforupdateoperation.cpp
+++ b/src/libs/installer/fakestopprocessforupdateoperation.cpp
@@ -31,9 +31,17 @@
#include "messageboxhandler.h"
#include "packagemanagercore.h"
+#include <QDir>
+
using namespace KDUpdater;
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::FakeStopProcessForUpdateOperation
+ \internal
+*/
+
FakeStopProcessForUpdateOperation::FakeStopProcessForUpdateOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -62,7 +70,7 @@ bool FakeStopProcessForUpdateOperation::undoOperation()
return false;
}
- QStringList processes = arguments().at(0).split(QLatin1Char(','), QString::SkipEmptyParts);
+ QStringList processes = arguments().at(0).split(QLatin1Char(','), Qt::SkipEmptyParts);
for (int i = processes.count() - 1; i >= 0; --i) {
if (!core->isProcessRunning(processes.at(i)))
processes.removeAt(i);
@@ -73,11 +81,11 @@ bool FakeStopProcessForUpdateOperation::undoOperation()
if (processes.count() == 1) {
setError(UpdateOperation::UserDefinedError, tr("This process should be stopped before "
- "continuing: %1").arg(processes.first()));
+ "continuing: %1").arg(QDir::toNativeSeparators(processes.first())));
} else {
const QString sep = QString::fromWCharArray(L"\n \u2022 "); // Unicode bullet
setError(UpdateOperation::UserDefinedError, tr("These processes should be stopped before "
- "continuing: %1").arg(sep + processes.join(sep)));
+ "continuing: %1").arg(sep + QDir::toNativeSeparators(processes.join(sep))));
}
return false;
}
diff --git a/src/libs/installer/fakestopprocessforupdateoperation.h b/src/libs/installer/fakestopprocessforupdateoperation.h
index 6fd5da44b..546c6e95c 100644
--- a/src/libs/installer/fakestopprocessforupdateoperation.h
+++ b/src/libs/installer/fakestopprocessforupdateoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,10 +40,10 @@ class INSTALLER_EXPORT FakeStopProcessForUpdateOperation : public QObject, publi
public:
explicit FakeStopProcessForUpdateOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
}
diff --git a/src/libs/installer/fileguard.cpp b/src/libs/installer/fileguard.cpp
new file mode 100644
index 000000000..1f5fb46b7
--- /dev/null
+++ b/src/libs/installer/fileguard.cpp
@@ -0,0 +1,120 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "fileguard.h"
+
+#include <QTimer>
+
+using namespace QInstaller;
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::FileGuard
+ \brief The \c FileGuard class provides basic access serialization for file paths.
+
+ This class keeps a list of file paths that are locked from mutual
+ access. Attempting to lock them from another thread will fail until the
+ the locked path name is released.
+*/
+
+Q_GLOBAL_STATIC(FileGuard, globalFileGuard)
+
+/*!
+ Attempts to lock \a path. Returns \c true if the lock could be
+ acquired, \c false if another thread has already locked the path.
+*/
+bool FileGuard::tryLock(const QString &path)
+{
+ QMutexLocker _(&m_mutex);
+ if (path.isEmpty())
+ return false;
+
+ if (m_paths.contains(path))
+ return false;
+
+ m_paths.append(path);
+ return true;
+}
+
+/*!
+ Unlocks \a path.
+*/
+void FileGuard::release(const QString &path)
+{
+ QMutexLocker _(&m_mutex);
+ m_paths.removeOne(path);
+}
+
+/*!
+ Returns the application global instance.
+*/
+FileGuard *FileGuard::globalObject()
+{
+ return globalFileGuard;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::FileGuardLocker
+ \brief The \c FileGuardLocker class locks a file path and releases it on destruction.
+
+ A convenience class for locking a file path using the resource acquisition
+ is initialization (RAII) programming idiom.
+*/
+
+/*!
+ Constructs the object and attempts to lock \a path with \a guard. If the lock is already
+ held by another thread, this method will wait for it to become available.
+*/
+FileGuardLocker::FileGuardLocker(const QString &path, FileGuard *guard)
+ : m_path(path)
+ , m_guard(guard)
+{
+ if (!m_guard->tryLock(m_path)) {
+ QTimer timer;
+ QEventLoop loop;
+
+ QObject::connect(&timer, &QTimer::timeout, [&]() {
+ if (m_guard->tryLock(m_path))
+ loop.quit();
+ });
+
+ timer.start(100);
+ loop.exec();
+ }
+}
+
+/*!
+ Destructs the object and unlocks the locked file path.
+*/
+FileGuardLocker::~FileGuardLocker()
+{
+ m_guard->release(m_path);
+}
+
+
diff --git a/src/libs/installer/fileguard.h b/src/libs/installer/fileguard.h
new file mode 100644
index 000000000..a2f96a16e
--- /dev/null
+++ b/src/libs/installer/fileguard.h
@@ -0,0 +1,66 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef FILEGUARD_H
+#define FILEGUARD_H
+
+#include "qinstallerglobal.h"
+
+#include <QMutex>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT FileGuard
+{
+public:
+ FileGuard() = default;
+
+ bool tryLock(const QString &path);
+ void release(const QString &path);
+
+ static FileGuard *globalObject();
+
+private:
+ QMutex m_mutex;
+ QStringList m_paths;
+};
+
+class INSTALLER_EXPORT FileGuardLocker
+{
+public:
+ explicit FileGuardLocker(const QString &path, FileGuard *guard);
+ ~FileGuardLocker();
+
+private:
+ QString m_path;
+ FileGuard *m_guard;
+};
+
+} // namespace QInstaller
+
+#endif // FILEGUARD_H
diff --git a/src/libs/installer/fileio.cpp b/src/libs/installer/fileio.cpp
index dac2e4c50..6f55ba4ff 100644
--- a/src/libs/installer/fileio.cpp
+++ b/src/libs/installer/fileio.cpp
@@ -37,6 +37,9 @@
#include <QFileDevice>
#include <QString>
+/*!
+ \internal
+*/
qint64 QInstaller::retrieveInt64(QFileDevice *in)
{
qint64 n = 0;
@@ -44,11 +47,17 @@ qint64 QInstaller::retrieveInt64(QFileDevice *in)
return n;
}
+/*!
+ \internal
+*/
void QInstaller::appendInt64(QFileDevice *out, qint64 n)
{
QInstaller::blockingWrite(out, reinterpret_cast<const char*>(&n), sizeof(n));
}
+/*!
+ \internal
+*/
Range<qint64> QInstaller::retrieveInt64Range(QFileDevice *in)
{
const quint64 start = QInstaller::retrieveInt64(in);
@@ -56,22 +65,34 @@ Range<qint64> QInstaller::retrieveInt64Range(QFileDevice *in)
return Range<qint64>::fromStartAndLength(start, length);
}
+/*!
+ \internal
+*/
void QInstaller::appendInt64Range(QFileDevice *out, const Range<qint64> &r)
{
QInstaller::appendInt64(out, r.start());
QInstaller::appendInt64(out, r.length());
}
+/*!
+ \internal
+*/
QString QInstaller::retrieveString(QFileDevice *in)
{
return QString::fromUtf8(QInstaller::retrieveByteArray(in));
}
+/*!
+ \internal
+*/
void QInstaller::appendString(QFileDevice *out, const QString &str)
{
QInstaller::appendByteArray(out, str.toUtf8());
}
+/*!
+ \internal
+*/
QByteArray QInstaller::retrieveByteArray(QFileDevice *in)
{
QByteArray ba(QInstaller::retrieveInt64(in), '\0');
@@ -79,12 +100,18 @@ QByteArray QInstaller::retrieveByteArray(QFileDevice *in)
return ba;
}
+/*!
+ \internal
+*/
void QInstaller::appendByteArray(QFileDevice *out, const QByteArray &ba)
{
QInstaller::appendInt64(out, ba.size());
QInstaller::blockingWrite(out, ba.constData(), ba.size());
}
+/*!
+ \internal
+*/
QByteArray QInstaller::retrieveData(QFileDevice *in, qint64 size)
{
QByteArray ba(size, '\0');
@@ -92,12 +119,18 @@ QByteArray QInstaller::retrieveData(QFileDevice *in, qint64 size)
return ba;
}
+/*!
+ \internal
+*/
void QInstaller::appendData(QFileDevice *out, QFileDevice *in, qint64 size)
{
Q_ASSERT(!in->isSequential());
QInstaller::blockingCopy(in, out, size);
}
+/*!
+ \internal
+*/
void QInstaller::openForRead(QFileDevice *dev)
{
Q_ASSERT(dev);
@@ -108,6 +141,9 @@ void QInstaller::openForRead(QFileDevice *dev)
}
}
+/*!
+ \internal
+*/
void QInstaller::openForWrite(QFileDevice *dev)
{
Q_ASSERT(dev);
@@ -118,6 +154,9 @@ void QInstaller::openForWrite(QFileDevice *dev)
}
}
+/*!
+ \internal
+*/
void QInstaller::openForAppend(QFileDevice *dev)
{
Q_ASSERT(dev);
@@ -128,6 +167,9 @@ void QInstaller::openForAppend(QFileDevice *dev)
}
}
+/*!
+ \internal
+*/
qint64 QInstaller::blockingRead(QFileDevice *in, char *buffer, qint64 size)
{
if (in->atEnd())
@@ -146,6 +188,9 @@ qint64 QInstaller::blockingRead(QFileDevice *in, char *buffer, qint64 size)
return size;
}
+/*!
+ \internal
+*/
qint64 QInstaller::blockingCopy(QFileDevice *in, QFileDevice *out, qint64 size)
{
static const qint64 blockSize = 4096;
@@ -165,11 +210,17 @@ qint64 QInstaller::blockingCopy(QFileDevice *in, QFileDevice *out, qint64 size)
return size;
}
+/*!
+ \internal
+*/
qint64 QInstaller::blockingWrite(QFileDevice *out, const QByteArray &data)
{
return QInstaller::blockingWrite(out, data.constData(), data.size());
}
+/*!
+ \internal
+*/
qint64 QInstaller::blockingWrite(QFileDevice *out, const char *data, qint64 size)
{
qint64 left = size;
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp
index 18c5f90fd..044eeb34f 100644
--- a/src/libs/installer/fileutils.cpp
+++ b/src/libs/installer/fileutils.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -27,6 +27,9 @@
**************************************************************************/
#include "fileutils.h"
+#include "globals.h"
+#include "constants.h"
+#include "fileio.h"
#include <errors.h>
#include <QtCore/QDateTime>
@@ -38,6 +41,9 @@
#include <QtCore/QUrl>
#include <QtCore/QCoreApplication>
#include <QImageReader>
+#include <QRandomGenerator>
+#include <QGuiApplication>
+#include <QScreen>
#include <errno.h>
@@ -49,73 +55,73 @@
using namespace QInstaller;
+/*!
+ \enum QInstaller::DefaultFilePermissions
+
+ \value NonExecutable
+ Default permissions for a non-executable file.
+ \value Executable
+ Default permissions for an executable file.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::TempPathDeleter
+ \internal
+*/
// -- TempDirDeleter
-TempDirDeleter::TempDirDeleter(const QString &path)
+TempPathDeleter::TempPathDeleter(const QString &path)
{
m_paths.insert(path);
}
-TempDirDeleter::TempDirDeleter(const QStringList &paths)
- : m_paths(paths.toSet())
+TempPathDeleter::TempPathDeleter(const QStringList &paths)
+ : m_paths(QSet<QString>(paths.begin(), paths.end()))
{
}
-TempDirDeleter::~TempDirDeleter()
+TempPathDeleter::~TempPathDeleter()
{
releaseAndDeleteAll();
}
-QStringList TempDirDeleter::paths() const
+QStringList TempPathDeleter::paths() const
{
- return m_paths.toList();
+ return m_paths.values();
}
-void TempDirDeleter::add(const QString &path)
+void TempPathDeleter::add(const QString &path)
{
m_paths.insert(path);
}
-void TempDirDeleter::add(const QStringList &paths)
-{
- m_paths += paths.toSet();
-}
-
-void TempDirDeleter::releaseAll()
-{
- m_paths.clear();
-}
-
-void TempDirDeleter::release(const QString &path)
-{
- m_paths.remove(path);
-}
-
-void TempDirDeleter::passAndReleaseAll(TempDirDeleter &tdd)
-{
- tdd.m_paths = m_paths;
- releaseAll();
-}
-
-void TempDirDeleter::passAndRelease(TempDirDeleter &tdd, const QString &path)
+void TempPathDeleter::add(const QStringList &paths)
{
- tdd.add(path);
- release(path);
+ m_paths += QSet<QString>(paths.begin(), paths.end());
}
-void TempDirDeleter::releaseAndDeleteAll()
+void TempPathDeleter::releaseAndDeleteAll()
{
foreach (const QString &path, m_paths)
releaseAndDelete(path);
}
-void TempDirDeleter::releaseAndDelete(const QString &path)
+void TempPathDeleter::releaseAndDelete(const QString &path)
{
if (m_paths.contains(path)) {
try {
m_paths.remove(path);
- removeDirectory(path);
+ if (QFileInfo(path).isDir()) {
+ removeDirectory(path);
+ return;
+ }
+ QFile file(path);
+ if (file.exists() && !file.remove()) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot remove file \"%1\": %2").arg(file.fileName(), file.errorString()));
+ }
} catch (const Error &e) {
qCritical() << Q_FUNC_INFO << "Exception caught:" << e.message();
} catch (...) {
@@ -124,6 +130,10 @@ void TempDirDeleter::releaseAndDelete(const QString &path)
}
}
+/*!
+ Returns the given \a size in a measuring unit suffixed human readable format,
+ with \a precision marking the number of shown decimals.
+*/
QString QInstaller::humanReadableSize(const qint64 &size, int precision)
{
double sizeAsDouble = size;
@@ -153,11 +163,17 @@ QString QInstaller::humanReadableSize(const qint64 &size, int precision)
// -- read, write operations
+/*!
+ \internal
+*/
bool QInstaller::isLocalUrl(const QUrl &url)
{
return url.scheme().isEmpty() || url.scheme().toLower() == QLatin1String("file");
}
+/*!
+ \internal
+*/
QString QInstaller::pathFromUrl(const QUrl &url)
{
if (isLocalUrl(url))
@@ -168,6 +184,9 @@ QString QInstaller::pathFromUrl(const QUrl &url)
return str;
}
+/*!
+ \internal
+*/
void QInstaller::removeFiles(const QString &path, bool ignoreErrors)
{
const QFileInfoList entries = QDir(path).entryInfoList(QDir::AllEntries | QDir::Hidden);
@@ -188,7 +207,7 @@ void QInstaller::removeFiles(const QString &path, bool ignoreErrors)
QDir::toNativeSeparators(f.fileName()), f.errorString());
if (!ignoreErrors)
throw Error(errorMessage);
- qWarning().noquote() << errorMessage;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
}
}
}
@@ -207,6 +226,16 @@ static QString errnoToQString(int error)
#endif
}
+/*!
+ \internal
+
+ Removes the directory at \a path recursively.
+ @param path The directory to remove
+ @param ignoreErrors if @p true, errors will be silently ignored. Otherwise an exception will be thrown
+ if removing fails.
+
+ @throws QInstaller::Error if the directory cannot be removed and ignoreErrors is @p false
+*/
void QInstaller::removeDirectory(const QString &path, bool ignoreErrors)
{
if (path.isEmpty()) // QDir("") points to the working directory! We never want to remove that one.
@@ -231,7 +260,7 @@ void QInstaller::removeDirectory(const QString &path, bool ignoreErrors)
errnoToQString(errno));
if (!ignoreErrors)
throw Error(errorMessage);
- qWarning().noquote() << errorMessage;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
}
}
}
@@ -253,7 +282,7 @@ public:
}
protected:
- /*!
+ /*
\reimp
*/
void run()
@@ -271,6 +300,9 @@ private:
const bool ignore;
};
+/*!
+ \internal
+*/
void QInstaller::removeDirectoryThreaded(const QString &path, bool ignoreErrors)
{
RemoveDirectoryThread thread(path, ignoreErrors);
@@ -282,6 +314,9 @@ void QInstaller::removeDirectoryThreaded(const QString &path, bool ignoreErrors)
throw Error(thread.error());
}
+/*!
+ Removes system generated files from \a path on Windows and macOS. Does nothing on Linux.
+*/
void QInstaller::removeSystemGeneratedFiles(const QString &path)
{
if (path.isEmpty())
@@ -293,10 +328,50 @@ void QInstaller::removeSystemGeneratedFiles(const QString &path)
#endif
}
+/*!
+ Sets permissions of file or directory specified by \a fileName to \c 644 or \c 755
+ based by the value of \a permissions.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool QInstaller::setDefaultFilePermissions(const QString &fileName, DefaultFilePermissions permissions)
+{
+ QFile file(fileName);
+ return setDefaultFilePermissions(&file, permissions);
+}
+
+/*!
+ Sets permissions of file or directory specified by \a file to \c 644 or \c 755
+ based by the value of \a permissions. This is effective only on Unix platforms
+ as \c setPermissions() does not manipulate ACLs. On Windows NTFS volumes this
+ only unsets the legacy read-only flag regardless of the value of \a permissions.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool QInstaller::setDefaultFilePermissions(QFile *file, DefaultFilePermissions permissions)
+{
+ if (!file->exists()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Target" << file->fileName() << "does not exists.";
+ return false;
+ }
+ if (file->permissions() == static_cast<QFileDevice::Permission>(permissions))
+ return true;
+
+ if (!file->setPermissions(static_cast<QFileDevice::Permission>(permissions))) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot set default permissions for target"
+ << file->fileName() << ":" << file->errorString();
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &targetDir)
{
Q_ASSERT(QFileInfo(sourceDir).isDir());
- Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ Q_ASSERT(!QFileInfo::exists(targetDir) || QFileInfo(targetDir).isDir());
if (!QDir().mkpath(targetDir)) {
throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".")
.arg(QDir::toNativeSeparators(targetDir)));
@@ -321,10 +396,13 @@ void QInstaller::copyDirectoryContents(const QString &sourceDir, const QString &
}
}
+/*!
+ \internal
+*/
void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &targetDir)
{
Q_ASSERT(QFileInfo(sourceDir).isDir());
- Q_ASSERT(!QFileInfo(targetDir).exists() || QFileInfo(targetDir).isDir());
+ Q_ASSERT(!QFileInfo::exists(targetDir) || QFileInfo(targetDir).isDir());
if (!QDir().mkpath(targetDir)) {
throw Error(QCoreApplication::translate("QInstaller", "Cannot create directory \"%1\".")
.arg(QDir::toNativeSeparators(targetDir)));
@@ -352,6 +430,9 @@ void QInstaller::moveDirectoryContents(const QString &sourceDir, const QString &
}
}
+/*!
+ \internal
+*/
void QInstaller::mkdir(const QString &path)
{
errno = 0;
@@ -361,6 +442,9 @@ void QInstaller::mkdir(const QString &path)
}
}
+/*!
+ \internal
+*/
void QInstaller::mkpath(const QString &path)
{
errno = 0;
@@ -370,6 +454,58 @@ void QInstaller::mkpath(const QString &path)
}
}
+/*!
+ \internal
+
+ Creates directory \a path including all parent directories. Return \c true on
+ success, \c false otherwise.
+
+ On Windows \c QDir::mkpath() doesn't check if the leading directories were created
+ elsewhere (i.e. in another thread) after the initial check that the given path
+ requires creating also parent directories, and returns \c false.
+
+ On Unix platforms this case is handled different by QFileSystemEngine though,
+ which checks for \c EEXIST error code in case any of the recursive directories
+ could not be created.
+
+ Compared to \c QInstaller::mkpath() and \c QDir::mkpath() this function checks if
+ each parent directory to-be-created were created elsewhere.
+*/
+bool QInstaller::createDirectoryWithParents(const QString &path)
+{
+ if (path.isEmpty())
+ return false;
+
+ QFileInfo dirInfo(path);
+ if (dirInfo.exists() && dirInfo.isDir())
+ return true;
+
+ // bail out if we are at the root directory
+ if (dirInfo.isRoot())
+ return false;
+
+ QDir dir(path);
+ if (dir.exists() || dir.mkdir(path))
+ return true;
+
+ // mkdir failed, try to create the parent directory
+ if (!createDirectoryWithParents(dirInfo.absolutePath()))
+ return false;
+
+ // now try again
+ if (dir.exists() || dir.mkdir(path))
+ return true;
+
+ // directory may be have also been created elsewhere
+ return (dirInfo.exists() && dirInfo.isDir());
+}
+
+/*!
+ \internal
+
+ Generates and returns a temporary file name. The name can start with
+ a template \a templ.
+*/
QString QInstaller::generateTemporaryFileName(const QString &templ)
{
if (templ.isEmpty()) {
@@ -383,9 +519,8 @@ QString QInstaller::generateTemporaryFileName(const QString &templ)
static const QString characters = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
QString suffix;
- qsrand(qrand() * QDateTime::currentDateTime().toTime_t());
for (int i = 0; i < 5; ++i)
- suffix += characters[qrand() % characters.length()];
+ suffix += characters[QRandomGenerator::global()->generate() % characters.length()];
const QString tmp = QLatin1String("%1.tmp.%2.%3");
int count = 1;
@@ -404,6 +539,9 @@ QString QInstaller::generateTemporaryFileName(const QString &templ)
#ifdef Q_OS_WIN
#include <qt_windows.h>
+/*!
+ \internal
+*/
QString QInstaller::getShortPathName(const QString &name)
{
if (name.isEmpty())
@@ -420,6 +558,9 @@ QString QInstaller::getShortPathName(const QString &name)
return rc;
}
+/*!
+ \internal
+*/
QString QInstaller::getLongPathName(const QString &name)
{
if (name.isEmpty())
@@ -436,6 +577,11 @@ QString QInstaller::getLongPathName(const QString &name)
return rc;
}
+/*!
+ \internal
+
+ Makes sure that capitalization of directory specified by \a name is canonical.
+*/
QString QInstaller::normalizePathName(const QString &name)
{
QString canonicalName = getShortPathName(name);
@@ -492,17 +638,23 @@ typedef struct {
#pragma pack(pop)
+/*!
+ \internal
+
+ Sets the .ico file at \a icon as application icon for \a application.
+*/
void QInstaller::setApplicationIcon(const QString &application, const QString &icon)
{
QFile iconFile(icon);
if (!iconFile.open(QIODevice::ReadOnly)) {
- qWarning() << "Cannot use" << icon << "as an application icon:" << iconFile.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot use" << icon << "as an application icon:"
+ << iconFile.errorString();
return;
}
if (QImageReader::imageFormat(icon) != "ico") {
- qWarning() << "Cannot use" << icon << "as an application icon, unsupported format"
- << QImageReader::imageFormat(icon).constData();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot use" << icon << "as an application icon, "
+ "unsupported format" << QImageReader::imageFormat(icon).constData();
return;
}
@@ -555,6 +707,9 @@ static quint64 symlinkSizeWin(const QString &path)
#endif
+/*!
+ \internal
+*/
quint64 QInstaller::fileSize(const QFileInfo &info)
{
if (!info.isSymLink())
@@ -570,10 +725,16 @@ quint64 QInstaller::fileSize(const QFileInfo &info)
#endif
}
+/*!
+ \internal
+
+ Returns \c true if a file specified by \a path points to a bundle. The
+ absolute path for the bundle can be retrieved with \a bundlePath. Works only on macOS.
+*/
bool QInstaller::isInBundle(const QString &path, QString *bundlePath)
{
#ifdef Q_OS_MACOS
- QFileInfo fi = QFileInfo(path).absoluteFilePath();
+ QFileInfo fi(QFileInfo(path).absoluteFilePath());
while (!fi.isRoot()) {
if (fi.isBundle()) {
if (bundlePath)
@@ -592,15 +753,131 @@ bool QInstaller::isInBundle(const QString &path, QString *bundlePath)
/*!
Replaces the path \a before with the path \a after at the beginning of \a path and returns
the replaced path. If \a before cannot be found in \a path, the original value is returned.
+ If \a cleanPath is \c true, path is returned with directory separators normalized (that is,
+ platform-native separators converted to "/") and redundant ones removed, and "."s and ".."s
+ resolved (as far as possible). If \a cleanPath is \c false, path is returned as such. Default
+ value is \c true.
*/
-QString QInstaller::replacePath(const QString &path, const QString &before, const QString &after)
+QString QInstaller::replacePath(const QString &path, const QString &before, const QString &after, bool cleanPath)
{
if (path.isEmpty() || before.isEmpty())
return path;
QString pathToPatch = QDir::cleanPath(path);
const QString pathToReplace = QDir::cleanPath(before);
- if (pathToPatch.startsWith(pathToReplace))
- return QDir::cleanPath(after) + pathToPatch.mid(pathToReplace.size());
+ if (pathToPatch.startsWith(pathToReplace)) {
+ if (cleanPath)
+ return QDir::cleanPath(after) + pathToPatch.mid(pathToReplace.size());
+ else
+ return after + path.mid(before.size());
+ }
return path;
}
+
+/*!
+ Replaces \a imagePath with high dpi image. If high dpi image is not provided or
+ high dpi screen is not in use, the original value is returned.
+*/
+void QInstaller::replaceHighDpiImage(QString &imagePath)
+{
+ if (QGuiApplication::primaryScreen()->devicePixelRatio() >= 2 ) {
+ QFileInfo fi(imagePath);
+ QString highdpiPixmap = fi.absolutePath() + QLatin1Char('/') + fi.baseName() + scHighDpi + fi.suffix();
+ if (QFileInfo::exists(highdpiPixmap))
+ imagePath = highdpiPixmap;
+ }
+}
+
+/*!
+ Copies an internal configuration file from \a source to \a target. The XML elements,
+ and their children, specified by \a elementsToRemoveTags will be removed from the \a target
+ file. All relative filenames referenced in the \a source configuration file will be
+ also copied to the location of the \a target file.
+
+ Throws \c QInstaller::Error in case of failure.
+*/
+void QInstaller::trimmedCopyConfigData(const QString &source, const QString &target, const QStringList &elementsToRemoveTags)
+{
+ qCDebug(QInstaller::lcDeveloperBuild) << "Copying configuration file and associated data.";
+
+ const QString targetPath = QFileInfo(target).absolutePath();
+ if (!QDir(targetPath).exists() && !QDir().mkpath(targetPath)) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot create directory \"%1\".").arg(targetPath));
+ }
+
+ QFile xmlFile(source);
+ if (!xmlFile.copy(target)) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot copy file \"%1\" to \"%2\": %3").arg(source, target, xmlFile.errorString()));
+ }
+ setDefaultFilePermissions(target, DefaultFilePermissions::NonExecutable);
+
+ xmlFile.setFileName(target);
+ QInstaller::openForRead(&xmlFile); // throws in case of error
+
+ QDomDocument dom;
+ dom.setContent(&xmlFile);
+ xmlFile.close();
+
+ foreach (auto elementTag, elementsToRemoveTags) {
+ QDomNodeList elementsToRemove = dom.elementsByTagName(elementTag);
+ for (int i = 0; i < elementsToRemove.length(); i++) {
+ QDomNode elementToRemove = elementsToRemove.item(i);
+ elementToRemove.parentNode().removeChild(elementToRemove);
+
+ qCDebug(QInstaller::lcDeveloperBuild) << "Removed dom element from target file:"
+ << elementToRemove.toElement().text();
+ }
+ }
+
+ const QDomNodeList children = dom.documentElement().childNodes();
+ copyConfigChildElements(dom, children, QFileInfo(source).absolutePath(), QFileInfo(target).absolutePath());
+
+ QInstaller::openForWrite(&xmlFile); // throws in case of error
+ QTextStream stream(&xmlFile);
+ dom.save(stream, 4); // use 4 as the amount of space for indentation
+
+ qCDebug(QInstaller::lcDeveloperBuild) << "Finished copying configuration data.";
+}
+
+/*!
+ \internal
+
+ Recursively iterates over a list of QDomNode \a objects belonging to \a dom and their
+ children accordingly, searching for relative file names. Found files are copied from
+ \a sourceDir to \a targetDir.
+
+ Throws \c QInstaller::Error in case of failure.
+*/
+void QInstaller::copyConfigChildElements(QDomDocument &dom, const QDomNodeList &objects,
+ const QString &sourceDir, const QString &targetDir)
+{
+ for (int i = 0; i < objects.length(); i++) {
+ QDomElement domElement = objects.at(i).toElement();
+ if (domElement.isNull())
+ continue;
+
+ // Iterate recursively over all child nodes
+ const QDomNodeList elementChildren = domElement.childNodes();
+ QInstaller::copyConfigChildElements(dom, elementChildren, sourceDir, targetDir);
+
+ // Filename may also contain a path relative to source directory but we
+ // copy it strictly into target directory without extra paths
+ static const QRegularExpression regex(QLatin1String("\\\\|/|\\.|:"));
+ const QString newName = domElement.text().replace(regex, QLatin1String("_"));
+
+ const QString targetFile = targetDir + QDir::separator() + newName;
+ const QFileInfo elementFileInfo = QFileInfo(sourceDir, domElement.text());
+
+ if (!elementFileInfo.exists() || elementFileInfo.isDir())
+ continue;
+
+ domElement.replaceChild(dom.createTextNode(newName), domElement.firstChild());
+
+ if (!QFile::copy(elementFileInfo.absoluteFilePath(), targetFile)) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot copy file \"%1\" to \"%2\".").arg(elementFileInfo.absoluteFilePath(), targetFile));
+ }
+ }
+}
diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h
index ae08f5ff3..3c995937c 100644
--- a/src/libs/installer/fileutils.h
+++ b/src/libs/installer/fileutils.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -33,53 +33,52 @@
#include <QtCore/QSet>
#include <QtCore/QString>
#include <QtCore/QStringList>
+#include <QtXml/QDomDocument>
+#include <QtXml/QDomNodeList>
QT_BEGIN_NAMESPACE
class QFileInfo;
+class QFile;
class QUrl;
QT_END_NAMESPACE
namespace QInstaller {
-class INSTALLER_EXPORT TempDirDeleter
+
+enum DefaultFilePermissions {
+ NonExecutable = 0x6644,
+ Executable = 0x7755
+};
+
+class INSTALLER_EXPORT TempPathDeleter
{
public:
- explicit TempDirDeleter(const QString &path);
- explicit TempDirDeleter(const QStringList &paths = QStringList());
- ~TempDirDeleter();
+ explicit TempPathDeleter(const QString &path);
+ explicit TempPathDeleter(const QStringList &paths = QStringList());
+ ~TempPathDeleter();
QStringList paths() const;
void add(const QString &path);
void add(const QStringList &paths);
- void releaseAll();
- void release(const QString &path);
- void passAndReleaseAll(TempDirDeleter &tdd);
- void passAndRelease(TempDirDeleter &tdd, const QString &path);
-
void releaseAndDeleteAll();
void releaseAndDelete(const QString &path);
private:
- Q_DISABLE_COPY(TempDirDeleter)
+ Q_DISABLE_COPY(TempPathDeleter)
QSet<QString> m_paths;
};
QString INSTALLER_EXPORT humanReadableSize(const qint64 &size, int precision = 2);
- /*!
- Removes the directory at \a path recursively.
- @param path The directory to remove
- @param ignoreErrors if @p true, errors will be silently ignored. Otherwise an exception will be thrown
- if removing fails.
-
- @throws QInstaller::Error if the directory cannot be removed and ignoreErrors is @p false
- */
void INSTALLER_EXPORT removeFiles(const QString &path, bool ignoreErrors = false);
void INSTALLER_EXPORT removeDirectory(const QString &path, bool ignoreErrors = false);
void INSTALLER_EXPORT removeDirectoryThreaded(const QString &path, bool ignoreErrors = false);
void INSTALLER_EXPORT removeSystemGeneratedFiles(const QString &path);
+ bool INSTALLER_EXPORT setDefaultFilePermissions(const QString &fileName, DefaultFilePermissions permissions);
+ bool INSTALLER_EXPORT setDefaultFilePermissions(QFile *file, DefaultFilePermissions permissions);
+
QString INSTALLER_EXPORT generateTemporaryFileName(const QString &templ=QString());
void INSTALLER_EXPORT moveDirectoryContents(const QString &sourceDir, const QString &targetDir);
@@ -90,24 +89,26 @@ private:
void INSTALLER_EXPORT mkdir(const QString &path);
void INSTALLER_EXPORT mkpath(const QString &path);
+ bool INSTALLER_EXPORT createDirectoryWithParents(const QString &path);
+#ifdef Q_OS_MACOS
+ void INSTALLER_EXPORT mkalias(const QString &path, const QString &alias);
+#endif
quint64 INSTALLER_EXPORT fileSize(const QFileInfo &info);
bool INSTALLER_EXPORT isInBundle(const QString &path, QString *bundlePath = 0);
- QString replacePath(const QString &path, const QString &pathBefore, const QString &pathAfter);
+ QString replacePath(const QString &path, const QString &pathBefore, const QString &pathAfter, bool cleanPath = true);
+ void replaceHighDpiImage(QString &imagePath);
+
+ void INSTALLER_EXPORT trimmedCopyConfigData(const QString &source, const QString &target, const QStringList &elementsToRemoveTags);
+ void copyConfigChildElements(QDomDocument &dom, const QDomNodeList &objects, const QString &sourceDir, const QString &targetDir);
#ifdef Q_OS_WIN
QString INSTALLER_EXPORT getLongPathName(const QString &name);
QString INSTALLER_EXPORT getShortPathName(const QString &name);
- /*!
- Makes sure that capitalization of directories is canonical.
- */
QString INSTALLER_EXPORT normalizePathName(const QString &name);
- /*!
- Sets the .ico file at \a icon as application icon for \a application.
- */
void INSTALLER_EXPORT setApplicationIcon(const QString &application, const QString &icon);
#endif
}
diff --git a/src/libs/installer/fileutils_mac.mm b/src/libs/installer/fileutils_mac.mm
new file mode 100644
index 000000000..d3792d510
--- /dev/null
+++ b/src/libs/installer/fileutils_mac.mm
@@ -0,0 +1,75 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "fileutils.h"
+
+#include "errors.h"
+
+#include <QCoreApplication>
+
+#include <objc/objc.h>
+#include <Foundation/NSURL.h>
+#include <Foundation/NSError.h>>
+
+namespace QInstaller {
+
+/*!
+ \internal
+
+ Creates a bookmark variant of Finder alias from target \a path to \a alias.
+ Throws \c Error on failure.
+*/
+void mkalias(const QString &path, const QString &alias)
+{
+ NSURL *targetUrl = [NSURL fileURLWithPath:path.toNSString()];
+ NSURL *aliasUrl = [NSURL fileURLWithPath:alias.toNSString()];
+
+ NSError *error = nil;
+ NSData *data = [targetUrl bookmarkDataWithOptions:NSURLBookmarkCreationSuitableForBookmarkFile
+ includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
+
+ if (data != nil) {
+ BOOL success = [NSURL writeBookmarkData:data toURL:aliasUrl
+ options:NSURLBookmarkCreationSuitableForBookmarkFile error:&error];
+ if (success == NO) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot create alias from \"%1\" to \"%2\": %3.")
+ .arg(path, alias, QString::fromNSString(error.localizedDescription))
+ );
+ }
+ } else {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Could not get bookmark from URL \"%1\": %2.").arg(
+ QString::fromNSString(targetUrl.absoluteString),
+ QString::fromNSString(error.localizedDescription)
+ )
+ );
+ }
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/genericdatacache.cpp b/src/libs/installer/genericdatacache.cpp
new file mode 100644
index 000000000..a1e31ccfe
--- /dev/null
+++ b/src/libs/installer/genericdatacache.cpp
@@ -0,0 +1,695 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "genericdatacache.h"
+
+#include "errors.h"
+#include "fileutils.h"
+#include "globals.h"
+#include "metadata.h"
+#include "updater.h"
+
+#include <QDir>
+#include <QDirIterator>
+#include <QtConcurrent>
+
+namespace QInstaller {
+
+static const QLatin1String scManifestFile("manifest.json");
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::CacheableItem
+ \brief The CacheableItem is a pure virtual class that defines an interface for
+ a type suited for storage with the \l{GenericDataCache} class.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::path() const
+
+ Returns the path of this item. A subclass may override this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::setPath(const QString &path)
+
+ Sets the path of the item to \a path. A subclass may override this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::checksum() const
+
+ Returns the checksum of this item. A subclass must implement this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::isValid() const
+
+ Returns \c true if this item is valid, \c false otherwise.
+ A subclass must implement this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::isActive() const
+
+ Returns \c true if this item is an actively used cache item, \c false otherwise.
+ This information is used as a hint for filtering obsolete entries, an active item
+ can never be obsolete.
+
+ A subclass must implement this method.
+*/
+
+/*!
+ \fn QInstaller::CacheableItem::obsoletes(CacheableItem *other)
+
+ Returns \c true if the calling item obsoletes \a other item, \c false otherwise.
+ This method is used for filtering obsolete entries from the cache.
+
+ A subclass must implement this method.
+*/
+
+/*!
+ Virtual destructor for \c CacheableItem.
+*/
+CacheableItem::~CacheableItem()
+{
+}
+
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::GenericDataCache
+ \brief The GenericDataCache is a template class for a checksum based storage
+ of items on disk.
+
+ GenericDataCache\<T\> manages a cache storage for a set \l{path()}, which contains
+ a subdirectory for each registered item. An item of type \c T should implement
+ methods declared in the \l{CacheableItem} interface. The GenericDataCache\<T\> class can
+ still be explicitly specialized to use the derived type as a template argument, to
+ allow retrieving items as the derived type without casting.
+
+ Each cache has a manifest file in its root directory, which lists the version
+ and wrapped type of the cache, and all its items. The file is updated automatically
+ when the cache object is destructed, or it can be updated periodically by
+ calling \l{sync()}.
+*/
+
+/*!
+ \enum GenericDataCache::RegisterMode
+ This enum holds the possible values for modes of registering items to cache.
+
+ \value Copy
+ The contents of the item are copied to the cache.
+ \value Move
+ The contents of the item are move to the cache.
+*/
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::GenericDataCache()
+
+ Constructs a new empty cache. The cache is invalid until set with a
+ path and initialized.
+*/
+template <typename T>
+GenericDataCache<T>::GenericDataCache()
+ : m_version(QLatin1String("1.0.0"))
+ , m_invalidated(true)
+{
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::GenericDataCache(const QString &path, const QString &type, const QString &version)
+
+ Constructs a cache to \a path with the given \a type and \a version.
+ The cache is initialized automatically.
+*/
+template <typename T>
+GenericDataCache<T>::GenericDataCache(const QString &path, const QString &type,
+ const QString &version)
+ : m_path(path)
+ , m_type(type)
+ , m_version(version)
+ , m_invalidated(true)
+{
+ initialize();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::~GenericDataCache()
+
+ Deletes the cache object. Item contents on disk are kept.
+*/
+template <typename T>
+GenericDataCache<T>::~GenericDataCache()
+{
+ if (m_invalidated)
+ return;
+
+ toDisk();
+ invalidate();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::setType(const QString &type)
+
+ Sets the name of the wrapped type to \a type. This is used for determining if an
+ existing cache holds items of the same type. Trying to load cached items with mismatching
+ type results in discarding the old items. Optional.
+*/
+template<typename T>
+void GenericDataCache<T>::setType(const QString &type)
+{
+ QMutexLocker _(&m_mutex);
+ m_type = type;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::setVersion(const QString &version)
+
+ Sets the version of the cache to \a version. Loading from a cache with different
+ expected version discards the old items. The version property defaults to \c{1.0.0}.
+*/
+template<typename T>
+void GenericDataCache<T>::setVersion(const QString &version)
+{
+ QMutexLocker _(&m_mutex);
+ m_version = version;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::initialize()
+
+ Initializes a cache. Creates a new directory for the path configured for
+ this cache if it does not exist, and loads any previously cached items from
+ the directory. The cache directory is locked for access by this process only.
+ Returns \c true on success, \c false otherwise.
+*/
+template <typename T>
+bool GenericDataCache<T>::initialize()
+{
+ QMutexLocker _(&m_mutex);
+ Q_ASSERT(m_items.isEmpty());
+
+ if (m_path.isEmpty()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot initialize cache with empty path."));
+ return false;
+ }
+
+ QDir directory(m_path);
+ if (!directory.exists() && !directory.mkpath(m_path)) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot create directory \"%1\" for cache.").arg(m_path));
+ return false;
+ }
+
+ if (m_lock && !m_lock->unlock()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot initialize cache: %1").arg(m_lock->errorString()));
+ return false;
+ }
+
+ m_lock.reset(new KDUpdater::LockFile(m_path + QLatin1String("/cache.lock")));
+ if (!m_lock->lock()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot initialize cache: %1").arg(m_lock->errorString()));
+ return false;
+ }
+
+ if (!fromDisk())
+ return false;
+
+ m_invalidated = false;
+ return true;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::clear()
+
+ Removes all items from the cache and deletes their contents on disk. If
+ the cache directory becomes empty, it is also deleted. The cache becomes
+ invalid after this action, even in case of error while clearing. In that
+ case already deleted items will be lost. Returns \c true on success,
+ \c false otherwise.
+*/
+template <typename T>
+bool GenericDataCache<T>::clear()
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot clear invalidated cache."));
+ return false;
+ }
+
+ QFile manifestFile(m_path + QDir::separator() + scManifestFile);
+ if (manifestFile.exists() && !manifestFile.remove()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove manifest file: %1").arg(manifestFile.errorString()));
+ invalidate();
+ return false;
+ }
+
+ bool success = true;
+ for (T *item : qAsConst(m_items)) {
+ try {
+ QInstaller::removeDirectory(item->path());
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while clearing cache: %1").arg(e.message()));
+ success = false;
+ }
+ }
+
+ invalidate();
+ QDir().rmdir(m_path);
+ return success;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::sync()
+
+ Synchronizes the contents of the cache to its manifest file. Returns \c true
+ if the manifest file was updates successfully, \c false otherwise.
+*/
+template<typename T>
+bool GenericDataCache<T>::sync()
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot synchronize invalidated cache."));
+ return false;
+ }
+ return toDisk();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::isValid() const
+
+ Returns \c true if the cache is valid, \c false otherwise. A cache is considered
+ valid when it is initialized to a set path.
+*/
+template<typename T>
+bool GenericDataCache<T>::isValid() const
+{
+ QMutexLocker _(&m_mutex);
+ return !m_invalidated;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::errorString() const
+
+ Returns a string representing the last error with the cache.
+*/
+template<typename T>
+QString GenericDataCache<T>::errorString() const
+{
+ QMutexLocker _(&m_mutex);
+ return m_error;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::path() const
+
+ Returns the path of the cache on disk.
+*/
+template <typename T>
+QString GenericDataCache<T>::path() const
+{
+ QMutexLocker _(&m_mutex);
+ return m_path;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::setPath(const QString &path)
+
+ Sets a new \a path for the cache and invalidates current items. Saves the information
+ of the old cache to its manifest file.
+*/
+template <typename T>
+void GenericDataCache<T>::setPath(const QString &path)
+{
+ QMutexLocker _(&m_mutex);
+ if (!m_invalidated)
+ toDisk();
+
+ m_path = path;
+ invalidate();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::items() const
+
+ Returns a list of cached items.
+*/
+template <typename T>
+QList<T *> GenericDataCache<T>::items() const
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot retrieve items from invalidated cache."));
+ return QList<T *>();
+ }
+ return m_items.values();
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::itemByChecksum(const QByteArray &checksum) const
+
+ Returns an item that matches the \a checksum or \c nullptr in case
+ no such item is cached.
+*/
+template <typename T>
+T *GenericDataCache<T>::itemByChecksum(const QByteArray &checksum) const
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot retrieve item from invalidated cache."));
+ return nullptr;
+ }
+ return m_items.value(checksum);
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::itemByPath(const QString &path) const
+
+ Returns an item from the \a path or \c nullptr in case no such item
+ is cached. Depending on the size of the cache, this can be much
+ slower than retrieving an item with \l{itemByChecksum()}.
+*/
+template <typename T>
+T *GenericDataCache<T>::itemByPath(const QString &path) const
+{
+ QMutexLocker _(&m_mutex);
+ auto it = std::find_if(m_items.constBegin(), m_items.constEnd(),
+ [&](T *item) {
+ return (QDir::fromNativeSeparators(path) == QDir::fromNativeSeparators(item->path()));
+ }
+ );
+ if (it != m_items.constEnd())
+ return it.value();
+
+ return nullptr;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode)
+
+ Registers the \a item to the cache. If \a replace is set to \c true,
+ the new \a item replaces a previous item with the same checksum.
+
+ The cache takes ownership of the object pointed by \a item. The contents of the
+ item are copied or moved to the cache with a subdirectory name that matches the checksum
+ of the item. The \a mode decides how the contents of the item are registered, either by
+ copying or moving.
+
+ Returns \c true on success or \c false if the item could not be registered.
+*/
+template <typename T>
+bool GenericDataCache<T>::registerItem(T *item, bool replace, RegisterMode mode)
+{
+ QMutexLocker _(&m_mutex);
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register item to invalidated cache."));
+ return false;
+ }
+ if (!item) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register null item."));
+ return false;
+ }
+ if (!item->isValid()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register invalid item with checksum %1").arg(QLatin1String(item->checksum())));
+ return false;
+ }
+ if (m_items.contains(item->checksum())) {
+ if (replace) {// replace existing item including contents on disk
+ remove(item->checksum());
+ } else {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot register item with checksum %1. An item with the same checksum "
+ "already exists in cache.").arg(QLatin1String(item->checksum())));
+ return false;
+ }
+ }
+
+ const QString newPath = m_path + QDir::separator() + QString::fromLatin1(item->checksum());
+ try {
+ // A directory is in the way but it isn't registered to the current cache, remove.
+ QDir dir;
+ if (dir.exists(newPath))
+ QInstaller::removeDirectory(newPath);
+
+ switch (mode) {
+ case Copy:
+ QInstaller::copyDirectoryContents(item->path(), newPath);
+ break;
+ case Move:
+ // First, try moving the top level directory
+ if (!dir.rename(item->path(), newPath)) {
+ qCDebug(lcDeveloperBuild) << "Failed to rename directory" << item->path()
+ << "to" << newPath << ". Trying again.";
+ // If that does not work, fallback to moving the contents one by one
+ QInstaller::moveDirectoryContents(item->path(), newPath);
+ }
+ break;
+ default:
+ throw Error(QCoreApplication::translate("GenericDataCache",
+ "Unknown register mode selected!"));
+ }
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while copying item to path \"%1\": %2").arg(newPath, e.message()));
+ return false;
+ }
+
+ item->setPath(newPath);
+ if (item->isValid()) {
+ m_items.insert(item->checksum(), item);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::removeItem(const QByteArray &checksum)
+
+ Removes the item specified by \a checksum from the cache and deletes the
+ contents of the item from disk. Returns \c true if the item
+ was removed successfully, \c false otherwise.
+*/
+template <typename T>
+bool GenericDataCache<T>::removeItem(const QByteArray &checksum)
+{
+ QMutexLocker _(&m_mutex);
+ return remove(checksum);
+}
+
+/*!
+ \fn template <typename T> QInstaller::GenericDataCache<T>::obsoleteItems() const
+
+ Returns items considered obsolete from the cache.
+*/
+template<typename T>
+QList<T *> GenericDataCache<T>::obsoleteItems() const
+{
+ QMutexLocker _(&m_mutex);
+ const QList<T *> obsoletes = QtConcurrent::blockingFiltered(m_items.values(),
+ [&](T *item1) {
+ if (item1->isActive()) // We can skip the iteration for active entries
+ return false;
+
+ for (T *item2 : qAsConst(m_items)) {
+ if (item2->obsoletes(item1))
+ return true;
+ }
+ return false;
+ }
+ );
+ return obsoletes;
+}
+
+/*!
+ \internal
+
+ Marks the cache invalid and clears all items. The contents
+ on disk are not deleted. Releases the lock file of the cache.
+*/
+template <typename T>
+void GenericDataCache<T>::invalidate()
+{
+ if (!m_items.isEmpty()) {
+ qDeleteAll(m_items);
+ m_items.clear();
+ }
+ if (m_lock && !m_lock->unlock()) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while invalidating cache: %1").arg(m_lock->errorString()));
+ }
+ m_invalidated = true;
+}
+
+/*!
+ \internal
+
+ Sets the current error string to \a error and prints it as a warning to the console.
+*/
+template <typename T>
+void GenericDataCache<T>::setErrorString(const QString &error) const
+{
+ m_error = error;
+ qCWarning(QInstaller::lcInstallerInstallLog) << error;
+}
+
+/*!
+ \internal
+
+ Reads the manifest file of the cache if one exists, and populates the internal
+ hash from the file contents. Returns \c true if the manifests was read successfully
+ or if the reading was omitted. This is the case if the file does not exist yet, or
+ the type or version of the manifest does not match the current cache object. In case
+ of mismatch the old items are not restored. Returns \c false otherwise.
+*/
+template<typename T>
+bool GenericDataCache<T>::fromDisk()
+{
+ QFile manifestFile(m_path + QDir::separator() + scManifestFile);
+ if (!manifestFile.exists()) // Not yet created
+ return true;
+
+ if (!manifestFile.open(QIODevice::ReadOnly)) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot open manifest file: %1").arg(manifestFile.errorString()));
+ return false;
+ }
+
+ const QByteArray manifestData = manifestFile.readAll();
+ const QJsonDocument manifestJsonDoc(QJsonDocument::fromJson(manifestData));
+ const QJsonObject docJsonObject = manifestJsonDoc.object();
+
+ const QJsonValue type = docJsonObject.value(QLatin1String("type"));
+ if (type.toString() != m_type) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Discarding existing items from cache of type:"
+ << type.toString() << ". New type:" << m_type;
+ return true;
+ }
+
+ const QJsonValue version = docJsonObject.value(QLatin1String("version"));
+ if (KDUpdater::compareVersion(version.toString(), m_version) != 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Discarding existing items from cache with version:"
+ << version.toString() << ". New version:" << m_version;
+ return true;
+ }
+
+ const QJsonArray itemsJsonArray = docJsonObject.value(QLatin1String("items")).toArray();
+ for (const auto &itemJsonValue : itemsJsonArray) {
+ const QString checksum = itemJsonValue.toString();
+
+ std::unique_ptr<T> item(new T(m_path + QDir::separator() + checksum));
+ m_items.insert(checksum.toLatin1(), item.release());
+
+ // The cache directory may contain other entries (unrelated directories or
+ // invalid old cache items) which we don't care about, unless registering
+ // a new entry requires overwriting them.
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ Writes the manifest file with the contents of the internal item hash.
+ Returns \c true on success, \c false otherwise.
+*/
+template<typename T>
+bool GenericDataCache<T>::toDisk()
+{
+ QFile manifestFile(m_path + QDir::separator() + scManifestFile);
+ if (!manifestFile.open(QIODevice::WriteOnly)) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot open manifest file: %1").arg(manifestFile.errorString()));
+ return false;
+ }
+
+ QJsonArray itemsJsonArray;
+ const QList<QByteArray> keys = m_items.keys();
+ for (const QByteArray &key : keys)
+ itemsJsonArray.append(QJsonValue(QLatin1String(key)));
+
+ QJsonObject docJsonObject;
+ docJsonObject.insert(QLatin1String("items"), itemsJsonArray);
+ docJsonObject.insert(QLatin1String("version"), m_version);
+ if (!m_type.isEmpty())
+ docJsonObject.insert(QLatin1String("type"), m_type);
+
+ QJsonDocument manifestJsonDoc;
+ manifestJsonDoc.setObject(docJsonObject);
+ if (manifestFile.write(manifestJsonDoc.toJson()) == -1) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot write contents for manifest file: %1").arg(manifestFile.errorString()));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+*/
+template<typename T>
+bool GenericDataCache<T>::remove(const QByteArray &checksum)
+{
+ if (m_invalidated) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove item from invalidated cache."));
+ return false;
+ }
+ QScopedPointer<T> item(m_items.take(checksum));
+ if (!item) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Cannot remove item specified by checksum %1: no such item exists.").arg(QLatin1String(checksum)));
+ return false;
+ }
+
+ try {
+ QInstaller::removeDirectory(item->path());
+ } catch (const Error &e) {
+ setErrorString(QCoreApplication::translate("GenericDataCache",
+ "Error while removing directory \"%1\": %2").arg(item->path(), e.message()));
+ return false;
+ }
+ return true;
+}
+
+template class GenericDataCache<Metadata>;
+
+} // namespace QInstaller
diff --git a/src/libs/installer/genericdatacache.h b/src/libs/installer/genericdatacache.h
new file mode 100644
index 000000000..94085502c
--- /dev/null
+++ b/src/libs/installer/genericdatacache.h
@@ -0,0 +1,122 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef GENERICDATACACHE_H
+#define GENERICDATACACHE_H
+
+#include "installer_global.h"
+#include "lockfile.h"
+
+#include <QHash>
+#include <QMutex>
+#include <QScopedPointer>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT CacheableItem
+{
+public:
+ CacheableItem() = default;
+ explicit CacheableItem(const QString &path)
+ : m_path(path)
+ {}
+ virtual ~CacheableItem() = 0;
+
+ virtual QString path() const { return m_path; }
+ virtual void setPath(const QString &path) { m_path = path; }
+
+ virtual QByteArray checksum() const = 0;
+ virtual bool isValid() const = 0;
+
+ virtual bool isActive() const = 0;
+ virtual bool obsoletes(CacheableItem *other) = 0;
+
+private:
+ QString m_path;
+};
+
+template <typename T>
+class INSTALLER_EXPORT GenericDataCache
+{
+public:
+ enum RegisterMode {
+ Copy = 0,
+ Move = 1
+ };
+
+ GenericDataCache();
+ explicit GenericDataCache(const QString &path, const QString &type, const QString &version);
+ virtual ~GenericDataCache();
+
+ void setType(const QString &type);
+ void setVersion(const QString &version);
+
+ bool initialize();
+ bool clear();
+ bool sync();
+
+ bool isValid() const;
+ QString errorString() const;
+
+ QString path() const;
+ void setPath(const QString &path);
+
+ QList<T *> items() const;
+ T *itemByChecksum(const QByteArray &checksum) const;
+ T *itemByPath(const QString &path) const;
+
+ bool registerItem(T *item, bool replace = false, RegisterMode mode = Copy);
+ bool removeItem(const QByteArray &checksum);
+
+ QList<T *> obsoleteItems() const;
+
+private:
+ void invalidate();
+ void setErrorString(const QString &error) const;
+
+ bool fromDisk();
+ bool toDisk();
+
+ bool remove(const QByteArray &checksum);
+
+private:
+ QScopedPointer<KDUpdater::LockFile> m_lock;
+ mutable QMutex m_mutex;
+
+ QHash<QByteArray, T *> m_items;
+ QString m_path;
+ QString m_type;
+ QString m_version;
+ mutable QString m_error;
+
+ bool m_invalidated;
+};
+
+} // namespace QInstaller
+
+#endif // GENERICDATACACHE_H
diff --git a/src/libs/installer/globals.cpp b/src/libs/installer/globals.cpp
index c059b6630..3fd084768 100644
--- a/src/libs/installer/globals.cpp
+++ b/src/libs/installer/globals.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -25,36 +25,154 @@
** $QT_END_LICENSE$
**
**************************************************************************/
+#include <QTextDocument>
+#include <QMetaEnum>
+
#include "globals.h"
-const char IFW_COMPONENT_CHECKER[] = "ifw.componentChecker";
-const char IFW_RESOURCES[] = "ifw.resources";
-const char IFW_TRANSLATIONS[] = "ifw.translations";
-const char IFW_NETWORK[] = "ifw.network";
+#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <conio.h>
+#endif
+#include <iostream>
+
+const char IFW_SERVER[] = "ifw.server";
+const char IFW_INSTALLER_INSTALLLOG[] = "ifw.installer.installlog";
+const char IFW_DEVELOPER_BUILD[] = "ifw.developer.build";
+
+// Internal-only, hidden in --help text
+const char IFW_PROGRESS_INDICATOR[] = "ifw.progress.indicator";
namespace QInstaller
{
-Q_LOGGING_CATEGORY(lcComponentChecker, IFW_COMPONENT_CHECKER)
-Q_LOGGING_CATEGORY(lcResources, IFW_RESOURCES)
-Q_LOGGING_CATEGORY(lcTranslations, IFW_TRANSLATIONS)
-Q_LOGGING_CATEGORY(lcNetwork, IFW_NETWORK)
+/*!
+ \fn QInstaller::lcServer()
+ \internal
+*/
+
+/*!
+ \fn QInstaller::lcInstallerInstallLog()
+ \internal
+*/
+
+/*!
+ \fn QInstaller::lcProgressIndicator()
+ \internal
+*/
+
+/*!
+ \fn QInstaller::lcDeveloperBuild()
+ \internal
+*/
+
+/*!
+ \fn inline QInstaller::splitStringWithComma(const QString &value())
+ Splits \a value into substrings wherever comma occurs, and returns the list of those strings.
+ \internal
+*/
+Q_LOGGING_CATEGORY(lcServer, IFW_SERVER)
+Q_LOGGING_CATEGORY(lcInstallerInstallLog, IFW_INSTALLER_INSTALLLOG)
+Q_LOGGING_CATEGORY(lcProgressIndicator, IFW_PROGRESS_INDICATOR)
+Q_LOGGING_CATEGORY(lcDeveloperBuild, IFW_DEVELOPER_BUILD)
+
+/*!
+ Returns available logging categories.
+*/
QStringList loggingCategories()
{
static QStringList categories = QStringList()
- << QLatin1String(IFW_COMPONENT_CHECKER)
- << QLatin1String(IFW_RESOURCES)
- << QLatin1String(IFW_TRANSLATIONS)
- << QLatin1String(IFW_NETWORK);
+ << QLatin1String(IFW_INSTALLER_INSTALLLOG)
+ << QLatin1String(IFW_SERVER)
+ << QLatin1String(IFW_DEVELOPER_BUILD)
+ << QLatin1String("js");
return categories;
+
}
-Q_GLOBAL_STATIC_WITH_ARGS(QRegExp, staticCommaRegExp, (QLatin1String("(, |,)")));
-QRegExp commaRegExp()
+Q_GLOBAL_STATIC_WITH_ARGS(QRegularExpression, staticCommaRegExp, (QLatin1String("(, |,)")));
+
+/*!
+ \internal
+*/
+QRegularExpression commaRegExp()
{
return *staticCommaRegExp();
}
+/*!
+ Converts and returns a string \a html containing an HTML document as a plain text.
+*/
+QString htmlToString(const QString &html)
+{
+ QTextDocument doc;
+ doc.setHtml(html);
+ return doc.toPlainText();
+}
+
+/*!
+ \internal
+*/
+QString enumToString(const QMetaObject& metaObject, const char *enumerator, int key)
+{
+ QString value = QString();
+ int enumIndex = metaObject.indexOfEnumerator(enumerator);
+ if (enumIndex != -1) {
+ QMetaEnum en = metaObject.enumerator(enumIndex);
+ value = QLatin1String(en.valueToKey(key));
+ }
+ return value;
+}
+
+void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle)
+{
+ std::string usernameStdStr;
+ std::string passwordStdStr;
+
+ std::cout << qPrintable(usernameTitle);
+ std::cin >> usernameStdStr;
+
+ std::cout << qPrintable(passwordTitle);
+#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
+ termios oldTerm;
+ termios term;
+
+ // Turn off echoing
+ tcgetattr(STDIN_FILENO, &oldTerm);
+ term = oldTerm;
+ term.c_lflag &= ~ECHO;
+ tcsetattr(STDIN_FILENO, TCSANOW, &term);
+
+ std::cin >> passwordStdStr;
+
+ // Clear input buffer
+ std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+
+ // Restore old attributes
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldTerm);
+#elif defined(Q_OS_WIN)
+ char ch;
+ while ((ch = _getch()) != '\r') { // Return key
+ if (ch == '\b') { // Backspace key
+ if (!passwordStdStr.empty())
+ passwordStdStr.pop_back();
+ } else {
+ passwordStdStr.push_back(ch);
+ }
+ }
+ // Clear input buffer
+ int c;
+ while ((c = getchar()) != '\n' && c != EOF);
+
+#endif
+ std::cout << "\n";
+
+ *username = username->fromStdString(usernameStdStr);
+ *password = password->fromStdString(passwordStdStr);
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/globals.h b/src/libs/installer/globals.h
index a5bd47ac8..2d119048b 100644
--- a/src/libs/installer/globals.h
+++ b/src/libs/installer/globals.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,19 +30,36 @@
#include "installer_global.h"
-#include <QRegExp>
+#include <QRegularExpression>
#include <QLoggingCategory>
namespace QInstaller {
-INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcComponentChecker)
-INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcResources)
-INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcTranslations)
-INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcNetwork)
+INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcServer)
+INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcInstallerInstallLog)
+
+INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcProgressIndicator)
+INSTALLER_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcDeveloperBuild)
QStringList INSTALLER_EXPORT loggingCategories();
-QRegExp INSTALLER_EXPORT commaRegExp();
+QRegularExpression INSTALLER_EXPORT commaRegExp();
+inline QStringList splitStringWithComma(const QString &value) {
+ if (!value.isEmpty())
+ return value.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ return QStringList();
+}
+
+QString htmlToString(const QString &html);
+QString enumToString(const QMetaObject& metaObject, const char *enumerator, int key);
+
+template <typename T, template<typename> typename C>
+QSet<T> toQSet(const C<T> &container)
+{
+ return QSet<T>(container.begin(), container.end());
+}
+
+void askForCredentials(QString *username, QString *password, const QString &usernameTitle, const QString &passwordTitle);
} // QInstaller
diff --git a/src/libs/installer/globalsettingsoperation.cpp b/src/libs/installer/globalsettingsoperation.cpp
index 0840cecf2..6ca50f96f 100644
--- a/src/libs/installer/globalsettingsoperation.cpp
+++ b/src/libs/installer/globalsettingsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::GlobalSettingsOperation
+ \internal
+*/
+
GlobalSettingsOperation::GlobalSettingsOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -43,8 +49,9 @@ void GlobalSettingsOperation::backup()
bool GlobalSettingsOperation::performOperation()
{
+ const QStringList args = parsePerformOperationArguments();
QString key, value;
- QScopedPointer<QSettingsWrapper> settings(setup(&key, &value, arguments()));
+ QScopedPointer<QSettingsWrapper> settings(setup(&key, &value, args));
if (settings.isNull())
return false;
@@ -70,8 +77,12 @@ bool GlobalSettingsOperation::performOperation()
bool GlobalSettingsOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
+
+ const QStringList args = parsePerformOperationArguments();
QString key, val;
- QScopedPointer<QSettingsWrapper> settings(setup(&key, &val, arguments()));
+ QScopedPointer<QSettingsWrapper> settings(setup(&key, &val, args));
if (settings.isNull())
return false;
@@ -117,7 +128,7 @@ QSettingsWrapper *GlobalSettingsOperation::setup(QString *key, QString *value, c
const QString &filename = arguments.at(0);
*key = arguments.at(1);
*value = arguments.at(2);
- return new QSettingsWrapper(filename, QSettingsWrapper::NativeFormat);
+ return new QSettingsWrapper(filename, QSettings::NativeFormat);
}
return nullptr;
diff --git a/src/libs/installer/globalsettingsoperation.h b/src/libs/installer/globalsettingsoperation.h
index fe5d14edb..473497614 100644
--- a/src/libs/installer/globalsettingsoperation.h
+++ b/src/libs/installer/globalsettingsoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,10 +40,10 @@ class INSTALLER_EXPORT GlobalSettingsOperation : public Operation
public:
explicit GlobalSettingsOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
private:
QSettingsWrapper *setup(QString *key, QString *value, const QStringList &args);
diff --git a/src/libs/installer/graph.cpp b/src/libs/installer/graph.cpp
new file mode 100644
index 000000000..51bfa53c3
--- /dev/null
+++ b/src/libs/installer/graph.cpp
@@ -0,0 +1,35 @@
+/**************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "graph.h"
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Graph
+ \internal
+*/
diff --git a/src/libs/installer/graph.h b/src/libs/installer/graph.h
index 5f330e69b..49da9c2ed 100644
--- a/src/libs/installer/graph.h
+++ b/src/libs/installer/graph.h
@@ -63,7 +63,7 @@ public:
QList<T> edges(const T &node) const
{
- return m_graph.value(node).toList();
+ return m_graph.value(node).values();
}
void addEdge(const T &node, const T &edge)
diff --git a/src/libs/installer/init.cpp b/src/libs/installer/init.cpp
index 8a13119b0..f47040d93 100644
--- a/src/libs/installer/init.cpp
+++ b/src/libs/installer/init.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,17 +47,16 @@
#include "licenseoperation.h"
#include "settingsoperation.h"
#include "consumeoutputoperation.h"
+#include "loggingutils.h"
+#ifdef IFW_LIB7Z
#include "lib7z_facade.h"
-#include "utils.h"
+#endif
#include "updateoperationfactory.h"
#include "filedownloaderfactory.h"
#include <QtPlugin>
-#include <QElapsedTimer>
-
-#include <iostream>
using namespace KDUpdater;
using namespace QInstaller;
@@ -69,77 +68,15 @@ static void initResources()
}
#endif
-static QString trimAndPrepend(QtMsgType type, const QString &msg)
-{
- QString ba(msg);
- // last character is a space from qDebug
- if (ba.endsWith(QLatin1Char(' ')))
- ba.chop(1);
-
- // remove quotes if the whole message is surrounded with them
- if (ba.startsWith(QLatin1Char('"')) && ba.endsWith(QLatin1Char('"')))
- ba = ba.mid(1, ba.length() - 2);
-
- // prepend the message type, skip QtDebugMsg
- switch (type) {
- case QtWarningMsg:
- ba.prepend(QStringLiteral("Warning: "));
- break;
-
- case QtCriticalMsg:
- ba.prepend(QStringLiteral("Critical: "));
- break;
-
- case QtFatalMsg:
- ba.prepend(QStringLiteral("Fatal: "));
- break;
-
- default:
- break;
- }
- return ba;
-}
-
-// start timer on construction (so we can use it as static member)
-class Uptime : public QElapsedTimer {
-public:
- Uptime() { start(); }
-};
-
-void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
-{
- // suppress warning from QPA minimal plugin
- if (msg.contains(QLatin1String("This plugin does not support propagateSizeHints")))
- return;
-
- static Uptime uptime;
-
- QString ba = QLatin1Char('[') + QString::number(uptime.elapsed()) + QStringLiteral("] ")
- + trimAndPrepend(type, msg);
-
- if (type != QtDebugMsg && context.file) {
- ba += QString(QStringLiteral(" (%1:%2, %3)")).arg(
- QString::fromLatin1(context.file)).arg(context.line).arg(
- QString::fromLatin1(context.function));
- }
-
- if (VerboseWriter *log = VerboseWriter::instance())
- log->appendLine(ba);
-
- if (type != QtDebugMsg || isVerbose())
- std::cout << qPrintable(ba) << std::endl;
-
- if (type == QtFatalMsg) {
- QtMessageHandler oldMsgHandler = qInstallMessageHandler(nullptr);
- qt_message_output(type, context, msg);
- qInstallMessageHandler(oldMsgHandler);
- }
-}
-
+/*!
+ Initializes the 7z library and installer resources. Registers
+ custom operations and installs a custom message handler.
+*/
void QInstaller::init()
{
+#ifdef IFW_LIB7Z
Lib7z::initSevenZ();
-
+#endif
#if defined(QT_STATIC)
::initResources();
#endif
@@ -168,5 +105,8 @@ void QInstaller::init()
FileDownloaderFactory::setFollowRedirects(true);
- qInstallMessageHandler(messageHandler);
+ auto messageHandler = [](QtMsgType type, const QMessageLogContext &context, const QString &msg) {
+ LoggingHandler::instance().messageHandler(type, context, msg);
+ };
+ qInstallMessageHandler(messageHandler);
}
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index a6dfbde50..ff7a0eed2 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -4,8 +4,8 @@ INCLUDEPATH += . ..
CONFIG += staticlib
-include(../7zip/7zip.pri)
include(../kdtools/kdtools.pri)
+include(../ifwtools/ifwtools.pri)
include(../../../installerfw.pri)
# productkeycheck API
@@ -23,6 +23,7 @@ HEADERS += productkeycheck.h
include($$PRODUCTKEYCHECK_PRI_FILE)
} else {
SOURCES += productkeycheck.cpp
+ SOURCES += commandlineparser_p.cpp
}
DESTDIR = $$IFW_LIB_PATH
@@ -38,14 +39,27 @@ QT += \
widgets \
core-private \
qml-private
-win32:QT += winextras
+
+win32:lessThan(QT_MAJOR_VERSION, 6):QT += winextras
+
+greaterThan(QT_MAJOR_VERSION, 5):QT += core5compat
HEADERS += packagemanagercore.h \
+ aspectratiolabel.h \
+ calculatorbase.h \
+ componentalias.h \
+ componentsortfilterproxymodel.h \
+ concurrentoperationrunner.h \
+ genericdatacache.h \
+ loggingutils.h \
+ metadata.h \
+ metadatacache.h \
packagemanagercore_p.h \
packagemanagergui.h \
binaryformat.h \
binaryformatengine.h \
binaryformatenginehandler.h \
+ fileguard.h \
repository.h \
utils.h \
errors.h \
@@ -75,7 +89,6 @@ HEADERS += packagemanagercore.h \
adminauthorization.h \
elevatedexecuteoperation.h \
fakestopprocessforupdateoperation.h \
- lazyplaintextedit.h \
progresscoordinator.h \
minimumprogressoperation.h \
performinstallationform.h \
@@ -87,7 +100,6 @@ HEADERS += packagemanagercore.h \
constants.h \
packagemanagerproxyfactory.h \
createlocalrepositoryoperation.h \
- lib7z_facade.h \
link.h \
createlinkoperation.h \
packagemanagercoredata.h \
@@ -101,7 +113,6 @@ HEADERS += packagemanagercore.h \
copyfiletask.h \
downloadfiletask.h \
downloadfiletask_p.h \
- unziptask.h \
observer.h \
runextensions.h \
metadatajob.h \
@@ -128,14 +139,31 @@ HEADERS += packagemanagercore.h \
keepaliveobject.h \
systeminfo.h \
packagesource.h \
- lib7z_guid.h \
- lib7z_create.h \
- lib7z_extract.h \
- lib7z_list.h \
repositorycategory.h \
- componentselectionpage_p.h
+ componentselectionpage_p.h \
+ commandlineparser.h \
+ commandlineparser_p.h \
+ abstractarchive.h \
+ directoryguard.h \
+ archivefactory.h \
+ operationtracer.h \
+ customcombobox.h
SOURCES += packagemanagercore.cpp \
+ abstractarchive.cpp \
+ archivefactory.cpp \
+ aspectratiolabel.cpp \
+ calculatorbase.cpp \
+ componentalias.cpp \
+ concurrentoperationrunner.cpp \
+ directoryguard.cpp \
+ fileguard.cpp \
+ componentsortfilterproxymodel.cpp \
+ genericdatacache.cpp \
+ loggingutils.cpp \
+ metadata.cpp \
+ metadatacache.cpp \
+ operationtracer.cpp \
packagemanagercore_p.cpp \
packagemanagergui.cpp \
binaryformat.cpp \
@@ -165,7 +193,6 @@ SOURCES += packagemanagercore.cpp \
init.cpp \
elevatedexecuteoperation.cpp \
fakestopprocessforupdateoperation.cpp \
- lazyplaintextedit.cpp \
progresscoordinator.cpp \
minimumprogressoperation.cpp \
performinstallationform.cpp \
@@ -178,7 +205,6 @@ SOURCES += packagemanagercore.cpp \
permissionsettings.cpp \
packagemanagerproxyfactory.cpp \
createlocalrepositoryoperation.cpp \
- lib7z_facade.cpp \
link.cpp \
createlinkoperation.cpp \
packagemanagercoredata.cpp \
@@ -189,7 +215,6 @@ SOURCES += packagemanagercore.cpp \
abstractfiletask.cpp \
copyfiletask.cpp \
downloadfiletask.cpp \
- unziptask.cpp \
observer.cpp \
metadatajob.cpp \
protocol.cpp \
@@ -210,7 +235,11 @@ SOURCES += packagemanagercore.cpp \
systeminfo.cpp \
packagesource.cpp \
repositorycategory.cpp \
- componentselectionpage_p.cpp
+ componentselectionpage_p.cpp \
+ commandlineparser.cpp \
+ customcombobox.cpp
+
+macos:SOURCES += fileutils_mac.mm
FORMS += proxycredentialsdialog.ui \
serverauthenticationdialog.ui
@@ -222,11 +251,38 @@ unix {
else: SOURCES += adminauthorization_x11.cpp
}
-LIBS += -l7z
+CONFIG(libarchive) {
+ HEADERS += libarchivearchive.h \
+ libarchivewrapper.h \
+ libarchivewrapper_p.h
+
+ SOURCES += libarchivearchive.cpp \
+ libarchivewrapper.cpp \
+ libarchivewrapper_p.cpp
+
+ LIBS += -llibarchive
+}
+
+CONFIG(lzmasdk) {
+ include(../3rdparty/7zip/7zip.pri)
+
+ HEADERS += lib7z_facade.h \
+ lib7z_guid.h \
+ lib7z_create.h \
+ lib7z_extract.h \
+ lib7z_list.h \
+ lib7zarchive.h
+
+ SOURCES += lib7z_facade.cpp \
+ lib7zarchive.cpp
+
+ LIBS += -l7z
+ win32:LIBS += -loleaut32 -luser32
+}
+
win32 {
SOURCES += adminauthorization_win.cpp sysinfo_win.cpp
- LIBS += -loleaut32 -luser32 # 7zip
LIBS += -ladvapi32 -lpsapi # kdtools
LIBS += -lole32 -lshell32 # createshortcutoperation
diff --git a/src/libs/installer/installer_global.h b/src/libs/installer/installer_global.h
index ea6865042..285eff910 100644
--- a/src/libs/installer/installer_global.h
+++ b/src/libs/installer/installer_global.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,4 +40,10 @@
# define INSTALLER_EXPORT
#endif
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+typedef uint hashValue;
+#else
+typedef size_t hashValue;
+#endif
+
#endif //INSTALLER_GLOBAL_H
diff --git a/src/libs/installer/installercalculator.cpp b/src/libs/installer/installercalculator.cpp
index db1f88563..4c53824af 100644
--- a/src/libs/installer/installercalculator.cpp
+++ b/src/libs/installer/installercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,149 +29,204 @@
#include "installercalculator.h"
#include "component.h"
+#include "componentalias.h"
+#include "componentmodel.h"
#include "packagemanagercore.h"
#include "settings.h"
-
-#include <QDebug>
+#include <globals.h>
namespace QInstaller {
-InstallerCalculator::InstallerCalculator(const QList<Component *> &allComponents)
- : m_allComponents(allComponents)
-{
-}
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::InstallerCalculator
+ \internal
+*/
-void InstallerCalculator::insertInstallReason(Component *component,
- InstallReasonType installReason, const QString &referencedComponentName)
+InstallerCalculator::InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash)
+ : CalculatorBase(core)
+ , m_autoDependencyComponentHash(autoDependencyComponentHash)
{
- // keep the first reason
- if (m_toInstallComponentIdReasonHash.contains(component->name()))
- return;
- m_toInstallComponentIdReasonHash.insert(component->name(),
- qMakePair(installReason, referencedComponentName));
}
-InstallerCalculator::InstallReasonType InstallerCalculator::installReasonType(Component *c) const
+InstallerCalculator::~InstallerCalculator()
{
- return m_toInstallComponentIdReasonHash.value(c->name(),
- qMakePair(InstallerCalculator::Selected, QString())).first;
}
-QString InstallerCalculator::installReasonReferencedComponent(Component *component) const
+bool InstallerCalculator::solve()
{
- return m_toInstallComponentIdReasonHash.value(component->name(),
- qMakePair(InstallerCalculator::Selected, QString())).second;
+ if (!solve(m_core->aliasesMarkedForInstallation()))
+ return false;
+
+ // Subtract components added by aliases
+ QList<Component *> components = m_core->componentsMarkedForInstallation();
+ for (auto *component : qAsConst(m_resolvedComponents))
+ components.removeAll(component);
+
+ return solve(components);
}
-QString InstallerCalculator::installReason(Component *component) const
+QString InstallerCalculator::resolutionText(Component *component) const
{
- InstallerCalculator::InstallReasonType reason = installReasonType(component);
+ const Resolution reason = resolutionType(component);
switch (reason) {
- case Automatic:
+ case Resolution::Automatic:
return QCoreApplication::translate("InstallerCalculator",
"Components added as automatic dependencies:");
- case Dependent:
+ case Resolution::Dependent:
return QCoreApplication::translate("InstallerCalculator", "Components added as "
- "dependency for \"%1\":").arg(installReasonReferencedComponent(component));
- case Resolved:
+ "dependency for \"%1\":").arg(referencedComponent(component));
+ case Resolution::Resolved:
return QCoreApplication::translate("InstallerCalculator",
"Components that have resolved dependencies:");
- case Selected:
+ case Resolution::Selected:
return QCoreApplication::translate("InstallerCalculator",
"Selected components without dependencies:");
+ case Resolution::Alias:
+ return QCoreApplication::translate("InstallerCalculator",
+ "Components selected by alias \"%1\":").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid install resolution detected!");
}
return QString();
}
-QList<Component*> InstallerCalculator::orderedComponentsToInstall() const
-{
- return m_orderedComponentsToInstall;
-}
-
-QString InstallerCalculator::componentsToInstallError() const
-{
- return m_componentsToInstallError;
-}
-
-void InstallerCalculator::realAppendToInstallComponents(Component *component, const QString &version)
-{
- if (!component->isInstalled(version) || component->updateRequested()) {
- m_orderedComponentsToInstall.append(component);
- m_toInstallComponentIds.insert(component->name());
- }
-}
-
-QString InstallerCalculator::recursionError(Component *component)
-{
- return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
- "already added with reason: \"%2\"").arg(component->name(), installReason(component));
-}
-
-bool InstallerCalculator::appendComponentsToInstall(const QList<Component *> &components)
+bool InstallerCalculator::solve(const QList<Component *> &components)
{
if (components.isEmpty())
return true;
QList<Component*> notAppendedComponents; // for example components with unresolved dependencies
- foreach (Component *component, components){
+ for (Component *component : qAsConst(components)){
+ if (!component)
+ continue;
if (m_toInstallComponentIds.contains(component->name())) {
const QString errorMessage = recursionError(component);
- qWarning().noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
Q_ASSERT_X(!m_toInstallComponentIds.contains(component->name()), Q_FUNC_INFO,
qPrintable(errorMessage));
return false;
}
- if (component->dependencies().isEmpty())
- realAppendToInstallComponents(component);
+ if (component->currentDependencies().isEmpty())
+ addComponentForInstall(component);
else
notAppendedComponents.append(component);
}
- foreach (Component *component, notAppendedComponents) {
- if (!appendComponentToInstall(component))
+ for (Component *component : qAsConst(notAppendedComponents)) {
+ if (!solveComponent(component))
return false;
}
- QList<Component *> foundAutoDependOnList;
// All regular dependencies are resolved. Now we are looking for auto depend on components.
- foreach (Component *component, m_allComponents) {
- // If a components is already installed or is scheduled for installation, no need to check
- // for auto depend installation.
- if ((!component->isInstalled() || component->updateRequested())
- && !m_toInstallComponentIds.contains(component->name())) {
- // If we figure out a component requests auto installation, keep it to resolve
- // their dependencies as well.
- if (component->isAutoDependOn(m_toInstallComponentIds)) {
- foundAutoDependOnList.append(component);
- insertInstallReason(component, InstallerCalculator::Automatic);
- }
+ QSet<Component *> foundAutoDependOnList = autodependencyComponents();
+ if (!foundAutoDependOnList.isEmpty())
+ return solve(foundAutoDependOnList.values());
+
+ return true;
+}
+
+bool InstallerCalculator::solve(const QList<ComponentAlias *> &aliases)
+{
+ if (aliases.isEmpty())
+ return true;
+
+ QList<ComponentAlias *> notAppendedAliases; // Aliases that require other aliases
+ for (auto *alias : aliases) {
+ if (!alias)
+ continue;
+
+ if (m_toInstallComponentAliases.contains(alias->name())) {
+ const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
+ "Recursion detected, component alias \"%1\" already added.").arg(alias->name());
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+
+ Q_ASSERT_X(!m_toInstallComponentAliases.contains(alias->name()), Q_FUNC_INFO,
+ qPrintable(errorMessage));
+
+ return false;
+ }
+
+ if (alias->aliases().isEmpty()) {
+ if (!addComponentsFromAlias(alias))
+ return false;
+ } else {
+ notAppendedAliases.append(alias);
}
}
- if (!foundAutoDependOnList.isEmpty())
- return appendComponentsToInstall(foundAutoDependOnList);
+ for (auto *alias : qAsConst(notAppendedAliases)) {
+ if (!solveAlias(alias))
+ return false;
+ }
+
return true;
}
-bool InstallerCalculator::appendComponentToInstall(Component *component, const QString &version)
+void InstallerCalculator::addComponentForInstall(Component *component, const QString &version)
+{
+ if (!m_componentsForAutodepencencyCheck.contains(component))
+ m_componentsForAutodepencencyCheck.append(component);
+
+ if (!component->isInstalled(version) || (m_core->isUpdater() && component->isUpdateAvailable())) {
+ m_resolvedComponents.append(component);
+ m_toInstallComponentIds.insert(component->name());
+ }
+}
+
+bool InstallerCalculator::addComponentsFromAlias(ComponentAlias *alias)
+{
+ QList<Component *> componentsToAdd;
+ for (auto *component : alias->components()) {
+ if (m_toInstallComponentIds.contains(component->name()))
+ continue; // Already added
+
+ componentsToAdd.append(component);
+ // Updates the model, so that we also check the descendant
+ // components when calculating components to install
+ updateCheckState(component, Qt::Checked);
+ insertResolution(component, Resolution::Alias, alias->name());
+ }
+
+ m_toInstallComponentAliases.insert(alias->name());
+ return solve(componentsToAdd);
+}
+
+QString InstallerCalculator::recursionError(Component *component) const
+{
+ return QCoreApplication::translate("InstallerCalculator", "Recursion detected, component \"%1\" "
+ "already added with reason: \"%2\"").arg(component->name(), resolutionText(component));
+}
+
+bool InstallerCalculator::updateCheckState(Component *component, Qt::CheckState state)
+{
+ ComponentModel *currentModel = m_core->isUpdater()
+ ? m_core->updaterComponentModel()
+ : m_core->defaultComponentModel();
+
+ const QModelIndex &idx = currentModel->indexFromComponentName(component->treeName());
+ return currentModel->setData(idx, state, Qt::CheckStateRole);
+}
+
+bool InstallerCalculator::solveComponent(Component *component, const QString &version)
{
- QSet<QString> allDependencies = component->dependencies().toSet();
+ const QStringList dependenciesList = component->currentDependencies();
QString requiredDependencyVersion = version;
- foreach (const QString &dependencyComponentName, allDependencies) {
+ for (const QString &dependencyComponentName : dependenciesList) {
// PackageManagerCore::componentByName returns 0 if dependencyComponentName contains a
// version which is not available
- Component *dependencyComponent =
- PackageManagerCore::componentByName(dependencyComponentName, m_allComponents);
+ Component *dependencyComponent = m_core->componentByName(dependencyComponentName);
if (!dependencyComponent) {
const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
"Cannot find missing dependency \"%1\" for \"%2\".").arg(dependencyComponentName,
component->name());
- qWarning().noquote() << errorMessage;
- m_componentsToInstallError.append(errorMessage);
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
if (component->packageManagerCore()->settings().allowUnstableComponents()) {
- component->setUnstable(PackageManagerCore::UnstableError::MissingDependency, errorMessage);
+ component->setUnstable(Component::UnstableError::MissingDependency, errorMessage);
continue;
} else {
return false;
@@ -184,11 +239,13 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
PackageManagerCore::parseNameAndVersion(dependencyComponentName, &requiredName, &requiredVersion);
if (!requiredVersion.isEmpty() &&
!dependencyComponent->value(scInstalledVersion).isEmpty()) {
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString installedVersion = compEx.exactMatch(dependencyComponent->value(scInstalledVersion)) ?
- compEx.cap(2) : dependencyComponent->value(scInstalledVersion);
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ QRegularExpressionMatch match = compEx.match(dependencyComponent->value(scInstalledVersion));
+ const QString installedVersion = match.hasMatch()
+ ? match.captured(2) : dependencyComponent->value(scInstalledVersion);
- requiredVersion = compEx.exactMatch(requiredVersion) ? compEx.cap(2) : requiredVersion;
+ match = compEx.match(requiredVersion);
+ requiredVersion = match.hasMatch() ? match.captured(2) : requiredVersion;
if (KDUpdater::compareVersion(requiredVersion, installedVersion) >= 1 ) {
isUpdateRequired = true;
@@ -204,28 +261,76 @@ bool InstallerCalculator::appendComponentToInstall(Component *component, const Q
&& !m_toInstallComponentIds.contains(component->name()))) {
if (m_visitedComponents.value(component).contains(dependencyComponent)) {
const QString errorMessage = recursionError(component);
- qWarning().noquote() << errorMessage;
- m_componentsToInstallError = errorMessage;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString = errorMessage;
Q_ASSERT_X(!m_visitedComponents.value(component).contains(dependencyComponent),
Q_FUNC_INFO, qPrintable(errorMessage));
return false;
}
m_visitedComponents[component].insert(dependencyComponent);
-
// add needed dependency components to the next run
- insertInstallReason(dependencyComponent, InstallerCalculator::Dependent,
+ insertResolution(dependencyComponent, Resolution::Dependent,
component->name());
- if (!appendComponentToInstall(dependencyComponent, requiredDependencyVersion))
+ if (!solveComponent(dependencyComponent, requiredDependencyVersion))
return false;
}
}
if (!m_toInstallComponentIds.contains(component->name())) {
- realAppendToInstallComponents(component, requiredDependencyVersion);
- insertInstallReason(component, InstallerCalculator::Resolved);
+ addComponentForInstall(component, requiredDependencyVersion);
+ insertResolution(component, Resolution::Resolved);
}
return true;
}
+bool InstallerCalculator::solveAlias(ComponentAlias *alias)
+{
+ for (auto *requiredAlias : alias->aliases()) {
+ if (!solveAlias(requiredAlias))
+ return false;
+ }
+
+ if (m_toInstallComponentAliases.contains(alias->name()))
+ return true;
+
+ return addComponentsFromAlias(alias);
+}
+
+QSet<Component *> InstallerCalculator::autodependencyComponents()
+{
+ // All regular dependencies are resolved. Now we are looking for auto depend on components.
+ // m_componentsForAutodepencencyCheck is a list of recently calculated components to be installed
+ // (normal components and regular dependencies components), and we check possible installable auto
+ // dependency components based on that list.
+ QSet<Component *> foundAutoDependOnList;
+ for (const Component *component : qAsConst(m_componentsForAutodepencencyCheck)) {
+ if (!m_autoDependencyComponentHash.contains(component->name())
+ || (m_core->isUpdater() && !component->updateRequested()))
+ continue;
+ for (const QString& autoDependency : m_autoDependencyComponentHash.value(component->name())) {
+ // If a components is already installed or is scheduled for installation, no need to check
+ // for auto depend installation.
+ if (m_toInstallComponentIds.contains(autoDependency)) {
+ continue;
+ }
+ Component *autoDependComponent = m_core->componentByName(autoDependency);
+ if (!autoDependComponent)
+ continue;
+ if ((!autoDependComponent->isInstalled()
+ || (m_core->isUpdater() && autoDependComponent->isUpdateAvailable()))
+ && !m_toInstallComponentIds.contains(autoDependComponent->name())) {
+ // One of the components autodependons is requested for install, check if there
+ // are other autodependencies as well
+ if (autoDependComponent->isAutoDependOn(m_toInstallComponentIds)) {
+ foundAutoDependOnList.insert(autoDependComponent);
+ insertResolution(autoDependComponent, Resolution::Automatic);
+ }
+ }
+ }
+ }
+ m_componentsForAutodepencencyCheck.clear();
+ return foundAutoDependOnList;
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/installercalculator.h b/src/libs/installer/installercalculator.h
index b2d05bdbe..e542dc664 100644
--- a/src/libs/installer/installercalculator.h
+++ b/src/libs/installer/installercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#define INSTALLERCALCULATOR_H
#include "installer_global.h"
+#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -38,48 +40,41 @@
namespace QInstaller {
class Component;
+class ComponentAlias;
+class PackageManagerCore;
-class INSTALLER_EXPORT InstallerCalculator
+class INSTALLER_EXPORT InstallerCalculator : public CalculatorBase
{
public:
- InstallerCalculator(const QList<Component *> &allComponents);
+ InstallerCalculator(PackageManagerCore *core, const AutoDependencyHash &autoDependencyComponentHash);
+ ~InstallerCalculator();
- enum InstallReasonType
- {
- Automatic, // "Component(s) added as automatic dependencies"
- Dependent, // "Added as dependency for %1."
- Resolved, // "Component(s) that have resolved Dependencies"
- Selected // "Selected Component(s) without Dependencies"
- };
+ bool solve();
+ bool solve(const QList<Component *> &components) override;
+ bool solve(const QList<ComponentAlias *> &aliases);
- InstallReasonType installReasonType(Component *component) const;
- QString installReasonReferencedComponent(Component *component) const;
- QString installReason(Component *component) const;
- QList<Component*> orderedComponentsToInstall() const;
- QString componentsToInstallError() const;
-
- bool appendComponentsToInstall(const QList<Component*> &components);
+ QString resolutionText(Component *component) const override;
private:
- void insertInstallReason(Component *component,
- InstallReasonType installReasonType,
- const QString &referencedComponentName = QString());
- void realAppendToInstallComponents(Component *component, const QString &version = QString());
- bool appendComponentToInstall(Component *components, const QString &version = QString());
- QString recursionError(Component *component);
+ bool solveComponent(Component *component, const QString &version = QString()) override;
+ bool solveAlias(ComponentAlias *alias);
+
+ void addComponentForInstall(Component *component, const QString &version = QString());
+ bool addComponentsFromAlias(ComponentAlias *alias);
+ QSet<Component *> autodependencyComponents();
+ QString recursionError(Component *component) const;
+
+ bool updateCheckState(Component *component, Qt::CheckState state);
- QList<Component*> m_allComponents;
+private:
QHash<Component*, QSet<Component*> > m_visitedComponents;
+ QList<const Component*> m_componentsForAutodepencencyCheck;
QSet<QString> m_toInstallComponentIds; //for faster lookups
- QString m_componentsToInstallError;
- //calculate installation order variables
- QList<Component*> m_orderedComponentsToInstall;
- //we can't use this reason hash as component id hash, because some reasons are ready before
- //the component is added
- QHash<QString, QPair<InstallReasonType, QString> > m_toInstallComponentIdReasonHash;
+ QSet<QString> m_toInstallComponentAliases;
+ //Helper hash for quicker search for autodependency components
+ AutoDependencyHash m_autoDependencyComponentHash;
};
-}
-
+} // namespace QInstaller
#endif // INSTALLERCALCULATOR_H
diff --git a/src/libs/installer/installiconsoperation.cpp b/src/libs/installer/installiconsoperation.cpp
index 15d47c72a..b21634cd7 100644
--- a/src/libs/installer/installiconsoperation.cpp
+++ b/src/libs/installer/installiconsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,10 @@
#include "fileutils.h"
#include "packagemanagercore.h"
+#include "globals.h"
+#include "adminauthorization.h"
+#include "remoteclient.h"
+#include "errors.h"
#include <QDebug>
#include <QDir>
@@ -36,16 +40,25 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::InstallIconsOperation
+ \internal
+*/
+
QString InstallIconsOperation::targetDirectory()
{
// we're not searching for the first time, let's re-use the old value
- if (hasValue(QLatin1String("targetdirectory")))
- return value(QLatin1String("targetdirectory")).toString();
+ if (hasValue(QLatin1String("directory")))
+ return value(QLatin1String("directory")).toString();
QStringList XDG_DATA_HOME = QString::fromLocal8Bit(qgetenv("XDG_DATA_HOME"))
.split(QLatin1Char(':'),
- QString::SkipEmptyParts);
- XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share/icons"))); // default path
+ Qt::SkipEmptyParts);
+ XDG_DATA_HOME.push_back(QDir::home().absoluteFilePath(QLatin1String(".local/share"))); // default user-specific path
+
+ if (packageManager() && packageManager()->hasAdminRights())
+ XDG_DATA_HOME.push_front(QLatin1String("/usr/local/share")); // default system-wide path
QString directory;
const QStringList& directories = XDG_DATA_HOME;
@@ -53,11 +66,7 @@ QString InstallIconsOperation::targetDirectory()
if (it->isEmpty())
continue;
- // our default dirs are correct, XDG_DATA_HOME set via env needs "icon" at the end
- if ((it + 1 == directories.end()) || (it + 2 == directories.end()) || (it + 3 == directories.end()))
- directory = QDir(*it).absolutePath();
- else
- directory = QDir(*it).absoluteFilePath(QLatin1String("icons"));
+ directory = QDir(*it).absoluteFilePath(QLatin1String("icons"));
QDir dir(directory);
// let's see if this dir exists or we're able to create it
@@ -89,11 +98,6 @@ InstallIconsOperation::InstallIconsOperation(PackageManagerCore *core)
setName(QLatin1String("InstallIcons"));
}
-InstallIconsOperation::~InstallIconsOperation()
-{
- const QStringList backupFiles = value(QLatin1String("backupfiles")).toStringList();
-}
-
void InstallIconsOperation::backup()
{
// we backup on the fly
@@ -131,8 +135,8 @@ bool InstallIconsOperation::performOperation()
if (status == PackageManagerCore::Canceled || status == PackageManagerCore::Failure)
return true;
- const QString source = it.next();
- QString target = targetDir.absoluteFilePath(sourceDir.relativeFilePath(source));
+ const QString &source2 = it.next();
+ QString target = targetDir.absoluteFilePath(sourceDir.relativeFilePath(source2));
emit outputTextChanged(target);
@@ -155,7 +159,16 @@ bool InstallIconsOperation::performOperation()
if (QFile(target).exists()) {
// first backup...
- const QString backup = generateTemporaryFileName(target);
+ QString backup;
+ try {
+ backup = generateTemporaryFileName(target);
+ } catch (const QInstaller::Error &e) {
+ setError(UserDefinedError);
+ setErrorString(tr("Cannot prepare to backup file \"%1\": %2")
+ .arg(QDir::toNativeSeparators(target), e.message()));
+ undoOperation();
+ return false;
+ }
QFile bf(target);
if (!bf.copy(backup)) {
setError(UserDefinedError);
@@ -182,7 +195,7 @@ bool InstallIconsOperation::performOperation()
}
// copy the file to its new location
- QFile cf(source);
+ QFile cf(source2);
if (!cf.copy(target)) {
setError(UserDefinedError);
setErrorString(tr("Failed to copy file \"%1\": %2").arg(
@@ -190,8 +203,8 @@ bool InstallIconsOperation::performOperation()
undoOperation();
return false;
}
- deleteFileNowOrLater(source);
- files.push_back(source);
+ deleteFileNowOrLater(source2);
+ files.push_back(source2);
files.push_back(target);
setValue(QLatin1String("files"), files);
} else if (fi.isDir() && !QDir(target).exists()) {
@@ -265,10 +278,10 @@ bool InstallIconsOperation::undoOperation()
}
if (!warningMessages.isEmpty()) {
- qWarning() << "Undo of operation" << name() << "with arguments"
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Undo of operation" << name() << "with arguments"
<< arguments().join(QLatin1String(", ")) << "had some problems.";
foreach (const QString &message, warningMessages) {
- qWarning().noquote() << message;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << message;
}
}
diff --git a/src/libs/installer/installiconsoperation.h b/src/libs/installer/installiconsoperation.h
index 5f8dff514..e7a8dd871 100644
--- a/src/libs/installer/installiconsoperation.h
+++ b/src/libs/installer/installiconsoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,12 +40,11 @@ class INSTALLER_EXPORT InstallIconsOperation : public QObject, public Operation
Q_OBJECT
public:
explicit InstallIconsOperation(PackageManagerCore *core);
- ~InstallIconsOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
Q_SIGNALS:
void outputTextChanged(const QString &progress);
diff --git a/src/libs/installer/keepaliveobject.cpp b/src/libs/installer/keepaliveobject.cpp
index f3b4e7779..18ec9743a 100644
--- a/src/libs/installer/keepaliveobject.cpp
+++ b/src/libs/installer/keepaliveobject.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::KeepAliveObject
+ \internal
+*/
+
KeepAliveObject::KeepAliveObject()
: m_timer(nullptr)
, m_socket(nullptr)
@@ -42,7 +48,12 @@ KeepAliveObject::KeepAliveObject()
void KeepAliveObject::start()
{
+ if (m_timer)
+ delete m_timer;
m_timer = new QTimer(this);
+
+ if (m_socket)
+ delete m_socket;
m_socket = new QLocalSocket(this);
connect(m_timer, &QTimer::timeout, [this]() {
diff --git a/src/libs/installer/lazyplaintextedit.cpp b/src/libs/installer/lazyplaintextedit.cpp
deleted file mode 100644
index a4a699a78..000000000
--- a/src/libs/installer/lazyplaintextedit.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/**************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Installer Framework.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-**************************************************************************/
-
-#include "lazyplaintextedit.h"
-
-#include <QScrollBar>
-
-const int INTERVAL = 20;
-
-LazyPlainTextEdit::LazyPlainTextEdit(QWidget *parent)
- : QPlainTextEdit(parent)
- , m_timerId(0)
-{
-}
-
-void LazyPlainTextEdit::timerEvent(QTimerEvent *event)
-{
- if (event->timerId() == m_timerId) {
- killTimer(m_timerId);
- m_timerId = 0;
- m_cachedOutput.chop(1); //removes the last \n
- if (!m_cachedOutput.isEmpty()) {
- appendPlainText(m_cachedOutput);
- updateCursor(TextCursorPosition::Keep);
- horizontalScrollBar()->setValue(0);
- m_cachedOutput.clear();
- }
- }
-}
-
-void LazyPlainTextEdit::append(const QString &text)
-{
- m_cachedOutput.append(text + QLatin1String("\n"));
- if (isVisible() && m_timerId == 0)
- m_timerId = startTimer(INTERVAL);
-}
-
-void LazyPlainTextEdit::clear()
-{
- if (m_timerId) {
- killTimer(m_timerId);
- m_timerId = 0;
- m_cachedOutput.clear();
- }
- QPlainTextEdit::clear();
-}
-
-void LazyPlainTextEdit::setVisible(bool visible)
-{
- if (m_timerId) {
- killTimer(m_timerId);
- m_timerId = 0;
- }
-
- if (visible)
- m_timerId = startTimer(INTERVAL);
-
- QPlainTextEdit::setVisible(visible);
- updateCursor(TextCursorPosition::Keep);
-}
-
-void LazyPlainTextEdit::updateCursor(TextCursorPosition position)
-{
- QTextCursor cursor = textCursor();
- if ((position == TextCursorPosition::ForceEnd) || cursor.atEnd()) {
- // Workaround for height calculation issue if scrollbar is set to Qt::ScrollBarAsNeeded.
- Qt::ScrollBarPolicy policy = horizontalScrollBarPolicy();
- setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); // enforce always on
-
- cursor.movePosition(QTextCursor::End);
- setTextCursor(cursor);
- ensureCursorVisible();
-
- setHorizontalScrollBarPolicy(policy); // but reset once we updated the cursor position
- }
-}
diff --git a/src/libs/installer/lib7z_create.h b/src/libs/installer/lib7z_create.h
index ad308ffd1..b908b028f 100644
--- a/src/libs/installer/lib7z_create.h
+++ b/src/libs/installer/lib7z_create.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
@@ -30,30 +30,25 @@
#define LIB7Z_CREATE_H
#include "installer_global.h"
+#include "abstractarchive.h"
#include <Common/MyCom.h>
#include <7zip/UI/Common/Update.h>
+#include <QStringList>
+
QT_BEGIN_NAMESPACE
class QFileDevice;
-class QStringList;
QT_END_NAMESPACE
namespace Lib7z
{
- enum struct QTmpFile {
+ enum struct TmpFile {
No,
Yes
};
- enum struct Compression {
- Non = 0,
- Fastest = 1,
- Fast = 3,
- Normal = 5,
- Maximum = 7,
- Ultra = 9
- };
+ typedef QInstaller::AbstractArchive::CompressionLevel Compression;
class INSTALLER_EXPORT UpdateCallback : public IUpdateCallbackUI2, public CMyUnknownImp
{
@@ -70,7 +65,7 @@ namespace Lib7z
void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sources,
Compression level = Compression::Normal, UpdateCallback *callback = 0);
void INSTALLER_EXPORT createArchive(const QString &archive, const QStringList &sources,
- QTmpFile mode, Compression level = Compression::Normal, UpdateCallback *callback = 0);
+ TmpFile mode, Compression level = Compression::Normal, UpdateCallback *callback = 0);
} // namespace Lib7z
diff --git a/src/libs/installer/lib7z_extract.h b/src/libs/installer/lib7z_extract.h
index f6182c727..6465a8cfc 100644
--- a/src/libs/installer/lib7z_extract.h
+++ b/src/libs/installer/lib7z_extract.h
@@ -1,32 +1,26 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
diff --git a/src/libs/installer/lib7z_facade.cpp b/src/libs/installer/lib7z_facade.cpp
index b3b3319e8..e0d8a53e4 100644
--- a/src/libs/installer/lib7z_facade.cpp
+++ b/src/libs/installer/lib7z_facade.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,6 +35,9 @@
#include "lib7z_extract.h"
#include "lib7z_list.h"
#include "lib7z_guid.h"
+#include "globals.h"
+#include "directoryguard.h"
+#include "fileguard.h"
#ifndef Q_OS_WIN
# include "StdAfx.h"
@@ -114,11 +117,85 @@ void registerCodecByteSwap();
namespace Lib7z {
+/*!
+ \inmodule Lib7z
+ \class Lib7z::File
+ \internal
+*/
+
+/*!
+ \inmodule Lib7z
+ \class Lib7z::UpdateCallback
+ \internal
+*/
+
+/*!
+ \inmodule Lib7z
+ \class Lib7z::SevenZipException
+ \brief The SevenZipException provides a class for lib7z exceptions.
+*/
+
+/*!
+ \fn explicit Lib7z::SevenZipException::SevenZipException(const QString &msg)
+
+ Constructs an instance of SevenZipException with error message \a msg.
+*/
+
+/*!
+ \fn explicit Lib7z::SevenZipException::SevenZipException(const char *msg)
+
+ Constructs an instance of SevenZipException with error message \a msg.
+*/
+
+/*!
+ \inmodule Lib7z
+ \class Lib7z::PercentPrinter
+ \brief The PercentPrinter class displays the archiving process.
+*/
+
+/*!
+ \fn void Lib7z::PercentPrinter::PrintRatio()
+
+ Prints ratio.
+*/
+
+/*!
+ \fn void Lib7z::PercentPrinter::ClosePrint()
+
+ Closes print.
+*/
+
+/*!
+ \fn void Lib7z::PercentPrinter::RePrintRatio()
+
+ Reprints ratio.
+*/
+
+/*!
+ \fn void Lib7z::PercentPrinter::PrintNewLine()
+
+ Prints new line.
+*/
+
+/*!
+ \fn void Lib7z::PercentPrinter::PrintString(const char *s)
+
+ Prints string \a s.
+*/
+
+/*!
+ \fn void Lib7z::PercentPrinter::PrintString(const wchar_t *s)
+
+ Prints string \a s.
+*/
// -- 7z init codecs, archives
std::once_flag gOnceFlag;
+/*!
+ Initializes 7z and registers codecs and compression methods.
+*/
void initSevenZ()
{
std::call_once(gOnceFlag, [] {
@@ -180,7 +257,7 @@ QString errorMessageFrom7zResult(const LONG &extractResult)
if (!lastError().isEmpty())
return lastError();
- QString errorMessage = QCoreApplication::translate("Lib7z", "internal code: %1");
+ QString errorMessage = QCoreApplication::translate("Lib7z", "Internal code: %1");
switch (extractResult) {
case S_OK:
qFatal("S_OK value is not a valid error code.");
@@ -201,7 +278,7 @@ QString errorMessageFrom7zResult(const LONG &extractResult)
errorMessage = errorMessage.arg(QLatin1String("STG_E_INVALIDFUNCTION"));
break;
case E_OUTOFMEMORY:
- errorMessage = QCoreApplication::translate("Lib7z", "not enough memory");
+ errorMessage = QCoreApplication::translate("Lib7z", "Not enough memory");
break;
case E_INVALIDARG:
errorMessage = errorMessage.arg(QLatin1String("E_INVALIDARG"));
@@ -213,73 +290,6 @@ QString errorMessageFrom7zResult(const LONG &extractResult)
return errorMessage;
}
-/*!
- RAII class to create a directory (tryCreate()) and delete it on destruction unless released.
-*/
-struct DirectoryGuard
-{
- explicit DirectoryGuard(const QString &path)
- : m_path(path)
- , m_created(false)
- , m_released(false)
- {
- m_path.replace(QLatin1Char('\\'), QLatin1Char('/'));
- }
-
- ~DirectoryGuard()
- {
- if (!m_created || m_released)
- return;
- QDir dir(m_path);
- if (!dir.rmdir(m_path))
- qWarning() << "Cannot delete directory " << m_path;
- }
-
- /*!
- Tries to create the directory structure.
- Returns a list of every directory created.
- */
- QStringList tryCreate()
- {
- if (m_path.isEmpty())
- return QStringList();
-
- const QFileInfo fi(m_path);
- if (fi.exists() && fi.isDir())
- return QStringList();
- if (fi.exists() && !fi.isDir()) {
- throw SevenZipException(QCoreApplication::translate("DirectoryGuard",
- "Path \"%1\" exists but is not a directory.").arg(QDir::toNativeSeparators(m_path)));
- }
- QStringList created;
-
- QDir toCreate(m_path);
- while (!toCreate.exists()) {
- QString p = toCreate.absolutePath();
- created.push_front(p);
- p = p.section(QLatin1Char('/'), 0, -2);
- toCreate = QDir(p);
- }
-
- QDir dir(m_path);
- m_created = dir.mkpath(m_path);
- if (!m_created) {
- throw SevenZipException(QCoreApplication::translate("DirectoryGuard",
- "Cannot create directory \"%1\".").arg(QDir::toNativeSeparators(m_path)));
- }
- return created;
- }
-
- void release()
- {
- m_released = true;
- }
-
- QString m_path;
- bool m_created;
- bool m_released;
-};
-
static UString QString2UString(const QString &str)
{
return str.toStdWString().c_str();
@@ -353,7 +363,7 @@ static quint32 getUInt32Property(IInArchive *archive, int index, int propId, qui
static QFile::Permissions getPermissions(IInArchive *archive, int index, bool *hasPermissions)
{
quint32 attributes = getUInt32Property(archive, index, kpidAttrib, 0);
- QFile::Permissions permissions = nullptr;
+ QFile::Permissions permissions = QFile::Permissions();
if (attributes & FILE_ATTRIBUTE_UNIX_EXTENSION) {
if (hasPermissions != nullptr)
*hasPermissions = true;
@@ -473,18 +483,9 @@ private:
QPointer<QIODevice> m_device;
};
-bool operator==(const File &lhs, const File &rhs)
-{
- return lhs.path == rhs.path
- && lhs.utcTime == rhs.utcTime
- && lhs.isDirectory == rhs.isDirectory
- && lhs.compressedSize == rhs.compressedSize
- && lhs.uncompressedSize == rhs.uncompressedSize
- && (lhs.permissions == rhs.permissions
- || lhs.permissions == static_cast<QFile::Permissions>(-1)
- || rhs.permissions == static_cast<QFile::Permissions>(-1));
-}
-
+/*!
+ Returns a list of files belonging to an \a archive.
+*/
QVector<File> listArchive(QFileDevice *archive)
{
LIB7Z_ASSERTS(archive, Readable)
@@ -502,6 +503,8 @@ QVector<File> listArchive(QFileDevice *archive)
op.types = &types; // Empty, because we use a stream.
CIntVector excluded;
+ excluded.Add(codecs.FindFormatForExtension(
+ QString2UString(QLatin1String("xz")))); // handled by libarchive
op.excludedFormats = &excluded;
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
@@ -536,7 +539,8 @@ QVector<File> listArchive(QFileDevice *archive)
f.archiveIndex.setY(item);
f.path = UString2QString(s).replace(QLatin1Char('\\'), QLatin1Char('/'));
Archive_IsItem_Folder(arch, item, f.isDirectory);
- f.permissions = getPermissions(arch, item, nullptr);
+ Archive_GetItemBoolProp(arch, item, kpidSymLink, f.isSymbolicLink);
+ f.permissions_enum = getPermissions(arch, item, nullptr);
getDateTimeProperty(arch, item, kpidMTime, &(f.utcTime));
f.uncompressedSize = getUInt64Property(arch, item, kpidSize, 0);
f.compressedSize = getUInt64Property(arch, item, kpidPackSize, 0);
@@ -558,15 +562,24 @@ QVector<File> listArchive(QFileDevice *archive)
return QVector<File>(); // never reached
}
+/*!
+ \inmodule Lib7z
+ \class Lib7z::ExtractCallback
+ \brief Provides a callback for archive extraction.
+*/
-// -- ExtractCallback
-
+/*!
+ \internal
+*/
STDMETHODIMP ExtractCallback::SetTotal(UInt64 t)
{
total = t;
return S_OK;
}
+/*!
+ \internal
+*/
STDMETHODIMP ExtractCallback::SetCompleted(const UInt64 *c)
{
completed = *c;
@@ -575,8 +588,12 @@ STDMETHODIMP ExtractCallback::SetCompleted(const UInt64 *c)
return S_OK;
}
-// this method will be called by CFolderOutStream::OpenFile to stream via
-// CDecoder::CodeSpec extracted content to an output stream.
+/*!
+ \internal
+
+ This method will be called by CFolderOutStream::OpenFile to stream via
+ CDecoder::CodeSpec extracted content to an output stream.
+*/
STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 /*askExtractMode*/)
{
*outStream = nullptr;
@@ -595,7 +612,7 @@ STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **out
const QFileInfo fi(QString::fromLatin1("%1/%2").arg(targetDir, UString2QString(s)));
- DirectoryGuard guard(fi.absolutePath());
+ QInstaller::DirectoryGuard guard(fi.absolutePath());
const QStringList directories = guard.tryCreate();
bool isDir = false;
@@ -607,6 +624,11 @@ STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **out
foreach (const QString &directory, directories)
setCurrentFile(directory);
+ QScopedPointer<QInstaller::FileGuardLocker> locker(nullptr);
+ if (!isDir) {
+ locker.reset(new QInstaller::FileGuardLocker(
+ fi.absoluteFilePath(), QInstaller::FileGuard::globalObject()));
+ }
if (!isDir && !prepareForFile(fi.absoluteFilePath()))
return E_FAIL;
@@ -637,11 +659,17 @@ STDMETHODIMP ExtractCallback::GetStream(UInt32 index, ISequentialOutStream **out
return S_OK;
}
+/*!
+ \internal
+*/
STDMETHODIMP ExtractCallback::PrepareOperation(Int32 /*askExtractMode*/)
{
return S_OK;
}
+/*!
+ \internal
+*/
STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*/)
{
if (targetDir.isEmpty())
@@ -732,6 +760,29 @@ STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*
}
/*!
+ \enum Lib7z::TmpFile
+
+ This enum type holds the temp file mode:
+
+ \value No
+ File is not a temporary file.
+ \value Yes
+ File is a tmp file.
+*/
+
+/*!
+ \typedef Lib7z::Compression
+
+ Synonym for QInstaller::CompressionLevel
+*/
+
+/*!
+ \typedef Lib7z::File
+
+ Synonym for QInstaller::ArchiveEntry
+*/
+
+/*!
\namespace Lib7z
\inmodule QtInstallerFramework
\brief The Lib7z namespace contains miscellaneous identifiers used throughout the Lib7z library.
@@ -745,6 +796,47 @@ STDMETHODIMP ExtractCallback::SetOperationResult(Int32 /*resultEOperationResult*
is returned, the extraction will be aborted. The default implementation returns \c true.
*/
+/*!
+ \fn bool Lib7z::ExtractCallback::setArchive(CArc *carc)
+
+ Sets \a carc as archive.
+*/
+
+/*!
+ \fn void Lib7z::ExtractCallback::setTarget(const QString &dir)
+
+ Sets the target directory to \a dir.
+*/
+
+/*!
+ \fn void Lib7z::ExtractCallback::setCurrentFile(const QString &filename)
+
+ Sets the current file to \a filename.
+*/
+
+/*!
+ \fn virtual Lib7z::ExtractCallback::setCompleted(quint64 completed, quint64 total)
+
+ Always returns \c true.
+*/
+
+/*!
+ \fn ULONG Lib7z::ExtractCallback::AddRef()
+
+ \internal
+*/
+
+/*!
+ \fn LONG Lib7z::ExtractCallback::QueryInterface(const GUID &iid, void **outObject)
+
+ \internal
+*/
+
+/*!
+ \fn ULONG Lib7z::ExtractCallback::Release()
+
+ \internal
+*/
// -- UpdateCallback
@@ -841,7 +933,7 @@ HRESULT UpdateCallback::SetOperationResult(Int32)
return S_OK;
}
-/*!
+/*
Function to create an empty 7z container. Using a temporary file only is not working, since
7z checks the output file for a valid signature, otherwise it rejects overwriting the file.
*/
@@ -861,9 +953,9 @@ static QString createTmp7z()
}
/*!
- Creates an archive using the given file device \a archive. \a sourcePaths can contain one or
- more files, one or more directories or a combination of files and folders. The \c * wildcard
- is supported also. The value of \a level specifies the compression ratio, the default is set
+ Creates an archive using the given file device \a archive. \a sources can contain one or
+ more files, one or more directories or a combination of files and folders. Also, \c * wildcard
+ is supported. The value of \a level specifies the compression ratio, the default is set
to \c 5 (Normal compression). The \a callback can be used to get information about the archive
creation process. If no \a callback is given, an empty implementation is used.
@@ -877,7 +969,7 @@ void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sou
LIB7Z_ASSERTS(archive, Writable)
const QString tmpArchive = createTmp7z();
- Lib7z::createArchive(tmpArchive, sources, QTmpFile::No, level, callback);
+ Lib7z::createArchive(tmpArchive, sources, TmpFile::No, level, callback);
try {
QFile source(tmpArchive);
@@ -889,10 +981,10 @@ void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sou
}
/*!
- Creates an archive with the given filename \a archive. \a sourcePaths can contain one or more
- files, one or more directories or a combination of files and folders. Also the \c * wildcard
+ Creates an archive with the given filename \a archive. \a sources can contain one or more
+ files, one or more directories or a combination of files and folders. Also, \c * wildcard
is supported. To be able to use the function during an elevated installation, set \a mode to
- \c QTmpFile::Yes. The value of \a level specifies the compression ratio, the default is set
+ \c TmpFile::Yes. The value of \a level specifies the compression ratio, the default is set
to \c 5 (Normal compression). The \a callback can be used to get information about the archive
creation process. If no \a callback is given, an empty implementation is used.
@@ -901,12 +993,12 @@ void INSTALLER_EXPORT createArchive(QFileDevice *archive, const QStringList &sou
\note Filenames are stored case-sensitive with UTF-8 encoding.
\note The ownership of \a callback is transferred to the function and gets delete on exit.
*/
-void createArchive(const QString &archive, const QStringList &sources, QTmpFile mode,
+void createArchive(const QString &archive, const QStringList &sources, TmpFile mode,
Compression level, UpdateCallback *callback)
{
try {
QString target = archive;
- if (mode == QTmpFile::Yes)
+ if (mode == TmpFile::Yes)
target = createTmp7z();
CArcCmdLineOptions options;
@@ -959,7 +1051,7 @@ void createArchive(const QString &archive, const QStringList &sources, QTmpFile
throw SevenZipException(errorMsg);
}
- if (mode == QTmpFile::Yes) {
+ if (mode == TmpFile::Yes) {
QFile org(archive);
if (org.exists() && !org.remove()) {
throw SevenZipException(QCoreApplication::translate("Lib7z", "Cannot remove "
@@ -1008,7 +1100,7 @@ void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallb
localCallback = callback;
}
- DirectoryGuard outDir(QFileInfo(directory).absolutePath());
+ QInstaller::DirectoryGuard outDir(QFileInfo(directory).absolutePath());
try {
outDir.tryCreate();
@@ -1023,6 +1115,8 @@ void extractArchive(QFileDevice *archive, const QString &directory, ExtractCallb
op.types = &types; // Empty, because we use a stream.
CIntVector excluded;
+ excluded.Add(codecs.FindFormatForExtension(
+ QString2UString(QLatin1String("xz")))); // handled by libarchive
op.excludedFormats = &excluded;
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
@@ -1080,6 +1174,8 @@ bool isSupportedArchive(QFileDevice *archive)
op.types = &types; // Empty, because we use a stream.
CIntVector excluded;
+ excluded.Add(codecs.FindFormatForExtension(
+ QString2UString(QLatin1String("xz")))); // handled by libarchive
op.excludedFormats = &excluded;
const CMyComPtr<IInStream> stream = new QIODeviceInStream(archive);
diff --git a/src/libs/installer/lib7z_guid.h b/src/libs/installer/lib7z_guid.h
index 16434558c..adc1aa948 100644
--- a/src/libs/installer/lib7z_guid.h
+++ b/src/libs/installer/lib7z_guid.h
@@ -1,32 +1,26 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
diff --git a/src/libs/installer/lib7z_list.h b/src/libs/installer/lib7z_list.h
index 6e2646025..2dec655b1 100644
--- a/src/libs/installer/lib7z_list.h
+++ b/src/libs/installer/lib7z_list.h
@@ -1,32 +1,26 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
@@ -35,6 +29,7 @@
#define LIB7Z_LIST_H
#include "installer_global.h"
+#include "abstractarchive.h"
#include <QDateTime>
#include <QFile>
@@ -42,18 +37,7 @@
namespace Lib7z
{
- struct INSTALLER_EXPORT File
- {
- public:
- QString path;
- QDateTime utcTime;
- QPoint archiveIndex;
- bool isDirectory = false;
- quint64 compressedSize = 0;
- quint64 uncompressedSize = 0;
- QFile::Permissions permissions = 0;
- };
- INSTALLER_EXPORT bool operator==(const File &lhs, const File &rhs);
+ typedef QInstaller::ArchiveEntry File;
QVector<File> INSTALLER_EXPORT listArchive(QFileDevice *archive);
diff --git a/src/libs/installer/lib7zarchive.cpp b/src/libs/installer/lib7zarchive.cpp
new file mode 100644
index 000000000..d7b0c0dc9
--- /dev/null
+++ b/src/libs/installer/lib7zarchive.cpp
@@ -0,0 +1,242 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "lib7zarchive.h"
+
+#include "errors.h"
+#include "lib7z_facade.h"
+#include "lib7z_create.h"
+#include "lib7z_list.h"
+
+#include <QCoreApplication>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Lib7zArchive
+ \brief The Lib7zArchive class represents an archive file
+ handled with the LZMA software development kit.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Lib7zArchive::ExtractCallbackWrapper
+ \internal
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename with \a parent as parent object.
+*/
+Lib7zArchive::Lib7zArchive(const QString &filename, QObject *parent)
+ : AbstractArchive(parent)
+ , m_extractCallback(new ExtractCallbackWrapper())
+{
+ Lib7zArchive::setFilename(filename);
+ listenExtractCallback();
+}
+
+/*!
+ Constructs an archive object with the given \a parent.
+*/
+Lib7zArchive::Lib7zArchive(QObject *parent)
+ : AbstractArchive(parent)
+ , m_extractCallback(new ExtractCallbackWrapper())
+{
+ listenExtractCallback();
+}
+
+/*!
+ Destroys the instance and releases resources.
+*/
+Lib7zArchive::~Lib7zArchive()
+{
+ delete m_extractCallback;
+}
+
+/*!
+ \reimp
+
+ Opens the underlying file device using \a mode. Returns \c true if
+ succesfull; otherwise \c false.
+*/
+bool Lib7zArchive::open(QIODevice::OpenMode mode)
+{
+ if (!m_file.open(mode)) {
+ setErrorString(m_file.errorString());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Closes the underlying file device.
+*/
+void Lib7zArchive::close()
+{
+ m_file.close();
+}
+
+/*!
+ \reimp
+
+ Sets the \a filename of the underlying file device.
+*/
+void Lib7zArchive::setFilename(const QString &filename)
+{
+ m_file.setFileName(filename);
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath.
+ Returns \c true on success; \c false otherwise.
+*/
+bool Lib7zArchive::extract(const QString &dirPath)
+{
+ m_extractCallback->setState(S_OK);
+ try {
+ Lib7z::extractArchive(&m_file, dirPath, m_extractCallback);
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath. The \a totalFiles
+ parameter is unused. Returns \c true on success; \c false otherwise.
+*/
+bool Lib7zArchive::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ Q_UNUSED(totalFiles)
+ return extract(dirPath);
+}
+
+/*!
+ \reimp
+
+ Packages the given \a data into the archive and creates the file on disk.
+*/
+bool Lib7zArchive::create(const QStringList &data)
+{
+ try {
+ // No support for callback yet.
+ Lib7z::createArchive(&m_file, data, compressionLevel());
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> Lib7zArchive::list()
+{
+ try {
+ return Lib7z::listArchive(&m_file);
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return QVector<ArchiveEntry>();
+ }
+}
+
+/*!
+ \reimp
+
+ Returns \c true if the current archive is of supported format;
+ \c false otherwise.
+*/
+bool Lib7zArchive::isSupported()
+{
+ try {
+ return Lib7z::isSupportedArchive(&m_file);
+ } catch (const Lib7z::SevenZipException &e) {
+ setErrorString(e.message());
+ return false;
+ }
+}
+
+/*!
+ \reimp
+
+ Cancels the extract operation in progress.
+*/
+void Lib7zArchive::cancel()
+{
+ m_extractCallback->setState(E_ABORT);
+}
+
+/*!
+ \internal
+*/
+void Lib7zArchive::listenExtractCallback()
+{
+ connect(m_extractCallback, &ExtractCallbackWrapper::currentEntryChanged,
+ this, &Lib7zArchive::currentEntryChanged);
+ connect(m_extractCallback, &ExtractCallbackWrapper::completedChanged,
+ this, &Lib7zArchive::completedChanged);
+}
+
+
+Lib7zArchive::ExtractCallbackWrapper::ExtractCallbackWrapper()
+ : m_state(S_OK)
+{
+}
+
+void Lib7zArchive::ExtractCallbackWrapper::setState(HRESULT state)
+{
+ m_state = state;
+}
+
+void Lib7zArchive::ExtractCallbackWrapper::setCurrentFile(const QString &filename)
+{
+ emit currentEntryChanged(filename);
+}
+
+HRESULT Lib7zArchive::ExtractCallbackWrapper::setCompleted(quint64 completed, quint64 total)
+{
+ qApp->processEvents();
+
+ emit completedChanged(completed, total);
+ return m_state;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/lib7zarchive.h b/src/libs/installer/lib7zarchive.h
new file mode 100644
index 000000000..f73670a4f
--- /dev/null
+++ b/src/libs/installer/lib7zarchive.h
@@ -0,0 +1,95 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIB7ZARCHIVE_H
+#define LIB7ZARCHIVE_H
+
+#include "installer_global.h"
+#include "abstractarchive.h"
+#include "lib7z_extract.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT Lib7zArchive : public AbstractArchive
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Lib7zArchive)
+
+public:
+ Lib7zArchive(const QString &filename, QObject *parent = nullptr);
+ explicit Lib7zArchive(QObject *parent = nullptr);
+ ~Lib7zArchive();
+
+ bool open(QIODevice::OpenMode mode) override;
+ void close() override;
+ void setFilename(const QString &filename) override;
+
+ bool extract(const QString &dirPath) override;
+ bool extract(const QString &dirPath, const quint64 totalFiles) override;
+ bool create(const QStringList &data) override;
+ QVector<ArchiveEntry> list() override;
+ bool isSupported() override;
+
+public Q_SLOTS:
+ void cancel() override;
+
+private:
+ void listenExtractCallback();
+
+ class ExtractCallbackWrapper;
+
+private:
+ QFile m_file;
+ ExtractCallbackWrapper *const m_extractCallback;
+};
+
+class Lib7zArchive::ExtractCallbackWrapper : public QObject, public Lib7z::ExtractCallback
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ExtractCallbackWrapper)
+
+public:
+ ExtractCallbackWrapper();
+
+ void setState(HRESULT state);
+
+Q_SIGNALS:
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(quint64 completed, quint64 total);
+
+private:
+ void setCurrentFile(const QString &filename) override;
+ HRESULT setCompleted(quint64 completed, quint64 total) override;
+
+private:
+ HRESULT m_state;
+};
+
+} // namespace QInstaller
+
+#endif // LIB7ZARCHIVE_H
diff --git a/src/libs/installer/libarchivearchive.cpp b/src/libs/installer/libarchivearchive.cpp
new file mode 100644
index 000000000..233a4c28b
--- /dev/null
+++ b/src/libs/installer/libarchivearchive.cpp
@@ -0,0 +1,1243 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "libarchivearchive.h"
+
+#include "directoryguard.h"
+#include "fileguard.h"
+#include "errors.h"
+#include "globals.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <QApplication>
+#include <QFileInfo>
+#include <QDir>
+#include <QTimer>
+
+#ifdef Q_OS_WIN
+#include <locale.h>
+#endif
+
+#if defined(Q_OS_WIN) && !defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
+#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+#endif
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ScopedPointerReaderDeleter
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ScopedPointerWriterDeleter
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ScopedPointerEntryDeleter
+ \internal
+*/
+
+namespace ArchiveEntryPaths {
+
+// TODO: it is expected that the filename handling will change in the major
+// version jump to libarchive 4.x, and the *_w methods will disappear.
+
+/*!
+ \internal
+
+ Returns the path name from the archive \a entry as a \c QString. The path is
+ stored internally in a multistring that can contain a combination of a wide
+ character or multibyte string in current locale or unicode string encoded as UTF-8.
+
+ \note The MBS version is expected to be convertable from latin-1 which might
+ not actually be the case, as the encoding depends on the current locale.
+*/
+static QString pathname(archive_entry *const entry)
+{
+ if (!entry)
+ return QString();
+#ifdef Q_OS_WIN
+ if (const wchar_t *path = archive_entry_pathname_w(entry))
+ return QString::fromWCharArray(path);
+#endif
+ if (const char *path = archive_entry_pathname_utf8(entry))
+ return QString::fromUtf8(path);
+
+ return QString::fromLatin1(archive_entry_pathname(entry));
+}
+
+/*!
+ \internal
+
+ Sets the path name for the archive \a entry to \a path. This function
+ tries to update all variants of the string in the internal multistring
+ struct.
+*/
+static void setPathname(archive_entry *const entry, const QString &path)
+{
+ if (!entry)
+ return;
+
+ // Try updating all variants at once, stops on failure
+ if (archive_entry_update_pathname_utf8(entry, path.toUtf8()))
+ return;
+
+ // If that does not work, then set them individually
+ archive_entry_set_pathname(entry, path.toLatin1());
+ archive_entry_set_pathname_utf8(entry, path.toUtf8());
+#ifdef Q_OS_WIN
+ wchar_t *wpath = new wchar_t[path.length() + 1];
+ path.toWCharArray(wpath);
+ wpath[path.length()] = '\0';
+
+ archive_entry_copy_pathname_w(entry, wpath);
+ delete[] wpath;
+#endif
+}
+
+/*!
+ \internal
+
+ Returns the source path on disk from the current archive \a entry as a \c QString.
+ The path is stored internally in a multistring that can contain a combination
+ of a wide character or multibyte string in current locale.
+
+ \note The MBS version is expected to be convertable from UTF-8.
+*/
+static QString sourcepath(archive_entry * const entry)
+{
+ if (!entry)
+ return QString();
+#ifdef Q_OS_WIN
+ if (const wchar_t *path = archive_entry_sourcepath_w(entry))
+ return QString::fromWCharArray(path);
+#endif
+ return QString::fromUtf8(archive_entry_sourcepath(entry));
+}
+
+/*!
+ \internal
+
+ Returns the hardlink path from the current archive \a entry as a \c QString.
+ The path is stored internally in a multistring that can contain a combination of a wide
+ character or multibyte string in current locale or unicode string encoded as UTF-8.
+
+ \note The MBS version is expected to be convertable from latin-1 which might
+ not actually be the case, as the encoding depends on the current locale.
+*/
+static QString hardlink(archive_entry * const entry)
+{
+ if (!entry)
+ return QString();
+#ifdef Q_OS_WIN
+ if (const wchar_t *path = archive_entry_hardlink_w(entry))
+ return QString::fromWCharArray(path);
+#endif
+ if (const char *path = archive_entry_hardlink_utf8(entry))
+ return QString::fromUtf8(path);
+
+ return QString::fromLatin1(archive_entry_hardlink(entry));
+}
+
+/*!
+ \internal
+
+ Sets the hard link path for the archive \a entry to \a path. This function
+ tries to update all variants of the string in the internal multistring
+ struct.
+*/
+static void setHardlink(archive_entry *const entry, const QString &path)
+{
+ if (!entry)
+ return;
+
+ // Try updating all variants at once, stops on failure
+ if (archive_entry_update_hardlink_utf8(entry, path.toUtf8()))
+ return;
+
+ // If that does not work, then set them individually
+ archive_entry_set_hardlink(entry, path.toLatin1());
+ archive_entry_set_hardlink_utf8(entry, path.toUtf8());
+#ifdef Q_OS_WIN
+ wchar_t *wpath = new wchar_t[path.length() + 1];
+ path.toWCharArray(wpath);
+ wpath[path.length()] = '\0';
+
+ archive_entry_copy_hardlink_w(entry, wpath);
+ delete[] wpath;
+#endif
+}
+
+/*!
+ \internal
+
+ Calls a function object or pointer \a func with any number of extra
+ arguments \a args. Returns the return value of the function of type T.
+
+ On Windows, this changes the locale category LC_CTYPE from C to system
+ locale. The original LC_CTYPE is restored after the function call.
+ Currently the locale is unchanged on other platforms.
+*/
+template <typename T, typename F, typename... Args>
+static T callWithSystemLocale(F func, Args... args)
+{
+#ifdef Q_OS_WIN
+ const QByteArray oldLocale = setlocale(LC_CTYPE, "");
+#endif
+ T returnValue = func(std::forward<Args>(args)...);
+#ifdef Q_OS_WIN
+ setlocale(LC_CTYPE, oldLocale.constData());
+#endif
+ return returnValue;
+}
+
+/*!
+ \internal
+
+ Calls a function object or pointer \a func with any number of extra
+ arguments \a args.
+
+ On Windows, this changes the locale category LC_CTYPE from C to system
+ locale. The original LC_CTYPE is restored after the function call.
+ Currently the locale is unchanged on other platforms.
+*/
+template <typename F, typename... Args>
+static void callWithSystemLocale(F func, Args... args)
+{
+#ifdef Q_OS_WIN
+ const QByteArray oldLocale = setlocale(LC_CTYPE, "");
+#endif
+ func(std::forward<Args>(args)...);
+#ifdef Q_OS_WIN
+ setlocale(LC_CTYPE, oldLocale.constData());
+#endif
+}
+
+} // namespace ArchiveEntryPaths
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ExtractWorker
+ \internal
+*/
+
+ExtractWorker::Status ExtractWorker::status() const
+{
+ return m_status;
+}
+
+void ExtractWorker::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ m_status = Unfinished;
+ quint64 completed = 0;
+
+ if (!totalFiles) {
+ m_status = Failure;
+ emit finished(QLatin1String("The file count for current archive is null!"));
+ return;
+ }
+
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_disk_new());
+ archive_entry *entry = nullptr;
+
+ LibArchiveArchive::configureReader(reader.get());
+ LibArchiveArchive::configureDiskWriter(writer.get());
+
+ DirectoryGuard targetDir(QFileInfo(dirPath).absoluteFilePath());
+ try {
+ const QStringList createdDirs = targetDir.tryCreate();
+ // Make sure that all leading directories created get removed as well
+ foreach (const QString &directory, createdDirs)
+ emit currentEntryChanged(directory);
+
+ archive_read_set_read_callback(reader.get(), readCallback);
+ archive_read_set_callback_data(reader.get(), this);
+ archive_read_set_seek_callback(reader.get(), seekCallback);
+
+ int status = archive_read_open1(reader.get());
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(tr("Cannot open archive for reading: %1")
+ .arg(LibArchiveArchive::errorStringWithCode(reader.get())));
+ return;
+ }
+
+ forever {
+ if (m_status == Canceled) {
+ emit finished(QLatin1String("Extract canceled."));
+ return;
+ }
+ status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(tr("Cannot read entry header: %1")
+ .arg(LibArchiveArchive::errorStringWithCode(reader.get())));
+ return;
+ }
+ const QString current = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry);
+ const QString outputPath = dirPath + QDir::separator() + current;
+ ArchiveEntryPaths::callWithSystemLocale(&ArchiveEntryPaths::setPathname, entry, outputPath);
+
+ const QString hardlink = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::hardlink, entry);
+ if (!hardlink.isEmpty()) {
+ const QString hardLinkPath = dirPath + QDir::separator() + hardlink;
+ ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setHardlink, entry, hardLinkPath);
+ }
+
+ emit currentEntryChanged(outputPath);
+ if (!writeEntry(reader.get(), writer.get(), entry))
+ return;
+
+ ++completed;
+ emit completedChanged(completed, totalFiles);
+
+ qApp->processEvents();
+ }
+ } catch (const Error &e) {
+ m_status = Failure;
+ emit finished(e.message());
+ return;
+ }
+ targetDir.release();
+ m_status = Success;
+ emit finished();
+}
+
+void ExtractWorker::addDataBlock(const QByteArray buffer)
+{
+ m_buffer.append(buffer);
+ emit dataReadyForRead();
+}
+
+void ExtractWorker::onFilePositionChanged(qint64 pos)
+{
+ m_lastPos = pos;
+ emit seekReady();
+}
+
+void ExtractWorker::cancel()
+{
+ m_status = Canceled;
+}
+
+ssize_t ExtractWorker::readCallback(archive *reader, void *caller, const void **buff)
+{
+ Q_UNUSED(reader)
+
+ ExtractWorker *obj;
+ if (!(obj = static_cast<ExtractWorker *>(caller)))
+ return ARCHIVE_FATAL;
+
+ QByteArray *buffer = &obj->m_buffer;
+ if (!buffer->isEmpty())
+ buffer->clear();
+
+ emit obj->dataBlockRequested();
+
+ // It's a bit bad that we have to wait here, but libarchive doesn't
+ // provide an event based reading method.
+ {
+ QEventLoop loop;
+ QTimer::singleShot(30000, &loop, &QEventLoop::quit);
+ connect(obj, &ExtractWorker::dataReadyForRead, &loop, &QEventLoop::quit);
+ connect(obj, &ExtractWorker::dataAtEnd, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+
+ if (!(*buff = static_cast<const void *>(buffer->constData())))
+ return ARCHIVE_FATAL;
+
+ return buffer->size();
+}
+
+la_int64_t ExtractWorker::seekCallback(archive *reader, void *caller, la_int64_t offset, int whence)
+{
+ Q_UNUSED(reader)
+
+ ExtractWorker *obj;
+ if (!(obj = static_cast<ExtractWorker *>(caller)))
+ return ARCHIVE_FATAL;
+
+ emit obj->seekRequested(static_cast<qint64>(offset), whence);
+
+ {
+ QEventLoop loop;
+ QTimer::singleShot(30000, &loop, &QEventLoop::quit);
+ connect(obj, &ExtractWorker::seekReady, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+
+ return static_cast<la_int64_t>(obj->m_lastPos);
+}
+
+bool ExtractWorker::writeEntry(archive *reader, archive *writer, archive_entry *entry)
+{
+ int status;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ const QString entryPath = ArchiveEntryPaths::callWithSystemLocale
+ <QString>(ArchiveEntryPaths::pathname, entry);
+
+ FileGuardLocker locker(entryPath, FileGuard::globalObject());
+
+ status = archive_write_header(writer, entry);
+ if (status != ARCHIVE_OK) {
+ emit finished(tr("Cannot write entry \"%1\" to disk: %2")
+ .arg(entryPath, LibArchiveArchive::errorStringWithCode(writer)));
+ return false;
+ }
+
+ forever {
+ status = archive_read_data_block(reader, &buff, &size, &offset);
+ if (status == ARCHIVE_EOF) {
+ status = archive_write_finish_entry(writer);
+ if (status == ARCHIVE_OK)
+ return true;
+ }
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(tr("Cannot write entry \"%1\" to disk: %2")
+ .arg(entryPath, LibArchiveArchive::errorStringWithCode(reader)));
+ return false;
+ }
+ status = archive_write_data_block(writer, buff, size, offset);
+ if (status != ARCHIVE_OK) {
+ m_status = Failure;
+ emit finished(tr("Cannot write entry \"%1\" to disk: %2")
+ .arg(entryPath, LibArchiveArchive::errorStringWithCode(writer)));
+ return false;
+ }
+ }
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveArchive
+ \brief The LibArchiveArchive class represents an archive file
+ handled with libarchive archive and compression library.
+
+ In addition to extracting data from the underlying file device,
+ the class supports a non-blocking mode of extracting from an external
+ data source. When using this mode, the calling client must pass the data
+ to be read in chunks of arbitrary size, and inform the object when there
+ is no more data to read.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveArchive::ArchiveData
+ \brief Bundles a file device and associated read buffer for access
+ as client data in libarchive callbacks.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::dataBlockRequested()
+
+ Emitted when the worker object requires more data to continue extracting.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::seekRequested(qint64 offset, int whence)
+
+ Emitted when the worker object requires a seek to \a offset to continue extracting.
+ The \a whence value defines the starting position for \a offset.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerFinished()
+
+ Emitted when the worker object finished extracting an archive.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToExtract(const QString &dirPath, const quint64 totalFiles)
+
+ Emitted when the worker object is about to extract \a totalFiles
+ from an archive to \a dirPath.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToAddDataBlock(const QByteArray buffer)
+
+ Emitted when the worker object is about to add and read the data block in \a buffer.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToSetDataAtEnd()
+
+ Emitted when the worker object is about to set data-at-end, meaning there
+ will be no further read requests for the calling client.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToCancel()
+
+ Emitted when the worker object is about to cancel extracting.
+*/
+
+/*!
+ \fn QInstaller::LibArchiveArchive::workerAboutToSetFilePosition(qint64 pos)
+
+ Emitted when the worker object is about to set new file position \a pos.
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename with \a parent as the parent object.
+*/
+LibArchiveArchive::LibArchiveArchive(const QString &filename, QObject *parent)
+ : AbstractArchive(parent)
+ , m_data(new ArchiveData())
+ , m_cancelScheduled(false)
+{
+ LibArchiveArchive::setFilename(filename);
+ initExtractWorker();
+}
+
+/*!
+ Constructs an archive object with the given \a parent.
+*/
+LibArchiveArchive::LibArchiveArchive(QObject *parent)
+ : AbstractArchive(parent)
+ , m_data(new ArchiveData())
+ , m_cancelScheduled(false)
+{
+ initExtractWorker();
+}
+
+/*!
+ Destroys the instance and releases resources.
+*/
+LibArchiveArchive::~LibArchiveArchive()
+{
+ m_workerThread.quit();
+ m_workerThread.wait();
+
+ delete m_data;
+}
+
+/*!
+ \reimp
+
+ Opens the underlying file device using \a mode. Returns \c true if
+ succesfull; otherwise \c false.
+*/
+bool LibArchiveArchive::open(QIODevice::OpenMode mode)
+{
+ if (!m_data->file.open(mode)) {
+ setErrorString(m_data->file.errorString());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Closes the underlying file device.
+*/
+void LibArchiveArchive::close()
+{
+ m_data->file.close();
+}
+
+/*!
+ \reimp
+
+ Sets the \a filename of the underlying file device.
+*/
+void LibArchiveArchive::setFilename(const QString &filename)
+{
+ m_data->file.setFileName(filename);
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath.
+ Returns \c true on success; \c false otherwise.
+*/
+bool LibArchiveArchive::extract(const QString &dirPath)
+{
+ return extract(dirPath, totalFiles());
+}
+
+/*!
+ \reimp
+
+ Extracts the contents of this archive to \a dirPath with
+ precalculated count of \a totalFiles. Returns \c true on
+ success; \c false otherwise.
+*/
+bool LibArchiveArchive::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ m_cancelScheduled = false;
+ quint64 completed = 0;
+ if (!totalFiles) {
+ setErrorString(QLatin1String("The file count for current archive is null!"));
+ return false;
+ }
+
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_disk_new());
+ archive_entry *entry = nullptr;
+
+ configureReader(reader.get());
+ configureDiskWriter(writer.get());
+
+ DirectoryGuard targetDir(QFileInfo(dirPath).absoluteFilePath());
+ try {
+ const QStringList createdDirs = targetDir.tryCreate();
+ // Make sure that all leading directories created get removed as well
+ foreach (const QString &directory, createdDirs)
+ emit currentEntryChanged(directory);
+
+ int status = archiveReadOpenWithCallbacks(reader.get());
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot open archive for reading: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ forever {
+ if (m_cancelScheduled)
+ throw Error(QLatin1String("Extract canceled."));
+
+ status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot read entry header: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ const QString current = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry);
+ const QString outputPath = dirPath + QDir::separator() + current;
+ ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setPathname, entry, outputPath);
+
+ const QString hardlink = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::hardlink, entry);
+ if (!hardlink.isEmpty()) {
+ const QString hardLinkPath = dirPath + QDir::separator() + hardlink;
+ ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setHardlink, entry, hardLinkPath);
+ }
+
+ emit currentEntryChanged(outputPath);
+ if (!writeEntry(reader.get(), writer.get(), entry)) {
+ throw Error(tr("Cannot write entry \"%1\" to disk: %2")
+ .arg(outputPath, errorString())); // appropriate error string set in writeEntry()
+ }
+
+ ++completed;
+ emit completedChanged(completed, totalFiles);
+
+ qApp->processEvents();
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return false;
+ }
+ targetDir.release();
+ m_data->file.seek(0);
+ return true;
+}
+
+/*!
+ \reimp
+
+ Packages the given \a data into the archive and creates the file on disk.
+*/
+bool LibArchiveArchive::create(const QStringList &data)
+{
+ QScopedPointer<archive, ScopedPointerWriterDeleter> writer(archive_write_new());
+ configureWriter(writer.get());
+
+ QStringList globbedData;
+ for (auto &dataEntry : data) { // Expand glob pattern entries with proper filenames
+ if (!dataEntry.contains(QLatin1Char('*'))) {
+ globbedData.append(dataEntry);
+ continue;
+ }
+ const QFileInfo entryInfo(dataEntry);
+ if (entryInfo.path().contains(QLatin1Char('*'))) {
+ setErrorString(QString::fromLatin1("Invalid argument \"%1\": glob patterns "
+ "are not supported between directory paths.").arg(dataEntry));
+ return false;
+ }
+ const QDir parentDir = entryInfo.dir();
+ const QList<QFileInfo> infoList = parentDir.entryInfoList(QStringList()
+ << entryInfo.fileName(), QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot);
+
+ for (auto &info : infoList)
+ globbedData.append(info.absoluteFilePath());
+ }
+
+ try {
+ int status;
+#ifdef Q_OS_WIN
+ QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> fileName_w(
+ new wchar_t[m_data->file.fileName().length() + 1]);
+
+ m_data->file.fileName().toWCharArray(fileName_w.get());
+ fileName_w.get()[m_data->file.fileName().length()] = '\0';
+
+ if ((status = archive_write_open_filename_w(writer.get(), fileName_w.get()))) {
+ throw Error(tr("Cannot open file \"%1\" for writing: %2")
+ .arg(m_data->file.fileName(), errorStringWithCode(writer.get())));
+ }
+#else
+ if ((status = archive_write_open_filename(writer.get(), m_data->file.fileName().toUtf8()))) {
+ throw Error(tr("Cannot open file \"%1\" for writing: %2")
+ .arg(m_data->file.fileName(), errorStringWithCode(writer.get())));
+ }
+#endif
+ for (auto &dataEntry : globbedData) {
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_disk_new());
+ configureDiskReader(reader.get());
+
+#ifdef Q_OS_WIN
+ QScopedPointer<wchar_t, QScopedPointerArrayDeleter<wchar_t>> dataEntry_w(
+ new wchar_t[dataEntry.length() + 1]);
+
+ dataEntry.toWCharArray(dataEntry_w.get());
+ dataEntry_w.get()[dataEntry.length()] = '\0';
+
+ if ((status = archive_read_disk_open_w(reader.get(), dataEntry_w.get()))) {
+ throw Error(tr("Cannot open file \"%1\" for reading: %2")
+ .arg(dataEntry, errorStringWithCode(reader.get())));
+ }
+#else
+ if ((status = archive_read_disk_open(reader.get(), dataEntry.toUtf8()))) {
+ throw Error(tr("Cannot open file \"%1\" for reading: %2")
+ .arg(dataEntry, errorStringWithCode(reader.get())));
+ }
+#endif
+ QDir basePath = QFileInfo(dataEntry).dir();
+ forever {
+ QScopedPointer<archive_entry, ScopedPointerEntryDeleter> entry(archive_entry_new());
+ status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header2, reader.get(), entry.get());
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot read entry header: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ const QFileInfo fileOrDir(pathWithoutNamespace(
+ ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::sourcepath, entry.get())));
+ // Set new path name in archive, otherwise we add all directories from absolute path
+ const QString newPath = basePath.relativeFilePath(fileOrDir.filePath());
+ ArchiveEntryPaths::callWithSystemLocale(ArchiveEntryPaths::setPathname, entry.get(), newPath);
+
+ archive_read_disk_descend(reader.get());
+ status = archive_write_header(writer.get(), entry.get());
+ if (status < ARCHIVE_OK) {
+ throw Error(tr("Cannot write entry header for \"%1\": %2")
+ .arg(fileOrDir.filePath(), errorStringWithCode(writer.get())));
+ }
+ if (fileOrDir.isDir() || archive_entry_size(entry.get()) == 0)
+ continue; // nothing to copy
+
+ QFile file(pathWithoutNamespace(ArchiveEntryPaths::callWithSystemLocale<QString>(
+ ArchiveEntryPaths::sourcepath, entry.get())));
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Error(tr("Cannot open file \"%1\" for reading: %2")
+ .arg(file.fileName(), file.errorString()));
+ }
+
+ QByteArray buffer;
+ constexpr qint64 blockSize = 4 * 1024;
+ buffer.resize(blockSize);
+
+ ssize_t bytesRead = readData(&file, buffer.data(), blockSize);
+ while (bytesRead > 0) {
+ archive_write_data(writer.get(), buffer.constData(), blockSize);
+ bytesRead = readData(&file, buffer.data(), blockSize);
+ }
+ file.close();
+ }
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \reimp
+
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> LibArchiveArchive::list()
+{
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ archive_entry *entry = nullptr;
+
+ configureReader(reader.get());
+
+ QVector<ArchiveEntry> entries;
+ try {
+ int status = archiveReadOpenWithCallbacks(reader.get());
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot open archive for reading: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ forever {
+ status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot read entry header: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ ArchiveEntry archiveEntry;
+ archiveEntry.path = ArchiveEntryPaths::callWithSystemLocale<QString>(ArchiveEntryPaths::pathname, entry);
+ archiveEntry.utcTime = QDateTime::fromSecsSinceEpoch(archive_entry_mtime(entry));
+ archiveEntry.isDirectory = (archive_entry_filetype(entry) == AE_IFDIR);
+ archiveEntry.isSymbolicLink = (archive_entry_filetype(entry) == AE_IFLNK);
+ archiveEntry.uncompressedSize = archive_entry_size(entry);
+ archiveEntry.permissions_mode = archive_entry_perm(entry);
+
+ entries.append(archiveEntry);
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return QVector<ArchiveEntry>();
+ }
+ m_data->file.seek(0);
+ return entries;
+}
+
+/*!
+ \reimp
+
+ Returns \c true if the current archive is of a supported format;
+ \c false otherwise.
+*/
+bool LibArchiveArchive::isSupported()
+{
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ configureReader(reader.get());
+
+ try {
+ const int status = archiveReadOpenWithCallbacks(reader.get());
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot open archive for reading: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return false;
+ }
+ m_data->file.seek(0);
+ return true;
+}
+
+/*!
+ Requests to extract the archive to \a dirPath with \a totalFiles
+ in a separate thread with a worker object.
+*/
+void LibArchiveArchive::workerExtract(const QString &dirPath, const quint64 totalFiles)
+{
+ emit workerAboutToExtract(dirPath, totalFiles);
+}
+
+/*!
+ Adds data to be read by the worker object in \a buffer.
+*/
+void LibArchiveArchive::workerAddDataBlock(const QByteArray buffer)
+{
+ emit workerAboutToAddDataBlock(buffer);
+}
+
+/*!
+ Signals the worker object that the client data is at end.
+*/
+void LibArchiveArchive::workerSetDataAtEnd()
+{
+ emit workerAboutToSetDataAtEnd();
+}
+
+/*!
+ Signals the worker object that the client file position changed to \a pos.
+*/
+void LibArchiveArchive::workerSetFilePosition(qint64 pos)
+{
+ emit workerAboutToSetFilePosition(pos);
+}
+
+/*!
+ Cancels the extract in progress for the worker object.
+*/
+void LibArchiveArchive::workerCancel()
+{
+ emit workerAboutToCancel();
+}
+
+/*!
+ Returns the status of the worker object.
+*/
+ExtractWorker::Status LibArchiveArchive::workerStatus() const
+{
+ return m_worker.status();
+}
+
+/*!
+ \reimp
+
+ Cancels the extract in progress.
+*/
+void LibArchiveArchive::cancel()
+{
+ m_cancelScheduled = true;
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::onWorkerFinished(const QString &errorString)
+{
+ setErrorString(errorString);
+ emit workerFinished();
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureReader(archive *archive)
+{
+ archive_read_support_filter_bzip2(archive);
+ archive_read_support_filter_gzip(archive);
+ archive_read_support_filter_xz(archive);
+
+ archive_read_support_format_tar(archive);
+ archive_read_support_format_zip(archive);
+ archive_read_support_format_7zip(archive);
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureWriter(archive *archive)
+{
+ const QString fileName = m_data->file.fileName();
+ if (fileName.endsWith(QLatin1String(".qbsp"), Qt::CaseInsensitive)) {
+ // The Qt board support package file extension is really a 7z.
+ archive_write_set_format_7zip(archive);
+ } else {
+ archive_write_set_format_filter_by_ext(archive, fileName.toUtf8());
+ }
+
+ const QByteArray charset = "hdrcharset=UTF-8";
+ // not checked as this is ignored on some archive formats like 7z
+ archive_write_set_options(archive, charset);
+
+ if (compressionLevel() == CompressionLevel::Normal)
+ return;
+
+ const QByteArray compression = "compression-level=" + QString::number(compressionLevel()).toLatin1();
+ if (archive_write_set_options(archive, compression.constData())) { // not fatal
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Could not set option" << compression
+ << "for archive" << m_data->file.fileName() << ":" << errorStringWithCode(archive);
+ }
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureDiskReader(archive *archive)
+{
+ archive_read_disk_set_standard_lookup(archive);
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::configureDiskWriter(archive *archive)
+{
+ constexpr int flags = ARCHIVE_EXTRACT_TIME
+ | ARCHIVE_EXTRACT_PERM
+ | ARCHIVE_EXTRACT_ACL
+ | ARCHIVE_EXTRACT_FFLAGS;
+
+ archive_write_disk_set_options(archive, flags);
+ archive_write_disk_set_standard_lookup(archive);
+}
+
+/*!
+ \internal
+*/
+void LibArchiveArchive::initExtractWorker()
+{
+ m_worker.moveToThread(&m_workerThread);
+
+ connect(this, &LibArchiveArchive::workerAboutToExtract, &m_worker, &ExtractWorker::extract);
+ connect(this, &LibArchiveArchive::workerAboutToAddDataBlock, &m_worker, &ExtractWorker::addDataBlock);
+ connect(this, &LibArchiveArchive::workerAboutToSetDataAtEnd, &m_worker, &ExtractWorker::dataAtEnd);
+ connect(this, &LibArchiveArchive::workerAboutToSetFilePosition, &m_worker, &ExtractWorker::onFilePositionChanged);
+ connect(this, &LibArchiveArchive::workerAboutToCancel, &m_worker, &ExtractWorker::cancel);
+
+ connect(&m_worker, &ExtractWorker::dataBlockRequested, this, &LibArchiveArchive::dataBlockRequested);
+ connect(&m_worker, &ExtractWorker::seekRequested, this, &LibArchiveArchive::seekRequested);
+ connect(&m_worker, &ExtractWorker::finished, this, &LibArchiveArchive::onWorkerFinished);
+
+ connect(&m_worker, &ExtractWorker::currentEntryChanged, this, &LibArchiveArchive::currentEntryChanged);
+ connect(&m_worker, &ExtractWorker::completedChanged, this, &LibArchiveArchive::completedChanged);
+
+ m_workerThread.start();
+}
+
+/*!
+ \internal
+
+ Sets default callbacks for archive \a reader and opens for reading.
+*/
+int LibArchiveArchive::archiveReadOpenWithCallbacks(archive *reader)
+{
+ archive_read_set_read_callback(reader, readCallback);
+ archive_read_set_callback_data(reader, m_data);
+ archive_read_set_seek_callback(reader, seekCallback);
+
+ return archive_read_open1(reader);
+}
+
+/*!
+ Writes the current \a entry header, then pulls data from the archive \a reader
+ and writes it to the \a writer handle.
+*/
+bool LibArchiveArchive::writeEntry(archive *reader, archive *writer, archive_entry *entry)
+{
+ int status;
+ const void *buff;
+ size_t size;
+ int64_t offset;
+
+ const QString entryPath = ArchiveEntryPaths::callWithSystemLocale
+ <QString>(ArchiveEntryPaths::pathname, entry);
+
+ FileGuardLocker locker(entryPath, FileGuard::globalObject());
+
+ status = archive_write_header(writer, entry);
+ if (status != ARCHIVE_OK) {
+ setErrorString(errorStringWithCode(writer));
+ return false;
+ }
+
+ forever {
+ status = archive_read_data_block(reader, &buff, &size, &offset);
+ if (status == ARCHIVE_EOF) {
+ status = archive_write_finish_entry(writer);
+ if (status == ARCHIVE_OK)
+ return true;
+ }
+ if (status != ARCHIVE_OK) {
+ setErrorString(errorStringWithCode(reader));
+ return false;
+ }
+ status = archive_write_data_block(writer, buff, size, offset);
+ if (status != ARCHIVE_OK) {
+ setErrorString(errorStringWithCode(writer));
+ return false;
+ }
+ }
+}
+
+/*!
+ \internal
+
+ Reads \a data from the current position of \a file. The maximum bytes to
+ read are specified by \a maxSize. Returns the amount of bytes read.
+*/
+qint64 LibArchiveArchive::readData(QFile *file, char *data, qint64 maxSize)
+{
+ if (!file->isOpen() || file->isSequential())
+ return ARCHIVE_FATAL;
+
+ if (file->atEnd() && file->seek(0))
+ return ARCHIVE_OK;
+
+ const qint64 bytesRead = file->read(data, maxSize);
+ if (bytesRead == -1)
+ return ARCHIVE_FATAL;
+
+ return bytesRead;
+}
+
+/*!
+ \internal
+
+ Called by libarchive when new data is needed. Reads data from the file device
+ in \a archiveData into the buffer referenced by \a buff. Returns the number of bytes read.
+*/
+ssize_t LibArchiveArchive::readCallback(archive *reader, void *archiveData, const void **buff)
+{
+ Q_UNUSED(reader)
+ constexpr qint64 blockSize = 1024 * 1024; // 1MB
+
+ ArchiveData *data;
+ if (!(data = static_cast<ArchiveData *>(archiveData)))
+ return ARCHIVE_FATAL;
+
+ if (!data->buffer.isEmpty())
+ data->buffer.clear();
+
+ if (data->buffer.size() != blockSize)
+ data->buffer.resize(blockSize);
+
+ if (!(*buff = static_cast<const void *>(data->buffer.constData())))
+ return ARCHIVE_FATAL;
+
+ // Doesn't matter if the buffer size exceeds the actual data read,
+ // the return value indicates the length of relevant bytes.
+ return readData(&data->file, data->buffer.data(), data->buffer.size());
+}
+
+/*!
+ \internal
+
+ Seeks to specified \a offset in the file device in \a archiveData and returns the position.
+ Possible \a whence values are \c SEEK_SET, \c SEEK_CUR, and \c SEEK_END. Returns
+ \c ARCHIVE_FATAL if the seek fails.
+*/
+la_int64_t LibArchiveArchive::seekCallback(archive *reader, void *archiveData, la_int64_t offset, int whence)
+{
+ Q_UNUSED(reader)
+
+ ArchiveData *data;
+ if (!(data = static_cast<ArchiveData *>(archiveData)))
+ return ARCHIVE_FATAL;
+
+ if (!data->file.isOpen() || data->file.isSequential())
+ return ARCHIVE_FATAL;
+
+ switch (whence) {
+ case SEEK_SET: // moves file pointer position to the beginning of the file
+ if (!data->file.seek(offset))
+ return ARCHIVE_FATAL;
+ break;
+ case SEEK_CUR: // moves file pointer position to given location
+ if (!data->file.seek(data->file.pos() + offset))
+ return ARCHIVE_FATAL;
+ break;
+ case SEEK_END: // moves file pointer position to the end of file
+ if (!data->file.seek(data->file.size() + offset))
+ return ARCHIVE_FATAL;
+ break;
+ default:
+ return ARCHIVE_FATAL;
+ }
+ return data->file.pos();
+}
+
+/*!
+ Returns the \a path to a file or directory, without the Win32 namespace prefix.
+ On Unix platforms, the \a path is returned unaltered.
+*/
+QString LibArchiveArchive::pathWithoutNamespace(const QString &path)
+{
+ QString aPath = path;
+#ifdef Q_OS_WIN
+ if (aPath.size() > 4 && aPath.at(0) == QLatin1Char('\\')
+ && aPath.at(2) == QLatin1Char('?') && aPath.at(3) == QLatin1Char('\\')) {
+ aPath = aPath.mid(4);
+ }
+#endif
+ return aPath;
+}
+
+/*!
+ Returns an error message and a textual representaion of the numeric error code
+ indicating the reason for the most recent error return for the \a archive object.
+*/
+QString LibArchiveArchive::errorStringWithCode(archive *const archive)
+{
+ if (!archive)
+ return QString();
+
+ return QString::fromLatin1("%1: %2.").arg(
+ QLatin1String(archive_error_string(archive)),
+ QLatin1String(strerror(archive_errno(archive)))
+ );
+}
+
+/*!
+ Returns the number of files in this archive.
+*/
+quint64 LibArchiveArchive::totalFiles()
+{
+ QScopedPointer<archive, ScopedPointerReaderDeleter> reader(archive_read_new());
+ archive_entry *entry = nullptr;
+ quint64 files = 0;
+
+ configureReader(reader.get());
+
+ try {
+ int status = archiveReadOpenWithCallbacks(reader.get());
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot open archive for reading: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ forever {
+ status = ArchiveEntryPaths::callWithSystemLocale<int>(archive_read_next_header, reader.get(), &entry);
+ if (status == ARCHIVE_EOF)
+ break;
+ if (status != ARCHIVE_OK) {
+ throw Error(tr("Cannot read entry header: %1")
+ .arg(errorStringWithCode(reader.get())));
+ }
+
+ ++files;
+ }
+ } catch (const Error &e) {
+ setErrorString(e.message());
+ m_data->file.seek(0);
+ return 0;
+ }
+ m_data->file.seek(0);
+ return files;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/libarchivearchive.h b/src/libs/installer/libarchivearchive.h
new file mode 100644
index 000000000..759f99046
--- /dev/null
+++ b/src/libs/installer/libarchivearchive.h
@@ -0,0 +1,201 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIBARCHIVEARCHIVE_H
+#define LIBARCHIVEARCHIVE_H
+
+#include "installer_global.h"
+#include "abstractarchive.h"
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include <QThread>
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#endif
+
+namespace QInstaller {
+
+class ExtractWorker : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ExtractWorker)
+
+public:
+ enum Status {
+ Success = 0,
+ Failure = 1,
+ Canceled = 2,
+ Unfinished = 3
+ };
+
+ ExtractWorker() = default;
+
+ Status status() const;
+
+public Q_SLOTS:
+ void extract(const QString &dirPath, const quint64 totalFiles);
+ void addDataBlock(const QByteArray buffer);
+ void onFilePositionChanged(qint64 pos);
+ void cancel();
+
+Q_SIGNALS:
+ void dataBlockRequested();
+ void dataAtEnd();
+ void dataReadyForRead();
+ void seekRequested(qint64 offset, int whence);
+ void seekReady();
+ void finished(const QString &errorString = QString());
+
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(quint64 completed, quint64 total);
+
+private:
+ static ssize_t readCallback(archive *reader, void *caller, const void **buff);
+ static la_int64_t seekCallback(archive *reader, void *caller, la_int64_t offset, int whence);
+ bool writeEntry(archive *reader, archive *writer, archive_entry *entry);
+
+private:
+ QByteArray m_buffer;
+ qint64 m_lastPos = 0;
+ Status m_status;
+};
+
+class INSTALLER_EXPORT LibArchiveArchive : public AbstractArchive
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LibArchiveArchive)
+
+public:
+ LibArchiveArchive(const QString &filename, QObject *parent = nullptr);
+ explicit LibArchiveArchive(QObject *parent = nullptr);
+ ~LibArchiveArchive();
+
+ bool open(QIODevice::OpenMode mode) override;
+ void close() override;
+ void setFilename(const QString &filename) override;
+
+ bool extract(const QString &dirPath) override;
+ bool extract(const QString &dirPath, const quint64 totalFiles) override;
+ bool create(const QStringList &data) override;
+ QVector<ArchiveEntry> list() override;
+ bool isSupported() override;
+
+ void workerExtract(const QString &dirPath, const quint64 totalFiles);
+ void workerAddDataBlock(const QByteArray buffer);
+ void workerSetDataAtEnd();
+ void workerSetFilePosition(qint64 pos);
+ void workerCancel();
+ ExtractWorker::Status workerStatus() const;
+
+Q_SIGNALS:
+ void dataBlockRequested();
+ void seekRequested(qint64 offset, int whence);
+ void workerFinished();
+
+ void workerAboutToExtract(const QString &dirPath, const quint64 totalFiles);
+ void workerAboutToAddDataBlock(const QByteArray buffer);
+ void workerAboutToSetDataAtEnd();
+ void workerAboutToSetFilePosition(qint64 pos);
+ void workerAboutToCancel();
+
+public Q_SLOTS:
+ void cancel() override;
+
+private Q_SLOTS:
+ void onWorkerFinished(const QString &errorString);
+
+private:
+ static void configureReader(archive *archive);
+ void configureWriter(archive *archive);
+ static void configureDiskReader(archive *archive);
+ static void configureDiskWriter(archive *archive);
+
+ void initExtractWorker();
+
+ int archiveReadOpenWithCallbacks(archive *reader);
+ bool writeEntry(archive *reader, archive *writer, archive_entry *entry);
+
+ static qint64 readData(QFile *file, char *data, qint64 maxSize);
+ static ssize_t readCallback(archive *reader, void *archiveData, const void **buff);
+
+ static la_int64_t seekCallback(archive *reader, void *archiveData, la_int64_t offset, int whence);
+
+ static QString pathWithoutNamespace(const QString &path);
+ static QString errorStringWithCode(archive *const archive);
+
+ quint64 totalFiles();
+
+private:
+ friend class ExtractWorker;
+ friend class LibArchiveWrapperPrivate;
+
+ struct ArchiveData
+ {
+ QFile file;
+ QByteArray buffer;
+ };
+
+private:
+ ArchiveData *m_data;
+ ExtractWorker m_worker;
+ QThread m_workerThread;
+
+ bool m_cancelScheduled;
+};
+
+struct ScopedPointerReaderDeleter
+{
+ static inline void cleanup(archive *p)
+ {
+ archive_read_free(p);
+ }
+};
+
+struct ScopedPointerWriterDeleter
+{
+ static inline void cleanup(archive *p)
+ {
+ archive_write_free(p);
+ }
+};
+
+struct ScopedPointerEntryDeleter
+{
+ static inline void cleanup(archive_entry *p)
+ {
+ archive_entry_free(p);
+ }
+};
+
+} // namespace QInstaller
+
+#endif // LIBARCHIVEARCHIVE_H
diff --git a/src/libs/installer/libarchivewrapper.cpp b/src/libs/installer/libarchivewrapper.cpp
new file mode 100644
index 000000000..e96cce9a9
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper.cpp
@@ -0,0 +1,192 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "libarchivewrapper.h"
+#include "libarchivewrapper_p.h"
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveWrapper
+ \brief The LibArchiveWrapper class provides an interface for interacting
+ with archives handled using the libarchive archive and compression library.
+
+ The invoked archive operations are performed in a normal user mode, or in an
+ on-demand elevated user mode through the remote client-server protocol of the framework.
+
+ This class is not thread-safe; extra care should be taken especially when
+ elevated mode is active to not call its functions from any thread other than
+ where the object was created.
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename with \a parent as the parent object.
+*/
+LibArchiveWrapper::LibArchiveWrapper(const QString &filename, QObject *parent)
+ : AbstractArchive(parent)
+ , d(new LibArchiveWrapperPrivate(filename))
+{
+ connect(d, &LibArchiveWrapperPrivate::currentEntryChanged,
+ this, &LibArchiveWrapper::currentEntryChanged);
+ connect(d, &LibArchiveWrapperPrivate::completedChanged,
+ this, &LibArchiveWrapper::completedChanged);
+}
+
+/*!
+ Constructs an archive object with the given \a parent.
+*/
+LibArchiveWrapper::LibArchiveWrapper(QObject *parent)
+ : AbstractArchive(parent)
+ , d(new LibArchiveWrapperPrivate())
+{
+ connect(d, &LibArchiveWrapperPrivate::currentEntryChanged,
+ this, &LibArchiveWrapper::currentEntryChanged);
+ connect(d, &LibArchiveWrapperPrivate::completedChanged,
+ this, &LibArchiveWrapper::completedChanged);
+}
+
+/*!
+ Destroys the instance.
+*/
+LibArchiveWrapper::~LibArchiveWrapper()
+{
+ delete d;
+}
+
+/*!
+ Opens the file device using \a mode.
+ Returns \c true if succesfull; otherwise \c false.
+*/
+bool LibArchiveWrapper::open(QIODevice::OpenMode mode)
+{
+ return d->open(mode);
+}
+
+/*!
+ Closes the file device.
+*/
+void LibArchiveWrapper::close()
+{
+ d->close();
+}
+
+/*!
+ Sets the \a filename for the archive.
+
+ If the remote connection is active, the same method is called by the server.
+*/
+void LibArchiveWrapper::setFilename(const QString &filename)
+{
+ d->setFilename(filename);
+}
+
+/*!
+ Returns a human-readable description of the last error that occurred.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+QString LibArchiveWrapper::errorString() const
+{
+ return d->errorString();
+}
+
+/*!
+ Extracts the contents of this archive to \a dirPath. Returns \c true
+ on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead,
+ with the client starting a new event loop waiting for the extraction to finish.
+*/
+bool LibArchiveWrapper::extract(const QString &dirPath)
+{
+ return d->extract(dirPath);
+}
+
+/*!
+ Extracts the contents of this archive to \a dirPath with
+ precalculated count of \a totalFiles. Returns \c true
+ on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead,
+ with the client starting a new event loop waiting for the extraction to finish.
+*/
+bool LibArchiveWrapper::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ return d->extract(dirPath, totalFiles);
+}
+
+/*!
+ Packages the given \a data into the archive and creates the file on disk.
+ Returns \c true on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+bool LibArchiveWrapper::create(const QStringList &data)
+{
+ return d->create(data);
+}
+
+/*!
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> LibArchiveWrapper::list()
+{
+ return d->list();
+}
+
+/*!
+ Returns \c true if the current archive is of a supported format;
+ \c false otherwise.
+*/
+bool LibArchiveWrapper::isSupported()
+{
+ return d->isSupported();
+}
+
+/*!
+ Sets the compression level for new archives to \a level.
+*/
+void LibArchiveWrapper::setCompressionLevel(const CompressionLevel level)
+{
+ d->setCompressionLevel(level);
+}
+
+/*!
+ Cancels the extract operation in progress.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+void LibArchiveWrapper::cancel()
+{
+ d->cancel();
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/libarchivewrapper.h b/src/libs/installer/libarchivewrapper.h
new file mode 100644
index 000000000..062d53f4a
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper.h
@@ -0,0 +1,72 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIBARCHIVEWRAPPER_H
+#define LIBARCHIVEWRAPPER_H
+
+#include "installer_global.h"
+#include "abstractarchive.h"
+
+namespace QInstaller {
+
+class LibArchiveWrapperPrivate;
+
+class INSTALLER_EXPORT LibArchiveWrapper : public AbstractArchive
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LibArchiveWrapper)
+
+public:
+ LibArchiveWrapper(const QString &filename, QObject *parent = nullptr);
+ explicit LibArchiveWrapper(QObject *parent = nullptr);
+ ~LibArchiveWrapper();
+
+ bool open(QIODevice::OpenMode mode) override;
+ void close() override;
+ void setFilename(const QString &filename) override;
+
+ QString errorString() const override;
+
+ bool extract(const QString &dirPath) override;
+ bool extract(const QString &dirPath, const quint64 totalFiles) override;
+ bool create(const QStringList &data) override;
+ QVector<ArchiveEntry> list() override;
+ bool isSupported() override;
+
+ void setCompressionLevel(const AbstractArchive::CompressionLevel level) override;
+
+public Q_SLOTS:
+ void cancel() override;
+
+private:
+ LibArchiveWrapperPrivate *const d;
+};
+
+} // namespace QInstaller
+
+#endif // LIBARCHIVEWRAPPER_H
diff --git a/src/libs/installer/libarchivewrapper_p.cpp b/src/libs/installer/libarchivewrapper_p.cpp
new file mode 100644
index 000000000..b4325243d
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper_p.cpp
@@ -0,0 +1,405 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "libarchivewrapper_p.h"
+
+#include "globals.h"
+
+#include <QFileInfo>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LibArchiveWrapperPrivate
+ \internal
+*/
+
+/*!
+ \internal
+ \fn QInstaller::LibArchiveWrapperPrivate::dataBlockRequested()
+
+ Emitted when the server process has requested another data block.
+*/
+
+/*!
+ \internal
+ \fn QInstaller::LibArchiveWrapperPrivate::remoteWorkerFinished()
+
+ Emitted when the server process has finished extracting an archive.
+*/
+
+/*!
+ Constructs an archive object representing an archive file
+ specified by \a filename.
+*/
+LibArchiveWrapperPrivate::LibArchiveWrapperPrivate(const QString &filename)
+ : RemoteObject(QLatin1String(Protocol::AbstractArchive))
+{
+ init();
+ LibArchiveWrapperPrivate::setFilename(filename);
+}
+
+/*!
+ Constructs the object.
+*/
+LibArchiveWrapperPrivate::LibArchiveWrapperPrivate()
+ : RemoteObject(QLatin1String(Protocol::AbstractArchive))
+{
+ init();
+}
+
+/*!
+ Destroys the instance.
+*/
+LibArchiveWrapperPrivate::~LibArchiveWrapperPrivate()
+{
+}
+
+/*!
+ Opens the file device using \a mode.
+ Returns \c true if succesfull; otherwise \c false.
+*/
+bool LibArchiveWrapperPrivate::open(QIODevice::OpenMode mode)
+{
+ return m_archive.open(mode);
+}
+
+/*!
+ Closes the file device.
+*/
+void LibArchiveWrapperPrivate::close()
+{
+ m_archive.close();
+}
+
+/*!
+ Sets the \a filename for the archive.
+
+ If the remote connection is active, the same method is called by the server.
+*/
+void LibArchiveWrapperPrivate::setFilename(const QString &filename)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilename), filename);
+ m_lock.unlock();
+ }
+ m_archive.setFilename(filename);
+}
+
+/*!
+ Returns a human-readable description of the last error that occurred.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+QString LibArchiveWrapperPrivate::errorString() const
+{
+ if ((const_cast<LibArchiveWrapperPrivate *>(this))->connectToServer()) {
+ m_lock.lockForWrite();
+ const QString errorString
+ = callRemoteMethod<QString>(QLatin1String(Protocol::AbstractArchiveErrorString));
+ m_lock.unlock();
+ return errorString;
+ }
+ return m_archive.errorString();
+}
+
+/*!
+ Extracts the contents of this archive to \a dirPath with
+ an optional precalculated count of \a totalFiles. Returns \c true
+ on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead,
+ with the client starting a new event loop waiting for the extraction to finish.
+*/
+bool LibArchiveWrapperPrivate::extract(const QString &dirPath, const quint64 totalFiles)
+{
+ const quint64 total = totalFiles ? totalFiles : m_archive.totalFiles();
+ if (connectToServer()) {
+ QTimer timer;
+ connect(&timer, &QTimer::timeout, this, &LibArchiveWrapperPrivate::processSignals);
+ timer.start();
+
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveExtract), dirPath, total);
+ m_lock.unlock();
+ {
+ QEventLoop loop;
+ connect(this, &LibArchiveWrapperPrivate::remoteWorkerFinished, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ timer.stop();
+ return (workerStatus() == ExtractWorker::Success);
+ }
+ return m_archive.extract(dirPath, total);
+}
+
+/*!
+ Packages the given \a data into the archive and creates the file on disk.
+ Returns \c true on success; \c false otherwise.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+bool LibArchiveWrapperPrivate::create(const QStringList &data)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ const bool success
+ = callRemoteMethod<bool>(QLatin1String(Protocol::AbstractArchiveCreate), data);
+ m_lock.unlock();
+ return success;
+ }
+ return m_archive.create(data);
+}
+
+/*!
+ Returns the contents of this archive as an array of \c ArchiveEntry objects.
+ On failure, returns an empty array.
+*/
+QVector<ArchiveEntry> LibArchiveWrapperPrivate::list()
+{
+ return m_archive.list();
+}
+
+/*!
+ Returns \c true if the current archive is of a supported format;
+ \c false otherwise.
+*/
+bool LibArchiveWrapperPrivate::isSupported()
+{
+ return m_archive.isSupported();
+}
+
+/*!
+ Sets the compression level for new archives to \a level.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+void LibArchiveWrapperPrivate::setCompressionLevel(const AbstractArchive::CompressionLevel level)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetCompressionLevel), level);
+ m_lock.unlock();
+ return;
+ }
+ m_archive.setCompressionLevel(level);
+}
+
+/*!
+ Cancels the extract operation in progress.
+
+ If the remote connection is active, the method is called by the server instead.
+*/
+void LibArchiveWrapperPrivate::cancel()
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveCancel));
+ m_lock.unlock();
+ return;
+ }
+ m_archive.cancel();
+}
+
+/*!
+ Calls a remote method to get the associated queued signals from the server.
+ Signals are then processed and emitted client-side.
+*/
+void LibArchiveWrapperPrivate::processSignals()
+{
+ if (!isConnectedToServer())
+ return;
+
+ if (!m_lock.tryLockForRead())
+ return;
+
+ QList<QVariant> receivedSignals =
+ callRemoteMethod<QList<QVariant>>(QString::fromLatin1(Protocol::GetAbstractArchiveSignals));
+
+ m_lock.unlock();
+ while (!receivedSignals.isEmpty()) {
+ const QString name = receivedSignals.takeFirst().toString();
+ if (name == QLatin1String(Protocol::AbstractArchiveSignalCurrentEntryChanged)) {
+ emit currentEntryChanged(receivedSignals.takeFirst().toString());
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalCompletedChanged)) {
+ const quint64 completed = receivedSignals.takeFirst().value<quint64>();
+ const quint64 total = receivedSignals.takeFirst().value<quint64>();
+ emit completedChanged(completed, total);
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalDataBlockRequested)) {
+ emit dataBlockRequested();
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalSeekRequested)) {
+ const qint64 offset = receivedSignals.takeFirst().value<qint64>();
+ const int whence = receivedSignals.takeFirst().value<int>();
+ emit seekRequested(offset, whence);
+ } else if (name == QLatin1String(Protocol::AbstractArchiveSignalWorkerFinished)) {
+ emit remoteWorkerFinished();
+ }
+ }
+}
+
+/*!
+ Reads a block of data from the current position of the underlying file device.
+*/
+void LibArchiveWrapperPrivate::onDataBlockRequested()
+{
+ constexpr quint64 blockSize = 1024 * 1024; // 1MB
+
+ QFile *const file = &m_archive.m_data->file;
+ if (!file->isOpen() || file->isSequential()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << file->errorString();
+ setClientDataAtEnd();
+ return;
+ }
+ if (file->atEnd() && file->seek(0)) {
+ setClientDataAtEnd();
+ return;
+ }
+
+ QByteArray *const buff = &m_archive.m_data->buffer;
+ if (!buff->isEmpty())
+ buff->clear();
+
+ if (buff->size() != blockSize)
+ buff->resize(blockSize);
+
+ const qint64 bytesRead = file->read(buff->data(), blockSize);
+ if (bytesRead == -1) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << file->errorString();
+ setClientDataAtEnd();
+ return;
+ }
+ // The read callback in ExtractWorker class expects the buffer size to
+ // match the number of bytes read. Some formats will fail if the buffer
+ // is larger than the actual data.
+ if (buff->size() != bytesRead)
+ buff->resize(bytesRead);
+
+ addDataBlock(*buff);
+}
+
+/*!
+ Seeks to specified \a offset in the underlying file device. Possible \a whence
+ values are \c SEEK_SET, \c SEEK_CUR, and \c SEEK_END.
+*/
+void LibArchiveWrapperPrivate::onSeekRequested(qint64 offset, int whence)
+{
+ QFile *const file = &m_archive.m_data->file;
+ if (!file->isOpen() || file->isSequential()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << file->errorString();
+ setClientFilePosition(ARCHIVE_FATAL);
+ return;
+ }
+ bool success = false;
+ switch (whence) {
+ case SEEK_SET: // moves file pointer position to the beginning of the file
+ success = file->seek(offset);
+ break;
+ case SEEK_CUR: // moves file pointer position to given location
+ success = file->seek(file->pos() + offset);
+ break;
+ case SEEK_END: // moves file pointer position to the end of file
+ success = file->seek(file->size() + offset);
+ break;
+ default:
+ break;
+ }
+ setClientFilePosition(success ? file->pos() : ARCHIVE_FATAL);
+}
+
+/*!
+ Connects handler signals for the matching signals of the wrapper object.
+*/
+void LibArchiveWrapperPrivate::init()
+{
+ QObject::connect(&m_archive, &LibArchiveArchive::currentEntryChanged,
+ this, &LibArchiveWrapperPrivate::currentEntryChanged);
+ QObject::connect(&m_archive, &LibArchiveArchive::completedChanged,
+ this, &LibArchiveWrapperPrivate::completedChanged);
+
+ QObject::connect(this, &LibArchiveWrapperPrivate::dataBlockRequested,
+ this, &LibArchiveWrapperPrivate::onDataBlockRequested);
+ QObject::connect(this, &LibArchiveWrapperPrivate::seekRequested,
+ this, &LibArchiveWrapperPrivate::onSeekRequested);
+}
+
+/*!
+ Calls a remote method to add a \a buffer for reading.
+*/
+void LibArchiveWrapperPrivate::addDataBlock(const QByteArray &buffer)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveAddDataBlock), buffer);
+ m_lock.unlock();
+ }
+}
+
+/*!
+ Calls a remote method to inform that the client has finished
+ reading the current file.
+*/
+void LibArchiveWrapperPrivate::setClientDataAtEnd()
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd));
+ m_lock.unlock();
+ }
+}
+
+/*!
+ Calls a remote method to set new file position \a pos.
+*/
+void LibArchiveWrapperPrivate::setClientFilePosition(qint64 pos)
+{
+ if (connectToServer()) {
+ m_lock.lockForWrite();
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::AbstractArchiveSetFilePosition), pos);
+ m_lock.unlock();
+ }
+}
+
+/*!
+ Calls a remote method to retrieve and return the status of
+ the extract worker on a server process.
+*/
+ExtractWorker::Status LibArchiveWrapperPrivate::workerStatus() const
+{
+ ExtractWorker::Status status = ExtractWorker::Unfinished;
+ if ((const_cast<LibArchiveWrapperPrivate *>(this))->connectToServer()) {
+ m_lock.lockForWrite();
+ status = static_cast<ExtractWorker::Status>(
+ callRemoteMethod<qint32>(QLatin1String(Protocol::AbstractArchiveWorkerStatus)));
+ m_lock.unlock();
+ }
+ return status;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/libarchivewrapper_p.h b/src/libs/installer/libarchivewrapper_p.h
new file mode 100644
index 000000000..9e612fab3
--- /dev/null
+++ b/src/libs/installer/libarchivewrapper_p.h
@@ -0,0 +1,95 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LIBARCHIVEWRAPPER_P_H
+#define LIBARCHIVEWRAPPER_P_H
+
+#include "installer_global.h"
+#include "remoteobject.h"
+#include "libarchivearchive.h"
+
+#include <QTimer>
+#include <QReadWriteLock>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT LibArchiveWrapperPrivate : public RemoteObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(LibArchiveWrapperPrivate)
+
+public:
+ explicit LibArchiveWrapperPrivate(const QString &filename);
+ LibArchiveWrapperPrivate();
+ ~LibArchiveWrapperPrivate();
+
+ bool open(QIODevice::OpenMode mode);
+ void close();
+ void setFilename(const QString &filename);
+
+ QString errorString() const;
+
+ bool extract(const QString &dirPath, const quint64 totalFiles = 0);
+ bool create(const QStringList &data);
+ QVector<ArchiveEntry> list();
+ bool isSupported();
+
+ void setCompressionLevel(const AbstractArchive::CompressionLevel level);
+
+Q_SIGNALS:
+ void currentEntryChanged(const QString &filename);
+ void completedChanged(const quint64 completed, const quint64 total);
+ void dataBlockRequested();
+ void seekRequested(qint64 offset, int whence);
+ void remoteWorkerFinished();
+
+public Q_SLOTS:
+ void cancel();
+
+private Q_SLOTS:
+ void processSignals();
+ void onDataBlockRequested();
+ void onSeekRequested(qint64 offset, int whence);
+
+private:
+ void init();
+
+ void addDataBlock(const QByteArray &buffer);
+ void setClientDataAtEnd();
+ void setClientFilePosition(qint64 pos);
+ ExtractWorker::Status workerStatus() const;
+
+private:
+ mutable QReadWriteLock m_lock;
+
+ LibArchiveArchive m_archive;
+};
+
+} // namespace QInstaller
+
+#endif // LIBARCHIVEWRAPPER_P_H
diff --git a/src/libs/installer/licenseoperation.cpp b/src/libs/installer/licenseoperation.cpp
index 059798338..0d30ab514 100644
--- a/src/libs/installer/licenseoperation.cpp
+++ b/src/libs/installer/licenseoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,8 @@
#include "packagemanagercore.h"
#include "settings.h"
+#include "fileutils.h"
+#include "globals.h"
#include <QtCore/QDir>
#include <QtCore/QFile>
@@ -37,6 +39,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LicenseOperation
+ \internal
+*/
+
LicenseOperation::LicenseOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -49,7 +57,7 @@ void LicenseOperation::backup()
bool LicenseOperation::performOperation()
{
- QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ QVariantMap licenses = value(scLicensesValue).toMap();
if (licenses.isEmpty()) {
setError(UserDefinedError);
setErrorString(tr("No license files found to copy."));
@@ -68,18 +76,22 @@ bool LicenseOperation::performOperation()
QDir dir;
dir.mkpath(targetDir);
+ setDefaultFilePermissions(targetDir, DefaultFilePermissions::Executable);
setArguments(QStringList(targetDir));
for (QVariantMap::const_iterator it = licenses.constBegin(); it != licenses.constEnd(); ++it) {
QFile file(targetDir + QLatin1Char('/') + it.key());
- if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
setError(UserDefinedError);
setErrorString(tr("Can not write license file \"%1\".").arg(QDir::toNativeSeparators(file.fileName())));
return false;
}
- QTextStream stream(&file);
+ QString outString;
+ QTextStream stream(&outString);
stream << it.value().toString();
+
+ file.write(outString.toUtf8());
}
return true;
@@ -87,11 +99,10 @@ bool LicenseOperation::performOperation()
bool LicenseOperation::undoOperation()
{
- const QVariantMap licenses = value(QLatin1String("licenses")).toMap();
+ const QVariantMap licenses = value(scLicensesValue).toMap();
if (licenses.isEmpty()) {
- setError(UserDefinedError);
- setErrorString(tr("No license files found to delete."));
- return false;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "No license files found to delete.";
+ return true;
}
QString targetDir = arguments().value(0);
diff --git a/src/libs/installer/licenseoperation.h b/src/libs/installer/licenseoperation.h
index 49b885565..6e7743428 100644
--- a/src/libs/installer/licenseoperation.h
+++ b/src/libs/installer/licenseoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT LicenseOperation : public Operation
public:
explicit LicenseOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
} // namespace QInstaller
diff --git a/src/libs/installer/linereplaceoperation.cpp b/src/libs/installer/linereplaceoperation.cpp
index e67335e55..a384dbc4c 100644
--- a/src/libs/installer/linereplaceoperation.cpp
+++ b/src/libs/installer/linereplaceoperation.cpp
@@ -34,6 +34,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LineReplaceOperation
+ \internal
+*/
+
LineReplaceOperation::LineReplaceOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -58,6 +64,14 @@ bool LineReplaceOperation::performOperation()
const QString searchString = args.at(1);
const QString replaceString = args.at(2);
+ if (searchString.isEmpty()) {
+ setError(InvalidArguments);
+ setErrorString(tr("Invalid argument in %1: Empty search "
+ "argument is not supported.").arg(name()));
+
+ return false;
+ }
+
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
setError(UserDefinedError);
diff --git a/src/libs/installer/linereplaceoperation.h b/src/libs/installer/linereplaceoperation.h
index d15e11fef..13e764acc 100644
--- a/src/libs/installer/linereplaceoperation.h
+++ b/src/libs/installer/linereplaceoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT LineReplaceOperation : public Operation
public:
explicit LineReplaceOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
} // namespace
diff --git a/src/libs/installer/link.cpp b/src/libs/installer/link.cpp
index 62ba06cb2..027dac232 100644
--- a/src/libs/installer/link.cpp
+++ b/src/libs/installer/link.cpp
@@ -28,6 +28,7 @@
#include "link.h"
#include "utils.h"
+#include "globals.h"
#include <QFileInfo>
#include <QDir>
@@ -88,7 +89,8 @@ public:
OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, nullptr);
if (m_dirHandle == INVALID_HANDLE_VALUE) {
- qWarning() << "Cannot open" << path << ":" << QInstaller::windowsErrorString(GetLastError());
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open" << path << ":"
+ << QInstaller::windowsErrorString(GetLastError());
}
}
@@ -137,18 +139,22 @@ QString readWindowsSymLink(const QString &path)
Link createJunction(const QString &linkPath, const QString &targetPath)
{
if (!QDir().mkpath(linkPath)) {
- qWarning() << "Cannot create the mount directory" << linkPath;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create the mount directory" << linkPath;
return Link(linkPath);
}
FileHandleWrapper dirHandle(linkPath);
if (dirHandle.handle() == INVALID_HANDLE_VALUE) {
- qWarning() << "Cannot open" << linkPath << ":" << QInstaller::windowsErrorString(GetLastError());
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open" << linkPath << ":"
+ << QInstaller::windowsErrorString(GetLastError());
return Link(linkPath);
}
- const QString szDestDir = QString::fromLatin1("\\??\\").arg(targetPath).replace(QLatin1Char('/'),
+ const QString szDestDir = QString::fromLatin1("\\??\\%1").arg(targetPath).replace(QLatin1Char('/'),
QLatin1Char('\\'));
+ // Get string length in bytes, not in characters count
+ const size_t destDirBytes = szDestDir.size() * sizeof(ushort);
+
// Allocates a block of memory for an array of num elements(1) and initializes all its bits to zero.
REPARSE_DATA_BUFFER* reparseStructData = (REPARSE_DATA_BUFFER*)calloc(1,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
@@ -156,11 +162,11 @@ Link createJunction(const QString &linkPath, const QString &targetPath)
reparseStructData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseStructData->MountPointReparseBuffer.PrintNameLength = 0;
reparseStructData->MountPointReparseBuffer.SubstituteNameOffset = 0;
- reparseStructData->MountPointReparseBuffer.SubstituteNameLength = szDestDir.length();
- reparseStructData->MountPointReparseBuffer.PrintNameOffset = szDestDir.length() + sizeof(WCHAR);
+ reparseStructData->MountPointReparseBuffer.SubstituteNameLength = destDirBytes;
+ reparseStructData->MountPointReparseBuffer.PrintNameOffset = destDirBytes + sizeof(WCHAR);
uint spaceAfterGeneralData = sizeof(USHORT) * 5 + sizeof(WCHAR); //should be 12
- reparseStructData->ReparseDataLength = szDestDir.length() + spaceAfterGeneralData;
+ reparseStructData->ReparseDataLength = destDirBytes + spaceAfterGeneralData;
#ifndef Q_CC_MINGW
StringCchCopy(reparseStructData->MountPointReparseBuffer.PathBuffer, 1024, (wchar_t*)szDestDir.utf16());
@@ -172,7 +178,7 @@ Link createJunction(const QString &linkPath, const QString &targetPath)
if (!::DeviceIoControl(dirHandle.handle(), FSCTL_SET_REPARSE_POINT, reparseStructData,
reparseStructData->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE, nullptr, 0,
&bytesReturned, nullptr)) {
- qWarning() << "Cannot set the reparse point for" << linkPath << "to" << targetPath
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot set the reparse point for" << linkPath << "to" << targetPath
<< ":" << QInstaller::windowsErrorString(GetLastError());
}
return Link(linkPath);
@@ -194,7 +200,8 @@ bool removeJunction(const QString &path)
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, nullptr, 0,
&bytesReturned, nullptr)) {
- qWarning() << "Cannot remove the reparse point" << path << ":" << QInstaller::windowsErrorString(GetLastError());
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot remove the reparse point" << path << ":"
+ << QInstaller::windowsErrorString(GetLastError());
return false;
}
}
@@ -207,7 +214,8 @@ Link createLnSymlink(const QString &linkPath, const QString &targetPath)
int linkedError = symlink(QFileInfo(targetPath).absoluteFilePath().toUtf8(),
QFileInfo(linkPath).absoluteFilePath().toUtf8());
if (linkedError != 0) {
- qWarning() << "Cannot create a symlink from" << linkPath << "to" << targetPath << ":" << linkedError;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create a symlink from" << linkPath << "to"
+ << targetPath << ":" << linkedError;
}
@@ -234,7 +242,7 @@ Link Link::create(const QString &link, const QString &targetPath)
if (!linkPathExists)
linkPathExists = QDir().mkpath(linkPath);
if (!linkPathExists) {
- qWarning() << "Cannot create the needed directories" << link;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create the needed directories" << link;
return Link(link);
}
@@ -242,8 +250,8 @@ Link Link::create(const QString &link, const QString &targetPath)
if (QFileInfo(targetPath).isDir())
return createJunction(link, targetPath);
- qWarning() << "At the moment the" << Q_FUNC_INFO << "can not create anything else as "
- << "junctions for directories under windows";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "At the moment the" << Q_FUNC_INFO
+ << "can not create anything else as " << "junctions for directories under windows";
return Link(link);
#else
return createLnSymlink(link, targetPath);
@@ -255,7 +263,7 @@ QString Link::targetPath() const
#ifdef Q_OS_WIN
return readWindowsSymLink(m_path);
#else
- return QFileInfo(m_path).readLink();
+ return QFileInfo(m_path).symLinkTarget();
#endif
}
diff --git a/src/libs/installer/loggingutils.cpp b/src/libs/installer/loggingutils.cpp
new file mode 100644
index 000000000..9a36720dd
--- /dev/null
+++ b/src/libs/installer/loggingutils.cpp
@@ -0,0 +1,567 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "loggingutils.h"
+
+#include "component.h"
+#include "componentalias.h"
+#include "globals.h"
+#include "fileutils.h"
+#include "packagemanagercore.h"
+#include "remoteclient.h"
+#include "remotefileengine.h"
+
+#include <QXmlStreamWriter>
+#include <QElapsedTimer>
+
+#include <iostream>
+#if defined(Q_OS_UNIX)
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <stdio.h>
+#include <io.h>
+#endif
+
+namespace QInstaller {
+
+/*!
+ \class QInstaller::LoggingHandler
+ \inmodule QtInstallerFramework
+ \brief The LoggingHandler class provides methods for manipulating the
+ application-wide verbosiveness and format of printed debug messages.
+
+ The class also contains utility methods for printing common preformatted messages.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Uptime
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::VerboseWriter
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::VerboseWriterOutput
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::PlainVerboseWriterOutput
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::VerboseWriterAdminOutput
+ \internal
+*/
+
+/*!
+ \enum LoggingHandler::VerbosityLevel
+ \brief This enum holds the possible levels of output verbosity.
+
+ \value Silent
+ \value Normal
+ \value Detailed
+ \value Minimum
+ Minimum possible verbosity level. Synonym for \c VerbosityLevel::Silent.
+ \value Maximum
+ Maximum possible verbosity level. Synonym for \c VerbosityLevel::Detailed.
+*/
+
+// start timer on construction (so we can use it as static member)
+class Uptime : public QElapsedTimer {
+public:
+ Uptime() { start(); }
+};
+
+/*!
+ \internal
+*/
+LoggingHandler::LoggingHandler()
+ : m_verbLevel(VerbosityLevel::Silent)
+ , m_outputRedirected(false)
+{
+#if defined(Q_OS_UNIX)
+ m_outputRedirected = !isatty(fileno(stdout));
+#elif defined(Q_OS_WIN)
+ m_outputRedirected = !_isatty(_fileno(stdout));
+#endif
+}
+
+/*!
+ \internal
+*/
+LoggingHandler::~LoggingHandler()
+{
+}
+
+/*!
+ Prints out preformatted debug messages, warnings, critical and fatal error messages
+ specified by \a msg and \a type. The message \a context provides information about
+ the source code location the message was generated.
+*/
+void LoggingHandler::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
+{
+ QMutexLocker _(&m_mutex);
+
+ // suppress warning from QPA minimal plugin
+ if (msg.contains(QLatin1String("This plugin does not support propagateSizeHints")))
+ return;
+
+ if (context.category == lcProgressIndicator().categoryName()) {
+ if (!outputRedirected())
+ std::cout << msg.toStdString() << "\r" << std::flush;
+ return;
+ }
+
+ static Uptime uptime;
+
+ QString ba = QLatin1Char('[') + QString::number(uptime.elapsed()) + QStringLiteral("] ");
+ ba += trimAndPrepend(type, msg);
+
+ if (type != QtDebugMsg && context.file) {
+ ba += QString(QStringLiteral(" (%1:%2, %3)")).arg(
+ QString::fromLatin1(context.file)).arg(context.line).arg(
+ QString::fromLatin1(context.function));
+ }
+
+ if (VerboseWriter *log = VerboseWriter::instance())
+ log->appendLine(ba);
+
+ if (type != QtDebugMsg || isVerbose())
+ std::cout << qPrintable(ba) << std::endl;
+
+ if (type == QtFatalMsg) {
+ QtMessageHandler oldMsgHandler = qInstallMessageHandler(nullptr);
+ qt_message_output(type, context, msg);
+ qInstallMessageHandler(oldMsgHandler);
+ }
+}
+
+/*!
+ Trims the trailing space character and surrounding quotes from \a msg.
+ Also prepends the message \a type to the message.
+*/
+QString LoggingHandler::trimAndPrepend(QtMsgType type, const QString &msg) const
+{
+ QString ba(msg);
+ // last character is a space from qDebug
+ if (ba.endsWith(QLatin1Char(' ')))
+ ba.chop(1);
+
+ // remove quotes if the whole message is surrounded with them
+ if (ba.startsWith(QLatin1Char('"')) && ba.endsWith(QLatin1Char('"')))
+ ba = ba.mid(1, ba.length() - 2);
+
+ // prepend the message type, skip QtDebugMsg
+ switch (type) {
+ case QtWarningMsg:
+ ba.prepend(QStringLiteral("Warning: "));
+ break;
+
+ case QtCriticalMsg:
+ ba.prepend(QStringLiteral("Critical: "));
+ break;
+
+ case QtFatalMsg:
+ ba.prepend(QStringLiteral("Fatal: "));
+ break;
+
+ default:
+ break;
+ }
+ return ba;
+}
+
+/*!
+ Returns the only instance of this class.
+*/
+LoggingHandler &LoggingHandler::instance()
+{
+ static LoggingHandler instance;
+ return instance;
+}
+
+/*!
+ Sets to verbose output if \a v is set to \c true. Calling this multiple
+ times increases or decreases the verbosity level accordingly.
+*/
+void LoggingHandler::setVerbose(bool v)
+{
+ if (v)
+ m_verbLevel++;
+ else
+ m_verbLevel--;
+}
+
+/*!
+ Returns \c true if the installer is set to verbose output.
+*/
+bool LoggingHandler::isVerbose() const
+{
+ return m_verbLevel != VerbosityLevel::Silent;
+}
+
+/*!
+ Returns the current verbosity level.
+*/
+LoggingHandler::VerbosityLevel LoggingHandler::verboseLevel() const
+{
+ return m_verbLevel;
+}
+
+/*!
+ Returns \c true if output is redirected, i.e. to a file.
+*/
+bool LoggingHandler::outputRedirected() const
+{
+ return m_outputRedirected;
+}
+
+/*!
+ Prints update information from \a components.
+*/
+void LoggingHandler::printUpdateInformation(const QList<Component *> &components) const
+{
+ QString output;
+ QXmlStreamWriter stream(&output);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(QLatin1String("updates"));
+ foreach (const Component *component, components) {
+ stream.writeStartElement(QLatin1String("update"));
+ stream.writeAttribute(QLatin1String("name"), component->value(scDisplayName));
+ stream.writeAttribute(QLatin1String("version"), component->value(scVersion));
+ stream.writeAttribute(QLatin1String("size"), component->value(scUncompressedSize));
+ stream.writeAttribute(QLatin1String("id"), component->value(scName));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+
+ stream.writeEndDocument();
+ std::cout << qPrintable(output);
+}
+
+/*!
+ Prints basic or more detailed information about local \a packages,
+ depending on the current verbosity level.
+*/
+void LoggingHandler::printLocalPackageInformation(const QList<KDUpdater::LocalPackage> &packages) const
+{
+ QString output;
+ QXmlStreamWriter stream(&output);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(QLatin1String("localpackages"));
+ foreach (KDUpdater::LocalPackage package, packages) {
+ stream.writeStartElement(QLatin1String("package"));
+ stream.writeAttribute(QLatin1String("name"), package.name);
+ stream.writeAttribute(QLatin1String("displayname"), package.title);
+ stream.writeAttribute(QLatin1String("version"), package.version);
+ if (verboseLevel() == VerbosityLevel::Detailed) {
+ stream.writeAttribute(QLatin1String("description"), package.description);
+ stream.writeAttribute(QLatin1String("sortingPriority"), QVariant(package.sortingPriority).toString());
+ stream.writeAttribute(QLatin1String("treeName"), package.treeName.first);
+ stream.writeAttribute(QLatin1String("moveChildren"), QVariant(package.treeName.second).toString());
+ stream.writeAttribute(QLatin1String("dependencies"), package.dependencies.join(QLatin1Char(',')));
+ stream.writeAttribute(QLatin1String("autoDependencies"), package.autoDependencies.join(QLatin1Char(',')));
+ stream.writeAttribute(QLatin1String("virtual"), QVariant(package.virtualComp).toString());
+ stream.writeAttribute(QLatin1String("forcedInstallation"), QVariant(package.forcedInstallation).toString());
+ stream.writeAttribute(QLatin1String("checkable"), QVariant(package.checkable).toString());
+ stream.writeAttribute(QLatin1String("uncompressedSize"), QVariant(package.uncompressedSize).toString());
+ stream.writeAttribute(QLatin1String("installDate"), package.installDate.toString());
+ stream.writeAttribute(QLatin1String("lastUpdateDate"), package.lastUpdateDate.toString());
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+
+ stream.writeEndDocument();
+ std::cout << qPrintable(output);
+}
+
+/*!
+ Prints basic or more detailed information about available \a matchedPackages,
+ depending on the current verbosity level. If a package is also present in \a installedPackages,
+ the installed version will be included in printed information.
+*/
+void LoggingHandler::printPackageInformation(const PackagesList &matchedPackages, const LocalPackagesMap &installedPackages) const
+{
+ QString output;
+ QXmlStreamWriter stream(&output);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(QLatin1String("availablepackages"));
+ foreach (Package *package, matchedPackages) {
+ const QString name = package->data(scName).toString();
+ stream.writeStartElement(QLatin1String("package"));
+ stream.writeAttribute(QLatin1String("name"), name);
+ stream.writeAttribute(QLatin1String("displayname"), package->data(scDisplayName).toString());
+ stream.writeAttribute(QLatin1String("version"), package->data(scVersion).toString());
+ //Check if package already installed
+ if (installedPackages.contains(name))
+ stream.writeAttribute(QLatin1String("installedVersion"), installedPackages.value(name).version);
+ if (verboseLevel() == VerbosityLevel::Detailed) {
+ stream.writeAttribute(QLatin1String("description"), package->data(scDescription).toString());
+ stream.writeAttribute(QLatin1String("treeName"), package->data(scTreeName).value<QPair<QString, bool>>().first);
+ stream.writeAttribute(QLatin1String("moveChildren"), QVariant(package->data(scTreeName).value<QPair<QString, bool>>().second).toString());
+ stream.writeAttribute(QLatin1String("dependencies"), package->data(scDependencies).toString());
+ stream.writeAttribute(QLatin1String("autoDependencies"), package->data(scAutoDependOn).toString());
+ stream.writeAttribute(QLatin1String("virtual"), package->data(scVirtual).toString());
+ stream.writeAttribute(QLatin1String("forcedInstallation"), package->data(QLatin1String("ForcedInstallation")).toString());
+ stream.writeAttribute(QLatin1String("checkable"), package->data(scCheckable).toString());
+ stream.writeAttribute(QLatin1String("default"), package->data(scDefault).toString());
+ stream.writeAttribute(QLatin1String("essential"), package->data(scEssential).toString());
+ stream.writeAttribute(QLatin1String("forcedUpdate"), package->data(scForcedUpdate).toString());
+ stream.writeAttribute(QLatin1String("compressedsize"), package->data(QLatin1String("CompressedSize")).toString());
+ stream.writeAttribute(QLatin1String("uncompressedsize"), package->data(QLatin1String("UncompressedSize")).toString());
+ stream.writeAttribute(QLatin1String("releaseDate"), package->data(scReleaseDate).toString());
+ stream.writeAttribute(QLatin1String("downloadableArchives"), package->data(scDownloadableArchives).toString());
+ stream.writeAttribute(QLatin1String("licenses"), package->data(QLatin1String("Licenses")).toString());
+ stream.writeAttribute(QLatin1String("script"), package->data(scScript).toString());
+ stream.writeAttribute(QLatin1String("sortingPriority"), package->data(scSortingPriority).toString());
+ stream.writeAttribute(QLatin1String("replaces"), package->data(scReplaces).toString());
+ stream.writeAttribute(QLatin1String("requiresAdminRights"), package->data(scRequiresAdminRights).toString());
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+
+ stream.writeEndDocument();
+ std::cout << qPrintable(output);
+}
+
+/*!
+ Prints basic or more detailed information about component \a aliases,
+ depending on the current verbosity level.
+*/
+void LoggingHandler::printAliasInformation(const QList<ComponentAlias *> &aliases)
+{
+ QList<ComponentAlias *> sortedAliases = aliases;
+ std::sort(sortedAliases.begin(), sortedAliases.end(),
+ [](const ComponentAlias *lhs, const ComponentAlias *rhs) {
+ return lhs->name() < rhs->name();
+ }
+ );
+
+ QString output;
+ QTextStream stream(&output);
+
+ stream << Qt::endl;
+ for (auto *alias : qAsConst(sortedAliases)) {
+ stream << "Name: " << alias->name() << Qt::endl;
+ stream << "Display name: " << alias->displayName() << Qt::endl;
+ stream << "Description: " << alias->description() << Qt::endl;
+ stream << "Version: " << alias->version() << Qt::endl;
+ if (verboseLevel() == VerbosityLevel::Detailed)
+ stream << "Virtual: " << alias->value(scVirtual) << Qt::endl;
+
+ stream << "Components: " << alias->value(scRequiredComponents) << Qt::endl;
+ stream << "Required aliases: " << alias->value(scRequiredAliases) << Qt::endl;
+
+ stream << "Optional components: " << alias->value(scOptionalComponents) << Qt::endl;
+ stream << "Optional aliases: " << alias->value(scOptionalAliases) << Qt::endl;
+
+ if (sortedAliases.indexOf(alias) != (sortedAliases.count() - 1))
+ stream << "========================================" << Qt::endl;
+ }
+
+ std::cout << qPrintable(output);
+}
+
+/*!
+ \internal
+*/
+VerboseWriter::VerboseWriter()
+{
+ m_preFileBuffer.open(QIODevice::ReadWrite);
+ m_stream.setDevice(&m_preFileBuffer);
+ m_currentDateTimeAsString = QDateTime::currentDateTime().toString();
+}
+
+/*!
+ \internal
+*/
+VerboseWriter::~VerboseWriter()
+{
+ if (m_preFileBuffer.isOpen()) {
+ PlainVerboseWriterOutput output;
+ (void)flush(&output);
+ }
+}
+
+/*!
+ \internal
+*/
+bool VerboseWriter::flush(VerboseWriterOutput *output)
+{
+ m_stream.flush();
+ if (m_logFileName.isEmpty()) // binarycreator
+ return true;
+ if (!m_preFileBuffer.isOpen())
+ return true;
+ //if the installer installed nothing - there is no target directory - where the logfile can be saved
+ if (!QFileInfo(m_logFileName).absoluteDir().exists())
+ return true;
+
+ QString logInfo;
+ logInfo += QLatin1String("************************************* Invoked: ");
+ logInfo += m_currentDateTimeAsString;
+ logInfo += QLatin1String("\n");
+
+ QBuffer buffer;
+ buffer.open(QIODevice::WriteOnly);
+ buffer.write(logInfo.toLocal8Bit());
+ buffer.write(m_preFileBuffer.data());
+ buffer.close();
+
+ if (output->write(m_logFileName, QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text, buffer.data())) {
+ m_preFileBuffer.close();
+ m_stream.setDevice(nullptr);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+*/
+void VerboseWriter::setFileName(const QString &fileName)
+{
+ m_logFileName = fileName;
+}
+
+Q_GLOBAL_STATIC(VerboseWriter, verboseWriter)
+
+/*!
+ \internal
+*/
+VerboseWriter *VerboseWriter::instance()
+{
+ return verboseWriter();
+}
+
+/*!
+ \internal
+*/
+void VerboseWriter::appendLine(const QString &msg)
+{
+ m_stream << msg << Qt::endl;
+}
+
+/*!
+ \internal
+*/
+VerboseWriterOutput::~VerboseWriterOutput()
+{
+}
+
+/*!
+ \internal
+*/
+bool PlainVerboseWriterOutput::write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data)
+{
+ QFile output(fileName);
+ if (output.open(openMode)) {
+ output.write(data);
+ setDefaultFilePermissions(&output, DefaultFilePermissions::NonExecutable);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ \internal
+*/
+bool VerboseWriterAdminOutput::write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data)
+{
+ bool gainedAdminRights = false;
+
+ if (!RemoteClient::instance().isActive()) {
+ m_core->gainAdminRights();
+ gainedAdminRights = true;
+ }
+
+ RemoteFileEngine file;
+ file.setFileName(fileName);
+ if (file.open(openMode)) {
+ file.write(data.constData(), data.size());
+ file.close();
+ if (gainedAdminRights)
+ m_core->dropAdminRights();
+ return true;
+ }
+
+ if (gainedAdminRights)
+ m_core->dropAdminRights();
+
+ return false;
+}
+
+/*!
+ \internal
+
+ Increments verbosity \a level.
+*/
+LoggingHandler::VerbosityLevel &operator++(LoggingHandler::VerbosityLevel &level, int)
+{
+ const int i = static_cast<int>(level) + 1;
+ level = (i > LoggingHandler::VerbosityLevel::Maximum)
+ ? LoggingHandler::VerbosityLevel::Maximum
+ : static_cast<LoggingHandler::VerbosityLevel>(i);
+
+ return level;
+}
+
+/*!
+ \internal
+
+ Decrements verbosity \a level.
+*/
+LoggingHandler::VerbosityLevel &operator--(LoggingHandler::VerbosityLevel &level, int)
+{
+ const int i = static_cast<int>(level) - 1;
+ level = (i < LoggingHandler::VerbosityLevel::Minimum)
+ ? LoggingHandler::VerbosityLevel::Minimum
+ : static_cast<LoggingHandler::VerbosityLevel>(i);
+
+ return level;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/loggingutils.h b/src/libs/installer/loggingutils.h
new file mode 100644
index 000000000..18ff2d2c5
--- /dev/null
+++ b/src/libs/installer/loggingutils.h
@@ -0,0 +1,140 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef LOGGINGUTILS_H
+#define LOGGINGUTILS_H
+
+#include "qinstallerglobal.h"
+#include "localpackagehub.h"
+
+#include <QObject>
+#include <QIODevice>
+#include <QTextStream>
+#include <QBuffer>
+#include <QMutex>
+
+namespace QInstaller {
+
+class Component;
+class ComponentAlias;
+
+class INSTALLER_EXPORT LoggingHandler
+{
+ Q_DISABLE_COPY(LoggingHandler)
+ Q_ENUMS(VerbosityLevel)
+
+public:
+ enum VerbosityLevel {
+ Silent = 0,
+ Normal = 1,
+ Detailed = 2,
+ Minimum = Silent,
+ Maximum = Detailed
+ };
+
+ static LoggingHandler &instance();
+ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+
+ void setVerbose(bool v);
+ bool isVerbose() const;
+ VerbosityLevel verboseLevel() const;
+ bool outputRedirected() const;
+
+ void printUpdateInformation(const QList<Component *> &components) const;
+ void printLocalPackageInformation(const QList<KDUpdater::LocalPackage> &packages) const;
+ void printPackageInformation(const PackagesList &matchedPackages, const LocalPackagesMap &installedPackages) const;
+ void printAliasInformation(const QList<ComponentAlias *> &aliases);
+
+ friend VerbosityLevel &operator++(VerbosityLevel &level, int);
+ friend VerbosityLevel &operator--(VerbosityLevel &level, int);
+
+private:
+ LoggingHandler();
+ ~LoggingHandler();
+
+ QString trimAndPrepend(QtMsgType type, const QString &msg) const;
+
+private:
+ VerbosityLevel m_verbLevel;
+ bool m_outputRedirected;
+
+ QMutex m_mutex;
+};
+
+class INSTALLER_EXPORT VerboseWriterOutput
+{
+public:
+ virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) = 0;
+
+protected:
+ ~VerboseWriterOutput();
+};
+
+class INSTALLER_EXPORT PlainVerboseWriterOutput : public VerboseWriterOutput
+{
+public:
+ virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) override;
+};
+
+class INSTALLER_EXPORT VerboseWriterAdminOutput : public VerboseWriterOutput
+{
+public:
+ VerboseWriterAdminOutput(PackageManagerCore *core) : m_core(core) {}
+
+ virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) override;
+
+private:
+ PackageManagerCore *m_core;
+};
+
+class INSTALLER_EXPORT VerboseWriter
+{
+public:
+ VerboseWriter();
+ ~VerboseWriter();
+
+ static VerboseWriter *instance();
+
+ bool flush(VerboseWriterOutput *output);
+
+ void appendLine(const QString &msg);
+ void setFileName(const QString &fileName);
+
+private:
+ QTextStream m_stream;
+ QBuffer m_preFileBuffer;
+ QString m_logFileName;
+ QString m_currentDateTimeAsString;
+};
+
+LoggingHandler::VerbosityLevel &operator++(LoggingHandler::VerbosityLevel &level, int);
+LoggingHandler::VerbosityLevel &operator--(LoggingHandler::VerbosityLevel &level, int);
+
+} // namespace QInstaller
+
+#endif // LOGGINGUTILS_H
diff --git a/src/libs/installer/messageboxhandler.cpp b/src/libs/installer/messageboxhandler.cpp
index 28e09df44..052709e51 100644
--- a/src/libs/installer/messageboxhandler.cpp
+++ b/src/libs/installer/messageboxhandler.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,11 +28,15 @@
#include "messageboxhandler.h"
+#include "globals.h"
+#include "loggingutils.h"
+
#include <QtCore/QDebug>
#include <QApplication>
#include <QDialogButtonBox>
#include <QPushButton>
+#include <QMetaEnum>
/*!
\inmodule QtInstallerFramework
@@ -90,6 +94,8 @@ using namespace QInstaller;
Accept the message box.
\value Reject
Reject the message box.
+ \value Default
+ Uses default answer set for message box.
*/
/*!
@@ -280,7 +286,7 @@ QMessageBox::StandardButton MessageBoxHandler::warning(QWidget *parent, const QS
returns the escape button.
*/
int MessageBoxHandler::critical(const QString &identifier, const QString &title,
- const QString &text, int buttons, int button) const
+ const QString &text, int buttons, int button)
{
return showMessageBox(criticalType, currentBestSuitParent(), identifier, title, text,
QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button));
@@ -299,7 +305,7 @@ int MessageBoxHandler::critical(const QString &identifier, const QString &title,
returns the escape button.
*/
int MessageBoxHandler::information(const QString &identifier, const QString &title,
- const QString &text, int buttons, int button) const
+ const QString &text, int buttons, int button)
{
return showMessageBox(informationType, currentBestSuitParent(), identifier, title, text,
QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button));
@@ -318,7 +324,7 @@ int MessageBoxHandler::information(const QString &identifier, const QString &tit
returns the escape button.
*/
int MessageBoxHandler::question(const QString &identifier, const QString &title,
- const QString &text, int buttons, int button) const
+ const QString &text, int buttons, int button)
{
return showMessageBox(questionType, currentBestSuitParent(), identifier, title, text,
QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button));
@@ -336,7 +342,7 @@ int MessageBoxHandler::question(const QString &identifier, const QString &title,
returns the escape button.
*/
int MessageBoxHandler::warning(const QString &identifier, const QString &title, const QString &text,
- int buttons, int button) const
+ int buttons, int button)
{
return showMessageBox(warningType, currentBestSuitParent(), identifier, title, text,
QMessageBox::StandardButtons(buttons), QMessageBox::StandardButton(button));
@@ -362,6 +368,9 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton defaultButton)
{
QMessageBox msgBox(icon, title, text, QMessageBox::NoButton, parent);
+ msgBox.setTextInteractionFlags(Qt::TextBrowserInteraction);
+ msgBox.setTextFormat(Qt::RichText);
+
QDialogButtonBox *buttonBox = msgBox.findChild<QDialogButtonBox *>();
Q_ASSERT(buttonBox != nullptr);
@@ -391,24 +400,49 @@ static QMessageBox::StandardButton showNewMessageBox(QWidget *parent, QMessageBo
QMessageBox::StandardButton MessageBoxHandler::showMessageBox(MessageType messageType, QWidget *parent,
const QString &identifier, const QString &title, const QString &text, QMessageBox::StandardButtons buttons,
- QMessageBox::StandardButton defaultButton) const
+ const QMessageBox::StandardButton defaultButton)
{
- static QHash<MessageType, QString> messageTypeHash;
- if (messageTypeHash.isEmpty()) {
- messageTypeHash.insert(criticalType, QLatin1String("critical"));
- messageTypeHash.insert(informationType, QLatin1String("information"));
- messageTypeHash.insert(questionType, QLatin1String("question"));
- messageTypeHash.insert(warningType, QLatin1String("warning"));
- };
-
- qDebug().nospace() << "Created " << messageTypeHash.value(messageType).toUtf8().constData()
- << " message box " << identifier << ": " << title << ", " << text;
-
- if (qobject_cast<QApplication*> (qApp) == nullptr)
- return defaultButton;
+ QString availableAnswers = availableAnswerOptions(buttons);
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << identifier << ":" << title << ":" << text
+ << availableAnswers;
+
+ if (m_automaticAnswers.contains(identifier)) {
+ QMessageBox::StandardButton selectedButton = m_automaticAnswers.value(identifier);
+ const QString buttonName = enumToString(QMessageBox::staticMetaObject, "StandardButton", selectedButton);
+ if (buttons & selectedButton) {
+ qCDebug(QInstaller::lcInstallerInstallLog).nospace() << "Automatic answer for "<< identifier
+ << ": " << buttonName;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog()).nospace() << "Invalid answer " << buttonName
+ << "for " << identifier << ". Using default value " << enumToString(QMessageBox::staticMetaObject,
+ "StandardButton", defaultButton) << " instead.";
+ selectedButton = defaultButton;
+ }
+ return selectedButton;
+ }
- if (m_automaticAnswers.contains(identifier))
- return m_automaticAnswers.value(identifier);
+ if (qobject_cast<QApplication*> (qApp) == nullptr) {
+ QMessageBox::StandardButton button = defaultButton;
+ bool showAnswerInLog = true;
+ if (LoggingHandler::instance().outputRedirected() && (m_defaultAction == AskUser))
+ setDefaultAction(Reject);
+
+ if (m_defaultAction == AskUser) {
+ if (!availableAnswers.isEmpty()) {
+ while (!askAnswerFromUser(button, buttons)) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Invalid answer, please retry";
+ }
+ }
+ showAnswerInLog = false;
+ } else if (m_defaultAction != Default) {
+ button = autoReply(buttons);
+ }
+ if (showAnswerInLog) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Answer:"
+ << enumToString(QMessageBox::staticMetaObject, "StandardButton", button);
+ }
+ return button;
+ }
if (m_defaultAction == AskUser) {
switch (messageType) {
@@ -428,3 +462,43 @@ QMessageBox::StandardButton MessageBoxHandler::showMessageBox(MessageType messag
Q_ASSERT_X(false, Q_FUNC_INFO, "Something went really wrong.");
return defaultButton;
}
+
+bool MessageBoxHandler::askAnswerFromUser(QMessageBox::StandardButton &selectedButton,
+ QMessageBox::StandardButtons &availableButtons) const
+{
+ QTextStream stream(stdin);
+
+ QString answer;
+ stream.readLineInto(&answer);
+
+ const QMetaObject metaObject = QMessageBox::staticMetaObject;
+ int enumIndex = metaObject.indexOfEnumerator("StandardButton");
+ if (enumIndex != -1) {
+ QMetaEnum en = metaObject.enumerator(enumIndex);
+ answer.prepend(QLatin1String("QMessageBox::"));
+
+ bool ok = false;
+ int button = en.keyToValue(answer.toLocal8Bit().data(), &ok);
+ if (ok) {
+ selectedButton = static_cast<QMessageBox::Button>(button);
+ if (availableButtons & selectedButton)
+ return true;
+ }
+ }
+ return false;
+}
+
+QString MessageBoxHandler::availableAnswerOptions(const QFlags<QMessageBox::StandardButton> &flags) const
+{
+ QString answers = QString();
+ QMetaObject metaObject = QMessageBox::staticMetaObject;
+ int enumIndex = metaObject.indexOfEnumerator("StandardButton");
+ if (enumIndex != -1) {
+ QMetaEnum en = metaObject.enumerator(enumIndex);
+ // If valueToKey returned a value, we don't have a question
+ // as there was only one value in the flags.
+ if (QLatin1String(en.valueToKey(quint64(flags))).isEmpty())
+ answers = QLatin1String(en.valueToKeys(quint64(flags)));
+ }
+ return answers;
+}
diff --git a/src/libs/installer/messageboxhandler.h b/src/libs/installer/messageboxhandler.h
index b31c5dbec..54945ed5a 100644
--- a/src/libs/installer/messageboxhandler.h
+++ b/src/libs/installer/messageboxhandler.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -45,7 +45,8 @@ public:
enum DefaultAction {
AskUser,
Accept,
- Reject
+ Reject,
+ Default
};
enum MessageType{
@@ -79,16 +80,16 @@ public:
QMessageBox::StandardButton button = QMessageBox::NoButton);
Q_INVOKABLE int critical(const QString &identifier, const QString &title, const QString &text,
- int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton) const;
+ int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton);
Q_INVOKABLE int information(const QString &identifier, const QString &title, const QString &text,
- int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton) const;
+ int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton);
Q_INVOKABLE int question(const QString &identifier, const QString &title, const QString &text,
- int buttons = QMessageBox::Yes | QMessageBox::No, int button = QMessageBox::NoButton) const;
+ int buttons = QMessageBox::Yes | QMessageBox::No, int button = QMessageBox::NoButton);
Q_INVOKABLE int warning(const QString &identifier, const QString &title, const QString &text,
- int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton) const;
+ int buttons = QMessageBox::Ok, int button = QMessageBox::NoButton);
static QList<QMessageBox::Button> orderedButtons();
@@ -105,7 +106,11 @@ private:
QMessageBox::StandardButton showMessageBox(MessageType messageType, QWidget *parent,
const QString &identifier, const QString &title, const QString &text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
- QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) const;
+ const QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
+ bool askAnswerFromUser(QMessageBox::StandardButton &selectedButton,
+ QMessageBox::StandardButtons &availableButtons) const;
+ QString availableAnswerOptions(const QFlags<QMessageBox::StandardButton> &flags) const;
+
private:
static MessageBoxHandler *m_instance;
diff --git a/src/libs/installer/metadata.cpp b/src/libs/installer/metadata.cpp
new file mode 100644
index 000000000..2eccb020e
--- /dev/null
+++ b/src/libs/installer/metadata.cpp
@@ -0,0 +1,410 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "metadata.h"
+
+#include "constants.h"
+#include "globals.h"
+#include "metadatajob.h"
+
+#include <QCryptographicHash>
+#include <QDir>
+#include <QDomDocument>
+#include <QFile>
+#include <QByteArrayMatcher>
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Metadata
+ \brief The Metadata class represents fetched metadata from a repository.
+*/
+
+
+/*!
+ \internal
+*/
+static bool verifyFileIntegrityFromElement(const QDomElement &element, const QString &childNodeName,
+ const QString &attribute, const QString &metaDirectory, bool testChecksum)
+{
+ const QDomNodeList nodes = element.childNodes();
+ for (int i = 0; i < nodes.count(); ++i) {
+ const QDomNode node = nodes.at(i);
+ if (node.nodeName() != childNodeName)
+ continue;
+
+ const QDir dir(metaDirectory);
+ const QString filename = attribute.isEmpty()
+ ? node.toElement().text()
+ : node.toElement().attribute(attribute);
+
+ if (filename.isEmpty())
+ continue;
+
+ QFile file(dir.absolutePath() + QDir::separator() + filename);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName()
+ << "for reading:" << file.errorString();
+ return false;
+ }
+
+ if (!testChecksum)
+ continue;
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray checksum = hash.result().toHex();
+ if (!QFileInfo::exists(dir.absolutePath() + QDir::separator()
+ + QString::fromLatin1(checksum) + QLatin1String(".sha1"))) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Unexpected checksum for file" << file.fileName();
+ return false;
+ }
+ }
+ return true;
+}
+
+/*!
+ Constructs a new metadata object.
+*/
+Metadata::Metadata()
+ : CacheableItem()
+ , m_fromDefaultRepository(false)
+{
+}
+
+/*!
+ Constructs a new metadata object with a \a path.
+*/
+Metadata::Metadata(const QString &path)
+ : CacheableItem(path)
+ , m_fromDefaultRepository(false)
+{
+}
+
+/*!
+ Returns the checksum of this metadata which is the checksum of the Updates.xml file.
+ The checksum value is stored to memory after first read, so a single object should
+ not be reused for referring other metadata.
+*/
+QByteArray Metadata::checksum() const
+{
+ if (!m_checksum.isEmpty())
+ return m_checksum;
+
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly))
+ return QByteArray();
+
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&updateFile);
+ m_checksum = hash.result().toHex();
+
+ return m_checksum;
+}
+
+/*!
+ Sets the checksum of this metadata to \a checksum. Calling this function
+ will omit calculating the checksum from the update file when retrieving
+ the checksum with \l{checksum()} for the first time.
+*/
+void Metadata::setChecksum(const QByteArray &checksum)
+{
+ m_checksum = checksum;
+}
+
+/*!
+ Returns the root of the document tree representing the \c Updates.xml
+ document of this metadata. Returns an empty \c QDomDocument in case
+ of failure to reading the file.
+*/
+QDomDocument Metadata::updatesDocument() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return QDomDocument();
+ }
+
+ QDomDocument doc;
+ QString errorString;
+ if (!doc.setContent(&updateFile, &errorString)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot set document content:" << errorString;
+ return QDomDocument();
+ }
+
+ return doc;
+}
+
+/*!
+ Returns \c true if the \c Updates.xml document of this metadata exists, and that all
+ meta files referenced in the document exist. If the \c Updates.xml contains a \c Checksum
+ element with a value of \c true, the integrity of the files is also verified.
+
+ Returns \c false otherwise.
+*/
+bool Metadata::isValid() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return false;
+ }
+
+ return verifyMetaFiles(&updateFile);
+}
+
+/*!
+ Returns \c true if this metadata is active, \c false otherwise. Metadata is
+ considered active if it is currently associated with a valid repository.
+*/
+bool Metadata::isActive() const
+{
+ return m_repository.isValid();
+}
+
+/*!
+ Checks whether this metadata object obsoletes the \a other metadata. The other metadata
+ is considered obsolete if it is not currently associated with any repository, and the
+ URL of the calling metadata matches the last known URL of the other metadata. Returns
+ \c true if the current metadata obsoletes the other, \c false otherwise.
+*/
+bool Metadata::obsoletes(CacheableItem *other)
+{
+ if (Metadata *meta = dynamic_cast<Metadata *>(other)) {
+ // If the current metadata is not in use it should not replace anything.
+ if (!isActive())
+ return false;
+
+ // If the other metadata is in use it is not obsolete.
+ if (meta->isActive())
+ return false;
+
+ // Current metadata has the same persistent url as other metadata, other is obsolete.
+ if (persistentRepositoryPath() == meta->persistentRepositoryPath())
+ return true;
+
+ // The refreshed url of the current metadata matches the persistent url of other
+ // metadata, other is obsolete.
+ if (m_repository.url().path(QUrl::FullyEncoded) == meta->persistentRepositoryPath())
+ return true;
+ }
+ return false;
+}
+
+/*!
+ Returns the repository object set for this metadata. This is the repository
+ the metadata is currently associated with, which may be different from the
+ repository where it was originally fetched.
+*/
+Repository Metadata::repository() const
+{
+ return m_repository;
+}
+
+/*!
+ Sets the \a repository object of this metadata. If a repository
+ is already set, the new one will override the previous one. The metadata becomes
+ associated with the set repository even if it was fetched from another one.
+*/
+void Metadata::setRepository(const Repository &repository)
+{
+ m_repository = repository;
+}
+
+/*!
+ Returns \c true if this metadata is available from a default repository,
+ which means a repository without category, \c false otherwise.
+*/
+bool Metadata::isAvailableFromDefaultRepository() const
+{
+ return m_fromDefaultRepository;
+}
+
+/*!
+ Sets the metadata available from a default repository based on the value
+ of \a defaultRepository. This is not mutually exclusive from a metadata
+ that has repository categories set.
+*/
+void Metadata::setAvailableFromDefaultRepository(bool defaultRepository)
+{
+ m_fromDefaultRepository = defaultRepository;
+}
+
+/*!
+ Sets the repository path of this metadata from \a url, without the protocol or hostname.
+ Unlike \l{setRepository()} this value is saved to disk, which allows retrieving the
+ repository path of the metadata on later runs.
+*/
+void Metadata::setPersistentRepositoryPath(const QUrl &url)
+{
+ const QString newPath = url.path(QUrl::FullyEncoded).trimmed();
+ if (m_persistentRepositoryPath == newPath)
+ return;
+
+ QFile file(path() + QLatin1String("/repository.txt"));
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName() << "for writing:" << file.errorString();
+ return;
+ }
+ QTextStream out(&file);
+ out << newPath;
+
+ m_persistentRepositoryPath = newPath;
+}
+
+/*!
+ Returns the persistent repository path of the metadata.
+*/
+QString Metadata::persistentRepositoryPath()
+{
+ if (!m_persistentRepositoryPath.isEmpty())
+ return m_persistentRepositoryPath;
+
+ QFile file(path() + QLatin1String("/repository.txt"));
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << file.fileName() << "for reading:" << file.errorString();
+ return QString();
+ }
+ m_persistentRepositoryPath = QString::fromLatin1(file.readAll()).trimmed();
+ return m_persistentRepositoryPath;
+}
+
+/*!
+ Returns true if the updates document of this metadata contains the repository
+ update element, which can include actions to \c add, \c remove, and \c replace
+ repositories.
+
+ \note This function does not check that the repository updates are actually
+ valid, only that the updates document contains the \c RepositoryUpdate element.
+*/
+bool Metadata::containsRepositoryUpdates() const
+{
+ QFile updateFile(path() + QLatin1String("/Updates.xml"));
+ if (!updateFile.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot open" << updateFile.fileName()
+ << "for reading:" << updateFile.errorString();
+ return false;
+ }
+
+ static const auto matcher = qMakeStaticByteArrayMatcher("<RepositoryUpdate>");
+ while (!updateFile.atEnd()) {
+ const QByteArray line = updateFile.readLine().simplified();
+ if (matcher.indexIn(line) != -1)
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Verifies that the files referenced in \a updateFile document exist
+ on disk. If the document contains a \c Checksum element with a value
+ of \c true, the integrity of the files is also verified.
+
+ Returns \c true if the meta files are valid, \c false otherwise.
+*/
+bool Metadata::verifyMetaFiles(QFile *updateFile) const
+{
+ QDomDocument doc;
+ QString errorString;
+ if (!doc.setContent(updateFile, &errorString)) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "Cannot set document content:" << errorString;
+ return false;
+ }
+
+ const QDomElement rootElement = doc.documentElement();
+ const QDomNodeList childNodes = rootElement.childNodes();
+
+ bool testChecksum = true;
+ const QDomElement checksumElement = rootElement.firstChildElement(QLatin1String("Checksum"));
+ if (!checksumElement.isNull())
+ testChecksum = (checksumElement.text().toLower() == scTrue);
+
+ for (int i = 0; i < childNodes.count(); ++i) {
+ const QDomElement element = childNodes.at(i).toElement();
+ if (element.isNull() || element.tagName() != QLatin1String("PackageUpdate"))
+ continue;
+
+ const QDomNodeList c2 = element.childNodes();
+ QString packageName;
+ QString unused1;
+ QString unused2;
+
+ // Only need the package name, so values for "online" and "testCheckSum" do not matter
+ if (!MetadataJob::parsePackageUpdate(c2, packageName, unused1, unused2, true, true))
+ continue; // nothing to check for this package
+
+ const QString packagePath = QString::fromLatin1("%1/%2/").arg(path(), packageName);
+ for (auto &metaTagName : scMetaElements) {
+ const QDomElement metaElement = element.firstChildElement(metaTagName);
+ if (metaElement.isNull())
+ continue;
+
+ if (metaElement.tagName() == QLatin1String("Licenses")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("License"),
+ QLatin1String("file"), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("UserInterfaces")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("UserInterface"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Translations")) {
+ if (!verifyFileIntegrityFromElement(metaElement, QLatin1String("Translation"),
+ QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else if (metaElement.tagName() == QLatin1String("Script")) {
+ if (!verifyFileIntegrityFromElement(metaElement.parentNode().toElement(),
+ QLatin1String("Script"), QString(), packagePath, testChecksum)) {
+ return false;
+ }
+ } else {
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown meta element.");
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/metadata.h b/src/libs/installer/metadata.h
new file mode 100644
index 000000000..c7e4e857c
--- /dev/null
+++ b/src/libs/installer/metadata.h
@@ -0,0 +1,81 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef METADATA_H
+#define METADATA_H
+
+#include "installer_global.h"
+#include "genericdatacache.h"
+#include "repository.h"
+
+#include <QDomDocument>
+
+class QFile;
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT Metadata : public CacheableItem
+{
+public:
+ Metadata();
+ explicit Metadata(const QString &path);
+ ~Metadata() {}
+
+ QByteArray checksum() const override;
+ void setChecksum(const QByteArray &checksum);
+ QDomDocument updatesDocument() const;
+
+ bool isValid() const override;
+ bool isActive() const override;
+ bool obsoletes(CacheableItem *other) override;
+
+ Repository repository() const;
+ void setRepository(const Repository &repository);
+
+ bool isAvailableFromDefaultRepository() const;
+ void setAvailableFromDefaultRepository(bool defaultRepository);
+
+ void setPersistentRepositoryPath(const QUrl &url);
+ QString persistentRepositoryPath();
+
+ bool containsRepositoryUpdates() const;
+
+private:
+ bool verifyMetaFiles(QFile *updateFile) const;
+
+private:
+ Repository m_repository;
+ QString m_persistentRepositoryPath;
+ mutable QByteArray m_checksum;
+
+ bool m_fromDefaultRepository;
+};
+
+} // namespace QInstaller
+
+#endif // METADATA_H
diff --git a/src/libs/installer/metadatacache.cpp b/src/libs/installer/metadatacache.cpp
new file mode 100644
index 000000000..744e455f4
--- /dev/null
+++ b/src/libs/installer/metadatacache.cpp
@@ -0,0 +1,67 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "metadatacache.h"
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::MetadataCache
+ \brief The MetadataCache is a class for a checksum based storage of \c Metadata objects on disk.
+
+ MetadataCache manages a cache storage for a set \l{path()}, which contains
+ a subdirectory for each registered \c Metadata item. The cache has a manifest file in
+ its root directory, which lists the version and type of the cache, and all its items.
+ The file is updated automatically when the metadata cache object is destructed, or
+ it can be updated periodically by calling \l{sync()}.
+*/
+
+/*!
+ Constructs a new empty cache. The cache is invalid until set with a
+ path and initialized.
+*/
+MetadataCache::MetadataCache()
+ : GenericDataCache<Metadata>()
+{
+ setType(QLatin1String("Metadata"));
+ setVersion(QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)));
+}
+
+/*!
+ Constructs a cache to \a path. The cache is initialized automatically.
+*/
+MetadataCache::MetadataCache(const QString &path)
+ : GenericDataCache(path, QLatin1String("Metadata"), QLatin1String(QUOTE(IFW_CACHE_FORMAT_VERSION)))
+{
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/metadatacache.h b/src/libs/installer/metadatacache.h
new file mode 100644
index 000000000..804d1b6db
--- /dev/null
+++ b/src/libs/installer/metadatacache.h
@@ -0,0 +1,46 @@
+/**************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef METADATACACHE_H
+#define METADATACACHE_H
+
+#include "genericdatacache.h"
+#include "metadata.h"
+
+namespace QInstaller {
+
+class MetadataCache : public GenericDataCache<Metadata>
+{
+public:
+ MetadataCache();
+ explicit MetadataCache(const QString &path);
+};
+
+} // namespace QInstaller
+
+#endif // METADATACACHE_H
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index 7d7b46e72..1bed304c6 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,14 +35,41 @@
#include "serverauthenticationdialog.h"
#include "settings.h"
#include "testrepository.h"
+#include "globals.h"
#include <QTemporaryDir>
+#include <QtConcurrent>
#include <QtMath>
-
-const QStringList metaElements = {QLatin1String("Script"), QLatin1String("Licenses"), QLatin1String("UserInterfaces"), QLatin1String("Translations")};
+#include <QRandomGenerator>
+#include <QApplication>
namespace QInstaller {
+/*!
+ \enum QInstaller::DownloadType
+
+ \value All
+ \value CompressedPackage
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::MetadataJob
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::UnzipArchiveTask
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::UnzipArchiveException
+ \internal
+*/
+
static QUrl resolveUrl(const FileTaskResult &result, const QString &url)
{
QUrl u(url);
@@ -54,20 +81,35 @@ static QUrl resolveUrl(const FileTaskResult &result, const QString &url)
MetadataJob::MetadataJob(QObject *parent)
: Job(parent)
, m_core(nullptr)
- , m_addCompressedPackages(false)
+ , m_downloadType(DownloadType::All)
, m_downloadableChunkSize(1000)
, m_taskNumber(0)
+ , m_defaultRepositoriesFetched(false)
{
+ QByteArray downloadableChunkSize = qgetenv("IFW_METADATA_SIZE");
+ if (!downloadableChunkSize.isEmpty()) {
+ int chunkSize = QString::fromLocal8Bit(downloadableChunkSize).toInt();
+ if (chunkSize > 0)
+ m_downloadableChunkSize = chunkSize;
+ }
+
setCapabilities(Cancelable);
connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished);
connect(&m_metadataTask, &QFutureWatcherBase::finished, this, &MetadataJob::metadataTaskFinished);
connect(&m_metadataTask, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged);
+ connect(&m_updateCacheTask, &QFutureWatcherBase::finished, this, &MetadataJob::updateCacheTaskFinished);
}
MetadataJob::~MetadataJob()
{
resetCompressedFetch();
reset();
+
+ if (!m_core)
+ return;
+
+ if (m_metaFromCache.isValid() && !m_core->settings().persistentLocalCache())
+ m_metaFromCache.clear();
}
/*
@@ -76,26 +118,101 @@ MetadataJob::~MetadataJob()
* repositories which might not be currently selected.
*/
-QList<Metadata> MetadataJob::metadata() const
+QList<Metadata *> MetadataJob::metadata() const
{
- QList<Metadata> metadata = m_metaFromDefaultRepositories.values();
- foreach (RepositoryCategory repositoryCategory, m_core->settings().repositoryCategories()) {
- if (m_core->isUpdater() || (repositoryCategory.isEnabled() && m_fetchedArchive.contains(repositoryCategory.displayname()))) {
- QList<ArchiveMetadata> archiveMetaList = m_fetchedArchive.values(repositoryCategory.displayname());
- foreach (ArchiveMetadata archiveMeta, archiveMetaList) {
- metadata.append(archiveMeta.metaData);
- }
+ const QSet<RepositoryCategory> categories = m_core->settings().repositoryCategories();
+ QHash<RepositoryCategory, QSet<Repository>> repositoryHash;
+ // Create hash of categorized repositories to avoid constructing
+ // excess temp objects when filtering below.
+ for (const RepositoryCategory &category : categories)
+ repositoryHash.insert(category, category.repositories());
+
+ QList<Metadata *> metadata = m_metaFromCache.items();
+ // Filter cache items not associated with current repositories and categories
+ QtConcurrent::blockingFilter(metadata, [&](const Metadata *item) {
+ if (!item->isActive())
+ return false;
+
+ // No need to check if the repository is enabled here. Changing the network
+ // settings resets the cache and we don't fetch the disabled repositories,
+ // so the cached items stay inactive.
+
+ if (item->isAvailableFromDefaultRepository())
+ return true;
+
+ QHash<RepositoryCategory, QSet<Repository>>::const_iterator it;
+ for (it = repositoryHash.constBegin(); it != repositoryHash.constEnd(); ++it) {
+ if (!it.key().isEnabled())
+ continue; // Let's try the next one
+
+ if (it->contains(item->repository()))
+ return true;
}
- }
+ return false;
+ });
+
return metadata;
}
-Repository MetadataJob::repositoryForDirectory(const QString &directory) const
+/*
+ Returns a repository object from the cache item matching \a directory. If the
+ \a directory does not belong to the cache, an empty repository is returned.
+*/
+Repository MetadataJob::repositoryForCacheDirectory(const QString &directory) const
{
- if (m_metaFromDefaultRepositories.contains(directory))
- return m_metaFromDefaultRepositories.value(directory).repository;
- else
- return m_metaFromArchive.value(directory).repository;
+ QDir dir(directory);
+ if (!QDir::fromNativeSeparators(dir.path())
+ .startsWith(QDir::fromNativeSeparators(m_metaFromCache.path()))) {
+ return Repository();
+ }
+ const QString dirName = dir.dirName();
+ Metadata *cachedMeta = m_metaFromCache.itemByChecksum(dirName.toUtf8());
+ if (cachedMeta)
+ return cachedMeta->repository();
+
+ return Repository();
+}
+
+bool MetadataJob::resetCache(bool init)
+{
+ // Need the path from current settings
+ if (!m_core) {
+ qCWarning(lcInstallerInstallLog) << "Cannot reset metadata cache: "
+ "missing package manager core engine.";
+ return false;
+ }
+
+ if (m_metaFromCache.isValid() && !m_core->settings().persistentLocalCache())
+ m_metaFromCache.clear();
+
+ m_metaFromCache.setPath(m_core->settings().localCachePath());
+
+ if (!init)
+ return true;
+
+ const bool success = m_metaFromCache.initialize();
+ if (success) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Using metadata cache from"
+ << m_metaFromCache.path();
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Found"
+ << m_metaFromCache.items().count() << "cached items.";
+ }
+ return success;
+}
+
+bool MetadataJob::clearCache()
+{
+ if (m_metaFromCache.clear())
+ return true;
+
+ setError(JobError::CacheError);
+ setErrorString(m_metaFromCache.errorString());
+ return false;
+}
+
+bool MetadataJob::isValidCache() const
+{
+ return m_metaFromCache.isValid();
}
// -- private slots
@@ -104,43 +221,91 @@ void MetadataJob::doStart()
{
setError(Job::NoError);
setErrorString(QString());
+ m_metadataResult.clear();
+ setProgressTotalAmount(100);
+
if (!m_core) {
emitFinishedWithError(Job::Canceled, tr("Missing package manager core engine."));
return; // We can't do anything here without core, so avoid tons of !m_core checks.
}
+ if (!m_metaFromCache.isValid() && !resetCache(true)) {
+ emitFinishedWithError(JobError::CacheError, m_metaFromCache.errorString());
+ return;
+ }
+
const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance();
- if (!m_addCompressedPackages) {
- emit infoMessage(this, tr("Preparing meta information download..."));
+ if (m_downloadType != DownloadType::CompressedPackage) {
+ emit infoMessage(this, tr("Fetching latest update information..."));
const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly();
- if (onlineInstaller || m_core->isMaintainer()) {
- QList<FileTaskItem> items;
- QSet<Repository> repositories = getRepositories();
+ const QSet<Repository> repositories = getRepositories();
+
+ if (onlineInstaller || m_core->isMaintainer()
+ || (m_core->settings().allowRepositoriesForOfflineInstaller() && !repositories.isEmpty())) {
+ static const QString updateFilePath(QLatin1Char('/') + scUpdatesXML + QLatin1Char('?'));
+ static const QString randomQueryString = QString::number(QRandomGenerator::global()->generate());
+
+ quint64 cachedCount = 0;
+ setProgressTotalAmount(0); // Show only busy indicator during this loop as we have no progress to measure
foreach (const Repository &repo, repositories) {
+ // For not blocking the UI
+ qApp->processEvents();
+
if (repo.isEnabled() &&
productKeyCheck->isValidRepository(repo)) {
QAuthenticator authenticator;
authenticator.setUser(repo.username());
authenticator.setPassword(repo.password());
- if (!repo.isCompressed()) {
- QString url = repo.url().toString() + QLatin1String("/Updates.xml?");
- if (!m_core->value(scUrlQueryString).isEmpty())
- url += m_core->value(scUrlQueryString) + QLatin1Char('&');
-
- // also append a random string to avoid proxy caches
- FileTaskItem item(url.append(QString::number(qrand() * qrand())));
- item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
- items.append(item);
+ if (repo.isCompressed())
+ continue;
+
+ QString url;
+ url = repo.url().toString() + updateFilePath;
+ if (!m_core->value(scUrlQueryString).isEmpty())
+ url += m_core->value(scUrlQueryString) + QLatin1Char('&');
+ // also append a random string to avoid proxy caches
+ url.append(randomQueryString);
+
+ // Check if we can skip downloading already cached repositories
+ const Status foundStatus = findCachedUpdatesFile(repo, url);
+ if (foundStatus == XmlDownloadSuccess) {
+ // Found existing Updates.xml
+ ++cachedCount;
+ continue;
+ } else if (foundStatus == XmlDownloadRetry) {
+ // Repositories changed, restart with the new repositories
+ QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
+ return;
}
- else {
- qDebug() << "Trying to parse compressed repo as normal repository."\
- "Check repository syntax.";
+
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
}
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+ item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
+ item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
+ m_updatesXmlItems.append(item);
}
}
- if (items.count() > 0) {
- startXMLTask(items);
+ setProgressTotalAmount(100);
+ const quint64 totalCount = repositories.count();
+ if (cachedCount > 0) {
+ qCDebug(lcInstallerInstallLog).nospace() << "Loaded from cache "
+ << cachedCount << "/" << totalCount << ". Downloading remaining "
+ << m_updatesXmlItems.count() << "/" << totalCount <<".";
+ } else {
+ qCDebug(lcInstallerInstallLog).nospace() <<"Downloading " << m_updatesXmlItems.count()
+ << " items to cache.";
+ }
+ if (m_updatesXmlItems.count() > 0) {
+ double taskCount = m_updatesXmlItems.length()/static_cast<double>(m_downloadableChunkSize);
+ m_totalTaskCount = qCeil(taskCount);
+ m_taskNumber = 0;
+ startXMLTask();
} else {
emitFinished();
}
@@ -179,24 +344,36 @@ void MetadataJob::doStart()
}
}
-void MetadataJob::startXMLTask(const QList<FileTaskItem> items)
+bool MetadataJob::startXMLTask()
{
- DownloadFileTask *const xmlTask = new DownloadFileTask(items);
- xmlTask->setProxyFactory(m_core->proxyFactory());
- m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
+ int chunkSize = qMin(m_updatesXmlItems.length(), m_downloadableChunkSize);
+ QList<FileTaskItem> tempPackages = m_updatesXmlItems.mid(0, chunkSize);
+ m_updatesXmlItems = m_updatesXmlItems.mid(chunkSize, m_updatesXmlItems.length());
+ if (tempPackages.length() > 0) {
+ DownloadFileTask *const xmlTask = new DownloadFileTask(tempPackages);
+ xmlTask->setProxyFactory(m_core->proxyFactory());
+ connect(&m_xmlTask, &QFutureWatcher<FileTaskResult>::progressValueChanged, this,
+ &MetadataJob::progressChanged);
+ m_xmlTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, xmlTask));
+
+ setInfoMessage(tr("Retrieving information from remote repositories..."));
+ return true;
+ }
+ return false;
}
void MetadataJob::doCancel()
{
reset();
- emitFinishedWithError(Job::Canceled, tr("Meta data download canceled."));
+ resetCache();
+ emitFinishedWithError(Job::Canceled, tr("Metadata download canceled."));
}
void MetadataJob::startUnzipRepositoryTask(const Repository &repo)
{
QTemporaryDir tempRepoDir(QDir::tempPath() + QLatin1String("/compressedRepo-XXXXXX"));
if (!tempRepoDir.isValid()) {
- qDebug() << "Cannot create unique temporary directory.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
return;
}
tempRepoDir.setAutoRemove(false);
@@ -212,6 +389,27 @@ void MetadataJob::startUnzipRepositoryTask(const Repository &repo)
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
+void MetadataJob::startUpdateCacheTask()
+{
+ const int toRegisterCount = m_fetchedMetadata.count();
+ if (toRegisterCount > 0)
+ emit infoMessage(this, tr("Updating local cache with %n new items...",
+ nullptr, toRegisterCount));
+
+ UpdateCacheTask *task = new UpdateCacheTask(m_metaFromCache, m_fetchedMetadata);
+ m_updateCacheTask.setFuture(QtConcurrent::run(&UpdateCacheTask::doTask, task));
+}
+
+/*
+ Resets the repository information from all cache items, which
+ makes them inactive until associated with new repositories.
+*/
+void MetadataJob::resetCacheRepositories()
+{
+ for (auto *metaToReset : m_metaFromCache.items())
+ metaToReset->setRepository(Repository());
+}
+
void MetadataJob::unzipRepositoryTaskFinished()
{
QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender());
@@ -238,13 +436,19 @@ void MetadataJob::unzipRepositoryTaskFinished()
error = testJob.error();
errorString = testJob.errorString();
if (error == Job::NoError) {
- FileTaskItem item(url);
+ QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
+ if (!tmp.isValid()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot create unique temporary directory.";
+ continue;
+ }
+ tmp.setAutoRemove(false);
+ m_tempDirDeleter.add(tmp.path());
+ FileTaskItem item(url, tmp.path() + QLatin1String("/Updates.xml"));
+
item.insert(TaskRole::UserRole, QVariant::fromValue(repo));
- m_unzipRepositoryitems.append(item);
+ m_updatesXmlItems.append(item);
} else {
//Repository is not valid, remove it
- Repository repository;
- repository.setUrl(QUrl(task->archive()));
Settings &s = m_core->settings();
QSet<Repository> temporaries = s.temporaryRepositories();
foreach (Repository repository, temporaries) {
@@ -262,8 +466,8 @@ void MetadataJob::unzipRepositoryTaskFinished()
//One can specify many zipped repository items at once. As the repositories are
//unzipped one by one, we collect here all items before parsing xml files from those.
- if (m_unzipRepositoryitems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) {
- startXMLTask(m_unzipRepositoryitems);
+ if (m_updatesXmlItems.count() > 0 && m_unzipRepositoryTasks.isEmpty()) {
+ startXMLTask();
} else {
if (error != Job::NoError) {
emitFinishedWithError(QInstaller::DownloadError, errorString);
@@ -287,19 +491,34 @@ void MetadataJob::xmlTaskFinished()
Status status = XmlDownloadFailure;
try {
m_xmlTask.waitForFinished();
- status = parseUpdatesXml(m_xmlTask.future().results());
+ m_updatesXmlResult.append(m_xmlTask.future().results());
+ if (!startXMLTask()) {
+ status = parseUpdatesXml(m_updatesXmlResult);
+ m_updatesXmlResult.clear();
+ } else {
+ return;
+ }
} catch (const AuthenticationRequiredException &e) {
if (e.type() == AuthenticationRequiredException::Type::Proxy) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << e.message();
+ QString username;
+ QString password;
const QNetworkProxy proxy = e.proxy();
- ProxyCredentialsDialog proxyCredentials(proxy);
- qDebug().noquote() << e.message();
-
- if (proxyCredentials.exec() == QDialog::Accepted) {
- qDebug() << "Retrying with new credentials ...";
+ if (m_core->isCommandLineInstance()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << QString::fromLatin1("The proxy %1:%2 requires a username and password").arg(proxy.hostName(), proxy.port());
+ askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: "));
+ } else {
+ ProxyCredentialsDialog proxyCredentials(proxy);
+ if (proxyCredentials.exec() == QDialog::Accepted) {
+ username = proxyCredentials.userName();
+ password = proxyCredentials.password();
+ }
+ }
+ if (!username.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Retrying with new credentials ...";
PackageManagerProxyFactory *factory = m_core->proxyFactory();
- factory->setProxyCredentials(proxy, proxyCredentials.userName(),
- proxyCredentials.password());
+ factory->setProxyCredentials(proxy, username, password);
m_core->setProxyFactory(factory);
status = XmlDownloadRetry;
} else {
@@ -307,14 +526,26 @@ void MetadataJob::xmlTaskFinished()
emitFinishedWithError(QInstaller::DownloadError, tr("Missing proxy credentials."));
}
} else if (e.type() == AuthenticationRequiredException::Type::Server) {
- qDebug().noquote() << e.message();
- ServerAuthenticationDialog dlg(e.message(), e.taskItem());
- if (dlg.exec() == QDialog::Accepted) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << e.message();
+ QString username;
+ QString password;
+ if (m_core->isCommandLineInstance()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Server Requires Authentication";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "You need to supply a username and password to access this site.";
+ askForCredentials(&username, &password, QLatin1String("Username: "), QLatin1String("Password: "));
+ } else {
+ ServerAuthenticationDialog dlg(e.message(), e.taskItem());
+ if (dlg.exec() == QDialog::Accepted) {
+ username = dlg.user();
+ password = dlg.password();
+ }
+ }
+ if (!username.isEmpty()) {
Repository original = e.taskItem().value(TaskRole::UserRole)
.value<Repository>();
Repository replacement = original;
- replacement.setUsername(dlg.user());
- replacement.setPassword(dlg.password());
+ replacement.setUsername(username);
+ replacement.setPassword(password);
Settings &s = m_core->settings();
QSet<Repository> temporaries = s.temporaryRepositories();
@@ -323,11 +554,11 @@ void MetadataJob::xmlTaskFinished()
temporaries.insert(replacement);
s.addTemporaryRepositories(temporaries, true);
} else {
- QHash<QString, QPair<Repository, Repository> > update;
+ QMultiHash<QString, QPair<Repository, Repository> > update;
update.insert(QLatin1String("replace"), qMakePair(original, replacement));
if (s.updateRepositoryCategories(update) == Settings::UpdatesApplied)
- qDebug() << "Repository categories updated.";
+ qCDebug(QInstaller::lcDeveloperBuild) << "Repository categories updated.";
if (s.updateDefaultRepositories(update) == Settings::UpdatesApplied
|| s.updateUserRepositories(update) == Settings::UpdatesApplied) {
@@ -365,9 +596,12 @@ void MetadataJob::xmlTaskFinished()
if (status == XmlDownloadSuccess) {
if (!fetchMetaDataPackages()) {
- emitFinished();
+ // No new metadata packages to fetch, still need to update the cache
+ // for refreshed repositories.
+ startUpdateCacheTask();
}
} else if (status == XmlDownloadRetry) {
+ reset();
QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
} else {
reset();
@@ -395,10 +629,8 @@ void MetadataJob::unzipTaskFinished()
m_unzipTasks.remove(watcher);
delete watcher;
- if (m_unzipTasks.isEmpty()) {
- setProcessedAmount(100);
- emitFinished();
- }
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
}
void MetadataJob::progressChanged(int progress)
@@ -426,21 +658,33 @@ void MetadataJob::metadataTaskFinished()
.arg(item.value(TaskRole::SourceFile).toString());
if (m_core->settings().allowUnstableComponents()) {
m_shaMissmatchPackages.append(item.value(TaskRole::Name).toString());
- qWarning() << mismatchMessage;
+ qCWarning(QInstaller::lcInstallerInstallLog) << mismatchMessage;
} else {
throw QInstaller::TaskException(mismatchMessage);
}
+ QFileInfo fi(result.target());
+ QString targetPath = fi.absolutePath();
+ if (m_fetchedMetadata.contains(targetPath)) {
+ delete m_fetchedMetadata.value(targetPath);
+ m_fetchedMetadata.remove(targetPath);
+ }
+ continue;
}
UnzipArchiveTask *task = new UnzipArchiveTask(result.target(),
item.value(TaskRole::UserRole).toString());
+ task->setRemoveArchive(true);
+ task->setStoreChecksums(true);
QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
m_unzipTasks.insert(watcher, qobject_cast<QObject*> (task));
connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished);
watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task));
}
+ if (m_unzipTasks.isEmpty())
+ startUpdateCacheTask();
+
} else {
- emitFinished();
+ startUpdateCacheTask();
}
}
} catch (const TaskException &e) {
@@ -455,6 +699,25 @@ void MetadataJob::metadataTaskFinished()
}
}
+void MetadataJob::updateCacheTaskFinished()
+{
+ try {
+ m_updateCacheTask.waitForFinished();
+ } catch (const CacheTaskException &e) {
+ emitFinishedWithError(QInstaller::CacheError, e.message());
+ } catch (const QUnhandledException &e) {
+ emitFinishedWithError(QInstaller::CacheError, QLatin1String(e.what()));
+ } catch (...) {
+ emitFinishedWithError(QInstaller::CacheError, tr("Unknown exception during updating cache."));
+ }
+
+ if (error() != Job::NoError)
+ return;
+
+ setProcessedAmount(100);
+ emitFinished();
+}
+
// -- private
@@ -465,18 +728,11 @@ bool MetadataJob::fetchMetaDataPackages()
QList<FileTaskItem> tempPackages = m_packages.mid(0, chunkSize);
m_packages = m_packages.mid(chunkSize, m_packages.length());
if (tempPackages.length() > 0) {
- m_taskNumber++;
setProcessedAmount(0);
DownloadFileTask *const metadataTask = new DownloadFileTask(tempPackages);
metadataTask->setProxyFactory(m_core->proxyFactory());
m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask));
- setProgressTotalAmount(100);
- QString metaInformation;
- if (m_totalTaskCount > 1)
- metaInformation = tr("Retrieving meta information from remote repository... %1/%2 ").arg(m_taskNumber).arg(m_totalTaskCount);
- else
- metaInformation = tr("Retrieving meta information from remote repository... ");
- emit infoMessage(this, metaInformation);
+ setInfoMessage(tr("Retrieving meta information from remote repository..."));
return true;
}
return false;
@@ -485,9 +741,12 @@ bool MetadataJob::fetchMetaDataPackages()
void MetadataJob::reset()
{
m_packages.clear();
- m_metaFromDefaultRepositories.clear();
- m_metaFromArchive.clear();
- m_fetchedArchive.clear();
+ m_updatesXmlItems.clear();
+ m_defaultRepositoriesFetched = false;
+ m_fetchedCategorizedRepositories.clear();
+
+ qDeleteAll(m_fetchedMetadata);
+ m_fetchedMetadata.clear();
setError(Job::NoError);
setErrorString(QString());
@@ -495,10 +754,13 @@ void MetadataJob::reset()
try {
m_xmlTask.cancel();
+ m_xmlTask.waitForFinished();
m_metadataTask.cancel();
+ m_metadataTask.waitForFinished();
} catch (...) {}
m_tempDirDeleter.releaseAndDeleteAll();
m_metadataResult.clear();
+ m_updatesXmlResult.clear();
m_taskNumber = 0;
}
@@ -506,7 +768,6 @@ void MetadataJob::resetCompressedFetch()
{
setError(Job::NoError);
setErrorString(QString());
- m_unzipRepositoryitems.clear();
try {
foreach (QFutureWatcher<void> *const watcher, m_unzipTasks.keys()) {
@@ -538,41 +799,54 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
if (result.target().isEmpty()) {
continue;
}
- Metadata metadata;
- QTemporaryDir tmp(QDir::tempPath() + QLatin1String("/remoterepo-XXXXXX"));
- if (!tmp.isValid()) {
- qDebug() << "Cannot create unique temporary directory.";
- return XmlDownloadFailure;
- }
-
- tmp.setAutoRemove(false);
- metadata.directory = tmp.path();
- m_tempDirDeleter.add(metadata.directory);
+ QFileInfo fileInfo(result.target());
+ std::unique_ptr<Metadata> metadata(new Metadata(fileInfo.absolutePath()));
QFile file(result.target());
- if (!file.rename(metadata.directory + QLatin1String("/Updates.xml"))) {
- qDebug() << "Cannot rename target to Updates.xml:" << file.errorString();
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open Updates.xml for reading:"
+ << file.errorString();
return XmlDownloadFailure;
}
+ const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
+ const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
- if (!file.open(QIODevice::ReadOnly)) {
- qDebug() << "Cannot open Updates.xml for reading:" << file.errorString();
- return XmlDownloadFailure;
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+ const QByteArray updatesChecksum = hash.result().toHex();
+
+ if (!repository.xmlChecksum().isEmpty() && updatesChecksum != repository.xmlChecksum()) {
+ qCWarning(lcDeveloperBuild).noquote().nospace() << "The checksum for Updates.xml "
+ "file downloaded from repository:\n" << repository.url().toString() << "\ndoes not "
+ "match the expected value:\n\tActual SHA1: " << updatesChecksum << "\n\tExpected SHA1: "
+ << repository.xmlChecksum() << Qt::endl;
}
+ bool refreshed;
+ // Check if we have cached the metadata for this repository already
+ Status status = refreshCacheItem(result, updatesChecksum, &refreshed);
+ if (status != XmlDownloadSuccess)
+ return status;
+
+ if (refreshed) // Found existing metadata
+ continue;
+
+ metadata->setChecksum(updatesChecksum);
+
+ file.seek(0);
+
QString error;
QDomDocument doc;
if (!doc.setContent(&file, &error)) {
- qDebug().nospace() << "Cannot fetch a valid version of Updates.xml from repository "
- << metadata.repository.displayname() << ": " << error;
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Cannot fetch a valid version of Updates.xml from repository "
+ << metadata->repository().displayname() << ": " << error;
//If there are other repositories, try to use those
continue;
}
file.close();
- const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
- metadata.repository = item.value(TaskRole::UserRole).value<Repository>();
- const bool online = !(metadata.repository.url().scheme()).isEmpty();
+ metadata->setRepository(repository);
+ const bool online = !(metadata->repository().url().scheme()).isEmpty();
bool testCheckSum = true;
const QDomElement root = doc.documentElement();
@@ -580,167 +854,63 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
if (!checksum.isNull())
testCheckSum = (checksum.toElement().text().toLower() == scTrue);
+ // If we have top level sha1 and MetadataName elements, we have compressed
+ // all metadata inside one repository to a single 7z file. Fetch that
+ // instead of component specific meta 7z files.
+ const QDomNode sha1 = root.firstChildElement(scSHA1);
+ QDomElement metadataNameElement = root.firstChildElement(scMetadataName);
QDomNodeList children = root.childNodes();
- for (int i = 0; i < children.count(); ++i) {
- const QDomElement el = children.at(i).toElement();
- if (!el.isNull() && el.tagName() == QLatin1String("PackageUpdate")) {
- const QDomNodeList c2 = el.childNodes();
- QString packageName, packageVersion, packageHash;
- bool metaFound = false;
- for (int j = 0; j < c2.count(); ++j) {
- if (c2.at(j).toElement().tagName() == scName)
- packageName = c2.at(j).toElement().text();
- else if (c2.at(j).toElement().tagName() == scVersion)
- packageVersion = (online ? c2.at(j).toElement().text() : QString());
- else if ((c2.at(j).toElement().tagName() == QLatin1String("SHA1")) && testCheckSum)
- packageHash = c2.at(j).toElement().text();
- else {
- foreach (QString meta, metaElements) {
- if (c2.at(j).toElement().tagName() == meta) {
- metaFound = true;
- break;
- }
- }
- }
- }
-
- const QString repoUrl = metadata.repository.url().toString();
- //If script element is not found, no need to fetch metadata
- if (metaFound) {
- FileTaskItem item(QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, packageName,
- packageVersion), metadata.directory + QString::fromLatin1("/%1-%2-meta.7z")
- .arg(packageName, packageVersion));
-
- QAuthenticator authenticator;
- authenticator.setUser(metadata.repository.username());
- authenticator.setPassword(metadata.repository.password());
-
- item.insert(TaskRole::UserRole, metadata.directory);
- item.insert(TaskRole::Checksum, packageHash.toLatin1());
- item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
- item.insert(TaskRole::Name, packageName);
-
- m_packages.append(item);
- } else {
- QString fileName = metadata.directory + QLatin1Char('/') + packageName;
- QDir directory(fileName);
- if (!directory.exists()) {
- directory.mkdir(fileName);
- }
- }
- }
- }
- if (metadata.repository.categoryname().isEmpty()) {
- m_metaFromDefaultRepositories.insert(metadata.directory, metadata);
+ if (!sha1.isNull() && !metadataNameElement.isNull()) {
+ const QString repoUrl = metadata->repository().url().toString();
+ const QString metadataName = metadataNameElement.toElement().text();
+ addFileTaskItem(QString::fromLatin1("%1/%2").arg(repoUrl, metadataName),
+ metadata->path() + QString::fromLatin1("/%1").arg(metadataName),
+ metadata.get(), sha1.toElement().text(), QString());
} else {
- //Hash metadata to help checking if meta for repository is already fetched
- ArchiveMetadata archiveMetadata;
- archiveMetadata.metaData = metadata;
- m_fetchedArchive.insertMulti(metadata.repository.categoryname(), archiveMetadata);
-
- //Check if other categories have the same url (contains same metadata)
- //so we can speed up other category fetches
- foreach (RepositoryCategory category, m_core->settings().repositoryCategories()) {
- if (category.displayname() != metadata.repository.categoryname()) {
- foreach (Repository repository, category.repositories()) {
- if (repository.url() == metadata.repository.url()) {
- m_fetchedArchive.insertMulti(category.displayname(), archiveMetadata);
+ bool metaFound = false;
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if (!el.isNull() && el.tagName() == QLatin1String("PackageUpdate")) {
+ const QDomNodeList c2 = el.childNodes();
+ QString packageName, packageVersion, packageHash;
+ metaFound = parsePackageUpdate(c2, packageName, packageVersion, packageHash,
+ online, testCheckSum);
+
+ // If meta element (script, licenses, etc.) is not found, no need to fetch metadata.
+ if (metaFound) {
+ const QString repoUrl = metadata->repository().url().toString();
+ addFileTaskItem(QString::fromLatin1("%1/%2/%3meta.7z").arg(repoUrl, packageName, packageVersion),
+ metadata->path() + QString::fromLatin1("/%1-%2-meta.7z").arg(packageName, packageVersion),
+ metadata.get(), packageHash, packageName);
+ } else {
+ QString fileName = metadata->path() + QLatin1Char('/') + packageName;
+ QDir directory(fileName);
+ if (!directory.exists()) {
+ directory.mkdir(fileName);
}
}
}
}
- // Hash for faster lookups
- m_metaFromArchive.insert(metadata.directory, metadata);
}
+ // Remember the fetched metadata
+ Metadata *const metadataPtr = metadata.get();
+ const QString categoryName = metadata->repository().categoryname();
+ if (categoryName.isEmpty())
+ metadata->setAvailableFromDefaultRepository(true);
+ else
+ m_fetchedCategorizedRepositories.insert(metadataPtr->repository()); // For faster lookups
- // search for additional repositories that we might need to check
- const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
- if (repositoryUpdate.isNull())
- continue;
+ const QString metadataPath = metadata->path();
+ m_fetchedMetadata.insert(metadataPath, metadata.release());
- QHash<QString, QPair<Repository, Repository> > repositoryUpdates;
- children = repositoryUpdate.toElement().childNodes();
- for (int i = 0; i < children.count(); ++i) {
- const QDomElement el = children.at(i).toElement();
- if (!el.isNull() && el.tagName() == QLatin1String("Repository")) {
- const QString action = el.attribute(QLatin1String("action"));
- if (action == QLatin1String("add")) {
- // add a new repository to the defaults list
- Repository repository(resolveUrl(result, el.attribute(QLatin1String("url"))), true);
- repository.setUsername(el.attribute(QLatin1String("username")));
- repository.setPassword(el.attribute(QLatin1String("password")));
- repository.setDisplayName(el.attribute(QLatin1String("displayname")));
- if (ProductKeyCheck::instance()->isValidRepository(repository)) {
- repositoryUpdates.insertMulti(action, qMakePair(repository, Repository()));
- qDebug() << "Repository to add:" << repository.displayname();
- }
- } else if (action == QLatin1String("remove")) {
- // remove possible default repositories using the given server url
- Repository repository(resolveUrl(result, el.attribute(QLatin1String("url"))), true);
- repository.setDisplayName(el.attribute(QLatin1String("displayname")));
- repositoryUpdates.insertMulti(action, qMakePair(repository, Repository()));
-
- qDebug() << "Repository to remove:" << repository.displayname();
- } else if (action == QLatin1String("replace")) {
- // replace possible default repositories using the given server url
- Repository oldRepository(resolveUrl(result, el.attribute(QLatin1String("oldUrl"))), true);
- Repository newRepository(resolveUrl(result, el.attribute(QLatin1String("newUrl"))), true);
- newRepository.setUsername(el.attribute(QLatin1String("username")));
- newRepository.setPassword(el.attribute(QLatin1String("password")));
- newRepository.setDisplayName(el.attribute(QLatin1String("displayname")));
-
- if (ProductKeyCheck::instance()->isValidRepository(newRepository)) {
- // store the new repository and the one old it replaces
- repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository));
- qDebug() << "Replace repository" << oldRepository.displayname() << "with"
- << newRepository.displayname();
- }
- } else {
- qDebug() << "Invalid additional repositories action set in Updates.xml fetched "
- "from" << metadata.repository.displayname() << "line:" << el.lineNumber();
- }
- }
- }
-
- if (!repositoryUpdates.isEmpty()) {
- Settings &s = m_core->settings();
- const QSet<Repository> temporaries = s.temporaryRepositories();
- // in case the temp repository introduced something new, we only want that temporary
- if (temporaries.contains(metadata.repository)) {
- QSet<Repository> tmpRepositories;
- typedef QPair<Repository, Repository> RepositoryPair;
-
- QList<RepositoryPair> values = repositoryUpdates.values(QLatin1String("add"));
- foreach (const RepositoryPair &value, values)
- tmpRepositories.insert(value.first);
-
- values = repositoryUpdates.values(QLatin1String("replace"));
- foreach (const RepositoryPair &value, values)
- tmpRepositories.insert(value.first);
-
- tmpRepositories = tmpRepositories.subtract(temporaries);
- if (tmpRepositories.count() > 0) {
- s.addTemporaryRepositories(tmpRepositories, true);
- QFile::remove(result.target());
- m_metaFromDefaultRepositories.clear();
- return XmlDownloadRetry;
- }
- } else if (s.updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) {
- if (m_core->isMaintainer()) {
- bool gainedAdminRights = false;
- if (!m_core->directoryWritable(m_core->value(scTargetDir))) {
- m_core->gainAdminRights();
- gainedAdminRights = true;
- }
- m_core->writeMaintenanceConfigFiles();
- if (gainedAdminRights)
- m_core->dropAdminRights();
- }
- m_metaFromDefaultRepositories.clear();
- QFile::remove(result.target());
- return XmlDownloadRetry;
- }
+ // search for additional repositories that we might need to check
+ status = parseRepositoryUpdates(root, result, metadataPtr);
+ if (status == XmlDownloadRetry) {
+ // The repository update may have removed or replaced current repositories,
+ // clear repository information from cached items and refresh on next fetch run.
+ resetCacheRepositories();
+ return status;
}
}
double taskCount = m_packages.length()/static_cast<double>(m_downloadableChunkSize);
@@ -750,35 +920,263 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
return XmlDownloadSuccess;
}
+MetadataJob::Status MetadataJob::refreshCacheItem(const FileTaskResult &result,
+ const QByteArray &checksum, bool *refreshed)
+{
+ Q_ASSERT(refreshed);
+ *refreshed = false;
+
+ Metadata *cachedMetadata = m_metaFromCache.itemByChecksum(checksum);
+ if (!cachedMetadata)
+ return XmlDownloadSuccess;
+
+ const FileTaskItem item = result.value(TaskRole::TaskItem).value<FileTaskItem>();
+ const Repository repository = item.value(TaskRole::UserRole).value<Repository>();
+
+ if (cachedMetadata->isValid() && !repository.isCompressed()) {
+ // Refresh repository information to cache. Same repository may appear in multiple
+ // categories and the metadata may be available from default repositories simultaneously.
+ cachedMetadata->setRepository(repository);
+ if (!repository.categoryname().isEmpty())
+ m_fetchedCategorizedRepositories.insert(repository); // For faster lookups
+ else
+ cachedMetadata->setAvailableFromDefaultRepository(true);
+
+ // Refresh also persistent information, the url of the repository may have changed
+ // from the last fetch.
+ cachedMetadata->setPersistentRepositoryPath(repository.url());
+
+ // search for additional repositories that we might need to check
+ if (cachedMetadata->containsRepositoryUpdates()) {
+ QDomDocument doc = cachedMetadata->updatesDocument();
+ const Status status = parseRepositoryUpdates(doc.documentElement(), result, cachedMetadata);
+ if (status == XmlDownloadRetry) {
+ // The repository update may have removed or replaced current repositories,
+ // clear repository information from cached items and refresh on next fetch run.
+ resetCacheRepositories();
+ return status;
+ }
+ }
+ *refreshed = true;
+ return XmlDownloadSuccess;
+ }
+ // Missing or corrupted files, or compressed repository which takes priority
+ // over remote repository. We will re-download and uncompress
+ // the metadata. Remove broken item from the cache.
+ if (!m_metaFromCache.removeItem(checksum)) {
+ qCWarning(lcInstallerInstallLog) << m_metaFromCache.errorString();
+ return XmlDownloadFailure;
+ }
+ return XmlDownloadSuccess;
+}
+
+MetadataJob::Status MetadataJob::findCachedUpdatesFile(const Repository &repository, const QString &fileUrl)
+{
+ if (repository.xmlChecksum().isEmpty())
+ return XmlDownloadFailure;
+
+ Metadata *metadata = m_metaFromCache.itemByChecksum(repository.xmlChecksum());
+ if (!metadata)
+ return XmlDownloadFailure;
+
+ const QString targetPath = metadata->path() + QLatin1Char('/') + scUpdatesXML;
+
+ FileTaskItem cachedMetaTaskItem(fileUrl, targetPath);
+ cachedMetaTaskItem.insert(TaskRole::UserRole, QVariant::fromValue(repository));
+ const FileTaskResult cachedMetaTaskResult(targetPath, repository.xmlChecksum(), cachedMetaTaskItem, false);
+
+ bool isCached = false;
+ const Status status = refreshCacheItem(cachedMetaTaskResult, repository.xmlChecksum(), &isCached);
+ if (isCached)
+ return XmlDownloadSuccess;
+ else if (status == XmlDownloadRetry)
+ return XmlDownloadRetry;
+ else
+ return XmlDownloadFailure;
+}
+
+MetadataJob::Status MetadataJob::parseRepositoryUpdates(const QDomElement &root,
+ const FileTaskResult &result, Metadata *metadata)
+{
+ MetadataJob::Status status = XmlDownloadSuccess;
+ const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
+ if (!repositoryUpdate.isNull()) {
+ const QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates
+ = searchAdditionalRepositories(repositoryUpdate, result, *metadata);
+ if (!repositoryUpdates.isEmpty())
+ status = setAdditionalRepositories(repositoryUpdates, result, *metadata);
+ }
+ return status;
+}
+
QSet<Repository> MetadataJob::getRepositories()
{
QSet<Repository> repositories;
- //In the first run, m_metadata is empty. Get always the default repositories
- if (m_metaFromDefaultRepositories.isEmpty()) {
+ //In the first run, get always the default repositories
+ if (!m_defaultRepositoriesFetched) {
repositories = m_core->settings().repositories();
+ m_defaultRepositoriesFetched = true;
}
// Fetch repositories under archive which are selected in UI.
// If repository is already fetched, do not fetch it again.
- // In updater mode, fetch always all archive repositories to get updates
- foreach (RepositoryCategory repositoryCategory, m_core->settings().repositoryCategories()) {
- if (m_core->isUpdater() || (repositoryCategory.isEnabled())) {
- foreach (Repository repository, repositoryCategory.repositories()) {
- QHashIterator<QString, ArchiveMetadata> i(m_fetchedArchive);
- bool fetch = true;
- while (i.hasNext()) {
- i.next();
- ArchiveMetadata metaData = i.value();
- if (repository.url() == metaData.metaData.repository.url())
- fetch = false;
- }
- if (fetch)
- repositories.insert(repository);
+ for (const RepositoryCategory &repositoryCategory : m_core->settings().repositoryCategories()) {
+ if (!repositoryCategory.isEnabled())
+ continue;
+
+ for (const Repository &repository : repositoryCategory.repositories()) {
+ if (!m_fetchedCategorizedRepositories.contains(repository))
+ repositories.insert(repository);
+ }
+ }
+ return repositories;
+}
+
+void MetadataJob::addFileTaskItem(const QString &source, const QString &target, Metadata *metadata,
+ const QString &sha1, const QString &packageName)
+{
+ FileTaskItem item(source, target);
+ QAuthenticator authenticator;
+ authenticator.setUser(metadata->repository().username());
+ authenticator.setPassword(metadata->repository().password());
+
+ item.insert(TaskRole::UserRole, metadata->path());
+ item.insert(TaskRole::Checksum, sha1.toLatin1());
+ item.insert(TaskRole::Authenticator, QVariant::fromValue(authenticator));
+ item.insert(TaskRole::Name, packageName);
+ m_packages.append(item);
+}
+
+bool MetadataJob::parsePackageUpdate(const QDomNodeList &c2, QString &packageName,
+ QString &packageVersion, QString &packageHash,
+ bool online, bool testCheckSum)
+{
+ bool metaFound = false;
+ for (int j = 0; j < c2.count(); ++j) {
+ const QDomElement element = c2.at(j).toElement();
+ if (element.tagName() == scName)
+ packageName = element.text();
+ else if (element.tagName() == scVersion)
+ packageVersion = (online ? element.text() : QString());
+ else if ((element.tagName() == QLatin1String("SHA1")) && testCheckSum)
+ packageHash = element.text();
+ else {
+ foreach (QString meta, scMetaElements) {
+ if (element.tagName() == meta) {
+ metaFound = true;
+ break;
}
}
+ }
}
- return repositories;
+ return metaFound;
+}
+
+QMultiHash<QString, QPair<Repository, Repository> > MetadataJob::searchAdditionalRepositories
+ (const QDomNode &repositoryUpdate, const FileTaskResult &result, const Metadata &metadata)
+{
+ QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates;
+ const QDomNodeList children = repositoryUpdate.toElement().childNodes();
+ for (int i = 0; i < children.count(); ++i) {
+ const QDomElement el = children.at(i).toElement();
+ if (!el.isNull() && el.tagName() == QLatin1String("Repository")) {
+ const QString action = el.attribute(QLatin1String("action"));
+ if (action == QLatin1String("add")) {
+ // add a new repository to the defaults list
+ Repository repository(resolveUrl(result, el.attribute(QLatin1String("url"))), true);
+ repository.setUsername(el.attribute(QLatin1String("username")));
+ repository.setPassword(el.attribute(QLatin1String("password")));
+ repository.setDisplayName(el.attribute(QLatin1String("displayname")));
+ if (ProductKeyCheck::instance()->isValidRepository(repository)) {
+ repositoryUpdates.insert(action, qMakePair(repository, Repository()));
+ qDebug() << "Repository to add:" << repository.displayname();
+ }
+ } else if (action == QLatin1String("remove")) {
+ // remove possible default repositories using the given server url
+ Repository repository(resolveUrl(result, el.attribute(QLatin1String("url"))), true);
+ repository.setDisplayName(el.attribute(QLatin1String("displayname")));
+ repositoryUpdates.insert(action, qMakePair(repository, Repository()));
+
+ qDebug() << "Repository to remove:" << repository.displayname();
+ } else if (action == QLatin1String("replace")) {
+ // replace possible default repositories using the given server url
+ Repository oldRepository(resolveUrl(result, el.attribute(QLatin1String("oldUrl"))), true);
+ Repository newRepository(resolveUrl(result, el.attribute(QLatin1String("newUrl"))), true);
+ newRepository.setUsername(el.attribute(QLatin1String("username")));
+ newRepository.setPassword(el.attribute(QLatin1String("password")));
+ newRepository.setDisplayName(el.attribute(QLatin1String("displayname")));
+
+ if (ProductKeyCheck::instance()->isValidRepository(newRepository)) {
+ // store the new repository and the one old it replaces
+ repositoryUpdates.insert(action, qMakePair(newRepository, oldRepository));
+ qDebug() << "Replace repository" << oldRepository.displayname() << "with"
+ << newRepository.displayname();
+ }
+ } else {
+ qDebug() << "Invalid additional repositories action set in Updates.xml fetched "
+ "from" << metadata.repository().displayname() << "line:" << el.lineNumber();
+ }
+ }
+ }
+ return repositoryUpdates;
+}
+
+MetadataJob::Status MetadataJob::setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+ const FileTaskResult &result, const Metadata& metadata)
+{
+ MetadataJob::Status status = XmlDownloadSuccess;
+ Settings &s = m_core->settings();
+ const QSet<Repository> temporaries = s.temporaryRepositories();
+ // in case the temp repository introduced something new, we only want that temporary
+ if (temporaries.contains(metadata.repository())) {
+ QSet<Repository> tmpRepositories;
+ typedef QPair<Repository, Repository> RepositoryPair;
+
+ QList<RepositoryPair> values = repositoryUpdates.values(QLatin1String("add"));
+ foreach (const RepositoryPair &value, values)
+ tmpRepositories.insert(value.first);
+
+ values = repositoryUpdates.values(QLatin1String("replace"));
+ foreach (const RepositoryPair &value, values)
+ tmpRepositories.insert(value.first);
+
+ tmpRepositories = tmpRepositories.subtract(temporaries);
+ if (tmpRepositories.count() > 0) {
+ s.addTemporaryRepositories(tmpRepositories, true);
+ QFile::remove(result.target());
+ m_defaultRepositoriesFetched = false;
+ qDeleteAll(m_fetchedMetadata);
+ m_fetchedMetadata.clear();
+ status = XmlDownloadRetry;
+ }
+ } else if (s.updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) {
+ if (m_core->isMaintainer()) {
+ bool gainedAdminRights = false;
+ if (!m_core->directoryWritable(m_core->value(scTargetDir))) {
+ m_core->gainAdminRights();
+ gainedAdminRights = true;
+ }
+ m_core->writeMaintenanceConfigFiles();
+ if (gainedAdminRights)
+ m_core->dropAdminRights();
+ }
+ m_defaultRepositoriesFetched = false;
+ qDeleteAll(m_fetchedMetadata);
+ m_fetchedMetadata.clear();
+ QFile::remove(result.target());
+ status = XmlDownloadRetry;
+ }
+ return status;
}
+void MetadataJob::setInfoMessage(const QString &message)
+{
+ m_taskNumber++;
+ QString metaInformation = message;
+ if (m_totalTaskCount > 1)
+ metaInformation = QLatin1String(" %1 %2/%3 ").arg(message).arg(m_taskNumber).arg(m_totalTaskCount);
+ emit infoMessage(this, metaInformation);
+
+}
} // namespace QInstaller
diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h
index 973275de3..13ad3ea8c 100644
--- a/src/libs/installer/metadatajob.h
+++ b/src/libs/installer/metadatajob.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,24 +32,23 @@
#include "downloadfiletask.h"
#include "fileutils.h"
#include "job.h"
+#include "metadata.h"
+#include "metadatacache.h"
#include "repository.h"
#include <QFutureWatcher>
+class QDomNodeList;
+class QDomNode;
+
namespace QInstaller {
class PackageManagerCore;
-struct Metadata
-{
- QString directory;
- Repository repository;
-};
-
-struct ArchiveMetadata
+enum DownloadType
{
- QString archive;
- Metadata metaData;
+ All,
+ CompressedPackage
};
class INSTALLER_EXPORT MetadataJob : public Job
@@ -67,51 +66,77 @@ public:
explicit MetadataJob(QObject *parent = 0);
~MetadataJob();
- QList<Metadata> metadata() const;
- Repository repositoryForDirectory(const QString &directory) const;
+ QList<Metadata *> metadata() const;
+ Repository repositoryForCacheDirectory(const QString &directory) const;
void setPackageManagerCore(PackageManagerCore *core) { m_core = core; }
- void addCompressedPackages(bool addCompressPackage) { m_addCompressedPackages = addCompressPackage;}
+ void addDownloadType(DownloadType downloadType) { m_downloadType = downloadType;}
QStringList shaMismatchPackages() const { return m_shaMissmatchPackages; }
+ bool resetCache(bool init = false);
+ bool clearCache();
+ bool isValidCache() const;
+
private slots:
- void doStart();
- void doCancel();
+ void doStart() override;
+ void doCancel() override;
void xmlTaskFinished();
void unzipTaskFinished();
void metadataTaskFinished();
+ void updateCacheTaskFinished();
void progressChanged(int progress);
void setProgressTotalAmount(int maximum);
void unzipRepositoryTaskFinished();
- void startXMLTask(const QList<FileTaskItem> items);
+ bool startXMLTask();
private:
bool fetchMetaDataPackages();
void startUnzipRepositoryTask(const Repository &repo);
+ void startUpdateCacheTask();
+ void resetCacheRepositories();
void reset();
void resetCompressedFetch();
Status parseUpdatesXml(const QList<FileTaskResult> &results);
+ Status refreshCacheItem(const FileTaskResult &result, const QByteArray &checksum, bool *refreshed);
+ Status findCachedUpdatesFile(const Repository &repository, const QString &fileUrl);
+ Status parseRepositoryUpdates(const QDomElement &root, const FileTaskResult &result, Metadata *metadata);
QSet<Repository> getRepositories();
+ void addFileTaskItem(const QString &source, const QString &target, Metadata *metadata,
+ const QString &sha1, const QString &packageName);
+ static bool parsePackageUpdate(const QDomNodeList &c2, QString &packageName, QString &packageVersion,
+ QString &packageHash, bool online, bool testCheckSum);
+ QMultiHash<QString, QPair<Repository, Repository> > searchAdditionalRepositories(const QDomNode &repositoryUpdate,
+ const FileTaskResult &result, const Metadata &metadata);
+ MetadataJob::Status setAdditionalRepositories(QMultiHash<QString, QPair<Repository, Repository> > repositoryUpdates,
+ const FileTaskResult &result, const Metadata& metadata);
+ void setInfoMessage(const QString &message);
+
+private:
+ friend class Metadata;
private:
PackageManagerCore *m_core;
QList<FileTaskItem> m_packages;
- TempDirDeleter m_tempDirDeleter;
+ QList<FileTaskItem> m_updatesXmlItems;
+ TempPathDeleter m_tempDirDeleter;
QFutureWatcher<FileTaskResult> m_xmlTask;
QFutureWatcher<FileTaskResult> m_metadataTask;
+ QFutureWatcher<void> m_updateCacheTask;
QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks;
QHash<QFutureWatcher<void> *, QObject*> m_unzipRepositoryTasks;
- bool m_addCompressedPackages;
- QList<FileTaskItem> m_unzipRepositoryitems;
+ DownloadType m_downloadType;
QList<FileTaskResult> m_metadataResult;
+ QList<FileTaskResult> m_updatesXmlResult;
int m_downloadableChunkSize;
int m_taskNumber;
int m_totalTaskCount;
QStringList m_shaMissmatchPackages;
- QHash<QString, ArchiveMetadata> m_fetchedArchive;
- QHash<QString, Metadata> m_metaFromDefaultRepositories;
- QHash<QString, Metadata> m_metaFromArchive; //for faster lookups.
+ bool m_defaultRepositoriesFetched;
+
+ QSet<Repository> m_fetchedCategorizedRepositories;
+ QHash<QString, Metadata *> m_fetchedMetadata;
+ MetadataCache m_metaFromCache;
};
} // namespace QInstaller
diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h
index 9160f4cc9..837a7e9ae 100644
--- a/src/libs/installer/metadatajob_p.h
+++ b/src/libs/installer/metadatajob_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,10 +29,10 @@
#ifndef METADATAJOB_P_H
#define METADATAJOB_P_H
-#include "lib7z_extract.h"
-#include "lib7z_facade.h"
+#include "archivefactory.h"
#include "metadatajob.h"
+#include <QCryptographicHash>
#include <QDir>
#include <QFile>
@@ -47,9 +47,9 @@ public:
: m_message(message)
{}
- void raise() const { throw *this; }
+ void raise() const override { throw *this; }
QString message() const { return m_message; }
- UnzipArchiveException *clone() const { return new UnzipArchiveException(*this); }
+ UnzipArchiveException *clone() const override{ return new UnzipArchiveException(*this); }
private:
QString m_message;
@@ -62,11 +62,18 @@ class UnzipArchiveTask : public AbstractTask<void>
public:
UnzipArchiveTask(const QString &arcive, const QString &target)
- : m_archive(arcive), m_targetDir(target)
+ : m_archive(arcive)
+ , m_targetDir(target)
+ , m_removeArchive(false)
+ , m_storeChecksums(false)
{}
+
QString target() { return m_targetDir; }
QString archive() { return m_archive; }
- void doTask(QFutureInterface<void> &fi)
+ void setRemoveArchive(bool remove) { m_removeArchive = remove; }
+ void setStoreChecksums(bool store) { m_storeChecksums = store; }
+
+ void doTask(QFutureInterface<void> &fi) override
{
fi.reportStarted();
fi.setExpectedResultCount(1);
@@ -76,28 +83,136 @@ public:
return; // ignore already canceled
}
- QFile archive(m_archive);
- if (archive.open(QIODevice::ReadOnly)) {
- try {
- Lib7z::extractArchive(&archive, m_targetDir);
- } catch (const Lib7z::SevenZipException& e) {
- fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting "
- "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), e.message())));
- } catch (...) {
- fi.reportException(UnzipArchiveException(MetadataJob::tr("Unknown exception "
- "caught while extracting archive \"%1\".").arg(QDir::toNativeSeparators(m_archive))));
- }
- } else {
+ QScopedPointer<AbstractArchive> archive(ArchiveFactory::instance().create(m_archive));
+ if (!archive) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Unsupported archive \"%1\": no handler "
+ "registered for file suffix \"%2\".").arg(m_archive, QFileInfo(m_archive).suffix())));
+ return;
+ } else if (!archive->open(QIODevice::ReadOnly)) {
fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
- "reading: %2").arg(QDir::toNativeSeparators(m_archive), archive.errorString())));
+ "reading: %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
+ } else if (!archive->extract(m_targetDir)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Error while extracting "
+ "archive \"%1\": %2").arg(QDir::toNativeSeparators(m_archive), archive->errorString())));
+ return;
+ }
+
+ if (m_storeChecksums) {
+ // Calculate and store checksums of extracted files for later use
+ const QVector<ArchiveEntry> entries = archive->list();
+ for (auto &entry : entries) {
+ if (entry.isDirectory)
+ continue;
+
+ QFile file(m_targetDir + QDir::separator() + entry.path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open extracted file \"%1\" for "
+ "reading: %2").arg(QDir::toNativeSeparators(file.fileName()), file.errorString())));
+ break;
+ }
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(&file);
+
+ const QByteArray hexChecksum = hash.result().toHex();
+ QFileInfo fileInfo(file.fileName());
+ QFile hashFile(fileInfo.absolutePath() + QDir::separator()
+ + QString::fromLatin1(hexChecksum) + QLatin1String(".sha1"));
+ if (!hashFile.open(QIODevice::WriteOnly)) {
+ fi.reportException(UnzipArchiveException(MetadataJob::tr("Cannot open file \"%1\" for "
+ "writing: %2").arg(QDir::toNativeSeparators(hashFile.fileName()), hashFile.errorString())));
+ break;
+ }
+ }
}
+ archive->close();
+ if (m_removeArchive)
+ QFile::remove(m_archive);
+
fi.reportFinished();
}
private:
QString m_archive;
QString m_targetDir;
+ bool m_removeArchive;
+ bool m_storeChecksums;
+};
+
+class CacheTaskException : public QException
+{
+public:
+ CacheTaskException() {}
+ explicit CacheTaskException(const QString &message)
+ : m_message(message)
+ {}
+ ~CacheTaskException() {}
+
+ void raise() const override { throw *this; }
+ QString message() const { return m_message; }
+ CacheTaskException *clone() const override { return new CacheTaskException(*this); }
+
+private:
+ QString m_message;
+};
+
+class UpdateCacheTask : public AbstractTask<void>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(UpdateCacheTask)
+
+public:
+ UpdateCacheTask(MetadataCache &cache, QHash<QString, Metadata *> &updates)
+ : m_cache(&cache)
+ , m_updates(&updates)
+ {}
+
+ void doTask(QFutureInterface<void> &fi) override
+ {
+ fi.reportStarted();
+ fi.setExpectedResultCount(1);
+
+ // Register items from current run to cache
+ QStringList registeredKeys;
+ bool success = true;
+ for (auto *meta : qAsConst(*m_updates)) {
+ if (!m_cache->registerItem(meta, true, MetadataCache::Move)) {
+ success = false;
+ break;
+ }
+ meta->setPersistentRepositoryPath(meta->repository().url());
+ registeredKeys.append(m_updates->key(meta));
+ }
+ // Remove items whose ownership was transferred to cache
+ for (auto &key : qAsConst(registeredKeys))
+ m_updates->remove(key);
+
+ // Bail out if there was error while registering items
+ if (!success) {
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
+ m_cache->sync();
+ fi.reportFinished();
+ return;
+ }
+
+ // ...and clean up obsolete cached items
+ const QList<Metadata *> obsolete = m_cache->obsoleteItems();
+ for (auto *meta : obsolete)
+ m_cache->removeItem(meta->checksum());
+
+ if (!m_cache->sync()) {
+ fi.reportException(CacheTaskException(m_cache->errorString() + u' '
+ + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this.")));
+ }
+
+ fi.reportFinished();
+ }
+
+private:
+ MetadataCache *const m_cache;
+ QHash<QString, Metadata *> *const m_updates;
};
} // namespace QInstaller
diff --git a/src/libs/installer/minimumprogressoperation.cpp b/src/libs/installer/minimumprogressoperation.cpp
index 5ae571a59..c055a5c89 100644
--- a/src/libs/installer/minimumprogressoperation.cpp
+++ b/src/libs/installer/minimumprogressoperation.cpp
@@ -30,6 +30,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::MinimumProgressOperation
+ \internal
+*/
+
MinimumProgressOperation::MinimumProgressOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
diff --git a/src/libs/installer/minimumprogressoperation.h b/src/libs/installer/minimumprogressoperation.h
index 4cdafca70..a16345730 100644
--- a/src/libs/installer/minimumprogressoperation.h
+++ b/src/libs/installer/minimumprogressoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,10 +42,10 @@ class MinimumProgressOperation : public QObject, public Operation
public:
explicit MinimumProgressOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
signals:
void progressChanged(double progress);
diff --git a/src/libs/installer/observer.cpp b/src/libs/installer/observer.cpp
index ee143193e..57b67d8e1 100644
--- a/src/libs/installer/observer.cpp
+++ b/src/libs/installer/observer.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,18 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::FileTaskObserver
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Observer
+ \internal
+*/
+
FileTaskObserver::FileTaskObserver(QCryptographicHash::Algorithm algorithm)
: m_hash(algorithm)
{
@@ -109,9 +121,9 @@ QByteArray FileTaskObserver::checkSum() const
return m_hash.result();
}
-void FileTaskObserver::addCheckSumData(const char *data, int length)
+void FileTaskObserver::addCheckSumData(const QByteArray &data)
{
- m_hash.addData(data, length);
+ m_hash.addData(data);
}
void FileTaskObserver::addSample(qint64 sample)
diff --git a/src/libs/installer/observer.h b/src/libs/installer/observer.h
index 166ede6ac..d638d8ee4 100644
--- a/src/libs/installer/observer.h
+++ b/src/libs/installer/observer.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -56,14 +56,14 @@ public:
FileTaskObserver(QCryptographicHash::Algorithm algorithm);
~FileTaskObserver();
- int progressValue() const;
- QString progressText() const;
+ int progressValue() const override;
+ QString progressText() const override;
QByteArray checkSum() const;
- void addCheckSumData(const char *data, int length);
+ void addCheckSumData(const QByteArray &data);
void addSample(qint64 sample);
- void timerEvent(QTimerEvent *event);
+ void timerEvent(QTimerEvent *event) override;
void setBytesTransfered(qint64 bytesTransfered);
void addBytesTransfered(qint64 bytesTransfered);
diff --git a/src/libs/installer/operationtracer.cpp b/src/libs/installer/operationtracer.cpp
new file mode 100644
index 000000000..b09c08d2e
--- /dev/null
+++ b/src/libs/installer/operationtracer.cpp
@@ -0,0 +1,151 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "operationtracer.h"
+
+#include "packagemanagercore.h"
+
+namespace QInstaller {
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::AbstractOperationTracer
+ \brief The AbstractOperationTracer is a pure virtual base class for classes
+ tracing starting and finishing of installer operations.
+*/
+
+/*!
+ \fn QInstaller::AbstractOperationTracer::~AbstractOperationTracer()
+
+ Destructs object. A subclass may override this method.
+*/
+
+/*!
+ \fn QInstaller::AbstractOperationTracer::trace(const QString &state)
+
+ Prints trace output for starting operation in \a state.
+ A subclass should implement this method.
+*/
+
+/*!
+ Constructs tracer for \a operation. Objects of this class cannot
+ be constructed directly, but the derived classes should explicitly call
+ the base class constructor in their constructors.
+*/
+AbstractOperationTracer::AbstractOperationTracer(Operation *operation)
+ : m_operation(nullptr)
+{
+ // don't create output for that hacky pseudo operation
+ if (operation->name() != QLatin1String("MinimumProgress"))
+ m_operation = operation;
+}
+
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::OperationTracer
+ \brief The OperationTracer prints trace output for starting of operations
+ and automatically indicates finish on destruction.
+*/
+
+/*!
+ Constructs tracer for \a operation.
+*/
+OperationTracer::OperationTracer(Operation *operation)
+ : AbstractOperationTracer(operation)
+{
+}
+
+/*!
+ Destructs object and prints message indicating finished operation.
+*/
+OperationTracer::~OperationTracer()
+{
+ if (!m_operation)
+ return;
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Done";
+}
+
+/*!
+ Prints trace output for starting operation in \a state.
+*/
+void OperationTracer::trace(const QString &state)
+{
+ if (!m_operation)
+ return;
+
+ qCDebug(lcInstallerInstallLog).noquote() << QString::fromLatin1("%1 %2 operation: %3")
+ .arg(state, m_operation->value(QLatin1String("component")).toString(), m_operation->name());
+
+ QStringList args = m_operation->arguments();
+ if (m_operation->requiresUnreplacedVariables())
+ args = m_operation->packageManager()->replaceVariables(m_operation->arguments());
+
+ qCDebug(lcInstallerInstallLog).noquote() << QString::fromLatin1("\t- arguments: %1")
+ .arg(args.join(QLatin1String(", ")));
+}
+
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ConcurrentOperationTracer
+ \brief The ConcurrentOperationTracer prints trace output for starting of
+ asynchronous operations.
+*/
+
+/*!
+ Constructs tracer for \a operation.
+*/
+ConcurrentOperationTracer::ConcurrentOperationTracer(Operation *operation)
+ : AbstractOperationTracer(operation)
+{
+}
+
+/*!
+ Prints trace output for starting operation in \a state.
+*/
+void ConcurrentOperationTracer::trace(const QString &state)
+{
+ if (!m_operation)
+ return;
+
+ qCDebug(lcInstallerInstallLog).noquote() << QString::fromLatin1("%1 %2 concurrent operation: %3")
+ .arg(state, m_operation->value(QLatin1String("component")).toString(), m_operation->name());
+
+ QStringList args = m_operation->arguments();
+ if (m_operation->requiresUnreplacedVariables())
+ args = m_operation->packageManager()->replaceVariables(m_operation->arguments());
+
+ qCDebug(lcInstallerInstallLog).noquote() << QString::fromLatin1("\t- arguments: %1")
+ .arg(args.join(QLatin1String(", ")));
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Started";
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/operationtracer.h b/src/libs/installer/operationtracer.h
new file mode 100644
index 000000000..ce479662c
--- /dev/null
+++ b/src/libs/installer/operationtracer.h
@@ -0,0 +1,68 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef OPERATIONTRACER_H
+#define OPERATIONTRACER_H
+
+#include "qinstallerglobal.h"
+#include "globals.h"
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT AbstractOperationTracer
+{
+public:
+ explicit AbstractOperationTracer(Operation *operation);
+ virtual ~AbstractOperationTracer() = default;
+
+ virtual void trace(const QString &state) = 0;
+
+protected:
+ Operation *m_operation;
+};
+
+class INSTALLER_EXPORT OperationTracer : public AbstractOperationTracer
+{
+public:
+ explicit OperationTracer(Operation *operation);
+ ~OperationTracer() override;
+
+ void trace(const QString &state) override;
+};
+
+class INSTALLER_EXPORT ConcurrentOperationTracer : public AbstractOperationTracer
+{
+public:
+ explicit ConcurrentOperationTracer(Operation *operation);
+
+ void trace(const QString &state) override;
+};
+
+} // namespace QInstaller
+
+#endif // OPERATIONTRACER_H
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index c154edf76..0eae41ea3 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,7 @@
#include "adminauthorization.h"
#include "binarycontent.h"
#include "component.h"
+#include "componentalias.h"
#include "componentmodel.h"
#include "downloadarchivesjob.h"
#include "errors.h"
@@ -43,9 +44,10 @@
#include "remoteclient.h"
#include "remotefileengine.h"
#include "settings.h"
-#include "utils.h"
#include "installercalculator.h"
#include "uninstallercalculator.h"
+#include "loggingutils.h"
+#include "componentsortfilterproxymodel.h"
#include <productkeycheck.h>
@@ -54,22 +56,29 @@
#include <QtConcurrentRun>
#include <QtCore/QMutex>
-#include <QtCore/QRegExp>
#include <QtCore/QSettings>
#include <QtCore/QTemporaryFile>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QtCore5Compat/QTextCodec>
+#include <QtCore5Compat/QTextDecoder>
+#include <QtCore5Compat/QTextEncoder>
+#else
#include <QtCore/QTextCodec>
#include <QtCore/QTextDecoder>
#include <QtCore/QTextEncoder>
+#endif
#include <QtCore/QTextStream>
#include <QDesktopServices>
#include <QFileDialog>
+#include <QRegularExpression>
#include "sysinfo.h"
#include "updateoperationfactory.h"
#ifdef Q_OS_WIN
-# include "qt_windows.h"
+#include "qt_windows.h"
+#include <limits>
#endif
#include <QStandardPaths>
@@ -134,6 +143,11 @@ using namespace QInstaller;
\value Unfinished
Installation was not completed.
\value ForceUpdate
+ Installation has to be updated.
+ \value EssentialUpdated
+ Installation essential components were updated.
+ \value NoPackagesFound
+ No packages found from remote.
*/
/*!
@@ -142,80 +156,68 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::aboutCalculateComponentsToInstall() const
+ \fn QInstaller::PackageManagerCore::aboutCalculateComponentsToInstall() const
\sa {installer::aboutCalculateComponentsToInstall}{installer.aboutCalculateComponentsToInstall}
*/
/*!
- \fn PackageManagerCore::finishedCalculateComponentsToInstall() const
+ \fn QInstaller::PackageManagerCore::finishedCalculateComponentsToInstall() const
\sa {installer::finishedCalculateComponentsToInstall}{installer.finishedCalculateComponentsToInstall}
*/
/*!
- \fn PackageManagerCore::aboutCalculateComponentsToUninstall() const
+ \fn QInstaller::PackageManagerCore::aboutCalculateComponentsToUninstall() const
\sa {installer::aboutCalculateComponentsToUninstall}{installer.aboutCalculateComponentsToUninstall}
*/
/*!
- \fn PackageManagerCore::finishedCalculateComponentsToUninstall() const
+ \fn QInstaller::PackageManagerCore::finishedCalculateComponentsToUninstall() const
\sa {installer::finishedCalculateComponentsToUninstall}{installer.finishedCalculateComponentsToUninstall}
*/
/*!
- \fn PackageManagerCore::componentAdded(QInstaller::Component *comp)
+ \fn QInstaller::PackageManagerCore::componentAdded(QInstaller::Component *comp)
Emitted when the new root component \a comp is added.
\sa {installer::componentAdded}{installer.componentAdded}
- \sa rootComponentsAdded(), updaterComponentsAdded()
-*/
-
-/*!
- \fn PackageManagerCore::rootComponentsAdded(QList<QInstaller::Component*> components)
-
- Emitted when the list of root components specified by \a components is added.
-
- \sa {installer::rootComponentsAdded}{installer.rootComponentsAdded}
- \sa componentAdded(), updaterComponentsAdded()
*/
/*!
- \fn PackageManagerCore::updaterComponentsAdded(QList<QInstaller::Component*> components)
+ \fn QInstaller::PackageManagerCore::valueChanged(const QString &key, const QString &value)
- Emitted when a new list of updater components specified by \a components is added.
+ Emitted when the value \a value of the key \a key changes.
- \sa {installer::updaterComponentsAdded}{installer.updaterComponentsAdded}
- \sa componentAdded(), rootComponentsAdded()
+ \sa {installer::valueChanged}{installer.valueChanged}, setValue()
*/
/*!
- \fn PackageManagerCore::valueChanged(const QString &key, const QString &value)
+ \fn QInstaller::PackageManagerCore::currentPageChanged(int page)
- Emitted when the value \a value of the key \a key changes.
+ Emitted when the current page \a page changes.
- \sa {installer::valueChanged}{installer.valueChanged}, setValue()
+ \sa {installer::currentPageChanged}{installer.currentPageChanged}
*/
/*!
- \fn PackageManagerCore::currentPageChanged(int page)
+ \fn QInstaller::PackageManagerCore::defaultTranslationsLoadedForLanguage(QLocale lang)
- Emitted when the current page \a page changes.
+ Emitted when the language \a lang has changed.
- \sa {installer::currentPageChanged}{installer.currentPageChanged}
*/
/*!
- \fn PackageManagerCore::finishButtonClicked()
+ \fn QInstaller::PackageManagerCore::finishButtonClicked()
\sa {installer::finishButtonClicked}{installer.finishButtonClicked}
*/
/*!
- \fn PackageManagerCore::metaJobProgress(int progress)
+ \fn QInstaller::PackageManagerCore::metaJobProgress(int progress)
Triggered with progress updates of the communication with a remote
repository. Values of \a progress range from 0 to 100.
@@ -224,7 +226,16 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::metaJobInfoMessage(const QString &message)
+ \fn QInstaller::PackageManagerCore::metaJobTotalProgress(int progress)
+
+ Triggered when the total \a progress value of the communication with a
+ remote repository changes.
+
+ \sa {installer::metaJobTotalProgress}{installer.metaJobTotalProgress}
+*/
+
+/*!
+ \fn QInstaller::PackageManagerCore::metaJobInfoMessage(const QString &message)
Triggered with informative updates, \a message, of the communication with a remote repository.
@@ -232,14 +243,14 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::startAllComponentsReset()
+ \fn QInstaller::PackageManagerCore::startAllComponentsReset()
\sa {installer::startAllComponentsReset}{installer.startAllComponentsReset}
\sa finishAllComponentsReset()
*/
/*!
- \fn PackageManagerCore::finishAllComponentsReset(const QList<QInstaller::Component*> &rootComponents)
+ \fn QInstaller::PackageManagerCore::finishAllComponentsReset(const QList<QInstaller::Component*> &rootComponents)
Triggered when the list of new root components, \a rootComponents, has been updated.
@@ -248,13 +259,13 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::startUpdaterComponentsReset()
+ \fn QInstaller::PackageManagerCore::startUpdaterComponentsReset()
\sa {installer::startUpdaterComponentsReset}{installer.startUpdaterComponentsReset}
*/
/*!
- \fn PackageManagerCore::finishUpdaterComponentsReset(const QList<QInstaller::Component*> &componentsWithUpdates)
+ \fn QInstaller::PackageManagerCore::finishUpdaterComponentsReset(const QList<QInstaller::Component*> &componentsWithUpdates)
Triggered when the list of available remote updates, \a componentsWithUpdates,
has been updated.
@@ -263,48 +274,60 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::installationStarted()
+ \fn QInstaller::PackageManagerCore::installationStarted()
- \sa {installer::installationStarted}{installer.installationStarted}
- \sa installationFinished() installationInterrupted()
+ \sa {installer::installationStarted}{installer.installationStarted},
+ installationFinished(), installationInterrupted()
*/
/*!
- \fn PackageManagerCore::installationInterrupted()
+ \fn QInstaller::PackageManagerCore::installationInterrupted()
- \sa {installer::installationInterrupted}{installer.installationInterrupted}
- \sa interrupt() installationStarted() installationFinished()
+ \sa {installer::installationInterrupted}{installer.installationInterrupted},
+ interrupt(), installationStarted(), installationFinished()
*/
/*!
- \fn PackageManagerCore::installationFinished()
+ \fn QInstaller::PackageManagerCore::installationFinished()
- \sa {installer::installationFinished}{installer.installationFinished}
- \sa installationStarted() installationInterrupted()
+ \sa {installer::installationFinished}{installer.installationFinished},
+ installationStarted(), installationInterrupted()
*/
/*!
- \fn PackageManagerCore::updateFinished()
+ \fn QInstaller::PackageManagerCore::updateFinished()
\sa {installer::installationFinished}{installer.installationFinished}
*/
/*!
- \fn PackageManagerCore::uninstallationStarted()
+ \fn QInstaller::PackageManagerCore::uninstallationStarted()
\sa {installer::uninstallationStarted}{installer.uninstallationStarted}
\sa uninstallationFinished()
*/
/*!
- \fn PackageManagerCore::uninstallationFinished()
+ \fn QInstaller::PackageManagerCore::uninstallationFinished()
\sa {installer::uninstallationFinished}{installer.uninstallationFinished}
\sa uninstallationStarted()
*/
/*!
- \fn PackageManagerCore::titleMessageChanged(const QString &title)
+ \fn QInstaller::PackageManagerCore::offlineGenerationStarted()
+
+ \sa {installer::offlineGenerationStarted()}{installer.offlineGenerationStarted()}
+*/
+
+/*!
+ \fn QInstaller::PackageManagerCore::offlineGenerationFinished()
+
+ \sa {installer::offlineGenerationFinished()}{installer.offlineGenerationFinished()}
+*/
+
+/*!
+ \fn QInstaller::PackageManagerCore::titleMessageChanged(const QString &title)
Emitted when the text of the installer status (on the PerformInstallation page) changes to
\a title.
@@ -313,7 +336,15 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page)
+ \fn QInstaller::PackageManagerCore::downloadArchivesFinished()
+
+ Emitted when all data archives for components have been downloaded successfully.
+
+ \sa {installer::downloadArchivesFinished}{installer.downloadArchivesFinished}
+*/
+
+/*!
+ \fn QInstaller::PackageManagerCore::wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page)
Emitted when a custom \a widget is about to be inserted into \a page by
addWizardPage().
@@ -322,7 +353,7 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::wizardPageRemovalRequested(QWidget *widget)
+ \fn QInstaller::PackageManagerCore::wizardPageRemovalRequested(QWidget *widget)
Emitted when a \a widget is removed by removeWizardPage().
@@ -330,15 +361,17 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page)
+ \fn QInstaller::PackageManagerCore::wizardWidgetInsertionRequested(QWidget *widget,
+ QInstaller::PackageManagerCore::WizardPage page, int position)
- Emitted when a \a widget is inserted into \a page by addWizardPageItem().
+ Emitted when a \a widget is inserted into \a page by addWizardPageItem(). If several widgets
+ are added to the same \a page, widget with lower \a position number will be inserted on top.
\sa {installer::wizardWidgetInsertionRequested}{installer.wizardWidgetInsertionRequested}
*/
/*!
- \fn PackageManagerCore::wizardWidgetRemovalRequested(QWidget *widget)
+ \fn QInstaller::PackageManagerCore::wizardWidgetRemovalRequested(QWidget *widget)
Emitted when a \a widget is removed by removeWizardPageItem().
@@ -346,7 +379,7 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::wizardPageVisibilityChangeRequested(bool visible, int page)
+ \fn QInstaller::PackageManagerCore::wizardPageVisibilityChangeRequested(bool visible, int page)
Emitted when the visibility of the page with the ID \a page changes to \a visible.
@@ -355,7 +388,7 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name,
+ \fn QInstaller::PackageManagerCore::setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name,
const QString &callbackName)
Requests that a validator be set for the custom page specified by \a name and
@@ -366,7 +399,7 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::setAutomatedPageSwitchEnabled(bool request)
+ \fn QInstaller::PackageManagerCore::setAutomatedPageSwitchEnabled(bool request)
Triggered when the automatic switching from the perform installation to the installation
finished page is enabled (\a request is \c true) or disabled (\a request is \c false).
@@ -378,26 +411,45 @@ using namespace QInstaller;
*/
/*!
- \fn PackageManagerCore::coreNetworkSettingsChanged()
+ \fn QInstaller::PackageManagerCore::coreNetworkSettingsChanged()
\sa {installer::coreNetworkSettingsChanged}{installer.coreNetworkSettingsChanged}
*/
/*!
- \fn PackageManagerCore::guiObjectChanged(QObject *gui)
+ \fn QInstaller::PackageManagerCore::guiObjectChanged(QObject *gui)
Emitted when the GUI object is set to \a gui.
*/
+/*!
+ \fn QInstaller::PackageManagerCore::unstableComponentFound(const QString &type, const QString &errorMessage, const QString &component)
+
+ Emitted when an unstable \a component is found containing an unstable \a type and \a errorMessage.
+*/
+
+/*!
+ \fn QInstaller::PackageManagerCore::installerBinaryMarkerChanged(qint64 magicMarker)
+
+ Emitted when installer binary marker \a magicMarker has changed.
+*/
+
+/*!
+ \fn QInstaller::PackageManagerCore::componentsRecalculated()
+ Emitted when the component tree is recalculated. In a graphical interface,
+ this signal is emitted also after the categories are fetched.
+*/
Q_GLOBAL_STATIC(QMutex, globalModelMutex);
static QFont *sVirtualComponentsFont = nullptr;
Q_GLOBAL_STATIC(QMutex, globalVirtualComponentsFontMutex);
static bool sNoForceInstallation = false;
+static bool sNoDefaultInstallation = false;
static bool sVirtualComponentsVisible = false;
static bool sCreateLocalRepositoryFromBinary = false;
+static int sMaxConcurrentOperations = 0;
static bool componentMatches(const Component *component, const QString &name,
const QString &version = QString())
@@ -417,6 +469,11 @@ static bool componentMatches(const Component *component, const QString &name,
*/
void PackageManagerCore::writeMaintenanceTool()
{
+ if (d->m_disableWriteMaintenanceTool) {
+ qCDebug(QInstaller::lcInstallerInstallLog()) << "Maintenance tool writing disabled.";
+ return;
+ }
+
if (d->m_needToWriteMaintenanceTool) {
try {
d->writeMaintenanceTool(d->m_performedOperationsOld + d->m_performedOperationsCurrentSession);
@@ -448,6 +505,14 @@ void PackageManagerCore::writeMaintenanceConfigFiles()
}
/*!
+ Disables writing of maintenance tool for the current session if \a disable is \c true.
+ */
+void PackageManagerCore::disableWriteMaintenanceTool(bool disable)
+{
+ d->m_disableWriteMaintenanceTool = disable;
+}
+
+/*!
Resets the class to its initial state.
*/
void PackageManagerCore::reset()
@@ -457,7 +522,6 @@ void PackageManagerCore::reset()
d->m_status = PackageManagerCore::Unfinished;
d->m_installerBaseBinaryUnreplaced.clear();
d->m_coreCheckedHash.clear();
- d->m_componentsToInstallCalculated = false;
}
/*!
@@ -499,49 +563,113 @@ void PackageManagerCore::cancelMetaInfoJob()
}
/*!
- \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
- */
-void PackageManagerCore::componentsToInstallNeedsRecalculation()
+ Resets the cache used to store downloaded metadata, if one was previously
+ initialized. If \a init is set to \c true, the cache is reinitialized for
+ the path configured in installer's settings.
+
+ Returns \c true on success, \c false otherwise.
+*/
+bool PackageManagerCore::resetLocalCache(bool init)
{
- d->clearInstallerCalculator();
- d->clearUninstallerCalculator();
- QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
+ return d->m_metadataJob.resetCache(init);
+}
- d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall);
+/*!
+ Clears the contents of the cache used to store downloaded metadata.
+ Returns \c true on success, \c false otherwise. An error string can
+ be retrieved with \a error.
+*/
+bool PackageManagerCore::clearLocalCache(QString *error)
+{
+ if (d->m_metadataJob.clearCache())
+ return true;
- QList<Component *> componentsToInstall = d->installerCalculator()->orderedComponentsToInstall();
+ if (error)
+ *error = d->m_metadataJob.errorString();
- QList<Component *> selectedComponentsToUninstall;
- foreach (Component *component, components(ComponentType::All)) {
- if (component->uninstallationRequested() && !selectedComponentsToInstall.contains(component))
- selectedComponentsToUninstall.append(component);
- }
+ return false;
+}
- d->uninstallerCalculator()->appendComponentsToUninstall(selectedComponentsToUninstall);
+/*!
+ Returns \c true if the metadata cache is initialized and valid, \c false otherwise.
+*/
+bool PackageManagerCore::isValidCache() const
+{
+ return d->m_metadataJob.isValidCache();
+}
- QSet<Component *> componentsToUninstall = d->uninstallerCalculator()->componentsToUninstall();
+/*!
+ \internal
+ */
+template <typename T>
+bool PackageManagerCore::loadComponentScripts(const T &components, const bool postScript)
+{
+ return d->loadComponentScripts(components, postScript);
+}
- foreach (Component *component, components(ComponentType::All))
- component->setInstallAction(component->isInstalled()
- ? ComponentModelHelper::KeepInstalled
- : ComponentModelHelper::KeepUninstalled);
- foreach (Component *component, componentsToUninstall)
- component->setInstallAction(ComponentModelHelper::Uninstall);
- foreach (Component *component, componentsToInstall)
- component->setInstallAction(ComponentModelHelper::Install);
+template bool PackageManagerCore::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCore::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
- // update all nodes uncompressed size
- foreach (Component *const component, components(ComponentType::Root))
- component->updateUncompressedSize(); // this is a recursive call
+/*!
+ Saves the installer \a args user has given when running installer. Command and option arguments
+ are not saved.
+*/
+void PackageManagerCore::saveGivenArguments(const QStringList &args)
+{
+ m_arguments = args;
+}
+
+/*!
+ Returns the commands and options user has given when running installer.
+*/
+QStringList PackageManagerCore::givenArguments() const
+{
+ return m_arguments;
+}
+/*!
+ \deprecated [4.5] Use recalculateAllComponents() instead.
+
+ \sa {installer::componentsToInstallNeedsRecalculation}{installer.componentsToInstallNeedsRecalculation}
+ */
+void PackageManagerCore::componentsToInstallNeedsRecalculation()
+{
+ recalculateAllComponents();
}
/*!
+ \fn QInstaller::PackageManagerCore::clearComponentsToInstallCalculated()
+
+ \deprecated [4.5] Installer framework recalculates components each time the calculation
+ of components to install is requested, so there is no need to call this anymore, and the
+ method does nothing. On previous versions calling this forced a recalculation of
+ components to install.
+
\sa {installer::clearComponentsToInstallCalculated}{installer.clearComponentsToInstallCalculated}
*/
-void PackageManagerCore::clearComponentsToInstallCalculated()
+
+/*!
+ Recalculates all components to install and uninstall. Returns \c true
+ on success, \c false otherwise. Detailed error messages can be retrieved
+ with {installer::componentsToInstallError} and {installer::componentsToUninstallError}.
+ */
+bool PackageManagerCore::recalculateAllComponents()
{
- d->m_componentsToInstallCalculated = false;
+ // Clear previous results first, as the check states are updated
+ // at the end of both calculate methods, which refer to the results
+ // from both calculators. Needed to keep the state correct.
+ d->clearInstallerCalculator();
+ d->clearUninstallerCalculator();
+
+ if (!calculateComponentsToInstall())
+ return false;
+ if (!isInstaller() && !calculateComponentsToUninstall())
+ return false;
+
+ // update all nodes uncompressed size
+ foreach (Component *const component, components(ComponentType::Root))
+ component->updateUncompressedSize(); // this is a recursive call
+
+ return true;
}
/*!
@@ -578,6 +706,84 @@ void PackageManagerCore::setMessageBoxAutomaticAnswer(const QString &identifier,
}
/*!
+ Automatically uses the default button value set for the message box.
+
+ This can be used for unattended (automatic) installations.
+ */
+void PackageManagerCore::acceptMessageBoxDefaultButton()
+{
+ MessageBoxHandler::instance()->setDefaultAction(MessageBoxHandler::Default);
+}
+
+/*!
+ Automatically accepts all license agreements required to install the selected components.
+
+ \sa {installer::setAutoAcceptLicenses}{installer.setAutoAcceptLicenses}
+*/
+void PackageManagerCore::setAutoAcceptLicenses()
+{
+ d->m_autoAcceptLicenses = true;
+}
+
+/*!
+ Automatically sets the existing directory or filename \a value to QFileDialog with the ID
+ \a identifier. QFileDialog can be called from script.
+
+ This can be used for unattended (automatic) installations.
+
+ \sa {installer::setFileDialogAutomaticAnswer}{installer.setFileDialogAutomaticAnswer}
+ \sa {QFileDialog::getExistingDirectory}{QFileDialog.getExistingDirectory}
+ \sa {QFileDialog::getOpenFileName}{QFileDialog.getOpenFileName}
+ */
+void PackageManagerCore::setFileDialogAutomaticAnswer(const QString &identifier, const QString &value)
+{
+ m_fileDialogAutomaticAnswers.insert(identifier, value);
+}
+
+/*!
+ Removes the automatic answer from QFileDialog with the ID \a identifier.
+ QFileDialog can be called from script.
+
+ \sa {installer::removeFileDialogAutomaticAnswer}{installer.removeFileDialogAutomaticAnswer}
+ \sa {QFileDialog::getExistingDirectory}{QFileDialog.getExistingDirectory}
+ \sa {QFileDialog::getOpenFileName}{QFileDialog.getOpenFileName}
+ */
+void PackageManagerCore::removeFileDialogAutomaticAnswer(const QString &identifier)
+{
+ m_fileDialogAutomaticAnswers.remove(identifier);
+}
+
+/*!
+ Returns \c true if QFileDialog with the ID \a identifier has an automatic answer set.
+
+ \sa {installer::containsFileDialogAutomaticAnswer}{installer.containsFileDialogAutomaticAnswer}
+ \sa {installer::removeFileDialogAutomaticAnswer}{installer.removeFileDialogAutomaticAnswer}
+ \sa {QFileDialog::getExistingDirectory}{QFileDialog.getExistingDirectory}
+ \sa {QFileDialog::getOpenFileName}{QFileDialog.getOpenFileName}
+ */
+bool PackageManagerCore::containsFileDialogAutomaticAnswer(const QString &identifier) const
+{
+ return m_fileDialogAutomaticAnswers.contains(identifier);
+}
+
+/*!
+ * Returns the hash of file dialog automatic answers
+ * \sa setFileDialogAutomaticAnswer()
+ */
+QHash<QString, QString> PackageManagerCore::fileDialogAutomaticAnswers() const
+{
+ return m_fileDialogAutomaticAnswers;
+}
+
+/*!
+ Set to automatically confirm install, update or remove without asking user.
+*/
+void PackageManagerCore::setAutoConfirmCommand()
+{
+ d->m_autoConfirmCommand = true;
+}
+
+/*!
Returns the size of the component \a component as \a value.
*/
quint64 PackageManagerCore::size(QInstaller::Component *component, const QString &value) const
@@ -596,7 +802,7 @@ quint64 PackageManagerCore::requiredDiskSpace() const
quint64 result = 0;
foreach (QInstaller::Component *component, orderedComponentsToInstall())
- result += size(component, scUncompressedSize);
+ result += size(component, isOfflineGenerator() ? scCompressedSize : scUncompressedSize);
return result;
}
@@ -607,12 +813,13 @@ quint64 PackageManagerCore::requiredDiskSpace() const
*/
quint64 PackageManagerCore::requiredTemporaryDiskSpace() const
{
- if (isOfflineOnly())
- return 0;
-
quint64 result = 0;
- foreach (QInstaller::Component *component, orderedComponentsToInstall())
+ foreach (QInstaller::Component *component, orderedComponentsToInstall()) {
+ if (!component->isFromOnlineRepository())
+ continue;
+
result += size(component, scCompressedSize);
+ }
return result;
}
@@ -625,31 +832,43 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
{
Q_ASSERT(partProgressSize >= 0 && partProgressSize <= 1);
- QList<QPair<QString, QString> > archivesToDownload;
+ QList<DownloadItem> archivesToDownload;
+ quint64 archivesToDownloadTotalSize = 0;
QList<Component*> neededComponents = orderedComponentsToInstall();
foreach (Component *component, neededComponents) {
// collect all archives to be downloaded
const QStringList toDownload = component->downloadableArchives();
+ bool checkSha1CheckSum = (component->value(scCheckSha1CheckSum).toLower() == scTrue);
foreach (const QString &versionFreeString, toDownload) {
- archivesToDownload.push_back(qMakePair(QString::fromLatin1("installer://%1/%2")
- .arg(component->name(), versionFreeString), QString::fromLatin1("%1/%2/%3")
- .arg(component->repositoryUrl().toString(), component->name(), versionFreeString)));
+ DownloadItem item;
+ item.checkSha1CheckSum = checkSha1CheckSum;
+ item.fileName = scInstallerPrefixWithTwoArgs.arg(component->name(), versionFreeString);
+ item.sourceUrl = scThreeArgs.arg(component->repositoryUrl().toString(), component->name(), versionFreeString);
+ archivesToDownload.push_back(item);
}
+ archivesToDownloadTotalSize += component->value(scCompressedSize).toULongLong();
}
if (archivesToDownload.isEmpty())
return 0;
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nDownloading packages..."));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Downloading packages..."));
- DownloadArchivesJob archivesJob(this);
+ DownloadArchivesJob archivesJob(this, QLatin1String("downloadArchiveJob"));
archivesJob.setAutoDelete(false);
archivesJob.setArchivesToDownload(archivesToDownload);
+ archivesJob.setExpectedTotalSize(archivesToDownloadTotalSize);
connect(this, &PackageManagerCore::installationInterrupted, &archivesJob, &Job::cancel);
connect(&archivesJob, &DownloadArchivesJob::outputTextChanged,
ProgressCoordinator::instance(), &ProgressCoordinator::emitLabelAndDetailTextChanged);
connect(&archivesJob, &DownloadArchivesJob::downloadStatusChanged,
- ProgressCoordinator::instance(), &ProgressCoordinator::downloadStatusChanged);
+ ProgressCoordinator::instance(), &ProgressCoordinator::additionalProgressStatusChanged);
+
+ connect(&archivesJob, &DownloadArchivesJob::fileDownloadReady,
+ d, &PackageManagerCorePrivate::addPathForDeletion);
+ connect(&archivesJob, &DownloadArchivesJob::hashDownloadReady,
+ d, &PackageManagerCorePrivate::addPathForDeletion);
ProgressCoordinator::instance()->registerPartProgress(&archivesJob,
SIGNAL(progressChanged(double)), partProgressSize);
@@ -665,7 +884,8 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize)
if (d->statusCanceledOrFailed())
throw Error(tr("Installation canceled by user."));
- ProgressCoordinator::instance()->emitDownloadStatus(tr("All downloads finished."));
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("All downloads finished."));
+ emit downloadArchivesFinished();
return archivesJob.numberOfDownloads();
}
@@ -709,7 +929,7 @@ void PackageManagerCore::setNeedsHardRestart(bool needsHardRestart)
*/
void PackageManagerCore::rollBackInstallation()
{
- emit titleMessageChanged(tr("Cancelling the Installer"));
+ emit titleMessageChanged(tr("Canceling the Installer"));
// this unregisters all operation progressChanged connected
ProgressCoordinator::instance()->setUndoMode();
@@ -745,7 +965,7 @@ void PackageManagerCore::rollBackInstallation()
operation->setValue(QLatin1String("forceremoval"), false);
}
- PackageManagerCorePrivate::performOperationThreaded(operation, PackageManagerCorePrivate::Undo);
+ PackageManagerCorePrivate::performOperationThreaded(operation, Operation::Undo);
const QString componentName = operation->value(QLatin1String("component")).toString();
if (!componentName.isEmpty()) {
@@ -759,11 +979,10 @@ void PackageManagerCore::rollBackInstallation()
}
d->m_localPackageHub->writeToDisk();
- if (isInstaller()) {
- if (d->m_localPackageHub->packageInfoCount() == 0) {
- QFile file(d->m_localPackageHub->fileName());
+ if (isInstaller() && d->m_localPackageHub->packageInfoCount() == 0) {
+ QFile file(d->m_localPackageHub->fileName());
+ if (!file.fileName().isEmpty() && file.exists())
file.remove();
- }
}
if (becameAdmin)
@@ -789,7 +1008,7 @@ void PackageManagerCore::rollBackInstallation()
*/
bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) const
{
- QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettingsWrapper::NativeFormat);
+ QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettings::NativeFormat);
return settings.value(QString::fromLatin1(".%1/Default").arg(extension)).isValid();
}
@@ -804,7 +1023,7 @@ bool PackageManagerCore::isFileExtensionRegistered(const QString &extension) con
*/
bool PackageManagerCore::fileExists(const QString &filePath) const
{
- return QFileInfo(filePath).exists();
+ return QFileInfo::exists(filePath);
}
/*!
@@ -829,8 +1048,219 @@ QString PackageManagerCore::readFile(const QString &filePath, const QString &cod
return QString();
QTextStream stream(&f);
- stream.setCodec(codec);
- return stream.readAll();
+ return QString::fromUtf8(codec->fromUnicode(stream.readAll()));
+}
+
+/*!
+ * Prints \a title to console and reads console input. Function will halt the
+ * installer and wait for user input. Returns a line which user has typed into
+ * console. The maximum allowed line length is set to \a maxlen. If the stream
+ * contains lines longer than this, then the line will be split after maxlen
+ * characters. If \a maxlen is 0, the line can be of any length.
+ *
+ * \note Can be only called when installing from command line instance without GUI.
+ * If the output device is not a TTY, i.e. when forwarding to a file, the function
+ * will throw an error.
+ *
+ * \sa {installer::readConsoleLine}{installer.readConsoleLine}
+ */
+QString PackageManagerCore::readConsoleLine(const QString &title, qint64 maxlen) const
+{
+ if (!isCommandLineInstance())
+ return QString();
+ if (LoggingHandler::instance().outputRedirected()) {
+ throw Error(tr("User input is required but the output "
+ "device is not associated with a terminal."));
+ }
+ if (!title.isEmpty())
+ qDebug() << title;
+ QTextStream stream(stdin);
+ QString input;
+ stream.readLineInto(&input, maxlen);
+ return input;
+}
+
+/*!
+ Returns \a path with the '/' separators converted to separators that are
+ appropriate for the underlying operating system.
+
+ On Unix platforms the returned string is the same as the argument.
+
+ \note Predefined variables, such as @TargetDir@, are not resolved by
+ this function. To convert the separators to predefined variables, use
+ \c installer.value() to resolve the variables first.
+
+ \sa {installer::toNativeSeparators}{installer.toNativeSeparators}
+ \sa fromNativeSeparators()
+ \sa {installer::value}{installer.value}
+*/
+QString PackageManagerCore::toNativeSeparators(const QString &path)
+{
+ return QDir::toNativeSeparators(path);
+}
+
+/*!
+ Returns \a path using '/' as file separator.
+
+ On Unix platforms the returned string is the same as the argument.
+
+ \note Predefined variables, such as @TargetDir@, are not resolved by
+ this function. To convert the separators to predefined variables, use
+ \c installer.value() to resolve the variables first.
+
+ \sa {installer::fromNativeSeparators}{installer.fromNativeSeparators}
+ \sa toNativeSeparators()
+ \sa {installer::value}{installer.value}
+*/
+QString PackageManagerCore::fromNativeSeparators(const QString &path)
+{
+ return QDir::fromNativeSeparators(path);
+}
+
+/*!
+ Checks whether installation is allowed to \a targetDirectory:
+ \list
+ \li Returns \c true if the directory does not exist.
+ \li Returns \c true if the directory exists and is empty.
+ \li Returns \c false if the directory already exists and contains an installation.
+ \li Returns \c false if the target is a file or a symbolic link.
+ \li Returns \c true or \c false if the directory exists but is not empty, depending on the
+ choice that the end users make in the displayed message box.
+ \endlist
+*/
+bool PackageManagerCore::installationAllowedToDirectory(const QString &targetDirectory)
+{
+ const QFileInfo fi(targetDirectory);
+ if (!fi.exists())
+ return true;
+
+ const QDir dir(targetDirectory);
+ // the directory exists and is empty...
+ if (dir.exists() && dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty())
+ return true;
+
+ if (fi.isDir()) {
+ QString fileName = settings().maintenanceToolName();
+#if defined(Q_OS_MACOS)
+ if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
+ fileName += QLatin1String(".app/Contents/MacOS/") + fileName;
+#elif defined(Q_OS_WIN)
+ fileName += QLatin1String(".exe");
+#endif
+
+ QFileInfo fi2(targetDirectory + QDir::separator() + fileName);
+ if (fi2.exists()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("TargetDirectoryInUse"),
+ tr("Error"), tr("The directory you selected already "
+ "exists and contains an installation. Choose a different target for installation."));
+ return false;
+ }
+
+ QMessageBox::StandardButton bt =
+ MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), QLatin1String("OverwriteTargetDirectory"),
+ tr("Warning"), tr("You have selected an existing, non-empty directory for installation.\nNote that it will be "
+ "completely wiped on uninstallation of this application.\nIt is not advisable to install into "
+ "this directory as installation might fail.\nDo you want to continue?"), QMessageBox::Yes | QMessageBox::No);
+ return bt == QMessageBox::Yes;
+ } else if (fi.isFile() || fi.isSymLink()) {
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("WrongTargetDirectory"),
+ tr("Error"), tr("You have selected an existing file "
+ "or symlink, please choose a different target for installation."));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Returns a warning if the path to the target directory \a targetDirectory
+ is not set or if it is invalid.
+*/
+QString PackageManagerCore::targetDirWarning(const QString &targetDirectory) const
+{
+ if (targetDirectory.isEmpty())
+ return tr("The installation path cannot be empty, please specify a valid directory.");
+
+ QDir target(targetDirectory);
+ if (target.isRelative())
+ return tr("The installation path cannot be relative, please specify an absolute path.");
+
+ QString nativeTargetDir = QDir::toNativeSeparators(target.absolutePath());
+ if (!settings().allowNonAsciiCharacters()) {
+ for (int i = 0; i < nativeTargetDir.length(); ++i) {
+ if (nativeTargetDir.at(i).unicode() & 0xff80) {
+ return tr("The path or installation directory contains non ASCII characters. This "
+ "is currently not supported! Please choose a different path or installation "
+ "directory.");
+ }
+ }
+ }
+
+ target.setPath(target.canonicalPath());
+ if (!target.path().isEmpty() && (target == QDir::root() || target == QDir::home())) {
+ return tr("As the install directory is completely deleted, installing in %1 is forbidden.")
+ .arg(QDir::toNativeSeparators(target.path()));
+ }
+
+#ifdef Q_OS_WIN
+ // folder length (set by user) + maintenance tool name length (no extension) + extra padding
+ if ((nativeTargetDir.length()
+ + settings().maintenanceToolName().length() + 20) >= MAX_PATH) {
+ return tr("The path you have entered is too long, please make sure to "
+ "specify a valid path.");
+ }
+
+ static QRegularExpression reg(QLatin1String(
+ "^(?<drive>[a-zA-Z]:\\\\)|"
+ "^(\\\\\\\\(?<path>\\w+)\\\\)|"
+ "^(\\\\\\\\(?<ip>\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\\\)"));
+ const QRegularExpressionMatch regMatch = reg.match(nativeTargetDir);
+
+ const QString ipMatch = regMatch.captured(QLatin1String("ip"));
+ const QString pathMatch = regMatch.captured(QLatin1String("path"));
+ const QString driveMatch = regMatch.captured(QLatin1String("drive"));
+
+ if (ipMatch.isEmpty() && pathMatch.isEmpty() && driveMatch.isEmpty()) {
+ return tr("The path you have entered is not valid, please make sure to "
+ "specify a valid target.");
+ }
+
+ if (!driveMatch.isEmpty()) {
+ bool validDrive = false;
+ const QFileInfo drive(driveMatch);
+ foreach (const QFileInfo &driveInfo, QDir::drives()) {
+ if (drive == driveInfo) {
+ validDrive = true;
+ break;
+ }
+ }
+ if (!validDrive) { // right now we can only verify local drives
+ return tr("The path you have entered is not valid, please make sure to "
+ "specify a valid drive.");
+ }
+ nativeTargetDir = nativeTargetDir.mid(2);
+ }
+
+ if (nativeTargetDir.endsWith(QLatin1Char('.')))
+ return tr("The installation path must not end with '.', please specify a valid directory.");
+
+ QString ambiguousChars = QLatin1String("[\"~<>|?*!@#$%^&:,; ]"
+ "|(\\\\CON)(\\\\|$)|(\\\\PRN)(\\\\|$)|(\\\\AUX)(\\\\|$)|(\\\\NUL)(\\\\|$)|(\\\\COM\\d)(\\\\|$)|(\\\\LPT\\d)(\\\\|$)");
+#else // Q_OS_WIN
+ QString ambiguousChars = QStringLiteral("[~<>|?*!@#$%^&:,; \\\\]");
+#endif // Q_OS_WIN
+
+ if (settings().allowSpaceInPath())
+ ambiguousChars.remove(QLatin1Char(' '));
+
+ static QRegularExpression ambCharRegEx(ambiguousChars, QRegularExpression::CaseInsensitiveOption);
+ // check if there are not allowed characters in the target path
+ QRegularExpressionMatch match = ambCharRegEx.match(nativeTargetDir);
+ if (match.hasMatch()) {
+ return tr("The installation path must not contain \"%1\", "
+ "please specify a valid directory.").arg(match.captured(0));
+ }
+
+ return QString();
}
// -- QInstaller
@@ -849,7 +1279,9 @@ PackageManagerCore::PackageManagerCore()
/*!
Creates an installer or uninstaller and performs sanity checks on the operations specified
- by \a operations.
+ by \a operations. A hash table of variables to be stored as package manager core values
+ can be specified by \a params. Sets the current instance type to be either a GUI or CLI one based
+ on the value of \a commandLineInstance.
The magic marker \a magicmaker is a \c quint64 that identifies the type of the binary:
\c installer or \c uninstaller.
@@ -857,16 +1289,24 @@ PackageManagerCore::PackageManagerCore()
Creates and initializes a remote client. Requests administrator's rights for
QFile, QSettings, and QProcess operations. Calls \c init() with \a socketName, \a key,
and \a mode to set the server side authorization key.
+
+ The \a datFileName contains the corresponding .dat file name for the running
+ \c maintenance tool binary. \a datFileName can be empty if \c maintenance tool
+ fails to find it or if \c installer is run instead of \c maintenance tool.
*/
PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &operations,
- const QString &socketName, const QString &key, Protocol::Mode mode)
- : d(new PackageManagerCorePrivate(this, magicmaker, operations))
+ const QString &datFileName,
+ const QString &socketName, const QString &key, Protocol::Mode mode,
+ const QHash<QString, QString> &params, const bool commandLineInstance)
+ : d(new PackageManagerCorePrivate(this, magicmaker, operations, datFileName))
{
+ setCommandLineInstance(commandLineInstance);
Repository::registerMetaType(); // register, cause we stream the type as QVariant
qRegisterMetaType<QInstaller::PackageManagerCore::Status>("QInstaller::PackageManagerCore::Status");
qRegisterMetaType<QInstaller::PackageManagerCore::WizardPage>("QInstaller::PackageManagerCore::WizardPage");
- d->initialize(QHash<QString, QString>());
+ d->initialize(params);
+
// Creates and initializes a remote client, makes us get admin rights for QFile, QSettings
// and QProcess operations. Init needs to called to set the server side authorization key.
if (!d->isUpdater()) {
@@ -878,7 +1318,8 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB
// Sanity check to detect a broken installations with missing operations.
// Every installed package should have at least one MinimalProgress operation.
//
- QSet<QString> installedPackages = d->m_core->localInstalledPackages().keys().toSet();
+ const QStringList localPackageList = d->m_core->localInstalledPackages().keys();
+ QSet<QString> installedPackages(localPackageList.begin(), localPackageList.end());
QSet<QString> operationPackages;
foreach (QInstaller::Operation *operation, d->m_performedOperationsOld) {
if (operation->hasValue(QLatin1String("component")))
@@ -888,50 +1329,20 @@ PackageManagerCore::PackageManagerCore(qint64 magicmaker, const QList<OperationB
QSet<QString> packagesWithoutOperation = installedPackages - operationPackages;
QSet<QString> orphanedOperations = operationPackages - installedPackages;
if (!packagesWithoutOperation.isEmpty() || !orphanedOperations.isEmpty()) {
- qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.toList();
- qCritical() << "Orphaned operations" << orphanedOperations.toList();
+ qCritical() << "Operations missing for installed packages" << packagesWithoutOperation.values();
+ qCritical() << "Orphaned operations" << orphanedOperations.values();
qCritical() << "Your installation seems to be corrupted. Please consider re-installing from scratch, "
"remove the packages from components.xml which operations are missing, "
"or reinstall the packages.";
} else {
- qDebug() << "Operations sanity check succeeded.";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Operations sanity check succeeded.";
}
+ connect(this, &PackageManagerCore::metaJobProgress,
+ ProgressCoordinator::instance(), &ProgressCoordinator::printProgressPercentage);
+ connect(this, &PackageManagerCore::metaJobInfoMessage,
+ ProgressCoordinator::instance(), &ProgressCoordinator::printProgressMessage);
}
-class VerboseWriterAdminOutput : public VerboseWriterOutput
-{
-public:
- VerboseWriterAdminOutput(PackageManagerCore *core) : m_core(core) {}
-
- virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data)
- {
- bool gainedAdminRights = false;
-
- if (!RemoteClient::instance().isActive()) {
- m_core->gainAdminRights();
- gainedAdminRights = true;
- }
-
- RemoteFileEngine file;
- file.setFileName(fileName);
- if (file.open(openMode)) {
- file.write(data.constData(), data.size());
- file.close();
- if (gainedAdminRights)
- m_core->dropAdminRights();
- return true;
- }
-
- if (gainedAdminRights)
- m_core->dropAdminRights();
-
- return false;
- }
-
-private:
- PackageManagerCore *m_core;
-};
-
/*!
Destroys the instance.
*/
@@ -1030,6 +1441,26 @@ void PackageManagerCore::setNoForceInstallation(bool value)
/* static */
/*!
+ Returns \c true if components are not selected by default although
+ \c <Default> element is set in the package information file.
+*/
+bool PackageManagerCore::noDefaultInstallation()
+{
+ return sNoDefaultInstallation;
+}
+
+/* static */
+/*!
+ Overwrites the value specified for the component in the \c <Default>
+ element in the package information file with \a value. Setting \a value
+ to \c true unselects the components.
+*/
+void PackageManagerCore::setNoDefaultInstallation(bool value)
+{
+ sNoDefaultInstallation = value;
+}
+/* static */
+/*!
Returns \c true if a local repository should be created from binary content.
*/
bool PackageManagerCore::createLocalRepositoryFromBinary()
@@ -1047,6 +1478,30 @@ void PackageManagerCore::setCreateLocalRepositoryFromBinary(bool create)
sCreateLocalRepositoryFromBinary = create;
}
+/* static */
+/*!
+ Returns the maximum count of operations that should be run concurrently
+ at the given time.
+
+ Currently this affects only operations in the unpacking phase.
+*/
+int PackageManagerCore::maxConcurrentOperations()
+{
+ return sMaxConcurrentOperations;
+}
+
+/* static */
+/*!
+ Sets the maximum \a count of operations that should be run concurrently
+ at the given time. A value of \c 0 is synonym for automatic count.
+
+ Currently this affects only operations in the unpacking phase.
+*/
+void PackageManagerCore::setMaxConcurrentOperations(int count)
+{
+ sMaxConcurrentOperations = count;
+}
+
/*!
Returns \c true if the package manager is running and installed packages are
found. Otherwise, returns \c false.
@@ -1060,7 +1515,7 @@ bool PackageManagerCore::fetchLocalPackagesTree()
return false;
}
- LocalPackagesHash installedPackages = d->localInstalledPackages();
+ LocalPackagesMap installedPackages = d->localInstalledPackages();
if (installedPackages.isEmpty()) {
if (status() != Failure)
d->setStatus(Failure, tr("No installed packages found."));
@@ -1071,23 +1526,64 @@ bool PackageManagerCore::fetchLocalPackagesTree()
d->clearAllComponentLists();
QHash<QString, QInstaller::Component*> components;
+ QMap<QString, QString> treeNameComponents;
+
+ std::function<void(QList<LocalPackage> *, bool)> loadLocalPackages;
+ loadLocalPackages = [&](QList<LocalPackage> *treeNamePackages, bool firstRun) {
+ foreach (auto &package, (firstRun ? installedPackages.values() : *treeNamePackages)) {
+ if (firstRun && !package.treeName.first.isEmpty()) {
+ // Package has a tree name, leave for later
+ treeNamePackages->append(package);
+ continue;
+ }
- const QStringList &keys = installedPackages.keys();
- foreach (const QString &key, keys) {
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
- component->loadDataFromPackage(installedPackages.value(key));
- const QString &name = component->name();
- if (components.contains(name)) {
- qCritical("Cannot register component! Component with identifier %s already registered.",
- qPrintable(name));
- continue;
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
+ component->loadDataFromPackage(package);
+ QString name = component->treeName();
+ if (components.contains(name)) {
+ qCritical() << "Cannot register component" << component->name() << "with name" << name
+ << "! Component with identifier" << name << "already exists.";
+ // Conflicting original name, skip.
+ if (component->value(scTreeName).isEmpty())
+ continue;
+
+ // Conflicting tree name, check if we can add with original name.
+ name = component->name();
+ if (!settings().allowUnstableComponents() || components.contains(name))
+ continue;
+
+ qCDebug(lcInstallerInstallLog)
+ << "Registering component with the original indetifier:" << name;
+ component->removeValue(scTreeName);
+ const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
+ }
+ const QString treeName = component->value(scTreeName);
+ if (!treeName.isEmpty())
+ treeNameComponents.insert(component->name(), treeName);
+
+ components.insert(name, component.release());
}
- components.insert(name, component.take());
+ // Second pass with leftover packages
+ if (firstRun)
+ loadLocalPackages(treeNamePackages, false);
+ };
+
+ {
+ // Loading package data is performed in two steps: first, components without
+ // - and then components with tree names. This is to ensure components with tree
+ // names do not replace other components when registering fails to a name conflict.
+ QList<LocalPackage> treeNamePackagesTmp;
+ loadLocalPackages(&treeNamePackagesTmp, true);
}
+ createAutoTreeNames(components, treeNameComponents);
+
if (!d->buildComponentTree(components, false))
return false;
+ d->commitPendingUnstableComponents();
updateDisplayVersions(scDisplayVersion);
emit finishAllComponentsReset(d->m_rootComponents);
@@ -1099,7 +1595,7 @@ bool PackageManagerCore::fetchLocalPackagesTree()
/*!
Returns a list of local installed packages. The list can be empty.
*/
-LocalPackagesHash PackageManagerCore::localInstalledPackages()
+LocalPackagesMap PackageManagerCore::localInstalledPackages()
{
return d->localInstalledPackages();
}
@@ -1112,10 +1608,11 @@ void PackageManagerCore::networkSettingsChanged()
cancelMetaInfoJob();
d->m_updates = false;
+ d->m_aliases = false;
d->m_repoFetched = false;
d->m_updateSourcesAdded = false;
- if (isMaintainer() ) {
+ if (!isInstaller()) {
bool gainedAdminRights = false;
if (!directoryWritable(d->targetDir())) {
gainAdminRights();
@@ -1170,33 +1667,60 @@ PackagesList PackageManagerCore::remotePackages()
*/
bool PackageManagerCore::fetchCompressedPackagesTree()
{
- const LocalPackagesHash installedPackages = d->localInstalledPackages();
+ const LocalPackagesMap installedPackages = d->localInstalledPackages();
if (!isInstaller() && status() == Failure)
return false;
- if (!d->fetchMetaInformationFromCompressedRepositories())
+ if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true, true)) {
+ if (!d->addUpdateResourcesFromRepositories(true)) {
return false;
}
- PackagesList packages;
- const PackagesList &compPackages = d->compressedPackages();
- if (compPackages.isEmpty())
+ const PackagesList &packages = d->remotePackages();
+ if (packages.isEmpty())
return false;
- packages.append(compPackages);
- const PackagesList &rPackages = d->remotePackages();
- packages.append(rPackages);
return fetchPackagesTree(packages, installedPackages);
}
+bool PackageManagerCore::fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched)
+{
+ auto checkComponents = [&]() {
+ if (!fetchRemotePackagesTree(components))
+ return false;
+ return true;
+ };
+
+ if (!checkComponents()) {
+ // error when fetching packages tree
+ if (status() != NoPackagesFound)
+ return false;
+ //retry fetching packages with all categories enabled
+ fallBackReposFetched = true;
+ if (!d->enableAllCategories())
+ return false;
+
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << "Components not found with the current selection."
+ << "Searching from additional repositories";
+ if (!ProductKeyCheck::instance()->securityWarning().isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << ProductKeyCheck::instance()->securityWarning();
+ }
+ if (!checkComponents()) {
+ return false;
+ }
+ }
+ return true;
+}
+
/*!
Checks for packages to install. Returns \c true if newer versions exist
- and they can be installed.
+ and they can be installed. Returns \c false if not \a components are found
+ for install, or if error occurred when fetching and generating package tree.
*/
-bool PackageManagerCore::fetchRemotePackagesTree()
+bool PackageManagerCore::fetchRemotePackagesTree(const QStringList& components)
{
d->setStatus(Running);
@@ -1210,51 +1734,64 @@ bool PackageManagerCore::fetchRemotePackagesTree()
return false;
}
- const LocalPackagesHash installedPackages = d->localInstalledPackages();
+ const LocalPackagesMap installedPackages = d->localInstalledPackages();
if (!isInstaller() && status() == Failure)
return false;
if (!d->fetchMetaInformationFromRepositories())
return false;
- if (!d->fetchMetaInformationFromCompressedRepositories())
+ if (!d->fetchMetaInformationFromRepositories(DownloadType::CompressedPackage))
return false;
- if (!d->addUpdateResourcesFromRepositories(true))
+ if (!d->addUpdateResourcesFromRepositories())
return false;
const PackagesList &packages = d->remotePackages();
- if (packages.isEmpty())
+ if (packages.isEmpty()) {
+ d->setStatus(PackageManagerCore::NoPackagesFound);
+ return false;
+ }
+
+ if (!d->installablePackagesFound(components))
return false;
+ d->m_componentsToBeInstalled = components;
return fetchPackagesTree(packages, installedPackages);
}
-bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const LocalPackagesHash installedPackages) {
+bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const LocalPackagesMap installedPackages) {
bool success = false;
if (!isUpdater()) {
success = fetchAllPackages(packages, installedPackages);
- if (success && !d->statusCanceledOrFailed() && isPackageManager()) {
+ if (d->statusCanceledOrFailed())
+ return false;
+ if (success && isPackageManager()) {
foreach (Package *const update, packages) {
- if (update->data(scEssential, scFalse).toString().toLower() == scTrue) {
+ bool essentialUpdate = (update->data(scEssential, scFalse).toString().toLower() == scTrue);
+ bool forcedUpdate = (update->data(scForcedUpdate, scFalse).toString().toLower() == scTrue);
+ if (essentialUpdate || forcedUpdate) {
const QString name = update->data(scName).toString();
- if (!installedPackages.contains(name)) {
+ // 'Essential' package not installed, install.
+ if (essentialUpdate && !installedPackages.contains(name)) {
success = false;
- continue; // unusual, the maintenance tool should always be available
+ continue;
}
+ // 'Forced update' package not installed, no update needed
+ if (forcedUpdate && !installedPackages.contains(name))
+ continue;
const LocalPackage localPackage = installedPackages.value(name);
- const QString updateVersion = update->data(scVersion).toString();
- if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0)
- continue; // remote version equals or is less than the installed maintenance tool
+ if (!d->packageNeedsUpdate(localPackage, update))
+ continue;
const QDate updateDate = update->data(scReleaseDate).toDate();
if (localPackage.lastUpdateDate >= updateDate)
continue; // remote release date equals or is less than the installed maintenance tool
success = false;
- break; // we found a newer version of the maintenance tool
+ break; // we found a newer version of the forced/essential update package
}
}
@@ -1273,11 +1810,12 @@ bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const L
if (success && !d->statusCanceledOrFailed())
d->setStatus(Success);
+ emit componentsRecalculated();
return success;
}
/*!
- \fn PackageManagerCore::addWizardPage(QInstaller::Component * component, const QString & name, int page)
+ \fn QInstaller::PackageManagerCore::addWizardPage(QInstaller::Component * component, const QString & name, int page)
Adds the widget with object name \a name registered by \a component as a new page
into the installer's GUI wizard. The widget is added before \a page.
@@ -1290,15 +1828,19 @@ bool PackageManagerCore::fetchPackagesTree(const PackagesList &packages, const L
*/
bool PackageManagerCore::addWizardPage(Component *component, const QString &name, int page)
{
- if (QWidget* const widget = component->userInterface(name)) {
- emit wizardPageInsertionRequested(widget, static_cast<WizardPage>(page));
- return true;
+ if (!isCommandLineInstance()) {
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardPageInsertionRequested(widget, static_cast<WizardPage>(page));
+ return true;
+ }
+ } else {
+ qCDebug(QInstaller::lcDeveloperBuild) << "Headless installation: skip wizard page addition: " << name;
}
return false;
}
/*!
- \fn PackageManagerCore::removeWizardPage(QInstaller::Component * component, const QString & name)
+ \fn QInstaller::PackageManagerCore::removeWizardPage(QInstaller::Component * component, const QString & name)
Removes the widget with the object name \a name previously added to the installer's wizard
by \a component.
@@ -1310,9 +1852,13 @@ bool PackageManagerCore::addWizardPage(Component *component, const QString &name
*/
bool PackageManagerCore::removeWizardPage(Component *component, const QString &name)
{
- if (QWidget* const widget = component->userInterface(name)) {
- emit wizardPageRemovalRequested(widget);
- return true;
+ if (!isCommandLineInstance()) {
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardPageRemovalRequested(widget);
+ return true;
+ }
+ } else {
+ qCDebug(QInstaller::lcDeveloperBuild) << "Headless installation: skip wizard page removal: " << name;
}
return false;
}
@@ -1334,7 +1880,7 @@ bool PackageManagerCore::setDefaultPageVisible(int page, bool visible)
}
/*!
- \fn PackageManagerCore::setValidatorForCustomPage(QInstaller::Component * component, const QString & name, const QString & callbackName)
+ \fn QInstaller::PackageManagerCore::setValidatorForCustomPage(QInstaller::Component * component, const QString & name, const QString & callbackName)
Sets a validator for the custom page specified by \a name and \a callbackName
for the component \a component.
@@ -1355,10 +1901,33 @@ void PackageManagerCore::setValidatorForCustomPage(Component *component, const Q
}
/*!
- \fn PackageManagerCore::addWizardPageItem(QInstaller::Component * component, const QString & name, int page)
+ Selects the component with \a id.
+ \sa {installer::selectComponent}{installer.selectComponent}
+ \sa deselectComponent()
+*/
+void PackageManagerCore::selectComponent(const QString &id)
+{
+ d->setComponentSelection(id, Qt::Checked);
+}
+
+/*!
+ Deselects the component with \a id.
+ \sa {installer::deselectComponent}{installer.deselectComponent}
+ \sa selectComponent()
+*/
+void PackageManagerCore::deselectComponent(const QString &id)
+{
+ d->setComponentSelection(id, Qt::Unchecked);
+}
+
+/*!
+ \fn QInstaller::PackageManagerCore::addWizardPageItem(QInstaller::Component * component, const QString & name,
+ int page, int position)
Adds the widget with the object name \a name registered by \a component as a GUI element
- into the installer's GUI wizard. The widget is added on \a page.
+ into the installer's GUI wizard. The widget is added on \a page ordered by
+ \a position number. If several widgets are added to the same page, the widget
+ with lower \a position number will be inserted on top.
See \l{Controller Scripting} for the possible values of \a page.
@@ -1368,17 +1937,21 @@ void PackageManagerCore::setValidatorForCustomPage(Component *component, const Q
\sa {installer::addWizardPageItem}{installer.addWizardPageItem}
\sa removeWizardPageItem(), wizardWidgetInsertionRequested()
*/
-bool PackageManagerCore::addWizardPageItem(Component *component, const QString &name, int page)
+bool PackageManagerCore::addWizardPageItem(Component *component, const QString &name, int page, int position)
{
- if (QWidget* const widget = component->userInterface(name)) {
- emit wizardWidgetInsertionRequested(widget, static_cast<WizardPage>(page));
- return true;
+ if (!isCommandLineInstance()) {
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardWidgetInsertionRequested(widget, static_cast<WizardPage>(page), position);
+ return true;
+ }
+ } else {
+ qCDebug(QInstaller::lcDeveloperBuild) << "Headless installation: skip wizard page item addition: " << name;
}
return false;
}
/*!
- \fn PackageManagerCore::removeWizardPageItem(QInstaller::Component * component, const QString & name)
+ \fn QInstaller::PackageManagerCore::removeWizardPageItem(QInstaller::Component * component, const QString & name)
Removes the widget with the object name \a name previously added to the installer's wizard
by \a component.
@@ -1391,9 +1964,11 @@ bool PackageManagerCore::addWizardPageItem(Component *component, const QString &
*/
bool PackageManagerCore::removeWizardPageItem(Component *component, const QString &name)
{
- if (QWidget* const widget = component->userInterface(name)) {
- emit wizardWidgetRemovalRequested(widget);
- return true;
+ if (!isCommandLineInstance()) {
+ if (QWidget* const widget = component->userInterface(name)) {
+ emit wizardWidgetRemovalRequested(widget);
+ return true;
+ }
}
return false;
}
@@ -1415,7 +1990,8 @@ void PackageManagerCore::addUserRepositories(const QStringList &repositories)
/*!
Sets additional \a repositories for this instance of the installer or updater
- if \a replace is \c false. Will be removed after invoking it again.
+ if \a replace is \c false. \a compressed repositories can be added as well.
+ Will be removed after invoking it again.
\sa {installer::setTemporaryRepositories}{installer.setTemporaryRepositories}
\sa addUserRepositories()
@@ -1429,24 +2005,82 @@ void PackageManagerCore::setTemporaryRepositories(const QStringList &repositorie
settings().setTemporaryRepositories(repositorySet, replace);
}
-/*!
- Checks whether the downloader should try to download SHA-1 checksums for
- archives and returns the checksums.
-*/
-bool PackageManagerCore::testChecksum() const
+bool PackageManagerCore::addQBspRepositories(const QStringList &repositories)
{
- return d->m_testChecksum;
+ QSet<Repository> set;
+ foreach (QString fileName, repositories) {
+ Repository repository = Repository::fromUserInput(fileName, true);
+ repository.setEnabled(true);
+ set.insert(repository);
+ }
+ if (set.count() > 0) {
+ settings().addTemporaryRepositories(set, false);
+ return true;
+ }
+ return false;
}
-/*!
- The \a test argument determines whether the downloader should try to
- download SHA-1 checksums for archives.
-*/
-void PackageManagerCore::setTestChecksum(bool test)
+bool PackageManagerCore::validRepositoriesAvailable() const
+{
+ foreach (const Repository &repo, settings().repositories()) {
+ if (repo.isEnabled() && repo.isValid()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PackageManagerCore::setAllowCompressedRepositoryInstall(bool allow)
{
- d->m_testChecksum = test;
+ d->m_allowCompressedRepositoryInstall = allow;
}
+bool PackageManagerCore::allowCompressedRepositoryInstall() const
+{
+ return d->m_allowCompressedRepositoryInstall;
+}
+
+bool PackageManagerCore::showRepositoryCategories() const
+{
+ bool showCagetories = settings().repositoryCategories().count() > 0 && !isOfflineOnly() && !isUpdater();
+ if (showCagetories)
+ settings().setAllowUnstableComponents(true);
+ return showCagetories;
+}
+
+QVariantMap PackageManagerCore::organizedRepositoryCategories() const
+{
+ QVariantMap map;
+ QSet<RepositoryCategory> categories = settings().repositoryCategories();
+ foreach (const RepositoryCategory &category, categories)
+ map.insert(category.displayname(), QVariant::fromValue(category));
+ return map;
+}
+
+void PackageManagerCore::enableRepositoryCategory(const QString &repositoryName, bool enable)
+{
+ QMap<QString, RepositoryCategory> organizedRepositoryCategories = settings().organizedRepositoryCategories();
+
+ QMap<QString, RepositoryCategory>::iterator i = organizedRepositoryCategories.find(repositoryName);
+ while (i != organizedRepositoryCategories.end() && i.key() == repositoryName) {
+ d->enableRepositoryCategory(i.value(), enable);
+ i++;
+ }
+}
+
+void PackageManagerCore::runProgram()
+{
+ const QString program = replaceVariables(value(scRunProgram));
+
+ const QStringList args = replaceVariables(values(scRunProgramArguments));
+ if (program.isEmpty())
+ return;
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "starting" << program << args;
+ QProcess::startDetached(program, args);
+}
+
+
/*!
Returns the script engine that prepares and runs the component scripts.
@@ -1475,6 +2109,10 @@ ScriptEngine *PackageManagerCore::controlScriptEngine() const
*/
void PackageManagerCore::appendRootComponent(Component *component)
{
+ // For normal installer runs components aren't appended after model reset
+ if (Q_UNLIKELY(!d->m_componentByNameHash.isEmpty()))
+ d->m_componentByNameHash.clear();
+
d->m_rootComponents.append(component);
emit componentAdded(component);
}
@@ -1513,8 +2151,9 @@ void PackageManagerCore::appendRootComponent(Component *component)
/*!
Returns a list of components depending on the component types passed in \a mask.
+ Optionally, a \a regexp expression can be used to further filter the listed packages.
*/
-QList<Component *> PackageManagerCore::components(ComponentTypes mask) const
+QList<Component *> PackageManagerCore::components(ComponentTypes mask, const QString &regexp) const
{
QList<Component *> components;
@@ -1535,6 +2174,17 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask) const
// No descendants here, updates are always a flat list and cannot have children!
}
+ if (!regexp.isEmpty()) {
+ QRegularExpression re(regexp);
+ QList<Component*>::iterator iter = components.begin();
+ while (iter != components.end()) {
+ if (!re.match((*iter)->name()).hasMatch())
+ iter = components.erase(iter);
+ else
+ iter++;
+ }
+ }
+
return components;
}
@@ -1544,6 +2194,10 @@ QList<Component *> PackageManagerCore::components(ComponentTypes mask) const
*/
void PackageManagerCore::appendUpdaterComponent(Component *component)
{
+ // For normal installer runs components aren't appended after model reset
+ if (Q_UNLIKELY(!d->m_componentByNameHash.isEmpty()))
+ d->m_componentByNameHash.clear();
+
component->setUpdateAvailable(true);
d->m_updaterComponents.append(component);
emit componentAdded(component);
@@ -1557,7 +2211,39 @@ void PackageManagerCore::appendUpdaterComponent(Component *component)
*/
Component *PackageManagerCore::componentByName(const QString &name) const
{
- return componentByName(name, components(ComponentType::AllNoReplacements));
+ if (name.isEmpty())
+ return nullptr;
+
+ if (d->m_componentByNameHash.isEmpty()) {
+ // We can avoid the linear lookups from the component list by creating
+ // a <name,component> hash once, and reusing it on subsequent calls.
+ const QList<Component *> componentsList = components(ComponentType::AllNoReplacements);
+ for (Component *component : componentsList)
+ d->m_componentByNameHash.insert(component->name(), component);
+ }
+
+ QString fixedVersion;
+ QString fixedName;
+
+ parseNameAndVersion(name, &fixedName, &fixedVersion);
+
+ Component *component = d->m_componentByNameHash.value(fixedName);
+ if (!component)
+ return nullptr;
+
+ if (componentMatches(component, fixedName, fixedVersion))
+ return component;
+
+ return nullptr;
+}
+
+/*!
+ Searches for a component alias matching \a name and returns it.
+ If no alias matches the name, \c nullptr is returned.
+*/
+ComponentAlias *PackageManagerCore::aliasByName(const QString &name) const
+{
+ return d->m_componentAliases.value(name);
}
/*!
@@ -1585,14 +2271,25 @@ Component *PackageManagerCore::componentByName(const QString &name, const QList<
return nullptr;
}
-bool PackageManagerCore::directoryWritable(const QString &path) const
+/*!
+ Returns an array of all components currently available. If the repository
+ metadata have not been fetched yet, the array will be empty. Optionally, a
+ \a regexp expression can be used to further filter the listed packages.
+
+ \sa {installer::components}{installer.components}
+ */
+QList<Component *> PackageManagerCore::components(const QString &regexp) const
{
- return d->directoryWritable(path);
+ return components(PackageManagerCore::ComponentType::All, regexp);
}
-bool PackageManagerCore::subdirectoriesWritable(const QString &path) const
+/*!
+ Returns \c true if directory specified by \a path is writable by
+ the current user.
+*/
+bool PackageManagerCore::directoryWritable(const QString &path) const
{
- return d->subdirectoriesWritable(path);
+ return d->directoryWritable(path);
}
/*!
@@ -1624,8 +2321,27 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const
}
/*!
- Determines which components to install based on the current run mode and returns an
- ordered list of components to install. Also auto installed dependencies are resolved.
+ Returns a list of component aliases that are marked for installation.
+ The list can be empty.
+*/
+QList<ComponentAlias *> PackageManagerCore::aliasesMarkedForInstallation() const
+{
+ if (isUpdater()) // Aliases not supported on update at the moment
+ return QList<ComponentAlias *>();
+
+ QList<ComponentAlias *> markedForInstallation;
+ for (auto *alias : qAsConst(d->m_componentAliases)) {
+ if (alias && alias->isSelected())
+ markedForInstallation.append(alias);
+ }
+
+ return markedForInstallation;
+}
+
+/*!
+ Determines which components to install based on the current run mode, including component aliases,
+ dependencies and automatic dependencies. Returns \c true on success, \c false otherwise.
+
The aboutCalculateComponentsToInstall() signal is emitted
before the calculation starts, the finishedCalculateComponentsToInstall()
signal once all calculations are done.
@@ -1636,16 +2352,15 @@ QList<Component *> PackageManagerCore::componentsMarkedForInstallation() const
bool PackageManagerCore::calculateComponentsToInstall() const
{
emit aboutCalculateComponentsToInstall();
- if (!d->m_componentsToInstallCalculated) {
- d->clearInstallerCalculator();
- QList<Component*> selectedComponentsToInstall = componentsMarkedForInstallation();
- d->storeCheckState();
- d->m_componentsToInstallCalculated =
- d->installerCalculator()->appendComponentsToInstall(selectedComponentsToInstall);
- }
+ d->clearInstallerCalculator();
+
+ const bool calculated = d->installerCalculator()->solve();
+
+ d->updateComponentInstallActions();
+
emit finishedCalculateComponentsToInstall();
- return d->m_componentsToInstallCalculated;
+ return calculated;
}
/*!
@@ -1653,40 +2368,56 @@ bool PackageManagerCore::calculateComponentsToInstall() const
*/
QList<Component*> PackageManagerCore::orderedComponentsToInstall() const
{
- return d->installerCalculator()->orderedComponentsToInstall();
+ return d->installerCalculator()->resolvedComponents();
}
-bool PackageManagerCore::calculateComponents(QString *displayString)
+/*!
+ Returns a HTML-formatted description of the reasons each component is about
+ to be installed or uninstalled, or a description of the error occurred while
+ calculating components to install and uninstall.
+*/
+QString PackageManagerCore::componentResolveReasons() const
{
QString htmlOutput;
- QString lastInstallReason;
- if (!calculateComponentsToUninstall() ||
- !calculateComponentsToInstall()) {
+ if (!componentsToInstallError().isEmpty()) {
htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>")
- .arg(tr("Cannot resolve all dependencies.")));
+ .arg(tr("Cannot resolve all dependencies.")));
//if we have a missing dependency or a recursion we can display it
- if (!componentsToInstallError().isEmpty()) {
- htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
- componentsToInstallError()));
- }
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
+ componentsToInstallError()));
htmlOutput.append(QLatin1String("</ul>"));
- if (displayString)
- *displayString = htmlOutput;
- return false;
+ return htmlOutput;
}
- // In case of updater mode we don't uninstall components.
- if (!isUpdater()) {
- QList<Component*> componentsToRemove = componentsToUninstall();
- if (!componentsToRemove.isEmpty()) {
- htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(tr("Components about to "
- "be removed.")));
- foreach (Component *component, componentsToRemove)
- htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name()));
+ if (!componentsToUninstallError().isEmpty()) {
+ htmlOutput.append(QString::fromLatin1("<h2><font color=\"red\">%1</font></h2><ul>")
+ .arg(tr("Cannot resolve components to uninstall.")));
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(
+ componentsToUninstallError()));
+ htmlOutput.append(QLatin1String("</ul>"));
+ return htmlOutput;
+ }
+
+ QList<Component*> componentsToRemove = componentsToUninstall();
+ if (!componentsToRemove.isEmpty()) {
+ QMap<QString, QStringList> orderedUninstallReasons;
+ htmlOutput.append(QString::fromLatin1("<h3>%1</h3><ul>").arg(tr("Components about to "
+ "be removed:")));
+ foreach (Component *component, componentsToRemove) {
+ const QString reason = uninstallReason(component);
+ QStringList value = orderedUninstallReasons.value(reason);
+ orderedUninstallReasons.insert(reason, value << component->name());
+ }
+ for (auto &reason : orderedUninstallReasons.keys()) {
+ htmlOutput.append(QString::fromLatin1("<h4>%1</h4><ul>").arg(reason));
+ foreach (const QString componentName, orderedUninstallReasons.value(reason))
+ htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(componentName));
htmlOutput.append(QLatin1String("</ul>"));
}
+ htmlOutput.append(QLatin1String("</ul>"));
}
+ QString lastInstallReason;
foreach (Component *component, orderedComponentsToInstall()) {
const QString reason = installReason(component);
if (lastInstallReason != reason) {
@@ -1697,38 +2428,46 @@ bool PackageManagerCore::calculateComponents(QString *displayString)
}
htmlOutput.append(QString::fromLatin1("<li> %1 </li>").arg(component->name()));
}
- if (displayString)
- *displayString = htmlOutput;
- return true;
+ return htmlOutput;
}
/*!
- Calculates a list of components to uninstall based on the current run mode.
+ Calculates a list of components to uninstall.
+
The aboutCalculateComponentsToUninstall() signal is emitted
before the calculation starts, the finishedCalculateComponentsToUninstall() signal once all
- calculations are done. Always returns \c true.
+ calculations are done. Returns \c true on success, \c false otherwise.
\sa {installer::calculateComponentsToUninstall}{installer.calculateComponentsToUninstall}
*/
bool PackageManagerCore::calculateComponentsToUninstall() const
{
emit aboutCalculateComponentsToUninstall();
- if (!isUpdater()) {
- // hack to avoid removing needed dependencies
- QSet<Component*> componentsToInstall = d->installerCalculator()->orderedComponentsToInstall().toSet();
- QList<Component*> componentsToUninstall;
- foreach (Component *component, components(ComponentType::All)) {
- if (component->uninstallationRequested() && !componentsToInstall.contains(component))
- componentsToUninstall.append(component);
- }
+ d->clearUninstallerCalculator();
+ const QList<Component *> componentsToInstallList = d->installerCalculator()->resolvedComponents();
- d->clearUninstallerCalculator();
- d->storeCheckState();
- d->uninstallerCalculator()->appendComponentsToUninstall(componentsToUninstall);
+ QList<Component *> selectedComponentsToUninstall;
+ foreach (Component* component, components(PackageManagerCore::ComponentType::Replacements)) {
+ // Uninstall the component if replacement is selected for install or update
+ QPair<Component*, Component*> comp = d->componentsToReplace().value(component->name());
+ if (comp.first && d->m_installerCalculator->resolvedComponents().contains(comp.first)) {
+ d->uninstallerCalculator()->insertResolution(component,
+ CalculatorBase::Resolution::Replaced, comp.first->name());
+ selectedComponentsToUninstall.append(comp.second);
+ }
+ }
+ foreach (Component *component, components(PackageManagerCore::ComponentType::AllNoReplacements)) {
+ if (component->uninstallationRequested() && !componentsToInstallList.contains(component))
+ selectedComponentsToUninstall.append(component);
}
+ const bool componentsToUninstallCalculated =
+ d->uninstallerCalculator()->solve(selectedComponentsToUninstall);
+
+ d->updateComponentInstallActions();
+
emit finishedCalculateComponentsToUninstall();
- return true;
+ return componentsToUninstallCalculated;
}
/*!
@@ -1740,7 +2479,7 @@ bool PackageManagerCore::calculateComponentsToUninstall() const
*/
QList<Component *> PackageManagerCore::componentsToUninstall() const
{
- return d->uninstallerCalculator()->componentsToUninstall().toList();
+ return d->uninstallerCalculator()->resolvedComponents();
}
/*!
@@ -1748,7 +2487,15 @@ QList<Component *> PackageManagerCore::componentsToUninstall() const
*/
QString PackageManagerCore::componentsToInstallError() const
{
- return d->installerCalculator()->componentsToInstallError();
+ return d->installerCalculator()->error();
+}
+
+/*!
+ Returns errors found in the components that are marked for uninstallation.
+*/
+QString PackageManagerCore::componentsToUninstallError() const
+{
+ return d->uninstallerCalculator()->error();
}
/*!
@@ -1762,7 +2509,23 @@ QString PackageManagerCore::componentsToInstallError() const
*/
QString PackageManagerCore::installReason(Component *component) const
{
- return d->installerCalculator()->installReason(component);
+ return d->installerCalculator()->resolutionText(component);
+}
+
+/*!
+ Returns the reason why \a component needs to be uninstalled:
+
+ \list
+ \li The component was scheduled for uninstallation.
+ \li The component was replaced by another component.
+ \li The component is virtual and its dependencies are uninstalled.
+ \li The components dependencies are uninstalled.
+ \li The components autodependencies are uninstalled.
+ \endlist
+*/
+QString PackageManagerCore::uninstallReason(Component *component) const
+{
+ return d->uninstallerCalculator()->resolutionText(component);
}
/*!
@@ -1795,6 +2558,68 @@ QList<Component*> PackageManagerCore::dependees(const Component *_component) con
}
/*!
+ Returns true if components which are about to be installed or updated
+ are dependent on \a component.
+*/
+bool PackageManagerCore::isDependencyForRequestedComponent(const Component *component) const
+{
+ if (!component)
+ return false;
+
+ const QList<QInstaller::Component *> availableComponents = components(ComponentType::All);
+ if (availableComponents.isEmpty())
+ return false;
+
+ QString name;
+ QString version;
+ for (Component *availableComponent : availableComponents) {
+ if (!availableComponent) {
+ continue;
+ }
+ // 1. In updater mode, component to be updated might have new dependencies
+ // Check if the dependency is still needed
+ // 2. If component is selected and not installed, check if the dependency is needed
+ if (availableComponent->isSelected()
+ && ((isUpdater() && availableComponent->isInstalled())
+ || (isPackageManager() && !availableComponent->isInstalled()))) {
+ const QStringList &dependencies = availableComponent->dependencies();
+ foreach (const QString &dependency, dependencies) {
+ parseNameAndVersion(dependency, &name, &version);
+ if (componentMatches(component, name, version)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+/*!
+ Returns a list of local components which are dependent on \a component.
+*/
+QStringList PackageManagerCore::localDependenciesToComponent(const Component *component) const
+{
+ if (!component)
+ return QStringList();
+
+ QStringList dependents;
+ QString name;
+ QString version;
+
+ QMap<QString, LocalPackage> localPackages = d->m_localPackageHub->localPackages();
+ for (const KDUpdater::LocalPackage &localPackage : qAsConst(localPackages)) {
+ for (const QString &dependency : localPackage.dependencies) {
+ parseNameAndVersion(dependency, &name, &version);
+ if (componentMatches(component, name, version)) {
+ dependents.append(localPackage.name);
+ }
+ }
+ }
+ return dependents;
+}
+
+/*!
Returns the default component model.
*/
ComponentModel *PackageManagerCore::defaultComponentModel() const
@@ -1803,9 +2628,13 @@ ComponentModel *PackageManagerCore::defaultComponentModel() const
if (!d->m_defaultModel) {
d->m_defaultModel = componentModel(const_cast<PackageManagerCore*> (this),
QLatin1String("AllComponentsModel"));
+
+ connect(this, &PackageManagerCore::startAllComponentsReset, [&] {
+ d->m_defaultModel->reset();
+ });
+ connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel,
+ &ComponentModel::reset);
}
- connect(this, &PackageManagerCore::finishAllComponentsReset, d->m_defaultModel,
- &ComponentModel::setRootComponents);
return d->m_defaultModel;
}
@@ -1818,69 +2647,587 @@ ComponentModel *PackageManagerCore::updaterComponentModel() const
if (!d->m_updaterModel) {
d->m_updaterModel = componentModel(const_cast<PackageManagerCore*> (this),
QLatin1String("UpdaterComponentsModel"));
+
+ connect(this, &PackageManagerCore::startUpdaterComponentsReset, [&] {
+ d->m_updaterModel->reset();
+ });
+ connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel,
+ &ComponentModel::reset);
}
- connect(this, &PackageManagerCore::finishUpdaterComponentsReset, d->m_updaterModel,
- &ComponentModel::setRootComponents);
return d->m_updaterModel;
}
-void PackageManagerCore::updateComponentsSilently()
+/*!
+ Returns the proxy model
+*/
+
+ComponentSortFilterProxyModel *PackageManagerCore::componentSortFilterProxyModel()
{
- //Check if there are processes running in the install
- QStringList excludeFiles;
- excludeFiles.append(maintenanceToolName());
+ if (!d->m_componentSortFilterProxyModel) {
+ d->m_componentSortFilterProxyModel = new ComponentSortFilterProxyModel(this);
+ d->m_componentSortFilterProxyModel->setRecursiveFilteringEnabled(true);
+ d->m_componentSortFilterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ }
+ return d->m_componentSortFilterProxyModel;
+}
- QStringList runningProcesses = d->runningInstallerProcesses(excludeFiles);
- if (!runningProcesses.isEmpty()) {
- qDebug() << "Unable to update components. Please stop these processes: "
- << runningProcesses << " and try again.";
- return;
+/*!
+ Lists available packages filtered with \a regexp without GUI. Virtual
+ components are not listed unless set visible. Optionally, a \a filters
+ hash containing package information elements and regular expressions
+ can be used to further filter listed packages.
+
+ Returns \c true if matching packages were found, \c false otherwise.
+
+ \sa setVirtualComponentsVisible()
+*/
+bool PackageManagerCore::listAvailablePackages(const QString &regexp, const QHash<QString, QString> &filters)
+{
+ setPackageViewer();
+ d->enableAllCategories();
+ qCDebug(QInstaller::lcInstallerInstallLog)
+ << "Searching packages with regular expression:" << regexp;
+
+ ComponentModel *model = defaultComponentModel();
+ PackagesList packages;
+
+ if (!d->m_updates) {
+ d->fetchMetaInformationFromRepositories();
+ d->addUpdateResourcesFromRepositories();
+
+ packages = d->remotePackages();
+ if (!fetchAllPackages(packages, LocalPackagesMap())) {
+ qCWarning(QInstaller::lcInstallerInstallLog)
+ << "There was a problem with loading the package data.";
+ return false;
+ }
+ } else {
+ // No need to fetch metadata again
+ packages = d->remotePackages();
}
- autoAcceptMessageBoxes();
+ QRegularExpression re(regexp);
+ re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
- //Prevent infinite loop if installation for some reason fails.
- setMessageBoxAutomaticAnswer(QLatin1String("installationErrorWithRetry"), QMessageBox::Cancel);
+ PackagesList matchedPackages;
+ foreach (Package *package, qAsConst(packages)) {
+ const QString name = package->data(scName).toString();
+ Component *component = componentByName(name);
+ if (!component)
+ continue;
- fetchRemotePackagesTree();
+ const QModelIndex &idx = model->indexFromComponentName(component->treeName());
+ if (idx.isValid() && re.match(name).hasMatch()) {
+ bool ignoreComponent = false;
+ for (auto &key : filters.keys()) {
+ const QString elementValue = component->value(key);
+ QRegularExpression elementRegexp(filters.value(key));
+ elementRegexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+ if (elementValue.isEmpty() || !elementRegexp.match(elementValue).hasMatch()) {
+ ignoreComponent = true;
+ break;
+ }
+ }
+ if (!ignoreComponent)
+ matchedPackages.append(package);
+ }
+ }
+ if (matchedPackages.count() == 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No matching packages found.";
+ return false;
+ }
+
+ LoggingHandler::instance().printPackageInformation(matchedPackages, localInstalledPackages());
+ return true;
+}
+
+/*!
+ Lists available component aliases filtered with \a regexp without GUI. Virtual
+ aliases are not listed unless set visible.
+
+ Returns \c true if matching package aliases were found, \c false otherwise.
+
+ \sa setVirtualComponentsVisible()
+*/
+bool PackageManagerCore::listAvailableAliases(const QString &regexp)
+{
+ setPackageViewer();
+ d->enableAllCategories();
+ qCDebug(QInstaller::lcInstallerInstallLog)
+ << "Searching aliases with regular expression:" << regexp;
+
+ if (!d->buildComponentAliases())
+ return false;
+
+ QRegularExpression re(regexp);
+ re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
- const QList<QInstaller::Component*> componentList = components(
- ComponentType::Root | ComponentType::Descendants);
+ QList<ComponentAlias *> matchedAliases;
+ for (auto *alias : std::as_const(d->m_componentAliases)) {
+ if (!alias)
+ continue;
+
+ if (re.match(alias->name()).hasMatch()) {
+ if (alias->isVirtual() && !virtualComponentsVisible())
+ continue;
+
+ matchedAliases.append(alias);
+ }
+ }
+
+ if (matchedAliases.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No matching package aliases found.";
+ return false;
+ }
+
+ LoggingHandler::instance().printAliasInformation(matchedAliases);
+ return true;
+}
+
+bool PackageManagerCore::componentUninstallableFromCommandLine(const QString &componentName)
+{
+ // We will do a recursive check for every child this component has.
+ Component *component = componentByName(componentName);
+ const QList<Component*> childComponents = component->childItems();
+ foreach (const Component *childComponent, childComponents) {
+ if (!componentUninstallableFromCommandLine(childComponent->name()))
+ return false;
+ }
+ ComponentModel *model = defaultComponentModel();
+ const QModelIndex &idx = model->indexFromComponentName(component->treeName());
+ if (model->data(idx, Qt::CheckStateRole) == QVariant()) {
+ // Component cannot be unselected, check why
+ if (component->forcedInstallation()) {
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "Cannot uninstall ForcedInstallation component " << component->name();
+ } else if (component->autoDependencies().count() > 0) {
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot uninstall component "
+ << componentName << " because it is added as auto dependency to "
+ << component->autoDependencies().join(QLatin1Char(','));
+ } else if (component->isVirtual() && !virtualComponentsVisible()) {
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "Cannot uninstall virtual component " << component->name();
+ } else {
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "Cannot uninstall component " << component->name();
+ }
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ Tries to set \c Qt::CheckStateRole to \c Qt::Checked for given component \a names in the
+ default component model, and select given aliases in the \c names list.
+
+ Returns \c true if \a names contains at least one component or component alias
+ eligible for installation, otherwise returns \c false. An error message can be retrieved
+ with \a errorMessage.
+*/
+bool PackageManagerCore::checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound, bool fallbackReposFetched)
+{
+ bool installComponentsFound = false;
+
+ ComponentModel *model = defaultComponentModel();
+ foreach (const QString &name, names) {
+ Component *component = componentByName(name);
+ if (!component) {
+ // No such component, check if we have an alias by the name
+ if (ComponentAlias *alias = aliasByName(name)) {
+ if (alias->isUnstable()) {
+ errorMessage.append(tr("Cannot select alias %1. There was a problem loading this alias, "
+ "so it is marked unstable and cannot be selected.").arg(name) + QLatin1Char('\n'));
+ unstableAliasFound = true;
+ setCanceled();
+ return false;
+ } else if (alias->isVirtual()) {
+ errorMessage.append(tr("Cannot select %1. Alias is marked virtual, meaning it cannot "
+ "be selected manually.").arg(name) + QLatin1Char('\n'));
+ continue;
+ } else if (alias->missingOptionalComponents() && !fallbackReposFetched) {
+ unstableAliasFound = true;
+ setCanceled();
+ return false;
+ }
+
+ alias->setSelected(true);
+ installComponentsFound = true;
+ } else {
+ errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n'));
+ }
+
+ continue;
+ }
+ const QModelIndex &idx = model->indexFromComponentName(component->treeName());
+ if (idx.isValid()) {
+ if ((model->data(idx, Qt::CheckStateRole) == QVariant()) && !component->forcedInstallation()) {
+ // User cannot select the component, check why
+ if (component->autoDependencies().count() > 0) {
+ errorMessage.append(tr("Cannot install component %1. Component is installed only as automatic "
+ "dependency to %2.").arg(name, component->autoDependencies().join(QLatin1Char(','))) + QLatin1Char('\n'));
+ } else if (!component->isCheckable()) {
+ errorMessage.append(tr("Cannot install component %1. Component is not checkable, meaning you "
+ "have to select one of the subcomponents.").arg(name) + QLatin1Char('\n'));
+ } else if (component->isUnstable()) {
+ errorMessage.append(tr("Cannot install component %1. There was a problem loading this component, "
+ "so it is marked unstable and cannot be selected.").arg(name) + QLatin1Char('\n'));
+ }
+ } else if (component->isInstalled()) {
+ errorMessage.append(tr("Component %1 already installed").arg(name) + QLatin1Char('\n'));
+ } else {
+ model->setData(idx, Qt::Checked, Qt::CheckStateRole);
+ installComponentsFound = true;
+ }
+ } else {
+ auto isDescendantOfVirtual = [&]() {
+ Component *trace = component;
+ forever {
+ trace = trace->parentComponent();
+ if (!trace) {
+ // We already checked the root component if there is no parent
+ return false;
+ } else if (trace->isVirtual()) {
+ errorMessage.append(tr("Cannot install %1. Component is a descendant "
+ "of a virtual component %2.").arg(name, trace->name()) + QLatin1Char('\n'));
+ return true;
+ }
+ }
+ };
+ // idx is invalid and component valid when we have invisible virtual component
+ if (component->isVirtual())
+ errorMessage.append(tr("Cannot install %1. Component is virtual.").arg(name) + QLatin1Char('\n'));
+ else if (!isDescendantOfVirtual())
+ errorMessage.append(tr("Cannot install %1. Component not found.").arg(name) + QLatin1Char('\n'));
+ }
+ }
+ if (!installComponentsFound)
+ setCanceled();
+
+ return installComponentsFound;
+}
+
+/*!
+ Lists installed packages without GUI. List of packages can be filtered with \a regexp.
+*/
+void PackageManagerCore::listInstalledPackages(const QString &regexp)
+{
+ setPackageViewer();
+ LocalPackagesMap installedPackages = this->localInstalledPackages();
+
+ if (!regexp.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog)
+ << "Searching packages with regular expression:" << regexp;
+ }
+ QRegularExpression re(regexp);
+ re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+ const QStringList &keys = installedPackages.keys();
+ QList<LocalPackage> packages;
+ foreach (const QString &key, keys) {
+ KDUpdater::LocalPackage package = installedPackages.value(key);
+ if (re.match(package.name).hasMatch())
+ packages.append(package);
+ }
+ LoggingHandler::instance().printLocalPackageInformation(packages);
+}
+
+PackageManagerCore::Status PackageManagerCore::searchAvailableUpdates()
+{
+ setUpdater();
+ d->enableAllCategories();
+ if (!fetchRemotePackagesTree()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error();
+ return status();
+ }
+
+ const QList<QInstaller::Component *> availableUpdates =
+ components(QInstaller::PackageManagerCore::ComponentType::Root);
+ if (availableUpdates.isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "There are currently no updates available.";
+ return status();
+ }
+ QInstaller::LoggingHandler::instance().printUpdateInformation(availableUpdates);
+ return status();
+}
+
+/*!
+ Updates the selected components \a componentsToUpdate without GUI.
+ If essential components are found, then only those will be updated.
+ Returns PackageManagerCore installation status.
+*/
+PackageManagerCore::Status PackageManagerCore::updateComponentsSilently(const QStringList &componentsToUpdate)
+{
+ setUpdater();
+
+ ComponentModel *model = updaterComponentModel();
+
+ if (componentsToUpdate.isEmpty()) {
+ d->enableAllCategories();
+ fetchRemotePackagesTree();
+ } else {
+ bool fallbackReposFetched = false;
+ bool packagesFound = fetchPackagesWithFallbackRepositories(componentsToUpdate, fallbackReposFetched);
+
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available for update with the current selection.";
+ d->setStatus(Canceled);
+ return status();
+ }
+ }
+
+ // List contains components containing update, if essential found contains only essential component
+ const QList<QInstaller::Component*> componentList = componentsMarkedForInstallation();
if (componentList.count() == 0) {
- qDebug() << "No updates available.";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No updates available.";
+ setCanceled();
} else {
// Check if essential components are available (essential components are disabled).
// If essential components are found, update first essential updates,
// restart installer and install rest of the updates.
bool essentialUpdatesFound = false;
foreach (Component *component, componentList) {
- if (component->value(scEssential, scFalse).toLower() == scTrue)
+ if ((component->value(scEssential, scFalse).toLower() == scTrue)
+ || component->isForcedUpdate())
essentialUpdatesFound = true;
}
if (!essentialUpdatesFound) {
- //Mark all components to be updated
+ const bool userSelectedComponents = !componentsToUpdate.isEmpty();
+ QList<Component*> componentsToBeUpdated;
+ //Mark components to be updated
foreach (Component *comp, componentList) {
- comp->setCheckState(Qt::Checked);
+ const QModelIndex &idx = model->indexFromComponentName(comp->treeName());
+ if (!userSelectedComponents) { // No components given, update all
+ model->setData(idx, Qt::Checked, Qt::CheckStateRole);
+ } else {
+ //Collect the componets to list which we want to update
+ foreach (const QString &name, componentsToUpdate) {
+ if (comp->name() == name)
+ componentsToBeUpdated.append(comp);
+ else
+ model->setData(idx, Qt::Unchecked, Qt::CheckStateRole);
+ }
+ }
+ }
+ // No updates for selected components, do not run updater
+ if (userSelectedComponents && componentsToBeUpdated.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog)
+ << "No updates available for selected components.";
+ return PackageManagerCore::Canceled;
+ }
+ foreach (Component *componentToUpdate, componentsToBeUpdated) {
+ const QModelIndex &idx = model->indexFromComponentName(componentToUpdate->treeName());
+ model->setData(idx, Qt::Checked, Qt::CheckStateRole);
}
}
- QString htmlOutput;
- bool componentsOk = calculateComponents(&htmlOutput);
- if (componentsOk) {
- if (runPackageUpdater()) {
- writeMaintenanceTool();
- if (essentialUpdatesFound) {
- qDebug() << "Essential components updated successfully.";
- }
- else {
- qDebug() << "Components updated successfully.";
- }
+
+ if (!d->calculateComponentsAndRun())
+ return status();
+
+ if (essentialUpdatesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Essential components updated successfully."
+ " Please restart maintenancetool to update other components.";
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components updated successfully.";
+ }
+ }
+ return status();
+}
+
+/*!
+ Saves temporarily current operations for installer usage. This is needed
+ for unit tests when several commands (for example install and uninstall)
+ are performed with the same installer instance.
+*/
+void PackageManagerCore::commitSessionOperations()
+{
+ d->commitSessionOperations();
+}
+
+/*!
+ * Clears all previously added licenses.
+ */
+void PackageManagerCore::clearLicenses()
+{
+ d->m_licenseItems.clear();
+}
+
+/*!
+ * Returns licenses hash which can be sorted by priority.
+ */
+QHash<QString, QMap<QString, QString>> PackageManagerCore::sortedLicenses()
+{
+ QHash<QString, QMap<QString, QString>> priorityHash;
+ for (QString licenseName : d->m_licenseItems.keys()) {
+ QMap<QString, QString> licenses;
+ QString priority = d->m_licenseItems.value(licenseName).value(QLatin1String("priority")).toString();
+ licenses = priorityHash.value(priority);
+ licenses.insert(licenseName, d->m_licenseItems.value(licenseName).value(QLatin1String("content")).toString());
+ priorityHash.insert(priority, licenses);
+ }
+ return priorityHash;
+}
+
+/*!
+ * Adds new set of \a licenses. If a license with the key already exists, it is not added again.
+ */
+void PackageManagerCore::addLicenseItem(const QHash<QString, QVariantMap> &licenses)
+{
+ for (QHash<QString, QVariantMap>::const_iterator it = licenses.begin();
+ it != licenses.end(); ++it) {
+ if (!d->m_licenseItems.contains(it.key()))
+ d->m_licenseItems.insert(it.key(), it.value());
+ }
+}
+
+bool PackageManagerCore::hasLicenses() const
+{
+ foreach (Component* component, orderedComponentsToInstall()) {
+ if (isMaintainer() && component->isInstalled())
+ continue; // package manager or updater, hide as long as the component is installed
+
+ // The component is about to be installed and provides a license, so the page needs to
+ // be shown.
+ if (!component->licenses().isEmpty())
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * Adds \a component local \a dependencies to a hash table for quicker search for
+ * uninstall dependency components.
+ */
+void PackageManagerCore::createLocalDependencyHash(const QString &component, const QString &dependencies) const
+{
+ d->createLocalDependencyHash(component, dependencies);
+}
+
+/*!
+ * Adds \a component \a newDependencies to a hash table for quicker search for
+ * install and uninstall autodependency components. Removes \a oldDependencies
+ * from the hash table if dependencies have changed.
+ */
+void PackageManagerCore::createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies) const
+{
+ d->createAutoDependencyHash(component, oldDependencies, newDependencies);
+}
+/*!
+ Uninstalls the selected components \a components without GUI.
+ Returns PackageManagerCore installation status.
+*/
+PackageManagerCore::Status PackageManagerCore::uninstallComponentsSilently(const QStringList& components)
+{
+ if (components.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No components selected for uninstallation.";
+ return PackageManagerCore::Canceled;
+ }
+
+ ComponentModel *model = defaultComponentModel();
+ fetchLocalPackagesTree();
+
+ bool uninstallComponentFound = false;
+
+ foreach (const QString &componentName, components){
+ Component *component = componentByName(componentName);
+
+ if (component) {
+ const QModelIndex &idx = model->indexFromComponentName(component->treeName());
+ if (componentUninstallableFromCommandLine(component->name())) {
+ model->setData(idx, Qt::Unchecked, Qt::CheckStateRole);
+ uninstallComponentFound = true;
}
+ } else {
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot uninstall component " << componentName <<". Component not found in install tree.";
}
- else {
- qDebug() << htmlOutput;
+ }
+
+ if (uninstallComponentFound) {
+ if (d->calculateComponentsAndRun())
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components uninstalled successfully";
+ }
+ return status();
+}
+
+/*!
+ Uninstalls all installed components without GUI and removes
+ the program directory. Returns PackageManagerCore installation status.
+*/
+PackageManagerCore::Status PackageManagerCore::removeInstallationSilently()
+{
+ setCompleteUninstallation(true);
+
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen.";
+ if (!(d->m_autoConfirmCommand || d->askUserConfirmCommand())) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Uninstallation aborted.";
+ return status();
+ }
+ if (run())
+ return PackageManagerCore::Success;
+ else
+ return PackageManagerCore::Failure;
+}
+
+/*!
+ Creates an offline installer from selected \a componentsToAdd without displaying
+ a user interface. Virtual components cannot be selected unless made visible with
+ --show-virtual-components as in installation. AutoDependOn nor non-checkable components
+ cannot be selected directly. Returns \c PackageManagerCore::Status.
+*/
+PackageManagerCore::Status PackageManagerCore::createOfflineInstaller(const QStringList &componentsToAdd)
+{
+ setOfflineGenerator();
+ return d->fetchComponentsAndInstall(componentsToAdd);
+}
+
+/*!
+ Installs the selected components \a components without displaying a user
+ interface. Virtual components cannot be installed unless made visible with
+ --show-virtual-components. AutoDependOn nor non-checkable components cannot
+ be installed directly. Returns PackageManagerCore installation status.
+*/
+PackageManagerCore::Status PackageManagerCore::installSelectedComponentsSilently(const QStringList& components)
+{
+ if (!isInstaller()) {
+ setPackageManager();
+
+ //Check that packages are not already installed
+ const LocalPackagesMap installedPackages = this->localInstalledPackages();
+ QStringList helperStrList;
+ helperStrList << components << installedPackages.keys();
+ helperStrList.removeDuplicates();
+ if (helperStrList.count() == installedPackages.count()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components already installed.";
+ return PackageManagerCore::Canceled;
+ }
+ }
+ return d->fetchComponentsAndInstall(components);
+}
+
+/*!
+ Installs components that are checked by default, i.e. those that are set
+ with <Default> or <ForcedInstallation> and their respective dependencies
+ without GUI.
+ Returns PackageManagerCore installation status.
+*/
+PackageManagerCore::Status PackageManagerCore::installDefaultComponentsSilently()
+{
+ d->m_defaultInstall = true;
+ ComponentModel *model = defaultComponentModel();
+ fetchRemotePackagesTree();
+
+ if (!(model->checkedState() & ComponentModel::AllUnchecked)) {
+ // There are components that are checked by default, we should install them
+ if (d->calculateComponentsAndRun()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully.";
}
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "No components available for default installation.";
+ setCanceled();
}
+ return status();
}
/*!
@@ -1902,6 +3249,10 @@ bool PackageManagerCore::gainAdminRights()
if (AdminAuthorization::hasAdminRights())
return true;
+ if (isCommandLineInstance()) {
+ throw Error(tr("Cannot elevate access rights while running from command line. "
+ "Please restart the application as administrator."));
+ }
RemoteClient::instance().setActive(true);
if (!RemoteClient::instance().isActive())
throw Error(tr("Error while elevating access rights."));
@@ -1918,6 +3269,148 @@ void PackageManagerCore::dropAdminRights()
}
/*!
+ Returns \c true if the installer has admin rights. For example, if the installer
+ was started with a root account, or the internal admin rights elevation is active.
+
+ Returns \c false otherwise.
+
+ \sa {installer::hasAdminRights}{installer.hasAdminRights}
+ \sa gainAdminRights()
+ \sa dropAdminRights()
+*/
+bool PackageManagerCore::hasAdminRights() const
+{
+ return AdminAuthorization::hasAdminRights() || RemoteClient::instance().isActive();
+}
+
+/*!
+ Sets checkAvailableSpace based on value of \a check.
+*/
+void PackageManagerCore::setCheckAvailableSpace(bool check)
+{
+ d->m_checkAvailableSpace = check;
+}
+
+/*!
+ * Returns informative text about disk space status
+ */
+QString PackageManagerCore::availableSpaceMessage() const
+{
+ return m_availableSpaceMessage;
+}
+
+/*!
+ Checks available disk space if the feature is not explicitly disabled. Returns
+ \c true if there is sufficient free space on installation and temporary volumes.
+
+ \sa availableSpaceMessage()
+*/
+bool PackageManagerCore::checkAvailableSpace()
+{
+ m_availableSpaceMessage.clear();
+ const quint64 extraSpace = 256 * 1024 * 1024LL;
+ quint64 required(requiredDiskSpace());
+ quint64 tempRequired(requiredTemporaryDiskSpace());
+ if (required < extraSpace) {
+ required += 0.1 * required;
+ tempRequired += 0.1 * tempRequired;
+ } else {
+ required += extraSpace;
+ tempRequired += extraSpace;
+ }
+
+ quint64 repositorySize = 0;
+ const bool createLocalRepository = createLocalRepositoryFromBinary();
+ if (createLocalRepository && isInstaller()) {
+ repositorySize = QFile(QCoreApplication::applicationFilePath()).size();
+ // if we create a local repository, take that space into account as well
+ required += repositorySize;
+ }
+ // if we create offline installer, take current executable size into account
+ if (isOfflineGenerator())
+ required += QFile(QCoreApplication::applicationFilePath()).size();
+
+ qDebug() << "Installation space required:" << humanReadableSize(required) << "Temporary space "
+ "required:" << humanReadableSize(tempRequired) << "Local repository size:"
+ << humanReadableSize(repositorySize);
+
+ if (d->m_checkAvailableSpace) {
+ const VolumeInfo cacheVolume = VolumeInfo::fromPath(settings().localCachePath());
+ const VolumeInfo targetVolume = VolumeInfo::fromPath(value(scTargetDir));
+
+ const quint64 cacheVolumeAvailableSize = cacheVolume.availableSize();
+ const quint64 installVolumeAvailableSize = targetVolume.availableSize();
+
+ // at the moment there is no better way to check this
+ if (targetVolume.size() == 0 && installVolumeAvailableSize == 0) {
+ qDebug().nospace() << "Cannot determine available space on device. "
+ "Volume descriptor: " << targetVolume.volumeDescriptor()
+ << ", Mount path: " << targetVolume.mountPath() << ". Continue silently.";
+ return true;
+ }
+
+ const bool cacheOnSameVolume = (targetVolume == cacheVolume);
+ if (cacheOnSameVolume) {
+ qDebug() << "Cache and install directories are on the same volume. Volume mount point:"
+ << targetVolume.mountPath() << "Free space available:"
+ << humanReadableSize(installVolumeAvailableSize);
+ } else {
+ qDebug() << "Cache is on a different volume than the installation directory. Cache volume mount point:"
+ << cacheVolume.mountPath() << "Free space available:"
+ << humanReadableSize(cacheVolumeAvailableSize) << "Install volume mount point:"
+ << targetVolume.mountPath() << "Free space available:"
+ << humanReadableSize(installVolumeAvailableSize);
+ }
+
+ if (cacheOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
+ m_availableSpaceMessage = tr("Not enough disk space to store temporary files and the "
+ "installation. %1 are available, while the minimum required is %2.").arg(
+ humanReadableSize(installVolumeAvailableSize), humanReadableSize(required + tempRequired));
+ return false;
+ }
+
+ if (installVolumeAvailableSize < required) {
+ m_availableSpaceMessage = tr("Not enough disk space to store all selected components! %1 are "
+ "available, while the minimum required is %2.").arg(humanReadableSize(installVolumeAvailableSize),
+ humanReadableSize(required));
+ return false;
+ }
+
+ if (cacheVolumeAvailableSize < tempRequired) {
+ m_availableSpaceMessage = tr("Not enough disk space to store temporary files! %1 are available, "
+ "while the minimum required is %2. You may select another location for the "
+ "temporary files by modifying the local cache path from the installer settings.")
+ .arg(humanReadableSize(cacheVolumeAvailableSize), humanReadableSize(tempRequired));
+ return false;
+ }
+
+ if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) {
+ // warn for less than 1% of the volume's space being free
+ m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient space for "
+ "installation, but there will be less than 1% of the volume's space available afterwards.");
+ } else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) {
+ // warn for less than 100MB being free
+ m_availableSpaceMessage = tr("The volume you selected for installation seems to have sufficient "
+ "space for installation, but there will be less than 100 MB available afterwards.");
+ }
+#ifdef Q_OS_WIN
+ if (isOfflineGenerator() && (required > UINT_MAX)) {
+ m_availableSpaceMessage = tr("The estimated installer size %1 would exceed the supported executable "
+ "size limit of %2. The application may not be able to run.")
+ .arg(humanReadableSize(required), humanReadableSize(UINT_MAX));
+ }
+#endif
+ }
+ m_availableSpaceMessage = QString::fromLatin1("%1 %2").arg(m_availableSpaceMessage,
+ (isOfflineGenerator()
+ ? tr("Created installer will use %1 of disk space.")
+ : tr("Installation will use %1 of disk space."))
+ .arg(humanReadableSize(requiredDiskSpace()))).simplified();
+
+ return true;
+}
+
+/*!
Returns \c true if a process with \a name is running. On Windows, the comparison
is case-insensitive.
@@ -1947,7 +3440,8 @@ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const
processPath = QDir::cleanPath(processPath.replace(QLatin1Char('\\'), QLatin1Char('/')));
if (processPath == normalizedPath) {
- qDebug().nospace() << "try to kill process " << process.name << " (" << process.id << ")";
+ qCDebug(QInstaller::lcInstallerInstallLog).nospace() << "try to kill process " << process.name
+ << " (" << process.id << ")";
//to keep the ui responsible use QtConcurrent::run
QFutureWatcher<bool> futureWatcher;
@@ -1961,13 +3455,42 @@ bool PackageManagerCore::killProcess(const QString &absoluteFilePath) const
if (!future.isFinished())
loop.exec();
- qDebug() << process.name << "killed!";
+ qCDebug(QInstaller::lcInstallerInstallLog) << process.name << "killed!";
return future.result();
}
}
return true;
}
+/*!
+ \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To manually check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
+ Sets additional \a processes that can run when
+ updating with the maintenance tool.
+
+ \sa {installer::setAllowedRunningProcesses}{installer.setAllowedRunningProcesses}
+*/
+void PackageManagerCore::setAllowedRunningProcesses(const QStringList &processes)
+{
+ d->m_allowedRunningProcesses = processes;
+}
+
+/*!
+ \deprecated [4.6] Maintenance tool no longer automatically checks for all running processes
+ in the installation directory for CLI runs. To manually check for a process to stop, use
+ \l {component::addStopProcessForUpdateRequest}{component.addStopProcessForUpdateRequest} instead.
+
+ Returns processes that are allowed to run when
+ updating with the maintenance tool.
+
+ \sa {installer::allowedRunningProcesses}{installer.allowedRunningProcesses}
+*/
+QStringList PackageManagerCore::allowedRunningProcesses() const
+{
+ return d->m_allowedRunningProcesses;
+}
/*!
Makes sure the installer runs from a local drive. Otherwise the user will get an
@@ -2084,7 +3607,8 @@ bool PackageManagerCore::executeDetached(const QString &program, const QStringLi
QString adjustedWorkingDir = replaceVariables(workingDirectory);
foreach (const QString &argument, arguments)
adjustedArguments.append(replaceVariables(argument));
- qDebug() << "run application as detached process:" << adjustedProgram << adjustedArguments << adjustedWorkingDir;
+ qCDebug(QInstaller::lcInstallerInstallLog) << "run application as detached process:" << adjustedProgram
+ << adjustedArguments << adjustedWorkingDir;
if (workingDirectory.isEmpty())
return QProcess::startDetached(adjustedProgram, adjustedArguments);
else
@@ -2150,7 +3674,7 @@ bool PackageManagerCore::performOperation(const QString &name, const QStringList
op->setArguments(replaceVariables(arguments));
op->backup();
if (!PackageManagerCorePrivate::performOperationThreaded(op.data())) {
- PackageManagerCorePrivate::performOperationThreaded(op.data(), PackageManagerCorePrivate::Undo);
+ PackageManagerCorePrivate::performOperationThreaded(op.data(), Operation::Undo);
return false;
}
return true;
@@ -2165,9 +3689,10 @@ bool PackageManagerCore::performOperation(const QString &name, const QStringList
*/
bool PackageManagerCore::versionMatches(const QString &version, const QString &requirement)
{
- QRegExp compEx(QLatin1String("([<=>]+)(.*)"));
- const QString comparator = compEx.exactMatch(requirement) ? compEx.cap(1) : QLatin1String("=");
- const QString ver = compEx.exactMatch(requirement) ? compEx.cap(2) : requirement;
+ static const QRegularExpression compEx(QLatin1String("^([<=>]+)(.*)$"));
+ const QRegularExpressionMatch match = compEx.match(requirement);
+ const QString comparator = match.hasMatch() ? match.captured(1) : QLatin1String("=");
+ const QString ver = match.hasMatch() ? match.captured(2) : requirement;
const bool allowEqual = comparator.contains(QLatin1Char('='));
const bool allowLess = comparator.contains(QLatin1Char('<'));
@@ -2263,15 +3788,51 @@ void PackageManagerCore::setInstallerBaseBinary(const QString &path)
}
/*!
+ Returns the value of \c installerbase binary which is used when writing
+ the maintenance tool. Value can be empty.
+
+ \sa setInstallerBaseBinary()
+*/
+QString PackageManagerCore::installerBaseBinary() const
+{
+ return d->m_installerBaseBinaryUnreplaced;
+}
+
+/*!
+ Sets the \c installerbase binary located at \a path to use when writing the
+ offline installer. Setting this makes it possible to run the offline generator
+ in cases where we are not running a real installer, i.e. when executing autotests.
+
+ For normal runs, the executable segment of the running installer will be used.
+*/
+void PackageManagerCore::setOfflineBaseBinary(const QString &path)
+{
+ d->m_offlineBaseBinaryUnreplaced = path;
+}
+
+/*!
+ Adds the resource collection in \a rcPath to the list of resource files
+ to be included into the generated offline installer binary.
+*/
+void PackageManagerCore::addResourcesForOfflineGeneration(const QString &rcPath)
+{
+ d->m_offlineGeneratorResourceCollections.append(rcPath);
+}
+
+/*!
Returns the installer value for \a key. If \a key is not known to the system, \a defaultValue is
- returned. Additionally, on Windows, \a key can be a registry key.
+ returned. Additionally, on Windows, \a key can be a registry key. Optionally, you can specify the
+ \a format of the registry key. By default the \a format is QSettings::NativeFormat.
+ For accessing the 32-bit system registry from a 64-bit application running on 64-bit Windows,
+ use QSettings::Registry32Format. For accessing the 64-bit system registry from a 32-bit
+ application running on 64-bit Windows, use QSettings::Registry64Format.
\sa {installer::value}{installer.value}
\sa setValue(), containsValue(), valueChanged()
*/
-QString PackageManagerCore::value(const QString &key, const QString &defaultValue) const
+QString PackageManagerCore::value(const QString &key, const QString &defaultValue, const int &format) const
{
- return d->m_data.value(key, defaultValue).toString();
+ return d->m_data.value(key, defaultValue, static_cast<QSettings::Format>(format)).toString();
}
/*!
@@ -2287,6 +3848,17 @@ QStringList PackageManagerCore::values(const QString &key, const QStringList &de
}
/*!
+ Returns the installer key for \a value. If \a value is not known, empty string is
+ returned.
+
+ \sa {installer::key}{installer.key}
+*/
+QString PackageManagerCore::key(const QString &value) const
+{
+ return d->m_data.key(value);
+}
+
+/*!
Sets the installer value for \a key to \a value.
\sa {installer::setValue}{installer.setValue}
@@ -2311,50 +3883,20 @@ bool PackageManagerCore::containsValue(const QString &key) const
}
/*!
- \obsolete
- Sets a shared flag with the name \a key to \a value. This is one option
- to share information between scripts.
-
- Deprecated since 2.0.0. Use setValue() instead.
-
- \sa setValue()
-*/
-void PackageManagerCore::setSharedFlag(const QString &key, bool value)
-{
- qDebug() << "sharedFlag is deprecated";
- d->m_sharedFlags.insert(key, value);
-}
-
-/*!
- \obsolete
- Returns the shared flag with the name \a key. This is one option
- to share information between scripts.
-
- Deprecated since 2.0.0. Use value() or values() instead.
-
- \sa value(), values()
-*/
-bool PackageManagerCore::sharedFlag(const QString &key) const
-{
- qDebug() << "sharedFlag is deprecated";
- return d->m_sharedFlags.value(key, false);
-}
-
-/*!
Returns \c true if the package manager displays detailed information.
*/
bool PackageManagerCore::isVerbose() const
{
- return QInstaller::isVerbose();
+ return LoggingHandler::instance().isVerbose();
}
/*!
Determines that the package manager displays detailed information if
- \a on is \c true.
+ \a on is \c true. Calling setVerbose() more than once increases verbosity.
*/
void PackageManagerCore::setVerbose(bool on)
{
- QInstaller::setVerbose(on);
+ LoggingHandler::instance().setVerbose(on);
}
PackageManagerCore::Status PackageManagerCore::status() const
@@ -2439,6 +3981,40 @@ QString PackageManagerCore::installerBinaryPath() const
}
/*!
+ Sets the \a name for the generated offline binary.
+*/
+void PackageManagerCore::setOfflineBinaryName(const QString &name)
+{
+ setValue(scOfflineBinaryName, name);
+}
+
+/*!
+ Returns the path set for the generated offline binary.
+*/
+QString PackageManagerCore::offlineBinaryName() const
+{
+ return d->offlineBinaryName();
+}
+
+/*!
+ Add new \a source for looking component aliases.
+*/
+void PackageManagerCore::addAliasSource(const AliasSource &source)
+{
+ d->m_aliasSources.insert(source);
+}
+
+/*!
+ \sa {installer::setInstaller}{installer.setInstaller}
+ \sa isInstaller(), setUpdater(), setPackageManager()
+*/
+void PackageManagerCore::setInstaller()
+{
+ d->m_magicBinaryMarker = BinaryContent::MagicInstallerMarker;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
Returns \c true if running as installer.
\sa {installer::isInstaller}{installer.isInstaller}
@@ -2466,6 +4042,7 @@ bool PackageManagerCore::isOfflineOnly() const
void PackageManagerCore::setUninstaller()
{
d->m_magicBinaryMarker = BinaryContent::MagicUninstallerMarker;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -2486,6 +4063,8 @@ bool PackageManagerCore::isUninstaller() const
void PackageManagerCore::setUpdater()
{
d->m_magicBinaryMarker = BinaryContent::MagicUpdaterMarker;
+ d->m_componentByNameHash.clear();
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
/*!
@@ -2505,6 +4084,8 @@ bool PackageManagerCore::isUpdater() const
void PackageManagerCore::setPackageManager()
{
d->m_magicBinaryMarker = BinaryContent::MagicPackageManagerMarker;
+ d->m_componentByNameHash.clear();
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
}
@@ -2520,6 +4101,104 @@ bool PackageManagerCore::isPackageManager() const
}
/*!
+ Sets current installer to be offline generator.
+*/
+void PackageManagerCore::setOfflineGenerator()
+{
+ d->m_magicMarkerSupplement = BinaryContent::OfflineGenerator;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
+ Returns \c true if current installer is executed as offline generator.
+
+ \sa {installer::isOfflineGenerator}{installer.isOfflineGenerator}
+*/
+bool PackageManagerCore::isOfflineGenerator() const
+{
+ return d->isOfflineGenerator();
+}
+
+/*!
+ Sets the current installer as the package viewer.
+*/
+void PackageManagerCore::setPackageViewer()
+{
+ d->m_magicMarkerSupplement = BinaryContent::PackageViewer;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
+ Returns \c true if the current installer is executed as package viewer.
+
+ \sa {installer::isPackageViewer}{installer.isPackageViewer}
+*/
+bool PackageManagerCore::isPackageViewer() const
+{
+ return d->isPackageViewer();
+}
+
+/*!
+ Resets the binary marker supplement of the installer to \c Default.
+ The supplement enables or disables additional features on top of the binary
+ marker state (\c Installer, \c Updater, \c PackageManager, \c Uninstaller).
+*/
+void PackageManagerCore::resetBinaryMarkerSupplement()
+{
+ d->m_magicMarkerSupplement = BinaryContent::Default;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
+ Sets the installer magic binary marker based on \a magicMarker and
+ userSetBinaryMarker to \c true.
+*/
+void PackageManagerCore::setUserSetBinaryMarker(qint64 magicMarker)
+{
+ d->m_magicBinaryMarker = magicMarker;
+ d->m_userSetBinaryMarker = true;
+ emit installerBinaryMarkerChanged(d->m_magicBinaryMarker);
+}
+
+/*!
+ Returns \c true if the magic binary marker has been set by user,
+ for example from a command line argument.
+
+ \sa {installer::isUserSetBinaryMarker}{installer.isUserSetBinaryMarker}
+*/
+bool PackageManagerCore::isUserSetBinaryMarker() const
+{
+ return d->m_userSetBinaryMarker;
+}
+
+/*!
+ Set to use command line instance based on \a commandLineInstance.
+*/
+void PackageManagerCore::setCommandLineInstance(bool commandLineInstance)
+{
+ d->m_commandLineInstance = commandLineInstance;
+}
+
+/*!
+ Returns \c true if running as command line instance.
+
+ \sa {installer::isCommandLineInstance}{installer.isCommandLineInstance}
+*/
+bool PackageManagerCore::isCommandLineInstance() const
+{
+ return d->m_commandLineInstance;
+}
+
+/*!
+ Returns \c true if installation is performed with default components.
+
+ \sa {installer::isCommandLineDefaultInstall}{installer.isCommandLineDefaultInstall}
+*/
+bool PackageManagerCore::isCommandLineDefaultInstall() const
+{
+ return d->m_defaultInstall;
+}
+/*!
Returns \c true if it is a package manager or an updater.
*/
bool PackageManagerCore::isMaintainer() const
@@ -2558,6 +4237,16 @@ bool PackageManagerCore::runPackageUpdater()
}
/*!
+ Runs the offline generator. Returns \c true on success, \c false otherwise.
+
+ \sa {installer::runOfflineGenerator}{installer.runOfflineGenerator}
+*/
+bool PackageManagerCore::runOfflineGenerator()
+{
+ return d->runOfflineGenerator();
+}
+
+/*!
\sa {installer::languageChanged}{installer.languageChanged}
*/
void PackageManagerCore::languageChanged()
@@ -2567,17 +4256,20 @@ void PackageManagerCore::languageChanged()
}
/*!
- Runs the installer, uninstaller, updater, or package manager, depending on
+ Runs the installer, uninstaller, updater, package manager, or offline generator depending on
the type of this binary. Returns \c true on success, otherwise \c false.
*/
bool PackageManagerCore::run()
{
- if (isInstaller())
+ if (isOfflineGenerator())
+ return d->runOfflineGenerator();
+ else if (isInstaller())
return d->runInstaller();
else if (isUninstaller())
return d->runUninstaller();
else if (isMaintainer())
return d->runPackageUpdater();
+
return false;
}
@@ -2592,54 +4284,58 @@ QString PackageManagerCore::maintenanceToolName() const
bool PackageManagerCore::updateComponentData(struct Data &data, Component *component)
{
try {
- // check if we already added the component to the available components list
- const QString name = data.package->data(scName).toString();
+ // Check if we already added the component to the available components list.
+ // Component treenames and names must be unique.
+ const QString packageName = data.package->data(scName).toString();
+ const QString packageTreeName = data.package->data(scTreeName).value<QPair<QString, bool>>().first;
+
+ QString name = packageTreeName.isEmpty() ? packageName : packageTreeName;
if (data.components->contains(name)) {
- qCritical("Cannot register component! Component with identifier %s already registered.",
- qPrintable(name));
- return false;
- }
+ qCritical() << "Cannot register component" << packageName << "with name" << name
+ << "! Component with identifier" << name << "already exists.";
+ // Conflicting original name, skip.
+ if (packageTreeName.isEmpty())
+ return false;
+
+ // Conflicting tree name, check if we can add with original name.
+ if (!settings().allowUnstableComponents() || data.components->contains(packageName))
+ return false;
+
+ qCDebug(lcInstallerInstallLog)
+ << "Registering component with the original indetifier:" << packageName;
+ component->removeValue(scTreeName);
+ const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
+ }
+ name = packageName;
if (settings().allowUnstableComponents()) {
// Check if there are sha checksum mismatch. Component will still show in install tree
// but is unselectable.
- foreach (const QString packageName, d->m_metadataJob.shaMismatchPackages()) {
- if (packageName == component->name()) {
- QString errorString = QLatin1String("SHA mismatch detected for component ") + packageName;
- component->setUnstable(PackageManagerCore::UnstableError::ShaMismatch, errorString);
+ foreach (const QString pkgName, d->m_metadataJob.shaMismatchPackages()) {
+ if (pkgName == component->name()) {
+ const QString errorString = QLatin1String("SHA mismatch detected for component ") + pkgName;
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::ShaMismatch, errorString));
}
}
}
component->setUninstalled();
const QString localPath = component->localTempPath();
- if (isVerbose()) {
- static QString lastLocalPath;
- if (lastLocalPath != localPath)
- qDebug() << "Url is:" << localPath;
- lastLocalPath = localPath;
- }
-
- const Repository repo = d->m_metadataJob.repositoryForDirectory(localPath);
+ const Repository repo = d->m_metadataJob.repositoryForCacheDirectory(localPath);
if (repo.isValid()) {
component->setRepositoryUrl(repo.url());
component->setValue(QLatin1String("username"), repo.username());
component->setValue(QLatin1String("password"), repo.password());
}
- // add downloadable archive from xml
- const QStringList downloadableArchives = data.package->data(scDownloadableArchives).toString()
- .split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
-
- if (component->isFromOnlineRepository()) {
- foreach (const QString downloadableArchive, downloadableArchives)
- component->addDownloadableArchive(downloadableArchive);
- }
-
- const QStringList componentsToReplace = data.package->data(scReplaces).toString()
- .split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
+ if (component->isFromOnlineRepository())
+ component->addDownloadableArchives(data.package->data(scDownloadableArchives).toString());
+ const QStringList componentsToReplace = QInstaller::splitStringWithComma(data.package->data(scReplaces).toString());
if (!componentsToReplace.isEmpty()) {
// Store the component (this is a component that replaces others) and all components that
// this one will replace.
@@ -2657,6 +4353,8 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
// a possible component to replace that might be installed (to mark the replacement as installed).
component->setInstalled();
component->setValue(scInstalledVersion, data.installedPackages->value(name).version);
+ component->setValue(scLocalDependencies, data.installedPackages->value(name).
+ dependencies.join(QLatin1String(",")));
return true;
}
@@ -2664,8 +4362,8 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
foreach (const QString &componentName, componentsToReplace) {
if (data.installedPackages->contains(componentName)) {
// We found a replacement that is installed.
- if (isPackageManager()) {
- // Mark the replacement component as installed as well. Only do this in package manager
+ if (isUpdater()) {
+ // Mark the replacement component as installed as well. Only do this in updater
// mode, otherwise it would not show up in the updaters component list.
component->setInstalled();
component->setValue(scInstalledVersion, data.installedPackages->value(componentName).version);
@@ -2680,167 +4378,306 @@ bool PackageManagerCore::updateComponentData(struct Data &data, Component *compo
return true;
}
-void PackageManagerCore::storeReplacedComponents(QHash<QString, Component *> &components, const struct Data &data)
+void PackageManagerCore::storeReplacedComponents(QHash<QString, Component *> &components,
+ const struct Data &data, QMap<QString, QString> *const treeNameComponents)
{
QHash<Component*, QStringList>::const_iterator it = data.replacementToExchangeables.constBegin();
// remember all components that got a replacement, required for uninstall
for (; it != data.replacementToExchangeables.constEnd(); ++it) {
foreach (const QString &componentName, it.value()) {
- Component *componentToReplace = components.take(componentName);
+ QString key = componentName;
+ if (treeNameComponents && treeNameComponents->contains(componentName)) {
+ // The exchangeable component is stored with a tree name key,
+ // remove from the list of components with tree name.
+ key = treeNameComponents->value(componentName);
+ treeNameComponents->remove(componentName);
+ }
+ Component *componentToReplace = components.value(key);
if (!componentToReplace) {
// If a component replaces another component which is not existing in the
// installer binary or the installed component list, just ignore it. This
// can happen when in installer mode and probably package manager mode too.
if (isUpdater())
- qDebug() << componentName << "- Does not exist in the repositories anymore.";
+ qCWarning(QInstaller::lcDeveloperBuild) << componentName << "- Does not exist in the repositories anymore.";
continue;
}
- if (!componentToReplace && !d->componentsToReplace().contains(componentName)) {
- componentToReplace = new Component(this);
- componentToReplace->setValue(scName, componentName);
- } else {
- // This case can happen when in installer mode as well, a component
- // is in the installer binary and its replacement component as well.
- d->replacementDependencyComponents().append(componentToReplace);
+ // Remove the replaced component from instal tree if
+ // 1. Running installer (component is replaced by other component)
+ // 2. Replacement is already installed but replacable is not
+ // Do not remove the replaced component from install tree
+ // in updater so that would show as an update
+ // Also do not remove the replaced component from install tree
+ // if it is already installed together with replacable component,
+ // otherwise it does not match what we have defined in components.xml
+ if (!isUpdater()
+ && (isInstaller() || (it.key() && it.key()->isInstalled() && !componentToReplace->isInstalled()))) {
+ components.remove(key);
+ d->m_deletedReplacedComponents.append(componentToReplace);
}
+ d->replacementDependencyComponents().append(componentToReplace);
+
+ //Following hashes are created for quicker search of components
d->componentsToReplace().insert(componentName, qMakePair(it.key(), componentToReplace));
+ QStringList oldValue = d->componentReplaces().value(it.key()->name());
+ d->componentReplaces().insert(it.key()->name(), oldValue << componentToReplace->name());
}
}
}
-bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const LocalPackagesHash &locals)
+bool PackageManagerCore::fetchAllPackages(const PackagesList &remotes, const LocalPackagesMap &locals)
{
emit startAllComponentsReset();
- d->clearAllComponentLists();
- QHash<QString, QInstaller::Component*> components;
+ try {
+ d->clearAllComponentLists();
+ QHash<QString, QInstaller::Component*> allComponents;
- Data data;
- data.components = &components;
- data.installedPackages = &locals;
+ Data data;
+ data.components = &allComponents;
+ data.installedPackages = &locals;
- foreach (Package *const package, remotes) {
- if (d->statusCanceledOrFailed())
- return false;
+ QMap<QString, QString> remoteTreeNameComponents;
+ QMap<QString, QString> allTreeNameComponents;
- if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString()))
- continue;
+ std::function<bool(PackagesList *, bool)> loadRemotePackages;
+ loadRemotePackages = [&](PackagesList *treeNamePackages, bool firstRun) -> bool {
+ foreach (Package *const package, (firstRun ? remotes : *treeNamePackages)) {
+ if (d->statusCanceledOrFailed())
+ return false;
+
+ if (!ProductKeyCheck::instance()->isValidPackage(package->data(scName).toString()))
+ continue;
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
- data.package = package;
- component->loadDataFromPackage(*package);
- if (updateComponentData(data, component.data())) {
- const QString name = component->name();
- components.insert(name, component.take());
+ if (firstRun && !package->data(scTreeName)
+ .value<QPair<QString, bool>>().first.isEmpty()) {
+ // Package has a tree name, leave for later
+ treeNamePackages->append(package);
+ continue;
+ }
+
+ std::unique_ptr<QInstaller::Component> remoteComponent(new QInstaller::Component(this));
+ data.package = package;
+ remoteComponent->loadDataFromPackage(*package);
+ if (updateComponentData(data, remoteComponent.get())) {
+ // Create a list where is name and treename. Repo can contain a package with
+ // a different treename of component which is already installed. We don't want
+ // to move already installed local packages.
+ const QString treeName = remoteComponent->value(scTreeName);
+ if (!treeName.isEmpty())
+ remoteTreeNameComponents.insert(remoteComponent->name(), treeName);
+ const QString name = remoteComponent->treeName();
+ allComponents.insert(name, remoteComponent.release());
+ }
+ }
+ // Second pass with leftover packages
+ return firstRun ? loadRemotePackages(treeNamePackages, false) : true;
+ };
+
+ {
+ // Loading remote package data is performed in two steps: first, components without
+ // - and then components with tree names. This is to ensure components with tree
+ // names do not replace other components when registering fails to a name conflict.
+ PackagesList treeNamePackagesTmp;
+ if (!loadRemotePackages(&treeNamePackagesTmp, true))
+ return false;
}
- }
+ allTreeNameComponents = remoteTreeNameComponents;
- foreach (const QString &key, locals.keys()) {
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
- component->loadDataFromPackage(locals.value(key));
- const QString &name = component->name();
- if (!components.contains(name))
- components.insert(name, component.take());
- }
+ foreach (auto &package, locals) {
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!d->m_localVirtualComponents.contains(package.name))
+ d->m_localVirtualComponents.append(package.name);
+ }
+
+ std::unique_ptr<QInstaller::Component> localComponent(new QInstaller::Component(this));
+ localComponent->loadDataFromPackage(package);
+ const QString name = localComponent->treeName();
+
+ // 1. Component has a treename in local but not in remote, add with local treename
+ if (!remoteTreeNameComponents.contains(localComponent->name()) && !localComponent->value(scTreeName).isEmpty()) {
+ delete allComponents.take(localComponent->name());
+ // 2. Component has different treename in local and remote, add with local treename
+ } else if (remoteTreeNameComponents.contains(localComponent->name())) {
+ const QString remoteTreeName = remoteTreeNameComponents.value(localComponent->name());
+ const QString localTreeName = localComponent->value(scTreeName);
+ if (remoteTreeName != localTreeName) {
+ delete allComponents.take(remoteTreeNameComponents.value(localComponent->name()));
+ } else {
+ // 3. Component has same treename in local and remote, don't add the component again.
+ continue;
+ }
+ // 4. Component does not have treename in local or remote, don't add the component again.
+ } else if (allComponents.contains(localComponent->name())) {
+ Component *const component = allComponents.value(localComponent->name());
+ if (component->value(scTreeName).isEmpty() && localComponent->value(scTreeName).isEmpty())
+ continue;
+ }
+ // 5. Remote has treename for a different component that is already reserved
+ // by this local component, Or, remote adds component without treename
+ // but it conflicts with a local treename.
+ if (allComponents.contains(name)) {
+ const QString key = remoteTreeNameComponents.key(name);
+ qCritical() << "Cannot register component" << (key.isEmpty() ? name : key)
+ << "with name" << name << "! Component with identifier" << name
+ << "already exists.";
+
+ if (!key.isEmpty())
+ allTreeNameComponents.remove(key);
+
+ // Try to re-add the remote component as unstable
+ if (!key.isEmpty() && !allComponents.contains(key) && settings().allowUnstableComponents()) {
+ qCDebug(lcInstallerInstallLog)
+ << "Registering component with the original indetifier:" << key;
+
+ Component *component = allComponents.take(name);
+ component->removeValue(scTreeName);
+ const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
+
+ allComponents.insert(key, component);
+ } else {
+ delete allComponents.take(name);
+ }
+ }
+
+ const QString treeName = localComponent->value(scTreeName);
+ if (!treeName.isEmpty())
+ allTreeNameComponents.insert(localComponent->name(), treeName);
+ allComponents.insert(name, localComponent.release());
+ }
+
+ // store all components that got a replacement
+ storeReplacedComponents(allComponents, data, &allTreeNameComponents);
- // store all components that got a replacement
- storeReplacedComponents(components, data);
+ // Move children of treename components
+ createAutoTreeNames(allComponents, allTreeNameComponents);
- if (!d->buildComponentTree(components, true))
+ if (!d->buildComponentTree(allComponents, true))
+ return false;
+
+ d->commitPendingUnstableComponents();
+
+ if (!d->buildComponentAliases())
+ return false;
+
+ } catch (const Error &error) {
+ d->clearAllComponentLists();
+ d->setStatus(PackageManagerCore::Failure, error.message());
+
+ // TODO: make sure we remove all message boxes inside the library at some point.
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
return false;
+ }
emit finishAllComponentsReset(d->m_rootComponents);
return true;
}
-bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const LocalPackagesHash &locals)
+bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const LocalPackagesMap &locals)
{
emit startUpdaterComponentsReset();
- d->clearUpdaterComponentLists();
- QHash<QString, QInstaller::Component *> components;
+ try {
+ d->clearUpdaterComponentLists();
+ QHash<QString, QInstaller::Component *> components;
- Data data;
- data.components = &components;
- data.installedPackages = &locals;
+ Data data;
+ data.components = &components;
+ data.installedPackages = &locals;
- setFoundEssentialUpdate(false);
- LocalPackagesHash installedPackages = locals;
- QStringList replaceMes;
+ setFoundEssentialUpdate(false);
+ LocalPackagesMap installedPackages = locals;
+ QStringList replaceMes;
- foreach (Package *const update, remotes) {
- if (d->statusCanceledOrFailed())
- return false;
+ foreach (Package *const update, remotes) {
+ if (d->statusCanceledOrFailed())
+ return false;
- if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
- continue;
+ if (!ProductKeyCheck::instance()->isValidPackage(update->data(scName).toString()))
+ continue;
- QScopedPointer<QInstaller::Component> component(new QInstaller::Component(this));
- data.package = update;
- component->loadDataFromPackage(*update);
- if (updateComponentData(data, component.data())) {
- // Keep a reference so we can resolve dependencies during update.
- d->m_updaterComponentsDeps.append(component.take());
-
-// const QString isNew = update->data(scNewComponent).toString();
-// if (isNew.toLower() != scTrue)
-// continue;
-
- const QString &name = d->m_updaterComponentsDeps.last()->name();
- const QString replaces = data.package->data(scReplaces).toString();
- installedPackages.take(name); // remove from local installed packages
-
- bool isValidUpdate = locals.contains(name);
- if (!isValidUpdate && !replaces.isEmpty()) {
- const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
- QString::SkipEmptyParts);
- foreach (const QString &possibleName, possibleNames) {
- if (locals.contains(possibleName)) {
- isValidUpdate = true;
- replaceMes << possibleName;
+ std::unique_ptr<QInstaller::Component> component(new QInstaller::Component(this));
+ data.package = update;
+ component->loadDataFromPackage(*update);
+ if (updateComponentData(data, component.get())) {
+ // Keep a reference so we can resolve dependencies during update.
+ d->m_updaterComponentsDeps.append(component.release());
+
+ // const QString isNew = update->data(scNewComponent).toString();
+ // if (isNew.toLower() != scTrue)
+ // continue;
+
+ const QString &name = d->m_updaterComponentsDeps.last()->name();
+ const QString replaces = data.package->data(scReplaces).toString();
+ installedPackages.take(name); // remove from local installed packages
+
+ bool isValidUpdate = locals.contains(name);
+ if (!replaces.isEmpty()) {
+ const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
+ Qt::SkipEmptyParts);
+ foreach (const QString &possibleName, possibleNames) {
+ if (locals.contains(possibleName)) {
+ isValidUpdate = true;
+ replaceMes << possibleName;
+ }
}
}
- }
- // break if the update is not valid and if it's not the maintenance tool (we might get an update
- // for the maintenance tool even if it's not currently installed - possible offline installation)
- if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse))
- continue; // Update for not installed package found, skip it.
-
- const LocalPackage &localPackage = locals.value(name);
- const QString updateVersion = update->data(scVersion).toString();
- if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0)
- continue;
-
- // It is quite possible that we may have already installed the update. Lets check the last
- // update date of the package and the release date of the update. This way we can compare and
- // figure out if the update has been installed or not.
- const QDate updateDate = update->data(scReleaseDate).toDate();
- if (localPackage.lastUpdateDate > updateDate)
- continue;
+ // break if the update is not valid and if it's not the maintenance tool (we might get an update
+ // for the maintenance tool even if it's not currently installed - possible offline installation)
+ if (!isValidUpdate && (update->data(scEssential, scFalse).toString().toLower() == scFalse))
+ continue; // Update for not installed package found, skip it.
+
+ const LocalPackage &localPackage = locals.value(name);
+ if (!d->packageNeedsUpdate(localPackage, update))
+ continue;
+ // It is quite possible that we may have already installed the update. Lets check the last
+ // update date of the package and the release date of the update. This way we can compare and
+ // figure out if the update has been installed or not.
+ const QDate updateDate = update->data(scReleaseDate).toDate();
+ if (localPackage.lastUpdateDate > updateDate)
+ continue;
+
+ if (update->data(scEssential, scFalse).toString().toLower() == scTrue ||
+ update->data(scForcedUpdate, scFalse).toString().toLower() == scTrue) {
+ setFoundEssentialUpdate(true);
+ }
- if (update->data(scEssential, scFalse).toString().toLower() == scTrue)
- setFoundEssentialUpdate(true);
+ // this is not a dependency, it is a real update
+ components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ }
+ }
- // this is not a dependency, it is a real update
- components.insert(name, d->m_updaterComponentsDeps.takeLast());
+ QHash<QString, QInstaller::Component *> localReplaceMes;
+ foreach (const QString &key, installedPackages.keys()) {
+ QInstaller::Component *component = new QInstaller::Component(this);
+ component->loadDataFromPackage(installedPackages.value(key));
+ d->m_updaterComponentsDeps.append(component);
}
- }
- QHash<QString, QInstaller::Component *> localReplaceMes;
- foreach (const QString &key, installedPackages.keys()) {
- QInstaller::Component *component = new QInstaller::Component(this);
- component->loadDataFromPackage(installedPackages.value(key));
- d->m_updaterComponentsDeps.append(component);
- // Keep a list of local components that should be replaced
- if (replaceMes.contains(component->name()))
- localReplaceMes.insert(component->name(), component);
- }
+ foreach (const QString &key, locals.keys()) {
+ LocalPackage package = locals.value(key);
+ if (package.virtualComp && package.autoDependencies.isEmpty()) {
+ if (!d->m_localVirtualComponents.contains(package.name))
+ d->m_localVirtualComponents.append(package.name);
+ }
+ // Keep a list of local components that should be replaced
+ // Remove from components list - we don't want to update the component
+ // as it is replaced by other component
+ if (replaceMes.contains(key)) {
+ QInstaller::Component *component = new QInstaller::Component(this);
+ component->loadDataFromPackage(locals.value(key));
+ localReplaceMes.insert(component->name(), component);
+ delete components.take(component->name());
+ }
+ }
- // store all components that got a replacement, but do not modify the components list
- storeReplacedComponents(localReplaceMes.unite(components), data);
+ // store all components that got a replacement, but do not modify the components list
+ localReplaceMes.insert(components);
+ storeReplacedComponents(localReplaceMes, data);
- try {
if (!components.isEmpty()) {
// append all components w/o parent to the direct list
foreach (QInstaller::Component *component, components) {
@@ -2848,21 +4685,25 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
}
// after everything is set up, load the scripts
+ if (!d->loadComponentScripts(components))
+ return false;
+
foreach (QInstaller::Component *component, components) {
if (d->statusCanceledOrFailed())
return false;
- component->loadComponentScript();
if (!component->isUnstable())
component->setCheckState(Qt::Checked);
}
+ // even for possible dependencies we need to load the scripts for example to get archives
+ if (!d->loadComponentScripts(d->m_updaterComponentsDeps))
+ return false;
+
// after everything is set up, check installed components
foreach (QInstaller::Component *component, d->m_updaterComponentsDeps) {
if (d->statusCanceledOrFailed())
return false;
- // even for possible dependency we need to load the script for example to get archives
- component->loadComponentScript();
if (component->isInstalled()) {
// since we do not put them into the model, which would force a update of e.g. tri state
// components, we have to check all installed components ourselves
@@ -2870,7 +4711,6 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
component->setCheckState(Qt::Checked);
}
}
-
if (foundEssentialUpdate()) {
foreach (QInstaller::Component *component, components) {
if (d->statusCanceledOrFailed())
@@ -2878,13 +4718,14 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
component->setCheckable(false);
component->setSelectable(false);
- if (component->value(scEssential, scFalse).toLower() == scFalse) {
+ if ((component->value(scEssential, scFalse).toLower() == scTrue)
+ || (component->value(scForcedUpdate, scFalse).toLower() == scTrue)) {
+ // essential updates are enabled, still not checkable but checked
+ component->setEnabled(true);
+ } else {
// non essential updates are disabled, not checkable and unchecked
component->setEnabled(false);
component->setCheckState(Qt::Unchecked);
- } else {
- // essential updates are enabled, still not checkable but checked
- component->setEnabled(true);
}
}
}
@@ -2897,7 +4738,6 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
}
} catch (const Error &error) {
d->clearUpdaterComponentLists();
- emit finishUpdaterComponentsReset(QList<QInstaller::Component*>());
d->setStatus(Failure, error.message());
// TODO: make sure we remove all message boxes inside the library at some point.
@@ -2910,6 +4750,80 @@ bool PackageManagerCore::fetchUpdaterPackages(const PackagesList &remotes, const
return true;
}
+/*!
+ \internal
+
+ Creates automatic tree names for \a components that have a parent declaring
+ an explicit tree name. The child components keep the relative location
+ to their parent component.
+
+ The \a treeNameComponents is a map of original component names and new tree names.
+*/
+void PackageManagerCore::createAutoTreeNames(QHash<QString, Component *> &components
+ , const QMap<QString, QString> &treeNameComponents)
+{
+ if (treeNameComponents.isEmpty())
+ return;
+
+ QHash<QString, Component *> componentsTemp;
+ QMutableHashIterator<QString, Component* > i(components);
+ while (i.hasNext()) {
+ i.next();
+ Component *component = i.value();
+ if (component->treeName() != component->name()) // already handled
+ continue;
+
+ QString newName;
+ // Check treename candidates, keep the name closest to a leaf component
+ QMap<QString, QString>::const_iterator j;
+ for (j = treeNameComponents.begin(); j != treeNameComponents.end(); ++j) {
+ const QString name = j.key();
+ if (!component->name().startsWith(name))
+ continue;
+
+ const Component *parent = components.value(treeNameComponents.value(name));
+ if (!(parent && parent->treeNameMoveChildren()))
+ continue; // TreeName only applied to parent
+
+ if (newName.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()
+ > name.split(QLatin1Char('.'), Qt::SkipEmptyParts).count()) {
+ continue;
+ }
+ newName = name;
+ }
+ if (newName.isEmpty()) // Nothing to do
+ continue;
+
+ const QString treeName = component->name()
+ .replace(newName, treeNameComponents.value(newName));
+
+ if (components.contains(treeName) || treeNameComponents.contains(treeName)) {
+ // Can happen if the parent was moved to an existing identifier (which did not
+ // have a component) and contains child that has a conflicting name with a component
+ // in the existing branch.
+ qCritical() << "Cannot register component" << component->name() << "with automatic "
+ "tree name" << treeName << "! Component with identifier" << treeName << "already exists.";
+
+ if (settings().allowUnstableComponents()) {
+ qCDebug(lcInstallerInstallLog)
+ << "Falling back to using the original indetifier:" << component->name();
+
+ const QString errorString = QLatin1String("Tree name conflicts with an existing indentifier");
+ d->m_pendingUnstableComponents.insert(component->name(),
+ QPair<Component::UnstableError, QString>(Component::InvalidTreeName, errorString));
+ } else {
+ i.remove();
+ }
+ continue;
+ }
+ component->setValue(scAutoTreeName, treeName);
+
+ i.remove();
+ componentsTemp.insert(treeName, component);
+ }
+ components.insert(componentsTemp);
+}
+
void PackageManagerCore::restoreCheckState()
{
d->restoreCheckState();
@@ -2933,7 +4847,7 @@ void PackageManagerCore::updateDisplayVersions(const QString &displayKey)
const QString displayVersionRemote = findDisplayVersion(key, componentsHash,
scVersion, visited);
if (displayVersionRemote.isEmpty())
- componentsHash.value(key)->setValue(displayKey, tr("invalid"));
+ componentsHash.value(key)->setValue(displayKey, tr("Invalid"));
else
componentsHash.value(key)->setValue(displayKey, displayVersionRemote);
}
@@ -2974,22 +4888,30 @@ ComponentModel *PackageManagerCore::componentModel(PackageManagerCore *core, con
ComponentModel::tr("Release Date"));
model->setHeaderData(ComponentModelHelper::UncompressedSizeColumn, Qt::Horizontal,
ComponentModel::tr("Size"));
- connect(model, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)), this,
- SLOT(componentsToInstallNeedsRecalculation()));
return model;
}
+/*!
+ Returns the file list used for delayed deletion.
+*/
QStringList PackageManagerCore::filesForDelayedDeletion() const
{
return d->m_filesForDelayedDeletion;
}
+/*!
+ Adds \a files for delayed deletion.
+*/
void PackageManagerCore::addFilesForDelayedDeletion(const QStringList &files)
{
d->m_filesForDelayedDeletion.append(files);
}
+/*!
+ Adds a colon symbol to the component \c name as a separator between
+ component \a name and version.
+*/
QString PackageManagerCore::checkableName(const QString &name)
{
// to ensure backward compatibility, fix component name with dash (-) symbol
@@ -3000,6 +4922,10 @@ QString PackageManagerCore::checkableName(const QString &name)
return name;
}
+/*!
+ Parses \a name and \a version from \a requirement component. \c requirement
+ contains both \a name and \a version separated either with ':' or with '-'.
+*/
void PackageManagerCore::parseNameAndVersion(const QString &requirement, QString *name, QString *version)
{
if (requirement.isEmpty()) {
@@ -3027,6 +4953,12 @@ void PackageManagerCore::parseNameAndVersion(const QString &requirement, QString
}
}
+/*!
+ Excludes version numbers from names from \a requirements components.
+ \a requirements list contains names that have both name and version.
+
+ Returns a list containing names without version numbers.
+*/
QStringList PackageManagerCore::parseNames(const QStringList &requirements)
{
QString name;
diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h
index f4240fde0..d9da87d85 100644
--- a/src/libs/installer/packagemanagercore.h
+++ b/src/libs/installer/packagemanagercore.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,23 +29,31 @@
#define PACKAGEMANAGERCORE_H
#include "binaryformat.h"
+#include "binarycontent.h"
+#include "component.h"
#include "protocol.h"
#include "repository.h"
#include "qinstallerglobal.h"
+#include "utils.h"
+#include "commandlineparser.h"
#include <QtCore/QHash>
#include <QtCore/QObject>
#include <QtCore/QStringList>
-#include <QtCore/QVector>
+#include <QtCore/QList>
+#include <QSettings>
+#include <QModelIndex>
namespace QInstaller {
-class Component;
+struct AliasSource;
class ComponentModel;
+class ComponentAlias;
class ScriptEngine;
class PackageManagerCorePrivate;
class PackageManagerProxyFactory;
class Settings;
+class ComponentSortFilterProxyModel;
// -- PackageManagerCore
@@ -60,27 +68,24 @@ class INSTALLER_EXPORT PackageManagerCore : public QObject
public:
PackageManagerCore();
PackageManagerCore(qint64 magicmaker, const QList<OperationBlob> &ops,
+ const QString &datFilename = QString(),
const QString &socketName = QString(),
const QString &key = QLatin1String(Protocol::DefaultAuthorizationKey),
- Protocol::Mode mode = Protocol::Mode::Production);
+ Protocol::Mode mode = Protocol::Mode::Production,
+ const QHash<QString, QString> &params = QHash<QString, QString>(),
+ const bool commandLineInstance = false);
~PackageManagerCore();
- enum UnstableError {
- DepencyToUnstable = 0,
- ShaMismatch,
- ScriptLoadingFailed,
- MissingDependency
- };
- Q_ENUM(UnstableError)
-
// status
enum Status {
Success = EXIT_SUCCESS,
Failure = EXIT_FAILURE,
- Running,
- Canceled,
- Unfinished,
- ForceUpdate
+ Running = 2,
+ Canceled = 3,
+ Unfinished = 4,
+ ForceUpdate = 5,
+ EssentialUpdated = 6,
+ NoPackagesFound = 7
};
Status status() const;
QString error() const;
@@ -105,6 +110,14 @@ public:
AllNoReplacements = (Root | Descendants | Dependencies),
All = (Root | Descendants | Dependencies | Replacements)
};
+
+ struct DownloadItem
+ {
+ QString fileName;
+ QString sourceUrl;
+ bool checkSha1CheckSum;
+ };
+
Q_DECLARE_FLAGS(ComponentTypes, ComponentType)
static QFont virtualComponentsFont();
@@ -116,24 +129,30 @@ public:
static bool noForceInstallation();
static void setNoForceInstallation(bool value);
+ static bool noDefaultInstallation();
+ static void setNoDefaultInstallation(bool value);
+
static bool createLocalRepositoryFromBinary();
static void setCreateLocalRepositoryFromBinary(bool create);
+ static int maxConcurrentOperations();
+ static void setMaxConcurrentOperations(int count);
+
static Component *componentByName(const QString &name, const QList<Component *> &components);
bool directoryWritable(const QString &path) const;
- bool subdirectoriesWritable(const QString &path) const;
bool fetchLocalPackagesTree();
- LocalPackagesHash localInstalledPackages();
+ LocalPackagesMap localInstalledPackages();
void networkSettingsChanged();
PackageManagerProxyFactory *proxyFactory() const;
void setProxyFactory(PackageManagerProxyFactory *factory);
PackagesList remotePackages();
- bool fetchRemotePackagesTree();
+ bool fetchRemotePackagesTree(const QStringList& components = QStringList());
bool fetchCompressedPackagesTree();
+ bool fetchPackagesWithFallbackRepositories(const QStringList& components, bool &fallBackReposFetched);
bool run();
void reset();
@@ -162,17 +181,17 @@ public:
Q_INVOKABLE static QString findPath(const QString &name, const QStringList &paths = QStringList());
Q_INVOKABLE void setInstallerBaseBinary(const QString &path);
+ QString installerBaseBinary() const;
+ void setOfflineBaseBinary(const QString &path);
+
+ void addResourcesForOfflineGeneration(const QString &rcPath);
// parameter handling
Q_INVOKABLE bool containsValue(const QString &key) const;
Q_INVOKABLE void setValue(const QString &key, const QString &value);
- Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString()) const;
+ Q_INVOKABLE QString value(const QString &key, const QString &defaultValue = QString(), const int &format = QSettings::NativeFormat) const;
Q_INVOKABLE QStringList values(const QString &key, const QStringList &defaultValue = QStringList()) const;
-
- // a way to have global flags shareable from a component script to another one
- // Deprecated since 2.0.0
- Q_INVOKABLE bool sharedFlag(const QString &key) const;
- Q_INVOKABLE void setSharedFlag(const QString &key, bool value = true);
+ Q_INVOKABLE QString key(const QString &value) const;
QString replaceVariables(const QString &str) const;
QByteArray replaceVariables(const QByteArray &str) const;
@@ -181,24 +200,53 @@ public:
void writeMaintenanceTool();
void writeMaintenanceConfigFiles();
+ void disableWriteMaintenanceTool(bool disable = true);
+
QString maintenanceToolName() const;
QString installerBinaryPath() const;
- bool testChecksum() const;
- void setTestChecksum(bool test);
+ void setOfflineBinaryName(const QString &name);
+ QString offlineBinaryName() const;
+
+ void addAliasSource(const AliasSource &source);
Q_INVOKABLE void addUserRepositories(const QStringList &repositories);
Q_INVOKABLE void setTemporaryRepositories(const QStringList &repositories,
bool replace = false, bool compressed = false);
+ bool addQBspRepositories(const QStringList &repositories);
+ bool validRepositoriesAvailable() const;
+ Q_INVOKABLE void setAllowCompressedRepositoryInstall(bool allow);
+ bool allowCompressedRepositoryInstall() const;
+ bool showRepositoryCategories() const;
+ QVariantMap organizedRepositoryCategories() const;
+ void enableRepositoryCategory(const QString &repositoryName, bool enable);
+ void runProgram();
+
Q_INVOKABLE void autoAcceptMessageBoxes();
Q_INVOKABLE void autoRejectMessageBoxes();
Q_INVOKABLE void setMessageBoxAutomaticAnswer(const QString &identifier, int button);
+ Q_INVOKABLE void acceptMessageBoxDefaultButton();
+
+ Q_INVOKABLE void setAutoAcceptLicenses();
+ Q_INVOKABLE void setFileDialogAutomaticAnswer(const QString &identifier, const QString &value);
+ Q_INVOKABLE void removeFileDialogAutomaticAnswer(const QString &identifier);
+ Q_INVOKABLE bool containsFileDialogAutomaticAnswer(const QString &identifier) const;
+ QHash<QString, QString> fileDialogAutomaticAnswers() const;
+
+ void setAutoConfirmCommand();
quint64 size(QInstaller::Component *component, const QString &value) const;
Q_INVOKABLE bool isFileExtensionRegistered(const QString &extension) const;
Q_INVOKABLE bool fileExists(const QString &filePath) const;
Q_INVOKABLE QString readFile(const QString &filePath, const QString &codecName) const;
+ Q_INVOKABLE QString readConsoleLine(const QString &title = QString(), qint64 maxlen = 0) const;
+
+ Q_INVOKABLE QString toNativeSeparators(const QString &path);
+ Q_INVOKABLE QString fromNativeSeparators(const QString &path);
+
+ bool installationAllowedToDirectory(const QString &targetDirectory);
+ QString targetDirWarning(const QString &targetDirectory) const;
public:
ScriptEngine *componentScriptEngine() const;
@@ -209,26 +257,52 @@ public:
void appendRootComponent(Component *components);
void appendUpdaterComponent(Component *components);
- QList<Component *> components(ComponentTypes mask) const;
- Component *componentByName(const QString &identifier) const;
+ QList<Component *> components(ComponentTypes mask, const QString &regexp = QString()) const;
+ Q_INVOKABLE QInstaller::Component *componentByName(const QString &identifier) const;
+ Q_INVOKABLE QList<QInstaller::Component *> components(const QString &regexp = QString()) const;
+
+ ComponentAlias *aliasByName(const QString &name) const;
Q_INVOKABLE bool calculateComponentsToInstall() const;
QList<Component*> orderedComponentsToInstall() const;
- bool calculateComponents(QString *displayString);
+
+ Q_INVOKABLE bool recalculateAllComponents();
+ QString componentResolveReasons() const;
Q_INVOKABLE bool calculateComponentsToUninstall() const;
QList<Component*> componentsToUninstall() const;
+ QList<Component *> componentsMarkedForInstallation() const;
+ QList<ComponentAlias *> aliasesMarkedForInstallation() const;
+
QString componentsToInstallError() const;
+ QString componentsToUninstallError() const;
QString installReason(Component *component) const;
+ QString uninstallReason(Component *component) const;
QList<Component*> dependees(const Component *component) const;
+ bool isDependencyForRequestedComponent(const Component *component) const;
+ QStringList localDependenciesToComponent(const Component *component) const;
ComponentModel *defaultComponentModel() const;
ComponentModel *updaterComponentModel() const;
- void updateComponentsSilently();
+ ComponentSortFilterProxyModel *componentSortFilterProxyModel();
+
+ void listInstalledPackages(const QString &regexp = QString());
+ bool listAvailablePackages(const QString &regexp = QString(),
+ const QHash<QString, QString> &filters = QHash<QString, QString>());
+ bool listAvailableAliases(const QString &regexp = QString());
+
+ PackageManagerCore::Status searchAvailableUpdates();
+ PackageManagerCore::Status updateComponentsSilently(const QStringList &componentsToUpdate);
+ PackageManagerCore::Status installSelectedComponentsSilently(const QStringList& components);
+ PackageManagerCore::Status installDefaultComponentsSilently();
+ PackageManagerCore::Status uninstallComponentsSilently(const QStringList& components);
+ PackageManagerCore::Status removeInstallationSilently();
+ PackageManagerCore::Status createOfflineInstaller(const QStringList &componentsToAdd);
// convenience
+ Q_INVOKABLE void setInstaller();
Q_INVOKABLE bool isInstaller() const;
Q_INVOKABLE bool isOfflineOnly() const;
@@ -241,6 +315,21 @@ public:
Q_INVOKABLE void setPackageManager();
Q_INVOKABLE bool isPackageManager() const;
+ void setOfflineGenerator();
+ Q_INVOKABLE bool isOfflineGenerator() const;
+
+ void setPackageViewer();
+ Q_INVOKABLE bool isPackageViewer() const;
+
+ void resetBinaryMarkerSupplement();
+
+ void setUserSetBinaryMarker(qint64 magicMarker);
+ Q_INVOKABLE bool isUserSetBinaryMarker() const;
+
+ void setCommandLineInstance(bool commandLineInstance);
+ Q_INVOKABLE bool isCommandLineInstance() const;
+ Q_INVOKABLE bool isCommandLineDefaultInstall() const;
+
bool isMaintainer() const;
bool isVerbose() const;
@@ -248,22 +337,32 @@ public:
Q_INVOKABLE bool gainAdminRights();
Q_INVOKABLE void dropAdminRights();
+ Q_INVOKABLE bool hasAdminRights() const;
+
+ void setCheckAvailableSpace(bool check);
+ bool checkAvailableSpace();
+ QString availableSpaceMessage() const;
Q_INVOKABLE quint64 requiredDiskSpace() const;
Q_INVOKABLE quint64 requiredTemporaryDiskSpace() const;
Q_INVOKABLE bool isProcessRunning(const QString &name) const;
Q_INVOKABLE bool killProcess(const QString &absoluteFilePath) const;
+ Q_INVOKABLE void setAllowedRunningProcesses(const QStringList &processes);
+ Q_INVOKABLE QStringList allowedRunningProcesses() const;
Settings &settings() const;
Q_INVOKABLE bool addWizardPage(QInstaller::Component *component, const QString &name, int page);
Q_INVOKABLE bool removeWizardPage(QInstaller::Component *component, const QString &name);
- Q_INVOKABLE bool addWizardPageItem(QInstaller::Component *component, const QString &name, int page);
+ Q_INVOKABLE bool addWizardPageItem(QInstaller::Component *component, const QString &name,
+ int page, int position = 100);
Q_INVOKABLE bool removeWizardPageItem(QInstaller::Component *component, const QString &name);
Q_INVOKABLE bool setDefaultPageVisible(int page, bool visible);
Q_INVOKABLE void setValidatorForCustomPage(QInstaller::Component *component, const QString &name,
const QString &callbackName);
+ Q_INVOKABLE void selectComponent(const QString &id);
+ Q_INVOKABLE void deselectComponent(const QString &id);
void rollBackInstallation();
@@ -282,18 +381,36 @@ public:
static QString checkableName(const QString &name);
static void parseNameAndVersion(const QString &requirement, QString *name, QString *version);
static QStringList parseNames(const QStringList &requirements);
+ void commitSessionOperations();
+ void clearLicenses();
+ QHash<QString, QMap<QString, QString>> sortedLicenses();
+ void addLicenseItem(const QHash<QString, QVariantMap> &licenses);
+ bool hasLicenses() const;
+ void createLocalDependencyHash(const QString &component, const QString &dependencies) const;
+ void createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies) const;
+
+ bool resetLocalCache(bool init = false);
+ bool clearLocalCache(QString *error = nullptr);
+ bool isValidCache() const;
+
+ template <typename T>
+ bool loadComponentScripts(const T &components, const bool postScript = false);
+
+ void saveGivenArguments(const QStringList &args);
+ QStringList givenArguments() const;
public Q_SLOTS:
bool runInstaller();
bool runUninstaller();
bool runPackageUpdater();
+ bool runOfflineGenerator();
void interrupt();
void setCanceled();
void languageChanged();
void setCompleteUninstallation(bool complete);
void cancelMetaInfoJob();
- void componentsToInstallNeedsRecalculation();
- void clearComponentsToInstallCalculated();
+ void componentsToInstallNeedsRecalculation(); // TODO: deprecated, remove
+ void clearComponentsToInstallCalculated() {} // TODO: deprecated, remove
Q_SIGNALS:
void aboutCalculateComponentsToInstall() const;
@@ -301,10 +418,9 @@ Q_SIGNALS:
void aboutCalculateComponentsToUninstall() const;
void finishedCalculateComponentsToUninstall() const;
void componentAdded(QInstaller::Component *comp);
- void rootComponentsAdded(QList<QInstaller::Component*> components);
- void updaterComponentsAdded(QList<QInstaller::Component*> components);
void valueChanged(const QString &key, const QString &value);
void statusChanged(QInstaller::PackageManagerCore::Status);
+ void defaultTranslationsLoadedForLanguage(QLocale lang);
void currentPageChanged(int page);
void finishButtonClicked();
@@ -324,11 +440,15 @@ Q_SIGNALS:
void updateFinished();
void uninstallationStarted();
void uninstallationFinished();
+ void offlineGenerationStarted();
+ void offlineGenerationFinished();
void titleMessageChanged(const QString &title);
+ void downloadArchivesFinished();
void wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
void wizardPageRemovalRequested(QWidget *widget);
- void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
+ void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page,
+ int position);
void wizardWidgetRemovalRequested(QWidget *widget);
void wizardPageVisibilityChangeRequested(bool visible, int page);
void setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name,
@@ -339,31 +459,42 @@ Q_SIGNALS:
void guiObjectChanged(QObject *gui);
void unstableComponentFound(const QString &type, const QString &errorMessage, const QString &component);
+ void installerBinaryMarkerChanged(qint64 magicMarker);
+ void componentsRecalculated();
private:
struct Data {
Package *package;
QHash<QString, Component*> *components;
- const LocalPackagesHash *installedPackages;
+ const LocalPackagesMap *installedPackages;
QHash<Component*, QStringList> replacementToExchangeables;
};
bool updateComponentData(struct Data &data, QInstaller::Component *component);
- void storeReplacedComponents(QHash<QString, Component*> &components, const struct Data &data);
- bool fetchAllPackages(const PackagesList &remotePackages, const LocalPackagesHash &localPackages);
- bool fetchUpdaterPackages(const PackagesList &remotePackages, const LocalPackagesHash &localPackages);
+ void storeReplacedComponents(QHash<QString, Component*> &components, const struct Data &data,
+ QMap<QString, QString> *const treeNameComponents = nullptr);
+ bool fetchAllPackages(const PackagesList &remotePackages, const LocalPackagesMap &localPackages);
+ bool fetchUpdaterPackages(const PackagesList &remotePackages, const LocalPackagesMap &localPackages);
+
+ void createAutoTreeNames(QHash<QString, Component *> &components,
+ const QMap<QString, QString> &treeNameComponents);
void updateDisplayVersions(const QString &displayKey);
QString findDisplayVersion(const QString &componentName, const QHash<QString, QInstaller::Component*> &components,
const QString& versionKey, QHash<QString, bool> &visited);
ComponentModel *componentModel(PackageManagerCore *core, const QString &objectName) const;
- QList<Component *> componentsMarkedForInstallation() const;
- bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesHash installedPackages);
+ bool fetchPackagesTree(const PackagesList &packages, const LocalPackagesMap installedPackages);
+ bool componentUninstallableFromCommandLine(const QString &componentName);
+ bool checkComponentsForInstallation(const QStringList &names, QString &errorMessage, bool &unstableAliasFound, bool fallbackReposFetched);
private:
PackageManagerCorePrivate *const d;
friend class PackageManagerCorePrivate;
+ QHash<QString, QString> m_fileDialogAutomaticAnswers;
+ QHash<QString, QStringList> m_localVirtualWithDependants;
+ QString m_availableSpaceMessage;
+ QStringList m_arguments;
private:
// remove once we deprecate isSelected, setSelected etc...
@@ -375,5 +506,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(PackageManagerCore::ComponentTypes)
}
Q_DECLARE_METATYPE(QInstaller::PackageManagerCore*)
+Q_DECLARE_METATYPE(QInstaller::PackageManagerCore::Status)
#endif // PACKAGEMANAGERCORE_H
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 308bfa097..0ed4bf6d8 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,7 +31,6 @@
#include "binarycontent.h"
#include "binaryformatenginehandler.h"
#include "binarylayout.h"
-#include "component.h"
#include "scriptengine.h"
#include "componentmodel.h"
#include "errors.h"
@@ -46,17 +45,25 @@
#include "qsettingswrapper.h"
#include "installercalculator.h"
#include "uninstallercalculator.h"
+#include "componentalias.h"
#include "componentchecker.h"
#include "globals.h"
+#include "binarycreator.h"
+#include "loggingutils.h"
+#include "concurrentoperationrunner.h"
+#include "remoteclient.h"
+#include "operationtracer.h"
#include "selfrestarter.h"
#include "filedownloaderfactory.h"
#include "updateoperationfactory.h"
+#include "constants.h"
#include <productkeycheck.h>
#include <QSettings>
#include <QtConcurrentRun>
+#include <QtConcurrent>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
@@ -79,45 +86,24 @@
namespace QInstaller {
-class OperationTracer
-{
-public:
- OperationTracer(Operation *operation) : m_operation(nullptr)
- {
- // don't create output for that hacky pseudo operation
- if (operation->name() != QLatin1String("MinimumProgress"))
- m_operation = operation;
- }
- void trace(const QString &state)
- {
- if (!m_operation)
- return;
- qDebug().noquote() << QString::fromLatin1("%1 %2 operation: %3").arg(state, m_operation->value(
- QLatin1String("component")).toString(), m_operation->name());
- qDebug().noquote() << QString::fromLatin1("\t- arguments: %1").arg(m_operation->arguments()
- .join(QLatin1String(", ")));
- }
- ~OperationTracer() {
- if (!m_operation)
- return;
- qDebug() << "Done";
- }
-private:
- Operation *m_operation;
-};
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::PackageManagerCorePrivate
+ \internal
+*/
-static bool runOperation(Operation *operation, PackageManagerCorePrivate::OperationType type)
+static bool runOperation(Operation *operation, Operation::OperationType type)
{
OperationTracer tracer(operation);
switch (type) {
- case PackageManagerCorePrivate::Backup:
+ case Operation::Backup:
tracer.trace(QLatin1String("backup"));
operation->backup();
return true;
- case PackageManagerCorePrivate::Perform:
+ case Operation::Perform:
tracer.trace(QLatin1String("perform"));
return operation->performOperation();
- case PackageManagerCorePrivate::Undo:
+ case Operation::Undo:
tracer.trace(QLatin1String("undo"));
return operation->undoOperation();
default:
@@ -137,75 +123,67 @@ static QStringList checkRunningProcessesFromList(const QStringList &processList)
return stillRunningProcesses;
}
-static void deferredRename(const QString &oldName, const QString &newName, bool restart = false)
+static bool filterMissingAliasesToInstall(const QString& component, const QList<ComponentAlias *> packages)
{
-#ifdef Q_OS_WIN
- QStringList arguments;
-
- // Check if .vbs extension can be used for running renaming script. If not, create own extension
- QString extension = QLatin1String(".vbs");
- QSettingsWrapper settingRoot(QLatin1String("HKEY_CLASSES_ROOT\\.vbs"), QSettingsWrapper::NativeFormat);
- if (settingRoot.value(QLatin1String(".")).toString() != QLatin1String("VBSFile")) {
- extension = QLatin1String(".qtInstaller");
- QSettingsWrapper settingsUser(QLatin1String("HKEY_CURRENT_USER\\Software\\Classes"), QSettingsWrapper::NativeFormat);
- QString value = settingsUser.value(extension).toString();
- if (value != QLatin1String("VBSFile"))
- settingsUser.setValue(extension, QLatin1String("VBSFile"));
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->name() == component);
+ if (packageFound)
+ break;
}
- QTemporaryFile f(QDir::temp().absoluteFilePath(QLatin1String("deferredrenameXXXXXX%1")).arg(extension));
-
- QInstaller::openForWrite(&f);
- f.setAutoRemove(false);
-
- arguments << QDir::toNativeSeparators(f.fileName()) << QDir::toNativeSeparators(oldName)
- << QDir::toNativeSeparators(QFileInfo(oldName).dir().absoluteFilePath(QFileInfo(newName)
- .fileName()));
-
- QTextStream batch(&f);
- batch.setCodec("UTF-16");
- batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
- batch << "Set tmp = WScript.CreateObject(\"WScript.Shell\")\n";
- batch << QString::fromLatin1("file = \"%1\"\n").arg(arguments[2]);
- batch << "on error resume next\n";
+ return !packageFound;
+}
- batch << "while fso.FileExists(file)\n";
- batch << " fso.DeleteFile(file)\n";
- batch << " WScript.Sleep(1000)\n";
- batch << "wend\n";
- batch << QString::fromLatin1("fso.MoveFile \"%1\", file\n").arg(arguments[1]);
- if (restart) {
- //Restart with same command line arguments as first executable
- QStringList commandLineArguments = QCoreApplication::arguments();
- batch << QString::fromLatin1("tmp.exec \"%1 --updater").arg(arguments[2]);
- //Skip the first argument as that is executable itself
- for (int i = 1; i < commandLineArguments.count(); i++) {
- batch << QString::fromLatin1(" %1").arg(commandLineArguments.at(i));
- }
- batch << QString::fromLatin1("\"\n");
+static bool filterMissingPackagesToInstall(const QString& component, const PackagesList& packages)
+{
+ bool packageFound = false;
+ for (qsizetype i = 0; i < packages.size(); ++i) {
+ packageFound = (packages.at(i)->data(scName).toString() == component);
+ if (packageFound)
+ break;
}
- batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
-
- QProcessWrapper::startDetached(QLatin1String("cscript"), QStringList() << QLatin1String("//Nologo")
- << arguments[0]);
-#else
- QFile::remove(newName);
- QFile::rename(oldName, newName);
- SelfRestarter::setRestartOnQuit(restart);
-#endif
+ return !packageFound;
}
+static QString getAppBundlePath() {
+ QString appDirPath = QCoreApplication::applicationDirPath();
+ QDir dir(appDirPath);
+ while (!dir.isRoot()) {
+ if (dir.dirName().endsWith(QLatin1String(".app")))
+ return dir.absolutePath();
+ dir.cdUp();
+ }
+ return QString();
+}
// -- PackageManagerCorePrivate
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
: m_updateFinder(nullptr)
- , m_compressedFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
+ , m_status(PackageManagerCore::Unfinished)
+ , m_needsHardRestart(false)
+ , m_testChecksum(false)
+ , m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
+ , m_completeUninstall(false)
+ , m_needToWriteMaintenanceTool(false)
+ , m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
- , m_componentsToInstallCalculated(false)
+ , m_magicBinaryMarker(0) // initialize with pseudo marker
+ , m_magicMarkerSupplement(BinaryContent::Default)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -213,29 +191,46 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core)
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(nullptr)
+ , m_datFileName(QString())
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
}
PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
- const QList<OperationBlob> &performedOperations)
+ const QList<OperationBlob> &performedOperations, const QString &datFileName)
: m_updateFinder(nullptr)
- , m_compressedFinder(nullptr)
+ , m_aliasFinder(nullptr)
, m_localPackageHub(std::make_shared<LocalPackageHub>())
, m_status(PackageManagerCore::Unfinished)
, m_needsHardRestart(false)
, m_testChecksum(false)
, m_launchedAsRoot(AdminAuthorization::hasAdminRights())
+ , m_commandLineInstance(false)
+ , m_defaultInstall(false)
+ , m_userSetBinaryMarker(false)
+ , m_checkAvailableSpace(true)
, m_completeUninstall(false)
, m_needToWriteMaintenanceTool(false)
, m_dependsOnLocalInstallerBinary(false)
+ , m_autoAcceptLicenses(false)
+ , m_disableWriteMaintenanceTool(false)
+ , m_autoConfirmCommand(false)
, m_core(core)
, m_updates(false)
+ , m_aliases(false)
, m_repoFetched(false)
, m_updateSourcesAdded(false)
, m_magicBinaryMarker(magicInstallerMaker)
- , m_componentsToInstallCalculated(false)
+ , m_magicMarkerSupplement(BinaryContent::Default)
+ , m_foundEssentialUpdate(false)
, m_componentScriptEngine(nullptr)
, m_controlScriptEngine(nullptr)
, m_installerCalculator(nullptr)
@@ -243,22 +238,32 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
, m_proxyFactory(nullptr)
, m_defaultModel(nullptr)
, m_updaterModel(nullptr)
+ , m_componentSortFilterProxyModel(nullptr)
, m_guiObject(nullptr)
, m_remoteFileEngineHandler(new RemoteFileEngineHandler)
+ , m_datFileName(datFileName)
+#ifdef INSTALLCOMPRESSED
+ , m_allowCompressedRepositoryInstall(true)
+#else
+ , m_allowCompressedRepositoryInstall(false)
+#endif
+ , m_connectedOperations(0)
{
foreach (const OperationBlob &operation, performedOperations) {
- QScopedPointer<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
+ std::unique_ptr<QInstaller::Operation> op(KDUpdater::UpdateOperationFactory::instance()
.create(operation.name, core));
- if (op.isNull()) {
- qWarning() << "Failed to load unknown operation" << operation.name;
+ if (!op) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load unknown operation"
+ << operation.name;
continue;
}
if (!op->fromXml(operation.xml)) {
- qWarning() << "Failed to load XML for operation" << operation.name;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to load XML for operation"
+ << operation.name;
continue;
}
- m_performedOperationsOld.append(op.take());
+ m_performedOperationsOld.append(op.release());
}
connect(this, &PackageManagerCorePrivate::installationStarted,
@@ -269,6 +274,10 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q
m_core, &PackageManagerCore::uninstallationStarted);
connect(this, &PackageManagerCorePrivate::uninstallationFinished,
m_core, &PackageManagerCore::uninstallationFinished);
+ connect(this, &PackageManagerCorePrivate::offlineGenerationStarted,
+ m_core, &PackageManagerCore::offlineGenerationStarted);
+ connect(this, &PackageManagerCorePrivate::offlineGenerationFinished,
+ m_core, &PackageManagerCore::offlineGenerationFinished);
}
PackageManagerCorePrivate::~PackageManagerCorePrivate()
@@ -283,6 +292,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate()
qDeleteAll(m_performedOperationsCurrentSession);
delete m_updateFinder;
+ delete m_aliasFinder;
delete m_proxyFactory;
delete m_defaultModel;
@@ -292,7 +302,7 @@ PackageManagerCorePrivate::~PackageManagerCorePrivate()
// delete m_gui;
}
-/*!
+/*
Return true, if a process with \a name is running. On Windows, comparison is case-insensitive.
*/
/* static */
@@ -324,7 +334,8 @@ bool PackageManagerCorePrivate::isProcessRunning(const QString &name,
}
/* static */
-bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation, OperationType type)
+bool PackageManagerCorePrivate::performOperationThreaded(Operation *operation,
+ Operation::OperationType type)
{
QFutureWatcher<bool> futureWatcher;
const QFuture<bool> future = QtConcurrent::run(runOperation, operation, type);
@@ -347,23 +358,8 @@ QString PackageManagerCorePrivate::targetDir() const
bool PackageManagerCorePrivate::directoryWritable(const QString &path) const
{
- QTemporaryFile tempFile(path + QStringLiteral("/tempFile") + QString::number(qrand() % 1000));
- if (!tempFile.open() || !tempFile.isWritable())
- return false;
- else
- return true;
-}
-
-bool PackageManagerCorePrivate::subdirectoriesWritable(const QString &path) const
-{
- // Iterate over target directory subdirectories for writing access
- QDirIterator iterator(path, QDir::AllDirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
- while (iterator.hasNext()) {
- QTemporaryFile tempFile(iterator.next() + QLatin1String("/tempFile"));
- if (!tempFile.open() || !tempFile.isWritable())
- return false;
- }
- return true;
+ QTemporaryFile tempFile(path + QLatin1String("/tempFile.XXXXXX"));
+ return (tempFile.open() && tempFile.isWritable());
}
QString PackageManagerCorePrivate::configurationFileName() const
@@ -400,11 +396,10 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
m_core->appendRootComponent(component);
}
- // after everything is set up, load the scripts if needed
- if (loadScript) {
- foreach (QInstaller::Component *component, components)
- component->loadComponentScript();
- }
+ // after everything is set up, load the scripts if needed and create helper hashes
+ // for autodependency and dependency components for quicker search later
+ if (loadScript && !loadComponentScripts(components))
+ return false;
// now we can preselect components in the tree
foreach (QInstaller::Component *component, components) {
@@ -429,25 +424,25 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
component->setCheckState(Qt::Checked);
clearInstallerCalculator();
- if (installerCalculator()->appendComponentsToInstall(components.values()) == false) {
+ if (installerCalculator()->solve(components.values()) == false) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
- tr("Unresolved dependencies"), installerCalculator()->componentsToInstallError());
+ tr("Unresolved dependencies"), installerCalculator()->error());
return false;
}
restoreCheckState();
- if (m_core->isVerbose()) {
+ if (LoggingHandler::instance().verboseLevel() == LoggingHandler::Detailed) {
foreach (QInstaller::Component *component, components) {
const QStringList warnings = ComponentChecker::checkComponent(component);
foreach (const QString &warning, warnings)
- qCWarning(lcComponentChecker).noquote() << warning;
+ qCWarning(lcDeveloperBuild).noquote() << warning;
}
}
} catch (const Error &error) {
clearAllComponentLists();
- emit m_core->finishAllComponentsReset(QList<QInstaller::Component*>());
setStatus(PackageManagerCore::Failure, error.message());
// TODO: make sure we remove all message boxes inside the library at some point.
@@ -458,8 +453,154 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash<QString, Component*> &c
return true;
}
+bool PackageManagerCorePrivate::buildComponentAliases()
+{
+ // For now, aliases are only used for command line runs
+ if (!m_core->isCommandLineInstance())
+ return true;
+
+ {
+ const QList<ComponentAlias *> aliasList = componentAliases();
+ if (aliasList.isEmpty())
+ return true;
+
+ for (const auto *alias : aliasList) {
+ // Create a new alias object for package manager core to take ownership of
+ ComponentAlias *newAlias = new ComponentAlias(m_core);
+ for (const QString &key : alias->keys())
+ newAlias->setValue(key, alias->value(key));
+
+ m_componentAliases.insert(alias->name(), newAlias);
+ }
+ }
+
+ if (m_core->isPackageViewer())
+ return true;
+ // After aliases are loaded, perform sanity checks:
+
+ // 1. Component check state is changed by alias selection, so store the initial state
+ storeCheckState();
+
+ QStringList aliasNamesSelectedForInstall;
+
+ // 2. Get a list of alias names to be installed, dependency aliases needs to be listed first
+ // to get proper unstable state for parents
+ std::function<void(QStringList)> fetchAliases = [&](QStringList aliases) {
+ for (const QString &aliasName : aliases) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias || aliasNamesSelectedForInstall.contains(aliasName))
+ continue;
+ if (!aliasNamesSelectedForInstall.contains(aliasName))
+ aliasNamesSelectedForInstall.prepend(aliasName);
+ fetchAliases(QStringList() << QInstaller::splitStringWithComma(alias->value(scRequiredAliases))
+ << QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+ }
+ };
+ for (const QString &installComponent : m_componentsToBeInstalled) {
+ ComponentAlias *alias = m_componentAliases.value(installComponent);
+ if (!alias)
+ continue;
+ if (!aliasNamesSelectedForInstall.contains(installComponent))
+ aliasNamesSelectedForInstall.prepend(installComponent);
+ fetchAliases(QStringList() << QInstaller::splitStringWithComma(alias->value(scRequiredAliases))
+ << QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+ }
+
+ Graph<QString> aliasGraph;
+ QList<ComponentAlias *> aliasesSelectedForInstall;
+ for (auto &aliasName : std::as_const(aliasNamesSelectedForInstall)) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias)
+ continue;
+ aliasGraph.addNode(alias->name());
+ aliasGraph.addEdges(alias->name(),
+ QInstaller::splitStringWithComma(alias->value(scRequiredAliases)) <<
+ QInstaller::splitStringWithComma(alias->value(scOptionalAliases)));
+
+ if (!m_core->componentByName(alias->name())) {
+ // Name ok, select for sanity check calculation
+ alias->setSelected(true);
+ } else {
+ alias->setUnstable(ComponentAlias::ComponentNameConfict,
+ tr("Alias declares name that conflicts with an existing component \"%1\"")
+ .arg(alias->name()));
+ }
+ if (!aliasesSelectedForInstall.contains(alias))
+ aliasesSelectedForInstall.append(alias);
+ }
+
+ const QList<QString> sortedAliases = aliasGraph.sort();
+ // 3. Check for cyclic dependency errors
+ if (aliasGraph.hasCycle()) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"),
+ tr("Cyclic dependency between aliases \"%1\" and \"%2\" detected.")
+ .arg(aliasGraph.cycle().first, aliasGraph.cycle().second));
+
+ return false;
+ }
+
+ // 4. Test for required aliases and components, this triggers setting the
+ // alias unstable in case of a broken reference.
+ for (const auto &aliasName : sortedAliases) {
+ ComponentAlias *alias = m_componentAliases.value(aliasName);
+ if (!alias) // sortedAliases may contain dependencies that don't exist, we don't know it yet
+ continue;
+
+ alias->components();
+ alias->aliases();
+ }
+
+ clearInstallerCalculator();
+ // 5. Check for other errors preventing resolving components to install
+ if (!installerCalculator()->solve(aliasesSelectedForInstall)) {
+ setStatus(PackageManagerCore::Failure, installerCalculator()->error());
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Unresolved component aliases"), installerCalculator()->error());
+
+ return false;
+ }
+
+ for (auto *alias : std::as_const(m_componentAliases))
+ alias->setSelected(false);
+
+ // 6. Restore original state
+ restoreCheckState();
+
+ return true;
+}
+
+template <typename T>
+bool PackageManagerCorePrivate::loadComponentScripts(const T &components, const bool postScript)
+{
+ infoMessage(nullptr, tr("Loading component scripts..."));
+
+ quint64 loadedComponents = 0;
+ for (auto *component : components) {
+ if (statusCanceledOrFailed())
+ return false;
+
+ component->loadComponentScript(postScript);
+ ++loadedComponents;
+
+ const int currentProgress = qRound(double(loadedComponents) / components.count() * 100);
+ infoProgress(nullptr, currentProgress, 100);
+ qApp->processEvents();
+ }
+ return true;
+}
+
+template bool PackageManagerCorePrivate::loadComponentScripts<QList<Component *>>(const QList<Component *> &, const bool);
+template bool PackageManagerCorePrivate::loadComponentScripts<QHash<QString, Component *>>(const QHash<QString, Component *> &, const bool);
+
void PackageManagerCorePrivate::cleanUpComponentEnvironment()
{
+ m_componentReplaces.clear();
+ m_autoDependencyComponentHash.clear();
+ m_localDependencyComponentHash.clear();
+ m_localVirtualComponents.clear();
+ m_componentByNameHash.clear();
// clean up registered (downloaded) data
if (m_core->isMaintainer())
BinaryFormatEngineHandler::instance()->clear();
@@ -468,6 +609,10 @@ void PackageManagerCorePrivate::cleanUpComponentEnvironment()
// so we need to remove the current component script engine
delete m_componentScriptEngine;
m_componentScriptEngine = nullptr;
+
+ // Calculators become invalid after clearing components
+ clearInstallerCalculator();
+ clearUninstallerCalculator();
}
ScriptEngine *PackageManagerCorePrivate::componentScriptEngine() const
@@ -486,18 +631,18 @@ ScriptEngine *PackageManagerCorePrivate::controlScriptEngine() const
void PackageManagerCorePrivate::clearAllComponentLists()
{
+ qDeleteAll(m_componentAliases);
+ m_componentAliases.clear();
+
QList<QInstaller::Component*> toDelete;
- toDelete << m_rootComponents;
+ toDelete << m_rootComponents << m_deletedReplacedComponents;
m_rootComponents.clear();
-
m_rootDependencyReplacements.clear();
+ m_deletedReplacedComponents.clear();
- const QList<QPair<Component*, Component*> > list = m_componentsToReplaceAllMode.values();
- for (int i = 0; i < list.count(); ++i)
- toDelete << list.at(i).second;
m_componentsToReplaceAllMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(toDelete);
cleanUpComponentEnvironment();
@@ -505,13 +650,15 @@ void PackageManagerCorePrivate::clearAllComponentLists()
void PackageManagerCorePrivate::clearUpdaterComponentLists()
{
- QSet<Component*> usedComponents =
- QSet<Component*>::fromList(m_updaterComponents + m_updaterComponentsDeps);
+
+ QSet<Component*> usedComponents(m_updaterComponents.begin(), m_updaterComponents.end());
+ usedComponents.unite(QSet<Component*>(m_updaterComponentsDeps.begin(),
+ m_updaterComponentsDeps.end()));
const QList<QPair<Component*, Component*> > list = m_componentsToReplaceUpdaterMode.values();
for (int i = 0; i < list.count(); ++i) {
if (usedComponents.contains(list.at(i).second))
- qWarning() << "a replacement was already in the list - is that correct?";
+ qCWarning(QInstaller::lcDeveloperBuild) << "a replacement was already in the list - is that correct?";
else
usedComponents.insert(list.at(i).second);
}
@@ -522,7 +669,7 @@ void PackageManagerCorePrivate::clearUpdaterComponentLists()
m_updaterDependencyReplacements.clear();
m_componentsToReplaceUpdaterMode.clear();
- m_componentsToInstallCalculated = false;
+ m_foundEssentialUpdate = false;
qDeleteAll(usedComponents);
cleanUpComponentEnvironment();
@@ -538,6 +685,11 @@ QHash<QString, QPair<Component*, Component*> > &PackageManagerCorePrivate::compo
return (!isUpdater()) ? m_componentsToReplaceAllMode : m_componentsToReplaceUpdaterMode;
}
+QHash<QString, QStringList> &PackageManagerCorePrivate::componentReplaces()
+{
+ return m_componentReplaces;
+}
+
void PackageManagerCorePrivate::clearInstallerCalculator()
{
delete m_installerCalculator;
@@ -548,8 +700,7 @@ InstallerCalculator *PackageManagerCorePrivate::installerCalculator() const
{
if (!m_installerCalculator) {
PackageManagerCorePrivate *const pmcp = const_cast<PackageManagerCorePrivate *> (this);
- pmcp->m_installerCalculator = new InstallerCalculator(
- m_core->components(PackageManagerCore::ComponentType::AllNoReplacements));
+ pmcp->m_installerCalculator = new InstallerCalculator(m_core, pmcp->m_autoDependencyComponentHash);
}
return m_installerCalculator;
}
@@ -573,7 +724,8 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const
}
}
- pmcp->m_uninstallerCalculator = new UninstallerCalculator(installedComponents);
+ pmcp->m_uninstallerCalculator = new UninstallerCalculator(m_core,
+ pmcp->m_autoDependencyComponentHash, pmcp->m_localDependencyComponentHash, pmcp->m_localVirtualComponents);
}
return m_uninstallerCalculator;
}
@@ -581,11 +733,10 @@ UninstallerCalculator *PackageManagerCorePrivate::uninstallerCalculator() const
void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params)
{
m_coreCheckedHash.clear();
- m_data = PackageManagerCoreData(params);
- m_componentsToInstallCalculated = false;
+ m_data = PackageManagerCoreData(params, isInstaller());
#ifdef Q_OS_LINUX
- if (m_launchedAsRoot)
+ if (m_launchedAsRoot && isInstaller())
m_data.setValue(scTargetDir, replaceVariables(m_data.settings().adminTargetDir()));
#endif
@@ -595,9 +746,25 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
#else
readMaintenanceConfigFiles(QCoreApplication::applicationDirPath());
#endif
+ // Maintenancetool might have overwritten the variables
+ // user has given from command line. Reset those variables.
+ m_data.setUserDefinedVariables(params);
}
processFilesForDelayedDeletion();
- m_data.setDynamicPredefinedVariables();
+
+ // Set shortcut path for command line interface, in GUI version
+ // we have a separate page where the whole path is set.
+#ifdef Q_OS_WIN
+ if (m_core->isCommandLineInstance() && m_core->isInstaller()) {
+ QString startMenuPath;
+ if (params.value(QLatin1String("AllUsers")) == scTrue)
+ startMenuPath = m_data.value(scAllUsersStartMenuProgramsPath).toString();
+ else
+ startMenuPath = m_data.value(scUserStartMenuProgramsPath).toString();
+ QString startMenuDir = m_core->value(scStartMenuDir, m_core->value(QLatin1String("ProductName")));
+ m_data.setValue(scStartMenuDir, startMenuPath + QDir::separator() + startMenuDir);
+ }
+#endif
disconnect(this, &PackageManagerCorePrivate::installationStarted,
ProgressCoordinator::instance(), &ProgressCoordinator::reset);
@@ -607,6 +774,10 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
ProgressCoordinator::instance(), &ProgressCoordinator::reset);
connect(this, &PackageManagerCorePrivate::uninstallationStarted,
ProgressCoordinator::instance(), &ProgressCoordinator::reset);
+ disconnect(this, &PackageManagerCorePrivate::offlineGenerationStarted,
+ ProgressCoordinator::instance(), &ProgressCoordinator::reset);
+ connect(this, &PackageManagerCorePrivate::offlineGenerationStarted,
+ ProgressCoordinator::instance(), &ProgressCoordinator::reset);
if (!isInstaller())
m_localPackageHub->setFileName(componentsXmlPath());
@@ -620,11 +791,16 @@ void PackageManagerCorePrivate::initialize(const QHash<QString, QString> &params
m_localPackageHub->setApplicationVersion(QLatin1String(QUOTE(IFW_REPOSITORY_FORMAT_VERSION)));
if (isInstaller())
- m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0));
+ m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1));
+
+ const QString aliasFilePath = m_core->settings().aliasDefinitionsFile();
+ if (!aliasFilePath.isEmpty())
+ m_aliasSources.insert(AliasSource(AliasSource::SourceFileFormat::Xml, aliasFilePath, -1));
m_metadataJob.disconnect();
m_metadataJob.setAutoDelete(false);
m_metadataJob.setPackageManagerCore(m_core);
+
connect(&m_metadataJob, &Job::infoMessage, this, &PackageManagerCorePrivate::infoMessage);
connect(&m_metadataJob, &Job::progress, this, &PackageManagerCorePrivate::infoProgress);
connect(&m_metadataJob, &Job::totalProgress, this, &PackageManagerCorePrivate::totalProgress);
@@ -665,6 +841,16 @@ bool PackageManagerCorePrivate::isPackageManager() const
return m_magicBinaryMarker == BinaryContent::MagicPackageManagerMarker;
}
+bool PackageManagerCorePrivate::isOfflineGenerator() const
+{
+ return m_magicMarkerSupplement == BinaryContent::OfflineGenerator;
+}
+
+bool PackageManagerCorePrivate::isPackageViewer() const
+{
+ return m_magicMarkerSupplement == BinaryContent::PackageViewer;
+}
+
bool PackageManagerCorePrivate::statusCanceledOrFailed() const
{
return m_status == PackageManagerCore::Canceled || m_status == PackageManagerCore::Failure;
@@ -674,7 +860,7 @@ void PackageManagerCorePrivate::setStatus(int status, const QString &error)
{
m_error = error;
if (!error.isEmpty())
- qDebug() << m_error;
+ qCWarning(QInstaller::lcInstallerInstallLog) << m_error;
if (m_status != status) {
m_status = status;
emit m_core->statusChanged(PackageManagerCore::Status(m_status));
@@ -717,7 +903,12 @@ Operation *PackageManagerCorePrivate::takeOwnedOperation(Operation *operation)
QString PackageManagerCorePrivate::maintenanceToolName() const
{
- QString filename = m_data.settings().maintenanceToolName();
+ QString filename;
+ if (isInstaller())
+ filename = m_data.settings().maintenanceToolName();
+ else
+ filename = QCoreApplication::applicationName();
+
#if defined(Q_OS_MACOS)
if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
filename += QLatin1String(".app/Contents/MacOS/") + filename;
@@ -727,6 +918,46 @@ QString PackageManagerCorePrivate::maintenanceToolName() const
return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename);
}
+QString PackageManagerCorePrivate::maintenanceToolAliasPath() const
+{
+#ifdef Q_OS_MACOS
+ const QString aliasName = m_data.settings().maintenanceToolAlias();
+ if (aliasName.isEmpty())
+ return QString();
+
+ const bool isRoot = m_core->hasAdminRights();
+ const QString applicationsDir = m_core->value(
+ isRoot ? QLatin1String("ApplicationsDir") : QLatin1String("ApplicationsDirUser")
+ );
+ QString maintenanceToolAlias = QString::fromLatin1("%1/%2")
+ .arg(applicationsDir, aliasName);
+ if (!maintenanceToolAlias.endsWith(QLatin1String(".app")))
+ maintenanceToolAlias += QLatin1String(".app");
+
+ return maintenanceToolAlias;
+#endif
+ return QString();
+}
+
+QString PackageManagerCorePrivate::offlineBinaryName() const
+{
+ QString filename = m_core->value(scOfflineBinaryName, qApp->applicationName()
+ + QLatin1String("_offline-") + QDate::currentDate().toString(Qt::ISODate));
+#if defined(Q_OS_WIN)
+ const QString suffix = QLatin1String(".exe");
+ if (!filename.endsWith(suffix))
+ filename += suffix;
+#endif
+ return QString::fromLatin1("%1/%2").arg(targetDir()).arg(filename);
+}
+
+QString PackageManagerCorePrivate::datFileName()
+{
+ if (m_datFileName.isEmpty())
+ m_datFileName = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName() + QLatin1String(".dat");
+ return m_datFileName;
+}
+
static QNetworkProxy readProxy(QXmlStreamReader &reader)
{
QNetworkProxy proxy(QNetworkProxy::HttpProxy);
@@ -780,12 +1011,12 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
QVariantHash variables; // Do not change to QVariantMap! Breaks existing .ini files,
// cause the variant types do not match while restoring the variables from the file.
- QSettingsWrapper cfg(iniPath, QSettingsWrapper::IniFormat);
+ QSettingsWrapper cfg(iniPath, QSettings::IniFormat);
foreach (const QString &key, m_data.keys()) {
if (key == scRunProgramDescription || key == scRunProgram || key == scRunProgramArguments)
continue;
QVariant value = m_data.value(key);
- if (value.canConvert(QVariant::String))
+ if (value.canConvert<QString>())
value = replacePath(value.toString(), targetDir(), QLatin1String(scRelocatable));
variables.insert(key, value);
}
@@ -805,11 +1036,12 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
: tr("Format error");
throw Error(tr("Cannot write installer configuration to %1: %2").arg(iniPath, reason));
}
+ setDefaultFilePermissions(iniPath, DefaultFilePermissions::NonExecutable);
QFile file(targetDir() + QLatin1Char('/') + QLatin1String("network.xml"));
if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- QXmlStreamWriter writer(&file);
- writer.setCodec("UTF-8");
+ QString outputStr;
+ QXmlStreamWriter writer(&outputStr);
writer.setAutoFormatting(true);
writer.writeStartDocument();
@@ -840,19 +1072,28 @@ void PackageManagerCorePrivate::writeMaintenanceConfigFiles()
writer.writeEndElement();
}
writer.writeEndElement();
+ writer.writeTextElement(QLatin1String("LocalCachePath"), m_data.settings().localCachePath());
writer.writeEndElement();
+
+ file.write(outputStr.toUtf8());
}
+ setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable);
}
void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &targetDir)
{
QSettingsWrapper cfg(targetDir + QLatin1Char('/') + m_data.settings().maintenanceToolIniFile(),
- QSettingsWrapper::IniFormat);
+ QSettings::IniFormat);
const QVariantHash v = cfg.value(QLatin1String("Variables")).toHash(); // Do not change to
// QVariantMap! Breaks reading from existing .ini files, cause the variant types do not match.
for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); ++it) {
- m_data.setValue(it.key(), replacePath(it.value().toString(), QLatin1String(scRelocatable),
- targetDir));
+ if (m_data.contains(it.key()) && !m_data.value(it.key()).isNull()) {
+ // Exception: StartMenuDir should be permanent after initial installation
+ // and must be read from maintenancetool.ini
+ if (it.key() != scStartMenuDir)
+ continue;
+ }
+ m_data.setValue(it.key(), replacePath(it.value().toString(), QLatin1String(scRelocatable), targetDir));
}
QSet<Repository> repos;
const QVariantList variants = cfg.value(QLatin1String("DefaultRepositories"))
@@ -874,7 +1115,7 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
case QXmlStreamReader::StartElement: {
if (reader.name() == QLatin1String("Network")) {
while (reader.readNextStartElement()) {
- const QStringRef name = reader.name();
+ const QStringView name = reader.name();
if (name == QLatin1String("Ftp")) {
m_data.settings().setFtpProxy(readProxy(reader));
} else if (name == QLatin1String("Http")) {
@@ -883,6 +1124,8 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
m_data.settings().addUserRepositories(readRepositories(reader, false));
} else if (name == QLatin1String("ProxyType")) {
m_data.settings().setProxyType(Settings::ProxyType(reader.readElementText().toInt()));
+ } else if (name == QLatin1String("LocalCachePath")) {
+ m_data.settings().setLocalCachePath(reader.readElementText());
} else {
reader.skipCurrentElement();
}
@@ -891,7 +1134,7 @@ void PackageManagerCorePrivate::readMaintenanceConfigFiles(const QString &target
} break;
case QXmlStreamReader::Invalid: {
- qDebug() << reader.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog) << reader.errorString();
} break;
default:
@@ -917,6 +1160,7 @@ void PackageManagerCorePrivate::stopProcessesForUpdates(const QList<Component*>
if (processList.isEmpty())
return;
+ uint retryCount = 5;
while (true) {
const QStringList processes = checkRunningProcessesFromList(processList);
if (processes.isEmpty())
@@ -927,13 +1171,19 @@ void PackageManagerCorePrivate::stopProcessesForUpdates(const QList<Component*>
QLatin1String("stopProcessesForUpdates"), tr("Stop Processes"), tr("These processes "
"should be stopped to continue:\n\n%1").arg(QDir::toNativeSeparators(processes
.join(QLatin1String("\n")))), QMessageBox::Retry | QMessageBox::Ignore
- | QMessageBox::Cancel, QMessageBox::Retry);
+ | QMessageBox::Cancel, QMessageBox::Cancel);
if (button == QMessageBox::Ignore)
return;
if (button == QMessageBox::Cancel) {
m_core->setCanceled();
throw Error(tr("Installation canceled by user"));
}
+ if (!m_core->isCommandLineInstance())
+ continue;
+
+ // Do not allow infinite retries with cli instance
+ if (--retryCount == 0)
+ throw Error(tr("Retry count exceeded"));
}
}
@@ -974,8 +1224,11 @@ void PackageManagerCorePrivate::connectOperationToInstaller(Operation *const ope
connect(m_core, SIGNAL(installationInterrupted()), operationObject, SLOT(cancelOperation()));
if (mo->indexOfSignal(QMetaObject::normalizedSignature("progressChanged(double)")) > -1) {
+ // create unique object names for progress information track
+ operationObject->setObjectName(QLatin1String("operation_%1").arg(QString::number(m_connectedOperations)));
ProgressCoordinator::instance()->registerPartProgress(operationObject,
SIGNAL(progressChanged(double)), operationPartSize);
+ m_connectedOperations++;
}
}
}
@@ -1031,7 +1284,7 @@ void PackageManagerCorePrivate::registerPathsForUninstallation(
void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, qint64 size, bool writeBinaryLayout)
{
QString maintenanceToolRenamedName = maintenanceToolName() + QLatin1String(".new");
- qDebug() << "Writing maintenance tool:" << maintenanceToolRenamedName;
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Writing maintenance tool:" << maintenanceToolRenamedName;
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Writing maintenance tool."));
QFile out(generateTemporaryFileName());
@@ -1054,12 +1307,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
// other code a lot (since installers don't have any appended data either)
QFile dataOut(generateTemporaryFileName());
QInstaller::openForWrite(&dataOut);
- QInstaller::appendInt64(&dataOut, 0); // operations start
- QInstaller::appendInt64(&dataOut, 0); // operations end
- QInstaller::appendInt64(&dataOut, 0); // resource count
- QInstaller::appendInt64(&dataOut, 4 * sizeof(qint64)); // data block size
- QInstaller::appendInt64(&dataOut, BinaryContent::MagicUninstallerMarker);
- QInstaller::appendInt64(&dataOut, BinaryContent::MagicCookie);
+ QInstallerTools::createMTDatFile(dataOut);
{
QFile dummy(resourcePath.filePath(QLatin1String("installer.dat")));
@@ -1073,8 +1321,7 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
throw Error(tr("Cannot write maintenance tool data to %1: %2").arg(dataOut.fileName(),
dataOut.errorString()));
}
- dataOut.setPermissions(dataOut.permissions() | QFile::WriteUser | QFile::ReadGroup
- | QFile::ReadOther);
+ setDefaultFilePermissions(&dataOut, DefaultFilePermissions::NonExecutable);
}
{
@@ -1091,15 +1338,13 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinary(QFile *const input, q
}
QFile mt(maintenanceToolRenamedName);
- if (mt.setPermissions(out.permissions() | QFile::WriteUser | QFile::ReadGroup | QFile::ReadOther
- | QFile::ExeOther | QFile::ExeGroup | QFile::ExeUser)) {
- qDebug() << "Wrote permissions for maintenance tool.";
- } else {
- qDebug() << "Failed to write permissions for maintenance tool.";
- }
+ if (setDefaultFilePermissions(&mt, DefaultFilePermissions::Executable))
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Wrote permissions for maintenance tool.";
+ else
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Failed to write permissions for maintenance tool.";
if (out.exists() && !out.remove()) {
- qWarning() << tr("Cannot remove temporary data file \"%1\": %2")
+ qCWarning(QInstaller::lcInstallerInstallLog) << tr("Cannot remove temporary data file \"%1\": %2")
.arg(out.fileName(), out.errorString());
}
}
@@ -1123,7 +1368,8 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *outp
file.remove(); // clear all possible leftovers
m_core->setValue(QString::fromLatin1("DefaultResourceReplacement"), QString());
} else {
- qWarning() << "Cannot replace default resource with" << QDir::toNativeSeparators(newDefaultResource);
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot replace default resource with"
+ << QDir::toNativeSeparators(newDefaultResource);
}
}
@@ -1166,39 +1412,32 @@ void PackageManagerCorePrivate::writeMaintenanceToolBinaryData(QFileDevice *outp
QInstaller::appendInt64(output, BinaryContent::MagicUninstallerMarker);
}
-void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations)
+void PackageManagerCorePrivate::writeMaintenanceToolAppBundle(OperationList &performedOperations)
{
- bool gainedAdminRights = false;
- if (!directoryWritable(targetDir())) {
- m_core->gainAdminRights();
- gainedAdminRights = true;
- }
-
+#ifdef Q_OS_MACOS
const QString targetAppDirPath = QFileInfo(maintenanceToolName()).path();
if (!QDir().exists(targetAppDirPath)) {
// create the directory containing the maintenance tool (like a bundle structure on macOS...)
Operation *op = createOwnedOperation(QLatin1String("Mkdir"));
op->setArguments(QStringList() << targetAppDirPath);
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
performedOperations.append(takeOwnedOperation(op));
}
-
-#ifdef Q_OS_MACOS
// if it is a bundle, we need some stuff in it...
const QString sourceAppDirPath = QCoreApplication::applicationDirPath();
if (isInstaller() && QInstaller::isInBundle(sourceAppDirPath)) {
Operation *op = createOwnedOperation(QLatin1String("Copy"));
op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../PkgInfo"))
<< (targetAppDirPath + QLatin1String("/../PkgInfo")));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// copy Info.plist to target directory
op = createOwnedOperation(QLatin1String("Copy"));
op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Info.plist"))
<< (targetAppDirPath + QLatin1String("/../Info.plist")));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// patch the Info.plist after copying it
@@ -1214,13 +1453,14 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
const QString after = QLatin1String("<string>") + QFileInfo(maintenanceToolName()).baseName()
+ QLatin1String("</string>");
while (!in.atEnd())
- out << in.readLine().replace(before, after) << endl;
+ out << in.readLine().replace(before, after) << Qt::endl;
// copy qt_menu.nib if it exists
op = createOwnedOperation(QLatin1String("Mkdir"));
op->setArguments(QStringList() << (targetAppDirPath + QLatin1String("/../Resources/qt_menu.nib")));
if (!op->performOperation()) {
- qDebug() << "ERROR in Mkdir operation:" << op->errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "ERROR in Mkdir operation:"
+ << op->errorString();
}
op = createOwnedOperation(QLatin1String("CopyDirectory"));
@@ -1230,7 +1470,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
op = createOwnedOperation(QLatin1String("Mkdir"));
op->setArguments(QStringList() << (QFileInfo(targetAppDirPath).path() + QLatin1String("/Resources")));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// copy application icons if it exists.
@@ -1239,7 +1479,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
op = createOwnedOperation(QLatin1String("Copy"));
op->setArguments(QStringList() << (sourceAppDirPath + QLatin1String("/../Resources/") + icon)
<< (targetAppDirPath + QLatin1String("/../Resources/") + icon));
- performOperationThreaded(op, Backup);
+ performOperationThreaded(op, Operation::Backup);
performOperationThreaded(op);
// finally, copy everything within Frameworks and plugins
@@ -1253,7 +1493,23 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
<< (targetAppDirPath + QLatin1String("/../plugins")));
performOperationThreaded(op);
}
+#else
+ Q_UNUSED(performedOperations);
#endif
+}
+
+void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOperations)
+{
+ if (m_disableWriteMaintenanceTool) {
+ qCDebug(QInstaller::lcInstallerInstallLog()) << "Maintenance tool writing disabled.";
+ return;
+ }
+
+ bool gainedAdminRights = false;
+ if (!directoryWritable(targetDir())) {
+ m_core->gainAdminRights();
+ gainedAdminRights = true;
+ }
try {
// 1 - check if we have a installer base replacement
@@ -1289,48 +1545,61 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
// 5.1 - this will only happen -if- we wrote out a new binary
bool newBinaryWritten = false;
- bool replacementExists = false;
+ QString mtName = maintenanceToolName();
const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced);
- if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) {
- qDebug() << "Got a replacement installer base binary:" << installerBaseBinary;
-
- QFile replacementBinary(installerBaseBinary);
- try {
- QInstaller::openForRead(&replacementBinary);
- writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true);
- qDebug() << "Wrote the binary with the new replacement.";
-
+ bool macOsMTBundleExtracted = false;
+ if (!installerBaseBinary.isEmpty() && QFileInfo::exists(installerBaseBinary)) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
+ << installerBaseBinary;
+ if (QInstaller::isInBundle(installerBaseBinary)) {
+ // In macOS the installerbase is a whole app bundle. We do not modify the maintenancetool name in app bundle
+ // so that possible signing and notarization will remain. Therefore, the actual maintenance tool name might
+ // differ from the one defined in the settings.
newBinaryWritten = true;
- replacementExists = true;
- } catch (const Error &error) {
- qDebug() << error.message();
- }
-
- if (!replacementBinary.remove()) {
- // Is there anything more sensible we can do with this error? I think not. It's not serious
- // enough for throwing / aborting the process.
- qDebug() << "Cannot remove installer base binary" << installerBaseBinary
- << "after updating the maintenance tool:" << replacementBinary.errorString();
+ mtName = installerBaseBinary;
+ macOsMTBundleExtracted = true;
} else {
- qDebug() << "Removed installer base binary" << installerBaseBinary
- << "after updating the maintenance tool.";
+ writeMaintenanceToolAppBundle(performedOperations);
+ QFile replacementBinary(installerBaseBinary);
+ try {
+ QInstaller::openForRead(&replacementBinary);
+ writeMaintenanceToolBinary(&replacementBinary, replacementBinary.size(), true);
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Wrote the binary with the new replacement.";
+
+ newBinaryWritten = true;
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ }
+
+ if (!replacementBinary.remove()) {
+ // Is there anything more sensible we can do with this error? I think not. It's not serious
+ // enough for throwing / aborting the process.
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Cannot remove installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool:"
+ << replacementBinary.errorString();
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Removed installer base binary"
+ << installerBaseBinary << "after updating the maintenance tool.";
+ }
}
m_installerBaseBinaryUnreplaced.clear();
- } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) {
- qWarning() << "The current maintenance tool could not be updated." << installerBaseBinary
- << "does not exist. Please fix the \"setInstallerBaseBinary(<temp_installer_base_"
- "binary_path>)\" call in your script.";
+ } else if (!installerBaseBinary.isEmpty() && !QFileInfo::exists(installerBaseBinary)) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "The current maintenance tool could not be updated."
+ << installerBaseBinary << "does not exist. Please fix the \"setInstallerBaseBinary"
+ "(<temp_installer_base_binary_path>)\" call in your script.";
+ writeMaintenanceToolAppBundle(performedOperations);
+ } else {
+ writeMaintenanceToolAppBundle(performedOperations);
}
QFile input;
BinaryLayout layout;
- const QString dataFile = targetDir() + QLatin1Char('/') + m_data.settings().maintenanceToolName()
- + QLatin1String(".dat");
+ const QString dataFile = datFileName();
try {
if (isInstaller()) {
if (QFile::exists(dataFile)) {
- qWarning() << "Found binary data file" << dataFile << "but "
- "deliberately not used. Running as installer requires to read the "
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Found binary data file" << dataFile
+ << "but deliberately not used. Running as installer requires to read the "
"resources from the application binary.";
}
throw Error();
@@ -1386,8 +1655,7 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
throw Error(tr("Cannot write maintenance tool binary data to %1: %2")
.arg(file.fileName(), file.errorString()));
}
- file.setPermissions(file.permissions() | QFile::WriteUser | QFile::ReadGroup
- | QFile::ReadOther);
+ setDefaultFilePermissions(&file, DefaultFilePermissions::NonExecutable);
} catch (const Error &/*error*/) {
if (!newBinaryWritten) {
newBinaryWritten = true;
@@ -1407,13 +1675,40 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
input.close();
if (m_core->isInstaller())
registerMaintenanceTool();
+#ifdef Q_OS_MACOS
+ if (newBinaryWritten) {
+ // Remove old alias as the name may have changed.
+ deleteMaintenanceToolAlias();
+ const QString aliasPath = maintenanceToolAliasPath();
+ if (!aliasPath.isEmpty()) {
+ // The new alias file is created after the maintenance too binary is renamed,
+ // but we need to set the value before the variables get written to disk.
+ m_core->setValue(QLatin1String("CreatedMaintenanceToolAlias"), aliasPath);
+ }
+ }
+#endif
writeMaintenanceConfigFiles();
- deferredRename(dataFile + QLatin1String(".new"), dataFile, false);
+
+ QFile::remove(dataFile);
+ QFileInfo fi(mtName);
+ //Rename the dat file according to maintenancetool name
+ QFile::rename(dataFile + QLatin1String(".new"), targetDir() + QLatin1Char('/') + fi.baseName() + QLatin1String(".dat"));
+
+ const bool restart = !statusCanceledOrFailed() && m_needsHardRestart;
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Maintenance tool hard restart:"
+ << (restart ? "true." : "false.");
if (newBinaryWritten) {
- const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()) && m_needsHardRestart;
- deferredRename(maintenanceToolName() + QLatin1String(".new"), maintenanceToolName(), restart);
- qDebug() << "Maintenance tool restart:" << (restart ? "true." : "false.");
+ if (macOsMTBundleExtracted)
+ deferredRename(mtName, targetDir() + QDir::separator() + fi.fileName(), restart);
+ else if (isInstaller())
+ QFile::rename(mtName + QLatin1String(".new"), mtName);
+ else
+ deferredRename(mtName + QLatin1String(".new"), mtName, restart);
+ QFileInfo mtFileName(mtName);
+ writeMaintenanceToolAlias(mtFileName.fileName());
+ } else if (restart) {
+ SelfRestarter::setRestartOnQuit(true);
}
} catch (const Error &err) {
setStatus(PackageManagerCore::Failure);
@@ -1431,6 +1726,80 @@ void PackageManagerCorePrivate::writeMaintenanceTool(OperationList performedOper
m_needToWriteMaintenanceTool = false;
}
+void PackageManagerCorePrivate::writeOfflineBaseBinary()
+{
+ qint64 size;
+ QFile input(installerBinaryPath());
+
+ QInstaller::openForRead(&input);
+#ifndef Q_OS_MACOS
+ BinaryLayout layout = BinaryContent::binaryLayout(&input, BinaryContent::MagicCookie);
+ size = layout.endOfExectuable;
+#else
+ // On macOS the data is on a separate file so we can just get the size
+ size = input.size();
+#endif
+
+ const QString offlineBinaryTempName = offlineBinaryName() + QLatin1String(".new");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Writing offline base binary:" << offlineBinaryTempName;
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Writing offline base binary."));
+
+ QFile out(generateTemporaryFileName());
+ QInstaller::openForWrite(&out); // throws an exception in case of error
+
+ if (!input.seek(0))
+ throw Error(tr("Failed to seek in file %1: %2").arg(input.fileName(), input.errorString()));
+
+ QInstaller::appendData(&out, &input, size);
+
+ {
+ // Check if we have an existing binary, for any reason
+ QFile dummy(offlineBinaryTempName);
+ if (dummy.exists() && !dummy.remove()) {
+ throw Error(tr("Cannot remove file \"%1\": %2").arg(dummy.fileName(),
+ dummy.errorString()));
+ }
+ // Offline binary name might contain non-existing leading directories
+ const QString offlineBinaryAbsolutePath = QFileInfo(offlineBinaryTempName).absolutePath();
+ QDir dummyDir(offlineBinaryAbsolutePath);
+ if (!dummyDir.exists() && !dummyDir.mkpath(offlineBinaryAbsolutePath)) {
+ throw Error(tr("Cannot create directory \"%1\".").arg(dummyDir.absolutePath()));
+ }
+ }
+
+ if (!out.copy(offlineBinaryTempName)) {
+ throw Error(tr("Cannot write offline binary to \"%1\": %2").arg(offlineBinaryTempName,
+ out.errorString()));
+ }
+
+ if (out.exists() && !out.remove()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << tr("Cannot remove temporary file \"%1\": %2")
+ .arg(out.fileName(), out.errorString());
+ }
+}
+
+void PackageManagerCorePrivate::writeMaintenanceToolAlias(const QString &maintenanceToolName)
+{
+#ifdef Q_OS_MACOS
+ const QString aliasPath = maintenanceToolAliasPath();
+ if (aliasPath.isEmpty())
+ return;
+
+ QString maintenanceToolBundle = QString::fromLatin1("%1/%2")
+ .arg(targetDir(), maintenanceToolName);
+ if (!maintenanceToolBundle.endsWith(QLatin1String(".app")))
+ maintenanceToolBundle += QLatin1String(".app");
+
+ const QDir targetDir(QFileInfo(aliasPath).absolutePath());
+ if (!targetDir.exists())
+ targetDir.mkpath(targetDir.absolutePath());
+
+ mkalias(maintenanceToolBundle, aliasPath);
+#else
+ Q_UNUSED(maintenanceToolName)
+#endif
+}
+
QString PackageManagerCorePrivate::registerPath()
{
#ifdef Q_OS_WIN
@@ -1487,13 +1856,15 @@ bool PackageManagerCorePrivate::runInstaller()
mkdirOp->setValue(QLatin1String("forceremoval"), true);
mkdirOp->setValue(QLatin1String("uninstall-only"), true);
- performOperationThreaded(mkdirOp, Backup);
+ performOperationThreaded(mkdirOp, Operation::Backup);
if (!performOperationThreaded(mkdirOp)) {
// if we cannot create the target dir, we try to activate the admin rights
adminRightsGained = m_core->gainAdminRights();
if (!performOperationThreaded(mkdirOp))
throw Error(mkdirOp->errorString());
}
+ setDefaultFilePermissions(target, DefaultFilePermissions::Executable);
+
const QString remove = m_core->value(scRemoveTargetDir);
if (QVariant(remove).toBool())
addPerformed(takeOwnedOperation(mkdirOp));
@@ -1502,9 +1873,9 @@ bool PackageManagerCorePrivate::runInstaller()
ProgressCoordinator::instance()->addManualPercentagePoints(1);
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing the installation..."));
- m_core->calculateComponentsToInstall();
const QList<Component*> componentsToInstall = m_core->orderedComponentsToInstall();
- qDebug() << "Install size:" << componentsToInstall.size() << "components";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Install size:" << componentsToInstall.size()
+ << "components";
callBeginInstallation(componentsToInstall);
stopProcessesForUpdates(componentsToInstall);
@@ -1513,7 +1884,7 @@ bool PackageManagerCorePrivate::runInstaller()
throw Error(tr("It is not possible to install from network location"));
}
- if (!adminRightsGained) {
+ if (!m_core->hasAdminRights()) {
foreach (Component *component, componentsToInstall) {
if (component->value(scRequiresAdminRights, scFalse) == scFalse)
continue;
@@ -1546,8 +1917,8 @@ bool PackageManagerCorePrivate::runInstaller()
+ (PackageManagerCore::createLocalRepositoryFromBinary() ? 1 : 0);
double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
- foreach (Component *component, componentsToInstall)
- installComponent(component, progressOperationSize, adminRightsGained);
+ // Now install the requested components
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
if (m_core->isOfflineOnly() && PackageManagerCore::createLocalRepositoryFromBinary()) {
emit m_core->titleMessageChanged(tr("Creating local repository"));
@@ -1595,20 +1966,23 @@ bool PackageManagerCorePrivate::runInstaller()
}
}
}
-
emit m_core->titleMessageChanged(tr("Creating Maintenance Tool"));
- writeMaintenanceTool(m_performedOperationsOld + m_performedOperationsCurrentSession);
+ m_needToWriteMaintenanceTool = true;
+ m_core->writeMaintenanceTool();
// fake a possible wrong value to show a full progress bar
const int progress = ProgressCoordinator::instance()->progressInPercentage();
// usually this should be only the reserved one from the beginning
if (progress < 100)
ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation finished!"));
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installation finished!"));
if (adminRightsGained)
m_core->dropAdminRights();
+
setStatus(PackageManagerCore::Success);
emit installationFinished();
} catch (const Error &err) {
@@ -1616,12 +1990,15 @@ bool PackageManagerCorePrivate::runInstaller()
setStatus(PackageManagerCore::Failure);
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("installationError"), tr("Error"), err.message());
- qDebug() << "ROLLING BACK operations=" << m_performedOperationsCurrentSession.count();
+ qCDebug(QInstaller::lcInstallerInstallLog) << "ROLLING BACK operations="
+ << m_performedOperationsCurrentSession.count();
}
m_core->rollBackInstallation();
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstallation aborted!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installation aborted!"));
+
if (adminRightsGained)
m_core->dropAdminRights();
emit installationFinished();
@@ -1643,18 +2020,13 @@ bool PackageManagerCorePrivate::runPackageUpdater()
//to have some progress for the cleanup/write component.xml step
ProgressCoordinator::instance()->addReservePercentagePoints(1);
-#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
- // check if we need admin rights and ask before the action happens
- // on Linux and macOS also check target directory subdirectories
- if (!directoryWritable(targetDir()) || !subdirectoriesWritable(targetDir()))
- adminRightsGained = m_core->gainAdminRights();
-#else
// check if we need admin rights and ask before the action happens
if (!directoryWritable(targetDir()))
adminRightsGained = m_core->gainAdminRights();
-#endif
+
const QList<Component *> componentsToInstall = m_core->orderedComponentsToInstall();
- qDebug() << "Install size:" << componentsToInstall.size() << "components";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Install size:" << componentsToInstall.size()
+ << "components";
callBeginInstallation(componentsToInstall);
stopProcessesForUpdates(componentsToInstall);
@@ -1704,7 +2076,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// There is a replacement, but the replacement is not scheduled for update, keep it as well.
if (m_componentsToReplaceUpdaterMode.contains(name)
- && !m_componentsToReplaceUpdaterMode.value(name).first->updateRequested()) {
+ && !m_installerCalculator->resolvedComponents().contains(m_componentsToReplaceUpdaterMode.value(name).first)) {
nonRevertedOperations.append(operation);
continue;
}
@@ -1744,7 +2116,7 @@ bool PackageManagerCorePrivate::runPackageUpdater()
}
// we did not request admin rights till we found out that a component/ undo needs admin rights
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -1765,15 +2137,15 @@ bool PackageManagerCorePrivate::runPackageUpdater()
if (undoOperations.count() > 0) {
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Removing deselected components..."));
- runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, true);
+ runUndoOperations(undoOperations, undoOperationProgressSize, true);
}
m_performedOperationsOld = nonRevertedOperations; // these are all operations left: those not reverted
const double progressOperationCount = countProgressOperations(componentsToInstall);
const double progressOperationSize = componentsInstallPartProgressSize / progressOperationCount;
- foreach (Component *component, componentsToInstall)
- installComponent(component, progressOperationSize, adminRightsGained);
+ // Now install the requested new components
+ unpackAndInstallComponents(componentsToInstall, progressOperationSize);
emit m_core->titleMessageChanged(tr("Creating Maintenance Tool"));
@@ -1785,23 +2157,30 @@ bool PackageManagerCorePrivate::runPackageUpdater()
// usually this should be only the reserved one from the beginning
if (progress < 100)
ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate finished!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Update finished!"));
if (adminRightsGained)
m_core->dropAdminRights();
- setStatus(PackageManagerCore::Success);
+ if (m_foundEssentialUpdate)
+ setStatus(PackageManagerCore::EssentialUpdated);
+ else
+ setStatus(PackageManagerCore::Success);
emit installationFinished();
} catch (const Error &err) {
if (m_core->status() != PackageManagerCore::Canceled) {
setStatus(PackageManagerCore::Failure);
MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
QLatin1String("installationError"), tr("Error"), err.message());
- qDebug() << "ROLLING BACK operations=" << m_performedOperationsCurrentSession.count();
+ qCDebug(QInstaller::lcInstallerInstallLog) << "ROLLING BACK operations="
+ << m_performedOperationsCurrentSession.count();
}
m_core->rollBackInstallation();
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nUpdate aborted!"));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Update aborted!"));
+
if (adminRightsGained)
m_core->dropAdminRights();
emit installationFinished();
@@ -1835,7 +2214,7 @@ bool PackageManagerCorePrivate::runUninstaller()
}
// We did not yet request elevated permissions but they are required.
- if (updateAdminRights && !adminRightsGained) {
+ if (updateAdminRights && !m_core->hasAdminRights()) {
m_core->gainAdminRights();
m_core->dropAdminRights();
}
@@ -1843,18 +2222,21 @@ bool PackageManagerCorePrivate::runUninstaller()
const int uninstallOperationCount = countProgressOperations(undoOperations);
const double undoOperationProgressSize = double(1) / double(uninstallOperationCount);
- runUndoOperations(undoOperations, undoOperationProgressSize, adminRightsGained, false);
+ runUndoOperations(undoOperations, undoOperationProgressSize, false);
// No operation delete here, as all old undo operations are deleted in the destructor.
deleteMaintenanceTool(); // this will also delete the TargetDir on Windows
+ deleteMaintenanceToolAlias();
// If not on Windows, we need to remove TargetDir manually.
+#ifndef Q_OS_WIN
if (QVariant(m_core->value(scRemoveTargetDir)).toBool() && !targetDir().isEmpty()) {
- if (updateAdminRights && !adminRightsGained)
+ if (updateAdminRights && !m_core->hasAdminRights())
adminRightsGained = m_core->gainAdminRights();
removeDirectoryThreaded(targetDir(), true);
- qDebug() << "Complete uninstallation was chosen.";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Complete uninstallation was chosen.";
}
+#endif
unregisterMaintenanceTool();
m_needToWriteMaintenanceTool = false;
@@ -1872,16 +2254,289 @@ bool PackageManagerCorePrivate::runUninstaller()
m_core->dropAdminRights();
ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QString::fromLatin1("\n%1").arg(
- success ? tr("Uninstallation completed successfully.") : tr("Uninstallation aborted.")));
+ success ? tr("Removal completed successfully.") : tr("Removal aborted.")));
emit uninstallationFinished();
return success;
}
-void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained)
+bool PackageManagerCorePrivate::runOfflineGenerator()
{
- const OperationList operations = component->operations();
+ const QString offlineBinaryTempName = offlineBinaryName() + QLatin1String(".new");
+ const QString tempSettingsFilePath = generateTemporaryFileName()
+ + QDir::separator() + QLatin1String("config.xml");
+
+ bool adminRightsGained = false;
+ try {
+ setStatus(PackageManagerCore::Running);
+ emit offlineGenerationStarted(); // Resets also the ProgressCoordninator
+
+ // Never write the maintenance tool when generating offline installer
+ m_needToWriteMaintenanceTool = false;
+
+ // Reserve some progress for the final writing, it should take
+ // only a fraction of time spent in the download part
+ ProgressCoordinator::instance()->addReservePercentagePoints(1);
+
+ const QString target = QDir::cleanPath(targetDir().replace(QLatin1Char('\\'), QLatin1Char('/')));
+ if (target.isEmpty())
+ throw Error(tr("Variable 'TargetDir' not set."));
+
+ // Create target directory for installer to be generated
+ if (!QDir(target).exists()) {
+ if (!QDir().mkpath(target)) {
+ adminRightsGained = m_core->gainAdminRights();
+ // Try again with admin privileges
+ if (!QDir().mkpath(target))
+ throw Error(tr("Cannot create target directory for installer."));
+ }
+ } else if (QDir(target).exists()) {
+ if (!directoryWritable(targetDir()))
+ adminRightsGained = m_core->gainAdminRights();
+ }
+ setDefaultFilePermissions(target, DefaultFilePermissions::Executable);
+
+ // Show that there was some work
+ ProgressCoordinator::instance()->addManualPercentagePoints(1);
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing offline generation..."));
+
+ const QList<Component*> componentsToInclude = m_core->orderedComponentsToInstall();
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Included components:" << componentsToInclude.size();
+
+ // Give full part progress size as this is the most time consuming step
+ m_core->downloadNeededArchives(double(1));
+
+ const QString installerBaseReplacement = replaceVariables(m_offlineBaseBinaryUnreplaced);
+ if (!installerBaseReplacement.isEmpty() && QFileInfo::exists(installerBaseReplacement)) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Got a replacement installer base binary:"
+ << offlineBinaryTempName;
+
+ if (!QFile::copy(installerBaseReplacement, offlineBinaryTempName)) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << QString::fromLatin1("Cannot copy "
+ "replacemement binary to temporary location \"%1\" from \"%2\".")
+ .arg(offlineBinaryTempName, installerBaseReplacement);
+ }
+ } else {
+ writeOfflineBaseBinary();
+ }
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Preparing installer configuration..."));
+
+ // Create copy of internal config file and data, remove repository related elements
+ QInstaller::trimmedCopyConfigData(m_data.settingsFilePath(), tempSettingsFilePath,
+ QStringList() << scRemoteRepositories << scRepositoryCategories);
+
+ // Assemble final installer binary
+ QInstallerTools::BinaryCreatorArgs args;
+ args.target = offlineBinaryName();
+#ifdef Q_OS_MACOS
+ // Target is a disk image on macOS
+ args.target.append(QLatin1String(".dmg"));
+#endif
+ args.templateBinary = offlineBinaryTempName;
+ args.offlineOnly = true;
+ args.configFile = tempSettingsFilePath;
+ args.ftype = QInstallerTools::Include;
+ // Add possible custom resources
+ if (!m_offlineGeneratorResourceCollections.isEmpty())
+ args.resources = m_offlineGeneratorResourceCollections;
+
+ foreach (auto component, componentsToInclude) {
+ args.filteredPackages.append(component->name());
+ args.repositoryDirectories.append(component->localTempPath());
+ }
+ args.repositoryDirectories.removeDuplicates();
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("Creating the installer..."));
+
+ QString errorMessage;
+ if (QInstallerTools::createBinary(args, errorMessage) == EXIT_FAILURE) {
+ throw Error(tr("Failed to create offline installer. %1").arg(errorMessage));
+ } else {
+ setStatus(PackageManagerCore::Success);
+ }
+
+ // Fake a possible wrong value to show a full progress
+ const int progress = ProgressCoordinator::instance()->progressInPercentage();
+ // This should be only the reserved one (1) from the beginning
+ if (progress < 100)
+ ProgressCoordinator::instance()->addManualPercentagePoints(100 - progress);
+
+ } catch (const Error &err) {
+ if (m_core->status() != PackageManagerCore::Canceled) {
+ setStatus(PackageManagerCore::Failure);
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationError"), tr("Error"), err.message());
+ }
+ }
+ QFile tempBinary(offlineBinaryTempName);
+ if (tempBinary.exists() && !tempBinary.remove()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << tr("Cannot remove temporary file \"%1\": %2")
+ .arg(tempBinary.fileName(), tempBinary.errorString());
+ }
+
+ QDir tempSettingsDir(QFileInfo(tempSettingsFilePath).absolutePath());
+ if (tempSettingsDir.exists() && !tempSettingsDir.removeRecursively()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << tr("Cannot remove temporary directory \"%1\".")
+ .arg(tempSettingsDir.path());
+ }
+
+ const bool success = m_core->status() == PackageManagerCore::Success;
+ if (adminRightsGained)
+ m_core->dropAdminRights();
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QString::fromLatin1("\n%1").arg(
+ success ? tr("Offline generation completed successfully.") : tr("Offline generation aborted!")));
+
+ emit offlineGenerationFinished();
+ return success;
+}
+
+void PackageManagerCorePrivate::unpackComponents(const QList<Component *> &components,
+ double progressOperationSize)
+{
+ OperationList unpackOperations;
+ bool becameAdmin = false;
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Preparing to unpack components..."));
+
+ // 1. Collect operations
+ quint64 totalOperationsSizeHint = 0;
+ for (auto *component : components) {
+ const OperationList operations = component->operations(Operation::Unpack);
+ if (!component->operationsCreatedSuccessfully())
+ m_core->setCanceled();
+
+ for (auto &op : operations) {
+ if (statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+
+ unpackOperations.append(op);
+ totalOperationsSizeHint += op->sizeHint();
+
+ // There's currently no way to control this on a per-operation basis, so
+ // any op requesting execution as admin means all extracts are done as admin.
+ if (!m_core->hasAdminRights() && op->value(QLatin1String("admin")).toBool())
+ becameAdmin = m_core->gainAdminRights();
+ }
+ }
+
+ // 2. Calculate proportional progress sizes
+ const double totalOperationsSize = progressOperationSize * unpackOperations.size();
+ for (auto *op : unpackOperations) {
+ const double ratio = static_cast<double>(op->sizeHint()) / totalOperationsSizeHint;
+ const double proportionalProgressOperationSize = totalOperationsSize * ratio;
+
+ connectOperationToInstaller(op, proportionalProgressOperationSize);
+ connectOperationCallMethodRequest(op);
+ }
+
+ // 3. Backup operations
+ ConcurrentOperationRunner runner(&unpackOperations, Operation::Backup);
+ runner.setMaxThreadCount(m_core->maxConcurrentOperations());
+
+ connect(m_core, &PackageManagerCore::installationInterrupted,
+ &runner, &ConcurrentOperationRunner::cancel);
+
+ connect(&runner, &ConcurrentOperationRunner::progressChanged, [](const int completed, const int total) {
+ const QString statusText = tr("%1 of %2 operations completed.")
+ .arg(QString::number(completed), QString::number(total));
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(statusText);
+ });
+ connect(&runner, &ConcurrentOperationRunner::finished, [] {
+ // Clear the status text to not cause confusion when installations begin.
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(QLatin1String(""));
+ });
+
+ const QHash<Operation *, bool> backupResults = runner.run();
+ const OperationList backupOperations = backupResults.keys();
+
+ for (auto &operation : backupOperations) {
+ if (m_core->status() == PackageManagerCore::Canceled)
+ break; // User canceled, no need to print warnings
+
+ if (!backupResults.value(operation) || operation->error() != Operation::NoError) {
+ // For Extract, backup stops only on read errors. That means the perform step will
+ // also fail later on, which handles the user selection on what to do with the error.
+ qCWarning(QInstaller::lcInstallerInstallLog) << QString::fromLatin1("Backup of operation "
+ "\"%1\" with arguments \"%2\" failed: %3").arg(operation->name(), operation->arguments()
+ .join(QLatin1String("; ")), operation->errorString());
+ continue;
+ }
+ // Backup may request performing operation as admin
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool())
+ becameAdmin = m_core->gainAdminRights();
+ }
+
+ // TODO: Do we need to rollback backups too? For Extract op this works without,
+ // as the backup files are not used in Undo step.
+ if (statusCanceledOrFailed())
+ throw Error(tr("Installation canceled by user"));
+
+ // 4. Perform operations
+ std::sort(unpackOperations.begin(), unpackOperations.end(), [](Operation *lhs, Operation *rhs) {
+ // We want to run the longest taking operations first
+ return lhs->sizeHint() > rhs->sizeHint();
+ });
+
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Unpacking components..."));
+
+ runner.setType(Operation::Perform);
+ const QHash<Operation *, bool> results = runner.run();
+ const OperationList performedOperations = results.keys();
+
+ QString error;
+ for (auto &operation : performedOperations) {
+ const QString component = operation->value(QLatin1String("component")).toString();
+
+ bool ignoreError = false;
+ bool ok = results.value(operation);
+
+ while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << QString::fromLatin1("Operation \"%1\" with arguments "
+ "\"%2\" failed: %3").arg(operation->name(), operation->arguments()
+ .join(QLatin1String("; ")), operation->errorString());
+
+ const QMessageBox::StandardButton button =
+ MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
+ QLatin1String("installationErrorWithCancel"), tr("Installer Error"),
+ tr("Error during installation process (%1):\n%2").arg(component, operation->errorString()),
+ QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Cancel);
+
+ if (button == QMessageBox::Retry)
+ ok = performOperationThreaded(operation);
+ else if (button == QMessageBox::Ignore)
+ ignoreError = true;
+ else if (button == QMessageBox::Cancel)
+ m_core->interrupt();
+ }
+
+ if (ok || operation->error() > Operation::InvalidArguments) {
+ // Remember that the operation was performed, that allows us to undo it
+ // if this operation failed but still needs an undo call to cleanup.
+ addPerformed(operation);
+ }
+
+ // Catch the error message from first failure, but throw only after all
+ // operations requiring undo step are marked as performed.
+ if (!ok && !ignoreError && error.isEmpty())
+ error = operation->errorString();
+ }
+
+ if (becameAdmin)
+ m_core->dropAdminRights();
+
+ if (!error.isEmpty())
+ throw Error(error);
+
+ ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
+}
+
+void PackageManagerCorePrivate::installComponent(Component *component, double progressOperationSize)
+{
+ OperationList operations = component->operations(Operation::Install);
if (!component->operationsCreatedSuccessfully())
m_core->setCanceled();
@@ -1889,8 +2544,8 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// show only components which do something, MinimumProgress is only for progress calculation safeness
bool showDetailsLog = false;
if (opCount > 1 || (opCount == 1 && operations.at(0)->name() != QLatin1String("MinimumProgress"))) {
- ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(tr("\nInstalling component %1...")
- .arg(component->displayName()));
+ ProgressCoordinator::instance()->emitLabelAndDetailTextChanged(QLatin1Char('\n')
+ + tr("Installing component %1").arg(component->displayName()));
showDetailsLog = true;
}
@@ -1900,29 +2555,29 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
// maybe this operations wants us to be admin...
bool becameAdmin = false;
- if (!adminRightsGained && operation->value(QLatin1String("admin")).toBool()) {
+ if (!m_core->hasAdminRights() && operation->value(QLatin1String("admin")).toBool()) {
becameAdmin = m_core->gainAdminRights();
- qDebug() << operation->name() << "as admin:" << becameAdmin;
+ qCDebug(QInstaller::lcInstallerInstallLog) << operation->name() << "as admin:" << becameAdmin;
}
connectOperationToInstaller(operation, progressOperationSize);
connectOperationCallMethodRequest(operation);
// allow the operation to backup stuff before performing the operation
- performOperationThreaded(operation, PackageManagerCorePrivate::Backup);
+ performOperationThreaded(operation, Operation::Backup);
bool ignoreError = false;
bool ok = performOperationThreaded(operation);
while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) {
- qDebug() << QString::fromLatin1("Operation \"%1\" with arguments \"%2\" failed: %3")
- .arg(operation->name(), operation->arguments().join(QLatin1String("; ")),
- operation->errorString());
+ qCDebug(QInstaller::lcInstallerInstallLog) << QString::fromLatin1("Operation \"%1\" with arguments "
+ "\"%2\" failed: %3").arg(operation->name(), operation->arguments()
+ .join(QLatin1String("; ")), operation->errorString());
const QMessageBox::StandardButton button =
MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
- QLatin1String("installationErrorWithRetry"), tr("Installer Error"),
+ QLatin1String("installationErrorWithCancel"), tr("Installer Error"),
tr("Error during installation process (%1):\n%2").arg(component->name(),
operation->errorString()),
- QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Retry);
+ QMessageBox::Retry | QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Cancel);
if (button == QMessageBox::Retry)
ok = performOperationThreaded(operation);
@@ -1943,8 +2598,12 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
if (!ok && !ignoreError)
throw Error(operation->errorString());
+ }
- if (component->value(scEssential, scFalse) == scTrue)
+ if (!m_core->isCommandLineInstance()) {
+ if ((component->value(scEssential, scFalse) == scTrue) && !isInstaller())
+ m_needsHardRestart = true;
+ else if ((component->value(scForcedUpdate, scFalse) == scTrue) && isUpdater())
m_needsHardRestart = true;
}
@@ -1963,7 +2622,10 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
m_localPackageHub->addPackage(component->name(),
component->value(scVersion),
component->value(scDisplayName),
+ QPair<QString, bool>(component->value(scTreeName),
+ component->treeNameMoveChildren()),
component->value(scDescription),
+ component->value(scSortingPriority).toInt(),
component->dependencies(),
component->autoDependencies(),
component->forcedInstallation(),
@@ -1971,7 +2633,8 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
component->value(scUncompressedSize).toULongLong(),
component->value(scInheritVersion),
component->isCheckable(),
- component->isExpandedByDefault());
+ component->isExpandedByDefault(),
+ component->value(scContentSha1));
m_localPackageHub->writeToDisk();
component->setInstalled();
@@ -1981,10 +2644,95 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr
ProgressCoordinator::instance()->emitDetailTextChanged(tr("Done"));
}
+PackageManagerCore::Status PackageManagerCorePrivate::fetchComponentsAndInstall(const QStringList& components)
+{
+ // init default model before fetching remote packages tree
+ ComponentModel *model = m_core->defaultComponentModel();
+ Q_UNUSED(model);
+
+ bool fallbackReposFetched = false;
+ auto fetchComponents = [&]() {
+ bool packagesFound = m_core->fetchPackagesWithFallbackRepositories(components, fallbackReposFetched);
+
+ if (!packagesFound) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace()
+ << "No components available with the current selection.";
+ setStatus(PackageManagerCore::Canceled);
+ return false;
+ }
+ QString errorMessage;
+ bool unstableAliasFound = false;
+ if (m_core->checkComponentsForInstallation(components, errorMessage, unstableAliasFound, fallbackReposFetched)) {
+ if (!errorMessage.isEmpty())
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage;
+ if (calculateComponentsAndRun()) {
+ if (m_core->isOfflineGenerator())
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Created installer to:" << offlineBinaryName();
+ else
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Components installed successfully";
+ }
+ } else {
+ // We found unstable alias and all repos were not fetched. Alias might have dependency to component
+ // which exists in non-default repository. Fetch all repositories now.
+ if (unstableAliasFound && !fallbackReposFetched) {
+ return false;
+ } else {
+ for (const QString &possibleAliasName : components) {
+ if (ComponentAlias *alias = m_core->aliasByName(possibleAliasName)) {
+ if (alias->componentErrorMessage().isEmpty())
+ continue;
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace() << alias->componentErrorMessage();
+ }
+ }
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << errorMessage
+ << "No components available with the current selection.";
+ }
+ }
+ return true;
+ };
+
+ if (!fetchComponents() && !fallbackReposFetched) {
+ fallbackReposFetched = true;
+ setStatus(PackageManagerCore::Running);
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << "Components not found with the current selection."
+ << "Searching from additional repositories";
+ if (!ProductKeyCheck::instance()->securityWarning().isEmpty()) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << ProductKeyCheck::instance()->securityWarning();
+ }
+ enableAllCategories();
+ fetchComponents();
+ }
+
+ return m_core->status();
+}
+
+void PackageManagerCorePrivate::setComponentSelection(const QString &id, Qt::CheckState state)
+{
+ ComponentModel *model = m_core->isUpdater() ? m_core->updaterComponentModel() : m_core->defaultComponentModel();
+ Component *component = m_core->componentByName(id);
+ if (!component) {
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace()
+ << "Unable to set selection for: " << id << ". Component not found.";
+ return;
+ }
+ const QModelIndex &idx = model->indexFromComponentName(component->treeName());
+ if (idx.isValid())
+ model->setData(idx, state, Qt::CheckStateRole);
+}
+
// -- private
void PackageManagerCorePrivate::deleteMaintenanceTool()
{
+ QDir resourcePath(QFileInfo(maintenanceToolName()).dir());
+ resourcePath.remove(QLatin1String("installer.dat"));
+ QDir installDir(targetDir());
+ installDir.remove(m_data.settings().maintenanceToolName() + QLatin1String(".dat"));
+ installDir.remove(QLatin1String("network.xml"));
+ installDir.remove(m_data.settings().maintenanceToolIniFile());
+ QInstaller::VerboseWriter::instance()->setFileName(QString());
+ installDir.remove(m_core->value(QLatin1String("LogFileName"), QLatin1String("InstallationLog.txt")));
#ifdef Q_OS_WIN
// Since Windows does not support that the maintenance tool deletes itself we have to go with a rather dirty
// hack. What we do is to create a batchfile that will try to remove the maintenance tool once per second. Then
@@ -1995,8 +2743,9 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
QLatin1String("uninstall.vbs")).absoluteFilePath());
QFile f(batchfile);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
- throw Error(tr("Cannot prepare uninstall"));
+ throw Error(tr("Cannot prepare removal"));
+ const bool removeTargetDir = QVariant(m_core->value(scRemoveTargetDir)).toBool();
QTextStream batch(&f);
batch << "Set fso = WScript.CreateObject(\"Scripting.FileSystemObject\")\n";
batch << "file = WScript.Arguments.Item(0)\n";
@@ -2008,10 +2757,12 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
batch << " fso.DeleteFile(file)\n";
batch << " WScript.Sleep(1000)\n";
batch << "wend\n";
-// batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
+ if (!removeTargetDir)
+ batch << "if folder.SubFolders.Count = 0 and folder.Files.Count = 0 then\n";
batch << " Set folder = Nothing\n";
batch << " fso.DeleteFolder folderpath, true\n";
-// batch << "end if\n";
+ if (!removeTargetDir)
+ batch << "end if\n";
batch << "fso.DeleteFile(WScript.ScriptFullName)\n";
f.close();
@@ -2019,14 +2770,10 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
QStringList arguments;
arguments << QLatin1String("//Nologo") << batchfile; // execute the batchfile
arguments << QDir::toNativeSeparators(QFileInfo(installerBinaryPath()).absoluteFilePath());
- if (!m_performedOperationsOld.isEmpty()) {
- const Operation *const op = m_performedOperationsOld.first();
- if (op->name() == QLatin1String("Mkdir")) // the target directory name
- arguments << QDir::toNativeSeparators(QFileInfo(op->arguments().first()).absoluteFilePath());
- }
+ arguments << targetDir();
if (!QProcessWrapper::startDetached(QLatin1String("cscript"), arguments, QDir::rootPath()))
- throw Error(tr("Cannot start uninstall"));
+ throw Error(tr("Cannot start removal"));
#else
// every other platform has no problem if we just delete ourselves now
QFile maintenanceTool(QFileInfo(installerBinaryPath()).absoluteFilePath());
@@ -2047,10 +2794,27 @@ void PackageManagerCorePrivate::deleteMaintenanceTool()
}
}
+void PackageManagerCorePrivate::deleteMaintenanceToolAlias()
+{
+#ifdef Q_OS_MACOS
+ const QString maintenanceToolAlias = m_core->value(QLatin1String("CreatedMaintenanceToolAlias"));
+ QFile aliasFile(maintenanceToolAlias);
+ if (!maintenanceToolAlias.isEmpty() && !aliasFile.remove()) {
+ // Not fatal
+ qWarning(lcInstallerInstallLog) << "Cannot remove alias file"
+ << maintenanceToolAlias << "for maintenance tool:" << aliasFile.errorString();
+ }
+#endif
+}
+
void PackageManagerCorePrivate::registerMaintenanceTool()
{
#ifdef Q_OS_WIN
- QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ auto quoted = [](const QString &s) {
+ return QString::fromLatin1("\"%1\"").arg(s);
+ };
+
+ QSettingsWrapper settings(registerPath(), QSettings::NativeFormat);
settings.setValue(scDisplayName, m_data.value(QLatin1String("ProductName")));
settings.setValue(QLatin1String("DisplayVersion"), m_data.value(QLatin1String("ProductVersion")));
const QString maintenanceTool = QDir::toNativeSeparators(maintenanceToolName());
@@ -2060,9 +2824,12 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
settings.setValue(QLatin1String("Comments"), m_data.value(scTitle));
settings.setValue(QLatin1String("InstallDate"), QDateTime::currentDateTime().toString());
settings.setValue(QLatin1String("InstallLocation"), QDir::toNativeSeparators(targetDir()));
- settings.setValue(QLatin1String("UninstallString"), maintenanceTool);
- settings.setValue(QLatin1String("ModifyPath"), QString(maintenanceTool
- + QLatin1String(" --manage-packages")));
+ settings.setValue(QLatin1String("UninstallString"), QString(quoted(maintenanceTool)
+ + QLatin1String(" --") + CommandLineOptions::scStartUninstallerLong));
+ if (!isOfflineOnly()) {
+ settings.setValue(QLatin1String("ModifyPath"), QString(quoted(maintenanceTool)
+ + QLatin1String(" --") + CommandLineOptions::scStartPackageManagerLong));
+ }
// required disk space of the installed components
quint64 estimatedSizeKB = m_core->requiredDiskSpace() / 1024;
// add required space for the maintenance tool
@@ -2094,28 +2861,31 @@ void PackageManagerCorePrivate::registerMaintenanceTool()
void PackageManagerCorePrivate::unregisterMaintenanceTool()
{
#ifdef Q_OS_WIN
- QSettingsWrapper settings(registerPath(), QSettingsWrapper::NativeFormat);
+ QSettingsWrapper settings(registerPath(), QSettings::NativeFormat);
settings.remove(QString());
#endif
}
-void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations, double progressSize,
- bool adminRightsGained, bool deleteOperation)
+void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOperations,
+ double progressSize, bool deleteOperation)
{
try {
+ const int operationsCount = undoOperations.size();
+ int rolledBackOperations = 0;
+
foreach (Operation *undoOperation, undoOperations) {
if (statusCanceledOrFailed())
throw Error(tr("Installation canceled by user"));
bool becameAdmin = false;
- if (!adminRightsGained && undoOperation->value(QLatin1String("admin")).toBool())
+ if (!m_core->hasAdminRights() && undoOperation->value(QLatin1String("admin")).toBool())
becameAdmin = m_core->gainAdminRights();
connectOperationToInstaller(undoOperation, progressSize);
- qDebug() << "undo operation=" << undoOperation->name();
+ qCDebug(QInstaller::lcInstallerInstallLog) << "undo operation=" << undoOperation->name();
bool ignoreError = false;
- bool ok = performOperationThreaded(undoOperation, PackageManagerCorePrivate::Undo);
+ bool ok = performOperationThreaded(undoOperation, Operation::Undo);
const QString componentName = undoOperation->value(QLatin1String("component")).toString();
@@ -2123,12 +2893,12 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
while (!ok && !ignoreError && m_core->status() != PackageManagerCore::Canceled) {
const QMessageBox::StandardButton button =
MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(),
- QLatin1String("installationErrorWithRetry"), tr("Installer Error"),
- tr("Error during uninstallation process:\n%1").arg(undoOperation->errorString()),
- QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Retry);
+ QLatin1String("installationErrorWithIgnore"), tr("Installer Error"),
+ tr("Error during removal process:\n%1").arg(undoOperation->errorString()),
+ QMessageBox::Retry | QMessageBox::Ignore, QMessageBox::Ignore);
if (button == QMessageBox::Retry)
- ok = performOperationThreaded(undoOperation, Undo);
+ ok = performOperationThreaded(undoOperation, Operation::Undo);
else if (button == QMessageBox::Ignore)
ignoreError = true;
}
@@ -2141,6 +2911,10 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
}
}
+ ++rolledBackOperations;
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 operations rolled back.")
+ .arg(QString::number(rolledBackOperations), QString::number(operationsCount)));
+
if (becameAdmin)
m_core->dropAdminRights();
@@ -2155,6 +2929,7 @@ void PackageManagerCorePrivate::runUndoOperations(const OperationList &undoOpera
throw Error(tr("Unknown error"));
}
m_localPackageHub->writeToDisk();
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("Rollbacks complete."));
}
PackagesList PackageManagerCorePrivate::remotePackages()
@@ -2167,7 +2942,7 @@ PackagesList PackageManagerCorePrivate::remotePackages()
m_updateFinder = new KDUpdater::UpdateFinder;
m_updateFinder->setAutoDelete(false);
- m_updateFinder->setPackageSources(m_packageSources);
+ m_updateFinder->setPackageSources(m_packageSources + m_compressedPackageSources);
m_updateFinder->setLocalPackageHub(m_localPackageHub);
m_updateFinder->run();
@@ -2181,40 +2956,17 @@ PackagesList PackageManagerCorePrivate::remotePackages()
return m_updateFinder->updates();
}
-PackagesList PackageManagerCorePrivate::compressedPackages()
-{
- if (m_compressedUpdates && m_compressedFinder)
- return m_compressedFinder->updates();
- m_compressedUpdates = false;
- delete m_compressedFinder;
-
- m_compressedFinder = new KDUpdater::UpdateFinder;
- m_compressedFinder->setAutoDelete(false);
- m_compressedFinder->addCompressedPackage(true);
- m_compressedFinder->setPackageSources(m_compressedPackageSources);
-
- m_compressedFinder->setLocalPackageHub(m_localPackageHub);
- m_compressedFinder->run();
- if (m_compressedFinder->updates().isEmpty()) {
- setStatus(PackageManagerCore::Failure, tr("Cannot retrieve remote tree %1.")
- .arg(m_compressedFinder->errorString()));
- return PackagesList();
- }
- m_compressedUpdates = true;
- return m_compressedFinder->updates();
-}
-
/*!
Returns a hash containing the installed package name and it's associated package information. If
the application is running in installer mode or the local components file could not be parsed, the
hash is empty.
*/
-LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
+LocalPackagesMap PackageManagerCorePrivate::localInstalledPackages()
{
if (isInstaller())
- return LocalPackagesHash();
+ return LocalPackagesMap();
+
- LocalPackagesHash installedPackages;
if (m_localPackageHub->error() != LocalPackageHub::NoError) {
if (m_localPackageHub->fileName().isEmpty())
m_localPackageHub->setFileName(componentsXmlPath());
@@ -2231,24 +2983,36 @@ LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
setStatus(PackageManagerCore::Failure, tr("Failure to read packages from %1.")
.arg(componentsXmlPath()));
}
+ return m_localPackageHub->localPackages();
+}
- foreach (const LocalPackage &package, m_localPackageHub->packageInfos()) {
- if (statusCanceledOrFailed())
- break;
- installedPackages.insert(package.name, package);
+QList<ComponentAlias *> PackageManagerCorePrivate::componentAliases()
+{
+ if (m_aliases && m_aliasFinder)
+ return m_aliasFinder->aliases();
+
+ m_aliases = false;
+ delete m_aliasFinder;
+
+ m_aliasFinder = new AliasFinder(m_core);
+ m_aliasFinder->setAliasSources(m_aliasSources);
+ if (!m_aliasFinder->run()) {
+ qCDebug(lcDeveloperBuild) << "No component aliases found." << Qt::endl;
+ return QList<ComponentAlias *>();
}
- return installedPackages;
+ m_aliases = true;
+ return m_aliasFinder->aliases();
}
-bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories()
+bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories(DownloadType type)
{
m_updates = false;
m_repoFetched = false;
m_updateSourcesAdded = false;
try {
- m_metadataJob.addCompressedPackages(false);
+ m_metadataJob.addDownloadType(type);
m_metadataJob.start();
m_metadataJob.waitForFinished();
} catch (Error &error) {
@@ -2271,49 +3035,12 @@ bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories()
return m_repoFetched;
}
-bool PackageManagerCorePrivate::fetchMetaInformationFromCompressedRepositories()
-{
- bool compressedRepoFetched = false;
-
- m_compressedUpdates = false;
- m_updateSourcesAdded = false;
-
- try {
- //Tell MetadataJob that only compressed packages needed to be fetched and not all.
- //We cannot do this in general fetch meta method as the compressed packages might be
- //installed after components tree is generated
- m_metadataJob.addCompressedPackages(true);
- m_metadataJob.start();
- m_metadataJob.waitForFinished();
- m_metadataJob.addCompressedPackages(false);
- } catch (Error &error) {
- setStatus(PackageManagerCore::Failure, tr("Cannot retrieve meta information: %1")
- .arg(error.message()));
- return compressedRepoFetched;
- }
-
- if (m_metadataJob.error() != Job::NoError) {
- switch (m_metadataJob.error()) {
- case QInstaller::UserIgnoreError:
- break; // we can simply ignore this error, the user knows about it
- default:
- //Do not change core status here, we can recover if there is invalid
- //compressed repository
- setStatus(m_core->status(), m_metadataJob.errorString());
- return compressedRepoFetched;
- }
- }
-
- compressedRepoFetched = true;
- return compressedRepoFetched;
-}
-
-bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository)
+bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool compressedRepository)
{
if (!compressedRepository && m_updateSourcesAdded)
return m_updateSourcesAdded;
- const QList<Metadata> metadata = m_metadataJob.metadata();
+ const QList<Metadata *> metadata = m_metadataJob.metadata();
if (metadata.isEmpty()) {
m_updateSourcesAdded = true;
return m_updateSourcesAdded;
@@ -2326,51 +3053,25 @@ bool PackageManagerCorePrivate::addUpdateResourcesFromRepositories(bool parseChe
m_updates = false;
m_updateSourcesAdded = false;
if (isInstaller())
- m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 0));
+ m_packageSources.insert(PackageSource(QUrl(QLatin1String("resource://metadata/")), 1));
}
- foreach (const Metadata &data, metadata) {
- if (compressedRepository && !data.repository.isCompressed()) {
+ foreach (const Metadata *data, metadata) {
+ if (compressedRepository && !data->repository().isCompressed()) {
continue;
}
if (statusCanceledOrFailed())
return false;
- if (data.directory.isEmpty())
+ if (data->path().isEmpty())
continue;
- if (parseChecksum) {
- const QString updatesXmlPath = data.directory + QLatin1String("/Updates.xml");
- QFile updatesFile(updatesXmlPath);
- try {
- QInstaller::openForRead(&updatesFile);
- } catch(const Error &e) {
- qDebug() << "Error opening Updates.xml:" << e.message();
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- int line = 0;
- int column = 0;
- QString error;
- QDomDocument doc;
- if (!doc.setContent(&updatesFile, &error, &line, &column)) {
- qDebug().nospace() << "Parse error in file" << updatesFile.fileName()
- << ": " << error << " at line " << line << " col " << column;
- setStatus(PackageManagerCore::Failure, tr("Cannot add temporary update source information."));
- return false;
- }
-
- const QDomNode checksum = doc.documentElement().firstChildElement(QLatin1String("Checksum"));
- if (!checksum.isNull())
- m_core->setTestChecksum(checksum.toElement().text().toLower() == scTrue);
- }
- if (compressedRepository)
- m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1));
+ if (data->repository().isCompressed())
+ m_compressedPackageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 2, data->repository().postLoadComponentScript()));
else
- m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data.directory), 1));
+ m_packageSources.insert(PackageSource(QUrl::fromLocalFile(data->path()), 0, data->repository().postLoadComponentScript()));
- ProductKeyCheck::instance()->addPackagesFromXml(data.directory + QLatin1String("/Updates.xml"));
+ ProductKeyCheck::instance()->addPackagesFromXml(data->path() + QLatin1String("/Updates.xml"));
}
if ((compressedRepository && m_compressedPackageSources.count() == 0 ) ||
(!compressedRepository && m_packageSources.count() == 0)) {
@@ -2396,7 +3097,6 @@ void PackageManagerCorePrivate::restoreCheckState()
}
m_coreCheckedHash.clear();
- m_componentsToInstallCalculated = false;
}
void PackageManagerCorePrivate::storeCheckState()
@@ -2409,6 +3109,138 @@ void PackageManagerCorePrivate::storeCheckState()
m_coreCheckedHash.insert(component, component->checkState());
}
+void PackageManagerCorePrivate::updateComponentInstallActions()
+{
+ for (Component *component : m_core->components(PackageManagerCore::ComponentType::All)) {
+ component->setInstallAction(component->isInstalled()
+ ? ComponentModelHelper::KeepInstalled
+ : ComponentModelHelper::KeepUninstalled);
+ }
+ for (Component *component : uninstallerCalculator()->resolvedComponents())
+ component->setInstallAction(ComponentModelHelper::Uninstall);
+ for (Component *component : installerCalculator()->resolvedComponents())
+ component->setInstallAction(ComponentModelHelper::Install);
+}
+
+bool PackageManagerCorePrivate::enableAllCategories()
+{
+ QSet<RepositoryCategory> repoCategories = m_data.settings().repositoryCategories();
+ bool additionalRepositoriesEnabled = false;
+ for (const auto &category : repoCategories) {
+ if (!category.isEnabled()) {
+ additionalRepositoriesEnabled = true;
+ enableRepositoryCategory(category, true);
+ }
+ }
+ return additionalRepositoriesEnabled;
+}
+
+void PackageManagerCorePrivate::enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable)
+{
+ RepositoryCategory replacement = repoCategory;
+ replacement.setEnabled(enable);
+ QSet<RepositoryCategory> tmpRepoCategories = m_data.settings().repositoryCategories();
+ if (tmpRepoCategories.contains(repoCategory)) {
+ tmpRepoCategories.remove(repoCategory);
+ tmpRepoCategories.insert(replacement);
+ m_data.settings().addRepositoryCategories(tmpRepoCategories);
+ }
+}
+
+bool PackageManagerCorePrivate::installablePackagesFound(const QStringList& components)
+{
+ if (components.isEmpty())
+ return true;
+
+ PackagesList remotes = remotePackages();
+
+ auto componentsNotFoundForInstall = QtConcurrent::blockingFiltered(
+ components,
+ [remotes](const QString& installerPackage) {
+ return filterMissingPackagesToInstall(installerPackage, remotes);
+ }
+ );
+
+ if (componentsNotFoundForInstall.count() > 0) {
+ QList<ComponentAlias *> aliasList = componentAliases();
+ auto aliasesNotFoundForInstall = QtConcurrent::blockingFiltered(
+ componentsNotFoundForInstall,
+ [aliasList](const QString& installerPackage) {
+ return filterMissingAliasesToInstall(installerPackage, aliasList);
+ }
+ );
+
+ if (aliasesNotFoundForInstall.count() > 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote().nospace() << "Cannot select " << aliasesNotFoundForInstall.join(QLatin1String(", ")) << ". Component(s) not found.";
+ setStatus(PackageManagerCore::NoPackagesFound);
+ return false;
+ }
+ }
+ return true;
+}
+
+void PackageManagerCorePrivate::deferredRename(const QString &oldName, const QString &newName, bool restart)
+{
+
+#ifdef Q_OS_WINDOWS
+ const QString currentExecutable = QCoreApplication::applicationFilePath();
+ QString tmpExecutable = generateTemporaryFileName(currentExecutable);
+ QFile::rename(currentExecutable, tmpExecutable);
+ QFile::rename(oldName, newName);
+
+ QStringList arguments;
+ if (restart) {
+ // Restart with same command line arguments as first executable
+ arguments = QCoreApplication::arguments();
+ arguments.removeFirst(); // Remove program name
+ arguments.prepend(tmpExecutable);
+ arguments.prepend(QLatin1String("--")
+ + CommandLineOptions::scCleanupUpdate);
+ } else {
+ arguments.append(QLatin1String("--")
+ + CommandLineOptions::scCleanupUpdateOnly);
+ arguments.append(tmpExecutable);
+ }
+ QProcessWrapper::startDetached2(newName, arguments);
+#elif defined Q_OS_MACOS
+ // In macos oldName is the name of the maintenancetool we got from repository
+ // It might be extracted to a folder to avoid overlapping with running maintenancetool
+ // Here, ditto renames it to newName (and possibly moves from the subfolder).
+ if (oldName != newName) {
+ //1. Rename/move maintenancetool
+ QProcessWrapper process;
+ process.start(QLatin1String("ditto"), QStringList() << oldName << newName);
+ if (!process.waitForFinished()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Failed to rename maintenancetool from :" << oldName << " to "<<newName;
+ }
+ //2. Remove subfolder
+ QDir subDirectory(oldName);
+ subDirectory.cdUp();
+ QString subDirectoryPath = QDir::cleanPath(subDirectory.absolutePath());
+ QString targetDirectoryPath = QDir::cleanPath(targetDir());
+
+ //Make sure there is subdirectory in the targetdir so we don't delete the installation
+ if (subDirectoryPath.startsWith(targetDirectoryPath) && subDirectoryPath != targetDirectoryPath)
+ subDirectory.removeRecursively();
+ }
+
+ //3. If new maintenancetool name differs from original, remove the original maintenance tool
+ if (!isInstaller()) {
+ QString currentAppBundlePath = getAppBundlePath();
+ if (currentAppBundlePath != newName) {
+ QDir oldBundlePath(currentAppBundlePath);
+ oldBundlePath.removeRecursively();
+ }
+ }
+ SelfRestarter::setRestartOnQuit(restart);
+
+#elif defined Q_OS_LINUX
+ QFile::remove(newName);
+ QFile::rename(oldName, newName);
+ SelfRestarter::setRestartOnQuit(restart);
+#endif
+}
+
void PackageManagerCorePrivate::connectOperationCallMethodRequest(Operation *const operation)
{
QObject *const operationObject = dynamic_cast<QObject *> (operation);
@@ -2459,6 +3291,34 @@ void PackageManagerCorePrivate::handleMethodInvocationRequest(const QString &inv
QMetaObject::invokeMethod(obj, qPrintable(invokableMethodName));
}
+/*
+ Adds the \a path for deletetion. Unlike files for delayed deletion, which are deleted
+ on the start of next installer run, these paths are deleted on exit.
+*/
+void PackageManagerCorePrivate::addPathForDeletion(const QString &path)
+{
+ m_tmpPathDeleter.add(path);
+}
+
+void PackageManagerCorePrivate::unpackAndInstallComponents(const QList<Component *> &components,
+ const double progressOperationSize)
+{
+ // Perform extract operations
+ unpackComponents(components, progressOperationSize);
+
+ // Perform rest of the operations and mark component as installed
+ const int componentsToInstallCount = components.size();
+ int installedComponents = 0;
+ foreach (Component *component, components) {
+ installComponent(component, progressOperationSize);
+
+ ++installedComponents;
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("%1 of %2 components installed.")
+ .arg(QString::number(installedComponents), QString::number(componentsToInstallCount)));
+ }
+ ProgressCoordinator::instance()->emitAdditionalProgressStatus(tr("All components installed."));
+}
+
void PackageManagerCorePrivate::processFilesForDelayedDeletion()
{
if (m_filesForDelayedDeletion.isEmpty())
@@ -2468,35 +3328,218 @@ void PackageManagerCorePrivate::processFilesForDelayedDeletion()
foreach (const QString &i, filesForDelayedDeletion) {
QFile file(i); //TODO: this should happen asnyc and report errors, I guess
if (file.exists() && !file.remove()) {
- qWarning("Cannot delete file %s: %s", qPrintable(i),
- qPrintable(file.errorString()));
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot delete file " << qPrintable(i) <<
+ ": " << qPrintable(file.errorString());
+
m_filesForDelayedDeletion << i; // try again next time
}
}
}
-void PackageManagerCorePrivate::findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result)
+bool PackageManagerCorePrivate::calculateComponentsAndRun()
{
- QString executable;
- QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Executable | QDir::Files | QDir::System, QDirIterator::Subdirectories );
+ bool componentsOk = m_core->recalculateAllComponents();
- while (it.hasNext()) {
- executable = it.next();
- foreach (QString exclude, excludeFiles) {
- if (QDir::toNativeSeparators(executable.toLower())
- != QDir::toNativeSeparators(exclude.toLower())) {
- result->append(executable);
+ if (statusCanceledOrFailed()) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Installation canceled.";
+ } else if (componentsOk && acceptLicenseAgreements()) {
+ try {
+ loadComponentScripts(installerCalculator()->resolvedComponents(), true);
+ } catch (const Error &error) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << error.message();
+ return false;
+ }
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote()
+ << htmlToString(m_core->componentResolveReasons());
+
+ const bool spaceOk = m_core->checkAvailableSpace();
+ qCDebug(QInstaller::lcInstallerInstallLog) << m_core->availableSpaceMessage();
+
+ if (!spaceOk || !(m_autoConfirmCommand || askUserConfirmCommand())) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Installation aborted.";
+ } else if (m_core->run()) {
+ // Write maintenance tool if required
+ m_core->writeMaintenanceTool();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PackageManagerCorePrivate::acceptLicenseAgreements() const
+{
+ // Always skip for uninstaller
+ if (isUninstaller())
+ return true;
+
+ foreach (Component *component, m_core->orderedComponentsToInstall()) {
+ // Package manager or updater, no need to accept again as long as
+ // the component is installed.
+ if (m_core->isMaintainer() && component->isInstalled())
+ continue;
+ m_core->addLicenseItem(component->licenses());
+ }
+
+ const QString acceptanceText = ProductKeyCheck::instance()->licenseAcceptanceText();
+ if (!acceptanceText.isEmpty()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << acceptanceText;
+ if (!m_autoAcceptLicenses && !acceptRejectCliQuery())
+ return false;
+ }
+
+ QHash<QString, QMap<QString, QString>> priorityHash = m_core->sortedLicenses();
+ QStringList priorities = priorityHash.keys();
+ priorities.sort();
+ for (int i = priorities.length() - 1; i >= 0; --i) {
+ QString priority = priorities.at(i);
+ QMap<QString, QString> licenses = priorityHash.value(priority);
+
+ QStringList licenseNames = licenses.keys();
+ licenseNames.sort(Qt::CaseInsensitive);
+ for (QString licenseName : licenseNames) {
+ if (m_autoAcceptLicenses
+ || askUserAcceptLicense(licenseName, licenses.value(licenseName))) {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "License"
+ << licenseName << "accepted by user.";
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "License"
+ << licenseName<< "not accepted by user. Aborting.";
+ return false;
}
}
}
+ return true;
+}
+
+bool PackageManagerCorePrivate::askUserAcceptLicense(const QString &name, const QString &content) const
+{
+ qCDebug(QInstaller::lcInstallerInstallLog) << "You must accept "
+ "the terms contained in the following license agreement "
+ "before continuing with the installation:" << name;
+
+ forever {
+ const QString input = m_core->readConsoleLine(QLatin1String("Accept|Reject|Show"));
+
+ if (QString::compare(input, QLatin1String("Accept"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("A"), Qt::CaseInsensitive) == 0) {
+ return true;
+ } else if (QString::compare(input, QLatin1String("Reject"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("R"), Qt::CaseInsensitive) == 0) {
+ return false;
+ } else if (QString::compare(input, QLatin1String("Show"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("S"), Qt::CaseInsensitive) == 0) {
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << content;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input;
+ }
+ }
+}
+
+bool PackageManagerCorePrivate::acceptRejectCliQuery() const
+{
+ forever {
+ const QString input = m_core->readConsoleLine(QLatin1String("Accept|Reject"));
+
+ if (QString::compare(input, QLatin1String("Accept"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("A"), Qt::CaseInsensitive) == 0) {
+ return true;
+ } else if (QString::compare(input, QLatin1String("Reject"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("R"), Qt::CaseInsensitive) == 0) {
+ return false;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input;
+ }
+ }
+}
+
+bool PackageManagerCorePrivate::askUserConfirmCommand() const
+{
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Do you want to continue?";
+
+ forever {
+ const QString input = m_core->readConsoleLine(QLatin1String("Yes|No"));
+
+ if (QString::compare(input, QLatin1String("Yes"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("Y"), Qt::CaseInsensitive) == 0) {
+ return true;
+ } else if (QString::compare(input, QLatin1String("No"), Qt::CaseInsensitive) == 0
+ || QString::compare(input, QLatin1String("N"), Qt::CaseInsensitive) == 0) {
+ return false;
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Unknown answer:" << input;
+ }
+ }
+}
+
+bool PackageManagerCorePrivate::packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const
+{
+ bool updateNeeded = true;
+ const QString contentSha1 = update->data(scContentSha1).toString();
+ if (!contentSha1.isEmpty()) {
+ if (contentSha1 == localPackage.contentSha1)
+ updateNeeded = false;
+ } else {
+ const QString updateVersion = update->data(scVersion).toString();
+ if (KDUpdater::compareVersion(updateVersion, localPackage.version) <= 0)
+ updateNeeded = false;
+ }
+ return updateNeeded;
+}
+
+void PackageManagerCorePrivate::commitPendingUnstableComponents()
+{
+ if (m_pendingUnstableComponents.isEmpty())
+ return;
+
+ for (auto &componentName : m_pendingUnstableComponents.keys()) {
+ Component *const component = m_core->componentByName(componentName);
+ if (!component) {
+ qCWarning(lcInstallerInstallLog) << "Failure while marking component "
+ "unstable. No such component exists:" << componentName;
+ continue;
+ }
+
+ const QPair<Component::UnstableError, QString> unstableError
+ = m_pendingUnstableComponents.value(componentName);
+
+ component->setUnstable(unstableError.first, unstableError.second);
+ }
+ m_pendingUnstableComponents.clear();
}
-QStringList PackageManagerCorePrivate::runningInstallerProcesses(const QStringList &excludeFiles)
+void PackageManagerCorePrivate::createAutoDependencyHash(const QString &component, const QString &oldDependencies, const QString &newDependencies)
{
- QStringList resultFiles;
- findExecutablesRecursive(QCoreApplication::applicationDirPath(), excludeFiles, &resultFiles);
- return checkRunningProcessesFromList(resultFiles);
+ // User might have changed autodependencies with setValue. Remove the old values.
+ const QStringList oldDependencyList = oldDependencies.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ for (const QString &removedDependency : oldDependencyList) {
+ QStringList value = m_autoDependencyComponentHash.value(removedDependency);
+ value.removeAll(component);
+ if (value.isEmpty())
+ m_autoDependencyComponentHash.remove(removedDependency);
+ else
+ m_autoDependencyComponentHash.insert(removedDependency, value);
+ }
+
+ const QStringList newDependencyList = newDependencies.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ for (const QString &autodepend : newDependencyList) {
+ QStringList value = m_autoDependencyComponentHash.value(autodepend);
+ if (!value.contains(component)) {
+ value.append(component);
+ m_autoDependencyComponentHash.insert(autodepend, value);
+ }
+ }
}
+void PackageManagerCorePrivate::createLocalDependencyHash(const QString &component, const QString &dependencies)
+{
+ const QStringList localDependencies = dependencies.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
+ for (const QString &depend : localDependencies) {
+ QStringList value = m_localDependencyComponentHash.value(depend);
+ if (!value.contains(component)) {
+ value.append(component);
+ m_localDependencyComponentHash.insert(depend, value);
+ }
+ }
+}
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h
index 3e8f831a3..c1b50e615 100644
--- a/src/libs/installer/packagemanagercore_p.h
+++ b/src/libs/installer/packagemanagercore_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,6 +35,8 @@
#include "packagemanagerproxyfactory.h"
#include "packagesource.h"
#include "qinstallerglobal.h"
+#include "component.h"
+#include "fileutils.h"
#include "sysinfo.h"
#include "updatefinder.h"
@@ -52,13 +54,15 @@ using namespace KDUpdater;
namespace QInstaller {
struct BinaryLayout;
-class Component;
+struct AliasSource;
+class AliasFinder;
class ScriptEngine;
class ComponentModel;
-class TempDirDeleter;
+class ComponentAlias;
class InstallerCalculator;
class UninstallerCalculator;
class RemoteFileEngineHandler;
+class ComponentSortFilterProxyModel;
class PackageManagerCorePrivate : public QObject
{
@@ -67,21 +71,15 @@ class PackageManagerCorePrivate : public QObject
Q_DISABLE_COPY(PackageManagerCorePrivate)
public:
- enum OperationType {
- Backup,
- Perform,
- Undo
- };
-
explicit PackageManagerCorePrivate(PackageManagerCore *core);
explicit PackageManagerCorePrivate(PackageManagerCore *core, qint64 magicInstallerMaker,
- const QList<OperationBlob> &performedOperations);
+ const QList<OperationBlob> &performedOperations, const QString &datFileName);
~PackageManagerCorePrivate();
static bool isProcessRunning(const QString &name, const QList<ProcessInfo> &processes);
- static bool performOperationThreaded(Operation *op, PackageManagerCorePrivate::OperationType type
- = PackageManagerCorePrivate::Perform);
+ static bool performOperationThreaded(Operation *op, UpdateOperation::OperationType type
+ = UpdateOperation::Perform);
void initialize(const QHash<QString, QString> &params);
bool isOfflineOnly() const;
@@ -93,20 +91,29 @@ public:
QString registerPath();
bool directoryWritable(const QString &path) const;
- bool subdirectoriesWritable(const QString &path) const;
QString maintenanceToolName() const;
+ QString maintenanceToolAliasPath() const;
QString installerBinaryPath() const;
+ QString offlineBinaryName() const;
+ QString datFileName();
void writeMaintenanceConfigFiles();
void readMaintenanceConfigFiles(const QString &targetDir);
void writeMaintenanceTool(OperationList performedOperations);
+ void writeOfflineBaseBinary();
+
+ void writeMaintenanceToolAlias(const QString &maintenanceToolName);
QString componentsXmlPath() const;
QString configurationFileName() const;
bool buildComponentTree(QHash<QString, Component*> &components, bool loadScript);
+ bool buildComponentAliases();
+
+ template <typename T>
+ bool loadComponentScripts(const T &components, const bool postScript = false);
void cleanUpComponentEnvironment();
ScriptEngine *componentScriptEngine() const;
@@ -116,6 +123,7 @@ public:
void clearUpdaterComponentLists();
QList<Component*> &replacementDependencyComponents();
QHash<QString, QPair<Component*, Component*> > &componentsToReplace();
+ QHash<QString, QStringList > &componentReplaces();
void clearInstallerCalculator();
InstallerCalculator *installerCalculator() const;
@@ -129,12 +137,16 @@ public:
bool runUninstaller();
bool isUninstaller() const;
- void runUpdater();
bool isUpdater() const;
bool runPackageUpdater();
bool isPackageManager() const;
+ bool runOfflineGenerator();
+ bool isOfflineGenerator() const;
+
+ bool isPackageViewer() const;
+
QString replaceVariables(const QString &str) const;
QByteArray replaceVariables(const QByteArray &str) const;
@@ -162,20 +174,27 @@ public:
m_performedOperationsCurrentSession.clear();
}
- void installComponent(Component *component, double progressOperationSize,
- bool adminRightsGained = false);
+ void unpackComponents(const QList<Component *> &components, double progressOperationSize);
+
+ void installComponent(Component *component, double progressOperationSize);
+ PackageManagerCore::Status fetchComponentsAndInstall(const QStringList& components);
+
+ void setComponentSelection(const QString &id, Qt::CheckState state);
signals:
void installationStarted();
void installationFinished();
void uninstallationStarted();
void uninstallationFinished();
+ void offlineGenerationStarted();
+ void offlineGenerationFinished();
public:
UpdateFinder *m_updateFinder;
- UpdateFinder *m_compressedFinder;
+ AliasFinder *m_aliasFinder;
QSet<PackageSource> m_packageSources;
QSet<PackageSource> m_compressedPackageSources;
+ QSet<AliasSource> m_aliasSources;
std::shared_ptr<LocalPackageHub> m_localPackageHub;
QStringList m_filesForDelayedDeletion;
@@ -185,11 +204,16 @@ public:
bool m_needsHardRestart;
bool m_testChecksum;
bool m_launchedAsRoot;
+ bool m_commandLineInstance;
+ bool m_defaultInstall;
+ bool m_userSetBinaryMarker;
+ bool m_checkAvailableSpace;
bool m_completeUninstall;
bool m_needToWriteMaintenanceTool;
PackageManagerCoreData m_data;
- QHash<QString, bool> m_sharedFlags;
QString m_installerBaseBinaryUnreplaced;
+ QString m_offlineBaseBinaryUnreplaced;
+ QStringList m_offlineGeneratorResourceCollections;
QList<QInstaller::Component*> m_rootComponents;
QList<QInstaller::Component*> m_rootDependencyReplacements;
@@ -198,11 +222,17 @@ public:
QList<QInstaller::Component*> m_updaterComponentsDeps;
QList<QInstaller::Component*> m_updaterDependencyReplacements;
+ QHash<QString, QInstaller::ComponentAlias *> m_componentAliases;
+
OperationList m_ownedOperations;
OperationList m_performedOperationsOld;
OperationList m_performedOperationsCurrentSession;
bool m_dependsOnLocalInstallerBinary;
+ QStringList m_allowedRunningProcesses;
+ bool m_autoAcceptLicenses;
+ bool m_disableWriteMaintenanceTool;
+ bool m_autoConfirmCommand;
private slots:
void infoMessage(Job *, const QString &message) {
@@ -217,39 +247,66 @@ private slots:
}
void handleMethodInvocationRequest(const QString &invokableMethodName);
+ void addPathForDeletion(const QString &path);
private:
+ void unpackAndInstallComponents(const QList<Component *> &components,
+ const double progressOperationSize);
+
void deleteMaintenanceTool();
+ void deleteMaintenanceToolAlias();
void registerMaintenanceTool();
void unregisterMaintenanceTool();
void writeMaintenanceToolBinary(QFile *const input, qint64 size, bool writeBinaryLayout);
void writeMaintenanceToolBinaryData(QFileDevice *output, QFile *const input,
const OperationList &performed, const BinaryLayout &layout);
+ void writeMaintenanceToolAppBundle(OperationList &performedOperations);
void runUndoOperations(const OperationList &undoOperations, double undoOperationProgressSize,
- bool adminRightsGained, bool deleteOperation);
+ bool deleteOperation);
PackagesList remotePackages();
- PackagesList compressedPackages();
- LocalPackagesHash localInstalledPackages();
- bool fetchMetaInformationFromRepositories();
- bool fetchMetaInformationFromCompressedRepositories();
- bool addUpdateResourcesFromRepositories(bool parseChecksum, bool compressedRepository = false);
+ LocalPackagesMap localInstalledPackages();
+ QList<ComponentAlias *> componentAliases();
+
+ bool fetchMetaInformationFromRepositories(DownloadType type = DownloadType::All);
+ bool addUpdateResourcesFromRepositories(bool compressedRepository = false);
void processFilesForDelayedDeletion();
- void findExecutablesRecursive(const QString &path, const QStringList &excludeFiles, QStringList *result);
- QStringList runningInstallerProcesses(const QStringList &exludeFiles);
+ bool calculateComponentsAndRun();
+ bool acceptLicenseAgreements() const;
+ bool askUserAcceptLicense(const QString &name, const QString &content) const;
+ bool acceptRejectCliQuery() const;
+ bool askUserConfirmCommand() const;
+ bool packageNeedsUpdate(const LocalPackage &localPackage, const Package *update) const;
+ void commitPendingUnstableComponents();
+ void createAutoDependencyHash(const QString &componentName, const QString &oldValue, const QString &newValue);
+ void createLocalDependencyHash(const QString &componentName, const QString &dependencies);
+ void updateComponentInstallActions();
+
+ bool enableAllCategories();
+ void enableRepositoryCategory(const RepositoryCategory &repoCategory, const bool enable);
+
+ bool installablePackagesFound(const QStringList& components);
+
+ void deferredRename(const QString &oldName, const QString &newName, bool restart = false);
+
+ // remove once we deprecate isSelected, setSelected etc...
+ void restoreCheckState();
+ void storeCheckState();
private:
PackageManagerCore *m_core;
MetadataJob m_metadataJob;
+ TempPathDeleter m_tmpPathDeleter;
bool m_updates;
- bool m_compressedUpdates;
+ bool m_aliases;
bool m_repoFetched;
bool m_updateSourcesAdded;
qint64 m_magicBinaryMarker;
- bool m_componentsToInstallCalculated;
+ int m_magicMarkerSupplement;
+
bool m_foundEssentialUpdate;
mutable ScriptEngine *m_componentScriptEngine;
@@ -258,6 +315,8 @@ private:
QHash<QString, QPair<Component*, Component*> > m_componentsToReplaceAllMode;
QHash<QString, QPair<Component*, Component*> > m_componentsToReplaceUpdaterMode;
+ QHash<QString, QPair<Component::UnstableError, QString>> m_pendingUnstableComponents;
+
InstallerCalculator *m_installerCalculator;
UninstallerCalculator *m_uninstallerCalculator;
@@ -265,15 +324,27 @@ private:
ComponentModel *m_defaultModel;
ComponentModel *m_updaterModel;
+ ComponentSortFilterProxyModel *m_componentSortFilterProxyModel;
QObject *m_guiObject;
QScopedPointer<RemoteFileEngineHandler> m_remoteFileEngineHandler;
+ QHash<QString, QVariantMap> m_licenseItems;
-private:
- // remove once we deprecate isSelected, setSelected etc...
- void restoreCheckState();
- void storeCheckState();
QHash<Component*, Qt::CheckState> m_coreCheckedHash;
+ QList<Component*> m_deletedReplacedComponents;
+ AutoDependencyHash m_autoDependencyComponentHash;
+ LocalDependencyHash m_localDependencyComponentHash;
+ QHash<QString, Component *> m_componentByNameHash;
+
+ QStringList m_localVirtualComponents;
+
+ // < name (component replacing others), components to replace>
+ QHash<QString, QStringList > m_componentReplaces;
+
+ QString m_datFileName;
+ bool m_allowCompressedRepositoryInstall;
+ int m_connectedOperations;
+ QStringList m_componentsToBeInstalled;
};
} // namespace QInstaller
diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp
index 599352fb9..1113908bd 100644
--- a/src/libs/installer/packagemanagercoredata.cpp
+++ b/src/libs/installer/packagemanagercoredata.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,7 +34,9 @@
#include <QDesktopServices>
#include <QDir>
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QSettings>
+#include <QStandardPaths>
#ifdef Q_OS_WIN
# include <windows.h>
@@ -44,48 +46,66 @@
namespace QInstaller
{
-PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &variables)
-{
- m_variables = variables;
- setDynamicPredefinedVariables();
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::PackageManagerCoreData
+ \internal
+*/
+PackageManagerCoreData::PackageManagerCoreData(const QHash<QString, QString> &variables, const bool isInstaller)
+{
+ // Add user defined variables before dynamic as user settings can affect dynamic variables.
+ setUserDefinedVariables(variables);
+ addDynamicPredefinedVariables();
// Set some common variables that may used e.g. as placeholder in some of the settings variables or
// in a script or...
- m_variables.insert(scTargetConfigurationFile, QLatin1String("components.xml"));
- m_variables.insert(QLatin1String("InstallerDirPath"), QCoreApplication::applicationDirPath());
- m_variables.insert(QLatin1String("InstallerFilePath"), QCoreApplication::applicationFilePath());
+ addNewVariable(QLatin1String("InstallerDirPath"), QCoreApplication::applicationDirPath());
+ addNewVariable(QLatin1String("InstallerFilePath"), QCoreApplication::applicationFilePath());
#ifdef Q_OS_WIN
- m_variables.insert(QLatin1String("os"), QLatin1String("win"));
+ addNewVariable(QLatin1String("os"), QLatin1String("win"));
#elif defined(Q_OS_MACOS)
- m_variables.insert(QLatin1String("os"), QLatin1String("mac"));
+ addNewVariable(QLatin1String("os"), QLatin1String("mac"));
#elif defined(Q_OS_LINUX)
- m_variables.insert(QLatin1String("os"), QLatin1String("x11"));
+ addNewVariable(QLatin1String("os"), QLatin1String("x11"));
#else
// TODO: add more platforms as needed...
#endif
- m_settings = Settings::fromFileAndPrefix(QLatin1String(":/metadata/installer-config/config.xml"),
- QLatin1String(":/metadata/installer-config/"), Settings::RelaxedParseMode);
+ m_settingsFilePath = QLatin1String(":/metadata/installer-config/config.xml");
+ m_settings = Settings::fromFileAndPrefix(m_settingsFilePath,
+ QFileInfo(m_settingsFilePath).absolutePath(), Settings::RelaxedParseMode);
// fill the variables defined in the settings
- m_variables.insert(QLatin1String("ProductName"), m_settings.applicationName());
- m_variables.insert(QLatin1String("ProductVersion"), m_settings.version());
- m_variables.insert(scTitle, m_settings.title());
- m_variables.insert(scPublisher, m_settings.publisher());
- m_variables.insert(QLatin1String("Url"), m_settings.url());
- m_variables.insert(scStartMenuDir, m_settings.startMenuDir());
- m_variables.insert(scTargetConfigurationFile, m_settings.configurationFileName());
- m_variables.insert(QLatin1String("LogoPixmap"), m_settings.logo());
- m_variables.insert(QLatin1String("WatermarkPixmap"), m_settings.watermark());
- m_variables.insert(QLatin1String("BannerPixmap"), m_settings.banner());
+ addNewVariable(QLatin1String("ProductName"), m_settings.applicationName());
+ addNewVariable(QLatin1String("ProductVersion"), replaceVariables(m_settings.version()));
+ addNewVariable(scTitle, replaceVariables(m_settings.title()));
+ addNewVariable(scPublisher, m_settings.publisher());
+ addNewVariable(QLatin1String("Url"), m_settings.url());
+ addNewVariable(scLogo, m_settings.logo());
+ addNewVariable(scWatermark, m_settings.watermark());
+ addNewVariable(scBanner, m_settings.banner());
+ addNewVariable(scPageListPixmap, m_settings.pageListPixmap());
const QString description = m_settings.runProgramDescription();
if (!description.isEmpty())
- m_variables.insert(scRunProgramDescription, description);
+ addNewVariable(scRunProgramDescription, description);
- m_variables.insert(scTargetDir, replaceVariables(m_settings.targetDir()));
- m_variables.insert(scRemoveTargetDir, replaceVariables(m_settings.removeTargetDir()));
+ // Some settings might change during install, read those settings later from
+ // maintenancetool if maintenancetool is used.
+ if (isInstaller) {
+ addNewVariable(scTargetDir, replaceVariables(m_settings.targetDir()));
+ addNewVariable(scTargetConfigurationFile, m_settings.configurationFileName());
+ addNewVariable(scStartMenuDir, replaceVariables(m_settings.startMenuDir()));
+ } else {
+#ifdef Q_OS_MACOS
+ addNewVariable(scTargetDir, QFileInfo(QCoreApplication::applicationDirPath() + QLatin1String("/../../..")).absoluteFilePath());
+#else
+ addNewVariable(scTargetDir, QCoreApplication::applicationDirPath());
+#endif
+
+ }
+ addNewVariable(scRemoveTargetDir, replaceVariables(m_settings.removeTargetDir()));
}
void PackageManagerCoreData::clear()
@@ -98,12 +118,12 @@ void PackageManagerCoreData::clear()
Set some common variables that may be used e.g. as placeholder in some of the settings
variables or in a script or...
*/
-void PackageManagerCoreData::setDynamicPredefinedVariables()
+void PackageManagerCoreData::addDynamicPredefinedVariables()
{
- m_variables.insert(QLatin1String("rootDir"), QDir::rootPath());
- m_variables.insert(QLatin1String("homeDir"), QDir::homePath());
- m_variables.insert(QLatin1String("RootDir"), QDir::rootPath());
- m_variables.insert(QLatin1String("HomeDir"), QDir::homePath());
+ addNewVariable(QLatin1String("rootDir"), QDir::rootPath());
+ addNewVariable(QLatin1String("homeDir"), QDir::homePath());
+ addNewVariable(QLatin1String("RootDir"), QDir::rootPath());
+ addNewVariable(QLatin1String("HomeDir"), QDir::homePath());
QString dir = QLatin1String("/opt");
#ifdef Q_OS_WIN
@@ -111,21 +131,23 @@ void PackageManagerCoreData::setDynamicPredefinedVariables()
SHGetFolderPath(nullptr, CSIDL_PROGRAM_FILES, nullptr, 0, buffer);
dir = QString::fromWCharArray(buffer);
#elif defined (Q_OS_MACOS)
- dir = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(0);
+ dir = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(1);
+ if (dir.isEmpty())
+ dir = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(0);
#endif
- m_variables.insert(QLatin1String("ApplicationsDir"), dir);
+ addNewVariable(QLatin1String("ApplicationsDir"), dir);
QString dirUser = dir;
#ifdef Q_OS_MACOS
dirUser = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).value(0);
#endif
- m_variables.insert(QLatin1String("ApplicationsDirUser"), dirUser);
+ addNewVariable(QLatin1String("ApplicationsDirUser"), dirUser);
QString dirX86 = dir;
QString dirX64 = dir;
#ifdef Q_OS_WIN
QSettingsWrapper current(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion")
- , QSettingsWrapper::NativeFormat);
+ , QSettings::NativeFormat);
BOOL onWow64Or64bit = TRUE;
#ifndef Q_OS_WIN64
IsWow64Process(GetCurrentProcess(), &onWow64Or64bit);
@@ -142,14 +164,14 @@ void PackageManagerCoreData::setDynamicPredefinedVariables()
dirX86 = replaceWindowsEnvironmentVariables(programfilesX86);
dirX64 = replaceWindowsEnvironmentVariables(programfilesX64);
#endif
- m_variables.insert(QLatin1String("ApplicationsDirX86"), dirX86);
- m_variables.insert(QLatin1String("ApplicationsDirX64"), dirX64);
+ addNewVariable(QLatin1String("ApplicationsDirX86"), dirX86);
+ addNewVariable(QLatin1String("ApplicationsDirX64"), dirX64);
#ifdef Q_OS_WIN
QSettingsWrapper user(QLatin1String("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Explorer\\User Shell Folders"), QSettingsWrapper::NativeFormat);
+ "CurrentVersion\\Explorer\\User Shell Folders"), QSettings::NativeFormat);
QSettingsWrapper system(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\"
- "CurrentVersion\\Explorer\\Shell Folders"), QSettingsWrapper::NativeFormat);
+ "CurrentVersion\\Explorer\\Shell Folders"), QSettings::NativeFormat);
const QString programs = user.value(QLatin1String("Programs"), QString()).toString();
const QString allPrograms = system.value(QLatin1String("Common Programs"), QString())
@@ -157,16 +179,36 @@ void PackageManagerCoreData::setDynamicPredefinedVariables()
QString desktop;
if (m_variables.value(QLatin1String("AllUsers")) == scTrue) {
- desktop = system.value(QLatin1String("Desktop")).toString();
+ desktop = system.value(QLatin1String("Common Desktop")).toString();
} else {
desktop = user.value(QLatin1String("Desktop")).toString();
}
- m_variables.insert(QLatin1String("DesktopDir"), replaceWindowsEnvironmentVariables(desktop));
- m_variables.insert(QLatin1String("UserStartMenuProgramsPath"),
+ addNewVariable(QLatin1String("DesktopDir"), replaceWindowsEnvironmentVariables(desktop));
+ addNewVariable(scUserStartMenuProgramsPath,
replaceWindowsEnvironmentVariables(programs));
- m_variables.insert(QLatin1String("AllUsersStartMenuProgramsPath"),
+ addNewVariable(scAllUsersStartMenuProgramsPath,
replaceWindowsEnvironmentVariables(allPrograms));
#endif
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+ addNewVariable(QLatin1String("FrameworkVersion"), QLatin1String(QUOTE(IFW_VERSION_STR)));
+ // Undocumented, left for compatibility with scripts using the old key
+ addNewVariable(QLatin1String("IFW_VERSION_STR"), QLatin1String(QUOTE(IFW_VERSION_STR)));
+#undef QUOTE
+#undef QUOTE_
+}
+
+void PackageManagerCoreData::setUserDefinedVariables(const QHash<QString, QString> &variables)
+{
+ QHash<QString, QString>::const_iterator it;
+ for (it = variables.begin(); it != variables.end(); ++it)
+ m_variables.insert(it.key(), it.value());
+}
+
+void PackageManagerCoreData::addNewVariable(const QString &key, const QString &value)
+{
+ if (!m_variables.contains(key))
+ m_variables.insert(key, value);
}
Settings &PackageManagerCoreData::settings() const
@@ -192,14 +234,14 @@ bool PackageManagerCoreData::setValue(const QString &key, const QString &normali
return true;
}
-QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_default) const
+QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_default, const QSettings::Format &format) const
{
if (key == scTargetDir) {
QString dir = m_variables.value(key);
if (dir.isEmpty())
dir = replaceVariables(m_settings.value(key, _default).toString());
#ifdef Q_OS_WIN
- return QInstaller::normalizePathName(dir);
+ return QDir::fromNativeSeparators(QInstaller::normalizePathName(dir));
#else
if (dir.startsWith(QLatin1String("~/")))
return QDir::home().absoluteFilePath(dir.mid(2));
@@ -209,13 +251,15 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa
#ifdef Q_OS_WIN
if (!m_variables.contains(key)) {
- static const QRegExp regex(QLatin1String("\\\\|/"));
+ static const QRegularExpression regex(QLatin1String("\\\\|/"));
const QString filename = key.section(regex, 0, -2);
const QString regKey = key.section(regex, -1);
- const QSettingsWrapper registry(filename, QSettingsWrapper::NativeFormat);
+ const QSettingsWrapper registry(filename, format);
if (!filename.isEmpty() && !regKey.isEmpty() && registry.contains(regKey))
return registry.value(regKey).toString();
}
+#else
+ Q_UNUSED(format)
#endif
if (m_variables.contains(key))
@@ -224,6 +268,11 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa
return m_settings.value(key, _default);
}
+QString PackageManagerCoreData::key(const QString &value) const
+{
+ return m_variables.key(value, QString());
+}
+
QString PackageManagerCoreData::replaceVariables(const QString &str) const
{
static const QChar at = QLatin1Char('@');
@@ -247,7 +296,7 @@ QString PackageManagerCoreData::replaceVariables(const QString &str) const
QByteArray PackageManagerCoreData::replaceVariables(const QByteArray &ba) const
{
- static const QChar at = QLatin1Char('@');
+ static const char at = '@';
QByteArray res;
int pos = 0;
while (true) {
diff --git a/src/libs/installer/packagemanagercoredata.h b/src/libs/installer/packagemanagercoredata.h
index ec0d4c302..2dcddf102 100644
--- a/src/libs/installer/packagemanagercoredata.h
+++ b/src/libs/installer/packagemanagercoredata.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#define PACKAGEMANAGERCOREDATA_H
#include "settings.h"
+#include <QSettings>
namespace QInstaller {
@@ -37,23 +38,31 @@ class PackageManagerCoreData
{
public:
PackageManagerCoreData() {}
- explicit PackageManagerCoreData(const QHash<QString, QString> &variables);
+ explicit PackageManagerCoreData(const QHash<QString, QString> &variables, const bool isInstaller);
void clear();
- void setDynamicPredefinedVariables();
+ void addDynamicPredefinedVariables();
+ void setUserDefinedVariables(const QHash<QString, QString> &variables);
+ void addNewVariable(const QString &key, const QString &value);
Settings &settings() const;
QStringList keys() const;
+ inline QString settingsFilePath() {
+ return m_settingsFilePath;
+ }
+
bool contains(const QString &key) const;
bool setValue(const QString &key, const QString &normalizedValue);
- QVariant value(const QString &key, const QVariant &_default = QVariant()) const;
+ QVariant value(const QString &key, const QVariant &_default = QVariant(), const QSettings::Format &format = QSettings::NativeFormat) const;
+ QString key(const QString &value) const;
QString replaceVariables(const QString &str) const;
QByteArray replaceVariables(const QByteArray &ba) const;
private:
mutable Settings m_settings;
+ QString m_settingsFilePath;
QHash<QString, QString> m_variables;
};
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index 934570ae1..1f0462eea 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -41,8 +41,10 @@
#include "productkeycheck.h"
#include "repositorycategory.h"
#include "componentselectionpage_p.h"
+#include "loggingutils.h"
#include "sysinfo.h"
+#include "globals.h"
#include <QApplication>
@@ -52,6 +54,7 @@
#include <QtCore/QTimer>
#include <QAbstractItemView>
+#include <QAction>
#include <QCheckBox>
#include <QComboBox>
#include <QDesktopServices>
@@ -62,24 +65,30 @@
#include <QLineEdit>
#include <QListWidget>
#include <QListWidgetItem>
+#include <QMenuBar>
+#include <QMenu>
#include <QMessageBox>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
+#include <QSplitter>
#include <QStringListModel>
#include <QTextBrowser>
+#include <QFontDatabase>
#include <QVBoxLayout>
#include <QShowEvent>
#include <QFileDialog>
#include <QGroupBox>
-#include <QDesktopWidget>
+#include <QScreen>
#ifdef Q_OS_WIN
# include <qt_windows.h>
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace KDUpdater;
using namespace QInstaller;
@@ -122,7 +131,7 @@ public:
return m_widget;
}
- bool isComplete() const
+ bool isComplete() const override
{
return m_widget->property("complete").toBool();
}
@@ -203,8 +212,9 @@ Q_DECLARE_METATYPE(DynamicInstallerPage*)
class PackageManagerGui::Private
{
public:
- Private()
- : m_currentId(-1)
+ Private(PackageManagerGui *qq)
+ : q(qq)
+ , m_currentId(-1)
, m_modified(false)
, m_autoSwitchPage(true)
, m_showSettingsButton(false)
@@ -228,6 +238,20 @@ public:
QLatin1String("unknown button"));
}
+ void showSettingsButton(bool show)
+ {
+ if (m_showSettingsButton == show)
+ return;
+ q->setOption(QWizard::HaveCustomButton1, show);
+ q->setButtonText(QWizard::CustomButton1, tr("&Settings"));
+ q->button(QWizard::CustomButton1)->setToolTip(
+ PackageManagerGui::tr("Specify proxy settings and configure repositories for add-on components."));
+
+ q->updateButtonLayout();
+ m_showSettingsButton = show;
+ }
+
+ PackageManagerGui *q;
int m_currentId;
bool m_modified;
bool m_autoSwitchPage;
@@ -251,41 +275,37 @@ public:
*/
/*!
- \fn void PackageManagerGui::interrupted()
+ \fn void QInstaller::PackageManagerGui::interrupted()
\sa {gui::interrupted}{gui.interrupted}
*/
/*!
- \fn void PackageManagerGui::languageChanged()
+ \fn void QInstaller::PackageManagerGui::languageChanged()
\sa {gui::languageChanged}{gui.languageChanged}
*/
/*!
- \fn void PackageManagerGui::finishButtonClicked()
+ \fn void QInstaller::PackageManagerGui::finishButtonClicked()
\sa {gui::finishButtonClicked}{gui.finishButtonClicked}
*/
/*!
- \fn void PackageManagerGui::gotRestarted()
+ \fn void QInstaller::PackageManagerGui::gotRestarted()
\sa {gui::gotRestarted}{gui.gotRestarted}
*/
/*!
- \fn void PackageManagerGui::settingsButtonClicked()
+ \fn void QInstaller::PackageManagerGui::settingsButtonClicked()
\sa {gui::settingsButtonClicked}{gui.settingsButtonClicked}
*/
/*!
- \fn void PackageManagerGui::setValidatorForCustomPageRequested(QInstaller::Component *component,
- const QString &name,
- const QString &callbackName)
-
- Sets a validator for the custom page specified by \a name and
- \a callbackName requested by \a component.
+ \fn void QInstaller::PackageManagerGui::aboutApplicationClicked()
+ \sa {gui::aboutApplicationClicked}{gui.aboutApplicationClicked}
*/
/*!
- \fn void PackageManagerGui::packageManagerCore() const
+ \fn void QInstaller::PackageManagerGui::packageManagerCore() const
Returns the package manager core.
*/
@@ -296,7 +316,7 @@ public:
*/
PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
: QWizard(parent)
- , d(new Private)
+ , d(new Private(this))
, m_core(core)
{
if (m_core->isInstaller())
@@ -305,11 +325,24 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
setWindowTitle(tr("Maintain %1").arg(m_core->value(scTitle)));
setWindowFlags(windowFlags() &~ Qt::WindowContextHelpButtonHint);
-#ifndef Q_OS_MACOS
- setWindowIcon(QIcon(m_core->settings().installerWindowIcon()));
+#ifdef Q_OS_MACOS
+ QMenuBar *menuBar = new QMenuBar(this);
+ QMenu *applicationMenu = new QMenu(menuBar);
+ menuBar->addMenu(applicationMenu);
+
+ QAction *aboutAction = new QAction(applicationMenu);
+ aboutAction->setMenuRole(QAction::AboutRole);
+ applicationMenu->addAction(aboutAction);
+
+ connect(aboutAction, &QAction::triggered, this, &PackageManagerGui::aboutApplicationClicked);
#else
- setPixmap(QWizard::BackgroundPixmap, m_core->settings().background());
+ setWindowIcon(QIcon(m_core->settings().installerWindowIcon()));
#endif
+ if (!m_core->settings().wizardShowPageList()) {
+ QString pixmapStr = m_core->settings().background();
+ QInstaller::replaceHighDpiImage(pixmapStr);
+ setPixmap(QWizard::BackgroundPixmap, pixmapStr);
+ }
#ifdef Q_OS_LINUX
setWizardStyle(QWizard::ModernStyle);
setSizeGripEnabled(true);
@@ -323,25 +356,69 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
if (!styleSheetFile.isEmpty()) {
QFile sheet(styleSheetFile);
if (sheet.exists()) {
- if (sheet.open(QIODevice::ReadOnly))
- setStyleSheet(QString::fromLatin1(sheet.readAll()));
- else
- qWarning() << "The specified style sheet file can not be opened.";
+ if (sheet.open(QIODevice::ReadOnly)) {
+ qApp->setStyleSheet(QString::fromLatin1(sheet.readAll()));
+ } else {
+ qCWarning(QInstaller::lcDeveloperBuild) << "The specified style sheet file "
+ "can not be opened.";
+ }
} else {
- qWarning() << "A style sheet file is specified, but it does not exist.";
+ qCWarning(QInstaller::lcDeveloperBuild) << "A style sheet file is specified, "
+ "but it does not exist.";
}
}
setOption(QWizard::NoBackButtonOnStartPage);
setOption(QWizard::NoBackButtonOnLastPage);
+#ifdef Q_OS_MACOS
+ setOptions(options() & ~QWizard::NoDefaultButton);
+ if (QPushButton *nextButton = qobject_cast<QPushButton *>(button(QWizard::NextButton)))
+ nextButton->setFocusPolicy(Qt::StrongFocus);
+#endif
+
+ if (m_core->settings().wizardShowPageList()) {
+ QWidget *sideWidget = new QWidget(this);
+ sideWidget->setObjectName(QLatin1String("SideWidget"));
+
+ m_pageListWidget = new QListWidget(sideWidget);
+ m_pageListWidget->setObjectName(QLatin1String("PageListWidget"));
+ m_pageListWidget->viewport()->setAutoFillBackground(false);
+ m_pageListWidget->setFrameShape(QFrame::NoFrame);
+ m_pageListWidget->setMinimumWidth(200);
+ // The widget should be view-only but we do not want it to be grayed out,
+ // so instead of calling setEnabled(false), do not accept focus.
+ m_pageListWidget->setFocusPolicy(Qt::NoFocus);
+ m_pageListWidget->setSelectionMode(QAbstractItemView::NoSelection);
+ m_pageListWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+ m_pageListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ QVBoxLayout *sideWidgetLayout = new QVBoxLayout(sideWidget);
+
+ QString pageListPixmap = m_core->settings().pageListPixmap();
+ if (!pageListPixmap.isEmpty()) {
+ QInstaller::replaceHighDpiImage(pageListPixmap);
+ QLabel *pageListPixmapLabel = new QLabel(sideWidget);
+ pageListPixmapLabel->setObjectName(QLatin1String("PageListPixmapLabel"));
+ pageListPixmapLabel->setPixmap(pageListPixmap);
+ pageListPixmapLabel->setMinimumWidth(QPixmap(pageListPixmap).width());
+ sideWidgetLayout->addWidget(pageListPixmapLabel);
+ }
+ sideWidgetLayout->addWidget(m_pageListWidget);
+ sideWidget->setLayout(sideWidgetLayout);
+
+ setSideWidget(sideWidget);
+ }
connect(this, &QDialog::rejected, m_core, &PackageManagerCore::setCanceled);
connect(this, &PackageManagerGui::interrupted, m_core, &PackageManagerCore::interrupt);
- // both queued to show the finished page once everything is done
+ // all queued to show the finished page once everything is done
connect(m_core, &PackageManagerCore::installationFinished,
this, &PackageManagerGui::showFinishedPage,
Qt::QueuedConnection);
+ connect(m_core, &PackageManagerCore::offlineGenerationFinished,
+ this, &PackageManagerGui::showFinishedPage,
+ Qt::QueuedConnection);
connect(m_core, &PackageManagerCore::uninstallationFinished,
this, &PackageManagerGui::showFinishedPage,
Qt::QueuedConnection);
@@ -389,10 +466,69 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent)
QTimer::singleShot(30, this, SLOT(setMaxSize()));
}
+/*!
+ Limits installer maximum size to screen size.
+*/
void PackageManagerGui::setMaxSize()
{
- setMaximumSize(qApp->desktop()->availableGeometry(this).size());
+ QSize size = this->screen()->availableGeometry().size();
+ int windowFrameHeight = frameGeometry().height() - geometry().height();
+ int availableHeight = size.height() - windowFrameHeight;
+
+ size.setHeight(availableHeight);
+ setMaximumSize(size);
}
+
+/*!
+ Updates the installer page list.
+*/
+void PackageManagerGui::updatePageListWidget()
+{
+ if (!m_core->settings().wizardShowPageList() || !m_pageListWidget)
+ return;
+
+ static const QRegularExpression regExp1 {QLatin1String("(.)([A-Z][a-z]+)")};
+ static const QRegularExpression regExp2 {QLatin1String("([a-z0-9])([A-Z])")};
+
+ m_pageListWidget->clear();
+ foreach (int id, pageIds()) {
+ PackageManagerPage *page = qobject_cast<PackageManagerPage *>(pageById(id));
+ if (!page->showOnPageList())
+ continue;
+
+ // Use page list title if set, otherwise try to use the normal title. If that
+ // is not set either, use the object name with spaces added between words.
+ QString itemText;
+ if (!page->pageListTitle().isEmpty()) {
+ itemText = page->pageListTitle();
+ } else if (!page->title().isEmpty()) {
+ // Title may contain formatting, return only contained plain text
+ QTextDocument doc;
+ doc.setHtml(page->title());
+ itemText = doc.toPlainText().trimmed();
+ } else {
+ // Remove "Page" suffix from object name if exists and add spaces between words
+ itemText = page->objectName();
+ itemText.remove(QLatin1String("Page"), Qt::CaseInsensitive);
+ itemText.replace(regExp1, QLatin1String("\\1 \\2"));
+ itemText.replace(regExp2, QLatin1String("\\1 \\2"));
+ }
+ QListWidgetItem *item = new QListWidgetItem(itemText, m_pageListWidget);
+ item->setSizeHint(QSize(m_pageListWidget->width(), 30));
+
+ // Give visual indication about current & non-visited pages
+ if (id == d->m_currentId) {
+ QFont currentItemFont = item->font();
+ currentItemFont.setBold(true);
+ item->setFont(currentItemFont);
+ // Current item should be always visible on the list
+ m_pageListWidget->scrollToItem(item);
+ } else {
+ item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
+ }
+ }
+}
+
/*!
Destructs a package manager UI.
*/
@@ -461,7 +597,7 @@ void PackageManagerGui::setTextItems(QObject *object, const QStringList &items)
return;
}
- qDebug() << "Cannot set text items on object of type"
+ qCWarning(QInstaller::lcDeveloperBuild) << "Cannot set text items on object of type"
<< object->metaObject()->className() << ".";
}
@@ -518,7 +654,21 @@ void PackageManagerGui::clickButton(int wb, int delay)
if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb)))
QTimer::singleShot(delay, b, &QAbstractButton::click);
else
- qWarning() << "Button with type: " << d->buttonType(wb) << "not found!";
+ qCWarning(QInstaller::lcDeveloperBuild) << "Button with type: " << d->buttonType(wb) << "not found!";
+}
+
+/*!
+ Clicks the button specified by \a objectName after the delay specified by \a delay.
+
+ \sa {gui::clickButton}{gui.clickButton}
+*/
+void PackageManagerGui::clickButton(const QString &objectName, int delay) const
+{
+ QPushButton *button = findChild<QPushButton *>(objectName);
+ if (button)
+ QTimer::singleShot(delay, button, &QAbstractButton::click);
+ else
+ qCWarning(QInstaller::lcDeveloperBuild) << "Button with objectname: " << objectName << "not found!";
}
/*!
@@ -537,11 +687,27 @@ bool PackageManagerGui::isButtonEnabled(int wb)
if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb)))
return b->isEnabled();
- qWarning() << "Button with type: " << d->buttonType(wb) << "not found!";
+ qCWarning(QInstaller::lcDeveloperBuild) << "Button with type: " << d->buttonType(wb) << "not found!";
return false;
}
/*!
+ Sets \a buttonText for button specified by \a buttonId to a installer page \a pageId.
+
+ \note In some pages, installer will change the button text when entering
+ the page. In that case, you need to connect to \c entered() -signal of the
+ page to change the \a buttonText.
+
+ \sa {gui::setWizardPageButtonText}{gui.setWizardPageButtonText}
+*/
+void PackageManagerGui::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText)
+{
+ PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId));
+ if (p)
+ p->setButtonText(static_cast<QWizard::WizardButton>(buttonId), buttonText);
+}
+
+/*!
Sets a validator for the custom page specified by \a name and
\a callbackName requested by \a component.
*/
@@ -570,7 +736,7 @@ void PackageManagerGui::loadControlScript(const QString &scriptPath)
{
d->m_controlScriptContext = m_core->controlScriptEngine()->loadInContext(
QLatin1String("Controller"), scriptPath);
- qDebug() << "Loaded control script" << scriptPath;
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Loaded control script" << scriptPath;
}
/*!
@@ -584,7 +750,8 @@ void PackageManagerGui::callControlScriptMethod(const QString &methodName)
const QJSValue returnValue = m_core->controlScriptEngine()->callScriptMethod(
d->m_controlScriptContext, methodName);
if (returnValue.isUndefined()) {
- qDebug() << "Control script callback" << methodName << "does not exist.";
+ qCDebug(QInstaller::lcDeveloperBuild) << "Control script callback" << methodName
+ << "does not exist.";
return;
}
} catch (const QInstaller::Error &e) {
@@ -643,7 +810,16 @@ void PackageManagerGui::showEvent(QShowEvent *event)
}
}
}
- setMinimumSize(size());
+ QSize minimumSize;
+ minimumSize.setWidth(m_core->settings().wizardMinimumWidth()
+ ? m_core->settings().wizardMinimumWidth()
+ : width());
+
+ minimumSize.setHeight(m_core->settings().wizardMinimumHeight()
+ ? m_core->settings().wizardMinimumHeight()
+ : height());
+
+ setMinimumSize(minimumSize);
if (minimumWidth() < m_core->settings().wizardDefaultWidth())
resize(m_core->settings().wizardDefaultWidth(), height());
if (minimumHeight() < m_core->settings().wizardDefaultHeight())
@@ -670,6 +846,7 @@ void PackageManagerGui::wizardPageInsertionRequested(QWidget *widget,
// add it
setPage(pageId, new DynamicInstallerPage(widget, m_core));
+ updatePageListWidget();
}
/*!
@@ -688,17 +865,35 @@ void PackageManagerGui::wizardPageRemovalRequested(QWidget *widget)
packageManagerCore()->controlScriptEngine()->removeFromGlobalObject(dynamicPage);
packageManagerCore()->componentScriptEngine()->removeFromGlobalObject(dynamicPage);
}
+ updatePageListWidget();
}
/*!
- Requests the insertion of \a widget on \a page.
+ Requests the insertion of \a widget on \a page. Widget with lower
+ \a position number will be inserted on top.
*/
void PackageManagerGui::wizardWidgetInsertionRequested(QWidget *widget,
- QInstaller::PackageManagerCore::WizardPage page)
+ QInstaller::PackageManagerCore::WizardPage page, int position)
{
Q_ASSERT(widget);
- if (QWizardPage *const p = QWizard::page(page)) {
- p->layout()->addWidget(widget);
+
+ if (PackageManagerPage *p = qobject_cast<PackageManagerPage *>(QWizard::page(page))) {
+ p->m_customWidgets.insert(position, widget);
+ if (p->m_customWidgets.count() > 1 ) {
+ //Reorder the custom widgets based on their position
+ QMultiMap<int, QWidget*>::Iterator it = p->m_customWidgets.begin();
+ while (it != p->m_customWidgets.end()) {
+ p->layout()->removeWidget(it.value());
+ ++it;
+ }
+ it = p->m_customWidgets.begin();
+ while (it != p->m_customWidgets.end()) {
+ p->layout()->addWidget(it.value());
+ ++it;
+ }
+ } else {
+ p->layout()->addWidget(widget);
+ }
packageManagerCore()->controlScriptEngine()->addToGlobalObject(p);
packageManagerCore()->componentScriptEngine()->addToGlobalObject(p);
}
@@ -710,6 +905,12 @@ void PackageManagerGui::wizardWidgetInsertionRequested(QWidget *widget,
void PackageManagerGui::wizardWidgetRemovalRequested(QWidget *widget)
{
Q_ASSERT(widget);
+
+ const QList<int> pages = pageIds();
+ foreach (int id, pages) {
+ PackageManagerPage *managerPage = qobject_cast<PackageManagerPage *>(page(id));
+ managerPage->removeCustomWidget(widget);
+ }
widget->setParent(nullptr);
packageManagerCore()->controlScriptEngine()->removeFromGlobalObject(widget);
packageManagerCore()->componentScriptEngine()->removeFromGlobalObject(widget);
@@ -727,6 +928,7 @@ void PackageManagerGui::wizardPageVisibilityChangeRequested(bool visible, int p)
d->m_defaultPages[p] = page(p);
removePage(p);
}
+ updatePageListWidget();
}
/*!
@@ -752,7 +954,7 @@ QWidget *PackageManagerGui::pageByObjectName(const QString &name) const
if (p && p->objectName() == name)
return p;
}
- qWarning() << "No page found for object name" << name;
+ qCDebug(QInstaller::lcDeveloperBuild) << "No page found for object name" << name;
return nullptr;
}
@@ -780,7 +982,7 @@ QWidget *PackageManagerGui::pageWidgetByObjectName(const QString &name) const
return dp->widget();
return p;
}
- qWarning() << "No page found for object name" << name;
+ qCDebug(QInstaller::lcDeveloperBuild) << "No page found for object name" << name;
return nullptr;
}
@@ -804,7 +1006,7 @@ void PackageManagerGui::cancelButtonClicked()
interrupt = true;
question = tr("Do you want to cancel the installation process?");
if (m_core->isUninstaller())
- question = tr("Do you want to cancel the uninstallation process?");
+ question = tr("Do you want to cancel the removal process?");
} else {
question = tr("Do you want to quit the installer application?");
if (m_core->isUninstaller())
@@ -868,14 +1070,18 @@ void PackageManagerGui::showFinishedPage()
*/
void PackageManagerGui::showSettingsButton(bool show)
{
- if (d->m_showSettingsButton == show)
- return;
-
- d->m_showSettingsButton = show;
- setOption(QWizard::HaveCustomButton1, show);
- setButtonText(QWizard::CustomButton1, tr("Settings"));
+ m_core->setValue(QLatin1String("ShowSettingsButton"), QString::number(show));
+ d->showSettingsButton(show);
+}
- updateButtonLayout();
+/*!
+ Shows the \uicontrol Settings button if \a request is \c true. If script has
+ set the settings button visibility, this function has no effect.
+*/
+void PackageManagerGui::requestSettingsButtonByInstaller(bool request)
+{
+ if (m_core->value(QLatin1String("ShowSettingsButton")).isEmpty())
+ d->showSettingsButton(request);
}
/*!
@@ -953,7 +1159,8 @@ void PackageManagerGui::dependsOnLocalInstallerBinary()
/*!
Called when the current page changes to \a newId. Calls the leaving() method for the old page
and the entering() method for the new one. Also, executes the control script associated with the
- new page by calling executeControlScript().
+ new page by calling executeControlScript(). Updates the page list set as QWizard::sideWidget().
+
Emits the left() and entered() signals.
*/
@@ -971,6 +1178,7 @@ void PackageManagerGui::currentPageChanged(int newId)
if (newPage) {
newPage->entering();
emit newPage->entered();
+ updatePageListWidget();
}
executeControlScript(newId);
@@ -986,55 +1194,49 @@ void PackageManagerGui::currentPageChanged(int newId)
*/
/*!
- \fn PackageManagerPage::~PackageManagerPage()
+ \fn QInstaller::PackageManagerPage::~PackageManagerPage()
Destructs a package manager page.
*/
/*!
- \fn PackageManagerPage::gui() const
+ \fn QInstaller::PackageManagerPage::gui() const
Returns the wizard this page belongs to.
*/
/*!
- \fn PackageManagerPage::isInterruptible() const
+ \fn QInstaller::PackageManagerPage::isInterruptible() const
Returns \c true if the installation can be interrupted.
*/
/*!
- \fn PackageManagerPage::setValidatePageComponent(QInstaller::Component *component)
-
- Sets \a component as the component that validates the page.
-*/
-
-/*!
- \fn PackageManagerPage::settingsButtonRequested() const
+ \fn QInstaller::PackageManagerPage::settingsButtonRequested() const
Returns \c true if the page requests the wizard to show the \uicontrol Settings button.
*/
/*!
- \fn PackageManagerPage::setSettingsButtonRequested(bool request)
+ \fn QInstaller::PackageManagerPage::setSettingsButtonRequested(bool request)
Determines that the page should request the \uicontrol Settings button if \a request is \c true.
*/
/*!
- \fn PackageManagerPage::entered()
+ \fn QInstaller::PackageManagerPage::entered()
This signal is called when a page is entered.
*/
/*!
- \fn PackageManagerPage::left()
+ \fn QInstaller::PackageManagerPage::left()
This signal is called when a page is left.
*/
/*!
- \fn PackageManagerPage::entering()
+ \fn QInstaller::PackageManagerPage::entering()
Called when end users enter the page and the PackageManagerGui:currentPageChanged()
signal is triggered. Supports the QWizardPage::​initializePage() function to ensure
@@ -1044,7 +1246,13 @@ void PackageManagerGui::currentPageChanged(int newId)
*/
/*!
- \fn PackageManagerPage::leaving()
+ \fn QInstaller::PackageManagerPage::showOnPageListChanged()
+
+ Called when page visibility on page list has changed and refresh is needed.
+*/
+
+/*!
+ \fn QInstaller::PackageManagerPage::leaving()
Called when end users leave the page and the PackageManagerGui:currentPageChanged()
signal is triggered.
@@ -1055,19 +1263,26 @@ void PackageManagerGui::currentPageChanged(int newId)
*/
PackageManagerPage::PackageManagerPage(PackageManagerCore *core)
: m_complete(true)
+ , m_titleColor(QString())
+ , m_showOnPageList(true)
, m_needsSettingsButton(false)
, m_core(core)
, validatorComponent(nullptr)
{
- if (!m_core->settings().titleColor().isEmpty()) {
+ if (!m_core->settings().titleColor().isEmpty())
m_titleColor = m_core->settings().titleColor();
- } else {
- QColor defaultColor = style()->standardPalette().text().color();
- m_titleColor = defaultColor.name();
+
+ if (!m_core->settings().wizardShowPageList())
+ setPixmap(QWizard::WatermarkPixmap, wizardPixmap(scWatermark));
+
+ setPixmap(QWizard::BannerPixmap, wizardPixmap(scBanner));
+ setPixmap(QWizard::LogoPixmap, wizardPixmap(scLogo));
+
+ // Can't use PackageManagerPage::gui() here as the page is not set yet
+ if (PackageManagerGui *gui = qobject_cast<PackageManagerGui *>(core->guiObject())) {
+ connect(this, &PackageManagerPage::showOnPageListChanged,
+ gui, &PackageManagerGui::updatePageListWidget);
}
- setPixmap(QWizard::WatermarkPixmap, watermarkPixmap());
- setPixmap(QWizard::BannerPixmap, bannerPixmap());
- setPixmap(QWizard::LogoPixmap, logoPixmap());
}
/*!
@@ -1079,65 +1294,90 @@ PackageManagerCore *PackageManagerPage::packageManagerCore() const
}
/*!
- Returns the watermark pixmap specified in the \c <Watermark> element of the package information
- file.
+ Returns the pixmap specified by \a pixmapType. \a pixmapType can be \c <Banner>,
+ \c <Logo> or \c <Watermark> element of the package information file. If @2x image
+ is provided, returns that instead for high DPI displays.
*/
-QPixmap PackageManagerPage::watermarkPixmap() const
+QPixmap PackageManagerPage::wizardPixmap(const QString &pixmapType) const
{
- return QPixmap(m_core->value(QLatin1String("WatermarkPixmap")));
+ QString pixmapStr = m_core->value(pixmapType);
+ QInstaller::replaceHighDpiImage(pixmapStr);
+ QPixmap pixmap(pixmapStr);
+ if (pixmapType == scBanner) {
+ if (!pixmap.isNull()) {
+ int width;
+ if (m_core->settings().containsValue(QLatin1String("WizardDefaultWidth")) )
+ width = m_core->settings().wizardDefaultWidth();
+ else
+ width = size().width();
+ pixmap = pixmap.scaledToWidth(width, Qt::SmoothTransformation);
+ }
+ }
+ return pixmap;
}
/*!
- Returns the banner pixmap specified in the \c <Banner> element of the package information file.
- Only used by the modern UI style.
+ Returns the product name of the application being installed.
*/
-QPixmap PackageManagerPage::bannerPixmap() const
+QString PackageManagerPage::productName() const
{
- QPixmap banner(m_core->value(QLatin1String("BannerPixmap")));
+ return m_core->value(QLatin1String("ProductName"));
+}
- if (!banner.isNull()) {
- int width;
- if (m_core->settings().containsValue(QLatin1String("WizardDefaultWidth")) )
- width = m_core->settings().wizardDefaultWidth();
- else
- width = size().width();
- banner = banner.scaledToWidth(width, Qt::SmoothTransformation);
- }
- return banner;
+/*!
+ Sets the font color of \a title. The title is specified in the \c <Title>
+ element of the package information file. It is the name of the installer as
+ displayed on the title bar.
+*/
+void PackageManagerPage::setColoredTitle(const QString &title)
+{
+ setTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, title));
+}
+
+/*!
+ Sets the font color of \a subTitle.
+*/
+void PackageManagerPage::setColoredSubTitle(const QString &subTitle)
+{
+ setSubTitle(QString::fromLatin1("<center><font color=\"%1\">%2</font></center>").arg(m_titleColor, subTitle));
}
/*!
- Returns the logo pixmap specified in the \c <Logo> element of the package information file.
+ Sets the title shown on installer page indicator for this page to \a title.
+ Pages that do not set this will use a fallback title instead.
*/
-QPixmap PackageManagerPage::logoPixmap() const
+void PackageManagerPage::setPageListTitle(const QString &title)
{
- return QPixmap(m_core->value(QLatin1String("LogoPixmap")));
+ m_pageListTitle = title;
}
/*!
- Returns the product name of the application being installed.
+ Returns the title shown on installer page indicator for this page. If empty,
+ a fallback title is being used instead.
*/
-QString PackageManagerPage::productName() const
+QString PackageManagerPage::pageListTitle() const
{
- return m_core->value(QLatin1String("ProductName"));
+ return m_pageListTitle;
}
/*!
- Sets the font color of \a title. The title is specified in the \c <Title>
- element of the package information file. It is the name of the installer as
- displayed on the title bar.
+ Sets the page visibility on installer page indicator based on \a show.
+ All pages are shown by default.
*/
-void PackageManagerPage::setColoredTitle(const QString &title)
+void PackageManagerPage::setShowOnPageList(bool show)
{
- setTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, title));
+ if (m_showOnPageList != show)
+ emit showOnPageListChanged();
+
+ m_showOnPageList = show;
}
/*!
- Sets the font color of \a subTitle.
+ Returns \c true if the page should be shown on installer page indicator.
*/
-void PackageManagerPage::setColoredSubTitle(const QString &subTitle)
+bool PackageManagerPage::showOnPageList() const
{
- setSubTitle(QString::fromLatin1("<font color=\"%1\">%2</font>").arg(m_titleColor, subTitle));
+ return m_showOnPageList;
}
/*!
@@ -1156,12 +1396,8 @@ void PackageManagerPage::setComplete(bool complete)
{
m_complete = complete;
if (QWizard *w = wizard()) {
- if (QAbstractButton *cancel = w->button(QWizard::CancelButton)) {
- if (cancel->hasFocus()) {
- if (QAbstractButton *next = w->button(QWizard::NextButton))
- next->setFocus();
- }
- }
+ if (QAbstractButton *nextButton = w->button(QWizard::NextButton))
+ nextButton->setFocus();
}
emit completeChanged();
}
@@ -1185,6 +1421,18 @@ bool PackageManagerPage::validatePage()
}
/*!
+ \internal
+*/
+void PackageManagerPage::removeCustomWidget(const QWidget *widget)
+{
+ for (auto it = m_customWidgets.begin(); it != m_customWidgets.end();) {
+ if (it.value() == widget)
+ it = m_customWidgets.erase(it);
+ else
+ ++it;
+ }
+}
+/*!
Inserts \a widget at the position specified by \a offset in relation to
another widget specified by \a siblingName. The default position is directly
behind the sibling.
@@ -1223,20 +1471,12 @@ int PackageManagerPage::nextId() const
if (next == PackageManagerCore::LicenseCheck) {
// calculate the page after the license page
const int nextNextId = gui()->pageIds().value(gui()->pageIds().indexOf(next) + 1, -1);
- const PackageManagerCore *const core = packageManagerCore();
+ PackageManagerCore *const core = packageManagerCore();
if (core->isUninstaller())
return nextNextId; // forcibly hide the license page if we run as uninstaller
-
- core->calculateComponentsToInstall();
- foreach (Component* component, core->orderedComponentsToInstall()) {
- if (core->isMaintainer() && component->isInstalled())
- continue; // package manager or updater, hide as long as the component is installed
-
- // The component is about to be installed and provides a license, so the page needs to
- // be shown.
- if (!component->licenses().isEmpty())
- return next;
- }
+ core->recalculateAllComponents();
+ if (core->hasLicenses())
+ return next;
return nextNextId; // no component with a license or all components with license installed
}
return next; // default, show the next page
@@ -1252,7 +1492,7 @@ int PackageManagerPage::nextId() const
*/
/*!
- \fn IntroductionPage::packageManagerCoreTypeChanged()
+ \fn QInstaller::IntroductionPage::packageManagerCoreTypeChanged()
This signal is emitted when the package manager core type changes.
*/
@@ -1264,6 +1504,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
: PackageManagerPage(core)
, m_updatesFetched(false)
, m_allPackagesFetched(false)
+ , m_forceUpdate(false)
+ , m_offlineMaintenanceTool(false)
, m_label(nullptr)
, m_msgLabel(nullptr)
, m_errorLabel(nullptr)
@@ -1273,7 +1515,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
, m_removeAllComponents(nullptr)
{
setObjectName(QLatin1String("IntroductionPage"));
- setColoredTitle(tr("Setup - %1").arg(productName()));
+ setColoredTitle(tr("Welcome"));
QVBoxLayout *layout = new QVBoxLayout(this);
setLayout(layout);
@@ -1281,7 +1523,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_msgLabel = new QLabel(this);
m_msgLabel->setWordWrap(true);
m_msgLabel->setObjectName(QLatin1String("MessageLabel"));
- m_msgLabel->setText(tr("Welcome to the %1 Setup Wizard.").arg(productName()));
+ m_msgLabel->setText(tr("Welcome to the %1 Setup.").arg(productName()));
QWidget *widget = new QWidget(this);
QVBoxLayout *boxLayout = new QVBoxLayout(widget);
@@ -1289,19 +1531,16 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_packageManager = new QRadioButton(tr("&Add or remove components"), this);
m_packageManager->setObjectName(QLatin1String("PackageManagerRadioButton"));
boxLayout->addWidget(m_packageManager);
- m_packageManager->setChecked(core->isPackageManager());
connect(m_packageManager, &QAbstractButton::toggled, this, &IntroductionPage::setPackageManager);
m_updateComponents = new QRadioButton(tr("&Update components"), this);
m_updateComponents->setObjectName(QLatin1String("UpdaterRadioButton"));
boxLayout->addWidget(m_updateComponents);
- m_updateComponents->setChecked(core->isUpdater());
connect(m_updateComponents, &QAbstractButton::toggled, this, &IntroductionPage::setUpdater);
m_removeAllComponents = new QRadioButton(tr("&Remove all components"), this);
m_removeAllComponents->setObjectName(QLatin1String("UninstallerRadioButton"));
boxLayout->addWidget(m_removeAllComponents);
- m_removeAllComponents->setChecked(core->isUninstaller());
connect(m_removeAllComponents, &QAbstractButton::toggled,
this, &IntroductionPage::setUninstaller);
connect(m_removeAllComponents, &QAbstractButton::toggled,
@@ -1324,6 +1563,8 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_errorLabel = new QLabel(this);
m_errorLabel->setWordWrap(true);
+ m_errorLabel->setTextFormat(Qt::RichText);
+ m_errorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
boxLayout->addWidget(m_errorLabel);
m_errorLabel->setObjectName(QLatin1String("ErrorLabel"));
@@ -1331,17 +1572,16 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
layout->addWidget(widget);
layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
- core->setCompleteUninstallation(core->isUninstaller());
-
connect(core, &PackageManagerCore::metaJobProgress, this, &IntroductionPage::onProgressChanged);
connect(core, &PackageManagerCore::metaJobTotalProgress, this, &IntroductionPage::setTotalProgress);
connect(core, &PackageManagerCore::metaJobInfoMessage, this, &IntroductionPage::setMessage);
connect(core, &PackageManagerCore::coreNetworkSettingsChanged,
this, &IntroductionPage::onCoreNetworkSettingsChanged);
- m_updateComponents->setEnabled(ProductKeyCheck::instance()->hasValidKey());
+ m_updateComponents->setEnabled(!m_offlineMaintenanceTool && ProductKeyCheck::instance()->hasValidKey());
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
connect(core, &PackageManagerCore::metaJobProgress,
@@ -1350,6 +1590,7 @@ IntroductionPage::IntroductionPage(PackageManagerCore *core)
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -1361,9 +1602,6 @@ int IntroductionPage::nextId() const
if (packageManagerCore()->isUninstaller())
return PackageManagerCore::ReadyForInstallation;
- if (packageManagerCore()->isMaintainer())
- return PackageManagerCore::ComponentSelection;
-
return PackageManagerPage::nextId();
}
@@ -1379,7 +1617,11 @@ bool IntroductionPage::validatePage()
return true;
setComplete(false);
- if (!validRepositoriesAvailable()) {
+ setErrorMessage(QString());
+
+ bool isOfflineOnlyInstaller = core->isInstaller() && core->isOfflineOnly();
+ // If not offline only installer, at least one valid repository needs to be available
+ if (!isOfflineOnlyInstaller && !core->validRepositoriesAvailable()) {
setErrorMessage(QLatin1String("<font color=\"red\">") + tr("At least one valid and enabled "
"repository required for this action to succeed.") + QLatin1String("</font>"));
return isComplete();
@@ -1394,6 +1636,7 @@ bool IntroductionPage::validatePage()
}
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0 ,0)
if (m_taskButton) {
if (!m_taskButton->window()) {
if (QWidget *widget = QApplication::activeWindow())
@@ -1405,6 +1648,7 @@ bool IntroductionPage::validatePage()
m_taskButton->progress()->setVisible(true);
}
#endif
+#endif
// fetch updater packages
if (core->isUpdater()) {
@@ -1424,26 +1668,30 @@ bool IntroductionPage::validatePage()
// fetch common packages
if (core->isInstaller() || core->isPackageManager()) {
- bool localPackagesTreeFetched = false;
if (!m_allPackagesFetched) {
// first try to fetch the server side packages tree
m_allPackagesFetched = core->fetchRemotePackagesTree();
if (!m_allPackagesFetched) {
QString error = core->error();
- if (core->isPackageManager() && core->status() != PackageManagerCore::ForceUpdate) {
- // if that fails and we're in maintenance mode, try to fetch local installed tree
- localPackagesTreeFetched = core->fetchLocalPackagesTree();
- if (localPackagesTreeFetched) {
- // if that succeeded, adjust error message
- error = QLatin1String("<font color=\"red\">") + error + tr(" Only local package "
- "management available.") + QLatin1String("</font>");
- }
+ if (core->status() == PackageManagerCore::ForceUpdate) {
+ // replaces the error string from packagemanagercore
+ error = tr("There is an important update available. Please select '%1' first")
+ .arg(m_updateComponents->text().remove(QLatin1Char('&')));
+
+ m_forceUpdate = true;
+ // Don't call these directly. Need to finish the current validation first,
+ // because changing the selection updates the binary marker and resets the
+ // complete -state of the page.
+ QMetaObject::invokeMethod(m_updateComponents, "setChecked",
+ Qt::QueuedConnection, Q_ARG(bool, true));
+ QMetaObject::invokeMethod(this, "setErrorMessage",
+ Qt::QueuedConnection, Q_ARG(QString, error));
}
setErrorMessage(error);
}
}
- if (m_allPackagesFetched || localPackagesTreeFetched)
+ if (m_allPackagesFetched)
setComplete(true);
}
@@ -1456,9 +1704,11 @@ bool IntroductionPage::validatePage()
gui()->setSettingsButtonEnabled(true);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton)
m_taskButton->progress()->setVisible(!isComplete());
#endif
+#endif
return isComplete();
}
@@ -1500,15 +1750,41 @@ void IntroductionPage::showMaintenanceTools()
/*!
Sets \a enable to \c true to enable the options to install, add, and
- uninstall components on the page.
+ uninstall components on the page. For a maintenance tool without any enabled
+ repositories, the package manager and updater stay disabled regardless of
+ the value of \a enable.
*/
void IntroductionPage::setMaintenanceToolsEnabled(bool enable)
{
- m_packageManager->setEnabled(enable);
- m_updateComponents->setEnabled(enable && ProductKeyCheck::instance()->hasValidKey());
+ m_packageManager->setEnabled(enable && !m_offlineMaintenanceTool);
+ m_updateComponents->setEnabled(enable && !m_offlineMaintenanceTool
+ && ProductKeyCheck::instance()->hasValidKey());
m_removeAllComponents->setEnabled(enable);
}
+/*!
+ Enables or disables the options to add or update components based on the
+ value of \a enable. For a maintenance tool without any enabled repositories,
+ the package manager and updater stay disabled regardless of the value of \a enable.
+*/
+void IntroductionPage::setMaintainerToolsEnabled(bool enable)
+{
+ m_packageManager->setEnabled(enable && !m_offlineMaintenanceTool);
+ m_updateComponents->setEnabled(enable && !m_offlineMaintenanceTool
+ && ProductKeyCheck::instance()->hasValidKey());
+}
+
+/*!
+ Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be
+ fetched again.
+*/
+void IntroductionPage::resetFetchedState()
+{
+ m_updatesFetched = false;
+ m_allPackagesFetched = false;
+ m_forceUpdate = false;
+}
+
// -- public slots
/*!
@@ -1528,7 +1804,7 @@ void IntroductionPage::onProgressChanged(int progress)
}
/*!
- Sets total \a progress value to progress bar.
+ Sets total \a totalProgress value to progress bar.
*/
void IntroductionPage::setTotalProgress(int totalProgress)
{
@@ -1553,31 +1829,15 @@ void IntroductionPage::setErrorMessage(const QString &error)
m_errorLabel->setPalette(palette);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
m_taskButton->progress()->stop();
m_taskButton->progress()->setValue(100);
}
#endif
+#endif
}
-/*!
- Returns \c true if at least one valid and enabled repository is available.
-*/
-bool IntroductionPage::validRepositoriesAvailable() const
-{
- const PackageManagerCore *const core = packageManagerCore();
- bool valid = (core->isInstaller() && core->isOfflineOnly()) || core->isUninstaller();
-
- if (!valid) {
- foreach (const Repository &repo, core->settings().repositories()) {
- if (repo.isEnabled() && repo.isValid()) {
- valid = true;
- break;
- }
- }
- }
- return valid;
-}
// -- private slots
@@ -1585,9 +1845,11 @@ void IntroductionPage::setUpdater(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setUpdater();
emit packageManagerCoreTypeChanged();
+
+ gui()->updatePageListWidget();
}
}
@@ -1595,9 +1857,11 @@ void IntroductionPage::setUninstaller(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(false);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setUninstaller();
emit packageManagerCoreTypeChanged();
+
+ gui()->updatePageListWidget();
}
}
@@ -1605,20 +1869,58 @@ void IntroductionPage::setPackageManager(bool value)
{
if (value) {
entering();
- gui()->showSettingsButton(true);
+ gui()->requestSettingsButtonByInstaller(true);
packageManagerCore()->setPackageManager();
emit packageManagerCoreTypeChanged();
+
+ gui()->updatePageListWidget();
+ }
+}
+/*!
+ Initializes the page.
+*/
+void IntroductionPage::initializePage()
+{
+ PackageManagerCore *core = packageManagerCore();
+ const bool repositoriesAvailable = core->validRepositoriesAvailable();
+
+ if (core->isPackageManager()) {
+ m_packageManager->setChecked(true);
+ } else if (core->isUpdater()) {
+ m_updateComponents->setChecked(true);
+ } else if (core->isUninstaller()) {
+ // If we are running maintenance tool and the default uninstaller
+ // marker is not overridden, set the default checked radio button
+ // based on if we have valid repositories available.
+ if (!core->isUserSetBinaryMarker() && repositoriesAvailable) {
+ m_packageManager->setChecked(true);
+ } else {
+ // No repositories available, default to complete uninstallation.
+ m_removeAllComponents->setChecked(true);
+ core->setCompleteUninstallation(true);
+ }
+ // Disable options that are unusable without repositories
+ m_offlineMaintenanceTool = !repositoriesAvailable;
+ setMaintainerToolsEnabled(repositoriesAvailable);
}
}
/*!
Resets the internal page state, so that on clicking \uicontrol Next the metadata needs to be
- fetched again.
+ fetched again. For maintenance tool, enables or disables options requiring enabled repositories
+ based on the current repository settings.
*/
void IntroductionPage::onCoreNetworkSettingsChanged()
{
- m_updatesFetched = false;
- m_allPackagesFetched = false;
+ resetFetchedState();
+
+ PackageManagerCore *core = packageManagerCore();
+ if (core->isUninstaller() || core->isMaintainer()) {
+ m_offlineMaintenanceTool = !core->validRepositoriesAvailable();
+
+ setMaintainerToolsEnabled(!m_offlineMaintenanceTool);
+ m_removeAllComponents->setChecked(m_offlineMaintenanceTool);
+ }
}
// -- private
@@ -1641,7 +1943,10 @@ void IntroductionPage::entering()
showMaintenanceTools();
setMaintenanceToolsEnabled(true);
}
- setSettingsButtonRequested((!core->isOfflineOnly()) && (!core->isUninstaller()));
+ if (m_forceUpdate)
+ m_packageManager->setEnabled(false);
+
+ setSettingsButtonRequested((!core->isOfflineOnly()));
}
/*!
@@ -1727,7 +2032,6 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core)
m_licenseListWidget = new QListWidget(this);
m_licenseListWidget->setObjectName(QLatin1String("LicenseListWidget"));
- m_licenseListWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
connect(m_licenseListWidget, &QListWidget::currentItemChanged,
this, &LicenseAgreementPage::currentItemChanged);
@@ -1735,21 +2039,25 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core)
m_textBrowser->setReadOnly(true);
m_textBrowser->setOpenLinks(false);
m_textBrowser->setOpenExternalLinks(true);
+ m_textBrowser->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
m_textBrowser->setObjectName(QLatin1String("LicenseTextBrowser"));
- m_textBrowser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(m_textBrowser, &QTextBrowser::anchorClicked, this, &LicenseAgreementPage::openLicenseUrl);
- QVBoxLayout *licenseBoxLayout = new QVBoxLayout();
- licenseBoxLayout->addWidget(m_licenseListWidget);
- licenseBoxLayout->addWidget(m_textBrowser);
+ QSplitter *licenseSplitter = new QSplitter(this);
+ licenseSplitter->setOrientation(Qt::Vertical);
+ licenseSplitter->setChildrenCollapsible(false);
+ licenseSplitter->addWidget(m_licenseListWidget);
+ licenseSplitter->addWidget(m_textBrowser);
+ licenseSplitter->setStretchFactor(0, 1);
+ licenseSplitter->setStretchFactor(1, 3);
QVBoxLayout *layout = new QVBoxLayout(this);
- layout->addLayout(licenseBoxLayout);
+ layout->addWidget(licenseSplitter);
- m_acceptRadioButton = new QRadioButton(this);
- m_acceptRadioButton->setShortcut(QKeySequence(tr("Alt+A", "agree license")));
- m_acceptRadioButton->setObjectName(QLatin1String("AcceptLicenseRadioButton"));
- ClickForwarder *acceptClickForwarder = new ClickForwarder(m_acceptRadioButton);
+ m_acceptCheckBox = new QCheckBox(this);
+ m_acceptCheckBox->setShortcut(QKeySequence(tr("Alt+A", "Agree license")));
+ m_acceptCheckBox->setObjectName(QLatin1String("AcceptLicenseCheckBox"));
+ ClickForwarder *acceptClickForwarder = new ClickForwarder(m_acceptCheckBox);
m_acceptLabel = new QLabel;
m_acceptLabel->setWordWrap(true);
@@ -1757,29 +2065,13 @@ LicenseAgreementPage::LicenseAgreementPage(PackageManagerCore *core)
m_acceptLabel->setObjectName(QLatin1String("AcceptLicenseLabel"));
m_acceptLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
- m_rejectRadioButton = new QRadioButton(this);
- ClickForwarder *rejectClickForwarder = new ClickForwarder(m_rejectRadioButton);
- m_rejectRadioButton->setObjectName(QString::fromUtf8("RejectLicenseRadioButton"));
- m_rejectRadioButton->setShortcut(QKeySequence(tr("Alt+D", "do not agree license")));
-
- m_rejectLabel = new QLabel;
- m_rejectLabel->setWordWrap(true);
- m_rejectLabel->installEventFilter(rejectClickForwarder);
- m_rejectLabel->setObjectName(QLatin1String("RejectLicenseLabel"));
- m_rejectLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
-
QGridLayout *gridLayout = new QGridLayout;
gridLayout->setColumnStretch(1, 1);
- gridLayout->addWidget(m_acceptRadioButton, 0, 0);
+ gridLayout->addWidget(m_acceptCheckBox, 0, 0);
gridLayout->addWidget(m_acceptLabel, 0, 1);
- gridLayout->addWidget(m_rejectRadioButton, 1, 0);
- gridLayout->addWidget(m_rejectLabel, 1, 1);
layout->addLayout(gridLayout);
- connect(m_acceptRadioButton, &QAbstractButton::toggled, this, &QWizardPage::completeChanged);
- connect(m_rejectRadioButton, &QAbstractButton::toggled, this, &QWizardPage::completeChanged);
-
- m_rejectRadioButton->setChecked(true);
+ connect(m_acceptCheckBox, &QAbstractButton::toggled, this, &QWizardPage::completeChanged);
}
/*!
@@ -1792,9 +2084,10 @@ void LicenseAgreementPage::entering()
m_textBrowser->setHtml(QString());
m_licenseListWidget->setVisible(false);
- packageManagerCore()->calculateComponentsToInstall();
foreach (QInstaller::Component *component, packageManagerCore()->orderedComponentsToInstall())
- addLicenseItem(component->licenses());
+ packageManagerCore()->addLicenseItem(component->licenses());
+
+ createLicenseWidgets();
const int licenseCount = m_licenseListWidget->count();
if (licenseCount > 0) {
@@ -1802,6 +2095,8 @@ void LicenseAgreementPage::entering()
m_licenseListWidget->setCurrentItem(m_licenseListWidget->item(0));
}
+ packageManagerCore()->clearLicenses();
+
updateUi();
}
@@ -1811,7 +2106,7 @@ void LicenseAgreementPage::entering()
*/
bool LicenseAgreementPage::isComplete() const
{
- return m_acceptRadioButton->isChecked();
+ return m_acceptCheckBox->isChecked() && ProductKeyCheck::instance()->hasAcceptedAllLicenses();
}
void LicenseAgreementPage::openLicenseUrl(const QUrl &url)
@@ -1825,12 +2120,22 @@ void LicenseAgreementPage::currentItemChanged(QListWidgetItem *current)
m_textBrowser->setText(current->data(Qt::UserRole).toString());
}
-void LicenseAgreementPage::addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash)
+void LicenseAgreementPage::createLicenseWidgets()
{
- for (QHash<QString, QPair<QString, QString> >::const_iterator it = hash.begin();
- it != hash.end(); ++it) {
- QListWidgetItem *item = new QListWidgetItem(it.key(), m_licenseListWidget);
- item->setData(Qt::UserRole, it.value().second);
+ QHash<QString, QMap<QString, QString>> priorityHash = packageManagerCore()->sortedLicenses();
+
+ QStringList priorities = priorityHash.keys();
+ priorities.sort();
+
+ for (int i = priorities.length() - 1; i >= 0; --i) {
+ QString priority = priorities.at(i);
+ QMap<QString, QString> licenses = priorityHash.value(priority);
+ QStringList licenseNames = licenses.keys();
+ licenseNames.sort(Qt::CaseInsensitive);
+ for (QString licenseName : licenseNames) {
+ QListWidgetItem *item = new QListWidgetItem(licenseName, m_licenseListWidget);
+ item->setData(Qt::UserRole, licenses.value(licenseName));
+ }
}
}
@@ -1838,23 +2143,19 @@ void LicenseAgreementPage::updateUi()
{
QString subTitleText;
QString acceptButtonText;
- QString rejectButtonText;
if (m_licenseListWidget->count() == 1) {
subTitleText = tr("Please read the following license agreement. You must accept the terms "
"contained in this agreement before continuing with the installation.");
acceptButtonText = tr("I accept the license.");
- rejectButtonText = tr("I do not accept the license.");
} else {
subTitleText = tr("Please read the following license agreements. You must accept the terms "
"contained in these agreements before continuing with the installation.");
acceptButtonText = tr("I accept the licenses.");
- rejectButtonText = tr("I do not accept the licenses.");
}
m_licenseListWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
setColoredSubTitle(subTitleText);
m_acceptLabel->setText(acceptButtonText);
- m_rejectLabel->setText(rejectButtonText);
}
@@ -1898,7 +2199,7 @@ void ComponentSelectionPage::entering()
QT_TR_NOOP("Please select the components you want to update."),
QT_TR_NOOP("Please select the components you want to install."),
QT_TR_NOOP("Please select the components you want to uninstall."),
- QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them. Any components already installed will not be updated."),
+ QT_TR_NOOP("Select the components to install. Deselect installed components to uninstall them.<br>Any components already installed will not be updated."),
QT_TR_NOOP("Mandatory components need to be updated first before you can select other components to update.")
};
@@ -1913,23 +2214,26 @@ void ComponentSelectionPage::entering()
d->updateTreeView();
// check component model state so we can enable needed component selection buttons
- if (core->isUpdater())
- d->onModelStateChanged(d->m_currentModel->checkedState());
+ d->onModelStateChanged(d->m_currentModel->checkedState());
setModified(isComplete());
- if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly()
- && !core->isUpdater()) {
- d->showCategoryLayout(true);
- core->settings().setAllowUnstableComponents(true);
- } else {
- d->showCategoryLayout(false);
- }
+ d->showCategoryLayout(core->showRepositoryCategories());
d->showCompressedRepositoryButton();
+ d->showCreateOfflineInstallerButton(true);
+
+ // Reset to default supplement state. The page may set it to OfflineGenerator
+ // which needs to be reset after navigating back to the page.
+ core->resetBinaryMarkerSupplement();
}
+/*!
+ Called when end users leave the page and the PackageManagerGui:currentPageChanged()
+ signal is triggered.
+*/
void ComponentSelectionPage::leaving()
{
d->hideCompressedRepositoryButton();
+ d->showCreateOfflineInstallerButton(false);
}
/*!
@@ -1948,6 +2252,29 @@ void ComponentSelectionPage::showEvent(QShowEvent *event)
}
/*!
+ Called when \c ComponentSelectionPage is validated.
+ Tries to load \c component scripts for components about to be installed.
+ Returns \c true if the script loading succeeded and the next page is shown.
+*/
+bool ComponentSelectionPage::validatePage()
+{
+ PackageManagerCore *core = packageManagerCore();
+ try {
+ core->loadComponentScripts(core->orderedComponentsToInstall(), true);
+ } catch (const Error &error) {
+ // As component script loading failed, there is error in the script and component is
+ // marked as unselected. Recalculate so that unselected component is removed from install.
+ // User is then able to select other components for install.
+ core->clearComponentsToInstallCalculated();
+ core->calculateComponentsToInstall();
+ MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("Error"),
+ tr("Error"), error.message());
+ return false;
+ }
+ return true;
+}
+
+/*!
Selects all components in the component tree.
*/
void ComponentSelectionPage::selectAll()
@@ -1978,9 +2305,7 @@ void ComponentSelectionPage::selectDefault()
*/
void ComponentSelectionPage::selectComponent(const QString &id)
{
- const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id);
- if (idx.isValid())
- d->m_currentModel->setData(idx, Qt::Checked, Qt::CheckStateRole);
+ d->m_core->selectComponent(id);
}
/*!
@@ -1988,16 +2313,14 @@ void ComponentSelectionPage::selectComponent(const QString &id)
*/
void ComponentSelectionPage::deselectComponent(const QString &id)
{
- const QModelIndex &idx = d->m_currentModel->indexFromComponentName(id);
- if (idx.isValid())
- d->m_currentModel->setData(idx, Qt::Unchecked, Qt::CheckStateRole);
+ d->m_core->deselectComponent(id);
}
-void ComponentSelectionPage::allowCompressedRepositoryInstall()
-{
- d->allowCompressedRepositoryInstall();
-}
+/*!
+ Adds an additional virtual component with the \a name to be installed.
+ Returns \c true if the virtual component is found and not installed.
+*/
bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name)
{
PackageManagerCore *core = packageManagerCore();
@@ -2006,13 +2329,18 @@ bool ComponentSelectionPage::addVirtualComponentToUninstall(const QString &name)
name, allComponents);
if (component && component->isInstalled() && component->isVirtual()) {
component->setCheckState(Qt::Unchecked);
- core->componentsToInstallNeedsRecalculation();
- qDebug() << "Virtual component " << name << " was selected for uninstall by script.";
+ core->recalculateAllComponents();
+ qCDebug(QInstaller::lcDeveloperBuild) << "Virtual component " << name << " was selected for uninstall by script.";
return true;
}
return false;
}
+void ComponentSelectionPage::setAllowCreateOfflineInstaller(bool allow)
+{
+ d->setAllowCreateOfflineInstaller(allow);
+}
+
void ComponentSelectionPage::setModified(bool modified)
{
setComplete(modified);
@@ -2023,9 +2351,10 @@ void ComponentSelectionPage::setModified(bool modified)
*/
bool ComponentSelectionPage::isComplete() const
{
- if (packageManagerCore()->isInstaller() || packageManagerCore()->isUpdater())
- return d->m_currentModel->checked().count();
- return d->m_currentModel->checkedState().testFlag(ComponentModel::DefaultChecked) == false;
+ if (!d->componentsResolved())
+ return false;
+
+ return d->m_currentModel->componentsSelected();
}
@@ -2087,8 +2416,9 @@ TargetDirectoryPage::TargetDirectoryPage(PackageManagerCore *core)
QPushButton *browseButton = new QPushButton(this);
browseButton->setObjectName(QLatin1String("BrowseDirectoryButton"));
connect(browseButton, &QAbstractButton::clicked, this, &TargetDirectoryPage::dirRequested);
- browseButton->setShortcut(QKeySequence(tr("Alt+R", "browse file system to choose a file")));
+ browseButton->setShortcut(QKeySequence(tr("Alt+R", "Browse file system to choose a file")));
browseButton->setText(tr("B&rowse..."));
+ browseButton->setToolTip(TargetDirectoryPage::tr("Browse file system to choose the installation directory."));
hlayout->addWidget(browseButton);
layout->addLayout(hlayout);
@@ -2145,15 +2475,7 @@ void TargetDirectoryPage::initializePage()
}
/*!
- Checks whether the target directory exists and has contents:
-
- \list
- \li Returns \c true if the directory exists and is empty.
- \li Returns \c false if the directory already exists and contains an installation.
- \li Returns \c false if the target is a file or a symbolic link.
- \li Returns \c true or \c false if the directory exists but is not empty, depending on the
- choice that the end users make in the displayed message box.
- \endlist
+ Returns \c true if the target directory exists and has correct content.
*/
bool TargetDirectoryPage::validatePage()
{
@@ -2169,37 +2491,7 @@ bool TargetDirectoryPage::validatePage()
if (!QVariant(remove).toBool())
return true;
- const QString targetDir = this->targetDir();
- const QDir dir(targetDir);
- // the directory exists and is empty...
- if (dir.exists() && dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot).isEmpty())
- return true;
-
- const QFileInfo fi(targetDir);
- if (fi.isDir()) {
- QString fileName = packageManagerCore()->settings().maintenanceToolName();
-#if defined(Q_OS_MACOS)
- if (QInstaller::isInBundle(QCoreApplication::applicationDirPath()))
- fileName += QLatin1String(".app/Contents/MacOS/") + fileName;
-#elif defined(Q_OS_WIN)
- fileName += QLatin1String(".exe");
-#endif
-
- QFileInfo fi2(targetDir + QDir::separator() + fileName);
- if (fi2.exists()) {
- return failWithError(QLatin1String("TargetDirectoryInUse"), tr("The directory you selected already "
- "exists and contains an installation. Choose a different target for installation."));
- }
-
- return askQuestion(QLatin1String("OverwriteTargetDirectory"),
- tr("You have selected an existing, non-empty directory for installation.\nNote that it will be "
- "completely wiped on uninstallation of this application.\nIt is not advisable to install into "
- "this directory as installation might fail.\nDo you want to continue?"));
- } else if (fi.isFile() || fi.isSymLink()) {
- return failWithError(QLatin1String("WrongTargetDirectory"), tr("You have selected an existing file "
- "or symlink, please choose a different target for installation."));
- }
- return true;
+ return this->packageManagerCore()->installationAllowedToDirectory(targetDir());
}
/*!
@@ -2238,122 +2530,10 @@ void TargetDirectoryPage::dirRequested()
*/
bool TargetDirectoryPage::isComplete() const
{
- m_warningLabel->setText(targetDirWarning());
+ m_warningLabel->setText(packageManagerCore()->targetDirWarning(targetDir()));
return m_warningLabel->text().isEmpty();
}
-/*!
- Returns a warning if the path to the target directory is not set or if it
- is invalid. Installation can continue only after a valid target path is given.
-*/
-QString TargetDirectoryPage::targetDirWarning() const
-{
- if (targetDir().isEmpty())
- return tr("The installation path cannot be empty, please specify a valid directory.");
-
- QDir target(targetDir());
- if (target.isRelative())
- return tr("The installation path cannot be relative, please specify an absolute path.");
-
- QString nativeTargetDir = QDir::toNativeSeparators(target.absolutePath());
- if (!packageManagerCore()->settings().allowNonAsciiCharacters()) {
- for (int i = 0; i < nativeTargetDir.length(); ++i) {
- if (nativeTargetDir.at(i).unicode() & 0xff80) {
- return tr("The path or installation directory contains non ASCII characters. This "
- "is currently not supported! Please choose a different path or installation "
- "directory.");
- }
- }
- }
-
- target = target.canonicalPath();
- if (!target.isEmpty() && (target == QDir::root() || target == QDir::home())) {
- return tr("As the install directory is completely deleted, installing in %1 is forbidden.")
- .arg(QDir::toNativeSeparators(target.path()));
- }
-
-#ifdef Q_OS_WIN
- // folder length (set by user) + maintenance tool name length (no extension) + extra padding
- if ((nativeTargetDir.length()
- + packageManagerCore()->settings().maintenanceToolName().length() + 20) >= MAX_PATH) {
- return tr("The path you have entered is too long, please make sure to "
- "specify a valid path.");
- }
-
- static QRegularExpression reg(QLatin1String(
- "^(?<drive>[a-zA-Z]:\\\\)|"
- "^(\\\\\\\\(?<path>\\w+)\\\\)|"
- "^(\\\\\\\\(?<ip>\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\\\)"));
- const QRegularExpressionMatch regMatch = reg.match(nativeTargetDir);
-
- const QString ipMatch = regMatch.captured(QLatin1String("ip"));
- const QString pathMatch = regMatch.captured(QLatin1String("path"));
- const QString driveMatch = regMatch.captured(QLatin1String("drive"));
-
- if (ipMatch.isEmpty() && pathMatch.isEmpty() && driveMatch.isEmpty()) {
- return tr("The path you have entered is not valid, please make sure to "
- "specify a valid target.");
- }
-
- if (!driveMatch.isEmpty()) {
- bool validDrive = false;
- const QFileInfo drive(driveMatch);
- foreach (const QFileInfo &driveInfo, QDir::drives()) {
- if (drive == driveInfo) {
- validDrive = true;
- break;
- }
- }
- if (!validDrive) { // right now we can only verify local drives
- return tr("The path you have entered is not valid, please make sure to "
- "specify a valid drive.");
- }
- nativeTargetDir = nativeTargetDir.mid(2);
- }
-
- if (nativeTargetDir.endsWith(QLatin1Char('.')))
- return tr("The installation path must not end with '.', please specify a valid directory.");
-
- QString ambiguousChars = QLatin1String("[\"~<>|?*!@#$%^&:,; ]"
- "|(\\\\CON)(\\\\|$)|(\\\\PRN)(\\\\|$)|(\\\\AUX)(\\\\|$)|(\\\\NUL)(\\\\|$)|(\\\\COM\\d)(\\\\|$)|(\\\\LPT\\d)(\\\\|$)");
-#else // Q_OS_WIN
- QString ambiguousChars = QStringLiteral("[~<>|?*!@#$%^&:,; \\\\]");
-#endif // Q_OS_WIN
-
- if (packageManagerCore()->settings().allowSpaceInPath())
- ambiguousChars.remove(QLatin1Char(' '));
-
- static QRegularExpression ambCharRegEx(ambiguousChars, QRegularExpression::CaseInsensitiveOption);
- // check if there are not allowed characters in the target path
- QRegularExpressionMatch match = ambCharRegEx.match(nativeTargetDir);
- if (match.hasMatch()) {
- return tr("The installation path must not contain \"%1\", "
- "please specify a valid directory.").arg(match.captured(0));
- }
-
- return QString();
-}
-
-/*!
- Returns \c true if a warning message specified by \a message with the
- identifier \a identifier is presented to end users for acknowledgment.
-*/
-bool TargetDirectoryPage::askQuestion(const QString &identifier, const QString &message)
-{
- QMessageBox::StandardButton bt =
- MessageBoxHandler::warning(MessageBoxHandler::currentBestSuitParent(), identifier,
- tr("Warning"), message, QMessageBox::Yes | QMessageBox::No);
- return bt == QMessageBox::Yes;
-}
-
-bool TargetDirectoryPage::failWithError(const QString &identifier, const QString &message)
-{
- MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), identifier,
- tr("Error"), message);
- return false;
-}
-
-
// -- StartMenuDirectoryPage
/*!
@@ -2382,7 +2562,7 @@ StartMenuDirectoryPage::StartMenuDirectoryPage(PackageManagerCore *core)
startMenuPath = core->value(QLatin1String("UserStartMenuProgramsPath"));
QStringList dirs = QDir(startMenuPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
if (core->value(scAllUsers, scFalse) == scTrue) {
- startMenuPath = core->value(QLatin1String("AllUsersStartMenuProgramsPath"));
+ startMenuPath = core->value(scAllUsersStartMenuProgramsPath);
dirs += QDir(startMenuPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
}
dirs.removeDuplicates();
@@ -2453,6 +2633,7 @@ ReadyForInstallationPage::ReadyForInstallationPage(PackageManagerCore *core)
{
setPixmap(QWizard::WatermarkPixmap, QPixmap());
setObjectName(QLatin1String("ReadyForInstallationPage"));
+ updatePageListTitle();
QVBoxLayout *baseLayout = new QVBoxLayout();
baseLayout->setObjectName(QLatin1String("BaseLayout"));
@@ -2481,6 +2662,9 @@ ReadyForInstallationPage::ReadyForInstallationPage(PackageManagerCore *core)
setCommitPage(true);
setLayout(baseLayout);
+
+ connect(core, &PackageManagerCore::installerBinaryMarkerChanged,
+ this, &ReadyForInstallationPage::updatePageListTitle);
}
/*!
@@ -2496,7 +2680,7 @@ void ReadyForInstallationPage::entering()
m_taskDetailsBrowser->setVisible(false);
setButtonText(QWizard::CommitButton, tr("U&ninstall"));
setColoredTitle(tr("Ready to Uninstall"));
- m_msgLabel->setText(tr("Setup is now ready to begin removing %1 from your computer.<br>"
+ m_msgLabel->setText(tr("All required information is now available to begin removing %1 from your computer.<br>"
"<font color=\"red\">The program directory %2 will be deleted completely</font>, "
"including all content in that directory!")
.arg(productName(),
@@ -2507,115 +2691,35 @@ void ReadyForInstallationPage::entering()
} else if (packageManagerCore()->isMaintainer()) {
setButtonText(QWizard::CommitButton, tr("U&pdate"));
setColoredTitle(tr("Ready to Update Packages"));
- m_msgLabel->setText(tr("Setup is now ready to begin updating your installation."));
+ m_msgLabel->setText(tr("All required information is now available to begin updating your installation."));
+ } else if (packageManagerCore()->isOfflineGenerator()) {
+ setButtonText(QWizard::CommitButton, tr("Create Offline Installer"));
+ setColoredTitle(tr("Ready to Create Offline Installer"));
+ m_msgLabel->setText(tr("All required information is now available to create an offline installer for selected components."));
} else {
Q_ASSERT(packageManagerCore()->isInstaller());
setButtonText(QWizard::CommitButton, tr("&Install"));
setColoredTitle(tr("Ready to Install"));
- m_msgLabel->setText(tr("Setup is now ready to begin installing %1 on your computer.")
+ m_msgLabel->setText(tr("All required information is now available to begin installing %1 on your computer.")
.arg(productName()));
}
- QString htmlOutput;
- bool componentsOk = packageManagerCore()->calculateComponents(&htmlOutput);
+ bool componentsOk = packageManagerCore()->recalculateAllComponents();
+ const QString htmlOutput = packageManagerCore()->componentResolveReasons();
+
+ qCDebug(QInstaller::lcInstallerInstallLog).noquote() << htmlToString(htmlOutput);
m_taskDetailsBrowser->setHtml(htmlOutput);
- m_taskDetailsBrowser->setVisible(!componentsOk || isVerbose());
+ m_taskDetailsBrowser->setVisible(!componentsOk || LoggingHandler::instance().isVerbose());
setComplete(componentsOk);
- const VolumeInfo tempVolume = VolumeInfo::fromPath(QDir::tempPath());
- const VolumeInfo targetVolume = VolumeInfo::fromPath(packageManagerCore()->value(scTargetDir));
-
- const quint64 tempVolumeAvailableSize = tempVolume.availableSize();
- const quint64 installVolumeAvailableSize = targetVolume.availableSize();
-
- // at the moment there is no better way to check this
- if (targetVolume.size() == 0 && installVolumeAvailableSize == 0) {
- qDebug().nospace() << "Cannot determine available space on device. "
- "Volume descriptor: " << targetVolume.volumeDescriptor()
- << ", Mount path: " << targetVolume.mountPath() << ". Continue silently.";
- return; // TODO: Shouldn't this also disable the "Next" button?
- }
-
- const bool tempOnSameVolume = (targetVolume == tempVolume);
- if (tempOnSameVolume) {
- qDebug() << "Tmp and install directories are on the same volume. Volume mount point:"
- << targetVolume.mountPath() << "Free space available:"
- << humanReadableSize(installVolumeAvailableSize);
- } else {
- qDebug() << "Tmp is on a different volume than the installation directory. Tmp volume mount point:"
- << tempVolume.mountPath() << "Free space available:"
- << humanReadableSize(tempVolumeAvailableSize) << "Install volume mount point:"
- << targetVolume.mountPath() << "Free space available:"
- << humanReadableSize(installVolumeAvailableSize);
- }
-
- const quint64 extraSpace = 256 * 1024 * 1024LL;
- quint64 required(packageManagerCore()->requiredDiskSpace());
- quint64 tempRequired(packageManagerCore()->requiredTemporaryDiskSpace());
- if (required < extraSpace) {
- required += 0.1 * required;
- tempRequired += 0.1 * tempRequired;
+ if (packageManagerCore()->checkAvailableSpace()) {
+ m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(), packageManagerCore()->availableSpaceMessage()));
} else {
- required += extraSpace;
- tempRequired += extraSpace;
- }
-
- quint64 repositorySize = 0;
- const bool createLocalRepository = packageManagerCore()->createLocalRepositoryFromBinary();
- if (createLocalRepository && packageManagerCore()->isInstaller()) {
- repositorySize = QFile(QCoreApplication::applicationFilePath()).size();
- // if we create a local repository, take that space into account as well
- required += repositorySize;
- }
-
- qDebug() << "Installation space required:" << humanReadableSize(required) << "Temporary space "
- "required:" << humanReadableSize(tempRequired) << "Local repository size:"
- << humanReadableSize(repositorySize);
-
- if (tempOnSameVolume && (installVolumeAvailableSize <= (required + tempRequired))) {
- m_msgLabel->setText(tr("Not enough disk space to store temporary files and the "
- "installation. %1 are available, while %2 are at least required.")
- .arg(humanReadableSize(installVolumeAvailableSize),
- humanReadableSize(required + tempRequired)));
+ m_msgLabel->setText(packageManagerCore()->availableSpaceMessage());
setComplete(false);
- return;
}
-
- if (installVolumeAvailableSize < required) {
- m_msgLabel->setText(tr("Not enough disk space to store all selected components! %1 are available "
- "while %2 are at least required.").arg(humanReadableSize(installVolumeAvailableSize),
- humanReadableSize(required)));
- setComplete(false);
- return;
- }
-
- if (tempVolumeAvailableSize < tempRequired) {
- m_msgLabel->setText(tr("Not enough disk space to store temporary files! %1 are available "
- "while %2 are at least required.").arg(humanReadableSize(tempVolumeAvailableSize),
- humanReadableSize(tempRequired)));
- setComplete(false);
- return;
- }
-
- if (installVolumeAvailableSize - required < 0.01 * targetVolume.size()) {
- // warn for less than 1% of the volume's space being free
- m_msgLabel->setText(tr("The volume you selected for installation seems to have sufficient "
- "space for installation, but there will be less than 1% of the volume's space "
- "available afterwards. %1").arg(m_msgLabel->text()));
- } else if (installVolumeAvailableSize - required < 100 * 1024 * 1024LL) {
- // warn for less than 100MB being free
- m_msgLabel->setText(tr("The volume you selected for installation seems to have sufficient "
- "space for installation, but there will be less than 100 MB available afterwards. %1")
- .arg(m_msgLabel->text()));
- }
-
- m_msgLabel->setText(QString::fromLatin1("%1 %2").arg(m_msgLabel->text(),
- tr("Installation will use %1 of disk space.")
- .arg(humanReadableSize(packageManagerCore()->requiredDiskSpace()))));
}
-
-
/*!
Called when end users leave the page and the PackageManagerGui:currentPageChanged()
signal is triggered.
@@ -2625,6 +2729,22 @@ void ReadyForInstallationPage::leaving()
setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton));
}
+/*!
+ Updates page list title based on installer binary type.
+*/
+void ReadyForInstallationPage::updatePageListTitle()
+{
+ PackageManagerCore *core = packageManagerCore();
+ if (core->isOfflineGenerator())
+ setPageListTitle(tr("Ready to Create Offline Installer"));
+ else if (core->isInstaller())
+ setPageListTitle(tr("Ready to Install"));
+ else if (core->isMaintainer())
+ setPageListTitle(tr("Ready to Update"));
+ else if (core->isUninstaller())
+ setPageListTitle(tr("Ready to Uninstall"));
+}
+
// -- PerformInstallationPage
/*!
@@ -2637,13 +2757,13 @@ void ReadyForInstallationPage::leaving()
*/
/*!
- \fn PerformInstallationPage::isInterruptible() const
+ \fn QInstaller::PerformInstallationPage::isInterruptible() const
Returns \c true if the installation can be interrupted.
*/
/*!
- \fn PerformInstallationPage::setAutomatedPageSwitchEnabled(bool request)
+ \fn QInstaller::PerformInstallationPage::setAutomatedPageSwitchEnabled(bool request)
Enables automatic switching of pages when \a request is \c true.
*/
@@ -2654,12 +2774,14 @@ void ReadyForInstallationPage::leaving()
*/
PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core)
: PackageManagerPage(core)
- , m_performInstallationForm(new PerformInstallationForm(this))
+ , m_performInstallationForm(new PerformInstallationForm(core, this))
{
setPixmap(QWizard::WatermarkPixmap, QPixmap());
setObjectName(QLatin1String("PerformInstallationPage"));
+ updatePageListTitle();
m_performInstallationForm->setupUi(this);
+ m_imageChangeTimer.setInterval(10000);
connect(ProgressCoordinator::instance(), &ProgressCoordinator::detailTextChanged,
m_performInstallationForm, &PerformInstallationForm::appendProgressDetails);
@@ -2673,6 +2795,11 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core)
connect(core, &PackageManagerCore::installationFinished,
this, &PerformInstallationPage::installationFinished);
+ connect(core, &PackageManagerCore::offlineGenerationStarted,
+ this, &PerformInstallationPage::installationStarted);
+ connect(core, &PackageManagerCore::offlineGenerationFinished,
+ this, &PerformInstallationPage::installationFinished);
+
connect(core, &PackageManagerCore::uninstallationStarted,
this, &PerformInstallationPage::uninstallationStarted);
connect(core, &PackageManagerCore::uninstallationFinished,
@@ -2683,6 +2810,12 @@ PerformInstallationPage::PerformInstallationPage(PackageManagerCore *core)
connect(this, &PerformInstallationPage::setAutomatedPageSwitchEnabled,
core, &PackageManagerCore::setAutomatedPageSwitchEnabled);
+ connect(core, &PackageManagerCore::installerBinaryMarkerChanged,
+ this, &PerformInstallationPage::updatePageListTitle);
+
+ connect(&m_imageChangeTimer, &QTimer::timeout,
+ this, &PerformInstallationPage::changeCurrentImage);
+
m_performInstallationForm->setDetailsWidgetVisible(true);
setCommitPage(true);
@@ -2715,6 +2848,17 @@ void PerformInstallationPage::entering()
{
setComplete(false);
+ m_performInstallationForm->enableDetails();
+ emit setAutomatedPageSwitchEnabled(true);
+
+ changeCurrentImage();
+ // No need to start the timer if we only have one, or no images
+ if (packageManagerCore()->settings().productImages().count() > 1)
+ m_imageChangeTimer.start();
+
+ if (LoggingHandler::instance().isVerbose()) {
+ m_performInstallationForm->toggleDetails();
+ }
if (packageManagerCore()->isUninstaller()) {
setButtonText(QWizard::CommitButton, tr("U&ninstall"));
setColoredTitle(tr("Uninstalling %1").arg(productName()));
@@ -2725,18 +2869,17 @@ void PerformInstallationPage::entering()
setColoredTitle(tr("Updating components of %1").arg(productName()));
QTimer::singleShot(30, packageManagerCore(), SLOT(runPackageUpdater()));
+ } else if (packageManagerCore()->isOfflineGenerator()) {
+ setButtonText(QWizard::CommitButton, tr("&Create Offline Installer"));
+ setColoredTitle(tr("Creating Offline Installer for %1").arg(productName()));
+
+ QTimer::singleShot(30, packageManagerCore(), SLOT(runOfflineGenerator()));
} else {
setButtonText(QWizard::CommitButton, tr("&Install"));
setColoredTitle(tr("Installing %1").arg(productName()));
QTimer::singleShot(30, packageManagerCore(), SLOT(runInstaller()));
}
-
- m_performInstallationForm->enableDetails();
- emit setAutomatedPageSwitchEnabled(true);
-
- if (isVerbose())
- m_performInstallationForm->toggleDetails();
}
/*!
@@ -2746,6 +2889,23 @@ void PerformInstallationPage::entering()
void PerformInstallationPage::leaving()
{
setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton));
+ m_imageChangeTimer.stop();
+}
+
+/*!
+ Updates page list title based on installer binary type.
+*/
+void PerformInstallationPage::updatePageListTitle()
+{
+ PackageManagerCore *core = packageManagerCore();
+ if (core->isOfflineGenerator())
+ setPageListTitle(tr("Creating Offline Installer"));
+ else if (core->isInstaller())
+ setPageListTitle(tr("Installing"));
+ else if (core->isMaintainer())
+ setPageListTitle(tr("Updating"));
+ else if (core->isUninstaller())
+ setPageListTitle(tr("Uninstalling"));
}
// -- public slots
@@ -2758,6 +2918,36 @@ void PerformInstallationPage::setTitleMessage(const QString &title)
setColoredTitle(title);
}
+/*!
+ Changes the currently shown product image to the next available
+ image from installer configuration.
+*/
+void PerformInstallationPage::changeCurrentImage()
+{
+ const QMap<QString, QVariant> productImages = packageManagerCore()->settings().productImages();
+ if (productImages.isEmpty())
+ return;
+
+ QString nextImage;
+ QString nextUrl;
+ if (m_currentImage.isEmpty() || m_currentImage == productImages.lastKey()) {
+ nextImage = productImages.firstKey();
+ nextUrl = productImages.value(nextImage).toString();
+ } else {
+ QMap<QString, QVariant>::const_iterator i = productImages.constFind(m_currentImage);
+ if (++i != productImages.end()) {
+ nextImage = i.key();
+ nextUrl = i.value().toString();
+ }
+ }
+
+ // Do not update the pixmap if there was only one image available
+ if (nextImage != m_currentImage) {
+ m_performInstallationForm->setImageFromFileName(nextImage, nextUrl);
+ m_currentImage = nextImage;
+ }
+}
+
// -- private slots
void PerformInstallationPage::installationStarted()
@@ -2769,7 +2959,6 @@ void PerformInstallationPage::installationFinished()
{
m_performInstallationForm->stopUpdateProgress();
if (!isAutoSwitching()) {
- m_performInstallationForm->scrollDetailsToTheEnd();
m_performInstallationForm->setDetailsButtonEnabled(false);
setComplete(true);
@@ -2815,7 +3004,8 @@ FinishedPage::FinishedPage(PackageManagerCore *core)
, m_commitButton(nullptr)
{
setObjectName(QLatin1String("FinishedPage"));
- setColoredTitle(tr("Completing the %1 Wizard").arg(productName()));
+ setColoredTitle(tr("Finished the %1 Setup").arg(productName()));
+ setPageListTitle(tr("Finished"));
m_msgLabel = new QLabel(this);
m_msgLabel->setWordWrap(true);
@@ -2839,7 +3029,7 @@ FinishedPage::FinishedPage(PackageManagerCore *core)
*/
void FinishedPage::entering()
{
- m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.")
+ m_msgLabel->setText(tr("Click %1 to exit the %2 Setup.")
.arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&')))
.arg(productName()));
@@ -2886,7 +3076,8 @@ void FinishedPage::entering()
connect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked);
}
- if (packageManagerCore()->status() == PackageManagerCore::Success) {
+ if (packageManagerCore()->status() == PackageManagerCore::Success
+ || packageManagerCore()->status() == PackageManagerCore::EssentialUpdated) {
const QString finishedText = packageManagerCore()->value(QLatin1String("FinishedText"));
if (!finishedText.isEmpty())
m_msgLabel->setText(finishedText);
@@ -2900,7 +3091,7 @@ void FinishedPage::entering()
}
} else {
// TODO: how to handle this using the config.xml
- setColoredTitle(tr("The %1 Wizard failed.").arg(productName()));
+ setColoredTitle(tr("The %1 Setup failed.").arg(productName()));
}
m_runItCheckBox->hide();
@@ -2931,16 +3122,8 @@ void FinishedPage::leaving()
*/
void FinishedPage::handleFinishClicked()
{
- const QString program =
- packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram));
-
- const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore()
- ->values(scRunProgramArguments));
- if (!m_runItCheckBox->isChecked() || program.isEmpty())
- return;
-
- qDebug() << "starting" << program << args;
- QProcess::startDetached(program, args);
+ if (m_runItCheckBox->isChecked())
+ packageManagerCore()->runProgram();
}
/*!
@@ -2973,7 +3156,7 @@ void FinishedPage::cleanupChangedConnects()
*/
/*!
- \fn RestartPage::restart()
+ \fn QInstaller::RestartPage::restart()
This signal is emitted when the installer is restarted.
*/
@@ -2985,8 +3168,10 @@ RestartPage::RestartPage(PackageManagerCore *core)
: PackageManagerPage(core)
{
setObjectName(QLatin1String("RestartPage"));
- setColoredTitle(tr("Completing the %1 Setup Wizard").arg(productName()));
+ setColoredTitle(tr("Finished the %1 Setup").arg(productName()));
+ // Never show this page on the page list
+ setShowOnPageList(false);
setFinalPage(false);
}
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index b233086c3..d83643005 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -80,9 +80,12 @@ public:
QString defaultButtonText(int wizardButton) const;
void clickButton(int wizardButton, int delayInMs = 0);
+ void clickButton(const QString &objectName, int delayInMs = 0) const;
bool isButtonEnabled(int wizardButton);
+ void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText);
void showSettingsButton(bool show);
+ void requestSettingsButtonByInstaller(bool request);
void setSettingsButtonEnabled(bool enable);
void updateButtonLayout();
@@ -99,19 +102,22 @@ Q_SIGNALS:
void finishButtonClicked();
void gotRestarted();
void settingsButtonClicked();
+ void aboutApplicationClicked();
public Q_SLOTS:
void cancelButtonClicked();
- void reject();
+ void reject() override;
void rejectWithoutPrompt();
void showFinishedPage();
void setModified(bool value);
void setMaxSize();
+ void updatePageListWidget();
protected Q_SLOTS:
void wizardPageInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
void wizardPageRemovalRequested(QWidget *widget);
- void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page);
+ void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page,
+ int position);
void wizardWidgetRemovalRequested(QWidget *widget);
void wizardPageVisibilityChangeRequested(bool visible, int page);
void setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name,
@@ -126,8 +132,8 @@ private Q_SLOTS:
void currentPageChanged(int newId);
protected:
- bool event(QEvent *event);
- void showEvent(QShowEvent *event);
+ bool event(QEvent *event) override;
+ void showEvent(QShowEvent *event) override;
PackageManagerCore *packageManagerCore() const { return m_core; }
void executeControlScript(int pageId);
@@ -135,6 +141,7 @@ private:
class Private;
Private *const d;
PackageManagerCore *m_core;
+ QListWidget *m_pageListWidget;
};
@@ -148,15 +155,19 @@ public:
explicit PackageManagerPage(PackageManagerCore *core);
virtual ~PackageManagerPage() {}
- virtual QPixmap logoPixmap() const;
virtual QString productName() const;
- virtual QPixmap watermarkPixmap() const;
- virtual QPixmap bannerPixmap() const;
+ virtual QPixmap wizardPixmap(const QString &pixmapType) const;
void setColoredTitle(const QString &title);
void setColoredSubTitle(const QString &subTitle);
- virtual bool isComplete() const;
+ void setPageListTitle(const QString &title);
+ QString pageListTitle() const;
+
+ void setShowOnPageList(bool show);
+ bool showOnPageList() const;
+
+ virtual bool isComplete() const override;
void setComplete(bool complete);
virtual bool isInterruptible() const { return false; }
@@ -164,14 +175,16 @@ public:
void setValidatePageComponent(QInstaller::Component *component);
- bool validatePage();
+ bool validatePage() override;
bool settingsButtonRequested() const { return m_needsSettingsButton; }
void setSettingsButtonRequested(bool request) { m_needsSettingsButton = request; }
+ void removeCustomWidget(const QWidget *widget);
signals:
void entered();
void left();
+ void showOnPageListChanged();
protected:
PackageManagerCore *packageManagerCore() const;
@@ -181,7 +194,7 @@ protected:
virtual void insertWidget(QWidget *widget, const QString &siblingName, int offset = 1);
virtual QWidget *findWidget(const QString &objectName) const;
- virtual int nextId() const; // reimp
+ virtual int nextId() const override;
// Used to support some kind of initializePage() in the case the wizard has been set
// to QWizard::IndependentPages. If that option has been set, initializePage() would be only
@@ -192,10 +205,13 @@ protected:
private:
bool m_complete;
QString m_titleColor;
+ QString m_pageListTitle;
+ bool m_showOnPageList;
bool m_needsSettingsButton;
PackageManagerCore *m_core;
QInstaller::Component *validatorComponent;
+ QMultiMap<int, QWidget*> m_customWidgets;
friend class PackageManagerGui;
};
@@ -212,14 +228,17 @@ public:
void setText(const QString &text);
- int nextId() const;
- bool validatePage();
+ int nextId() const override;
+ bool validatePage() override;
void showAll();
void hideAll();
void showMetaInfoUpdate();
void showMaintenanceTools();
void setMaintenanceToolsEnabled(bool enable);
+ void setMaintainerToolsEnabled(bool enable);
+
+ void resetFetchedState();
public Q_SLOTS:
void onCoreNetworkSettingsChanged();
@@ -237,15 +256,18 @@ private Q_SLOTS:
void setPackageManager(bool value);
private:
- void entering();
- void leaving();
+ void initializePage() override;
+
+ void entering() override;
+ void leaving() override;
void showWidgets(bool show);
- bool validRepositoriesAvailable() const;
private:
bool m_updatesFetched;
bool m_allPackagesFetched;
+ bool m_forceUpdate;
+ bool m_offlineMaintenanceTool;
QLabel *m_label;
QLabel *m_msgLabel;
@@ -271,26 +293,23 @@ class INSTALLER_EXPORT LicenseAgreementPage : public PackageManagerPage
public:
explicit LicenseAgreementPage(PackageManagerCore *core);
- void entering();
- bool isComplete() const;
+ void entering() override;
+ bool isComplete() const override;
private Q_SLOTS:
void openLicenseUrl(const QUrl &url);
void currentItemChanged(QListWidgetItem *current);
private:
- void addLicenseItem(const QHash<QString, QPair<QString, QString> > &hash);
+ void createLicenseWidgets();
void updateUi();
private:
QTextBrowser *m_textBrowser;
QListWidget *m_licenseListWidget;
- QRadioButton *m_acceptRadioButton;
- QRadioButton *m_rejectRadioButton;
-
+ QCheckBox *m_acceptCheckBox;
QLabel *m_acceptLabel;
- QLabel *m_rejectLabel;
};
@@ -304,20 +323,22 @@ public:
explicit ComponentSelectionPage(PackageManagerCore *core);
~ComponentSelectionPage();
- bool isComplete() const;
+ bool isComplete() const override;
Q_INVOKABLE void selectAll();
Q_INVOKABLE void deselectAll();
Q_INVOKABLE void selectDefault();
Q_INVOKABLE void selectComponent(const QString &id);
Q_INVOKABLE void deselectComponent(const QString &id);
- Q_INVOKABLE void allowCompressedRepositoryInstall();
Q_INVOKABLE bool addVirtualComponentToUninstall(const QString &name);
+ void setAllowCreateOfflineInstaller(bool allow);
+
protected:
- void entering();
- void leaving();
- void showEvent(QShowEvent *event);
+ void entering() override;
+ void leaving() override;
+ void showEvent(QShowEvent *event) override;
+ bool validatePage() override;
private Q_SLOTS:
void setModified(bool modified);
@@ -339,21 +360,19 @@ public:
QString targetDir() const;
void setTargetDir(const QString &dirName);
- void initializePage();
- bool validatePage();
- bool isComplete() const;
+ void initializePage() override;
+ bool validatePage() override;
+ bool isComplete() const override;
protected:
- void entering();
- void leaving();
+ void entering() override;
+ void leaving() override;
private Q_SLOTS:
void dirRequested();
private:
QString targetDirWarning() const;
- bool askQuestion(const QString &identifier, const QString &message);
- bool failWithError(const QString &identifier, const QString &message);
private:
QLineEdit *m_lineEdit;
@@ -375,7 +394,7 @@ public:
void setStartMenuDir(const QString &startMenuDir);
protected:
- void leaving();
+ void leaving() override;
private Q_SLOTS:
void currentItemChanged(QListWidgetItem* current);
@@ -397,8 +416,11 @@ public:
explicit ReadyForInstallationPage(PackageManagerCore *core);
protected:
- void entering();
- void leaving();
+ void entering() override;
+ void leaving() override;
+
+private Q_SLOTS:
+ void updatePageListTitle();
private:
QLabel *m_msgLabel;
@@ -418,12 +440,13 @@ public:
bool isAutoSwitching() const;
protected:
- void entering();
- void leaving();
- bool isInterruptible() const { return true; }
+ void entering() override;
+ void leaving() override;
+ bool isInterruptible() const override { return true; }
public Q_SLOTS:
void setTitleMessage(const QString& title);
+ void changeCurrentImage();
Q_SIGNALS:
void setAutomatedPageSwitchEnabled(bool request);
@@ -436,9 +459,12 @@ private Q_SLOTS:
void uninstallationFinished();
void toggleDetailsWereChanged();
+ void updatePageListTitle();
private:
PerformInstallationForm *m_performInstallationForm;
+ QTimer m_imageChangeTimer;
+ QString m_currentImage;
};
@@ -456,8 +482,8 @@ public Q_SLOTS:
void cleanupChangedConnects();
protected:
- void entering();
- void leaving();
+ void entering() override;
+ void leaving() override;
private:
QLabel *m_msgLabel;
@@ -475,11 +501,11 @@ class INSTALLER_EXPORT RestartPage : public PackageManagerPage
public:
explicit RestartPage(PackageManagerCore *core);
- virtual int nextId() const;
+ virtual int nextId() const override;
protected:
- void entering();
- void leaving();
+ void entering() override;
+ void leaving() override;
Q_SIGNALS:
void restart();
diff --git a/src/libs/installer/packagemanagerpagefactory.cpp b/src/libs/installer/packagemanagerpagefactory.cpp
index 808dec263..ba6faac84 100644
--- a/src/libs/installer/packagemanagerpagefactory.cpp
+++ b/src/libs/installer/packagemanagerpagefactory.cpp
@@ -30,6 +30,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::PackageManagerPageFactory
+ \internal
+*/
+
PackageManagerPageFactory &PackageManagerPageFactory::instance()
{
static PackageManagerPageFactory factory;
diff --git a/src/libs/installer/packagemanagerproxyfactory.cpp b/src/libs/installer/packagemanagerproxyfactory.cpp
index 93135aa1f..1ccbf8af1 100644
--- a/src/libs/installer/packagemanagerproxyfactory.cpp
+++ b/src/libs/installer/packagemanagerproxyfactory.cpp
@@ -35,6 +35,18 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::PackageManagerProxyFactory
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ProxyCredential
+ \internal
+*/
+
PackageManagerProxyFactory::PackageManagerProxyFactory(const PackageManagerCore *const core)
: m_core(core)
{
diff --git a/src/libs/installer/packagemanagerproxyfactory.h b/src/libs/installer/packagemanagerproxyfactory.h
index 31f1ffeba..bbb30aed8 100644
--- a/src/libs/installer/packagemanagerproxyfactory.h
+++ b/src/libs/installer/packagemanagerproxyfactory.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -47,8 +47,8 @@ class PackageManagerProxyFactory : public KDUpdater::FileDownloaderProxyFactory
public:
explicit PackageManagerProxyFactory(const PackageManagerCore *const core);
- PackageManagerProxyFactory *clone() const;
- QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery());
+ PackageManagerProxyFactory *clone() const override;
+ QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query = QNetworkProxyQuery()) override;
void setProxyCredentials(const QNetworkProxy &proxy, const QString &user, const QString &password);
diff --git a/src/libs/installer/packagesource.cpp b/src/libs/installer/packagesource.cpp
index f60c5c8e1..0f87e0def 100644
--- a/src/libs/installer/packagesource.cpp
+++ b/src/libs/installer/packagesource.cpp
@@ -1,32 +1,26 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
@@ -49,16 +43,16 @@ namespace QInstaller {
*/
/*!
- \fn PackageSource::PackageSource()
+ \fn QInstaller::PackageSource::PackageSource()
Constructs an empty package source info object. The object's priority is set to -1. The url is
initialized using a \l{default-constructed value}.
*/
/*!
- \fn PackageSource::PackageSource(const QUrl &u, int p)
+ \fn QInstaller::PackageSource::PackageSource(const QUrl &u, int p, bool pl)
- Constructs an package source info object. The object's url is set to \a u, while the priority
+ Constructs a package source info object. The object's url is set to \a u, while the priority
is set to \a p.
*/
@@ -75,7 +69,7 @@ namespace QInstaller {
/*!
Returns the hash value for the \a key, using \a seed to seed the calculation.
*/
-uint qHash(const PackageSource &key, uint seed)
+hashValue qHash(const PackageSource &key, hashValue seed)
{
return qHash(key.url, seed) ^ key.priority;
}
diff --git a/src/libs/installer/packagesource.h b/src/libs/installer/packagesource.h
index 6bc565c15..f63b53cd8 100644
--- a/src/libs/installer/packagesource.h
+++ b/src/libs/installer/packagesource.h
@@ -1,32 +1,26 @@
/**************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
@@ -46,16 +40,18 @@ struct INSTALLER_EXPORT PackageSource
PackageSource()
: priority(-1)
{}
- PackageSource(const QUrl &u, int p)
+ PackageSource(const QUrl &u, int p, bool pl = false)
: url(u)
, priority(p)
+ , postLoadComponentScript(pl)
{}
QUrl url;
int priority;
+ bool postLoadComponentScript;
};
-INSTALLER_EXPORT uint qHash(const PackageSource &key, uint seed);
+INSTALLER_EXPORT hashValue qHash(const PackageSource &key, hashValue seed);
INSTALLER_EXPORT bool operator==(const PackageSource &lhs, const PackageSource &rhs);
} // namespace QInstaller
diff --git a/src/libs/installer/performinstallationform.cpp b/src/libs/installer/performinstallationform.cpp
index 28506bcde..0e4c561f6 100644
--- a/src/libs/installer/performinstallationform.cpp
+++ b/src/libs/installer/performinstallationform.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,8 +28,12 @@
#include "performinstallationform.h"
-#include "lazyplaintextedit.h"
#include "progresscoordinator.h"
+#include "globals.h"
+#include "aspectratiolabel.h"
+#include "packagemanagercore.h"
+#include "settings.h"
+#include "fileutils.h"
#include <QApplication>
#include <QLabel>
@@ -37,13 +41,19 @@
#include <QPushButton>
#include <QScrollBar>
#include <QVBoxLayout>
+#include <QImageReader>
+#include <QScrollArea>
+#include <QTextEdit>
+#include <QFileInfo>
#include <QtCore/QTimer>
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>
#endif
+#endif
using namespace QInstaller;
@@ -65,24 +75,30 @@ using namespace QInstaller;
*/
/*!
- \fn PerformInstallationForm::showDetailsChanged()
+ \fn QInstaller::PerformInstallationForm::showDetailsChanged()
This signal is emitted when the end users select the details button to show
or hide progress details.
*/
/*!
- Constructs the perform installation UI with \a parent as parent.
+ Constructs the perform installation UI with package manager specified by \a core and
+ with \a parent as parent.
*/
-PerformInstallationForm::PerformInstallationForm(QObject *parent)
+PerformInstallationForm::PerformInstallationForm(PackageManagerCore *core, QObject *parent)
: QObject(parent)
, m_progressBar(nullptr)
, m_progressLabel(nullptr)
+ , m_additionalProgressStatus(nullptr)
+ , m_productImagesScrollArea(nullptr)
+ , m_productImagesLabel(nullptr)
, m_detailsButton(nullptr)
, m_detailsBrowser(nullptr)
, m_updateTimer(nullptr)
+ , m_core(core)
{
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) {
m_taskButton = new QWinTaskbarButton(this);
m_taskButton->progress()->setVisible(true);
@@ -90,6 +106,7 @@ PerformInstallationForm::PerformInstallationForm(QObject *parent)
m_taskButton = nullptr;
}
#endif
+#endif
}
/*!
@@ -113,12 +130,14 @@ void PerformInstallationForm::setupUi(QWidget *widget)
m_progressLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
topLayout->addWidget(m_progressLabel);
- m_downloadStatus = new QLabel(widget);
- m_downloadStatus->setObjectName(QLatin1String("DownloadStatus"));
- m_downloadStatus->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
- topLayout->addWidget(m_downloadStatus);
- connect(ProgressCoordinator::instance(), &ProgressCoordinator::downloadStatusChanged, this,
- &PerformInstallationForm::onDownloadStatusChanged);
+ m_additionalProgressStatus = new QLabel(widget);
+ m_additionalProgressStatus->setObjectName(QLatin1String("DownloadStatus"));
+ m_additionalProgressStatus->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
+ m_additionalProgressStatus->setWordWrap(true);
+ m_additionalProgressStatus->setTextFormat(Qt::TextFormat::RichText);
+ topLayout->addWidget(m_additionalProgressStatus);
+ connect(ProgressCoordinator::instance(), &ProgressCoordinator::additionalProgressStatusChanged, this,
+ &PerformInstallationForm::onAdditionalProgressStatusChanged);
m_detailsButton = new QPushButton(tr("&Show Details"), widget);
m_detailsButton->setObjectName(QLatin1String("DetailsButton"));
@@ -130,7 +149,19 @@ void PerformInstallationForm::setupUi(QWidget *widget)
bottomLayout->setObjectName(QLatin1String("BottomLayout"));
bottomLayout->addStretch();
- m_detailsBrowser = new LazyPlainTextEdit(widget);
+ m_productImagesScrollArea = new QScrollArea(widget);
+ m_productImagesScrollArea->setObjectName(QLatin1String("ProductImagesScrollArea"));
+ m_productImagesScrollArea->setWidgetResizable(true);
+ m_productImagesScrollArea->setFrameShape(QFrame::NoFrame);
+ m_productImagesScrollArea->setStyleSheet(QLatin1String("background-color:transparent;"));
+
+ m_productImagesLabel = new AspectRatioLabel(widget);
+ m_productImagesLabel->setObjectName(QLatin1String("ProductImagesLabel"));
+
+ m_productImagesScrollArea->setWidget(m_productImagesLabel);
+ bottomLayout->addWidget(m_productImagesScrollArea);
+
+ m_detailsBrowser = new QTextEdit(widget);
m_detailsBrowser->setReadOnly(true);
m_detailsBrowser->setWordWrapMode(QTextOption::NoWrap);
m_detailsBrowser->setObjectName(QLatin1String("DetailsBrowser"));
@@ -138,6 +169,7 @@ void PerformInstallationForm::setupUi(QWidget *widget)
bottomLayout->addWidget(m_detailsBrowser);
bottomLayout->setStretch(1, 10);
+ bottomLayout->setStretch(2, 10);
baseLayout->addLayout(topLayout);
baseLayout->addLayout(bottomLayout);
@@ -176,12 +208,14 @@ void PerformInstallationForm::updateProgress()
m_progressBar->setValue(progressPercentage);
#ifdef Q_OS_WIN
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
if (m_taskButton) {
if (!m_taskButton->window() && QApplication::activeWindow())
m_taskButton->setWindow(QApplication::activeWindow()->windowHandle());
m_taskButton->progress()->setValue(progressPercentage);
}
#endif
+#endif
static QString lastLabelText;
if (lastLabelText == progressCoordninator->labelText())
@@ -200,6 +234,7 @@ void PerformInstallationForm::toggleDetails()
const bool willShow = !isShowingDetails();
m_detailsButton->setText(willShow ? tr("&Hide Details") : tr("&Show Details"));
m_detailsBrowser->setVisible(willShow);
+ m_productImagesScrollArea->setVisible(!willShow);
emit showDetailsChanged();
}
@@ -220,6 +255,7 @@ void PerformInstallationForm::enableDetails()
m_detailsButton->setEnabled(true);
m_detailsButton->setText(tr("&Show Details"));
m_detailsBrowser->setVisible(false);
+ m_productImagesScrollArea->setVisible(true);
}
/*!
@@ -249,14 +285,6 @@ void PerformInstallationForm::setDetailsButtonEnabled(bool enable)
}
/*!
- Scrolls to the bottom of the details browser.
-*/
-void PerformInstallationForm::scrollDetailsToTheEnd()
-{
- m_detailsBrowser->updateCursor(LazyPlainTextEdit::TextCursorPosition::ForceEnd);
-}
-
-/*!
Returns \c true if the details browser is visible.
*/
bool PerformInstallationForm::isShowingDetails() const
@@ -268,8 +296,33 @@ bool PerformInstallationForm::isShowingDetails() const
Changes the label text according to the changes in the download status
specified by \a status.
*/
-void PerformInstallationForm::onDownloadStatusChanged(const QString &status)
+void PerformInstallationForm::onAdditionalProgressStatusChanged(const QString &status)
{
- m_downloadStatus->setText(m_downloadStatus->fontMetrics().elidedText(status, Qt::ElideRight,
- m_downloadStatus->width()));
+ m_additionalProgressStatus->setText(status);
+}
+
+/*!
+ Sets currently shown form image specified by \a fileName. When clicking the image,
+ \a url is opened in a browser. If the \a url is a reference to a file, it will
+ be opened with a suitable application instead of a Web browser. \a url can be empty.
+*/
+void PerformInstallationForm::setImageFromFileName(const QString &fileName, const QString &url)
+{
+ QString imagePath = QFileInfo(fileName).isAbsolute()
+ ? fileName
+ : m_core->settings().value(QLatin1String("Prefix")).toString() + QLatin1Char('/') + fileName;
+ QInstaller::replaceHighDpiImage(imagePath);
+
+ if (!QFile::exists(imagePath)) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Image file does not exist:" << fileName;
+ return;
+ }
+ QImageReader reader(imagePath);
+ QPixmap pixmap = QPixmap::fromImageReader(&reader);
+ if (!pixmap.isNull()) {
+ m_productImagesLabel->setPixmapAndUrl(pixmap, url);
+ } else {
+ qCWarning(QInstaller::lcInstallerInstallLog) <<
+ QString::fromLatin1("Failed to load image '%1' : %2.").arg(imagePath, reader.errorString());
+ }
}
diff --git a/src/libs/installer/performinstallationform.h b/src/libs/installer/performinstallationform.h
index 954b8e09b..d5f87eeaa 100644
--- a/src/libs/installer/performinstallationform.h
+++ b/src/libs/installer/performinstallationform.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -38,18 +38,22 @@ class QPushButton;
class QTimer;
class QWidget;
class QWinTaskbarButton;
+class QScrollArea;
+class QTextEdit;
QT_END_NAMESPACE
-class LazyPlainTextEdit;
namespace QInstaller {
+class AspectRatioLabel;
+class PackageManagerCore;
+
class PerformInstallationForm : public QObject
{
Q_OBJECT
public:
- explicit PerformInstallationForm(QObject *parent);
+ explicit PerformInstallationForm(PackageManagerCore *core, QObject *parent);
void setupUi(QWidget *widget);
void setDetailsWidgetVisible(bool visible);
@@ -57,7 +61,6 @@ public:
void startUpdateProgress();
void stopUpdateProgress();
void setDetailsButtonEnabled(bool enable);
- void scrollDetailsToTheEnd();
bool isShowingDetails() const;
signals:
@@ -68,15 +71,19 @@ public slots:
void updateProgress();
void toggleDetails();
void clearDetailsBrowser();
- void onDownloadStatusChanged(const QString &status);
+ void onAdditionalProgressStatusChanged(const QString &status);
+ void setImageFromFileName(const QString &fileName, const QString &url);
private:
QProgressBar *m_progressBar;
QLabel *m_progressLabel;
- QLabel *m_downloadStatus;
+ QLabel *m_additionalProgressStatus;
+ QScrollArea *m_productImagesScrollArea;
+ AspectRatioLabel *m_productImagesLabel;
QPushButton *m_detailsButton;
- LazyPlainTextEdit *m_detailsBrowser;
+ QTextEdit *m_detailsBrowser;
QTimer *m_updateTimer;
+ PackageManagerCore *m_core;
#ifdef Q_OS_WIN
QWinTaskbarButton *m_taskButton;
diff --git a/src/libs/installer/permissionsettings.cpp b/src/libs/installer/permissionsettings.cpp
index cc0dfc7ab..d70cf5625 100644
--- a/src/libs/installer/permissionsettings.cpp
+++ b/src/libs/installer/permissionsettings.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::PermissionSettings
+ \internal
+*/
+
PermissionSettings::~PermissionSettings()
{
if (!fileName().isEmpty()) {
diff --git a/src/libs/installer/permissionsettings.h b/src/libs/installer/permissionsettings.h
index 3847c91db..c950d9c17 100644
--- a/src/libs/installer/permissionsettings.h
+++ b/src/libs/installer/permissionsettings.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,14 +39,17 @@ public:
explicit PermissionSettings(const QString &organization,
const QString &application = QString(), QObject *parent = 0)
: QSettings(organization, application, parent) {}
+
PermissionSettings(Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0)
: QSettings(scope, organization, application, parent) {}
+
PermissionSettings(Format format, Scope scope, const QString &organization,
- const QString &application = QString(), QObject *parent = 0)
- : QSettings(format, scope, organization, application, parent) {}
+ const QString &application = QString(), QObject *parent = 0)
+ : QSettings(format, scope, organization, application, parent) {}
PermissionSettings(const QString &fileName, Format format, QObject *parent = 0)
- : QSettings(fileName, format, parent) {}
+ : QSettings(fileName, format, parent) {}
+
~PermissionSettings();
};
diff --git a/src/libs/installer/productkeycheck.cpp b/src/libs/installer/productkeycheck.cpp
index c1dfe83d6..ed128fa61 100644
--- a/src/libs/installer/productkeycheck.cpp
+++ b/src/libs/installer/productkeycheck.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#include "productkeycheck.h"
#include "packagemanagercore.h"
+#include <QtUiTools/QUiLoader>
+
class ProductKeyCheckPrivate
{
};
@@ -49,6 +51,12 @@ ProductKeyCheck *ProductKeyCheck::instance()
return &instance;
}
+QUiLoader *ProductKeyCheck::uiLoader()
+{
+ static QUiLoader loader;
+ return &loader;
+}
+
void ProductKeyCheck::init(QInstaller::PackageManagerCore *core)
{
Q_UNUSED(core)
@@ -106,3 +114,22 @@ bool ProductKeyCheck::hasValidLicense() const
{
return true;
}
+
+bool ProductKeyCheck::hasAcceptedAllLicenses() const
+{
+ return true;
+}
+
+QString ProductKeyCheck::licenseAcceptanceText() const
+{
+ return QString();
+}
+QString ProductKeyCheck::securityWarning() const
+{
+ return QString();
+}
+
+QString ProductKeyCheck::additionalMetaDownloadWarning() const
+{
+ return QString();
+}
diff --git a/src/libs/installer/productkeycheck.h b/src/libs/installer/productkeycheck.h
index b7e8c6d52..8e7d6724f 100644
--- a/src/libs/installer/productkeycheck.h
+++ b/src/libs/installer/productkeycheck.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,7 @@
#include "installer_global.h"
#include <QString>
+#include <QUiLoader>
namespace QInstaller {
@@ -49,6 +50,8 @@ public:
static ProductKeyCheck *instance();
void init(QInstaller::PackageManagerCore *core);
+ static QUiLoader *uiLoader();
+
// was validLicense
bool hasValidKey();
QString lastErrorString();
@@ -67,6 +70,10 @@ public:
QList<int> registeredPages() const;
bool hasValidLicense() const;
+ bool hasAcceptedAllLicenses() const;
+ QString licenseAcceptanceText() const;
+ QString securityWarning() const;
+ QString additionalMetaDownloadWarning() const;
private:
ProductKeyCheck();
diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp
index d3fbd764f..6413efe28 100644
--- a/src/libs/installer/progresscoordinator.cpp
+++ b/src/libs/installer/progresscoordinator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,13 +28,19 @@
#include "progresscoordinator.h"
+#include <iostream>
+
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
+#include "globals.h"
+#include "utils.h"
+#include "loggingutils.h"
+
using namespace QInstaller;
QT_BEGIN_NAMESPACE
-uint qHash(QPointer<QObject> key)
+hashValue qHash(QPointer<QObject> key)
{
return qHash(key.data());
}
@@ -51,10 +57,13 @@ ProgressCoordinator::ProgressCoordinator(QObject *parent)
{
// it has to be in the main thread to be able refresh the ui with processEvents
Q_ASSERT(thread() == qApp->thread());
+ m_progressSpinner = new ProgressSpinner();
}
ProgressCoordinator::~ProgressCoordinator()
{
+ delete m_progressSpinner;
+ m_progressSpinner = nullptr;
}
ProgressCoordinator *ProgressCoordinator::instance()
@@ -67,7 +76,8 @@ ProgressCoordinator *ProgressCoordinator::instance()
void ProgressCoordinator::reset()
{
- disconnectAllSenders();
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
m_installationLabelText.clear();
m_currentCompletePercentage = 0;
m_currentBasePercentage = 0;
@@ -81,10 +91,11 @@ void ProgressCoordinator::reset()
void ProgressCoordinator::registerPartProgress(QObject *sender, const char *signal, double partProgressSize)
{
Q_ASSERT(sender);
+ Q_ASSERT(!sender->objectName().isEmpty());
Q_ASSERT(QString::fromLatin1(signal).contains(QLatin1String("(double)")));
Q_ASSERT(partProgressSize <= 1);
- m_senderPartProgressSizeHash.insert(sender, partProgressSize);
+ m_senderPartProgressSizeHash.insert(sender->objectName(), partProgressSize);
bool isConnected = connect(sender, signal, this, SLOT(partProgressChanged(double)));
Q_UNUSED(isConnected);
Q_ASSERT(isConnected);
@@ -92,7 +103,7 @@ void ProgressCoordinator::registerPartProgress(QObject *sender, const char *sign
/*!
- This slot gets the progress changed signals from different tasks. The values 0 and 1 are handled as
+ This slot gets the progress changed signals from different tasks. The \a fraction values 0 and 1 are handled as
special values.
0 - is just ignored, so you can use a timer which gives the progress, e.g. like a downloader does.
@@ -101,57 +112,63 @@ void ProgressCoordinator::registerPartProgress(QObject *sender, const char *sign
void ProgressCoordinator::partProgressChanged(double fraction)
{
if (fraction < 0 || fraction > 1) {
- qWarning() << "The fraction is outside from possible value:" << fraction;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "The fraction is outside from possible value:"
+ << fraction;
return;
}
// no fraction no change
- if (fraction == 0)
+ if (fraction == 0 || !sender())
return;
+ QString senderObjectName = sender()->objectName();
// ignore senders sending 100% multiple times
- if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender())
- && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) {
+ if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(senderObjectName)
+ && m_senderPendingCalculatedPercentageHash.value(senderObjectName) == 0) {
return;
}
- double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0);
+ double partProgressSize = m_senderPartProgressSizeHash.value(senderObjectName, 0);
if (partProgressSize == 0) {
- qWarning() << "It seems that this sender was not registered in the right way:" << sender();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "It seems that this sender was not registered "
+ "in the right way:" << sender();
return;
}
if (m_undoMode) {
- //qDebug() << "fraction:" << fraction;
double maxSize = m_reachedPercentageBeforeUndo * partProgressSize;
double pendingCalculatedPartPercentage = maxSize * fraction;
// allPendingCalculatedPartPercentages has negative values
double newCurrentCompletePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage
- + allPendingCalculatedPartPercentages(sender());
+ + allPendingCalculatedPartPercentages(senderObjectName);
//we can't check this here, because some round issues can make it little bit under 0 or over 100
//Q_ASSERT(newCurrentCompletePercentage >= 0);
//Q_ASSERT(newCurrentCompletePercentage <= 100);
if (newCurrentCompletePercentage < 0) {
- qDebug() << newCurrentCompletePercentage << "is smaller than 0 - this should not happen more than once";
+ qCDebug(QInstaller::lcDeveloperBuild) << newCurrentCompletePercentage << "is smaller than 0 "
+ "- this should not happen more than once";
newCurrentCompletePercentage = 0;
}
if (newCurrentCompletePercentage > 100) {
- qDebug() << newCurrentCompletePercentage << "is bigger than 100 - this should not happen more than once";
+ qCDebug(QInstaller::lcDeveloperBuild) << newCurrentCompletePercentage << "is bigger than 100 "
+ "- this should not happen more than once";
newCurrentCompletePercentage = 100;
}
// In undo mode, the progress has to go backward, new has to be smaller than current
- if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage))
- qDebug("Something is wrong with the calculation of the progress.");
+ if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage)) {
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Something is wrong with the calculation "
+ "of the progress.";
+ }
m_currentCompletePercentage = newCurrentCompletePercentage;
if (fraction == 1) {
m_currentBasePercentage = m_currentBasePercentage - pendingCalculatedPartPercentage;
- m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0);
} else {
- m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage);
}
} else { //if (m_undoMode)
@@ -160,34 +177,37 @@ void ProgressCoordinator::partProgressChanged(double fraction)
//double checkValue = allPendingCalculatedPartPercentages(sender());
double newCurrentCompletePercentage = m_manualAddedPercentage + m_currentBasePercentage
- + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(sender());
+ + pendingCalculatedPartPercentage + allPendingCalculatedPartPercentages(senderObjectName);
//we can't check this here, because some round issues can make it little bit under 0 or over 100
//Q_ASSERT(newCurrentCompletePercentage >= 0);
//Q_ASSERT(newCurrentCompletePercentage <= 100);
if (newCurrentCompletePercentage < 0) {
- qDebug() << newCurrentCompletePercentage << "is smaller than 0 - this should not happen more than once";
+ qCDebug(QInstaller::lcDeveloperBuild) << newCurrentCompletePercentage << "is smaller than 0 "
+ "- this should not happen more than once";
newCurrentCompletePercentage = 0;
}
if (newCurrentCompletePercentage > 100) {
- qDebug() << newCurrentCompletePercentage << "is bigger than 100 - this should not happen more than once";
+ qCDebug(QInstaller::lcDeveloperBuild) << newCurrentCompletePercentage << "is bigger than 100 "
+ "- this should not happen more than once";
newCurrentCompletePercentage = 100;
}
// In normal mode, the progress has to go forward, new has to be larger than current
if (qRound(m_currentCompletePercentage) > qRound(newCurrentCompletePercentage))
- qDebug("Something is wrong with the calculation of the progress.");
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Something is wrong with the calculation of the progress.";
m_currentCompletePercentage = newCurrentCompletePercentage;
if (fraction == 1) {
m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage;
- m_senderPendingCalculatedPercentageHash.insert(sender(), 0);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, 0);
} else {
- m_senderPendingCalculatedPercentageHash.insert(sender(), pendingCalculatedPartPercentage);
+ m_senderPendingCalculatedPercentageHash.insert(senderObjectName, pendingCalculatedPartPercentage);
}
} //if (m_undoMode)
+ printProgressPercentage(progressInPercentage());
}
@@ -202,25 +222,13 @@ int ProgressCoordinator::progressInPercentage() const
return currentValue;
}
-void ProgressCoordinator::disconnectAllSenders()
-{
- foreach (QPointer<QObject> sender, m_senderPartProgressSizeHash.keys()) {
- if (!sender.isNull()) {
- bool isDisconnected = sender->disconnect(this);
- Q_UNUSED(isDisconnected);
- Q_ASSERT(isDisconnected);
- }
- }
- m_senderPartProgressSizeHash.clear();
- m_senderPendingCalculatedPercentageHash.clear();
-}
-
void ProgressCoordinator::setUndoMode()
{
Q_ASSERT(!m_undoMode);
m_undoMode = true;
- disconnectAllSenders();
+ m_senderPartProgressSizeHash.clear();
+ m_senderPendingCalculatedPercentageHash.clear();
m_reachedPercentageBeforeUndo = progressInPercentage();
m_currentBasePercentage = m_reachedPercentageBeforeUndo;
}
@@ -250,6 +258,11 @@ void ProgressCoordinator::setLabelText(const QString &text)
if (m_installationLabelText == text)
return;
m_installationLabelText = text;
+
+ // Refresh both message & progress percentage on console
+ // when the label text is changed
+ printProgressMessage(text);
+ printProgressPercentage(progressInPercentage());
}
/*!
@@ -268,14 +281,14 @@ void ProgressCoordinator::emitDetailTextChanged(const QString &text)
void ProgressCoordinator::emitLabelAndDetailTextChanged(const QString &text)
{
emit detailTextChanged(text);
- m_installationLabelText = QString(text).remove(QLatin1String("\n"));
+ setLabelText(QString(text).remove(QLatin1String("\n")));
qApp->processEvents(); //makes the result available in the ui
}
-double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *excludeKeyObject)
+double ProgressCoordinator::allPendingCalculatedPartPercentages(const QString &excludeKeyObject)
{
double result = 0;
- QHash<QPointer<QObject>, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
+ QHash<QString, double>::iterator it = m_senderPendingCalculatedPercentageHash.begin();
while (it != m_senderPendingCalculatedPercentageHash.end()) {
if (it.key() != excludeKeyObject)
result += it.value();
@@ -284,7 +297,29 @@ double ProgressCoordinator::allPendingCalculatedPartPercentages(QObject *exclude
return result;
}
-void ProgressCoordinator::emitDownloadStatus(const QString &status)
+void ProgressCoordinator::emitAdditionalProgressStatus(const QString &status)
+{
+ emit additionalProgressStatusChanged(status);
+}
+
+void ProgressCoordinator::printProgressPercentage(int progress)
+{
+ if (!LoggingHandler::instance().isVerbose())
+ return;
+
+ Q_ASSERT(m_progressSpinner->currentIndex < m_progressSpinner->spinnerChars.size());
+
+ QString formatted = QString::fromLatin1("[%1 %2%]").arg(
+ m_progressSpinner->spinnerChars.at(m_progressSpinner->currentIndex), QString::number(progress));
+
+ qCDebug(QInstaller::lcProgressIndicator).noquote() << formatted;
+
+ m_progressSpinner->currentIndex == (m_progressSpinner->spinnerChars.size() - 1)
+ ? m_progressSpinner->currentIndex = 0
+ : m_progressSpinner->currentIndex++;
+}
+
+void ProgressCoordinator::printProgressMessage(const QString &message)
{
- emit downloadStatusChanged(status);
+ qCDebug(QInstaller::lcInstallerInstallLog).nospace().noquote() << message;
}
diff --git a/src/libs/installer/progresscoordinator.h b/src/libs/installer/progresscoordinator.h
index ec02b8296..75d5a5d30 100644
--- a/src/libs/installer/progresscoordinator.h
+++ b/src/libs/installer/progresscoordinator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -41,6 +41,15 @@ class INSTALLER_EXPORT ProgressCoordinator : public QObject
{
Q_OBJECT
+ struct ProgressSpinner {
+ ProgressSpinner()
+ : spinnerChars(QLatin1String("/-\\|"))
+ , currentIndex(0) {}
+
+ const QString spinnerChars;
+ quint8 currentIndex;
+ };
+
public:
static ProgressCoordinator *instance();
~ProgressCoordinator();
@@ -63,23 +72,26 @@ public slots:
void emitDetailTextChanged(const QString &text);
void emitLabelAndDetailTextChanged(const QString &text);
- void emitDownloadStatus(const QString &status);
+ void emitAdditionalProgressStatus(const QString &status);
+ void printProgressPercentage(int progress);
+ void printProgressMessage(const QString &message);
signals:
void detailTextChanged(const QString &text);
void detailTextResetNeeded();
- void downloadStatusChanged(const QString &status);
+ void additionalProgressStatusChanged(const QString &status);
protected:
explicit ProgressCoordinator(QObject *parent);
private:
- double allPendingCalculatedPartPercentages(QObject *excludeKeyObject = 0);
+ double allPendingCalculatedPartPercentages(const QString &excludeKeyObject = QString());
void disconnectAllSenders();
private:
- QHash<QPointer<QObject>, double> m_senderPendingCalculatedPercentageHash;
- QHash<QPointer<QObject>, double> m_senderPartProgressSizeHash;
+ QHash<QString, double> m_senderPendingCalculatedPercentageHash;
+ QHash<QString, double> m_senderPartProgressSizeHash;
+ ProgressSpinner *m_progressSpinner;
QString m_installationLabelText;
double m_currentCompletePercentage;
double m_currentBasePercentage;
diff --git a/src/libs/installer/protocol.cpp b/src/libs/installer/protocol.cpp
index d68af1722..fb16086e5 100644
--- a/src/libs/installer/protocol.cpp
+++ b/src/libs/installer/protocol.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,26 @@ namespace QInstaller {
typedef qint32 PackageSize;
/*!
+ \inmodule QtInstallerFramework
+ \namespace QInstaller::Protocol
+ \brief Contains values related to the internal client-server connection protocol.
+*/
+
+/*!
+ \enum QInstaller::Protocol::Mode
+
+ \value Debug
+ \value Production
+*/
+
+/*!
+ \enum QInstaller::Protocol::StartAs
+
+ \value User
+ \value SuperUser
+*/
+
+/*!
Write a packet containing \a command and \a data to \a device.
\note Both client and server need to have the same endianness.
diff --git a/src/libs/installer/protocol.h b/src/libs/installer/protocol.h
index 9ead7943c..8b2288a89 100644
--- a/src/libs/installer/protocol.h
+++ b/src/libs/installer/protocol.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -50,9 +50,9 @@ enum struct StartAs {
const char DefaultSocket[] = "ifw_srv";
const char DefaultAuthorizationKey[] = "DefaultAuthorizationKey";
+const char DefaultReply[] = "DefaultReply";
const char Create[] = "Create";
-const char Destroy[] = "Destroy";
const char Shutdown[] = "Shutdown";
const char Authorize[] = "Authorize";
const char Reply[] = "Reply";
@@ -67,6 +67,7 @@ const char QProcessReadAll[] = "QProcess::readAll";
const char QProcessReadAllStandardOutput[] = "QProcess::readAllStandardOutput";
const char QProcessReadAllStandardError[] = "QProcess::readAllStandardError";
const char QProcessStartDetached[] = "QProcess::startDetached";
+const char QProcessStartDetached2[] = "QProcess::startDetached2";
const char QProcessSetWorkingDirectory[] = "QProcess::setWorkingDirectory";
const char QProcessSetEnvironment[] = "QProcess::setEnvironment";
const char QProcessEnvironment[] = "QProcess::environment";
@@ -89,7 +90,7 @@ const char GetQProcessSignals[] = "GetQProcessSignals";
const char QProcessSignalBytesWritten[] = "QProcess::bytesWritten";
const char QProcessSignalAboutToClose[] = "QProcess::aboutToClose";
const char QProcessSignalReadChannelFinished[] = "QProcess::readChannelFinished";
-const char QProcessSignalError[] = "QProcess::error";
+const char QProcessSignalError[] = "QProcess::errorOccurred";
const char QProcessSignalReadyReadStandardOutput[] = "QProcess::readyReadStandardOutput";
const char QProcessSignalReadyReadStandardError[] = "QProcess::readyReadStandardError";
const char QProcessSignalStarted[] = "QProcess::started";
@@ -163,6 +164,31 @@ const char QAbstractFileEngineSyncToDisk[] = "QAbstractFileEngine::syncToDisk";
const char QAbstractFileEngineRenameOverwrite[] = "QAbstractFileEngine::renameOverwrite";
const char QAbstractFileEngineFileTime[] = "QAbstractFileEngine::fileTime";
+
+// LibArchiveWrapper
+const char AbstractArchive[] = "AbstractArchive";
+const char AbstractArchiveOpen[] = "AbstractArchive::open";
+const char AbstractArchiveClose[] = "AbstractArchive::close";
+const char AbstractArchiveSetFilename[] = "AbstractArchive::setFilename";
+const char AbstractArchiveErrorString[] = "AbstractArchive::errorString";
+const char AbstractArchiveExtract[] = "AbstractArchive::extract";
+const char AbstractArchiveCreate[] = "AbstractArchive::create";
+const char AbstractArchiveList[] = "AbstractArchive::list";
+const char AbstractArchiveIsSupported[] = "AbstractArchive::isSupported";
+const char AbstractArchiveSetCompressionLevel[] = "AbstractArchive::setCompressionLevel";
+const char AbstractArchiveAddDataBlock[] = "AbstractArchive::addDataBlock";
+const char AbstractArchiveSetClientDataAtEnd[] = "AbstractArchive::setClientDataAtEnd";
+const char AbstractArchiveSetFilePosition[] = "AbstractArchive::setFilePosition";
+const char AbstractArchiveWorkerStatus[] = "AbstractArchive::workerStatus";
+const char AbstractArchiveCancel[] = "AbstractArchive::cancel";
+
+const char GetAbstractArchiveSignals[] = "GetAbstractArchiveSignals";
+const char AbstractArchiveSignalCurrentEntryChanged[] = "AbstractArchive::currentEntryChanged";
+const char AbstractArchiveSignalCompletedChanged[] = "AbstractArchive::completedChanged";
+const char AbstractArchiveSignalDataBlockRequested[] = "AbstractArchive::dataBlockRequested";
+const char AbstractArchiveSignalSeekRequested[] = "AbstractArchive::seekRequested";
+const char AbstractArchiveSignalWorkerFinished[] = "AbstractArchive::workerFinished";
+
} // namespace Protocol
void INSTALLER_EXPORT sendPacket(QIODevice *device, const QByteArray &command, const QByteArray &data);
diff --git a/src/libs/installer/proxycredentialsdialog.cpp b/src/libs/installer/proxycredentialsdialog.cpp
index 471e6fe5b..2ac07b855 100644
--- a/src/libs/installer/proxycredentialsdialog.cpp
+++ b/src/libs/installer/proxycredentialsdialog.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,18 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \namespace QInstaller::Ui
+ \brief Groups user interface forms generated with Qt Designer.
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ProxyCredentialsDialog
+ \internal
+*/
+
ProxyCredentialsDialog::ProxyCredentialsDialog(const QNetworkProxy &proxy, QWidget *parent) :
QDialog(parent),
ui(new Ui::ProxyCredentialsDialog)
diff --git a/src/libs/installer/qinstallerglobal.cpp b/src/libs/installer/qinstallerglobal.cpp
new file mode 100644
index 000000000..457690892
--- /dev/null
+++ b/src/libs/installer/qinstallerglobal.cpp
@@ -0,0 +1,99 @@
+/**************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "qinstallerglobal.h"
+
+/*!
+ \enum QInstaller::JobError
+
+ \value InvalidUrl
+ \value Timeout
+ \value DownloadError
+ \value InvalidUpdatesXml
+ \value InvalidMetaInfo
+ \value ExtractionError
+ \value UserIgnoreError
+ \value RepositoryUpdatesReceived
+ \value CacheError
+*/
+
+/*!
+ \typedef QInstaller::Operation
+
+ Synonym for KDUpdater::UpdateOperation.
+*/
+
+/*!
+ \typedef QInstaller::OperationList
+
+ Synonym for QList<QInstaller::Operation*>.
+*/
+
+/*!
+ \typedef QInstaller::Package
+
+ Synonym for KDUpdater::Update.
+*/
+
+/*!
+ \typedef QInstaller::PackagesList
+
+ Synonym for QList<QInstaller::Package*>.
+*/
+
+/*!
+ \typedef QInstaller::LocalPackagesMap
+
+ Synonym for QMap<QString, KDUpdater::LocalPackage>. The map key is component name,
+ and value contains information about the local package.
+*/
+
+/*!
+ \typedef QInstaller::AutoDependencyHash
+
+ Synonym for QHash<QString, QStringList>. The hash key is component name,
+ that other components automatically depend on. The value can contain
+ several component names, which are installed as an automatic dependency.
+ For example:
+ \badcode
+ <Name>A</Name> //Hash value
+ <AutoDependOn>B</AutoDependOn> //Hash key
+ \endcode
+*/
+
+/*!
+ \typedef QInstaller::LocalDependencyHash
+
+ Synonym for QHash<QString, QStringList>. The hash key is component name,
+ which other components depend on. The value can contain several component
+ names, which depend on the key. For example:
+ \badcode
+ <Name>A</Name> //Hash value
+ <Dependencies>B</Dependencies> //Hash key
+ \endcode
+*/
diff --git a/src/libs/installer/qinstallerglobal.h b/src/libs/installer/qinstallerglobal.h
index 884044db9..a750e6583 100644
--- a/src/libs/installer/qinstallerglobal.h
+++ b/src/libs/installer/qinstallerglobal.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -46,7 +46,8 @@ enum INSTALLER_EXPORT JobError
InvalidMetaInfo,
ExtractionError,
UserIgnoreError,
- RepositoryUpdatesReceived
+ RepositoryUpdatesReceived,
+ CacheError
};
typedef KDUpdater::UpdateOperation Operation;
@@ -55,7 +56,10 @@ typedef QList<QInstaller::Operation*> OperationList;
typedef KDUpdater::Update Package;
typedef QList<QInstaller::Package*> PackagesList;
-typedef QHash<QString, KDUpdater::LocalPackage> LocalPackagesHash;
+typedef QMap<QString, KDUpdater::LocalPackage> LocalPackagesMap;
+
+typedef QHash<QString, QStringList> AutoDependencyHash;
+typedef QHash<QString, QStringList> LocalDependencyHash;
} // namespace QInstaller
diff --git a/src/libs/installer/qprocesswrapper.cpp b/src/libs/installer/qprocesswrapper.cpp
index 7cd5ad7d1..44117eefb 100644
--- a/src/libs/installer/qprocesswrapper.cpp
+++ b/src/libs/installer/qprocesswrapper.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,9 +32,16 @@
#include "utils.h"
#include <QDir>
+#include <QVariant>
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::QProcessWrapper
+ \internal
+*/
+
QProcessWrapper::QProcessWrapper(QObject *parent)
: RemoteObject(QLatin1String(Protocol::QProcess), parent)
{
@@ -47,7 +54,7 @@ QProcessWrapper::QProcessWrapper(QObject *parent)
connect(&process, &QIODevice::bytesWritten, this, &QProcessWrapper::bytesWritten);
connect(&process, &QIODevice::aboutToClose, this, &QProcessWrapper::aboutToClose);
connect(&process, &QIODevice::readChannelFinished, this, &QProcessWrapper::readChannelFinished);
- connect(&process, SIGNAL(error(QProcess::ProcessError)), SIGNAL(error(QProcess::ProcessError)));
+ connect(&process, SIGNAL(errorOccurred(QProcess::ProcessError)), SIGNAL(errorOccurred(QProcess::ProcessError)));
connect(&process, &QProcess::readyReadStandardOutput, this, &QProcessWrapper::readyReadStandardOutput);
connect(&process, &QProcess::readyReadStandardError, this, &QProcessWrapper::readyReadStandardError);
connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), SIGNAL(finished(int,QProcess::ExitStatus)));
@@ -81,7 +88,7 @@ void QProcessWrapper::processSignals()
} else if (name == QLatin1String(Protocol::QProcessSignalReadChannelFinished)) {
emit readChannelFinished();
} else if (name == QLatin1String(Protocol::QProcessSignalError)) {
- emit error(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt()));
+ emit errorOccurred(static_cast<QProcess::ProcessError> (receivedSignals.takeFirst().toInt()));
} else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardOutput)) {
emit readyReadStandardOutput();
} else if (name == QLatin1String(Protocol::QProcessSignalReadyReadStandardError)) {
@@ -101,6 +108,11 @@ void QProcessWrapper::processSignals()
m_lock.unlock();
}
+/*!
+ Starts the \a program with \a arguments in the working directory \a workingDirectory as a detached
+ process. The process id can be retrieved with the \a pid parameter. Compared to the QProcess
+ implementation of the same method this does not show a window for the started process on Windows.
+*/
bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory, qint64 *pid)
{
@@ -117,22 +129,70 @@ bool QProcessWrapper::startDetached(const QString &program, const QStringList &a
return QInstaller::startDetached(program, arguments, workingDirectory, pid);
}
+/*!
+ Starts the \a program with \a arguments as a detached process. Compared to the QProcess
+ implementation of the same method this does not show a window for the started process on Windows.
+*/
bool QProcessWrapper::startDetached(const QString &program, const QStringList &arguments)
{
return startDetached(program, arguments, QDir::currentPath());
}
+/*!
+ Starts the \a program as a detached process. Compared to the QProcess implementation of the same
+ method this does not show a window for the started process on Windows.
+*/
bool QProcessWrapper::startDetached(const QString &program)
{
return startDetached(program, QStringList());
}
+/*!
+ Starts the \a program as a detached process. The variants of the function suffixed with \c 2
+ use the base \c QProcess::startDetached implementation internally to start the process.
+*/
+bool QProcessWrapper::startDetached2(const QString &program)
+{
+ return startDetached2(program, QStringList());
+}
+
+/*!
+ Starts the \a program with \a arguments as a detached process. The variants of the function
+ suffixed with \c 2 use the base \c QProcess::startDetached implementation internally to
+ start the process.
+*/
+bool QProcessWrapper::startDetached2(const QString &program, const QStringList &arguments)
+{
+ return startDetached2(program, arguments, QDir::currentPath());
+}
+
+/*!
+ Starts the \a program with \a arguments in the working directory \a workingDirectory as a detached
+ process. The process id can be retrieved with the \a pid parameter. The variants
+ of the function suffixed with \c 2 use the base \c QProcess::startDetached implementation
+ internally to start the process.
+*/
+bool QProcessWrapper::startDetached2(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
+{
+ QProcessWrapper w;
+ if (w.connectToServer()) {
+ const QPair<bool, qint64> result =
+ w.callRemoteMethod<QPair<bool, qint64> >(QLatin1String(Protocol::QProcessStartDetached2),
+ program, arguments, workingDirectory);
+ if (pid != nullptr)
+ *pid = result.second;
+ w.processSignals();
+ return result.first;
+ }
+ return QProcess::startDetached(program, arguments, workingDirectory, pid);
+}
+
void QProcessWrapper::setProcessChannelMode(QProcessWrapper::ProcessChannelMode mode)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetProcessChannelMode),
- static_cast<QProcess::ProcessChannelMode>(mode), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetProcessChannelMode),
+ static_cast<QProcess::ProcessChannelMode>(mode));
m_lock.unlock();
} else {
process.setProcessChannelMode(static_cast<QProcess::ProcessChannelMode>(mode));
@@ -156,8 +216,8 @@ void QProcessWrapper::setReadChannel(QProcessWrapper::ProcessChannel chan)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetReadChannel),
- static_cast<QProcess::ProcessChannel>(chan), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetReadChannel),
+ static_cast<QProcess::ProcessChannel>(chan));
m_lock.unlock();
} else {
process.setReadChannel(static_cast<QProcess::ProcessChannel>(chan));
@@ -203,7 +263,7 @@ void QProcessWrapper::closeWriteChannel()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessCloseWriteChannel));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessCloseWriteChannel));
m_lock.unlock();
} else {
process.closeWriteChannel();
@@ -236,7 +296,7 @@ void QProcessWrapper::kill()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessKill));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessKill));
m_lock.unlock();
} else {
process.kill();
@@ -283,7 +343,7 @@ void QProcessWrapper::start(const QString &param1, const QStringList &param2,
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart3Arg), param1, param2, param3);
m_lock.unlock();
} else {
process.start(param1, param2, param3);
@@ -294,10 +354,10 @@ void QProcessWrapper::start(const QString &param1, QIODevice::OpenMode param2)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessStart2Arg), param1, param2);
m_lock.unlock();
} else {
- process.start(param1, param2);
+ process.start(param1, {}, param2);
}
}
@@ -316,7 +376,7 @@ void QProcessWrapper::terminate()
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessTerminate));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessTerminate));
m_lock.unlock();
} else {
process.terminate();
@@ -383,7 +443,7 @@ void QProcessWrapper::setEnvironment(const QStringList &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetEnvironment), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetEnvironment), param1);
m_lock.unlock();
} else {
process.setEnvironment(param1);
@@ -395,7 +455,7 @@ void QProcessWrapper::setNativeArguments(const QString &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetNativeArguments), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetNativeArguments), param1);
m_lock.unlock();
} else {
process.setNativeArguments(param1);
@@ -407,7 +467,7 @@ void QProcessWrapper::setWorkingDirectory(const QString &param1)
{
if (connectToServer()) {
m_lock.lockForWrite();
- callRemoteMethod(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QProcessSetWorkingDirectory), param1);
m_lock.unlock();
} else {
process.setWorkingDirectory(param1);
diff --git a/src/libs/installer/qprocesswrapper.h b/src/libs/installer/qprocesswrapper.h
index e76217eda..b34d6f82c 100644
--- a/src/libs/installer/qprocesswrapper.h
+++ b/src/libs/installer/qprocesswrapper.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -104,6 +104,11 @@ public:
static bool startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory, qint64 *pid = 0);
+ static bool startDetached2(const QString &program);
+ static bool startDetached2(const QString &program, const QStringList &arguments);
+ static bool startDetached2(const QString &program, const QStringList &arguments,
+ const QString &workingDirectory, qint64 *pid = 0);
+
QString errorString() const;
qint64 write(const QByteArray &byteArray);
#ifdef Q_OS_WIN
@@ -114,7 +119,7 @@ Q_SIGNALS:
void bytesWritten(qint64);
void aboutToClose();
void readChannelFinished();
- void error(QProcess::ProcessError);
+ void errorOccurred(QProcess::ProcessError);
void readyReadStandardOutput();
void readyReadStandardError();
void finished(int exitCode, QProcess::ExitStatus exitStatus);
diff --git a/src/libs/installer/qsettingswrapper.cpp b/src/libs/installer/qsettingswrapper.cpp
index 7bc2ffeb4..d322728f5 100644
--- a/src/libs/installer/qsettingswrapper.cpp
+++ b/src/libs/installer/qsettingswrapper.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -33,6 +33,11 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::QSettingsWrapper
+ \internal
+*/
// -- QSettingsWrapper::Private
@@ -69,9 +74,9 @@ public:
Private(const QString &fileName, QSettings::Format format)
: m_filename(fileName)
+ , m_format(format)
, settings(fileName, format)
{
- m_format = format;
m_scope = settings.scope();
m_application = settings.applicationName();
m_organization = settings.organizationName();
@@ -102,18 +107,18 @@ QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Scope scope, const QString
{
}
-QSettingsWrapper::QSettingsWrapper(QSettingsWrapper::Format format, QSettingsWrapper::Scope scope,
+QSettingsWrapper::QSettingsWrapper(QSettings::Format format, QSettingsWrapper::Scope scope,
const QString &organization, const QString &application, QObject *parent)
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
- , d(new Private(static_cast<QSettings::Format>(format), static_cast<QSettings::Scope> (scope),
+ , d(new Private(format, static_cast<QSettings::Scope> (scope),
organization, application))
{
}
-QSettingsWrapper::QSettingsWrapper(const QString &fileName, QSettingsWrapper::Format format,
+QSettingsWrapper::QSettingsWrapper(const QString &fileName, QSettings::Format format,
QObject *parent)
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
- , d(new Private(fileName, static_cast<QSettings::Format>(format)))
+ , d(new Private(fileName, format))
{
}
@@ -136,27 +141,39 @@ QString QSettingsWrapper::applicationName() const
return d->settings.applicationName();
}
-void QSettingsWrapper::beginGroup(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::beginGroup(const QString &prefix)
+#else
+ void QSettingsWrapper::beginGroup(QAnyStringView prefix)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsBeginGroup), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginGroup), prefix);
else
- d->settings.beginGroup(param1);
+ d->settings.beginGroup(prefix);
}
-int QSettingsWrapper::beginReadArray(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ int QSettingsWrapper::beginReadArray(const QString &prefix)
+#else
+ int QSettingsWrapper::beginReadArray(QAnyStringView prefix)
+#endif
{
if (createSocket())
- return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), param1);
- return d->settings.beginReadArray(param1);
+ return callRemoteMethod<qint32>(QLatin1String(Protocol::QSettingsBeginReadArray), prefix);
+ return d->settings.beginReadArray(prefix);
}
-void QSettingsWrapper::beginWriteArray(const QString &param1, int param2)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::beginWriteArray(const QString &prefix, int size)
+#else
+ void QSettingsWrapper::beginWriteArray(QAnyStringView prefix, int size)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsBeginWriteArray), param1, qint32(param2));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsBeginWriteArray), prefix, qint32(size));
else
- d->settings.beginWriteArray(param1, param2);
+ d->settings.beginWriteArray(prefix, size);
}
QStringList QSettingsWrapper::childGroups() const
@@ -176,21 +193,25 @@ QStringList QSettingsWrapper::childKeys() const
void QSettingsWrapper::clear()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsClear));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsClear));
else d->settings.clear();
}
-bool QSettingsWrapper::contains(const QString &param1) const
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ bool QSettingsWrapper::contains(const QString &key) const
+#else
+ bool QSettingsWrapper::contains(QAnyStringView key) const
+#endif
{
if (createSocket())
- return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), param1);
- return d->settings.contains(param1);
+ return callRemoteMethod<bool>(QLatin1String(Protocol::QSettingsContains), key);
+ return d->settings.contains(key);
}
void QSettingsWrapper::endArray()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsEndArray));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndArray));
else
d->settings.endArray();
}
@@ -198,7 +219,7 @@ void QSettingsWrapper::endArray()
void QSettingsWrapper::endGroup()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsEndGroup));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsEndGroup));
else
d->settings.endGroup();
}
@@ -217,10 +238,10 @@ QString QSettingsWrapper::fileName() const
return d->settings.fileName();
}
-QSettingsWrapper::Format QSettingsWrapper::format() const
+QSettings::Format QSettingsWrapper::format() const
{
// No need to talk to the server, we've setup the local settings object the same way.
- return static_cast<QSettingsWrapper::Format>(d->settings.format());
+ return d->settings.format();
}
QString QSettingsWrapper::group() const
@@ -244,12 +265,16 @@ QString QSettingsWrapper::organizationName() const
return d->settings.organizationName();
}
-void QSettingsWrapper::remove(const QString &param1)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+ void QSettingsWrapper::remove(const QString &key)
+#else
+ void QSettingsWrapper::remove(QAnyStringView key)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsRemove), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsRemove), key);
else
- d->settings.remove(param1);
+ d->settings.remove(key);
}
QSettingsWrapper::Scope QSettingsWrapper::scope() const
@@ -261,7 +286,7 @@ QSettingsWrapper::Scope QSettingsWrapper::scope() const
void QSettingsWrapper::setArrayIndex(int param1)
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1), dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetArrayIndex), qint32(param1));
else
d->settings.setArrayIndex(param1);
}
@@ -269,17 +294,20 @@ void QSettingsWrapper::setArrayIndex(int param1)
void QSettingsWrapper::setFallbacksEnabled(bool param1)
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1, dummy);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetFallbacksEnabled), param1);
else
d->settings.setFallbacksEnabled(param1);
}
-
-void QSettingsWrapper::setValue(const QString &param1, const QVariant &param2)
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+void QSettingsWrapper::setValue(const QString &key, const QVariant &value)
+#else
+void QSettingsWrapper::setValue(QAnyStringView key, const QVariant &value)
+#endif
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSetValue), param1, param2);
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSetValue), key, value);
else
- d->settings.setValue(param1, param2);
+ d->settings.setValue(key, value);
}
QSettingsWrapper::Status QSettingsWrapper::status() const
@@ -294,27 +322,36 @@ QSettingsWrapper::Status QSettingsWrapper::status() const
void QSettingsWrapper::sync()
{
if (createSocket())
- callRemoteMethod(QLatin1String(Protocol::QSettingsSync));
+ callRemoteMethodDefaultReply(QLatin1String(Protocol::QSettingsSync));
else
d->settings.sync();
}
-QVariant QSettingsWrapper::value(const QString &param1, const QVariant &param2) const
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
+QVariant QSettingsWrapper::value(const QString &key, const QVariant &value) const
+#else
+QVariant QSettingsWrapper::value(QAnyStringView key, const QVariant &value) const
+#endif
{
if (createSocket())
- return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), param1, param2);
- return d->settings.value(param1, param2);
+ return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key, value);
+ return d->settings.value(key, value);
}
+#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
+QVariant QSettingsWrapper::value(QAnyStringView key) const
+{
+ if (createSocket())
+ return callRemoteMethod<QVariant>(QLatin1String(Protocol::QSettingsValue), key);
+ return d->settings.value(key);
+}
+#endif
+
// -- private
bool QSettingsWrapper::createSocket() const
{
- if ((d->m_format != QSettings::NativeFormat) && (d->m_format != QSettings::IniFormat)) {
- Q_ASSERT_X(false, Q_FUNC_INFO, "Settings wrapper only supports QSettingsWrapper::NativeFormat"
- " and QSettingsWrapper::IniFormat.");
- }
return (const_cast<QSettingsWrapper *>(this))->connectToServer(QVariantList()
<< d->m_application << d->m_organization << d->m_scope << d->m_format << d->m_filename);
}
diff --git a/src/libs/installer/qsettingswrapper.h b/src/libs/installer/qsettingswrapper.h
index c9e6d6265..f5d428b1e 100644
--- a/src/libs/installer/qsettingswrapper.h
+++ b/src/libs/installer/qsettingswrapper.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,10 @@
#include <QVariant>
+#include <QSettings>
+
+QT_FORWARD_DECLARE_CLASS(QTextCodec)
+
namespace QInstaller {
class INSTALLER_EXPORT QSettingsWrapper : public RemoteObject
@@ -48,12 +52,6 @@ public:
FormatError
};
- enum Format {
- NativeFormat,
- IniFormat,
- InvalidFormat = 16
- };
-
enum Scope {
UserScope,
SystemScope
@@ -63,21 +61,30 @@ public:
const QString &application = QString(), QObject *parent = 0);
QSettingsWrapper(Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0);
- QSettingsWrapper(Format format, Scope scope, const QString &organization,
+ QSettingsWrapper(QSettings::Format format, Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0);
- QSettingsWrapper(const QString &fileName, Format format, QObject *parent = 0);
+ QSettingsWrapper(const QString &fileName, QSettings::Format format, QObject *parent = 0);
~QSettingsWrapper();
void clear();
void sync();
Status status() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void beginGroup(const QString &prefix);
+#else
+ void beginGroup(QAnyStringView prefix);
+#endif
void endGroup();
QString group() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
int beginReadArray(const QString &prefix);
void beginWriteArray(const QString &prefix, int size = -1);
+#else
+ int beginReadArray(QAnyStringView prefix);
+ void beginWriteArray(QAnyStringView prefix, int size = -1);
+#endif
void endArray();
void setArrayIndex(int i);
@@ -86,17 +93,27 @@ public:
QStringList childGroups() const;
bool isWritable() const;
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void setValue(const QString &key, const QVariant &value);
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+#else
+ void setValue(QAnyStringView key, const QVariant &value);
+ QVariant value(QAnyStringView key, const QVariant &defaultValue) const;
+ QVariant value(QAnyStringView key) const;
+#endif
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void remove(const QString &key);
bool contains(const QString &key) const;
-
+#else
+ void remove(QAnyStringView key);
+ bool contains(QAnyStringView key) const;
+#endif
void setFallbacksEnabled(bool b);
bool fallbacksEnabled() const;
QString fileName() const;
- Format format() const;
+ QSettings::Format format() const;
Scope scope() const;
QString organizationName() const;
QString applicationName() const;
@@ -109,28 +126,27 @@ private: // we cannot support the following functionality
: RemoteObject(QLatin1String(Protocol::QSettings), parent)
{}
+#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
void setIniCodec(QTextCodec * /*codec*/);
void setIniCodec(const char * /*codecName*/);
QTextCodec *iniCodec() const { return 0; }
-
- static void setDefaultFormat(Format /*format*/);
- static Format defaultFormat() { return NativeFormat; }
- static void setSystemIniPath(const QString & /*dir*/);
- static void setUserIniPath(const QString & /*dir*/);
- static void setPath(Format /*format*/, Scope /*scope*/, const QString & /*path*/);
+#endif
+ static void setDefaultFormat(QSettings::Format /*format*/);
+ static QSettings::Format defaultFormat() { return QSettings::NativeFormat; }
+ static void setPath(QSettings::Format /*format*/, Scope /*scope*/, const QString & /*path*/);
typedef QMap<QString, QVariant> SettingsMap;
typedef bool(*ReadFunc)(QIODevice &device, SettingsMap &map);
typedef bool(*WriteFunc)(QIODevice &device, const SettingsMap &map);
- static Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc,
+ static QSettings::Format registerFormat(const QString &extension, ReadFunc readFunc, WriteFunc writeFunc,
Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive)
{
Q_UNUSED(extension)
Q_UNUSED(readFunc)
Q_UNUSED(writeFunc)
Q_UNUSED(caseSensitivity)
- return NativeFormat;
+ return QSettings::NativeFormat;
}
private:
diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp
index 23ac372fd..a29e99f94 100644
--- a/src/libs/installer/qtpatch.cpp
+++ b/src/libs/installer/qtpatch.cpp
@@ -28,6 +28,7 @@
#include "qtpatch.h"
#include "utils.h"
+#include "globals.h"
#include <QString>
#include <QStringList>
@@ -66,11 +67,11 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
QFileInfo qmake(qmakePath);
if (!qmake.exists()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not existing").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
if (!qmake.isExecutable()) {
- qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath));
+ qmakeOutput->append(QString::fromLatin1("%1 is not executable").arg(qmakePath).toUtf8());
return qmakeValueHash;
}
@@ -81,14 +82,16 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
if (process.waitForFinished(10000)) {
QByteArray output = process.readAllStandardOutput();
- qmakeOutput->append(output);
- if (process.exitStatus() == QProcess::CrashExit) {
- qWarning() << qmake.absoluteFilePath() << args
- << "crashed with exit code" << process.exitCode()
- << "standard output:" << output
- << "error output:" << process.readAllStandardError();
+ if ((process.exitStatus() == QProcess::CrashExit) || (process.exitCode() != EXIT_SUCCESS)) {
+ QStringList detailedOutput = { qmake.absoluteFilePath() + QLatin1Char(' ') + args.join(QLatin1Char(' '))
+ + QString::fromLatin1(" returned with exit code: \"%1\".").arg(QString::number(process.exitCode()))
+ , QString::fromLatin1("Standard output: \"%1\".").arg(QLatin1String(output))
+ , QString::fromLatin1("Error output: \"%1\".").arg(QLatin1String(process.readAllStandardError()))
+ };
+ qmakeOutput->append(detailedOutput.join(QLatin1Char('\n')).toUtf8());
return qmakeValueHash;
}
+ qmakeOutput->append(output);
qmakeValueHash = readQmakeOutput(output);
}
if (qmakeValueHash.isEmpty()) {
@@ -97,13 +100,13 @@ QHash<QString, QByteArray> QtPatch::qmakeValues(const QString &qmakePath, QByteA
QInstaller::uiDetachedWait(waitTimeInMilliSeconds);
}
if (process.state() > QProcess::NotRunning ) {
- qDebug() << "qmake process is still running, need to kill it.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "qmake process is still running, need to kill it.";
process.kill();
}
}
if (qmakeValueHash.isEmpty())
- qDebug() << "Cannot get any query output from qmake.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot get any query output from qmake.";
return qmakeValueHash;
}
@@ -113,14 +116,15 @@ bool QtPatch::patchBinaryFile(const QString &fileName,
{
QFile file(fileName);
if (!file.exists()) {
- qDebug() << "qpatch: warning: file" << fileName << "not found";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "qpatch: warning: file" << fileName << "not found";
return false;
}
openFileForPatching(&file);
if (!file.isOpen()) {
- qDebug() << "qpatch: warning: file" << qPrintable(fileName) << "cannot open.";
- qDebug().noquote() << file.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "qpatch: warning: file" << qPrintable(fileName)
+ << "cannot open.";
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << file.errorString();
return false;
}
@@ -136,7 +140,8 @@ bool QtPatch::patchBinaryFile(QIODevice *device,
const QByteArray &newQtPath)
{
if (!(device->openMode() == QIODevice::ReadWrite)) {
- qDebug() << "qpatch: warning: This function needs an open device for writing.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "qpatch: warning: This function needs an "
+ "open device for writing.";
return false;
}
const QByteArray source = device->readAll();
@@ -168,7 +173,8 @@ bool QtPatch::patchTextFile(const QString &fileName,
QFile file(fileName);
if (!file.open(QFile::ReadOnly)) {
- qDebug() << "Cannot open file" << fileName << "for patching:" << file.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot open file" << fileName << "for patching:"
+ << file.errorString();
return false;
}
@@ -182,7 +188,7 @@ bool QtPatch::patchTextFile(const QString &fileName,
}
if (!file.open(QFile::WriteOnly | QFile::Truncate)) {
- qDebug() << "File" << fileName << "not writable.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "File" << fileName << "not writable.";
return false;
}
@@ -202,6 +208,7 @@ bool QtPatch::openFileForPatching(QFile *file)
}
return file->openMode() == QFile::ReadWrite;
}
- qDebug() << "File" << file->fileName() << "is open, so it cannot be opened again.";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "File" << file->fileName() << "is open, "
+ "so it cannot be opened again.";
return false;
}
diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp
index 27794652f..852714dfb 100644
--- a/src/libs/installer/registerfiletypeoperation.cpp
+++ b/src/libs/installer/registerfiletypeoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RegisterFileTypeOperation
+ \internal
+*/
+
#ifdef Q_OS_WIN
#include <shlobj.h>
@@ -91,7 +97,7 @@ bool RegisterFileTypeOperation::performOperation()
ensureOptionalArgumentsRead();
if (!checkArgumentCount(2, 5, QString::fromLatin1("<extension> <command> [description [contentType [icon]]]")))
return false;
- QStringList args = arguments();
+ QStringList args = parsePerformOperationArguments();
bool allUsers = false;
PackageManagerCore *const core = packageManager();
@@ -99,7 +105,7 @@ bool RegisterFileTypeOperation::performOperation()
allUsers = true;
QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER")
- , QSettingsWrapper::NativeFormat);
+ , QSettings::NativeFormat);
const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId;
const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0));
@@ -147,8 +153,10 @@ bool RegisterFileTypeOperation::undoOperation()
{
#ifdef Q_OS_WIN
ensureOptionalArgumentsRead();
- QStringList args = arguments();
+ if (skipUndoOperation())
+ return true;
+ QStringList args = arguments();
if (!checkArgumentCount(2, 5, tr("Register File Type: Invalid arguments")))
return false;
@@ -158,7 +166,7 @@ bool RegisterFileTypeOperation::undoOperation()
allUsers = true;
QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER")
- , QSettingsWrapper::NativeFormat);
+ , QSettings::NativeFormat);
const QString classesProgId = QString::fromLatin1("Software/Classes/") + m_progId;
const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(args.at(0));
diff --git a/src/libs/installer/registerfiletypeoperation.h b/src/libs/installer/registerfiletypeoperation.h
index 8850ab44b..c8fd454fe 100644
--- a/src/libs/installer/registerfiletypeoperation.h
+++ b/src/libs/installer/registerfiletypeoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,10 +40,10 @@ class INSTALLER_EXPORT RegisterFileTypeOperation : public QObject, public Operat
public:
explicit RegisterFileTypeOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
private:
void ensureOptionalArgumentsRead();
diff --git a/src/libs/installer/remoteclient.cpp b/src/libs/installer/remoteclient.cpp
index ad1e5ecf6..54e174602 100644
--- a/src/libs/installer/remoteclient.cpp
+++ b/src/libs/installer/remoteclient.cpp
@@ -33,6 +33,18 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteClient
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteClientPrivate
+ \internal
+*/
+
RemoteClient *RemoteClient::s_instance = nullptr;
RemoteClient::RemoteClient()
diff --git a/src/libs/installer/remoteclient_p.h b/src/libs/installer/remoteclient_p.h
index ed1731342..3dec97c95 100644
--- a/src/libs/installer/remoteclient_p.h
+++ b/src/libs/installer/remoteclient_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -36,10 +36,12 @@
#include "remoteclient.h"
#include "remoteobject.h"
#include "utils.h"
+#include "constants.h"
#include <QCoreApplication>
-#include <QElapsedTimer>
-#include <QMutex>
+#include <QDeadlineTimer>
+#include <QRecursiveMutex>
+#include <QTimer>
#include <QThread>
namespace QInstaller {
@@ -53,7 +55,6 @@ public:
RemoteClientPrivate(RemoteClient *parent)
: RemoteObject(QLatin1String("RemoteClientPrivate"))
, q_ptr(parent)
- , m_mutex(QMutex::Recursive)
, m_startServerAs(Protocol::StartAs::User)
, m_serverStarted(false)
, m_active(false)
@@ -85,7 +86,7 @@ public:
if (mode == Protocol::Mode::Production) {
m_startServerAs = startAs;
m_serverCommand = QCoreApplication::applicationFilePath();
- m_serverArguments = QStringList() << QLatin1String("--startserver")
+ m_serverArguments = QStringList() << QLatin1String("--") + CommandLineOptions::scStartServerLong
<< QString::fromLatin1("%1,%2,%3")
.arg(QLatin1String(Protocol::ModeProduction))
.arg(socketName)
@@ -98,7 +99,7 @@ public:
m_thread.start();
} else if (mode == Protocol::Mode::Debug) {
// To be able to debug the client-server connection start and stop the server manually,
- // e.g. installer --startserver DEBUG.
+ // e.g. installer --start-server DEBUG.
}
}
@@ -132,9 +133,9 @@ public:
QCoreApplication::translate("RemoteClient", "Cannot get authorization."),
QCoreApplication::translate("RemoteClient",
"Cannot get authorization that is needed for continuing the installation.\n\n"
- "Please start the setup program as a user with the appropriate rights.\n"
- "Or accept the elevation of access rights if being asked."),
- QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Retry);
+ "Please start the setup program as a user with the appropriate rights,\n"
+ "or accept the elevation of access rights if being asked."),
+ QMessageBox::Abort | QMessageBox::Retry, QMessageBox::Abort);
if (res == QMessageBox::Retry)
started = AdminAuthorization::execute(0, m_serverCommand, m_serverArguments);
}
@@ -164,11 +165,19 @@ public:
}
if (started) {
- QElapsedTimer t;
- t.start();
+ QTimer timer;
+ QEventLoop loop;
// 30 seconds waiting ought to be enough for the app to start
- while ((!m_serverStarted) && (t.elapsed() < 30000))
+ QDeadlineTimer deadline(30000);
+
+ connect(&timer, &QTimer::timeout, [&]() {
m_serverStarted = authorize();
+ if (m_serverStarted || deadline.hasExpired())
+ loop.quit();
+ });
+
+ timer.start(100);
+ loop.exec();
}
}
@@ -191,7 +200,7 @@ public:
private:
RemoteClient *q_ptr;
- QMutex m_mutex;
+ QRecursiveMutex m_mutex;
QString m_socketName;
Protocol::StartAs m_startServerAs;
bool m_serverStarted;
diff --git a/src/libs/installer/remotefileengine.cpp b/src/libs/installer/remotefileengine.cpp
index 3c54d1e29..2ead83861 100644
--- a/src/libs/installer/remotefileengine.cpp
+++ b/src/libs/installer/remotefileengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,10 +31,15 @@
#include "protocol.h"
#include "remoteclient.h"
-#include <QRegExp>
+#include <QRegularExpression>
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteFileEngineHandler
+ \internal
+*/
// -- RemoteFileEngineHandler
@@ -43,17 +48,17 @@ QAbstractFileEngine* RemoteFileEngineHandler::create(const QString &fileName) co
if (!RemoteClient::instance().isActive())
return 0;
- static QRegExp re(QLatin1String("^[a-z0-9]*://.*$"));
- if (re.exactMatch(fileName)) // stuff like installer://
+ static const QRegularExpression re(QLatin1String("^[a-z0-9]*://.*$"));
+ if (re.match(fileName).hasMatch()) // stuff like installer://
return 0;
if (fileName.isEmpty() || fileName.startsWith(QLatin1String(":")))
return 0; // empty filename or Qt resource
- QScopedPointer<RemoteFileEngine> client(new RemoteFileEngine());
+ std::unique_ptr<RemoteFileEngine> client(new RemoteFileEngine());
client->setFileName(fileName);
if (client->isConnectedToServer())
- return client.take();
+ return client.release();
return 0;
}
@@ -80,7 +85,7 @@ private:
QStringList entries;
};
-/*!
+/*
Advances the iterator to the next entry, and returns the current file path of this new
entry. If hasNext() returns \c false, the function does nothing and returns an empty QString.
*/
@@ -89,7 +94,7 @@ bool RemoteFileEngineIterator::hasNext() const
return index < entries.size() - 1;
}
-/*!
+/*
Returns \c true if there is at least one more entry in the directory, otherwise returns
\c false.
*/
@@ -101,7 +106,7 @@ QString RemoteFileEngineIterator::next()
return currentFilePath();
}
-/*!
+/*
Returns the name of the current directory entry, excluding the path.
*/
QString RemoteFileEngineIterator::currentFileName() const
@@ -110,8 +115,11 @@ QString RemoteFileEngineIterator::currentFileName() const
}
-// -- RemoteFileEngine
-
+/*!
+ \class QInstaller::RemoteFileEngine
+ \inmodule QtInstallerFramework
+ \internal
+*/
RemoteFileEngine::RemoteFileEngine()
: RemoteObject(QLatin1String(Protocol::QAbstractFileEngine))
{
@@ -122,7 +130,6 @@ RemoteFileEngine::~RemoteFileEngine()
}
/*!
- \reimp
*/
bool RemoteFileEngine::atEnd() const
{
@@ -197,7 +204,6 @@ QStringList RemoteFileEngine::entryList(QDir::Filters filters, const QStringList
}
/*!
- \reimp
*/
QFile::FileError RemoteFileEngine::error() const
{
@@ -209,7 +215,6 @@ QFile::FileError RemoteFileEngine::error() const
}
/*!
- \reimp
*/
QString RemoteFileEngine::errorString() const
{
@@ -308,6 +313,7 @@ bool RemoteFileEngine::link(const QString &newName)
/*!
\reimp
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
{
if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
@@ -315,18 +321,38 @@ bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectorie
dirName, createParentDirectories);
}
return m_fileEngine.mkdir(dirName, createParentDirectories);
+
+}
+#else
+bool RemoteFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions) const
+{
+ if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
+ return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineMkdir),
+ dirName, createParentDirectories);
+ }
+ return m_fileEngine.mkdir(dirName, createParentDirectories, permissions);
}
+#endif
/*!
\reimp
*/
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
bool RemoteFileEngine::open(QIODevice::OpenMode mode)
+#else
+bool RemoteFileEngine::open(QIODevice::OpenMode mode, std::optional<QFile::Permissions> permissions)
+#endif
{
if (connectToServer()) {
return callRemoteMethod<bool>(QString::fromLatin1(Protocol::QAbstractFileEngineOpen),
static_cast<qint32>(mode | QIODevice::Unbuffered));
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
return m_fileEngine.open(mode | QIODevice::Unbuffered);
+#else
+ return m_fileEngine.open(mode | QIODevice::Unbuffered, permissions);
+#endif
}
/*!
@@ -413,8 +439,7 @@ bool RemoteFileEngine::seek(qint64 offset)
void RemoteFileEngine::setFileName(const QString &fileName)
{
if (connectToServer()) {
- callRemoteMethod(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName,
- dummy);
+ callRemoteMethodDefaultReply(QString::fromLatin1(Protocol::QAbstractFileEngineSetFileName), fileName);
}
m_fileEngine.setFileName(fileName);
}
@@ -529,7 +554,11 @@ bool RemoteFileEngine::renameOverwrite(const QString &newName)
return m_fileEngine.renameOverwrite(newName);
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
QDateTime RemoteFileEngine::fileTime(FileTime time) const
+#else
+QDateTime RemoteFileEngine::fileTime(QFile::FileTime time) const
+#endif
{
if ((const_cast<RemoteFileEngine *>(this))->connectToServer()) {
return callRemoteMethod<QDateTime>
diff --git a/src/libs/installer/remotefileengine.h b/src/libs/installer/remotefileengine.h
index 9544403f7..c46e861ca 100644
--- a/src/libs/installer/remotefileengine.h
+++ b/src/libs/installer/remotefileengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,7 +42,7 @@ class INSTALLER_EXPORT RemoteFileEngineHandler : public QAbstractFileEngineHandl
public:
RemoteFileEngineHandler() : QAbstractFileEngineHandler() {}
- QAbstractFileEngine* create(const QString &fileName) const Q_DECL_OVERRIDE;
+ QAbstractFileEngine* create(const QString &fileName) const override;
};
class RemoteFileEngine : public RemoteObject, public QAbstractFileEngine
@@ -53,50 +53,64 @@ public:
RemoteFileEngine();
~RemoteFileEngine();
- bool open(QIODevice::OpenMode mode) Q_DECL_OVERRIDE;
- bool close() Q_DECL_OVERRIDE;
- bool flush() Q_DECL_OVERRIDE;
- bool syncToDisk() Q_DECL_OVERRIDE;
- qint64 size() const Q_DECL_OVERRIDE;
- qint64 pos() const Q_DECL_OVERRIDE;
- bool seek(qint64 offset) Q_DECL_OVERRIDE;
- bool isSequential() const Q_DECL_OVERRIDE;
- bool remove() Q_DECL_OVERRIDE;
- bool copy(const QString &newName) Q_DECL_OVERRIDE;
- bool rename(const QString &newName) Q_DECL_OVERRIDE;
- bool renameOverwrite(const QString &newName) Q_DECL_OVERRIDE;
- bool link(const QString &newName) Q_DECL_OVERRIDE;
- bool mkdir(const QString &dirName, bool createParentDirectories) const Q_DECL_OVERRIDE;
- bool rmdir(const QString &dirName, bool recurseParentDirectories) const Q_DECL_OVERRIDE;
- bool setSize(qint64 size) Q_DECL_OVERRIDE;
- bool caseSensitive() const Q_DECL_OVERRIDE;
- bool isRelativePath() const Q_DECL_OVERRIDE;
- QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const Q_DECL_OVERRIDE;
- FileFlags fileFlags(FileFlags type = FileInfoAll) const Q_DECL_OVERRIDE;
- bool setPermissions(uint perms) Q_DECL_OVERRIDE;
- QString fileName(FileName file = DefaultName) const Q_DECL_OVERRIDE;
- uint ownerId(FileOwner owner) const Q_DECL_OVERRIDE;
- QString owner(FileOwner owner) const Q_DECL_OVERRIDE;
- QDateTime fileTime(FileTime time) const Q_DECL_OVERRIDE;
- void setFileName(const QString &fileName) Q_DECL_OVERRIDE;
- int handle() const Q_DECL_OVERRIDE;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ bool open(QIODevice::OpenMode mode) override;
+#else
+ bool open(QIODevice::OpenMode mode,
+ std::optional<QFile::Permissions> permissions = std::nullopt) override;
+#endif
+ bool close() override;
+ bool flush() override;
+ bool syncToDisk() override;
+ qint64 size() const override;
+ qint64 pos() const override;
+ bool seek(qint64 offset) override;
+ bool isSequential() const override;
+ bool remove() override;
+ bool copy(const QString &newName) override;
+ bool rename(const QString &newName) override;
+ bool renameOverwrite(const QString &newName) override;
+ bool link(const QString &newName) override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+#else
+ bool mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions = std::nullopt) const override;
+#endif
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
+ bool setSize(qint64 size) override;
+ bool caseSensitive() const override;
+ bool isRelativePath() const override;
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override;
+ FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
+ bool setPermissions(uint perms) override;
+ QString fileName(FileName file = DefaultName) const override;
+ uint ownerId(FileOwner owner) const override;
+ QString owner(FileOwner owner) const override;
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
+ QDateTime fileTime(FileTime time) const override;
+#else
+ QDateTime fileTime(QFile::FileTime time) const override;
+#endif
+ void setFileName(const QString &fileName) override;
+ int handle() const override;
bool atEnd() const;
uchar *map(qint64, qint64, QFile::MemoryMapFlags) { return 0; }
bool unmap(uchar *) { return true; }
- Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) Q_DECL_OVERRIDE;
- Iterator *endEntryList() Q_DECL_OVERRIDE;
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
+ Iterator *endEntryList() override;
- qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
- qint64 readLine(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
- qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE;
+ qint64 read(char *data, qint64 maxlen) override;
+ qint64 readLine(char *data, qint64 maxlen) override;
+ qint64 write(const char *data, qint64 len) override;
QFile::FileError error() const;
QString errorString() const;
bool extension(Extension extension, const ExtensionOption *option = 0,
- ExtensionReturn *output = 0) Q_DECL_OVERRIDE;
- bool supportsExtension(Extension extension) const Q_DECL_OVERRIDE;
+ ExtensionReturn *output = 0) override;
+ bool supportsExtension(Extension extension) const override;
private:
QFSFileEngine m_fileEngine;
diff --git a/src/libs/installer/remoteobject.cpp b/src/libs/installer/remoteobject.cpp
index c9c822299..b4dd0cbb7 100644
--- a/src/libs/installer/remoteobject.cpp
+++ b/src/libs/installer/remoteobject.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#include "protocol.h"
#include "remoteclient.h"
+#include "globals.h"
#include <QCoreApplication>
#include <QElapsedTimer>
@@ -37,9 +38,14 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteObject
+ \internal
+*/
+
RemoteObject::RemoteObject(const QString &wrappedType, QObject *parent)
: QObject(parent)
- , dummy(nullptr)
, m_type(wrappedType)
, m_socket(nullptr)
{
@@ -51,8 +57,22 @@ RemoteObject::~RemoteObject()
{
if (m_socket) {
if (QThread::currentThread() == m_socket->thread()) {
- if (m_type != QLatin1String("RemoteClientPrivate"))
- writeData(QLatin1String(Protocol::Destroy), m_type, dummy, dummy);
+ if ((m_type != QLatin1String("RemoteClientPrivate"))
+ && (m_socket->state() == QLocalSocket::ConnectedState)) {
+ while (m_socket->bytesToWrite()) {
+ // QAbstractSocket::waitForBytesWritten() may fail randomly on Windows, use
+ // an event loop and the bytesWritten() signal instead as the docs suggest.
+ QEventLoop loop;
+ connect(m_socket, &QLocalSocket::bytesWritten, &loop, &QEventLoop::quit);
+ loop.exec();
+ }
+ m_socket->disconnectFromServer();
+ if (!(m_socket->state() == QLocalSocket::UnconnectedState
+ || m_socket->waitForDisconnected())) {
+ qCWarning(lcServer) << "Error while disconnecting from remote server:"
+ << m_socket->error();
+ }
+ }
} else {
Q_ASSERT_X(false, Q_FUNC_INFO, "Socket running in a different Thread than this object.");
}
@@ -101,6 +121,11 @@ bool RemoteObject::connectToServer(const QVariantList &arguments)
sendPacket(m_socket, Protocol::Create, data);
m_socket->flush();
+ while (m_socket->bytesToWrite())
+ m_socket->waitForBytesWritten();
+
+ const QString reply = readData<QString>(QLatin1String(Protocol::Create));
+ Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
return true;
}
@@ -114,9 +139,4 @@ bool RemoteObject::isConnectedToServer() const
return false;
}
-void RemoteObject::callRemoteMethod(const QString &name)
-{
- writeData(name, dummy, dummy, dummy);
-}
-
} // namespace QInstaller
diff --git a/src/libs/installer/remoteobject.h b/src/libs/installer/remoteobject.h
index ecd86c984..ddd512588 100644
--- a/src/libs/installer/remoteobject.h
+++ b/src/libs/installer/remoteobject.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -35,8 +35,10 @@
#include <QCoreApplication>
#include <QDataStream>
-#include <QObject>
#include <QLocalSocket>
+#include <QObject>
+#include <QVariant>
+
namespace QInstaller {
@@ -50,45 +52,65 @@ public:
virtual ~RemoteObject() = 0;
bool isConnectedToServer() const;
- void callRemoteMethod(const QString &name);
- template<typename T1, typename T2>
- void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2)
+ template<typename... Args>
+ void callRemoteMethodDefaultReply(const QString &name, const Args&... args)
{
- writeData(name, arg, arg2, dummy);
+ const QString reply = sendReceivePacket<QString>(name, args...);
+ Q_ASSERT(reply == QLatin1String(Protocol::DefaultReply));
}
- template<typename T1, typename T2, typename T3>
- void callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 & arg3)
+ template<typename T, typename... Args>
+ T callRemoteMethod(const QString &name, const Args&... args) const
{
- writeData(name, arg, arg2, arg3);
+ return sendReceivePacket<T>(name, args...);
}
- template<typename T>
- T callRemoteMethod(const QString &name) const
+protected:
+ bool authorize();
+ bool connectToServer(const QVariantList &arguments = QVariantList());
+
+private:
+
+ template<typename T, typename... Args>
+ T sendReceivePacket(const QString &name, const Args&... args) const
{
- return callRemoteMethod<T>(name, dummy, dummy, dummy);
+ writeData(name, args...);
+ while (m_socket->bytesToWrite())
+ m_socket->waitForBytesWritten();
+
+ return readData<T>(name);
}
- template<typename T, typename T1>
- T callRemoteMethod(const QString &name, const T1 &arg) const
+ template <class T> int writeObject(QDataStream& out, const T& t) const
{
- return callRemoteMethod<T>(name, arg, dummy, dummy);
+ static_assert(!std::is_pointer<T>::value, "Pointer passed to remote server");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ out << t;
+#else
+ if constexpr (std::is_same<T, QAnyStringView>::value)
+ out << t.toString();
+ else
+ out << t;
+#endif
+
+ return 0;
}
- template<typename T, typename T1, typename T2>
- T callRemoteMethod(const QString &name, const T1 & arg, const T2 &arg2) const
+ template<typename... Args>
+ void writeData(const QString &name, const Args&... args) const
{
- return callRemoteMethod<T>(name, arg, arg2, dummy);
+ QByteArray data;
+ QDataStream out(&data, QIODevice::WriteOnly);
+
+ (void)std::initializer_list<int>{writeObject(out, args)...};
+ sendPacket(m_socket, name.toLatin1(), data);
+ m_socket->flush();
}
- template<typename T, typename T1, typename T2, typename T3>
- T callRemoteMethod(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
+ template<typename T>
+ T readData(const QString &name) const
{
- writeData(name, arg, arg2, arg3);
- while (m_socket->bytesToWrite())
- m_socket->waitForBytesWritten();
-
QByteArray command;
QByteArray data;
while (!receivePacket(m_socket, &command, &data)) {
@@ -110,46 +132,6 @@ public:
return result;
}
-protected:
- bool authorize();
- bool connectToServer(const QVariantList &arguments = QVariantList());
-
- // Use this structure to allow derived classes to manipulate the template
- // function signature of the callRemoteMethod templates, since most of the
- // generated functions will differ in return type rather given arguments.
- struct Dummy {}; Dummy *dummy;
-
-private:
- template<typename T> bool isValueType(T) const
- {
- return true;
- }
-
- template<typename T> bool isValueType(T *dummy) const
- {
- // Force compiler error while passing anything different then Dummy* to the function.
- // It really doesn't make sense to send any pointer over to the server, so bail early.
- Q_UNUSED(static_cast<Dummy*> (dummy))
- return false;
- }
-
- template<typename T1, typename T2, typename T3>
- void writeData(const QString &name, const T1 &arg, const T2 &arg2, const T3 &arg3) const
- {
- QByteArray data;
- QDataStream out(&data, QIODevice::WriteOnly);
-
- if (isValueType(arg))
- out << arg;
- if (isValueType(arg2))
- out << arg2;
- if (isValueType(arg3))
- out << arg3;
-
- sendPacket(m_socket, name.toLatin1(), data);
- m_socket->flush();
- }
-
private:
QString m_type;
QLocalSocket *m_socket;
diff --git a/src/libs/installer/remoteserver.cpp b/src/libs/installer/remoteserver.cpp
index 66cfefebb..7291f0c32 100644
--- a/src/libs/installer/remoteserver.cpp
+++ b/src/libs/installer/remoteserver.cpp
@@ -34,6 +34,18 @@
namespace QInstaller {
/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::LocalServer
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteServerPrivate
+ \internal
+*/
+
+/*!
Constructs an remote server object with \a parent.
*/
RemoteServer::RemoteServer(QObject *parent)
diff --git a/src/libs/installer/remoteserver_p.h b/src/libs/installer/remoteserver_p.h
index e39350f2f..0723c7be6 100644
--- a/src/libs/installer/remoteserver_p.h
+++ b/src/libs/installer/remoteserver_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -75,7 +75,7 @@ private slots:
}
private:
- void incomingConnection(quintptr socketDescriptor) Q_DECL_OVERRIDE {
+ void incomingConnection(quintptr socketDescriptor) override {
if (m_shutdown)
return;
diff --git a/src/libs/installer/remoteserverconnection.cpp b/src/libs/installer/remoteserverconnection.cpp
index b188e694e..55c4f48d9 100644
--- a/src/libs/installer/remoteserverconnection.cpp
+++ b/src/libs/installer/remoteserverconnection.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -33,6 +33,10 @@
#include "remoteserverconnection_p.h"
#include "utils.h"
#include "permissionsettings.h"
+#include "globals.h"
+#ifdef IFW_LIBARCHIVE
+#include "libarchivearchive.h"
+#endif
#include <QCoreApplication>
#include <QDataStream>
@@ -40,14 +44,77 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::QProcessSignalReceiver
+ \internal
+*/
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteServerReply
+ \internal
+ \brief The RemoteServerReply class is used for sending a reply to a local socket.
+
+ The class ensures exactly one reply is sent in the lifetime of an object
+ instantiated from this class. If the calling client has not explicitly sent
+ a reply, a default reply is automatically sent on destruction.
+*/
+
+/*!
+ Constructs reply object for \a socket.
+*/
+RemoteServerReply::RemoteServerReply(QLocalSocket *socket)
+ : m_socket(socket)
+ , m_sent(false)
+{}
+
+/*!
+ Destroys the object and sends the default reply, if
+ no other replies have been sent.
+*/
+RemoteServerReply::~RemoteServerReply()
+{
+ send(QString::fromLatin1(Protocol::DefaultReply));
+}
+
+/*!
+ \fn template <typename T> QInstaller::RemoteServerReply::send(const T &data)
+
+ Sends a reply packet with \a¸ data to the socket. If a reply
+ has been already sent, this function does not send another.
+*/
+template <typename T>
+void RemoteServerReply::send(const T &data)
+{
+ if (m_sent || m_socket->state() != QLocalSocket::ConnectedState)
+ return;
+
+ QByteArray result;
+ QDataStream returnStream(&result, QIODevice::WriteOnly);
+ returnStream << data;
+
+ sendPacket(m_socket, Protocol::Reply, result);
+ m_socket->flush();
+ m_sent = true;
+}
+
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::RemoteServerConnection
+ \internal
+*/
+
RemoteServerConnection::RemoteServerConnection(qintptr socketDescriptor, const QString &key,
QObject *parent)
: QThread(parent)
, m_socketDescriptor(socketDescriptor)
+ , m_authorizationKey(key)
, m_process(nullptr)
, m_engine(nullptr)
- , m_authorizationKey(key)
- , m_signalReceiver(nullptr)
+ , m_archive(nullptr)
+ , m_processSignalReceiver(nullptr)
+ , m_archiveSignalReceiver(nullptr)
{
setObjectName(QString::fromLatin1("RemoteServerConnection(%1)").arg(socketDescriptor));
}
@@ -76,6 +143,7 @@ void RemoteServerConnection::run()
if (!receivePacket(&socket, &cmd, &data)) {
socket.waitForReadyRead(250);
+ qApp->processEvents();
continue;
}
@@ -87,18 +155,18 @@ void RemoteServerConnection::run()
stream.setDevice(&buf);
StreamChecker streamChecker(&stream);
+ RemoteServerReply reply(&socket);
+
if (authorized && command == QLatin1String(Protocol::Shutdown)) {
authorized = false;
- sendData(&socket, true);
- socket.flush();
+ reply.send(true);
socket.close();
emit shutdownRequested();
return;
} else if (command == QLatin1String(Protocol::Authorize)) {
QString key;
stream >> key;
- sendData(&socket, (authorized = (key == m_authorizationKey)));
- socket.flush();
+ reply.send(authorized = (key == m_authorizationKey));
if (!authorized) {
socket.close();
return;
@@ -126,89 +194,80 @@ void RemoteServerConnection::run()
settings.reset(new PermissionSettings(fileName.toString(), QSettings::Format(format.toInt())));
}
} else if (type == QLatin1String(Protocol::QProcess)) {
- if (m_process)
- m_process->deleteLater();
- m_process = new QProcess;
- m_signalReceiver = new QProcessSignalReceiver(m_process);
+ m_process.reset(new QProcess);
+ m_processSignalReceiver = new QProcessSignalReceiver(m_process.get());
} else if (type == QLatin1String(Protocol::QAbstractFileEngine)) {
- if (m_engine)
- delete m_engine;
- m_engine = new QFSFileEngine;
+ m_engine.reset(new QFSFileEngine);
+ } else if (type == QLatin1String(Protocol::AbstractArchive)) {
+#ifdef IFW_LIBARCHIVE
+ m_archive.reset(new LibArchiveArchive);
+ m_archiveSignalReceiver = new AbstractArchiveSignalReceiver(
+ static_cast<LibArchiveArchive *>(m_archive.get()));
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
+ } else {
+ qCDebug(QInstaller::lcServer) << "Unknown type for Create command:" << type;
}
continue;
}
- if (command == QLatin1String(Protocol::Destroy)) {
- QString type;
- stream >> type;
- if (type == QLatin1String(Protocol::QSettings)) {
- settings.reset();
- } else if (type == QLatin1String(Protocol::QProcess)) {
- m_signalReceiver->m_receivedSignals.clear();
- m_process->deleteLater();
- m_process = nullptr;
- } else if (type == QLatin1String(Protocol::QAbstractFileEngine)) {
- delete m_engine;
- m_engine = nullptr;
- }
- return;
- }
-
if (command == QLatin1String(Protocol::GetQProcessSignals)) {
- if (m_signalReceiver) {
- QMutexLocker _(&m_signalReceiver->m_lock);
- sendData(&socket, m_signalReceiver->m_receivedSignals);
- socket.flush();
- m_signalReceiver->m_receivedSignals.clear();
+ if (m_processSignalReceiver) {
+ QMutexLocker _(&m_processSignalReceiver->m_lock);
+ reply.send(m_processSignalReceiver->m_receivedSignals);
+ m_processSignalReceiver->m_receivedSignals.clear();
+ }
+ continue;
+ } else if (command == QLatin1String(Protocol::GetAbstractArchiveSignals)) {
+#ifdef IFW_LIBARCHIVE
+ if (m_archiveSignalReceiver) {
+ QMutexLocker _(&m_archiveSignalReceiver->m_lock);
+ reply.send(m_archiveSignalReceiver->m_receivedSignals);
+ m_archiveSignalReceiver->m_receivedSignals.clear();
}
continue;
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
}
if (command.startsWith(QLatin1String(Protocol::QProcess))) {
- handleQProcess(&socket, command, stream);
+ handleQProcess(&reply, command, stream);
} else if (command.startsWith(QLatin1String(Protocol::QSettings))) {
- handleQSettings(&socket, command, stream, settings.data());
+ handleQSettings(&reply, command, stream, settings.data());
} else if (command.startsWith(QLatin1String(Protocol::QAbstractFileEngine))) {
- handleQFSFileEngine(&socket, command, stream);
+ handleQFSFileEngine(&reply, command, stream);
+ } else if (command.startsWith(QLatin1String(Protocol::AbstractArchive))) {
+ handleArchive(&reply, command, stream);
} else {
- qDebug() << "Unknown command:" << command;
+ qCDebug(QInstaller::lcServer) << "Unknown command:" << command;
}
- socket.flush();
} else {
// authorization failed, connection not wanted
socket.close();
- qDebug() << "Unknown command:" << command;
+ qCDebug(QInstaller::lcServer) << "Authorization failed.";
return;
}
}
}
-template <typename T>
-void RemoteServerConnection::sendData(QIODevice *device, const T &data)
-{
- QByteArray result;
- QDataStream returnStream(&result, QIODevice::WriteOnly);
- returnStream << data;
-
- sendPacket(device, Protocol::Reply, result);
-}
-
-void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &command, QDataStream &data)
+void RemoteServerConnection::handleQProcess(RemoteServerReply *reply, const QString &command, QDataStream &data)
{
if (command == QLatin1String(Protocol::QProcessCloseWriteChannel)) {
m_process->closeWriteChannel();
} else if (command == QLatin1String(Protocol::QProcessExitCode)) {
- sendData(socket, m_process->exitCode());
+ reply->send(m_process->exitCode());
} else if (command == QLatin1String(Protocol::QProcessExitStatus)) {
- sendData(socket, static_cast<qint32> (m_process->exitStatus()));
+ reply->send(static_cast<qint32> (m_process->exitStatus()));
} else if (command == QLatin1String(Protocol::QProcessKill)) {
m_process->kill();
} else if (command == QLatin1String(Protocol::QProcessReadAll)) {
- sendData(socket, m_process->readAll());
+ reply->send(m_process->readAll());
} else if (command == QLatin1String(Protocol::QProcessReadAllStandardOutput)) {
- sendData(socket, m_process->readAllStandardOutput());
+ reply->send(m_process->readAllStandardOutput());
} else if (command == QLatin1String(Protocol::QProcessReadAllStandardError)) {
- sendData(socket, m_process->readAllStandardError());
+ reply->send(m_process->readAllStandardError());
} else if (command == QLatin1String(Protocol::QProcessStartDetached)) {
QString program;
QStringList arguments;
@@ -219,7 +278,18 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
qint64 pid = -1;
bool success = QInstaller::startDetached(program, arguments, workingDirectory, &pid);
- sendData(socket, qMakePair< bool, qint64>(success, pid));
+ reply->send(QPair<bool, qint64>(success, pid));
+ } else if (command == QLatin1String(Protocol::QProcessStartDetached2)) {
+ QString program;
+ QStringList arguments;
+ QString workingDirectory;
+ data >> program;
+ data >> arguments;
+ data >> workingDirectory;
+
+ qint64 pid = -1;
+ bool success = QProcess::startDetached(program, arguments, workingDirectory, &pid);
+ reply->send(QPair<bool, qint64>(success, pid));
} else if (command == QLatin1String(Protocol::QProcessSetWorkingDirectory)) {
QString dir;
data >> dir;
@@ -229,7 +299,7 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
data >> env;
m_process->setEnvironment(env);
} else if (command == QLatin1String(Protocol::QProcessEnvironment)) {
- sendData(socket, m_process->environment());
+ reply->send(m_process->environment());
} else if (command == QLatin1String(Protocol::QProcessStart3Arg)) {
QString program;
QStringList arguments;
@@ -243,25 +313,25 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
qint32 mode;
data >> program;
data >> mode;
- m_process->start(program, static_cast<QIODevice::OpenMode> (mode));
+ m_process->start(program, {}, static_cast<QIODevice::OpenMode> (mode));
} else if (command == QLatin1String(Protocol::QProcessState)) {
- sendData(socket, static_cast<qint32> (m_process->state()));
+ reply->send(static_cast<qint32> (m_process->state()));
} else if (command == QLatin1String(Protocol::QProcessTerminate)) {
m_process->terminate();
} else if (command == QLatin1String(Protocol::QProcessWaitForFinished)) {
qint32 msecs;
data >> msecs;
- sendData(socket, m_process->waitForFinished(msecs));
+ reply->send(m_process->waitForFinished(msecs));
} else if (command == QLatin1String(Protocol::QProcessWaitForStarted)) {
qint32 msecs;
data >> msecs;
- sendData(socket, m_process->waitForStarted(msecs));
+ reply->send(m_process->waitForStarted(msecs));
} else if (command == QLatin1String(Protocol::QProcessWorkingDirectory)) {
- sendData(socket, m_process->workingDirectory());
+ reply->send(m_process->workingDirectory());
} else if (command == QLatin1String(Protocol::QProcessErrorString)) {
- sendData(socket, m_process->errorString());
+ reply->send(m_process->errorString());
} else if (command == QLatin1String(Protocol::QProcessReadChannel)) {
- sendData(socket, static_cast<qint32> (m_process->readChannel()));
+ reply->send(static_cast<qint32> (m_process->readChannel()));
} else if (command == QLatin1String(Protocol::QProcessSetReadChannel)) {
qint32 processChannel;
data >> processChannel;
@@ -269,9 +339,9 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
} else if (command == QLatin1String(Protocol::QProcessWrite)) {
QByteArray byteArray;
data >> byteArray;
- sendData(socket, m_process->write(byteArray));
+ reply->send(m_process->write(byteArray));
} else if (command == QLatin1String(Protocol::QProcessProcessChannelMode)) {
- sendData(socket, static_cast<qint32> (m_process->processChannelMode()));
+ reply->send(static_cast<qint32> (m_process->processChannelMode()));
} else if (command == QLatin1String(Protocol::QProcessSetProcessChannelMode)) {
qint32 processChannel;
data >> processChannel;
@@ -285,18 +355,18 @@ void RemoteServerConnection::handleQProcess(QIODevice *socket, const QString &co
}
#endif
else if (!command.isEmpty()) {
- qDebug() << "Unknown QProcess command:" << command;
+ qCDebug(QInstaller::lcServer) << "Unknown QProcess command:" << command;
}
}
-void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &command,
+void RemoteServerConnection::handleQSettings(RemoteServerReply *reply, const QString &command,
QDataStream &data, PermissionSettings *settings)
{
if (!settings)
return;
if (command == QLatin1String(Protocol::QSettingsAllKeys)) {
- sendData(socket, settings->allKeys());
+ reply->send(settings->allKeys());
} else if (command == QLatin1String(Protocol::QSettingsBeginGroup)) {
QString prefix;
data >> prefix;
@@ -310,29 +380,29 @@ void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &c
} else if (command == QLatin1String(Protocol::QSettingsBeginReadArray)) {
QString prefix;
data >> prefix;
- sendData(socket, settings->beginReadArray(prefix));
+ reply->send(settings->beginReadArray(prefix));
} else if (command == QLatin1String(Protocol::QSettingsChildGroups)) {
- sendData(socket, settings->childGroups());
+ reply->send(settings->childGroups());
} else if (command == QLatin1String(Protocol::QSettingsChildKeys)) {
- sendData(socket, settings->childKeys());
+ reply->send(settings->childKeys());
} else if (command == QLatin1String(Protocol::QSettingsClear)) {
settings->clear();
} else if (command == QLatin1String(Protocol::QSettingsContains)) {
QString key;
data >> key;
- sendData(socket, settings->contains(key));
+ reply->send(settings->contains(key));
} else if (command == QLatin1String(Protocol::QSettingsEndArray)) {
settings->endArray();
} else if (command == QLatin1String(Protocol::QSettingsEndGroup)) {
settings->endGroup();
} else if (command == QLatin1String(Protocol::QSettingsFallbacksEnabled)) {
- sendData(socket, settings->fallbacksEnabled());
+ reply->send(settings->fallbacksEnabled());
} else if (command == QLatin1String(Protocol::QSettingsFileName)) {
- sendData(socket, settings->fileName());
+ reply->send(settings->fileName());
} else if (command == QLatin1String(Protocol::QSettingsGroup)) {
- sendData(socket, settings->group());
+ reply->send(settings->group());
} else if (command == QLatin1String(Protocol::QSettingsIsWritable)) {
- sendData(socket, settings->isWritable());
+ reply->send(settings->isWritable());
} else if (command == QLatin1String(Protocol::QSettingsRemove)) {
QString key;
data >> key;
@@ -346,7 +416,7 @@ void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &c
data >> b;
settings->setFallbacksEnabled(b);
} else if (command == QLatin1String(Protocol::QSettingsStatus)) {
- sendData(socket, settings->status());
+ reply->send(settings->status());
} else if (command == QLatin1String(Protocol::QSettingsSync)) {
settings->sync();
} else if (command == QLatin1String(Protocol::QSettingsSetValue)) {
@@ -359,26 +429,27 @@ void RemoteServerConnection::handleQSettings(QIODevice *socket, const QString &c
QString key;
QVariant defaultValue;
data >> key;
- data >> defaultValue;
- sendData(socket, settings->value(key, defaultValue));
+ if (!data.atEnd())
+ data >> defaultValue;
+ reply->send(settings->value(key, defaultValue));
} else if (command == QLatin1String(Protocol::QSettingsOrganizationName)) {
- sendData(socket, settings->organizationName());
+ reply->send(settings->organizationName());
} else if (command == QLatin1String(Protocol::QSettingsApplicationName)) {
- sendData(socket, settings->applicationName());
+ reply->send(settings->applicationName());
} else if (!command.isEmpty()) {
- qDebug() << "Unknown QSettings command:" << command;
+ qCDebug(QInstaller::lcServer) << "Unknown QSettings command:" << command;
}
}
-void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QString &command,
+void RemoteServerConnection::handleQFSFileEngine(RemoteServerReply *reply, const QString &command,
QDataStream &data)
{
if (command == QLatin1String(Protocol::QAbstractFileEngineAtEnd)) {
- sendData(socket, m_engine->atEnd());
+ reply->send(m_engine->atEnd());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineCaseSensitive)) {
- sendData(socket, m_engine->caseSensitive());
+ reply->send(m_engine->caseSensitive());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineClose)) {
- sendData(socket, m_engine->close());
+ reply->send(m_engine->close());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineCopy)) {
QString newName;
data >>newName;
@@ -386,90 +457,99 @@ void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QStrin
// QFileSystemEngine::copyFile() is currently unimplemented on Linux,
// copy using QFile instead of directly with QFSFileEngine.
QFile file(m_engine->fileName(QAbstractFileEngine::AbsoluteName));
- sendData(socket, file.copy(newName));
+ reply->send(file.copy(newName));
#else
- sendData(socket, m_engine->copy(newName));
+ reply->send(m_engine->copy(newName));
#endif
} else if (command == QLatin1String(Protocol::QAbstractFileEngineEntryList)) {
qint32 filters;
QStringList filterNames;
data >>filters;
data >>filterNames;
- sendData(socket, m_engine->entryList(static_cast<QDir::Filters> (filters), filterNames));
+ reply->send(m_engine->entryList(static_cast<QDir::Filters> (filters), filterNames));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineError)) {
- sendData(socket, static_cast<qint32> (m_engine->error()));
+ reply->send(static_cast<qint32> (m_engine->error()));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineErrorString)) {
- sendData(socket, m_engine->errorString());
+ reply->send(m_engine->errorString());
}
else if (command == QLatin1String(Protocol::QAbstractFileEngineFileFlags)) {
qint32 flags;
data >>flags;
flags = m_engine->fileFlags(static_cast<QAbstractFileEngine::FileFlags>(flags));
- sendData(socket, static_cast<qint32>(flags));
+ reply->send(static_cast<qint32>(flags));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFileName)) {
qint32 file;
data >>file;
- sendData(socket, m_engine->fileName(static_cast<QAbstractFileEngine::FileName> (file)));
+ reply->send(m_engine->fileName(static_cast<QAbstractFileEngine::FileName> (file)));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFlush)) {
- sendData(socket, m_engine->flush());
+ reply->send(m_engine->flush());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineHandle)) {
- sendData(socket, m_engine->handle());
+ reply->send(m_engine->handle());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineIsRelativePath)) {
- sendData(socket, m_engine->isRelativePath());
+ reply->send(m_engine->isRelativePath());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineIsSequential)) {
- sendData(socket, m_engine->isSequential());
+ reply->send(m_engine->isSequential());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineLink)) {
QString newName;
data >>newName;
- sendData(socket, m_engine->link(newName));
+ reply->send(m_engine->link(newName));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineMkdir)) {
QString dirName;
bool createParentDirectories;
data >>dirName;
data >>createParentDirectories;
- sendData(socket, m_engine->mkdir(dirName, createParentDirectories));
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ reply->send(m_engine->mkdir(dirName, createParentDirectories));
+#else
+ reply->send(m_engine->mkdir(dirName, createParentDirectories, std::nullopt));
+#endif
+
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOpen)) {
qint32 openMode;
data >>openMode;
- sendData(socket, m_engine->open(static_cast<QIODevice::OpenMode> (openMode)));
+#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
+ reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode)));
+#else
+ reply->send(m_engine->open(static_cast<QIODevice::OpenMode> (openMode), std::nullopt));
+#endif
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOwner)) {
qint32 owner;
data >>owner;
- sendData(socket, m_engine->owner(static_cast<QAbstractFileEngine::FileOwner> (owner)));
+ reply->send(m_engine->owner(static_cast<QAbstractFileEngine::FileOwner> (owner)));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineOwnerId)) {
qint32 owner;
data >>owner;
- sendData(socket, m_engine->ownerId(static_cast<QAbstractFileEngine::FileOwner> (owner)));
+ reply->send(m_engine->ownerId(static_cast<QAbstractFileEngine::FileOwner> (owner)));
} else if (command == QLatin1String(Protocol::QAbstractFileEnginePos)) {
- sendData(socket, m_engine->pos());
+ reply->send(m_engine->pos());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRead)) {
qint64 maxlen;
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->read(byteArray.data(), maxlen);
- sendData(socket, qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineReadLine)) {
qint64 maxlen;
data >> maxlen;
QByteArray byteArray(maxlen, '\0');
const qint64 r = m_engine->readLine(byteArray.data(), maxlen);
- sendData(socket, qMakePair<qint64, QByteArray>(r, byteArray));
+ reply->send(QPair<qint64, QByteArray>(r, byteArray));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRemove)) {
- sendData(socket, m_engine->remove());
+ reply->send(m_engine->remove());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRename)) {
QString newName;
data >>newName;
- sendData(socket, m_engine->rename(newName));
+ reply->send(m_engine->rename(newName));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRmdir)) {
QString dirName;
bool recurseParentDirectories;
data >>dirName;
data >>recurseParentDirectories;
- sendData(socket, m_engine->rmdir(dirName, recurseParentDirectories));
+ reply->send(m_engine->rmdir(dirName, recurseParentDirectories));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSeek)) {
quint64 offset;
data >>offset;
- sendData(socket, m_engine->seek(offset));
+ reply->send(m_engine->seek(offset));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSetFileName)) {
QString fileName;
data >>fileName;
@@ -477,33 +557,93 @@ void RemoteServerConnection::handleQFSFileEngine(QIODevice *socket, const QStrin
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSetPermissions)) {
uint perms;
data >>perms;
- sendData(socket, m_engine->setPermissions(perms));
+ reply->send(m_engine->setPermissions(perms));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSetSize)) {
qint64 size;
data >>size;
- sendData(socket, m_engine->setSize(size));
+ reply->send(m_engine->setSize(size));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSize)) {
- sendData(socket, m_engine->size());
+ reply->send(m_engine->size());
} else if ((command == QLatin1String(Protocol::QAbstractFileEngineSupportsExtension))
|| (command == QLatin1String(Protocol::QAbstractFileEngineExtension))) {
// Implemented client side.
} else if (command == QLatin1String(Protocol::QAbstractFileEngineWrite)) {
QByteArray content;
data >> content;
- sendData(socket, m_engine->write(content.data(), content.size()));
+ reply->send(m_engine->write(content.data(), content.size()));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineSyncToDisk)) {
- sendData(socket, m_engine->syncToDisk());
+ reply->send(m_engine->syncToDisk());
} else if (command == QLatin1String(Protocol::QAbstractFileEngineRenameOverwrite)) {
QString newFilename;
data >> newFilename;
- sendData(socket, m_engine->renameOverwrite(newFilename));
+ reply->send(m_engine->renameOverwrite(newFilename));
} else if (command == QLatin1String(Protocol::QAbstractFileEngineFileTime)) {
qint32 filetime;
data >> filetime;
- sendData(socket, m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime)));
+#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
+ reply->send(m_engine->fileTime(static_cast<QAbstractFileEngine::FileTime> (filetime)));
+#else
+ reply->send(m_engine->fileTime(static_cast<QFile::FileTime> (filetime)));
+#endif
} else if (!command.isEmpty()) {
- qDebug() << "Unknown QAbstractFileEngine command:" << command;
+ qCDebug(QInstaller::lcServer) << "Unknown QAbstractFileEngine command:" << command;
}
}
+void RemoteServerConnection::handleArchive(RemoteServerReply *reply, const QString &command, QDataStream &data)
+{
+#ifdef IFW_LIBARCHIVE
+ LibArchiveArchive *archive = static_cast<LibArchiveArchive *>(m_archive.get());
+ if (command == QLatin1String(Protocol::AbstractArchiveOpen)) {
+ qint32 openMode;
+ data >> openMode;
+ reply->send(archive->open(static_cast<QIODevice::OpenMode>(openMode)));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveClose)) {
+ archive->close();
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetFilename)) {
+ QString fileName;
+ data >> fileName;
+ archive->setFilename(fileName);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveErrorString)) {
+ reply->send(archive->errorString());
+ } else if (command == QLatin1String(Protocol::AbstractArchiveExtract)) {
+ QString dirPath;
+ quint64 total;
+ data >> dirPath;
+ data >> total;
+ archive->workerExtract(dirPath, total);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveCreate)) {
+ QStringList entries;
+ data >> entries;
+ reply->send(archive->create(entries));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveList)) {
+ reply->send(archive->list());
+ } else if (command == QLatin1String(Protocol::AbstractArchiveIsSupported)) {
+ reply->send(archive->isSupported());
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetCompressionLevel)) {
+ qint32 level;
+ data >> level;
+ archive->setCompressionLevel(static_cast<AbstractArchive::CompressionLevel>(level));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveAddDataBlock)) {
+ QByteArray buff;
+ data >> buff;
+ archive->workerAddDataBlock(buff);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetClientDataAtEnd)) {
+ archive->workerSetDataAtEnd();
+ } else if (command == QLatin1String(Protocol::AbstractArchiveSetFilePosition)) {
+ qint64 pos;
+ data >> pos;
+ archive->workerSetFilePosition(pos);
+ } else if (command == QLatin1String(Protocol::AbstractArchiveWorkerStatus)) {
+ reply->send(static_cast<qint32>(archive->workerStatus()));
+ } else if (command == QLatin1String(Protocol::AbstractArchiveCancel)) {
+ archive->workerCancel();
+ } else if (!command.isEmpty()) {
+ qCDebug(QInstaller::lcServer) << "Unknown AbstractArchive command:" << command;
+ }
+#else
+ Q_ASSERT_X(false, Q_FUNC_INFO, "No compatible archive handler exists for protocol.");
+#endif
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/remoteserverconnection.h b/src/libs/installer/remoteserverconnection.h
index 07cfb98c0..53f6a412b 100644
--- a/src/libs/installer/remoteserverconnection.h
+++ b/src/libs/installer/remoteserverconnection.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,14 +29,16 @@
#ifndef REMOTESERVERCONNECTION_H
#define REMOTESERVERCONNECTION_H
+#include "abstractarchive.h"
+
#include <QPointer>
#include <QThread>
+#include <QProcess>
#include <QtCore/private/qfsfileengine_p.h>
QT_BEGIN_NAMESPACE
-class QProcess;
-class QIODevice;
+class QLocalSocket;
QT_END_NAMESPACE
namespace QInstaller {
@@ -44,6 +46,21 @@ namespace QInstaller {
class PermissionSettings;
class QProcessSignalReceiver;
+class AbstractArchiveSignalReceiver;
+
+class RemoteServerReply
+{
+public:
+ explicit RemoteServerReply(QLocalSocket *socket);
+ ~RemoteServerReply();
+
+ template <typename T>
+ void send(const T &data);
+
+private:
+ QLocalSocket *m_socket;
+ bool m_sent;
+};
class RemoteServerConnection : public QThread
{
@@ -54,26 +71,28 @@ public:
RemoteServerConnection(qintptr socketDescriptor, const QString &authorizationKey,
QObject *parent);
- void run() Q_DECL_OVERRIDE;
+ void run() override;
signals:
void shutdownRequested();
private:
- template <typename T>
- void sendData(QIODevice *device, const T &arg);
- void handleQProcess(QIODevice *device, const QString &command, QDataStream &data);
- void handleQSettings(QIODevice *device, const QString &command, QDataStream &data,
+ void handleQProcess(RemoteServerReply *reply, const QString &command, QDataStream &data);
+ void handleQSettings(RemoteServerReply *reply, const QString &command, QDataStream &data,
PermissionSettings *settings);
- void handleQFSFileEngine(QIODevice *device, const QString &command, QDataStream &data);
+ void handleQFSFileEngine(RemoteServerReply *reply, const QString &command, QDataStream &data);
+ void handleArchive(RemoteServerReply *reply, const QString &command, QDataStream &data);
private:
qintptr m_socketDescriptor;
-
- QProcess *m_process;
- QFSFileEngine *m_engine;
QString m_authorizationKey;
- QProcessSignalReceiver *m_signalReceiver;
+
+ QScopedPointer<QProcess> m_process;
+ QScopedPointer<QFSFileEngine> m_engine;
+ QScopedPointer<AbstractArchive> m_archive;
+
+ QProcessSignalReceiver *m_processSignalReceiver;
+ AbstractArchiveSignalReceiver *m_archiveSignalReceiver;
};
} // namespace QInstaller
diff --git a/src/libs/installer/remoteserverconnection_p.h b/src/libs/installer/remoteserverconnection_p.h
index dad5f4133..09e6de7d7 100644
--- a/src/libs/installer/remoteserverconnection_p.h
+++ b/src/libs/installer/remoteserverconnection_p.h
@@ -30,6 +30,9 @@
#define REMOTESERVERCONNECTION_P_H
#include "protocol.h"
+#ifdef IFW_LIBARCHIVE
+#include "libarchivearchive.h"
+#endif
#include <QMutex>
#include <QProcess>
@@ -49,7 +52,7 @@ private:
connect(process, &QIODevice::bytesWritten, this, &QProcessSignalReceiver::onBytesWritten);
connect(process, &QIODevice::aboutToClose, this, &QProcessSignalReceiver::onAboutToClose);
connect(process, &QIODevice::readChannelFinished, this, &QProcessSignalReceiver::onReadChannelFinished);
- connect(process, SIGNAL(error(QProcess::ProcessError)),
+ connect(process, SIGNAL(errorOccurred(QProcess::ProcessError)),
SLOT(onError(QProcess::ProcessError)));
connect(process, &QProcess::readyReadStandardOutput,
this, &QProcessSignalReceiver::onReadyReadStandardOutput);
@@ -124,6 +127,70 @@ private:
QVariantList m_receivedSignals;
};
+#ifdef IFW_LIBARCHIVE
+class AbstractArchiveSignalReceiver : public QObject
+{
+ Q_OBJECT
+ friend class RemoteServerConnection;
+
+private:
+ explicit AbstractArchiveSignalReceiver(LibArchiveArchive *archive)
+ : QObject(archive)
+ {
+ connect(archive, &LibArchiveArchive::currentEntryChanged,
+ this, &AbstractArchiveSignalReceiver::onCurrentEntryChanged);
+ connect(archive, &LibArchiveArchive::completedChanged,
+ this, &AbstractArchiveSignalReceiver::onCompletedChanged);
+ connect(archive, &LibArchiveArchive::dataBlockRequested,
+ this, &AbstractArchiveSignalReceiver::onDataBlockRequested);
+ connect(archive, &LibArchiveArchive::seekRequested,
+ this, &AbstractArchiveSignalReceiver::onSeekRequested);
+ connect(archive, &LibArchiveArchive::workerFinished,
+ this, &AbstractArchiveSignalReceiver::onWorkerFinished);
+ }
+
+private Q_SLOTS:
+ void onCurrentEntryChanged(const QString &filename)
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalCurrentEntryChanged));
+ m_receivedSignals.append(filename);
+ }
+
+ void onCompletedChanged(quint64 completed, quint64 total)
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalCompletedChanged));
+ m_receivedSignals.append(completed);
+ m_receivedSignals.append(total);
+ }
+
+ void onDataBlockRequested()
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalDataBlockRequested));
+ }
+
+ void onSeekRequested(qint64 offset, int whence)
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalSeekRequested));
+ m_receivedSignals.append(offset);
+ m_receivedSignals.append(whence);
+ }
+
+ void onWorkerFinished()
+ {
+ QMutexLocker _(&m_lock);
+ m_receivedSignals.append(QLatin1String(Protocol::AbstractArchiveSignalWorkerFinished));
+ }
+
+private:
+ QMutex m_lock;
+ QVariantList m_receivedSignals;
+};
+#endif
+
} // namespace QInstaller
#endif // REMOTESERVERCONNECTION_P_H
diff --git a/src/libs/installer/replaceoperation.cpp b/src/libs/installer/replaceoperation.cpp
index 692bba715..3355bef71 100644
--- a/src/libs/installer/replaceoperation.cpp
+++ b/src/libs/installer/replaceoperation.cpp
@@ -35,6 +35,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ReplaceOperation
+ \internal
+*/
+
ReplaceOperation::ReplaceOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
diff --git a/src/libs/installer/replaceoperation.h b/src/libs/installer/replaceoperation.h
index 0d2783ab6..09cca99c2 100644
--- a/src/libs/installer/replaceoperation.h
+++ b/src/libs/installer/replaceoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT ReplaceOperation : public Operation
public:
explicit ReplaceOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
} // namespace QInstaller
diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp
index e186073ce..f7035e732 100644
--- a/src/libs/installer/repository.cpp
+++ b/src/libs/installer/repository.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,6 +32,13 @@
#include <QDataStream>
#include <QFileInfo>
#include <QStringList>
+#include <QDir>
+
+/*!
+ \fn inline hashValue QInstaller::qHash(const Repository &repository)
+
+ Returns a hash of the \a repository.
+*/
namespace QInstaller {
@@ -42,8 +49,8 @@ Repository::Repository()
: m_default(false)
, m_enabled(false)
, m_compressed(false)
+ , m_postLoadComponentScript(false)
{
- registerMetaType();
}
/*!
@@ -56,23 +63,24 @@ Repository::Repository(const Repository &other)
, m_username(other.m_username)
, m_password(other.m_password)
, m_displayname(other.m_displayname)
- , m_compressed(other.m_compressed)
, m_categoryname(other.m_categoryname)
+ , m_compressed(other.m_compressed)
+ , m_xmlChecksum(other.m_xmlChecksum)
+ , m_postLoadComponentScript(other.m_postLoadComponentScript)
{
- registerMetaType();
}
/*!
Constructs a new repository by setting its address to \a url
- and its default and \a compressed states.
+ and \a isDefault and \a compressed states.
*/
Repository::Repository(const QUrl &url, bool isDefault, bool compressed)
: m_url(url)
, m_default(isDefault)
, m_enabled(true)
, m_compressed(compressed)
+ , m_postLoadComponentScript(false)
{
- registerMetaType();
}
/*!
@@ -83,7 +91,7 @@ Repository::Repository(const QUrl &url, bool isDefault, bool compressed)
*/
Repository Repository::fromUserInput(const QString &repositoryUrl, bool compressed)
{
- QUrl url = QUrl::fromUserInput(repositoryUrl);
+ QUrl url = QUrl::fromUserInput(repositoryUrl, QDir::currentPath());
const QStringList supportedSchemes = KDUpdater::FileDownloaderFactory::supportedSchemes();
if (!supportedSchemes.contains(url.scheme()) && QFileInfo(url.toString()).exists())
url = QLatin1String("file:///") + url.toString();
@@ -120,7 +128,7 @@ bool Repository::isDefault() const
}
/*!
- Returns the URL of the repository. By default an invalid \sa QUrl is returned.
+ Returns the URL of the repository. By default an invalid QUrl is returned.
*/
QUrl Repository::url() const
{
@@ -144,7 +152,8 @@ bool Repository::isEnabled() const
}
/*!
- Sets this repository to \n enabled state and thus to use this repository for information retrieval or not.
+ Sets this repository to \a enabled state. If \a enabled is \c true,
+ the repository is used for information retrieval.
*/
void Repository::setEnabled(bool enabled)
{
@@ -216,6 +225,28 @@ void Repository::setCategoryName(const QString &categoryname)
}
/*!
+ Returns the expected checksum of the repository, which is the checksum
+ calculated from the \c Updates.xml document at the root of the repository.
+
+ This value is used as a hint when looking for already fetched repositories
+ from the local cache. If the installer has cached a repository with a matching
+ checksum, it can skip downloading the \c Updates.xml file for that repository again.
+*/
+QByteArray Repository::xmlChecksum() const
+{
+ return m_xmlChecksum;
+}
+
+/*!
+ Sets the expected checksum of the repository to \c checksum. The checksum
+ is calculated from the \c Updates.xml document at the root of the repository.
+*/
+void Repository::setXmlChecksum(const QByteArray &checksum)
+{
+ m_xmlChecksum = checksum;
+}
+
+/*!
Returns true if repository is compressed
*/
bool Repository::isCompressed() const
@@ -224,13 +255,21 @@ bool Repository::isCompressed() const
}
/*!
- Sets this repository to \a compressed state to know weather the repository
- needs to be uncompressed before use.
+ \internal
*/
-void Repository::setCompressed(bool compressed)
+bool Repository::postLoadComponentScript() const
{
- m_compressed = compressed;
+ return m_postLoadComponentScript;
}
+
+/*!
+ \internal
+*/
+void Repository::setPostLoadComponentScript(const bool postLoad)
+{
+ m_postLoadComponentScript = postLoad;
+}
+
/*!
Compares the values of this repository to \a other and returns true if they are equal (same server,
default state, enabled state as well as username and password). \sa operator!=()
@@ -238,7 +277,9 @@ void Repository::setCompressed(bool compressed)
bool Repository::operator==(const Repository &other) const
{
return m_url == other.m_url && m_default == other.m_default && m_enabled == other.m_enabled
- && m_username == other.m_username && m_password == other.m_password && m_displayname == other.m_displayname;
+ && m_username == other.m_username && m_password == other.m_password
+ && m_displayname == other.m_displayname && m_xmlChecksum == other.m_xmlChecksum
+ && m_postLoadComponentScript == other.m_postLoadComponentScript;
}
/*!
@@ -266,6 +307,8 @@ const Repository &Repository::operator=(const Repository &other)
m_displayname = other.m_displayname;
m_compressed = other.m_compressed;
m_categoryname = other.m_categoryname;
+ m_xmlChecksum = other.m_xmlChecksum;
+ m_postLoadComponentScript = other.m_postLoadComponentScript;
return *this;
}
@@ -273,14 +316,19 @@ const Repository &Repository::operator=(const Repository &other)
void Repository::registerMetaType()
{
qRegisterMetaType<Repository>("Repository");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<Repository>("Repository");
+#endif
}
+/*!
+ \internal
+*/
QDataStream &operator>>(QDataStream &istream, Repository &repository)
{
QByteArray url, username, password, displayname, compressed;
istream >> url >> repository.m_default >> repository.m_enabled >> username >> password
- >> displayname >> repository.m_categoryname;
+ >> displayname >> repository.m_categoryname >> repository.m_xmlChecksum >> repository.m_postLoadComponentScript;
repository.setUrl(QUrl::fromEncoded(QByteArray::fromBase64(url)));
repository.setUsername(QString::fromUtf8(QByteArray::fromBase64(username)));
repository.setPassword(QString::fromUtf8(QByteArray::fromBase64(password)));
@@ -288,11 +336,15 @@ QDataStream &operator>>(QDataStream &istream, Repository &repository)
return istream;
}
+/*!
+ \internal
+*/
QDataStream &operator<<(QDataStream &ostream, const Repository &repository)
{
return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled
<< repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64()
- << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64();
+ << repository.m_displayname.toUtf8().toBase64() << repository.m_categoryname.toUtf8().toBase64()
+ << repository.m_xmlChecksum.toBase64() << repository.m_postLoadComponentScript;
}
}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
index 546cddc97..0a589a43e 100644
--- a/src/libs/installer/repository.h
+++ b/src/libs/installer/repository.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -67,12 +67,17 @@ public:
QString categoryname() const;
void setCategoryName(const QString &categoryname);
+ QByteArray xmlChecksum() const;
+ void setXmlChecksum(const QByteArray &checksum);
+
bool isCompressed() const;
- void setCompressed(bool compressed);
+ bool postLoadComponentScript() const;
+ void setPostLoadComponentScript(const bool postLoad);
+
bool operator==(const Repository &other) const;
bool operator!=(const Repository &other) const;
- uint qHash(const Repository &repository);
+ hashValue qHash(const Repository &repository);
const Repository &operator=(const Repository &other);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, Repository &repository);
@@ -87,9 +92,11 @@ private:
QString m_displayname;
QString m_categoryname;
bool m_compressed;
+ QByteArray m_xmlChecksum;
+ bool m_postLoadComponentScript;
};
-inline uint qHash(const Repository &repository)
+inline hashValue qHash(const Repository &repository)
{
return qHash(repository.url());
}
diff --git a/src/libs/installer/repositorycategory.cpp b/src/libs/installer/repositorycategory.cpp
index ce06480c8..c9bee6e3a 100644
--- a/src/libs/installer/repositorycategory.cpp
+++ b/src/libs/installer/repositorycategory.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +34,12 @@
#include <QFileInfo>
#include <QStringList>
+/*!
+ \fn inline hashValue QInstaller::qHash(const RepositoryCategory &repository)
+
+ Returns a hash of the repository category \a repository.
+*/
+
namespace QInstaller {
@@ -59,8 +65,8 @@ RepositoryCategory::RepositoryCategory()
Constructs a new category by using all fields of the given category \a other.
*/
RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
- : m_displayname(other.m_displayname), m_data(other.m_data), m_enabled(other.m_enabled),
- m_tooltip(other.m_tooltip)
+ : m_data(other.m_data), m_displayname(other.m_displayname), m_tooltip(other.m_tooltip),
+ m_enabled(other.m_enabled)
{
registerMetaType();
}
@@ -69,7 +75,9 @@ RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
void RepositoryCategory::registerMetaType()
{
qRegisterMetaType<RepositoryCategory>("RepositoryCategory");
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<RepositoryCategory>("RepositoryCategory");
+#endif
}
/*!
@@ -88,11 +96,17 @@ void RepositoryCategory::setDisplayName(const QString &displayname)
m_displayname = displayname;
}
+/*!
+ Returns the Tooltip for the category to be displayed.
+*/
QString RepositoryCategory::tooltip() const
{
return m_tooltip;
}
+/*!
+ Sets the Tooltip of the category to \a tooltip.
+*/
void RepositoryCategory::setTooltip(const QString &tooltip)
{
m_tooltip = tooltip;
@@ -116,15 +130,15 @@ void RepositoryCategory::setRepositories(const QSet<Repository> repositories, co
m_data.remove(scRepositories);
foreach (const Repository &repository, repositories)
- m_data.insertMulti(scRepositories, QVariant().fromValue(repository));
+ m_data.insert(scRepositories, QVariant().fromValue(repository));
}
/*!
Inserts \a repository to the category.
*/
-void RepositoryCategory::addRepository(const Repository repository)
+void RepositoryCategory::addRepository(const Repository &repository)
{
- m_data.insertMulti(scRepositories, QVariant().fromValue(repository));
+ m_data.insert(scRepositories, QVariant().fromValue(repository));
}
/*!
@@ -162,11 +176,17 @@ bool RepositoryCategory::operator!=(const RepositoryCategory &other) const
return !(*this == other);
}
+/*!
+ \internal
+*/
QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository)
{
return istream;
}
+/*!
+ \internal
+*/
QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository)
{
return ostream << repository.m_displayname.toUtf8().toBase64() << repository.m_data;
diff --git a/src/libs/installer/repositorycategory.h b/src/libs/installer/repositorycategory.h
index 1996103a5..993ae78aa 100644
--- a/src/libs/installer/repositorycategory.h
+++ b/src/libs/installer/repositorycategory.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -26,11 +26,12 @@
**
**************************************************************************/
-#ifndef ARCHIVEREPOSITORY_H
-#define ARCHIVEREPOSITORY_H
+#ifndef REPOSITORYCATEGORY_H
+#define REPOSITORYCATEGORY_H
#include "installer_global.h"
#include "repository.h"
+#include "qinstallerglobal.h"
#include <QtCore/QMetaType>
#include <QtCore/QUrl>
@@ -55,7 +56,7 @@ public:
QSet<Repository> repositories() const;
void setRepositories(const QSet<Repository> repositories, const bool replace = false);
- void addRepository(const Repository repository);
+ void addRepository(const Repository &repository);
bool isEnabled() const;
void setEnabled(bool enabled);
@@ -63,19 +64,19 @@ public:
bool operator==(const RepositoryCategory &other) const;
bool operator!=(const RepositoryCategory &other) const;
- uint qHash(const RepositoryCategory &repository);
+ hashValue qHash(const RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository);
private:
- QVariantHash m_data;
+ QMultiHash<QString, QVariant> m_data;
QString m_displayname;
QString m_tooltip;
bool m_enabled;
};
-inline uint qHash(const RepositoryCategory &repository)
+inline hashValue qHash(const RepositoryCategory &repository)
{
return qHash(repository.displayname());
}
@@ -87,4 +88,4 @@ INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryC
Q_DECLARE_METATYPE(QInstaller::RepositoryCategory)
-#endif // ARCHIVEREPOSITORY_H
+#endif // REPOSITORYCATEGORY_H
diff --git a/src/libs/installer/resources/installer.qrc b/src/libs/installer/resources/installer.qrc
index 48a7c65bd..a3855b5c4 100644
--- a/src/libs/installer/resources/installer.qrc
+++ b/src/libs/installer/resources/installer.qrc
@@ -7,5 +7,6 @@
<file>uninstall.png</file>
<file>keepinstalled.png</file>
<file>keepuninstalled.png</file>
+ <file>qt/etc/qt.conf</file>
</qresource>
</RCC>
diff --git a/src/libs/installer/resources/qt/etc/qt.conf b/src/libs/installer/resources/qt/etc/qt.conf
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/libs/installer/resources/qt/etc/qt.conf
diff --git a/src/libs/installer/runextensions.h b/src/libs/installer/runextensions.h
index fd76db060..9c7147141 100644
--- a/src/libs/installer/runextensions.h
+++ b/src/libs/installer/runextensions.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -37,384 +37,70 @@ QT_BEGIN_NAMESPACE
namespace QtConcurrent {
-template <typename T, typename FunctionPointer>
-class StoredInterfaceFunctionCall0 : public QRunnable
+template <typename T, typename FunctionPointer, typename... Args>
+class StoredInterfaceFunctionCall : public QRunnable
{
public:
- StoredInterfaceFunctionCall0(void (fn)(QFutureInterface<T> &))
- : fn(fn) { }
+ StoredInterfaceFunctionCall(void (fn)(QFutureInterface<T> &, Args...), const Args&&... args)
+ : m_fn(fn), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
- void run()
+ void run() override
{
- fn(futureInterface);
- futureInterface.reportFinished();
+ fn(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
-
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Class>
-class StoredInterfaceMemberFunctionCall0 : public QRunnable
+template <typename T, typename FunctionPointer, typename Class, typename... Args>
+class StoredInterfaceMemberFunctionCall : public QRunnable
{
public:
- StoredInterfaceMemberFunctionCall0(void (Class::*fn)(QFutureInterface<T> &), Class *object)
- : fn(fn), object(object) { }
+ StoredInterfaceMemberFunctionCall(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, const Args&&... args)
+ : m_fn(fn), m_object(object), m_args(std::make_tuple(std::forward<Args>(args)...)) { }
QFuture<T> start()
{
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
+ m_futureInterface.reportStarted();
+ QFuture<T> future = m_futureInterface.future();
QThreadPool::globalInstance()->start(this);
return future;
}
- void run()
+ void run() override
{
- (object->*fn)(futureInterface);
- futureInterface.reportFinished();
+ (m_object->*m_fn)(m_futureInterface, std::forward<Args>(m_args)...);
+ m_futureInterface.reportFinished();
}
private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
-
+ QFutureInterface<T> m_futureInterface;
+ FunctionPointer m_fn;
+ Class *m_object;
+ std::tuple<Args...> m_args;
};
-template <typename T, typename FunctionPointer, typename Arg1>
-class StoredInterfaceFunctionCall1 : public QRunnable
+template <typename T, typename... Args>
+QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Args...), Args... args)
{
-public:
- StoredInterfaceFunctionCall1(void (fn)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
- : fn(fn), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- fn(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1>
-class StoredInterfaceMemberFunctionCall1 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall1(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, const Arg1 &arg1)
- : fn(fn), object(object), arg1(arg1) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- (object->*fn)(futureInterface, arg1);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2>
-class StoredInterfaceFunctionCall2 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall2(void (fn)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), arg1(arg1), arg2(arg2) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- fn(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2>
-class StoredInterfaceMemberFunctionCall2 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall2(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
- : fn(fn), object(object), arg1(arg1), arg2(arg2) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- (object->*fn)(futureInterface, arg1, arg2);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceFunctionCall3 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall3(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- fn(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3>
-class StoredInterfaceMemberFunctionCall3 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall3(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall4(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- fn(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-class StoredInterfaceMemberFunctionCall4 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall4(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4;
-};
-
-template <typename T, typename FunctionPointer, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceFunctionCall5(void (fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- fn(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-template <typename T, typename FunctionPointer, typename Class, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-class StoredInterfaceMemberFunctionCall5 : public QRunnable
-{
-public:
- StoredInterfaceMemberFunctionCall5(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
- : fn(fn), object(object), arg1(arg1), arg2(arg2), arg3(arg3), arg4(arg4), arg5(arg5) { }
-
- QFuture<T> start()
- {
- futureInterface.reportStarted();
- QFuture<T> future = futureInterface.future();
- QThreadPool::globalInstance()->start(this);
- return future;
- }
-
- void run()
- {
- (object->*fn)(futureInterface, arg1, arg2, arg3, arg4, arg5);
- futureInterface.reportFinished();
- }
-private:
- QFutureInterface<T> futureInterface;
- FunctionPointer fn;
- Class *object;
- Arg1 arg1; Arg2 arg2; Arg3 arg3; Arg4 arg4; Arg5 arg5;
-};
-
-template <typename T>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &))
-{
- return (new StoredInterfaceFunctionCall0<T, void (*)(QFutureInterface<T> &)>(functionPointer))->start();
-}
-template <typename T, typename Arg1>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1), const Arg1 &arg1)
-{
- return (new StoredInterfaceFunctionCall1<T, void (*)(QFutureInterface<T> &, Arg1), Arg1>(functionPointer, arg1))->start();
-}
-template <typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2), const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceFunctionCall2<T, void (*)(QFutureInterface<T> &, Arg1, Arg2), Arg1, Arg2>(functionPointer, arg1, arg2))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
-{
- return (new StoredInterfaceFunctionCall3<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Arg1, Arg2, Arg3>(functionPointer, arg1, arg2, arg3))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceFunctionCall4<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Arg1, Arg2, Arg3, Arg4>(functionPointer, arg1, arg2, arg3, arg4))->start();
-}
-template <typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (*functionPointer)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceFunctionCall5<T, void (*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Arg1, Arg2, Arg3, Arg4, Arg5>(functionPointer, arg1, arg2, arg3, arg4, arg5))->start();
-}
-
-template <typename Class, typename T>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &), Class *object)
-{
- return (new StoredInterfaceMemberFunctionCall0<T, void (Class::*)(QFutureInterface<T> &), Class>(fn, object))->start();
-}
-
-template <typename Class, typename T, typename Arg1>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1), Class *object, Arg1 arg1)
-{
- return (new StoredInterfaceMemberFunctionCall1<T, void (Class::*)(QFutureInterface<T> &, Arg1), Class, Arg1>(fn, object, arg1))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2), Class *object, const Arg1 &arg1, const Arg2 &arg2)
-{
- return (new StoredInterfaceMemberFunctionCall2<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2), Class, Arg1, Arg2>(fn, object, arg1, arg2))->start();
+ return (new StoredInterfaceFunctionCall<T, void (*)(QFutureInterface<T> &, Args...), Args...>(functionPointer, args...))->start();
}
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3)
+template <typename Class, typename T, typename... Args>
+QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Args...), Class *object, Args... args)
{
- return (new StoredInterfaceMemberFunctionCall3<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3), Class, Arg1, Arg2, Arg3>(fn, object, arg1, arg2, arg3))->start();
+ return (new StoredInterfaceMemberFunctionCall<T, void (Class::*)(QFutureInterface<T> &, Args...), Class, Args...>(fn, object, args...))->start();
}
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4)
-{
- return (new StoredInterfaceMemberFunctionCall4<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4), Class, Arg1, Arg2, Arg3, Arg4>(fn, object, arg1, arg2, arg3, arg4))->start();
-}
-
-template <typename Class, typename T, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
-QFuture<T> run(void (Class::*fn)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class *object, const Arg1 &arg1, const Arg2 &arg2, const Arg3 &arg3, const Arg4 &arg4, const Arg5 &arg5)
-{
- return (new StoredInterfaceMemberFunctionCall5<T, void (Class::*)(QFutureInterface<T> &, Arg1, Arg2, Arg3, Arg4, Arg5), Class, Arg1, Arg2, Arg3, Arg4, Arg5>(fn, object, arg1, arg2, arg3, arg4, arg5))->start();
-}
} // namespace QtConcurrent
QT_END_NAMESPACE
diff --git a/src/libs/installer/scriptengine.cpp b/src/libs/installer/scriptengine.cpp
index 994fa1406..7e3e69eb2 100644
--- a/src/libs/installer/scriptengine.cpp
+++ b/src/libs/installer/scriptengine.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +31,10 @@
#include "errors.h"
#include "scriptengine_p.h"
#include "systeminfo.h"
+#include "loggingutils.h"
+#include "packagemanagergui.h"
+#include "component.h"
+#include "settings.h"
#include <QMetaEnum>
#include <QQmlEngine>
@@ -46,31 +50,21 @@ namespace QInstaller {
*/
/*!
- \fn ScriptEngine::globalObject() const
+ \fn QInstaller::ScriptEngine::globalObject() const
Returns a global object.
*/
-QJSValue InstallerProxy::components() const
-{
- if (m_core) {
- const QList<Component*> all = m_core->components(PackageManagerCore::ComponentType::All);
- QJSValue scriptComponentsObject = m_engine->newArray(all.count());
- for (int i = 0; i < all.count(); ++i) {
- Component *const component = all.at(i);
- QQmlEngine::setObjectOwnership(component, QQmlEngine::CppOwnership);
- scriptComponentsObject.setProperty(i, m_engine->newQObject(component));
- }
- return scriptComponentsObject;
- }
- return m_engine->newArray();
-}
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::QDesktopServicesProxy
+ \internal
+*/
-QJSValue InstallerProxy::componentByName(const QString &componentName)
-{
- if (m_core)
- return m_engine->newQObject(m_core->componentByName(componentName));
- return QJSValue();
-}
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::QFileDialogProxy
+ \internal
+*/
QJSValue QDesktopServicesProxy::findFiles(const QString &path, const QString &pattern)
{
@@ -178,6 +172,16 @@ void GuiProxy::clickButton(int wizardButton, int delayInMs)
m_gui->clickButton(wizardButton, delayInMs);
}
+/*!
+ Automatically clicks the button specified by \a objectName after a delay
+ in milliseconds specified by \a delayInMs.
+*/
+void GuiProxy::clickButton(const QString &objectName, int delayInMs) const
+{
+ if (m_gui)
+ m_gui->clickButton(objectName, delayInMs);
+}
+
bool GuiProxy::isButtonEnabled(int wizardButton)
{
if (!m_gui)
@@ -185,6 +189,12 @@ bool GuiProxy::isButtonEnabled(int wizardButton)
return m_gui->isButtonEnabled(wizardButton);
}
+void GuiProxy::setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText)
+{
+ if (m_gui)
+ m_gui->setWizardPageButtonText(pageId, buttonId, buttonText);
+}
+
void GuiProxy::showSettingsButton(bool show)
{
if (m_gui)
@@ -265,33 +275,98 @@ void GuiProxy::setModified(bool value)
m_gui->setModified(value);
}
+QFileDialogProxy::QFileDialogProxy(PackageManagerCore *core): m_core(core)
+{
+}
+
+QString QFileDialogProxy::getExistingDirectory(const QString &caption,
+ const QString &dir, const QString &identifier)
+{
+ if (m_core->isCommandLineInstance()) {
+ return getExistingFileOrDirectory(caption, identifier, true);
+ } else {
+ return QFileDialog::getExistingDirectory(0, caption, dir);
+ }
+}
+
+QString QFileDialogProxy::getOpenFileName(const QString &caption, const QString &dir,
+ const QString &filter, const QString &identifier)
+{
+ if (m_core->isCommandLineInstance()) {
+ return getExistingFileOrDirectory(caption, identifier, false);
+ } else {
+ return QFileDialog::getOpenFileName(0, caption, dir, filter);
+ }
+}
+
+QString QFileDialogProxy::getExistingFileOrDirectory(const QString &caption,
+ const QString &identifier, bool isDirectory)
+{
+ QHash<QString, QString> autoAnswers = m_core->fileDialogAutomaticAnswers();
+ QString selectedDirectoryOrFile;
+ QString errorString;
+ if (autoAnswers.contains(identifier)) {
+ selectedDirectoryOrFile = autoAnswers.value(identifier);
+ QFileInfo fileInfo(selectedDirectoryOrFile);
+ if (isDirectory ? fileInfo.isDir() : fileInfo.isFile()) {
+ qCDebug(QInstaller::lcInstallerInstallLog).nospace() << "Automatic answer for "<< identifier
+ << ": " << selectedDirectoryOrFile;
+ } else {
+ if (isDirectory)
+ errorString = QString::fromLatin1("Automatic answer for %1: Directory '%2' not found.")
+ .arg(identifier, selectedDirectoryOrFile);
+ else
+ errorString = QString::fromLatin1("Automatic answer for %1: File '%2' not found.")
+ .arg(identifier, selectedDirectoryOrFile);
+ selectedDirectoryOrFile = QString();
+ }
+ } else if (!LoggingHandler::instance().outputRedirected()) {
+ qDebug().nospace().noquote() << identifier << ": " << caption << ": ";
+ QTextStream stream(stdin);
+ stream.readLineInto(&selectedDirectoryOrFile);
+ QFileInfo fileInfo(selectedDirectoryOrFile);
+ if (isDirectory ? !fileInfo.isDir() : !fileInfo.isFile()) {
+ if (isDirectory)
+ errorString = QString::fromLatin1("Directory '%1' not found.")
+ .arg(selectedDirectoryOrFile);
+ else
+ errorString = QString::fromLatin1("File '%1' not found.")
+ .arg(selectedDirectoryOrFile);
+ selectedDirectoryOrFile = QString();
+ }
+ } else {
+ qCDebug(QInstaller::lcInstallerInstallLog).nospace()
+ << "No answer available for "<< identifier << ": " << caption;
+
+ throw Error(tr("User input is required but the output "
+ "device is not associated with a terminal."));
+ }
+ if (!errorString.isEmpty())
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace() << errorString;
+ return selectedDirectoryOrFile;
+}
+
/*!
Constructs a script engine with \a core as parent.
*/
-ScriptEngine::ScriptEngine(PackageManagerCore *core) :
- QObject(core),
- m_guiProxy(new GuiProxy(this, this))
+ScriptEngine::ScriptEngine(PackageManagerCore *core) : QObject(core)
+ , m_guiProxy(new GuiProxy(this, this))
+ , m_core(core)
{
+ m_engine.installExtensions(QJSEngine::TranslationExtension | QJSEngine::ConsoleExtension);
QJSValue global = m_engine.globalObject();
- global.setProperty(QLatin1String("console"), m_engine.newQObject(new ConsoleProxy));
- global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy));
- const QJSValue proxy = m_engine.newQObject(new InstallerProxy(this, core));
- global.setProperty(QLatin1String("InstallerProxy"), proxy);
- global.setProperty(QLatin1String("print"), m_engine.newQObject(new ConsoleProxy)
- .property(QLatin1String("log")));
-#if QT_VERSION < 0x050400
- global.setProperty(QLatin1String("qsTr"), m_engine.newQObject(new QCoreApplicationProxy)
- .property(QStringLiteral("qsTr")));
-#else
- m_engine.installTranslatorFunctions();
-#endif
+
+ global.setProperty(QLatin1String("QFileDialog"), m_engine.newQObject(new QFileDialogProxy(core)));
global.setProperty(QLatin1String("systemInfo"), m_engine.newQObject(new SystemInfo));
global.setProperty(QLatin1String("QInstaller"), generateQInstallerObject());
global.setProperty(QLatin1String("buttons"), generateWizardButtonsObject());
global.setProperty(QLatin1String("QMessageBox"), generateMessageBoxObject());
global.setProperty(QLatin1String("QDesktopServices"), generateDesktopServicesObject());
+#ifdef Q_OS_WIN
+ global.setProperty(QLatin1String("QSettings"), generateSettingsObject());
+#endif
if (core) {
setGuiQObject(core->guiObject());
@@ -302,19 +377,14 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
global.setProperty(QLatin1String("installer"), m_engine.newQObject(new QObject));
}
global.setProperty(QLatin1String("gui"), m_engine.newQObject(m_guiProxy));
-
- global.property(QLatin1String("installer")).setProperty(QLatin1String("components"),
- proxy.property(QLatin1String("components")));
- global.property(QLatin1String("installer")).setProperty(QLatin1String("componentByName"),
- proxy.property(QLatin1String("componentByName")));
}
/*!
Creates a JavaScript object that wraps the given QObject \a object.
- Signals and slots, properties and children of \a object are
- available as properties of the created QJSValue. In addition some helper methods and properties
- are added:
+ Signals and slots, properties and children of \a object are available as properties
+ of the created QJSValue. If \a qtScriptCompat is set to \c true (default), some helper
+ methods and properties from the legacy \c QtScript module are added:
\list
\li findChild(), findChildren() recursively search for child objects with the given
@@ -323,7 +393,7 @@ ScriptEngine::ScriptEngine(PackageManagerCore *core) :
names.
\endlist
*/
-QJSValue ScriptEngine::newQObject(QObject *object)
+QJSValue ScriptEngine::newQObject(QObject *object, bool qtScriptCompat)
{
QJSValue jsValue = m_engine.newQObject(object);
if (!jsValue.isQObject())
@@ -331,6 +401,9 @@ QJSValue ScriptEngine::newQObject(QObject *object)
QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership);
+ if (!qtScriptCompat) // skip adding the extra properties
+ return jsValue;
+
// add findChild(), findChildren() methods known from QtScript
QJSValue findChild = m_engine.evaluate(
QLatin1String("(function() { return gui.findChild(this, arguments[0]); })"));
@@ -412,7 +485,7 @@ QJSValue ScriptEngine::loadInContext(const QString &context, const QString &file
// replacements of %1, %2 or %3 inside the javascript code.
const QString scriptContent = QLatin1String("(function() {")
+ scriptInjection + QString::fromUtf8(file.readAll())
- + QString::fromLatin1(";"
+ + QString::fromLatin1("\n"
" if (typeof %1 != \"undefined\")"
" return new %1;"
" else"
@@ -465,14 +538,17 @@ QJSValue ScriptEngine::callScriptMethod(const QJSValue &scriptContext, const QSt
if (!method.isCallable())
return QJSValue(QJSValue::UndefinedValue);
if (method.isError()) {
- throw Error(method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : method.toString());
+ QString errorString = method.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : method.toString();
+
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
const QJSValue result = method.call(arguments);
if (result.isError()) {
- throw Error(result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
- : result.toString());
+ QString errorString = result.toString().isEmpty() ? QString::fromLatin1("Unknown error.")
+ : result.toString();
+ throw Error(QString::fromLatin1("%1 \n%2 \"%3\"").arg(errorString, tr(scClearCacheHint), m_core->settings().localCachePath()));
}
stack.removeLast();
@@ -548,20 +624,38 @@ QJSValue ScriptEngine::generateDesktopServicesObject()
SETPROPERTY(desktopServices, PicturesLocation, QStandardPaths)
SETPROPERTY(desktopServices, TempLocation, QStandardPaths)
SETPROPERTY(desktopServices, HomeLocation, QStandardPaths)
- SETPROPERTY(desktopServices, DataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppLocalDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, CacheLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericDataLocation, QStandardPaths)
SETPROPERTY(desktopServices, RuntimeLocation, QStandardPaths)
SETPROPERTY(desktopServices, ConfigLocation, QStandardPaths)
SETPROPERTY(desktopServices, DownloadLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericCacheLocation, QStandardPaths)
SETPROPERTY(desktopServices, GenericConfigLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppDataLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, AppConfigLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, PublicShareLocation, QStandardPaths)
+ SETPROPERTY(desktopServices, TemplatesLocation, QStandardPaths)
QJSValue object = m_engine.newQObject(new QDesktopServicesProxy(this));
object.setPrototype(desktopServices); // attach the properties
return object;
}
+#ifdef Q_OS_WIN
+QJSValue ScriptEngine::generateSettingsObject()
+{
+ QJSValue settingsObject = m_engine.newArray();
+ SETPROPERTY(settingsObject, NativeFormat, QSettings)
+ SETPROPERTY(settingsObject, IniFormat, QSettings)
+ SETPROPERTY(settingsObject, Registry32Format, QSettings)
+ SETPROPERTY(settingsObject, Registry64Format, QSettings)
+ SETPROPERTY(settingsObject, InvalidFormat, QSettings)
+ return settingsObject;
+}
+#endif
+
QJSValue ScriptEngine::generateQInstallerObject()
{
// register ::WizardPage enum in the script connection
diff --git a/src/libs/installer/scriptengine.h b/src/libs/installer/scriptengine.h
index a0c64c0e4..0b43465cb 100644
--- a/src/libs/installer/scriptengine.h
+++ b/src/libs/installer/scriptengine.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -48,7 +48,7 @@ public:
explicit ScriptEngine(PackageManagerCore *core = 0);
QJSValue globalObject() const { return m_engine.globalObject(); }
- QJSValue newQObject(QObject *object);
+ QJSValue newQObject(QObject *object, bool qtScriptCompat = true);
QJSValue newArray(uint length = 0);
QJSValue evaluate(const QString &program, const QString &fileName = QString(),
int lineNumber = 1);
@@ -69,11 +69,15 @@ private:
QJSValue generateQInstallerObject();
QJSValue generateWizardButtonsObject();
QJSValue generateDesktopServicesObject();
+#ifdef Q_OS_WIN
+ QJSValue generateSettingsObject();
+#endif
private:
QJSEngine m_engine;
QHash<QString, QStringList> m_callstack;
GuiProxy *m_guiProxy;
+ PackageManagerCore *m_core;
};
}
diff --git a/src/libs/installer/scriptengine_p.h b/src/libs/installer/scriptengine_p.h
index adbe2dc0c..101d4f303 100644
--- a/src/libs/installer/scriptengine_p.h
+++ b/src/libs/installer/scriptengine_p.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2024 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,64 +29,43 @@
#ifndef SCRIPTENGINE_P_H
#define SCRIPTENGINE_P_H
-#include "component.h"
-#include "packagemanagercore.h"
-#include "packagemanagergui.h"
+#include "globals.h"
#include <QDebug>
#include <QDesktopServices>
#include <QFileDialog>
#include <QStandardPaths>
+#include <QJSValue>
namespace QInstaller {
-class ConsoleProxy : public QObject
+class PackageManagerCore;
+class PackageManagerGui;
+class ScriptEngine;
+
+class QFileDialogProxy : public QObject
{
Q_OBJECT
- Q_DISABLE_COPY(ConsoleProxy)
+ Q_DISABLE_COPY(QFileDialogProxy)
public:
- ConsoleProxy() {}
+ QFileDialogProxy(PackageManagerCore *core);
public slots :
- void log(const QString &log) { qDebug().noquote() << log; }
-};
+ QString getExistingDirectory(const QString &caption, const QString &dir,
+ const QString &identifier = QLatin1String("GetExistingDirectory"));
-class InstallerProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(InstallerProxy)
-
-public:
- InstallerProxy(ScriptEngine *engine, PackageManagerCore *core)
- : m_engine(engine), m_core(core) {}
+ QString getOpenFileName(const QString &caption, const QString &dir, const QString &filter,
+ const QString &identifier = QLatin1String("GetExistingFile"));
-public slots:
- QJSValue components() const;
- QJSValue componentByName(const QString &componentName);
+private:
+ QString getExistingFileOrDirectory(const QString &caption, const QString &identifier,
+ bool isDirectory);
private:
- ScriptEngine *m_engine;
PackageManagerCore *m_core;
};
-class QFileDialogProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(QFileDialogProxy)
-
-public:
- QFileDialogProxy() {}
-
-public slots :
- QString getExistingDirectory(const QString &caption, const QString &dir) const {
- return QFileDialog::getExistingDirectory(0, caption, dir);
- }
- QString getOpenFileName(const QString &caption, const QString &dir, const QString &filter) const {
- return QFileDialog::getOpenFileName(0, caption, dir, filter);
- }
-};
-
class QDesktopServicesProxy : public QObject
{
Q_OBJECT
@@ -118,24 +97,6 @@ private:
ScriptEngine *m_engine;
};
-#if QT_VERSION < 0x050400
-class QCoreApplicationProxy : public QObject
-{
- Q_OBJECT
- Q_DISABLE_COPY(QCoreApplicationProxy)
-
-public:
- QCoreApplicationProxy() {}
-
-public slots:
- QString qsTr(const QString &text = QString(), const QString &disambiguation = QString(), int n = -1) const
- {
- return QCoreApplication::translate(QCoreApplication::applicationName().toUtf8().constData(),
- text.toUtf8().constData(), disambiguation.toUtf8().constData(), n);
- }
-};
-#endif
-
class GuiProxy : public QObject
{
Q_OBJECT
@@ -153,7 +114,9 @@ public:
Q_INVOKABLE QString defaultButtonText(int wizardButton) const;
Q_INVOKABLE void clickButton(int wizardButton, int delayInMs = 0);
+ Q_INVOKABLE void clickButton(const QString &objectName, int delayInMs = 0) const;
Q_INVOKABLE bool isButtonEnabled(int wizardButton);
+ Q_INVOKABLE void setWizardPageButtonText(int pageId, int buttonId, const QString &buttonText);
Q_INVOKABLE void showSettingsButton(bool show);
Q_INVOKABLE void setSettingsButtonEnabled(bool enable);
@@ -186,12 +149,7 @@ private:
} // namespace QInstaller
-Q_DECLARE_METATYPE(QInstaller::ConsoleProxy*)
-Q_DECLARE_METATYPE(QInstaller::InstallerProxy*)
Q_DECLARE_METATYPE(QInstaller::QFileDialogProxy*)
Q_DECLARE_METATYPE(QInstaller::QDesktopServicesProxy*)
-#if QT_VERSION < 0x050400
-Q_DECLARE_METATYPE(QInstaller::QCoreApplicationProxy*)
-#endif
#endif // SCRIPTENGINE_H
diff --git a/src/libs/installer/selfrestartoperation.cpp b/src/libs/installer/selfrestartoperation.cpp
index 586e5bb0b..360dc60b2 100644
--- a/src/libs/installer/selfrestartoperation.cpp
+++ b/src/libs/installer/selfrestartoperation.cpp
@@ -33,6 +33,12 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::SelfRestartOperation
+ \internal
+*/
+
SelfRestartOperation::SelfRestartOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -55,7 +61,7 @@ bool SelfRestartOperation::performOperation()
if (!core->isMaintainer()) {
setError(UserDefinedError);
- setErrorString(tr("Self Restart: Only valid within updater or packagemanager mode."));
+ setErrorString(tr("Self Restart: Only valid within updater or package manager mode."));
return false;
}
diff --git a/src/libs/installer/selfrestartoperation.h b/src/libs/installer/selfrestartoperation.h
index 7adccd183..0554df2af 100644
--- a/src/libs/installer/selfrestartoperation.h
+++ b/src/libs/installer/selfrestartoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT SelfRestartOperation : public Operation
public:
explicit SelfRestartOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
}
diff --git a/src/libs/installer/serverauthenticationdialog.cpp b/src/libs/installer/serverauthenticationdialog.cpp
index 1b39e6980..3c995a810 100644
--- a/src/libs/installer/serverauthenticationdialog.cpp
+++ b/src/libs/installer/serverauthenticationdialog.cpp
@@ -34,6 +34,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::ServerAuthenticationDialog
+ \internal
+*/
+
ServerAuthenticationDialog::ServerAuthenticationDialog(const QString &m, const FileTaskItem &item)
: ui(new Ui::ServerAuthenticationDialog)
{
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
index 3aa0baf18..e18f63689 100644
--- a/src/libs/installer/settings.cpp
+++ b/src/libs/installer/settings.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,9 +31,14 @@
#include "qinstallerglobal.h"
#include "repository.h"
#include "repositorycategory.h"
+#include "globals.h"
+#include "fileutils.h"
+#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QStringList>
+#include <QtCore/QStandardPaths>
+#include <QtCore/QUuid>
#include <QtGui/QFontMetrics>
#include <QtWidgets/QApplication>
@@ -42,21 +47,29 @@
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::Settings
+ \internal
+*/
+
+/*!
+ \typedef QInstaller::RepoHash
+
+ Synonym for QHash<QString, QPair<Repository, Repository> >. Describes a repository
+ update with the supported key strings being \e{replace}, \e{remove}, and \e{add}.
+*/
+
static const QLatin1String scInstallerApplicationIcon("InstallerApplicationIcon");
static const QLatin1String scInstallerWindowIcon("InstallerWindowIcon");
-static const QLatin1String scLogo("Logo");
static const QLatin1String scPrefix("Prefix");
-static const QLatin1String scWatermark("Watermark");
-static const QLatin1String scBanner("Banner");
static const QLatin1String scProductUrl("ProductUrl");
-static const QLatin1String scBackground("Background");
static const QLatin1String scAdminTargetDir("AdminTargetDir");
static const QLatin1String scMaintenanceToolName("MaintenanceToolName");
static const QLatin1String scUserRepositories("UserRepositories");
static const QLatin1String scTmpRepositories("TemporaryRepositories");
static const QLatin1String scMaintenanceToolIniFile("MaintenanceToolIniFile");
-static const QLatin1String scRemoteRepositories("RemoteRepositories");
-static const QLatin1String scRepositoryCategories("RepositoryCategories");
+static const QLatin1String scMaintenanceToolAlias("MaintenanceToolAlias");
static const QLatin1String scDependsOnLocalInstallerBinary("DependsOnLocalInstallerBinary");
static const QLatin1String scTranslations("Translations");
static const QLatin1String scCreateLocalRepository("CreateLocalRepository");
@@ -66,6 +79,8 @@ static const QLatin1String scFtpProxy("FtpProxy");
static const QLatin1String scHttpProxy("HttpProxy");
static const QLatin1String scProxyType("ProxyType");
+static const QLatin1String scLocalCachePath("LocalCachePath");
+
const char scControlScript[] = "ControlScript";
template <typename T>
@@ -84,12 +99,13 @@ static void raiseError(QXmlStreamReader &reader, const QString &error, Settings:
} else {
QFile *xmlFile = qobject_cast<QFile*>(reader.device());
if (xmlFile) {
- qWarning().noquote().nospace()
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote().nospace()
<< "Ignoring following settings reader error in " << xmlFile->fileName()
<< ", line " << reader.lineNumber() << ", column " << reader.columnNumber()
<< ": " << error;
} else {
- qWarning("Ignoring following settings reader error: %s", qPrintable(error));
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Ignoring following settings reader error: "
+ << qPrintable(error);
}
}
}
@@ -122,7 +138,7 @@ static QStringList readArgumentAttributes(QXmlStreamReader &reader, Settings::Pa
if (reader.isWhitespace())
continue;
arguments.append(reader.text().toString().split(QRegularExpression(QLatin1String("\\s+")),
- QString::SkipEmptyParts));
+ Qt::SkipEmptyParts));
}
break;
case QXmlStreamReader::EndElement: {
@@ -135,6 +151,26 @@ static QStringList readArgumentAttributes(QXmlStreamReader &reader, Settings::Pa
return arguments;
}
+static QMap<QString, QVariant> readProductImages(QXmlStreamReader &reader)
+{
+ QMap<QString, QVariant> productImages;
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("ProductImage")) {
+ QString key = QString();
+ QString value = QString();
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Image")) {
+ key = reader.readElementText();
+ } else if (reader.name() == QLatin1String("Url")) {
+ value = reader.readElementText();
+ }
+ }
+ productImages.insert(key, value);
+ }
+ }
+ return productImages;
+}
+
static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefault, Settings::ParseMode parseMode,
QString *displayName = nullptr, bool *preselected = nullptr,
QString *tooltip = nullptr)
@@ -219,7 +255,7 @@ public:
: m_replacementRepos(false)
{}
- QVariantHash m_data;
+ QMultiHash<QString, QVariant> m_data;
bool m_replacementRepos;
QString absolutePathFromKey(const QString &key, const QString &suffix = QString()) const
@@ -282,20 +318,23 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
elementList << scName << scVersion << scTitle << scPublisher << scProductUrl
<< scTargetDir << scAdminTargetDir
<< scInstallerApplicationIcon << scInstallerWindowIcon
- << scLogo << scWatermark << scBanner << scBackground
- << scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scRemoveTargetDir
+ << scLogo << scWatermark << scBanner << scBackground << scPageListPixmap << scAliasDefinitionsFile
+ << scStartMenuDir << scMaintenanceToolName << scMaintenanceToolIniFile << scMaintenanceToolAlias
+ << scRemoveTargetDir << scLocalCacheDir << scPersistentLocalCache
<< scRunProgram << scRunProgramArguments << scRunProgramDescription
<< scDependsOnLocalInstallerBinary
- << scAllowSpaceInPath << scAllowNonAsciiCharacters << scDisableAuthorizationFallback
+ << scAllowSpaceInPath << scAllowNonAsciiCharacters << scAllowRepositoriesForOfflineInstaller
+ << scDisableAuthorizationFallback << scDisableCommandLineInterface
<< scWizardStyle << scStyleSheet << scTitleColor
- << scWizardDefaultWidth << scWizardDefaultHeight
+ << scWizardDefaultWidth << scWizardDefaultHeight << scWizardMinimumWidth << scWizardMinimumHeight
+ << scWizardShowPageList << scProductImages
<< scRepositorySettingsPageVisible << scTargetConfigurationFile
<< scRemoteRepositories << scTranslations << scUrlQueryString << QLatin1String(scControlScript)
<< scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify << scAllowUnstableComponents
<< scSaveDefaultRepositories << scRepositoryCategories;
Settings s;
- s.d->m_data.insert(scPrefix, prefix);
+ s.d->m_data.replace(scPrefix, prefix);
while (reader.readNextStartElement()) {
const QString name = reader.name().toString();
if (!elementList.contains(name))
@@ -310,9 +349,11 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
reader.raiseError(QString::fromLatin1("Element \"%1\" has been defined before.").arg(name));
if (name == scTranslations) {
- s.setTranslations(readArgumentAttributes(reader, parseMode, QLatin1String("Translation"), true));
+ s.setTranslations(readArgumentAttributes(reader, parseMode, QLatin1String("Translation"), false));
} else if (name == scRunProgramArguments) {
s.setRunProgramArguments(readArgumentAttributes(reader, parseMode, QLatin1String("Argument")));
+ } else if (name == scProductImages) {
+ s.setProductImages(readProductImages(reader));
} else if (name == scRemoteRepositories) {
s.addDefaultRepositories(readRepositories(reader, true, parseMode));
} else if (name == scRepositoryCategories) {
@@ -322,7 +363,7 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
s.setRepositoryCategoryDisplayName(repositoryCategoryName);
}
} else {
- s.d->m_data.insert(name, reader.readElementText(QXmlStreamReader::SkipChildElements));
+ s.d->m_data.replace(name, reader.readElementText(QXmlStreamReader::SkipChildElements));
}
}
if (reader.error() != QXmlStreamReader::NoError) {
@@ -337,39 +378,40 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
// Add some possible missing values
if (!s.d->m_data.contains(scInstallerApplicationIcon))
- s.d->m_data.insert(scInstallerApplicationIcon, QLatin1String(":/installer"));
+ s.d->m_data.replace(scInstallerApplicationIcon, QLatin1String(":/installer"));
if (!s.d->m_data.contains(scInstallerWindowIcon)) {
- s.d->m_data.insert(scInstallerWindowIcon,
+ s.d->m_data.replace(scInstallerWindowIcon,
QString(QLatin1String(":/installer") + s.systemIconSuffix()));
}
if (!s.d->m_data.contains(scRemoveTargetDir))
- s.d->m_data.insert(scRemoveTargetDir, scTrue);
+ s.d->m_data.replace(scRemoveTargetDir, scTrue);
if (s.d->m_data.value(scMaintenanceToolName).toString().isEmpty()) {
- s.d->m_data.insert(scMaintenanceToolName,
+ s.d->m_data.replace(scMaintenanceToolName,
// TODO: Remove deprecated 'UninstallerName'.
s.d->m_data.value(QLatin1String("UninstallerName"), QLatin1String("maintenancetool"))
.toString());
}
+
if (s.d->m_data.value(scTargetConfigurationFile).toString().isEmpty())
- s.d->m_data.insert(scTargetConfigurationFile, QLatin1String("components.xml"));
+ s.d->m_data.replace(scTargetConfigurationFile, QLatin1String("components.xml"));
if (s.d->m_data.value(scMaintenanceToolIniFile).toString().isEmpty()) {
- s.d->m_data.insert(scMaintenanceToolIniFile,
+ s.d->m_data.replace(scMaintenanceToolIniFile,
// TODO: Remove deprecated 'UninstallerIniFile'.
s.d->m_data.value(QLatin1String("UninstallerIniFile"), QString(s.maintenanceToolName()
+ QLatin1String(".ini"))).toString());
}
if (!s.d->m_data.contains(scDependsOnLocalInstallerBinary))
- s.d->m_data.insert(scDependsOnLocalInstallerBinary, false);
+ s.d->m_data.replace(scDependsOnLocalInstallerBinary, false);
if (!s.d->m_data.contains(scRepositorySettingsPageVisible))
- s.d->m_data.insert(scRepositorySettingsPageVisible, true);
+ s.d->m_data.replace(scRepositorySettingsPageVisible, true);
if (!s.d->m_data.contains(scCreateLocalRepository))
- s.d->m_data.insert(scCreateLocalRepository, false);
+ s.d->m_data.replace(scCreateLocalRepository, false);
if (!s.d->m_data.contains(scInstallActionColumnVisible))
- s.d->m_data.insert(scInstallActionColumnVisible, false);
+ s.d->m_data.replace(scInstallActionColumnVisible, false);
if (!s.d->m_data.contains(scAllowUnstableComponents))
- s.d->m_data.insert(scAllowUnstableComponents, false);
+ s.d->m_data.replace(scAllowUnstableComponents, false);
if (!s.d->m_data.contains(scSaveDefaultRepositories))
- s.d->m_data.insert(scSaveDefaultRepositories, true);
+ s.d->m_data.replace(scSaveDefaultRepositories, true);
return s;
}
@@ -418,6 +460,11 @@ QString Settings::background() const
return d->absolutePathFromKey(scBackground);
}
+QString Settings::pageListPixmap() const
+{
+ return d->absolutePathFromKey(scPageListPixmap);
+}
+
QString Settings::wizardStyle() const
{
return d->m_data.value(scWizardStyle).toString();
@@ -438,11 +485,11 @@ static int lengthToInt(const QVariant &variant)
QString length = variant.toString().trimmed();
if (length.endsWith(QLatin1String("em"), Qt::CaseInsensitive)) {
length.chop(2);
- return qRound(length.toDouble() * QApplication::fontMetrics().height());
+ return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).height());
}
if (length.endsWith(QLatin1String("ex"), Qt::CaseInsensitive)) {
length.chop(2);
- return qRound(length.toDouble() * QApplication::fontMetrics().xHeight());
+ return qRound(length.toDouble() * QFontMetricsF(QApplication::font()).xHeight());
}
if (length.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
length.chop(2);
@@ -452,7 +499,10 @@ static int lengthToInt(const QVariant &variant)
int Settings::wizardDefaultWidth() const
{
- return lengthToInt(d->m_data.value(scWizardDefaultWidth));
+ // Add a bit more sensible default width in case the page list widget is shown
+ // as it can take quite a lot horizontal space. A vendor can always override
+ // the default value.
+ return lengthToInt(d->m_data.value(scWizardDefaultWidth, wizardShowPageList() ? 800 : 0));
}
int Settings::wizardDefaultHeight() const
@@ -460,6 +510,40 @@ int Settings::wizardDefaultHeight() const
return lengthToInt(d->m_data.value(scWizardDefaultHeight));
}
+int Settings::wizardMinimumWidth() const
+{
+ return lengthToInt(d->m_data.value(scWizardMinimumWidth));
+}
+
+int Settings::wizardMinimumHeight() const
+{
+ return lengthToInt(d->m_data.value(scWizardMinimumHeight));
+}
+
+bool Settings::wizardShowPageList() const
+{
+ return d->m_data.value(scWizardShowPageList, true).toBool();
+}
+
+QMap<QString, QVariant> Settings::productImages() const
+{
+ const QVariant variant = d->m_data.value(scProductImages);
+ QMap<QString, QVariant> imagePaths;
+ if (variant.canConvert<QVariantMap>())
+ imagePaths = variant.toMap();
+ return imagePaths;
+}
+
+void Settings::setProductImages(const QMap<QString, QVariant> &images)
+{
+ d->m_data.insert(scProductImages, QVariant::fromValue(images));
+}
+
+QString Settings::aliasDefinitionsFile() const
+{
+ return d->absolutePathFromKey(scAliasDefinitionsFile);
+}
+
QString Settings::installerApplicationIcon() const
{
return d->absolutePathFromKey(scInstallerApplicationIcon, systemIconSuffix());
@@ -496,6 +580,11 @@ QString Settings::maintenanceToolIniFile() const
return d->m_data.value(scMaintenanceToolIniFile).toString();
}
+QString Settings::maintenanceToolAlias() const
+{
+ return d->m_data.value(scMaintenanceToolAlias).toString();
+}
+
QString Settings::runProgram() const
{
return d->m_data.value(scRunProgram).toString();
@@ -503,7 +592,7 @@ QString Settings::runProgram() const
QStringList Settings::runProgramArguments() const
{
- const QVariant variant = d->m_data.values(scRunProgramArguments);
+ const QVariant variant = d->m_data.value(scRunProgramArguments);
if (variant.canConvert<QStringList>())
return variant.value<QStringList>();
return QStringList();
@@ -511,7 +600,7 @@ QStringList Settings::runProgramArguments() const
void Settings::setRunProgramArguments(const QStringList &arguments)
{
- d->m_data.insert(scRunProgramArguments, arguments);
+ d->m_data.replace(scRunProgramArguments, arguments);
}
@@ -560,11 +649,21 @@ bool Settings::allowNonAsciiCharacters() const
return d->m_data.value(scAllowNonAsciiCharacters, false).toBool();
}
+bool Settings::allowRepositoriesForOfflineInstaller() const
+{
+ return d->m_data.value(scAllowRepositoriesForOfflineInstaller, true).toBool();
+}
+
bool Settings::disableAuthorizationFallback() const
{
return d->m_data.value(scDisableAuthorizationFallback, false).toBool();
}
+bool Settings::disableCommandLineInterface() const
+{
+ return d->m_data.value(scDisableCommandLineInterface, false).toBool();
+}
+
bool Settings::dependsOnLocalInstallerBinary() const
{
return d->m_data.value(scDependsOnLocalInstallerBinary).toBool();
@@ -613,7 +712,7 @@ void Settings::setDefaultRepositories(const QSet<Repository> &repositories)
void Settings::addDefaultRepositories(const QSet<Repository> &repositories)
{
foreach (const Repository &repository, repositories)
- d->m_data.insertMulti(scRepositories, QVariant().fromValue(repository));
+ d->m_data.insert(scRepositories, QVariant().fromValue(repository));
}
void Settings::setRepositoryCategories(const QSet<RepositoryCategory> &repositories)
@@ -625,7 +724,7 @@ void Settings::setRepositoryCategories(const QSet<RepositoryCategory> &repositor
void Settings::addRepositoryCategories(const QSet<RepositoryCategory> &repositories)
{
foreach (const RepositoryCategory &repository, repositories)
- d->m_data.insertMulti(scRepositoryCategories, QVariant().fromValue(repository));
+ d->m_data.insert(scRepositoryCategories, QVariant().fromValue(repository));
}
Settings::Update Settings::updateRepositoryCategories(const RepoHash &updates)
@@ -650,13 +749,13 @@ Settings::Update Settings::updateRepositoryCategories(const RepoHash &updates)
}
}
if (update) {
- categories = categoriesList.toSet();
+ categories = QSet<RepositoryCategory>(categoriesList.begin(), categoriesList.end());
setRepositoryCategories(categories);
}
return update ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
}
-static bool apply(const RepoHash &updates, QHash<QUrl, Repository> *reposToUpdate)
+static bool apply(const RepoHash &updates, QMultiHash<QUrl, Repository> *reposToUpdate)
{
bool update = false;
QList<QPair<Repository, Repository> > values = updates.values(QLatin1String("replace"));
@@ -694,15 +793,17 @@ Settings::Update Settings::updateDefaultRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> defaultRepos;
+ QMultiHash <QUrl, Repository> defaultRepos;
foreach (const QVariant &variant, d->m_data.values(scRepositories)) {
const Repository repository = variant.value<Repository>();
defaultRepos.insert(repository.url(), repository);
}
const bool updated = apply(updates, &defaultRepos);
- if (updated)
- setDefaultRepositories(defaultRepos.values().toSet());
+ if (updated) {
+ const QList<Repository> repositoriesList = defaultRepos.values();
+ setDefaultRepositories(QSet<Repository>(repositoriesList.begin(), repositoriesList.end()));
+ }
return updated ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
}
@@ -721,7 +822,7 @@ void Settings::addTemporaryRepositories(const QSet<Repository> &repositories, bo
{
d->m_replacementRepos = replace;
foreach (const Repository &repository, repositories)
- d->m_data.insertMulti(scTmpRepositories, QVariant().fromValue(repository));
+ d->m_data.insert(scTmpRepositories, QVariant().fromValue(repository));
}
QSet<Repository> Settings::userRepositories() const
@@ -738,7 +839,7 @@ void Settings::setUserRepositories(const QSet<Repository> &repositories)
void Settings::addUserRepositories(const QSet<Repository> &repositories)
{
foreach (const Repository &repository, repositories)
- d->m_data.insertMulti(scUserRepositories, QVariant().fromValue(repository));
+ d->m_data.insert(scUserRepositories, QVariant().fromValue(repository));
}
Settings::Update Settings::updateUserRepositories(const RepoHash &updates)
@@ -746,15 +847,17 @@ Settings::Update Settings::updateUserRepositories(const RepoHash &updates)
if (updates.isEmpty())
return Settings::NoUpdatesApplied;
- QHash <QUrl, Repository> reposToUpdate;
+ QMultiHash <QUrl, Repository> reposToUpdate;
foreach (const QVariant &variant, d->m_data.values(scUserRepositories)) {
const Repository repository = variant.value<Repository>();
reposToUpdate.insert(repository.url(), repository);
}
const bool updated = apply(updates, &reposToUpdate);
- if (updated)
- setUserRepositories(reposToUpdate.values().toSet());
+ if (updated) {
+ const QList<Repository> repositoriesList = reposToUpdate.values();
+ setUserRepositories(QSet<Repository>(repositoriesList.begin(), repositoriesList.end()));
+ }
return updated ? Settings::UpdatesApplied : Settings::NoUpdatesApplied;
}
@@ -781,7 +884,41 @@ bool Settings::repositorySettingsPageVisible() const
void Settings::setRepositorySettingsPageVisible(bool visible)
{
- d->m_data.insert(scRepositorySettingsPageVisible, visible);
+ d->m_data.replace(scRepositorySettingsPageVisible, visible);
+}
+
+bool Settings::persistentLocalCache() const
+{
+ return d->m_data.value(scPersistentLocalCache, true).toBool();
+}
+
+void Settings::setPersistentLocalCache(bool enable)
+{
+ d->m_data.replace(scPersistentLocalCache, enable);
+}
+
+QString Settings::localCacheDir() const
+{
+ const QString fallback = QLatin1String("qt-installer-framework") + QDir::separator()
+ + QUuid::createUuidV3(QUuid(), applicationName()).toString(QUuid::WithoutBraces);
+ return d->m_data.value(scLocalCacheDir, fallback).toString();
+}
+
+void Settings::setLocalCacheDir(const QString &dir)
+{
+ d->m_data.replace(scLocalCacheDir, dir);
+}
+
+QString Settings::localCachePath() const
+{
+ const QString fallback = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)
+ + QDir::separator() + localCacheDir();
+ return d->m_data.value(scLocalCachePath, fallback).toString();
+}
+
+void Settings::setLocalCachePath(const QString &path)
+{
+ d->m_data.replace(scLocalCachePath, path);
}
Settings::ProxyType Settings::proxyType() const
@@ -791,7 +928,7 @@ Settings::ProxyType Settings::proxyType() const
void Settings::setProxyType(Settings::ProxyType type)
{
- d->m_data.insert(scProxyType, type);
+ d->m_data.replace(scProxyType, type);
}
QNetworkProxy Settings::ftpProxy() const
@@ -804,7 +941,7 @@ QNetworkProxy Settings::ftpProxy() const
void Settings::setFtpProxy(const QNetworkProxy &proxy)
{
- d->m_data.insert(scFtpProxy, QVariant::fromValue(proxy));
+ d->m_data.replace(scFtpProxy, QVariant::fromValue(proxy));
}
QNetworkProxy Settings::httpProxy() const
@@ -817,12 +954,12 @@ QNetworkProxy Settings::httpProxy() const
void Settings::setHttpProxy(const QNetworkProxy &proxy)
{
- d->m_data.insert(scHttpProxy, QVariant::fromValue(proxy));
+ d->m_data.replace(scHttpProxy, QVariant::fromValue(proxy));
}
QStringList Settings::translations() const
{
- const QVariant variant = d->m_data.values(scTranslations);
+ const QVariant variant = d->m_data.value(scTranslations);
if (variant.canConvert<QStringList>())
return variant.value<QStringList>();
return QStringList();
@@ -830,7 +967,7 @@ QStringList Settings::translations() const
void Settings::setTranslations(const QStringList &translations)
{
- d->m_data.insert(scTranslations, translations);
+ d->m_data.replace(scTranslations, translations);
}
QString Settings::controlScript() const
@@ -850,7 +987,7 @@ bool Settings::allowUnstableComponents() const
void Settings::setAllowUnstableComponents(bool allow)
{
- d->m_data.insert(scAllowUnstableComponents, allow);
+ d->m_data.replace(scAllowUnstableComponents, allow);
}
bool Settings::saveDefaultRepositories() const
@@ -860,16 +997,16 @@ bool Settings::saveDefaultRepositories() const
void Settings::setSaveDefaultRepositories(bool save)
{
- d->m_data.insert(scSaveDefaultRepositories, save);
+ d->m_data.replace(scSaveDefaultRepositories, save);
}
QString Settings::repositoryCategoryDisplayName() const
{
QString displayName = d->m_data.value(QLatin1String(scRepositoryCategoryDisplayName)).toString();
- return displayName.isEmpty() ? tr("Select Package Categories") : displayName;
+ return displayName.isEmpty() ? tr("Categories") : displayName;
}
void Settings::setRepositoryCategoryDisplayName(const QString& name)
{
- d->m_data.insert(scRepositoryCategoryDisplayName, name);
+ d->m_data.replace(scRepositoryCategoryDisplayName, name);
}
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index fff2324f1..85b59869c 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,7 +42,7 @@
namespace QInstaller {
class Repository;
-typedef QHash<QString, QPair<Repository, Repository> > RepoHash;
+typedef QMultiHash<QString, QPair<Repository, Repository> > RepoHash;
class INSTALLER_EXPORT Settings
{
@@ -80,6 +80,7 @@ public:
QString watermark() const;
QString banner() const;
QString background() const;
+ QString pageListPixmap() const;
QString installerApplicationIcon() const;
QString installerWindowIcon() const;
QString systemIconSuffix() const;
@@ -88,6 +89,13 @@ public:
QString titleColor() const;
int wizardDefaultWidth() const;
int wizardDefaultHeight() const;
+ int wizardMinimumWidth() const;
+ int wizardMinimumHeight() const;
+ bool wizardShowPageList() const;
+ QMap<QString, QVariant> productImages() const;
+ void setProductImages(const QMap<QString, QVariant> &images);
+
+ QString aliasDefinitionsFile() const;
QString applicationName() const;
QString version() const;
@@ -104,6 +112,7 @@ public:
QString removeTargetDir() const;
QString maintenanceToolName() const;
QString maintenanceToolIniFile() const;
+ QString maintenanceToolAlias() const;
QString configurationFileName() const;
@@ -136,7 +145,10 @@ public:
bool allowSpaceInPath() const;
bool allowNonAsciiCharacters() const;
+ bool allowRepositoriesForOfflineInstaller() const;
+
bool disableAuthorizationFallback() const;
+ bool disableCommandLineInterface() const;
bool containsValue(const QString &key) const;
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
@@ -145,6 +157,15 @@ public:
bool repositorySettingsPageVisible() const;
void setRepositorySettingsPageVisible(bool visible);
+ bool persistentLocalCache() const;
+ void setPersistentLocalCache(bool enable);
+
+ QString localCacheDir() const;
+ void setLocalCacheDir(const QString &dir);
+
+ QString localCachePath() const;
+ void setLocalCachePath(const QString &path);
+
Settings::ProxyType proxyType() const;
void setProxyType(Settings::ProxyType type);
diff --git a/src/libs/installer/settingsoperation.cpp b/src/libs/installer/settingsoperation.cpp
index ccded868d..a74161178 100644
--- a/src/libs/installer/settingsoperation.cpp
+++ b/src/libs/installer/settingsoperation.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,16 +29,24 @@
#include "packagemanagercore.h"
#include "updateoperations.h"
#include "qsettingswrapper.h"
+#include "globals.h"
#include <QDir>
#include <QDebug>
using namespace QInstaller;
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::SettingsOperation
+ \internal
+*/
+
SettingsOperation::SettingsOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
setName(QLatin1String("Settings"));
+ setRequiresUnreplacedVariables(true);
}
void SettingsOperation::backup()
@@ -75,7 +83,7 @@ bool SettingsOperation::checkArguments()
if (!possibleMethodValues.contains(method)) {
setError(InvalidArguments);
setErrorString(tr("Current method argument calling \"%1\" with arguments \"%2\" is not "
- "supported. Please use set, remove, add_array_value or remove_array_value.").arg(name(),
+ "supported. Please use set, remove, add_array_value, or remove_array_value.").arg(name(),
arguments().join(QLatin1String("; "))));
return false;
}
@@ -97,10 +105,17 @@ bool SettingsOperation::performOperation()
if (!checkArguments())
return false;
- const QString path = argumentKeyValue(QLatin1String("path"));
+ QString path = argumentKeyValue(QLatin1String("path"));
const QString method = argumentKeyValue(QLatin1String("method"));
const QString key = argumentKeyValue(QLatin1String("key"));
- const QString aValue = argumentKeyValue(QLatin1String("value"));
+ QString aValue = argumentKeyValue(QLatin1String("value"));
+
+ if (requiresUnreplacedVariables()) {
+ if (PackageManagerCore *const core = packageManager()) {
+ path = core->replaceVariables(path);
+ aValue = core->replaceVariables(aValue);
+ }
+ }
// use MkdirOperation to get the path so it can remove it with MkdirOperation::undoOperation later
KDUpdater::MkdirOperation mkDirOperation;
@@ -113,7 +128,7 @@ bool SettingsOperation::performOperation()
}
setValue(QLatin1String("createddir"), mkDirOperation.value(QLatin1String("createddir")));
- QSettingsWrapper settings(path, QSettingsWrapper::IniFormat);
+ QSettingsWrapper settings(path, QSettings::IniFormat);
if (method == QLatin1String("set"))
settings.setValue(key, aValue);
else if (method == QLatin1String("remove"))
@@ -145,17 +160,28 @@ bool SettingsOperation::undoOperation()
{
if (!checkArguments())
return false;
- const QString path = argumentKeyValue(QLatin1String("path"));
+ QString path = argumentKeyValue(QLatin1String("path"));
const QString method = argumentKeyValue(QLatin1String("method"));
const QString key = argumentKeyValue(QLatin1String("key"));
- const QString aValue = argumentKeyValue(QLatin1String("value"));
+ QString aValue = argumentKeyValue(QLatin1String("value"));
if (method.startsWith(QLatin1String("remove")))
return true;
+ if (requiresUnreplacedVariables()) {
+ if (PackageManagerCore *const core = packageManager()) {
+ path = core->replaceVariables(path);
+ aValue = core->replaceVariables(aValue);
+ // Check is different settings file is wanted to be used.
+ // Old settings file name should be set to variable <variable_name>_OLD,
+ // and new path is then read from <variable_name> variable.
+ variableReplacement(&path);
+ }
+ }
+
bool cleanUp = false;
{ // kill the scope to kill settings object, else remove file will not work
- QSettingsWrapper settings(path, QSettingsWrapper::IniFormat);
+ QSettingsWrapper settings(path, QSettings::IniFormat);
if (method == QLatin1String("set")) {
settings.remove(key);
} else if (method == QLatin1String("add_array_value")) {
@@ -178,14 +204,14 @@ bool SettingsOperation::undoOperation()
if (cleanUp) {
QFile settingsFile(path);
if (!settingsFile.remove())
- qWarning().noquote() << settingsFile.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << settingsFile.errorString();
if (!value(QLatin1String("createddir")).toString().isEmpty()) {
KDUpdater::MkdirOperation mkDirOperation(packageManager());
mkDirOperation.setArguments(QStringList() << QFileInfo(path).absolutePath());
mkDirOperation.setValue(QLatin1String("createddir"), value(QLatin1String("createddir")));
if (!mkDirOperation.undoOperation()) {
- qWarning().noquote() << mkDirOperation.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << mkDirOperation.errorString();
}
}
}
diff --git a/src/libs/installer/settingsoperation.h b/src/libs/installer/settingsoperation.h
index 0b94ea015..97655a85c 100644
--- a/src/libs/installer/settingsoperation.h
+++ b/src/libs/installer/settingsoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,10 +39,10 @@ class INSTALLER_EXPORT SettingsOperation : public Operation
public:
explicit SettingsOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
private:
bool checkArguments();
diff --git a/src/libs/installer/simplemovefileoperation.cpp b/src/libs/installer/simplemovefileoperation.cpp
index d5cbde2ee..5bbbdabb4 100644
--- a/src/libs/installer/simplemovefileoperation.cpp
+++ b/src/libs/installer/simplemovefileoperation.cpp
@@ -33,6 +33,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::SimpleMoveFileOperation
+ \internal
+*/
+
SimpleMoveFileOperation::SimpleMoveFileOperation(PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -87,6 +93,8 @@ bool SimpleMoveFileOperation::performOperation()
bool SimpleMoveFileOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
const QString source = arguments().at(0);
const QString target = arguments().at(1);
diff --git a/src/libs/installer/simplemovefileoperation.h b/src/libs/installer/simplemovefileoperation.h
index 26cb5c462..850468b2f 100644
--- a/src/libs/installer/simplemovefileoperation.h
+++ b/src/libs/installer/simplemovefileoperation.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -42,10 +42,10 @@ class INSTALLER_EXPORT SimpleMoveFileOperation : public QObject, public Operatio
public:
explicit SimpleMoveFileOperation(PackageManagerCore *core);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
Q_SIGNALS:
void outputTextChanged(const QString &progress);
diff --git a/src/libs/installer/sysinfo_win.cpp b/src/libs/installer/sysinfo_win.cpp
index 508ce7a6e..e5df8e35f 100644
--- a/src/libs/installer/sysinfo_win.cpp
+++ b/src/libs/installer/sysinfo_win.cpp
@@ -64,7 +64,7 @@ VolumeInfo updateVolumeSizeInformation(const VolumeInfo &info)
return update;
}
-/*!
+/*
Returns a list of volume info objects that are mounted as network drive shares.
*/
QList<VolumeInfo> networkVolumeInfosFromMountPoints()
@@ -95,7 +95,7 @@ QList<VolumeInfo> networkVolumeInfosFromMountPoints()
return volumes;
}
-/*!
+/*
Returns a list of volume info objects based on the given \a volumeGUID. The function also solves mounted
volume folder paths. It does not return any network drive shares.
*/
@@ -109,7 +109,7 @@ QList<VolumeInfo> localVolumeInfosFromMountPoints(PTCHAR volumeGUID)
TCHAR volumeNames[MAX_PATH + 1] = { 0 };
if (GetVolumePathNamesForVolumeName(volumeGUID, volumeNames, MAX_PATH, &bufferSize)) {
QStringList mountedPaths = QString::fromWCharArray(volumeNames, bufferSize).split(QLatin1Char(char(0)),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
foreach (const QString &mountedPath, mountedPaths) {
VolumeInfo info;
info.setMountPath(mountedPath);
@@ -150,10 +150,10 @@ QList<VolumeInfo> mountedVolumes()
bool pathIsOnLocalDevice(const QString &path)
{
- if (!QFileInfo(path).exists())
+ if (!QFileInfo::exists(path))
return false;
- if (path.startsWith(QLatin1String("\\\\")))
+ if (path.startsWith(QLatin1String("//")))
return false;
QDir dir(path);
diff --git a/src/libs/installer/systeminfo.cpp b/src/libs/installer/systeminfo.cpp
index 6a1976c4a..aed02c569 100644
--- a/src/libs/installer/systeminfo.cpp
+++ b/src/libs/installer/systeminfo.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -55,6 +55,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent)
\list
\li "i386"
\li "x86_64"
+ \li "arm64"
\endlist
\note This function depends on what the OS will report and may not detect the actual CPU
@@ -62,7 +63,7 @@ SystemInfo::SystemInfo(QObject *parent) : QObject(parent)
OS running on a 64-bit CPU is usually unable to determine whether the CPU is actually capable
of running 64-bit programs.
- \sa QSysInfo::currentCpuArchitecture()
+ \sa QSysInfo::currentCpuArchitecture() buildCpuArchitecture()
*/
QString SystemInfo::currentCpuArchitecture() const
{
@@ -70,6 +71,29 @@ QString SystemInfo::currentCpuArchitecture() const
}
/*!
+ \property SystemInfo::buildCpuArchitecture
+
+ The architecture of the CPU that the application was compiled for, in text format.
+
+ Possible values include:
+ \list
+ \li "i386"
+ \li "x86_64"
+ \li "arm64"
+ \endlist
+
+ \note Note that this may not match the actual CPU that the application is running on if
+ there's an emulation layer or if the CPU supports multiple architectures (like x86-64
+ processors supporting i386 applications). To detect that, use \c installer.currentCpuArchitecture()
+
+ \sa QSysInfo::buildCpuArchitecture() currentCpuArchitecture()
+*/
+QString SystemInfo::buildCpuArchitecture() const
+{
+ return QSysInfo::buildCpuArchitecture();
+}
+
+/*!
\property SystemInfo::kernelType
The type of the operating system kernel the installer was compiled for. It is also the
@@ -124,7 +148,7 @@ QString SystemInfo::kernelVersion() const
\list
\li "windows"
\li "opensuse" (for the Linux openSUSE distribution)
- \li "osx"
+ \li "macos"
\endlist
\sa QSysInfo::productType()
diff --git a/src/libs/installer/systeminfo.h b/src/libs/installer/systeminfo.h
index c5451605e..a1393397a 100644
--- a/src/libs/installer/systeminfo.h
+++ b/src/libs/installer/systeminfo.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -39,6 +39,7 @@ class SystemInfo : public QObject
Q_DISABLE_COPY(SystemInfo)
Q_PROPERTY(QString currentCpuArchitecture READ currentCpuArchitecture CONSTANT)
+ Q_PROPERTY(QString buildCpuArchitecture READ buildCpuArchitecture CONSTANT)
Q_PROPERTY(QString kernelType READ kernelType CONSTANT)
Q_PROPERTY(QString kernelVersion READ kernelVersion CONSTANT)
Q_PROPERTY(QString productType READ productType CONSTANT)
@@ -49,7 +50,7 @@ public:
explicit SystemInfo(QObject *parent = 0);
QString currentCpuArchitecture() const;
-
+ QString buildCpuArchitecture() const;
QString kernelType() const;
QString kernelVersion() const;
QString productType() const;
diff --git a/src/libs/installer/testrepository.cpp b/src/libs/installer/testrepository.cpp
index 4ee2d41ae..10c4360e6 100644
--- a/src/libs/installer/testrepository.cpp
+++ b/src/libs/installer/testrepository.cpp
@@ -33,9 +33,16 @@
#include "serverauthenticationdialog.h"
#include <QFile>
+#include <QRandomGenerator>
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::TestRepository
+ \internal
+*/
+
TestRepository::TestRepository(PackageManagerCore *parent)
: Job(parent)
, m_core(parent)
@@ -82,7 +89,7 @@ void TestRepository::doStart()
auth.setPassword(m_repository.password());
FileTaskItem item(m_repository.url().toString() + QLatin1String("/Updates.xml?") +
- QString::number(qrand() * qrand()));
+ QString::number(QRandomGenerator::global()->generate()));
item.insert(TaskRole::Authenticator, QVariant::fromValue(auth));
m_timer.start(10000);
diff --git a/src/libs/installer/testrepository.h b/src/libs/installer/testrepository.h
index 80d964692..a214188a9 100644
--- a/src/libs/installer/testrepository.h
+++ b/src/libs/installer/testrepository.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -52,8 +52,8 @@ public:
void setRepository(const Repository &repository);
private slots:
- void doStart();
- void doCancel();
+ void doStart() override;
+ void doCancel() override;
void onTimeout();
void downloadCompleted();
diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp
index 20ad3c049..de753f71a 100644
--- a/src/libs/installer/uninstallercalculator.cpp
+++ b/src/libs/installer/uninstallercalculator.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -32,93 +32,169 @@
#include "packagemanagercore.h"
#include "globals.h"
-#include <QDebug>
-
namespace QInstaller {
-UninstallerCalculator::UninstallerCalculator(const QList<Component *> &installedComponents)
- : m_installedComponents(installedComponents)
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::UninstallerCalculator
+ \internal
+*/
+
+UninstallerCalculator::UninstallerCalculator(PackageManagerCore *core
+ , const AutoDependencyHash &autoDependencyComponentHash
+ , const LocalDependencyHash &localDependencyComponentHash
+ , const QStringList &localVirtualComponents)
+ : CalculatorBase(core)
+ , m_autoDependencyComponentHash(autoDependencyComponentHash)
+ , m_localDependencyComponentHash(localDependencyComponentHash)
+ , m_localVirtualComponents(localVirtualComponents)
{
}
-QSet<Component *> UninstallerCalculator::componentsToUninstall() const
+UninstallerCalculator::~UninstallerCalculator()
{
- return m_componentsToUninstall;
}
-void UninstallerCalculator::appendComponentToUninstall(Component *component)
+bool UninstallerCalculator::solveComponent(Component *component, const QString &version)
{
+ Q_UNUSED(version)
+
if (!component)
- return;
+ return true;
if (!component->isInstalled())
- return;
+ return true;
+
+ if (m_localDependencyComponentHash.contains(component->name())) {
+ const QStringList &dependencies = PackageManagerCore::parseNames(m_localDependencyComponentHash.value(component->name()));
+ for (const QString &dependencyComponent : dependencies) {
+ Component *depComponent = m_core->componentByName(dependencyComponent);
+ if (!depComponent || !depComponent->isInstalled())
+ continue;
+
+ if (m_resolvedComponents.contains(depComponent)
+ || m_core->orderedComponentsToInstall().contains(depComponent)) {
+ // Component is already selected for uninstall or update
+ continue;
+ }
+
+ if (depComponent->isEssential() || depComponent->forcedInstallation()) {
+ const QString errorMessage = QCoreApplication::translate("InstallerCalculator",
+ "Impossible dependency resolution detected. Forced install component \"%1\" would be uninstalled "
+ "because its dependency \"%2\" is marked for uninstallation with reason: \"%3\".")
+ .arg(depComponent->name(), component->name(), resolutionText(component));
- PackageManagerCore *core = component->packageManagerCore();
- // remove all already resolved dependees
- QSet<Component *> dependees = core->dependees(component).toSet()
- .subtract(m_componentsToUninstall);
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << errorMessage;
+ m_errorString.append(errorMessage);
+ return false;
+ }
+ // Resolve also all cascading dependencies
+ if (!solveComponent(depComponent))
+ return false;
- foreach (Component *dependee, dependees)
- appendComponentToUninstall(dependee);
+ insertResolution(depComponent, Resolution::Dependent, component->name());
+ }
+ }
- m_componentsToUninstall.insert(component);
+ m_resolvedComponents.append(component);
+ return true;
}
-void UninstallerCalculator::appendComponentsToUninstall(const QList<Component*> &components)
+bool UninstallerCalculator::solve(const QList<Component*> &components)
{
- foreach (Component *component, components)
- appendComponentToUninstall(component);
+ foreach (Component *component, components) {
+ if (!solveComponent(component))
+ return false;
+ }
QList<Component*> autoDependOnList;
// All regular dependees are resolved. Now we are looking for auto depend on components.
- foreach (Component *component, m_installedComponents) {
+ for (Component *component : components) {
// If a components is installed and not yet scheduled for un-installation, check for auto depend.
- if (component->isInstalled() && !m_componentsToUninstall.contains(component)) {
- QStringList autoDependencies = PackageManagerCore::parseNames(component->autoDependencies());
- if (autoDependencies.isEmpty())
- continue;
-
- // This code needs to be enabled once the scripts use isInstalled, installationRequested and
- // uninstallationRequested...
- if (autoDependencies.first().compare(scScript, Qt::CaseInsensitive) == 0) {
- //QScriptValue valueFromScript;
- //try {
- // valueFromScript = callScriptMethod(QLatin1String("isAutoDependOn"));
- //} catch (const Error &error) {
- // // keep the component, should do no harm
- // continue;
- //}
-
- //if (valueFromScript.isValid() && !valueFromScript.toBool())
- // autoDependOnList.append(component);
- continue;
+ if (!m_autoDependencyComponentHash.contains(component->name()))
+ continue;
+ const QStringList autoDependencies = PackageManagerCore::parseNames(m_autoDependencyComponentHash.value(component->name()));
+ for (const QString &autoDependencyComponent : autoDependencies) {
+ Component *autoDepComponent = m_core->componentByName(autoDependencyComponent);
+ if (autoDepComponent && autoDepComponent->isInstalled()) {
+ // A component requested auto uninstallation, keep it to resolve their dependencies as well.
+ if (!m_resolvedComponents.contains(autoDepComponent)) {
+ insertResolution(autoDepComponent, Resolution::Automatic, component->name());
+ autoDepComponent->setInstallAction(ComponentModelHelper::AutodependUninstallation);
+ autoDependOnList.append(autoDepComponent);
+ }
}
+ }
+ }
- foreach (Component *c, m_installedComponents) {
- const QString replaces = c->value(scReplaces);
- const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(),
- QString::SkipEmptyParts) << c->name();
- foreach (const QString &possibleName, possibleNames) {
+ if (!autoDependOnList.isEmpty())
+ solve(autoDependOnList);
+ else
+ appendVirtualComponentsToUninstall();
- Component *cc = PackageManagerCore::componentByName(possibleName, m_installedComponents);
- if (cc && (cc->installAction() != ComponentModelHelper::AutodependUninstallation)) {
- autoDependencies.removeAll(possibleName);
+ return true;
+}
- }
- }
- }
+QString UninstallerCalculator::resolutionText(Component *component) const
+{
+ Resolution reason = resolutionType(component);
+ switch (reason) {
+ case Resolution::Selected:
+ return QCoreApplication::translate("UninstallerCalculator",
+ "Deselected Components:");
+ case Resolution::Replaced:
+ return QCoreApplication::translate("UninstallerCalculator", "Components replaced "
+ "by \"%1\":").arg(referencedComponent(component));
+ case Resolution::VirtualDependent:
+ return QCoreApplication::translate("UninstallerCalculator",
+ "Removing virtual components without existing dependencies:");
+ case Resolution::Dependent:
+ return QCoreApplication::translate("UninstallerCalculator", "Components "
+ "dependency \"%1\" removed:").arg(referencedComponent(component));
+ case Resolution::Automatic:
+ return QCoreApplication::translate("UninstallerCalculator", "Components "
+ "autodependency \"%1\" removed:").arg(referencedComponent(component));
+ default:
+ Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid uninstall resolution detected!");
+ }
+ return QString();
+}
- // A component requested auto uninstallation, keep it to resolve their dependencies as well.
- if (!autoDependencies.isEmpty()) {
- autoDependOnList.append(component);
- component->setInstallAction(ComponentModelHelper::AutodependUninstallation);
- }
+void UninstallerCalculator::appendVirtualComponentsToUninstall()
+{
+ QList<Component*> unneededVirtualList;
+ // Check for virtual components without dependees
+ for (const QString &componentName : qAsConst(m_localVirtualComponents)) {
+ Component *virtualComponent = m_core->componentByName(componentName, m_core->components(PackageManagerCore::ComponentType::All));
+ if (!virtualComponent)
+ continue;
+
+ if (virtualComponent->isInstalled() && !m_resolvedComponents.contains(virtualComponent)) {
+ // Components with auto dependencies were handled in the previous step
+ if (!virtualComponent->autoDependencies().isEmpty() || virtualComponent->forcedInstallation())
+ continue;
+
+ if (!isRequiredVirtualPackage(virtualComponent)) {
+ unneededVirtualList.append(virtualComponent);
+ insertResolution(virtualComponent, Resolution::VirtualDependent);
+ }
}
}
- if (!autoDependOnList.isEmpty())
- appendComponentsToUninstall(autoDependOnList);
+ if (!unneededVirtualList.isEmpty())
+ solve(unneededVirtualList);
+}
+
+bool UninstallerCalculator::isRequiredVirtualPackage(Component *component)
+{
+ const QStringList localInstallDependents = m_core->localDependenciesToComponent(component);
+ for (const QString &dependent : localInstallDependents) {
+ Component *comp = m_core->componentByName(dependent);
+ if (!m_resolvedComponents.contains(comp)) {
+ return true;
+ }
+ }
+ return m_core->isDependencyForRequestedComponent(component);
}
} // namespace QInstaller
diff --git a/src/libs/installer/uninstallercalculator.h b/src/libs/installer/uninstallercalculator.h
index cb4fe1ff6..24979a9bb 100644
--- a/src/libs/installer/uninstallercalculator.h
+++ b/src/libs/installer/uninstallercalculator.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -29,6 +29,8 @@
#define UNINSTALLERCALCULATOR_H
#include "installer_global.h"
+#include "qinstallerglobal.h"
+#include "calculatorbase.h"
#include <QHash>
#include <QList>
@@ -38,25 +40,32 @@
namespace QInstaller {
class Component;
+class PackageManagerCore;
-class INSTALLER_EXPORT UninstallerCalculator
+class INSTALLER_EXPORT UninstallerCalculator : public CalculatorBase
{
public:
- UninstallerCalculator(const QList<Component *> &installedComponents);
+ UninstallerCalculator(PackageManagerCore *core,
+ const AutoDependencyHash &autoDependencyComponentHash,
+ const LocalDependencyHash &localDependencyComponentHash,
+ const QStringList &localVirtualComponents);
+ ~UninstallerCalculator();
- QSet<Component*> componentsToUninstall() const;
-
- void appendComponentsToUninstall(const QList<Component*> &components);
+ bool solve(const QList<Component*> &components) override;
+ QString resolutionText(Component *component) const override;
private:
+ bool solveComponent(Component *component, const QString &version = QString()) override;
- void appendComponentToUninstall(Component *component);
+ bool isRequiredVirtualPackage(Component *component);
+ void appendVirtualComponentsToUninstall();
- QList<Component *> m_installedComponents;
- QSet<Component *> m_componentsToUninstall;
+private:
+ AutoDependencyHash m_autoDependencyComponentHash;
+ LocalDependencyHash m_localDependencyComponentHash;
+ QStringList m_localVirtualComponents;
};
-}
-
+} // namespace QInstaller
#endif // UNINSTALLERCALCULATOR_H
diff --git a/src/libs/installer/unziptask.cpp b/src/libs/installer/unziptask.cpp
index e0fb5449a..2142baa8b 100644
--- a/src/libs/installer/unziptask.cpp
+++ b/src/libs/installer/unziptask.cpp
@@ -47,6 +47,12 @@
namespace QInstaller {
+/*!
+ \inmodule QtInstallerFramework
+ \class QInstaller::UnzipTask
+ \internal
+*/
+
class ArchiveExtractCallback : public IArchiveExtractCallback, public CMyUnknownImp
{
public:
diff --git a/src/libs/installer/unziptask.h b/src/libs/installer/unziptask.h
index e382b9f47..877b9b970 100644
--- a/src/libs/installer/unziptask.h
+++ b/src/libs/installer/unziptask.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,7 +40,7 @@ public:
UnzipTask() {}
UnzipTask(const QString &source, const QString &target);
- void doTask(QFutureInterface<QString> &fi);
+ void doTask(QFutureInterface<QString> &fi) override;
private:
void setBytesToExtract(qint64 bytes);
diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp
index e4e3212ac..9b64ade37 100644
--- a/src/libs/installer/utils.cpp
+++ b/src/libs/installer/utils.cpp
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,6 +28,9 @@
#include "utils.h"
+#include "fileutils.h"
+#include "qsettingswrapper.h"
+
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
@@ -49,9 +52,12 @@
#include <time.h>
#endif
+/*!
+ \internal
+*/
void QInstaller::uiDetachedWait(int ms)
{
- QTime timer;
+ QElapsedTimer timer;
timer.start();
do {
QCoreApplication::processEvents(QEventLoop::AllEvents, ms);
@@ -72,7 +78,7 @@ void QInstaller::uiDetachedWait(int ms)
The process will be started in the directory \a workingDirectory.
- If the function is successful then \a *pid is set to the process identifier of the started
+ If the function is successful then \a pid is set to the process identifier of the started
process.
Additional note: The difference in using this function over its equivalent from QProcess
@@ -108,7 +114,11 @@ bool QInstaller::startDetached(const QString &program, const QStringList &argume
return success;
}
-// Returns ["en-us", "en"] for "en-us"
+/*!
+ \internal
+
+ Returns ["en-us", "en"] for "en-us".
+*/
QStringList QInstaller::localeCandidates(const QString &locale_)
{
QStringList candidates;
@@ -124,24 +134,34 @@ QStringList QInstaller::localeCandidates(const QString &locale_)
return candidates;
}
-
-static bool verb = false;
-
-void QInstaller::setVerbose(bool v)
-{
- verb = v;
-}
-
-bool QInstaller::isVerbose()
+/*!
+ Returns a list of mutually exclusive options passed to the \a parser, if there is
+ at least one mutually exclusive pair of options set. Otherwise returns an empty
+ \c QStringList. The options considered mutual are provided with \a options.
+*/
+QStringList QInstaller::checkMutualOptions(const CommandLineParser &parser, const QStringList &options)
{
- return verb;
+ QStringList mutual;
+ foreach (const QString &option, options) {
+ if (parser.isSet(option))
+ mutual << option;
+ }
+ return mutual.count() > 1
+ ? mutual
+ : QStringList();
}
+/*!
+ \internal
+*/
std::ostream &QInstaller::operator<<(std::ostream &os, const QString &string)
{
return os << qPrintable(string);
}
+/*!
+ \internal
+*/
QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo)
{
Q_ASSERT(device);
@@ -151,11 +171,14 @@ QByteArray QInstaller::calculateHash(QIODevice *device, QCryptographicHash::Algo
const qint64 numRead = device->read(buffer.data(), buffer.size());
if (numRead <= 0)
return hash.result();
- hash.addData(buffer.constData(), numRead);
+ hash.addData(buffer.left(numRead));
}
return QByteArray(); // never reached
}
+/*!
+ \internal
+*/
QByteArray QInstaller::calculateHash(const QString &path, QCryptographicHash::Algorithm algo)
{
QFile file(path);
@@ -164,6 +187,9 @@ QByteArray QInstaller::calculateHash(const QString &path, QCryptographicHash::Al
return calculateHash(&file, algo);
}
+/*!
+ \internal
+*/
QString QInstaller::replaceVariables(const QHash<QString, QString> &vars, const QString &str)
{
QString res;
@@ -184,6 +210,9 @@ QString QInstaller::replaceVariables(const QHash<QString, QString> &vars, const
return res;
}
+/*!
+ \internal
+*/
QString QInstaller::replaceWindowsEnvironmentVariables(const QString &str)
{
const QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@@ -205,83 +234,6 @@ QString QInstaller::replaceWindowsEnvironmentVariables(const QString &str)
return res;
}
-QInstaller::VerboseWriter::VerboseWriter()
-{
- preFileBuffer.open(QIODevice::ReadWrite);
- stream.setDevice(&preFileBuffer);
- currentDateTimeAsString = QDateTime::currentDateTime().toString();
-}
-
-QInstaller::VerboseWriter::~VerboseWriter()
-{
- if (preFileBuffer.isOpen()) {
- PlainVerboseWriterOutput output;
- (void)flush(&output);
- }
-}
-
-bool QInstaller::VerboseWriter::flush(VerboseWriterOutput *output)
-{
- stream.flush();
- if (logFileName.isEmpty()) // binarycreator
- return true;
- if (!preFileBuffer.isOpen())
- return true;
- //if the installer installed nothing - there is no target directory - where the logfile can be saved
- if (!QFileInfo(logFileName).absoluteDir().exists())
- return true;
-
- QString logInfo;
- logInfo += QLatin1String("************************************* Invoked: ");
- logInfo += currentDateTimeAsString;
- logInfo += QLatin1String("\n");
-
- QBuffer buffer;
- buffer.open(QIODevice::WriteOnly);
- buffer.write(logInfo.toLocal8Bit());
- buffer.write(preFileBuffer.data());
- buffer.close();
-
- if (output->write(logFileName, QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text, buffer.data())) {
- preFileBuffer.close();
- stream.setDevice(nullptr);
- return true;
- }
- return false;
-}
-
-void QInstaller::VerboseWriter::setFileName(const QString &fileName)
-{
- logFileName = fileName;
-}
-
-
-Q_GLOBAL_STATIC(QInstaller::VerboseWriter, verboseWriter)
-
-QInstaller::VerboseWriter *QInstaller::VerboseWriter::instance()
-{
- return verboseWriter();
-}
-
-void QInstaller::VerboseWriter::appendLine(const QString &msg)
-{
- stream << msg << endl;
-}
-
-QInstaller::VerboseWriterOutput::~VerboseWriterOutput()
-{
-}
-
-bool QInstaller::PlainVerboseWriterOutput::write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data)
-{
- QFile output(fileName);
- if (output.open(openMode)) {
- output.write(data);
- return true;
- }
- return false;
-}
-
#ifdef Q_OS_WIN
// taken from qcoreapplication_p.h
template<typename Char>
@@ -346,6 +298,9 @@ static QVector<Char*> qWinCmdLine(Char *cmdParam, int length, int &argc)
return argv;
}
+/*!
+ \internal
+*/
QStringList QInstaller::parseCommandLineArgs(int argc, char **argv)
{
Q_UNUSED(argc)
@@ -369,6 +324,21 @@ QStringList QInstaller::parseCommandLineArgs(int argc, char **argv)
}
#endif
+/*!
+ On Windows checks if the user account has the privilege required to create a symbolic links.
+ Returns \c true if the privilege is held, \c false otherwise.
+
+ On Unix platforms always returns \c true.
+*/
+bool QInstaller::canCreateSymbolicLinks()
+{
+#ifdef Q_OS_WIN
+ return ((setPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME, true)
+ && checkPrivilege(SE_CREATE_SYMBOLIC_LINK_NAME)) || developerModeEnabled());
+#endif
+ return true;
+}
+
#ifdef Q_OS_WIN
// taken from qprocess_win.cpp
static QString qt_create_commandline(const QString &program, const QStringList &arguments)
@@ -397,12 +367,12 @@ static QString qt_create_commandline(const QString &program, const QStringList &
// as escaping the quote -- rather put the \ behind the quote: e.g.
// rather use "foo"\ than "foo\"
QString endQuote(QLatin1Char('\"'));
- int i = tmp.length();
- while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\')) {
- --i;
+ int j = tmp.length();
+ while (j > 0 && tmp.at(j - 1) == QLatin1Char('\\')) {
+ --j;
endQuote += QLatin1Char('\\');
}
- args += QLatin1String(" \"") + tmp.left(i) + endQuote;
+ args += QLatin1String(" \"") + tmp.left(j) + endQuote;
} else {
args += QLatin1Char(' ') + tmp;
}
@@ -410,12 +380,19 @@ static QString qt_create_commandline(const QString &program, const QStringList &
return args;
}
+/*!
+ \internal
+*/
QString QInstaller::createCommandline(const QString &program, const QStringList &arguments)
{
return qt_create_commandline(program, arguments);
}
-//copied from qsystemerror.cpp in Qt
+/*!
+ \internal
+
+ Copied from qsystemerror.cpp in Qt.
+*/
QString QInstaller::windowsErrorString(int errorCode)
{
QString ret;
@@ -440,6 +417,84 @@ QString QInstaller::windowsErrorString(int errorCode)
return ret;
}
-#endif
+/*!
+ \internal
+ Sets the enabled state of \a privilege to \a enable for this process.
+ The privilege must be held by the current login user. Returns \c true
+ on success, \c false on failure.
+*/
+bool QInstaller::setPrivilege(const wchar_t *privilege, bool enable)
+{
+ LUID luid;
+ TOKEN_PRIVILEGES privileges;
+ HANDLE token;
+ HANDLE process = GetCurrentProcess();
+
+ if (!OpenProcessToken(process, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &token))
+ return false;
+
+ if (!LookupPrivilegeValue(nullptr, privilege, &luid))
+ return false;
+
+ privileges.PrivilegeCount = 1;
+ privileges.Privileges[0].Luid = luid;
+ if (enable)
+ privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ else
+ privileges.Privileges[0].Attributes = 0;
+
+ if (!AdjustTokenPrivileges(token, FALSE, &privileges,
+ sizeof(TOKEN_PRIVILEGES), nullptr, nullptr)) {
+ return false;
+ }
+ if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
+ return false;
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Returns \c true if the specified \a privilege is enabled for the client
+ process, \c false otherwise.
+*/
+bool QInstaller::checkPrivilege(const wchar_t *privilege)
+{
+ LUID luid;
+ PRIVILEGE_SET privileges;
+ HANDLE token;
+ HANDLE process = GetCurrentProcess();
+
+ if (!OpenProcessToken(process, TOKEN_QUERY, &token))
+ return false;
+
+ if (!LookupPrivilegeValue(nullptr, privilege, &luid))
+ return false;
+
+ privileges.PrivilegeCount = 1;
+ privileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
+ privileges.Privilege[0].Luid = luid;
+ privileges.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
+ BOOL result;
+ PrivilegeCheck(token, &privileges, &result);
+
+ return result;
+}
+
+/*!
+ \internal
+
+ Returns \c true if the 'Developer mode' is enabled on system.
+*/
+bool QInstaller::developerModeEnabled()
+{
+ QSettingsWrapper appModelUnlock(QLatin1String("HKLM\\SOFTWARE\\Microsoft\\Windows\\"
+ "CurrentVersion\\AppModelUnlock"), QSettings::NativeFormat);
+
+ return appModelUnlock.value(QLatin1String("AllowDevelopmentWithoutDevLicense"), false).toBool();
+}
+
+#endif
diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h
index 7cd237dcc..068490cc2 100644
--- a/src/libs/installer/utils.h
+++ b/src/libs/installer/utils.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -30,6 +30,7 @@
#define QINSTALLER_UTILS_H
#include "installer_global.h"
+#include "commandlineparser.h"
#include <QtCore/QBuffer>
#include <QtCore/QCryptographicHash>
@@ -44,6 +45,7 @@ class QIODevice;
QT_END_NAMESPACE
namespace QInstaller {
+
void INSTALLER_EXPORT uiDetachedWait(int ms);
bool INSTALLER_EXPORT startDetached(const QString &program, const QStringList &arguments,
const QString &workingDirectory, qint64 *pid = 0);
@@ -55,53 +57,21 @@ namespace QInstaller {
QString INSTALLER_EXPORT replaceWindowsEnvironmentVariables(const QString &str);
QStringList INSTALLER_EXPORT parseCommandLineArgs(int argc, char **argv);
+ bool INSTALLER_EXPORT canCreateSymbolicLinks();
+
#ifdef Q_OS_WIN
QString windowsErrorString(int errorCode);
QString createCommandline(const QString &program, const QStringList &arguments);
+ bool setPrivilege(const wchar_t *privilege, bool enable);
+ bool checkPrivilege(const wchar_t *privilege);
+ bool developerModeEnabled();
#endif
QStringList INSTALLER_EXPORT localeCandidates(const QString &locale);
- void INSTALLER_EXPORT setVerbose(bool v);
- bool INSTALLER_EXPORT isVerbose();
+ QStringList INSTALLER_EXPORT checkMutualOptions(const CommandLineParser &parser, const QStringList &options);
INSTALLER_EXPORT std::ostream& operator<<(std::ostream &os, const QString &string);
-
- class INSTALLER_EXPORT VerboseWriterOutput
- {
- public:
- virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data) = 0;
-
- protected:
- ~VerboseWriterOutput();
- };
-
- class INSTALLER_EXPORT PlainVerboseWriterOutput : public VerboseWriterOutput
- {
- public:
- virtual bool write(const QString &fileName, QIODevice::OpenMode openMode, const QByteArray &data);
- };
-
- class INSTALLER_EXPORT VerboseWriter
- {
- public:
- VerboseWriter();
- ~VerboseWriter();
-
- static VerboseWriter *instance();
-
- bool flush(VerboseWriterOutput *output);
-
- void appendLine(const QString &msg);
- void setFileName(const QString &fileName);
-
- private:
- QTextStream stream;
- QBuffer preFileBuffer;
- QString logFileName;
- QString currentDateTimeAsString;
- };
-
}
#endif // QINSTALLER_UTILS_H
diff --git a/src/libs/kdtools/filedownloader.cpp b/src/libs/kdtools/filedownloader.cpp
index fce1a6208..c99383892 100644
--- a/src/libs/kdtools/filedownloader.cpp
+++ b/src/libs/kdtools/filedownloader.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +32,8 @@
#include "ui_authenticationdialog.h"
#include "fileutils.h"
+#include "utils.h"
+#include "loggingutils.h"
#include <QDialog>
#include <QDir>
@@ -80,97 +83,107 @@ static double calcProgress(qint64 done, qint64 total)
*/
/*!
- \property FileDownloader::autoRemoveDownloadedFile
+ \property KDUpdater::FileDownloader::autoRemoveDownloadedFile
\brief Whether the downloaded file should be automatically removed after it
is downloaded and the class goes out of scope.
*/
/*!
- \property FileDownloader::url
+ \property KDUpdater::FileDownloader::url
\brief The URL to download files from.
*/
/*!
- \property FileDownloader::scheme
+ \property KDUpdater::FileDownloader::scheme
\brief The scheme to use for downloading files.
*/
/*!
- \fn FileDownloader::authenticatorChanged(const QAuthenticator &authenticator)
+ \fn KDUpdater::FileDownloader::authenticatorChanged(const QAuthenticator &authenticator)
This signal is emitted when the authenticator changes to \a authenticator.
*/
/*!
- \fn FileDownloader::canDownload() const = 0
+ \fn KDUpdater::FileDownloader::canDownload() const = 0
Returns \c true if the file exists and is readable.
*/
/*!
- \fn FileDownloader::clone(QObject *parent=0) const = 0
+ \fn KDUpdater::FileDownloader::clone(QObject *parent=0) const = 0
Clones the local file downloader and assigns it the parent \a parent.
*/
/*!
- \fn FileDownloader::downloadCanceled()
+ \fn KDUpdater::FileDownloader::downloadCanceled()
This signal is emitted if downloading a file is canceled.
*/
/*!
- \fn FileDownloader::downloadedFileName() const = 0
+ \fn KDUpdater::FileDownloader::downloadedFileName() const = 0
Returns the file name of the downloaded file.
*/
/*!
- \fn FileDownloader::downloadProgress(double progress)
+ \fn KDUpdater::FileDownloader::downloadProgress(double progress)
This signal is emitted with the current download \a progress.
*/
/*!
- \fn FileDownloader::downloadProgress(qint64 bytesReceived, qint64 bytesToReceive)
+ \fn KDUpdater::FileDownloader::downloadProgress(qint64 bytesReceived, qint64 bytesToReceive)
This signal is emitted with the download progress as the number of received bytes,
\a bytesReceived, and the total size of the file to download, \a bytesToReceive.
*/
/*!
- \fn FileDownloader::downloadSpeed(qint64 bytesPerSecond)
+ \fn KDUpdater::FileDownloader::downloadSpeed(qint64 bytesPerSecond)
This signal is emitted with the download speed in bytes per second as \a bytesPerSecond.
*/
/*!
- \fn FileDownloader::downloadStarted()
+ \fn KDUpdater::FileDownloader::downloadStarted()
This signal is emitted when downloading a file starts.
*/
/*!
- \fn FileDownloader::downloadStatus(const QString &status)
+ \fn KDUpdater::FileDownloader::downloadStatus(const QString &status)
This signal is emitted with textual representation of the current download \a status in the
following format: "100 MiB of 150 MiB - (DAYS) (HOURS) (MINUTES) (SECONDS) remaining".
*/
/*!
- \fn FileDownloader::estimatedDownloadTime(int seconds)
+ \fn KDUpdater::FileDownloader::downloadCompleted()
+ This signal is emitted when downloading a file ends.
+*/
+
+/*!
+ \fn KDUpdater::FileDownloader::downloadAborted(const QString &errorMessage)
+ This signal is emitted when downloading a file is aborted with \a errorMessage.
+*/
+
+/*!
+ \fn KDUpdater::FileDownloader::estimatedDownloadTime(int seconds)
This signal is emitted with the estimated download time in \a seconds.
*/
/*!
- \fn FileDownloader::isDownloaded() const = 0
+ \fn KDUpdater::FileDownloader::isDownloaded() const = 0
Returns \c true if the file is downloaded.
*/
/*!
- \fn FileDownloader::onError() = 0
+ \fn KDUpdater::FileDownloader::onError() = 0
Closes the destination file if an error occurs during copying and stops
the download speed timer.
*/
/*!
- \fn FileDownloader::onSuccess() = 0
+ \fn KDUpdater::FileDownloader::onSuccess() = 0
Closes the destination file after it has been successfully copied and stops
the download speed timer.
*/
/*!
- \fn FileDownloader::setDownloadedFileName(const QString &name) = 0
+ \fn KDUpdater::FileDownloader::setDownloadedFileName(const QString &name) = 0
Sets the file name of the downloaded file to \a name.
*/
@@ -180,6 +193,7 @@ struct KDUpdater::FileDownloader::Private
: m_hash(QCryptographicHash::Sha1)
, m_assumedSha1Sum("")
, autoRemove(true)
+ , followRedirect(false)
, m_speedTimerInterval(100)
, m_downloadDeadlineTimerInterval(30000)
, m_downloadPaused(false)
@@ -243,7 +257,6 @@ KDUpdater::FileDownloader::FileDownloader(const QString &scheme, QObject *parent
, d(new Private)
{
d->scheme = scheme;
- d->followRedirect = false;
}
/*!
@@ -424,7 +437,7 @@ void KDUpdater::FileDownloader::stopDownloadDeadlineTimer()
}
/*!
- Sets the download into a paused state.
+ Sets the download into a \a paused state.
*/
void KDUpdater::FileDownloader::setDownloadPaused(bool paused)
{
@@ -432,7 +445,7 @@ void KDUpdater::FileDownloader::setDownloadPaused(bool paused)
}
/*!
- Gets the download paused state.
+ Returns the download paused state.
*/
bool KDUpdater::FileDownloader::isDownloadPaused()
{
@@ -440,7 +453,7 @@ bool KDUpdater::FileDownloader::isDownloadPaused()
}
/*!
- Sets the download into a paused state.
+ Sets the download into a \a resumed state.
*/
void KDUpdater::FileDownloader::setDownloadResumed(bool resumed)
{
@@ -448,7 +461,7 @@ void KDUpdater::FileDownloader::setDownloadResumed(bool resumed)
}
/*!
- Gets the download resumed state.
+ Returns the download resumed state.
*/
bool KDUpdater::FileDownloader::isDownloadResumed()
{
@@ -481,7 +494,7 @@ void KDUpdater::FileDownloader::clearBytesDownloadedBeforeResume()
}
/*!
- Updates the amount of bytes downloaded before download resume.
+ Updates the amount of \a bytes downloaded before download resumes.
*/
void KDUpdater::FileDownloader::updateBytesDownloadedBeforeResume(qint64 bytes)
{
@@ -637,7 +650,7 @@ void KDUpdater::FileDownloader::emitEstimatedDownloadTime()
}
/*!
- \overload addCheckSumData()
+ Adds checksum \a data.
*/
void KDUpdater::FileDownloader::addCheckSumData(const QByteArray &data)
{
@@ -645,14 +658,6 @@ void KDUpdater::FileDownloader::addCheckSumData(const QByteArray &data)
}
/*!
- Adds the \a length of characters of \a data to the cryptographic hash of the downloaded file.
-*/
-void KDUpdater::FileDownloader::addCheckSumData(const char *data, int length)
-{
- d->m_hash.addData(data, length);
-}
-
-/*!
Resets SHA-1 checksum data of the downloaded file.
*/
void KDUpdater::FileDownloader::resetCheckSumData()
@@ -720,6 +725,14 @@ void KDUpdater::FileDownloader::setIgnoreSslErrors(bool ignore)
d->m_ignoreSslErrors = ignore;
}
+/*!
+ Returns the number of received bytes.
+*/
+qint64 FileDownloader::getBytesReceived() const
+{
+ return d->m_bytesReceived;
+}
+
// -- KDUpdater::LocalFileDownloader
/*!
@@ -802,9 +815,9 @@ void KDUpdater::LocalFileDownloader::doDownload()
QString localFile = this->url().toLocalFile();
d->source = new QFile(localFile, this);
if (!d->source->open(QFile::ReadOnly)) {
- onError();
setDownloadAborted(tr("Cannot open file \"%1\" for reading: %2").arg(QFileInfo(localFile)
.fileName(), d->source->errorString()));
+ onError();
return;
}
@@ -818,9 +831,9 @@ void KDUpdater::LocalFileDownloader::doDownload()
}
if (!d->destination->isOpen()) {
- onError();
setDownloadAborted(tr("Cannot open file \"%1\" for writing: %2")
.arg(QFileInfo(d->destination->fileName()).fileName(), d->destination->errorString()));
+ onError();
return;
}
@@ -891,16 +904,16 @@ void KDUpdater::LocalFileDownloader::timerEvent(QTimerEvent *event)
if (numWritten < 0) {
killTimer(d->timerId);
d->timerId = -1;
- onError();
setDownloadAborted(tr("Writing to file \"%1\" failed: %2").arg(
QDir::toNativeSeparators(d->destination->fileName()),
d->destination->errorString()));
+ onError();
return;
}
toWrite -= numWritten;
}
addSample(numRead);
- addCheckSumData(buffer.data(), numRead);
+ addCheckSumData(buffer.left(numRead));
if (numRead > 0) {
setProgress(d->source->pos(), d->source->size());
emit downloadProgress(calcProgress(d->source->pos(), d->source->size()));
@@ -1079,11 +1092,11 @@ void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent *event)
{
if (event->timerId() == d->timerId) {
if (!d->destFile.isOpen()) {
- onError();
killTimer(d->timerId);
emit downloadProgress(1);
setDownloadAborted(tr("Cannot read resource file \"%1\": %2").arg(downloadedFileName(),
d->destFile.errorString()));
+ onError();
return;
}
@@ -1092,7 +1105,7 @@ void KDUpdater::ResourceFileDownloader::timerEvent(QTimerEvent *event)
const qint64 numRead = d->destFile.read(buffer.data(), buffer.size());
addSample(numRead);
- addCheckSumData(buffer.data(), numRead);
+ addCheckSumData(buffer.left(numRead));
if (numRead > 0) {
setProgress(d->destFile.pos(), d->destFile.size());
@@ -1171,9 +1184,8 @@ struct KDUpdater::HttpDownloader::Private
disconnect(http, &QNetworkReply::finished, q, &HttpDownloader::httpReqFinished);
disconnect(http, &QNetworkReply::downloadProgress,
q, &HttpDownloader::httpReadProgress);
- void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
- disconnect(http, errorSignal, q, &HttpDownloader::httpError);
+ disconnect(http, &QNetworkReply::errorOccurred, q, &HttpDownloader::httpError);
http->deleteLater();
}
http = 0;
@@ -1199,8 +1211,14 @@ KDUpdater::HttpDownloader::HttpDownloader(QObject *parent)
#endif
connect(&d->manager, &QNetworkAccessManager::authenticationRequired,
this, &HttpDownloader::onAuthenticationRequired);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
connect(&d->manager, &QNetworkAccessManager::networkAccessibleChanged,
this, &HttpDownloader::onNetworkAccessibleChanged);
+#else
+ auto netInfo = QNetworkInformation::instance();
+ connect(netInfo, &QNetworkInformation::reachabilityChanged,
+ this, &HttpDownloader::onReachabilityChanged);
+#endif
}
@@ -1293,7 +1311,7 @@ void KDUpdater::HttpDownloader::httpReadyRead()
written += numWritten;
}
addSample(written);
- addCheckSumData(buffer.data(), read);
+ addCheckSumData(buffer.left(read));
updateBytesDownloadedBeforeResume(written);
}
}
@@ -1388,7 +1406,10 @@ void KDUpdater::HttpDownloader::httpReqFinished()
if (d->http == 0)
return;
const QUrl url = d->http->url();
- if (url.isValid() && QInstaller::lcNetwork().isDebugEnabled()){
+ // Only print host information when the logging category is enabled
+ // and output verbosity is set above standard level.
+ if (url.isValid() && QInstaller::lcServer().isDebugEnabled()
+ && LoggingHandler::instance().verboseLevel() == LoggingHandler::Detailed) {
const QFileInfo fi(d->http->url().toString());
if (fi.suffix() != QLatin1String("sha1")){
const QString hostName = url.host();
@@ -1396,7 +1417,7 @@ void KDUpdater::HttpDownloader::httpReqFinished()
QStringList hostAddresses;
foreach (const QHostAddress &address, info.addresses())
hostAddresses << address.toString();
- qCDebug(QInstaller::lcNetwork) << "Using host:" << hostName
+ qCDebug(QInstaller::lcServer) << "Using host:" << hostName
<< "for" << url.fileName() << "\nIP:" << hostAddresses;
}
}
@@ -1452,29 +1473,47 @@ void KDUpdater::HttpDownloader::startDownload(const QUrl &url)
d->m_authenticationCount = 0;
d->manager.setProxyFactory(proxyFactory());
clearBytesDownloadedBeforeResume();
- d->http = d->manager.get(QNetworkRequest(url));
+
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
+ request.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
+
+ d->http = d->manager.get(request);
connect(d->http, &QIODevice::readyRead, this, &HttpDownloader::httpReadyRead);
connect(d->http, &QNetworkReply::downloadProgress,
this, &HttpDownloader::httpReadProgress);
connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished);
- void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
- connect(d->http, errorSignal, this, &HttpDownloader::httpError);
+ connect(d->http, &QNetworkReply::errorOccurred, this, &HttpDownloader::httpError);
+ bool fileOpened = false;
if (d->destFileName.isEmpty()) {
QTemporaryFile *file = new QTemporaryFile(this);
- file->open();
+ fileOpened = file->open();
d->destination = file;
} else {
d->destination = new QFile(d->destFileName, this);
- d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ fileOpened = d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
}
-
- if (!d->destination->isOpen()) {
- const QString error = d->destination->errorString();
- const QString fileName = d->destination->fileName();
- d->shutDown();
- setDownloadAborted(tr("Cannot download %1. Cannot create file \"%2\": %3").arg(
- url.toString(), fileName, error));
+ if (!fileOpened) {
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "Failed to open file " << d->destFileName
+ << ": "<<d->destination->errorString() << ". Trying again.";
+ QFileInfo fileInfo;
+ fileInfo.setFile(d->destination->fileName());
+ if (!QDir().mkpath(fileInfo.absolutePath())) {
+ setDownloadAborted(tr("Cannot download %1. Cannot create directory for \"%2\"").arg(
+ url.toString(), fileInfo.filePath()));
+ } else {
+ fileOpened = d->destination->open(QIODevice::ReadWrite | QIODevice::Truncate);
+ if (fileOpened)
+ return;
+ if (d->destination->exists())
+ qCWarning(QInstaller::lcInstallerInstallLog) << "File exists but installer is unable to open it.";
+ else
+ qCWarning(QInstaller::lcInstallerInstallLog) << "File does not exist.";
+ setDownloadAborted(tr("Cannot download %1. Cannot create file \"%2\": %3").arg(
+ url.toString(), d->destination->fileName(), d->destination->errorString()));
+ d->shutDown();
+ }
}
}
@@ -1494,8 +1533,7 @@ void KDUpdater::HttpDownloader::resumeDownload()
connect(d->http, &QNetworkReply::downloadProgress,
this, &HttpDownloader::httpReadProgress);
connect(d->http, &QNetworkReply::finished, this, &HttpDownloader::httpReqFinished);
- void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
- connect(d->http, errorSignal, this, &HttpDownloader::httpError);
+ connect(d->http, &QNetworkReply::errorOccurred, this, &HttpDownloader::httpError);
runDownloadSpeedTimer();
runDownloadDeadlineTimer();
}
@@ -1537,6 +1575,7 @@ void KDUpdater::HttpDownloader::onAuthenticationRequired(QNetworkReply *reply, Q
}
}
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
{
if (accessible == QNetworkAccessManager::NotAccessible) {
@@ -1551,6 +1590,22 @@ void KDUpdater::HttpDownloader::onNetworkAccessibleChanged(QNetworkAccessManager
}
}
}
+#else
+void KDUpdater::HttpDownloader::onReachabilityChanged(QNetworkInformation::Reachability newReachability)
+{
+ if (newReachability == QNetworkInformation::Reachability::Online) {
+ if (isDownloadPaused()) {
+ setDownloadPaused(false);
+ resumeDownload();
+ }
+ } else {
+ d->shutDown(false);
+ setDownloadPaused(true);
+ setDownloadResumed(false);
+ stopDownloadDeadlineTimer();
+ }
+}
+#endif
#ifndef QT_NO_SSL
@@ -1565,7 +1620,7 @@ void KDUpdater::HttpDownloader::onSslErrors(QNetworkReply* reply, const QList<QS
errorString += QLatin1String(", ");
errorString += error.errorString();
}
- qDebug() << errorString;
+ qCWarning(QInstaller::lcInstallerInstallLog) << errorString;
const QStringList arguments = QCoreApplication::arguments();
if (arguments.contains(QLatin1String("--script")) || arguments.contains(QLatin1String("Script"))
@@ -1588,7 +1643,7 @@ void KDUpdater::HttpDownloader::onSslErrors(QNetworkReply* reply, const QList<QS
"the error may be temporary and you can try again.")));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
- msgBox.setButtonText(QMessageBox::Yes, tr("Try again"));
+ msgBox.addButton(tr("Try again"), QMessageBox::YesRole);
msgBox.setDefaultButton(QMessageBox::Cancel);
if (msgBox.exec() == QMessageBox::Cancel) {
diff --git a/src/libs/kdtools/filedownloader.h b/src/libs/kdtools/filedownloader.h
index ede20dcfa..e71f7d62f 100644
--- a/src/libs/kdtools/filedownloader.h
+++ b/src/libs/kdtools/filedownloader.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,6 +59,8 @@ public:
QByteArray assumedSha1Sum() const;
void setAssumedSha1Sum(const QByteArray &sha1);
+ void setCheckSha1Sum(const bool checkSha1Sum);
+ bool checkSha1Sum() const;
QString scheme() const;
void setScheme(const QString &scheme);
@@ -87,6 +90,8 @@ public:
bool ignoreSslErrors();
void setIgnoreSslErrors(bool ignore);
+ qint64 getBytesReceived() const;
+
public Q_SLOTS:
virtual void cancelDownload();
@@ -138,7 +143,6 @@ protected:
void emitEstimatedDownloadTime();
void addCheckSumData(const QByteArray &data);
- void addCheckSumData(const char *data, int length);
void resetCheckSumData();
private Q_SLOTS:
diff --git a/src/libs/kdtools/filedownloader_p.h b/src/libs/kdtools/filedownloader_p.h
index 41a430554..23eff08d7 100644
--- a/src/libs/kdtools/filedownloader_p.h
+++ b/src/libs/kdtools/filedownloader_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -34,6 +35,10 @@
#include <QtNetwork/QNetworkReply>
#include <QNetworkAccessManager>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QNetworkInformation>
+#endif
+
// these classes are not a part of the public API
namespace KDUpdater {
@@ -46,22 +51,22 @@ public:
explicit LocalFileDownloader(QObject *parent = 0);
~LocalFileDownloader();
- bool canDownload() const;
- bool isDownloaded() const;
- QString downloadedFileName() const;
- void setDownloadedFileName(const QString &name);
- LocalFileDownloader *clone(QObject *parent = 0) const;
+ bool canDownload() const override;
+ bool isDownloaded() const override;
+ QString downloadedFileName() const override;
+ void setDownloadedFileName(const QString &name) override;
+ LocalFileDownloader *clone(QObject *parent = 0) const override;
public Q_SLOTS:
- void cancelDownload();
+ void cancelDownload() override;
protected:
- void timerEvent(QTimerEvent *te);
- void onError();
- void onSuccess();
+ void timerEvent(QTimerEvent *te) override;
+ void onError() override;
+ void onSuccess() override;
private Q_SLOTS:
- void doDownload();
+ void doDownload() override;
private:
struct Private;
@@ -76,22 +81,22 @@ public:
explicit ResourceFileDownloader(QObject *parent = 0);
~ResourceFileDownloader();
- bool canDownload() const;
- bool isDownloaded() const;
- QString downloadedFileName() const;
- void setDownloadedFileName(const QString &name);
- ResourceFileDownloader *clone(QObject *parent = 0) const;
+ bool canDownload() const override;
+ bool isDownloaded() const override;
+ QString downloadedFileName() const override;
+ void setDownloadedFileName(const QString &name) override;
+ ResourceFileDownloader *clone(QObject *parent = 0) const override;
public Q_SLOTS:
- void cancelDownload();
+ void cancelDownload() override;
protected:
- void timerEvent(QTimerEvent *te);
- void onError();
- void onSuccess();
+ void timerEvent(QTimerEvent *te) override;
+ void onError() override;
+ void onSuccess() override;
private Q_SLOTS:
- void doDownload();
+ void doDownload() override;
private:
struct Private;
@@ -106,22 +111,22 @@ public:
explicit HttpDownloader(QObject *parent = 0);
~HttpDownloader();
- bool canDownload() const;
- bool isDownloaded() const;
- QString downloadedFileName() const;
- void setDownloadedFileName(const QString &name);
- HttpDownloader *clone(QObject *parent = 0) const;
+ bool canDownload() const override;
+ bool isDownloaded() const override;
+ QString downloadedFileName() const override;
+ void setDownloadedFileName(const QString &name) override;
+ HttpDownloader *clone(QObject *parent = 0) const override;
public Q_SLOTS:
- void cancelDownload();
+ void cancelDownload() override;
protected:
- void onError();
- void onSuccess();
- void timerEvent(QTimerEvent *event);
+ void onError() override;
+ void onSuccess() override;
+ void timerEvent(QTimerEvent *event) override;
private Q_SLOTS:
- void doDownload();
+ void doDownload() override;
void httpReadyRead();
void httpReadProgress(qint64 done, qint64 total);
@@ -129,7 +134,11 @@ private Q_SLOTS:
void httpDone(bool error);
void httpReqFinished();
void onAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible);
+#else
+ void onReachabilityChanged(QNetworkInformation::Reachability newReachability);
+#endif
#ifndef QT_NO_SSL
void onSslErrors(QNetworkReply* reply, const QList<QSslError> &errors);
#endif
diff --git a/src/libs/kdtools/filedownloaderfactory.cpp b/src/libs/kdtools/filedownloaderfactory.cpp
index 7350df6e3..fb6067f18 100644
--- a/src/libs/kdtools/filedownloaderfactory.cpp
+++ b/src/libs/kdtools/filedownloaderfactory.cpp
@@ -28,6 +28,7 @@
#include "filedownloaderfactory.h"
#include "filedownloader_p.h"
+#include "globals.h"
#include <QtNetwork/QSslSocket>
@@ -68,7 +69,7 @@ FileDownloaderFactory::FileDownloaderFactory()
if (QSslSocket::supportsSsl())
registerFileDownloader<HttpDownloader>(QLatin1String("https"));
else
- qWarning() << "Cannot register file downloader for https protocol: QSslSocket::supportsSsl() returns false";
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Cannot register file downloader for https protocol: QSslSocket::supportsSsl() returns false";
#endif
d->m_followRedirects = false;
@@ -180,13 +181,13 @@ FileDownloader *FileDownloaderFactory::create(const QString &scheme, QObject *pa
*/
/*!
- \fn FileDownloaderProxyFactory::~FileDownloaderProxyFactory()
+ \fn KDUpdater::FileDownloaderProxyFactory::~FileDownloaderProxyFactory()
Destroys the file downloader proxy factory.
*/
/*!
- \fn FileDownloaderProxyFactory::clone() const
+ \fn KDUpdater::FileDownloaderProxyFactory::clone() const
Clones a file downloader proxy factory.
*/
diff --git a/src/libs/kdtools/genericfactory.cpp b/src/libs/kdtools/genericfactory.cpp
index e29a8da4a..4d4fcac98 100644
--- a/src/libs/kdtools/genericfactory.cpp
+++ b/src/libs/kdtools/genericfactory.cpp
@@ -1,32 +1,26 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Contact: http://www.qt.io/licensing/
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
@@ -82,13 +76,13 @@
*/
/*!
- \fn GenericFactory::GenericFactory()
+ \fn template <typename BASE, typename IDENTIFIER, typename... ARGUMENTS> GenericFactory<BASE, IDENTIFIER, ARGUMENTS...>::GenericFactory()
Creates the generic factory.
*/
/*!
- \fn GenericFactory::~GenericFactory()
+ \fn template <typename BASE, typename IDENTIFIER, typename... ARGUMENTS> GenericFactory<BASE, IDENTIFIER, ARGUMENTS...>::~GenericFactory()
Destroys the generic factory.
*/
@@ -100,7 +94,7 @@
*/
/*!
- \fn void GenericFactory::registerProduct(const IDENTIFIER &id)
+ \fn template <typename BASE, typename IDENTIFIER, typename... ARGUMENTS> GenericFactory<BASE, IDENTIFIER, ARGUMENTS...>::registerProduct(const IDENTIFIER &id)
Registers a type DERIVED, identified by \a id in the factory. Any type with the same id gets
unregistered.
@@ -108,21 +102,22 @@
/*!
\overload
- \fn void GenericFactory::registerProduct(const IDENTIFIER &id, FactoryFunction func)
+ \fn template <typename BASE, typename IDENTIFIER, typename... ARGUMENTS> GenericFactory<BASE, IDENTIFIER, ARGUMENTS...>::registerProduct(const IDENTIFIER &id, FactoryFunction func)
Registers a function \a func that can create the type DERIVED, identified by \a id in the
factory. Any type with the same id gets unregistered.
*/
/*!
- \fn bool GenericFactory::containsProduct(const IDENTIFIER &id) const
+ \fn template <typename BASE, typename IDENTIFIER, typename... ARGUMENTS> GenericFactory<BASE, IDENTIFIER, ARGUMENTS...>::containsProduct(const IDENTIFIER &id) const
Returns \c true if the factory contains a type with the \a id; otherwise returns false.
*/
/*!
- \fn BASE *GenericFactory::create(const IDENTIFIER &id, ARGUMENTS... args) const
+ \fn template <typename BASE, typename IDENTIFIER, typename... ARGUMENTS> BASE *GenericFactory<BASE, IDENTIFIER, ARGUMENTS...>::create(const IDENTIFIER &id, ARGUMENTS... args) const
- Creates and returns the type identified by \a id, but automatically upcasted to BASE. Ownership
- of the type is transferred to the caller.
+ Creates and returns the type identified by \a id with variable number of
+ arguments \a args passed to the object's constructor, but automatically
+ upcasted to BASE. Ownership of the type is transferred to the caller.
*/
diff --git a/src/libs/kdtools/genericfactory.h b/src/libs/kdtools/genericfactory.h
index 8588ada5e..885271150 100644
--- a/src/libs/kdtools/genericfactory.h
+++ b/src/libs/kdtools/genericfactory.h
@@ -1,32 +1,26 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Contact: http://www.qt.io/licensing/
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
-** $QT_BEGIN_LICENSE:LGPL$
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
diff --git a/src/libs/kdtools/job.cpp b/src/libs/kdtools/job.cpp
index 426aa1198..8dc72da87 100644
--- a/src/libs/kdtools/job.cpp
+++ b/src/libs/kdtools/job.cpp
@@ -32,6 +32,7 @@
#include <QEventLoop>
#include <QTimer>
+#include "globals.h"
// -- Job::Private
@@ -183,7 +184,8 @@ void Job::cancel()
}
emitFinished();
} else {
- qDebug() << "The current job cannot be canceled, missing \"Cancelable\" capability.";
+ qCDebug(QInstaller::lcInstallerInstallLog) << "The current job cannot be canceled, "
+ "missing \"Cancelable\" capability.";
}
}
diff --git a/src/libs/kdtools/kdsysinfo_win.cpp b/src/libs/kdtools/kdsysinfo_win.cpp
index 0aa7e7aa3..d423ef01e 100644
--- a/src/libs/kdtools/kdsysinfo_win.cpp
+++ b/src/libs/kdtools/kdsysinfo_win.cpp
@@ -66,9 +66,15 @@ QList<ProcessInfo> runningProcesses()
QStringList deviceList;
const DWORD bufferSize = 1024;
char buffer[bufferSize + 1] = { 0 };
- if (QSysInfo::windowsVersion() <= QSysInfo::WV_5_2) {
+
+ // Qt6 does not support Win before 10
+ bool winVerLessEqual5_2 = false;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ winVerLessEqual5_2 = QSysInfo::windowsVersion() <= QSysInfo::WV_5_2;
+#endif
+ if (winVerLessEqual5_2) {
const DWORD size = GetLogicalDriveStringsA(bufferSize, buffer);
- deviceList = QString::fromLatin1(buffer, size).split(QLatin1Char(char(0)), QString::SkipEmptyParts);
+ deviceList = QString::fromLatin1(buffer, size).split(QLatin1Char(char(0)), Qt::SkipEmptyParts);
}
QLibrary kernel32(QLatin1String("Kernel32.dll"));
@@ -85,7 +91,7 @@ QList<ProcessInfo> runningProcesses()
processStruct.dwSize = sizeof(PROCESSENTRY32);
bool foundProcess = Process32First(snapshot, &processStruct);
while (foundProcess) {
- HANDLE procHandle = OpenProcess(QSysInfo::windowsVersion() > QSysInfo::WV_5_2
+ HANDLE procHandle = OpenProcess(!winVerLessEqual5_2
? KDSYSINFO_PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, false, processStruct
.th32ProcessID);
@@ -93,7 +99,7 @@ QList<ProcessInfo> runningProcesses()
QString executablePath;
DWORD bufferSize = 1024;
- if (QSysInfo::windowsVersion() > QSysInfo::WV_5_2) {
+ if (!winVerLessEqual5_2) {
succ = pQueryFullProcessImageNamePtr(procHandle, 0, buffer, &bufferSize);
executablePath = QString::fromLatin1(buffer);
} else if (pGetProcessImageFileNamePtr) {
diff --git a/src/libs/kdtools/localpackagehub.cpp b/src/libs/kdtools/localpackagehub.cpp
index 11ff36a88..c7b627c51 100644
--- a/src/libs/kdtools/localpackagehub.cpp
+++ b/src/libs/kdtools/localpackagehub.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
@@ -28,6 +28,7 @@
****************************************************************************/
#include "localpackagehub.h"
+#include "fileutils.h"
#include "globals.h"
#include "constants.h"
@@ -123,6 +124,13 @@ bool LocalPackageHub::isValid() const
}
/*!
+ Returns a map of all local installed packages. Map key is the package name.
+*/
+QMap<QString, LocalPackage> LocalPackageHub::localPackages() const
+{
+ return d->m_packageInfoMap;
+}
+/*!
Returns a list of all local installed packages.
*/
QStringList LocalPackageHub::packageNames() const
@@ -303,19 +311,25 @@ void LocalPackageHub::refresh()
Marks the package specified by \a name as installed. Sets the values of
\a version,
\a title,
+ \a treeName,
\a description,
+ \a sortingPriority,
\a dependencies,
\a autoDependencies,
\a forcedInstallation,
\a virtualComp,
\a uncompressedSize,
\a inheritVersionFrom,
- and \a checkable for the package.
+ \a checkable,
+ \a expandedByDefault,
+ and \a contentSha1 for the package.
*/
void LocalPackageHub::addPackage(const QString &name,
const QString &version,
const QString &title,
+ const QPair<QString, bool> &treeName,
const QString &description,
+ const int sortingPriority,
const QStringList &dependencies,
const QStringList &autoDependencies,
bool forcedInstallation,
@@ -323,7 +337,8 @@ void LocalPackageHub::addPackage(const QString &name,
quint64 uncompressedSize,
const QString &inheritVersionFrom,
bool checkable,
- bool expandedByDefault)
+ bool expandedByDefault,
+ const QString &contentSha1)
{
// TODO: This somewhat unexpected, remove?
if (d->m_packageInfoMap.contains(name)) {
@@ -337,7 +352,9 @@ void LocalPackageHub::addPackage(const QString &name,
info.inheritVersionFrom = inheritVersionFrom;
info.installDate = QDate::currentDate();
info.title = title;
+ info.treeName = treeName;
info.description = description;
+ info.sortingPriority = sortingPriority;
info.dependencies = dependencies;
info.autoDependencies = autoDependencies;
info.forcedInstallation = forcedInstallation;
@@ -345,6 +362,7 @@ void LocalPackageHub::addPackage(const QString &name,
info.uncompressedSize = uncompressedSize;
info.checkable = checkable;
info.expandedByDefault = expandedByDefault;
+ info.contentSha1 = contentSha1;
d->m_packageInfoMap.insert(name, info);
}
d->modified = true;
@@ -396,6 +414,9 @@ void LocalPackageHub::writeToDisk()
addTextChildHelper(&package, QLatin1String("Name"), info.name);
addTextChildHelper(&package, QLatin1String("Title"), info.title);
addTextChildHelper(&package, QLatin1String("Description"), info.description);
+ addTextChildHelper(&package, QLatin1String("SortingPriority"), QString::number(info.sortingPriority));
+ addTextChildHelper(&package, scTreeName, info.treeName.first, QLatin1String("moveChildren"),
+ QVariant(info.treeName.second).toString());
if (info.inheritVersionFrom.isEmpty())
addTextChildHelper(&package, QLatin1String("Version"), info.version);
else
@@ -420,6 +441,8 @@ void LocalPackageHub::writeToDisk()
addTextChildHelper(&package, QLatin1String("Checkable"), QLatin1String("true"));
if (info.expandedByDefault)
addTextChildHelper(&package, QLatin1String("ExpandedByDefault"), QLatin1String("true"));
+ if (!info.contentSha1.isEmpty())
+ addTextChildHelper(&package, scContentSha1, info.contentSha1);
root.appendChild(package);
}
@@ -431,6 +454,11 @@ void LocalPackageHub::writeToDisk()
file.write(doc.toByteArray(4));
file.close();
+
+ // Write permissions for installation information file
+ QInstaller::setDefaultFilePermissions(
+ &file, DefaultFilePermissions::NonExecutable);
+
d->modified = false;
}
}
@@ -461,7 +489,12 @@ void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packag
info.title = childNodeE.text();
else if (childNodeE.tagName() == QLatin1String("Description"))
info.description = childNodeE.text();
- else if (childNodeE.tagName() == QLatin1String("Version")) {
+ else if (childNodeE.tagName() == QLatin1String("SortingPriority"))
+ info.sortingPriority = childNodeE.text().toInt();
+ else if (childNodeE.tagName() == scTreeName) {
+ info.treeName.first = childNodeE.text();
+ info.treeName.second = QVariant(childNodeE.attribute(QLatin1String("moveChildren"))).toBool();
+ } else if (childNodeE.tagName() == QLatin1String("Version")) {
info.version = childNodeE.text();
info.inheritVersionFrom = childNodeE.attribute(QLatin1String("inheritVersionFrom"));
}
@@ -471,10 +504,10 @@ void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packag
info.uncompressedSize = childNodeE.text().toULongLong();
else if (childNodeE.tagName() == QLatin1String("Dependencies")) {
info.dependencies = childNodeE.text().split(QInstaller::commaRegExp(),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
} else if (childNodeE.tagName() == QLatin1String("AutoDependOn")) {
info.autoDependencies = childNodeE.text().split(QInstaller::commaRegExp(),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
} else if (childNodeE.tagName() == QLatin1String("ForcedInstallation"))
info.forcedInstallation = childNodeE.text().toLower() == QLatin1String( "true" ) ? true : false;
else if (childNodeE.tagName() == QLatin1String("LastUpdateDate"))
@@ -485,6 +518,8 @@ void LocalPackageHub::PackagesInfoData::addPackageFrom(const QDomElement &packag
info.checkable = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
else if (childNodeE.tagName() == QLatin1String("ExpandedByDefault"))
info.expandedByDefault = childNodeE.text().toLower() == QLatin1String("true") ? true : false;
+ else if (childNodeE.tagName() == QLatin1String("ContentSha1"))
+ info.contentSha1 = childNodeE.text();
}
m_packageInfoMap.insert(info.name, info);
}
diff --git a/src/libs/kdtools/localpackagehub.h b/src/libs/kdtools/localpackagehub.h
index 7a067e73e..5d69faf7d 100644
--- a/src/libs/kdtools/localpackagehub.h
+++ b/src/libs/kdtools/localpackagehub.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
@@ -43,6 +43,8 @@ struct KDTOOLS_EXPORT LocalPackage
QString name;
QString title;
QString description;
+ int sortingPriority;
+ QPair<QString, bool> treeName;
QString version;
QString inheritVersionFrom;
QStringList dependencies;
@@ -54,6 +56,7 @@ struct KDTOOLS_EXPORT LocalPackage
quint64 uncompressedSize;
bool checkable;
bool expandedByDefault;
+ QString contentSha1;
};
class KDTOOLS_EXPORT LocalPackageHub
@@ -75,6 +78,8 @@ public:
};
bool isValid() const;
+
+ QMap<QString, LocalPackage> localPackages() const;
QStringList packageNames() const;
Error error() const;
@@ -98,7 +103,9 @@ public:
void addPackage(const QString &pkgName,
const QString &version, // mandatory
const QString &title,
+ const QPair<QString, bool> &treeName,
const QString &description,
+ const int sortingPriority,
const QStringList &dependencies,
const QStringList &autoDependencies,
bool forcedInstallation,
@@ -106,7 +113,8 @@ public:
quint64 uncompressedSize,
const QString &inheritVersionFrom,
bool checkable,
- bool expandedByDefault);
+ bool expandedByDefault,
+ const QString &contentSha1);
bool removePackage(const QString &pkgName);
void refresh();
diff --git a/src/libs/kdtools/lockfile_win.cpp b/src/libs/kdtools/lockfile_win.cpp
index 20961cc4e..7fc808d39 100644
--- a/src/libs/kdtools/lockfile_win.cpp
+++ b/src/libs/kdtools/lockfile_win.cpp
@@ -45,7 +45,7 @@ bool LockFile::Private::lock()
errorString.clear();
handle = CreateFile(filename.toStdWString().data(), GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, NULL, QFileInfo(filename).exists() ? OPEN_EXISTING : CREATE_NEW,
+ FILE_SHARE_READ, NULL, QFileInfo::exists(filename) ? OPEN_EXISTING : CREATE_NEW,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle == INVALID_HANDLE_VALUE) {
diff --git a/src/libs/kdtools/runoncechecker.cpp b/src/libs/kdtools/runoncechecker.cpp
index 91161ba8e..35ec924ce 100644
--- a/src/libs/kdtools/runoncechecker.cpp
+++ b/src/libs/kdtools/runoncechecker.cpp
@@ -30,6 +30,7 @@
#include "runoncechecker.h"
#include "lockfile.h"
#include "sysinfo.h"
+#include "globals.h"
#include <QCoreApplication>
#include <QDebug>
@@ -49,7 +50,7 @@ RunOnceChecker::RunOnceChecker(const QString &filename)
RunOnceChecker::~RunOnceChecker()
{
if (!m_lockfile.unlock())
- qWarning().noquote() << m_lockfile.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << m_lockfile.errorString();
}
class ProcessnameEquals
@@ -97,7 +98,7 @@ bool RunOnceChecker::isRunning(RunOnceChecker::ConditionFlags flags)
if (flags.testFlag(ConditionFlag::Lockfile)) {
const bool locked = m_lockfile.lock();
if (!locked)
- qWarning().noquote() << m_lockfile.errorString();
+ qCWarning(QInstaller::lcInstallerInstallLog).noquote() << m_lockfile.errorString();
return !locked;
}
return false;
diff --git a/src/libs/kdtools/selfrestarter.cpp b/src/libs/kdtools/selfrestarter.cpp
index e94d0fea7..7771fafac 100644
--- a/src/libs/kdtools/selfrestarter.cpp
+++ b/src/libs/kdtools/selfrestarter.cpp
@@ -36,19 +36,21 @@ class SelfRestarter::Private
{
public:
Private(int argc, char *argv[])
- : restartOnQuit(false)
+ : executable(QString::fromLocal8Bit(argv[0]))
+ , restartOnQuit(false)
+ , workingPath(QDir::currentPath())
{
- executable = QString::fromLocal8Bit(argv[0]);
- workingPath = QDir::currentPath();
+
for (int i = 1; i < argc; ++i)
args << QString::fromLocal8Bit(argv[i]);
}
Private()
+ : executable(qApp->applicationFilePath())
+ , args(qApp->arguments().mid(1))
+ , restartOnQuit(false)
+ , workingPath(QDir::currentPath())
{
- executable = qApp->applicationFilePath();
- workingPath = QDir::currentPath();
- args = qApp->arguments().mid(1);
}
~Private()
diff --git a/src/libs/kdtools/sysinfo.cpp b/src/libs/kdtools/sysinfo.cpp
index 871596c7c..9512e59e3 100644
--- a/src/libs/kdtools/sysinfo.cpp
+++ b/src/libs/kdtools/sysinfo.cpp
@@ -71,7 +71,7 @@ VolumeInfo VolumeInfo::fromPath(const QString &path)
}
// the target directory does not exist yet, we need to cd up till we find the first existing dir
- QStringList parts = targetPath.absolutePath().split(QDir::separator(),QString::SkipEmptyParts);
+ QStringList parts = targetPath.absolutePath().split(QDir::separator(), Qt::SkipEmptyParts);
while (targetPath.absolutePath() != QDir::rootPath()) {
if (targetPath.exists())
break;
@@ -144,7 +144,7 @@ bool VolumeInfo::operator==(const VolumeInfo &other) const
return m_volumeDescriptor == other.m_volumeDescriptor;
}
-QDebug operator<<(QDebug dbg, VolumeInfo volume)
+QDebug operator<<(QDebug dbg, const VolumeInfo &volume)
{
return dbg << "KDUpdater::Volume(" << volume.mountPath() << ")";
}
diff --git a/src/libs/kdtools/sysinfo_x11.cpp b/src/libs/kdtools/sysinfo_x11.cpp
index 3099a369a..24ef099ca 100644
--- a/src/libs/kdtools/sysinfo_x11.cpp
+++ b/src/libs/kdtools/sysinfo_x11.cpp
@@ -40,10 +40,13 @@
#include <QtCore/QTextStream>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
-#include <QtCore/QRegExp>
+#include <QtCore/QRegularExpression>
namespace KDUpdater {
+/*!
+ Returns the amount of memory installed on a system.
+*/
quint64 installedMemory()
{
#ifdef Q_OS_LINUX
@@ -57,7 +60,7 @@ quint64 installedMemory()
else if (s.isEmpty())
return quint64();
- const QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ const QStringList parts = s.split(QLatin1Char(' '), Qt::SkipEmptyParts);
return quint64(parts.at(1).toInt() * 1024LL);
}
#else
@@ -74,6 +77,9 @@ quint64 installedMemory()
return 0;
}
+/*!
+ Returns currently mounted volumes as list of the \c VolumeInfo objects.
+*/
QList<VolumeInfo> mountedVolumes()
{
QList<VolumeInfo> result;
@@ -93,7 +99,7 @@ QList<VolumeInfo> mountedVolumes()
if (!s.startsWith(QLatin1Char('/')) && !s.startsWith(QLatin1String("tmpfs ") + QDir::tempPath()))
continue;
- const QStringList parts = s.split(QLatin1Char(' '), QString::SkipEmptyParts);
+ const QStringList parts = s.split(QLatin1Char(' '), Qt::SkipEmptyParts);
VolumeInfo v;
v.setMountPath(parts.at(1));
@@ -110,14 +116,17 @@ QList<VolumeInfo> mountedVolumes()
return result;
}
+/*!
+ Returns a list of currently running processes.
+*/
QList<ProcessInfo> runningProcesses()
{
QList<ProcessInfo> processes;
QDir procDir(QLatin1String("/proc"));
const QFileInfoList procCont = procDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable);
- QRegExp validator(QLatin1String("[0-9]+"));
- Q_FOREACH (const QFileInfo &info, procCont) {
- if (validator.exactMatch(info.fileName())) {
+ static const QRegularExpression validator(QLatin1String("^[0-9]+$"));
+ for (const QFileInfo &info : procCont) {
+ if (validator.match(info.fileName()).hasMatch()) {
const QString linkPath = QDir(info.absoluteFilePath()).absoluteFilePath(QLatin1String("exe"));
const QFileInfo linkInfo(linkPath);
if (linkInfo.exists()) {
@@ -131,6 +140,9 @@ QList<ProcessInfo> runningProcesses()
return processes;
}
+/*!
+ \internal
+*/
bool pathIsOnLocalDevice(const QString &path)
{
Q_UNUSED(path);
@@ -138,6 +150,9 @@ bool pathIsOnLocalDevice(const QString &path)
return true;
}
+/*!
+ \internal
+*/
bool killProcess(const ProcessInfo &process, int msecs)
{
Q_UNUSED(process);
diff --git a/src/libs/kdtools/task.cpp b/src/libs/kdtools/task.cpp
index 80bbcbd52..e63546bba 100644
--- a/src/libs/kdtools/task.cpp
+++ b/src/libs/kdtools/task.cpp
@@ -28,6 +28,8 @@
#include "task.h"
+#include "globals.h"
+
using namespace KDUpdater;
/*!
@@ -174,12 +176,12 @@ QString Task::progressText() const
void Task::run()
{
if (m_started) {
- qDebug("Trying to start an already started task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to start an already started task";
return;
}
if (m_stopped) {
- qDebug("Trying to start a finished or canceled task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to start a finished or canceled task";
return;
}
@@ -206,13 +208,13 @@ void Task::stop()
}
if (!m_started) {
- qDebug("Trying to stop an unstarted task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to stop an unstarted task";
return;
}
if(m_finished || m_stopped)
{
- qDebug("Trying to stop a finished or canceled task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to stop a finished or canceled task";
return;
}
@@ -243,12 +245,12 @@ void Task::pause()
}
if (!m_started) {
- qDebug("Trying to pause an unstarted task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to pause an unstarted task";
return;
}
if (m_finished || m_stopped) {
- qDebug("Trying to pause a finished or canceled task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to pause a finished or canceled task";
return;
}
@@ -276,7 +278,7 @@ void Task::pause()
void Task::resume()
{
if (!m_paused) {
- qDebug("Trying to resume an unpaused task");
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Trying to resume an unpaused task";
return;
}
@@ -396,7 +398,7 @@ void Task::setAutoDelete(bool autoDelete)
*/
/*!
- \fn void Task::error(int code, const QString &errorText)
+ \fn void KDUpdater::Task::error(int code, const QString &errorText)
This signal is emitted to notify an error during the execution of this task.
@@ -405,45 +407,45 @@ void Task::setAutoDelete(bool autoDelete)
*/
/*!
- \fn void Task::progressValue(int percent)
+ \fn void KDUpdater::Task::progressValue(int percent)
This signal is emitted to report progress made by the task. The \a percent parameter gives
the progress made as a percentage.
*/
/*!
- \fn void Task::progressText(const QString &progressText)
+ \fn void KDUpdater::Task::progressText(const QString &progressText)
This signal is emitted to report the progress made by the task. The \a progressText parameter
represents the progress made in a human-readable form.
*/
/*!
- \fn void Task::started()
+ \fn void KDUpdater::Task::started()
This signal is emitted when the task has started.
*/
/*!
- \fn void Task::paused()
+ \fn void KDUpdater::Task::paused()
This signal is emitted when the task has paused.
*/
/*!
- \fn void Task::resumed()
+ \fn void KDUpdater::Task::resumed()
This signal is emitted when the task has resumed.
*/
/*!
- \fn void Task::stopped()
+ \fn void KDUpdater::Task::stopped()
This signal is emitted when the task has stopped (or canceled).
*/
/*!
- \fn void Task::finished()
+ \fn void KDUpdater::Task::finished()
This signal is emitted when the task has finished.
*/
diff --git a/src/libs/kdtools/update.cpp b/src/libs/kdtools/update.cpp
index 63c515276..99869035d 100644
--- a/src/libs/kdtools/update.cpp
+++ b/src/libs/kdtools/update.cpp
@@ -33,7 +33,7 @@ using namespace KDUpdater;
/*!
\inmodule kdupdater
\class KDUpdater::Update
- \brief Represents a single update
+ \brief Represents a single update.
The KDUpdater::Update class contains information about an update. It is created by KDUpdater::UpdateFinder
corresponding to the update.
@@ -42,6 +42,11 @@ using namespace KDUpdater;
KDUpdater::UpdateFinder (which is a friend class). The destructor however is public.
*/
+/*!
+ \fn KDUpdater::Update::packageSource() const
+
+ Returns the package source.
+*/
/*!
\internal
diff --git a/src/libs/kdtools/updatefinder.cpp b/src/libs/kdtools/updatefinder.cpp
index b12ffac99..dbe7825df 100644
--- a/src/libs/kdtools/updatefinder.cpp
+++ b/src/libs/kdtools/updatefinder.cpp
@@ -1,8 +1,8 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
@@ -39,7 +39,8 @@
#include <QCoreApplication>
#include <QFileInfo>
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QFutureWatcher>
using namespace KDUpdater;
using namespace QInstaller;
@@ -55,71 +56,107 @@ using namespace QInstaller;
objects.
*/
-//
-// Private
-//
-class UpdateFinder::Private
+
+static int computeProgressPercentage(int min, int max, int percent)
{
-public:
- enum struct Resolution {
- AddPackage,
- KeepExisting,
- RemoveExisting
- };
-
- Private(UpdateFinder *qq)
- : q(qq)
- , downloadCompleteCount(0)
- , m_downloadsToComplete(0)
- {}
-
- ~Private()
- {
- clear();
- }
+ return min + qint64(max-min) * percent / 100;
+}
- struct Data {
- Data()
- : downloader(0) {}
- Data(const PackageSource &i, FileDownloader *d = 0)
- : info(i), downloader(d) {}
+static int computePercent(int done, int total)
+{
+ return total ? done * Q_INT64_C(100) / total : 0 ;
+}
- PackageSource info;
- FileDownloader *downloader;
- };
- UpdateFinder *q;
- QHash<QString, Update *> updates;
+/*!
+ Constructs an update finder.
+*/
+UpdateFinder::UpdateFinder()
+ : Task(QLatin1String("UpdateFinder"), Stoppable)
+ , m_cancel(false)
+ , m_downloadCompleteCount(0)
+ , m_downloadsToComplete(0)
+ , m_updatesXmlTasks(0)
+ , m_updatesXmlTasksToComplete(0)
+{
+}
- // Temporary structure that notes down information about updates.
- bool cancel;
- int downloadCompleteCount;
- int m_downloadsToComplete;
- QHash<UpdatesInfo *, Data> m_updatesInfoList;
+/*!
+ Destructor
+*/
+UpdateFinder::~UpdateFinder()
+{
+ clear();
+}
- void clear();
- void computeUpdates();
- void cancelComputeUpdates();
- bool downloadUpdateXMLFiles();
- bool computeApplicableUpdates();
+/*!
+ Returns a list of KDUpdater::Update objects.
+*/
+QList<Update *> UpdateFinder::updates() const
+{
+ return m_updates.values();
+}
- QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo);
- void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList);
- Resolution checkPriorityAndVersion(const PackageSource &source, const QVariantHash &data) const;
- void slotDownloadDone();
+/*!
+ Sets the information about installed local packages \a hub.
+*/
+void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub)
+{
+ m_localPackageHub = std::move(hub);
+}
- QSet<PackageSource> packageSources;
- std::weak_ptr<LocalPackageHub> m_localPackageHub;
-};
+/*!
+ Sets the package \a sources information when searching for applicable packages.
+*/
+void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources)
+{
+ m_packageSources = sources;
+}
+/*!
+ \internal
-static int computeProgressPercentage(int min, int max, int percent)
+ Implemented from KDUpdater::Task::doRun().
+*/
+void UpdateFinder::doRun()
{
- return min + qint64(max-min) * percent / 100;
+ computeUpdates();
}
-static int computePercent(int done, int total)
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doStop()
{
- return total ? done * Q_INT64_C(100) / total : 0 ;
+ cancelComputeUpdates();
+
+ // Wait until the cancel has actually happened, and then return.
+ // Thinking of using QMutex for this. Frank/Till any suggestions?
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doPause()
+{
+ // Not a pausable task
+ return false;
+}
+
+/*!
+ \internal
+
+ Implemented from KDUpdater::Task::doStop().
+*/
+bool UpdateFinder::doResume()
+{
+ // Not a pausable task, hence it is not resumable as well
+ return false;
}
/*!
@@ -127,20 +164,22 @@ static int computePercent(int done, int total)
Releases all internal resources consumed while downloading and computing updates.
*/
-void UpdateFinder::Private::clear()
+void UpdateFinder::clear()
{
- qDeleteAll(updates);
- updates.clear();
+ qDeleteAll(m_updates);
+ m_updates.clear();
const QList<Data> values = m_updatesInfoList.values();
foreach (const Data &data, values)
delete data.downloader;
- qDeleteAll(m_updatesInfoList.keys());
+ qDeleteAll(m_updatesInfoList.keyBegin(), m_updatesInfoList.keyEnd());
m_updatesInfoList.clear();
- downloadCompleteCount = 0;
+ m_downloadCompleteCount = 0;
m_downloadsToComplete = 0;
+ qDeleteAll(m_xmlFileTasks);
+ m_xmlFileTasks.clear();
}
/*!
@@ -163,51 +202,59 @@ void UpdateFinder::Private::clear()
\note Each time this function is called, all the previously computed updates are discarded
and its resources are freed.
*/
-void UpdateFinder::Private::computeUpdates()
+void UpdateFinder::computeUpdates()
{
// Computing updates is done in two stages
// 1. Downloading Update XML files from all the update sources
- // 2. Matching updates with Package XML and figuring out available updates
+ // 2. Parse attributes from Update XML documents to UpdateInfoList
+ // 3. Remove all updates with invalid content
+ // 4. Matching updates with Package XML and figuring out available updates
- if (!q->isCompressedPackage())
- clear();
- cancel = false;
+ clear();
+ m_cancel = false;
// First do some quick sanity checks on the packages info
std::shared_ptr<LocalPackageHub> packages = m_localPackageHub.lock();
if (!packages) {
- q->reportError(tr("Cannot access the package information of this application."));
+ reportError(tr("Cannot access the package information of this application."));
return;
}
if (!packages->isValid()) {
- q->reportError(packages->errorString());
+ reportError(packages->errorString());
return;
}
// Now do some quick sanity checks on the package sources.
- if (packageSources.count() <= 0) {
- q->reportError(tr("No package sources set for this application."));
+ if (m_packageSources.count() <= 0) {
+ reportError(tr("No package sources set for this application."));
return;
}
// Now we can start...
+ if (!downloadUpdateXMLFiles() || m_cancel) {
+ clear();
+ return;
+ }
- // Step 1: 0 - 49 percent
- if (!downloadUpdateXMLFiles() || cancel) {
+ if (!parseUpdateXMLFiles() || m_cancel) {
clear();
return;
}
- // Step 2: 50 - 100 percent
- if (!computeApplicableUpdates() || cancel) {
+ if (!removeInvalidObjects() || m_cancel) {
+ clear();
+ return;
+ }
+
+ if (!computeApplicableUpdates() || m_cancel) {
clear();
return;
}
// All done
- q->reportProgress(100, tr("%n update(s) found.", "", updates.count()));
- q->reportDone();
+ reportProgress(100, tr("%n update(s) found.", "", m_updates.count()));
+ reportDone();
}
/*!
@@ -217,9 +264,9 @@ void UpdateFinder::Private::computeUpdates()
\sa computeUpdates()
*/
-void UpdateFinder::Private::cancelComputeUpdates()
+void UpdateFinder::cancelComputeUpdates()
{
- cancel = true;
+ m_cancel = true;
}
/*!
@@ -238,32 +285,32 @@ void UpdateFinder::Private::cancelComputeUpdates()
The function gets into an event loop until all the downloads are complete.
*/
-bool UpdateFinder::Private::downloadUpdateXMLFiles()
+bool UpdateFinder::downloadUpdateXMLFiles()
{
// create UpdatesInfo for each update source
- foreach (const PackageSource &info, packageSources) {
+ foreach (const PackageSource &info, m_packageSources) {
const QUrl url = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString());
if (url.scheme() != QLatin1String("resource") && url.scheme() != QLatin1String("file")) {
// create FileDownloader (except for local files and resources)
- FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), q);
+ FileDownloader *downloader = FileDownloaderFactory::instance().create(url.scheme(), this);
if (!downloader)
break;
downloader->setUrl(url);
downloader->setAutoRemoveDownloadedFile(true);
- connect(downloader, SIGNAL(downloadCanceled()), q, SLOT(slotDownloadDone()));
- connect(downloader, SIGNAL(downloadCompleted()), q, SLOT(slotDownloadDone()));
- connect(downloader, SIGNAL(downloadAborted(QString)), q, SLOT(slotDownloadDone()));
- m_updatesInfoList.insert(new UpdatesInfo, Data(info, downloader));
+ connect(downloader, SIGNAL(downloadCanceled()), this, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadCompleted()), this, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadAborted(QString)), this, SLOT(slotDownloadDone()));
+ m_updatesInfoList.insert(new UpdatesInfo(info.postLoadComponentScript), Data(info, downloader));
} else {
- UpdatesInfo *updatesInfo = new UpdatesInfo;
+ UpdatesInfo *updatesInfo = new UpdatesInfo(info.postLoadComponentScript);
updatesInfo->setFileName(QInstaller::pathFromUrl(url));
m_updatesInfoList.insert(updatesInfo, Data(info));
}
}
// Trigger download of Updates.xml file
- downloadCompleteCount = 0;
+ m_downloadCompleteCount = 0;
m_downloadsToComplete = 0;
foreach (const Data &data, m_updatesInfoList) {
if (data.downloader) {
@@ -273,39 +320,53 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
}
// Wait until all downloaders have completed their downloads.
- while (true) {
- QCoreApplication::processEvents();
- if (cancel)
- return false;
-
- if (downloadCompleteCount == m_downloadsToComplete)
- break;
-
- q->reportProgress(computePercent(downloadCompleteCount, m_downloadsToComplete),
- tr("Downloading Updates.xml from update sources."));
- }
+ return waitForJobToFinish(m_downloadCompleteCount, m_downloadsToComplete);
+}
+/*!
+ \internal
+*/
+bool UpdateFinder::parseUpdateXMLFiles()
+{
// Setup the update info objects with the files from download.
- foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) {
+ m_updatesXmlTasks = 0;
+ m_updatesXmlTasksToComplete = 0;
+ QList<UpdatesInfo *> keys = m_updatesInfoList.keys();
+ for (UpdatesInfo *updatesInfo : qAsConst(keys)) {
const Data data = m_updatesInfoList.value(updatesInfo);
if (data.downloader) {
if (!data.downloader->isDownloaded()) {
- q->reportError(tr("Cannot download package source %1 from \"%2\".").arg(data
- .downloader->url().fileName(), data.info.url.toString()));
+ reportError(tr("Cannot download package source %1 from \"%2\".").arg(data.
+ downloader->url().fileName(), data.info.url.toString()));
} else {
updatesInfo->setFileName(data.downloader->downloadedFileName());
}
}
+ if (!updatesInfo->fileName().isEmpty()) {
+ ParseXmlFilesTask *const task = new ParseXmlFilesTask(updatesInfo);
+ m_xmlFileTasks.append(task);
+ QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
+ m_updatesXmlTasksToComplete++;
+ connect(watcher, &QFutureWatcherBase::finished, this, &UpdateFinder::parseUpdatesXmlTaskFinished);
+ watcher->setFuture(QtConcurrent::run(&ParseXmlFilesTask::doTask, task));
+ }
}
- // Remove all invalid update info objects.
+ // Wait until all updates.xml files are parsed
+ return waitForJobToFinish(m_updatesXmlTasks, m_updatesXmlTasksToComplete);
+}
+/*!
+ \internal
+*/
+bool UpdateFinder::removeInvalidObjects()
+{
QMutableHashIterator<UpdatesInfo *, Data> it(m_updatesInfoList);
while (it.hasNext()) {
UpdatesInfo *info = it.next().key();
if (info->isValid())
continue;
- q->reportError(info->errorString());
+ reportError(info->errorString());
delete info;
it.remove();
}
@@ -313,7 +374,7 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
if (m_updatesInfoList.isEmpty())
return false;
- q->reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
+ reportProgress(49, tr("Updates.xml file(s) downloaded from update sources."));
return true;
}
@@ -325,36 +386,40 @@ bool UpdateFinder::Private::downloadUpdateXMLFiles()
KDUpdater::PackagesInfo. Thereby figures out whether an update is applicable for
this application or not.
*/
-bool UpdateFinder::Private::computeApplicableUpdates()
+bool UpdateFinder::computeApplicableUpdates()
{
int i = 0;
- foreach (UpdatesInfo *updatesInfo, m_updatesInfoList.keys()) {
+ QList<UpdatesInfo *> keys = m_updatesInfoList.keys();
+ for (UpdatesInfo *updatesInfo : qAsConst(keys)) {
// Fetch updates applicable to this application.
QList<UpdateInfo> updates = applicableUpdates(updatesInfo);
if (!updates.count())
continue;
- if (cancel)
+ if (m_cancel)
return false;
const PackageSource updateSource = m_updatesInfoList.value(updatesInfo).info;
// Create Update objects for updates that have a valid
// UpdateFile
createUpdateObjects(updateSource, updates);
- if (cancel)
+ if (m_cancel)
return false;
// Report progress
- q->reportProgress(computeProgressPercentage(51, 100, computePercent(i,
+ reportProgress(computeProgressPercentage(51, 100, computePercent(i,
m_updatesInfoList.count())), tr("Computing applicable updates."));
++i;
}
- q->reportProgress(99, tr("Application updates computed."));
+ reportProgress(99, tr("Application updates computed."));
return true;
}
-QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesInfo)
+/*!
+ \internal
+*/
+QList<UpdateInfo> UpdateFinder::applicableUpdates(UpdatesInfo *updatesInfo)
{
const QList<UpdateInfo> dummy;
if (!updatesInfo || updatesInfo->updateInfoCount() == 0)
@@ -374,7 +439,7 @@ QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesI
// Catch hold of app names contained updatesInfo->applicationName()
// If the application appName isn't one of the app names, then the updates are not applicable.
- const QStringList apps = appName.split(QInstaller::commaRegExp(), QString::SkipEmptyParts);
+ const QStringList apps = appName.split(QInstaller::commaRegExp(), Qt::SkipEmptyParts);
if (apps.indexOf([&packages] { return packages->isValid() ? packages->applicationName()
: QCoreApplication::applicationName(); } ()) < 0) {
return dummy;
@@ -383,7 +448,10 @@ QList<UpdateInfo> UpdateFinder::Private::applicableUpdates(UpdatesInfo *updatesI
return updatesInfo->updatesInfo();
}
-void UpdateFinder::Private::createUpdateObjects(const PackageSource &source,
+/*!
+ \internal
+*/
+void UpdateFinder::createUpdateObjects(const PackageSource &source,
const QList<UpdateInfo> &updateInfoList)
{
foreach (const UpdateInfo &info, updateInfoList) {
@@ -393,24 +461,24 @@ void UpdateFinder::Private::createUpdateObjects(const PackageSource &source,
const QString name = info.data.value(QLatin1String("Name")).toString();
if (value == Resolution::RemoveExisting)
- delete updates.take(name);
+ delete m_updates.take(name);
// Create and register the update
- if (!q->isCompressedPackage() || value == Resolution::AddPackage)
- updates.insert(name, new Update(source, info));
+ m_updates.insert(name, new Update(source, info));
}
}
-/*
+/*!
+ \internal
If a package of the same name exists, always use the one with the higher
version. If the new package has the same version but a higher
priority, use the new new package, otherwise keep the already existing package.
*/
-UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion(
+UpdateFinder::Resolution UpdateFinder::checkPriorityAndVersion(
const PackageSource &source, const QVariantHash &newPackage) const
{
const QString name = newPackage.value(QLatin1String("Name")).toString();
- if (Update *existingPackage = updates.value(name)) {
+ if (Update *existingPackage = m_updates.value(name)) {
// Bingo, package was previously found elsewhere.
const int match = compareVersion(newPackage.value(QLatin1String("Version")).toString(),
@@ -418,27 +486,23 @@ UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion
if (match > 0) {
// new package has higher version, use
- qDebug().nospace() << "Remove Package 'Name: " << name << ", Version: "
- << existingPackage->data(QLatin1String("Version")).toString()
- << ", Source: " << QFileInfo(existingPackage->packageSource().url.toLocalFile()).fileName()
- << "' found a package with higher version 'Name: "
- << name << ", Version: " << newPackage.value(QLatin1String("Version")).toString()
- << ", Source: " << QFileInfo(source.url.toLocalFile()).fileName() << "'";
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Package 'Name: " << name
+ << ", Version: "<< existingPackage->data(QLatin1String("Version")).toString()
+ << ", Source: " << QFileInfo(existingPackage->packageSource().url.toLocalFile()).fileName()
+ << "' found a package with higher version 'Name: "
+ << name << ", Version: " << newPackage.value(QLatin1String("Version")).toString()
+ << ", Source: " << QFileInfo(source.url.toLocalFile()).fileName() << "'";
return Resolution::RemoveExisting;
}
if ((match == 0) && (source.priority > existingPackage->packageSource().priority)) {
// new package version equals but priority is higher, use
- qDebug().nospace() << "Remove Package 'Name: " << name << ", Priority: "
- << existingPackage->packageSource().priority
- << ", Source: " << QFileInfo(existingPackage->packageSource().url.toLocalFile()).fileName()
- << "' found a package with higher priority 'Name: "
- << name << ", Priority: " << source.priority
- << ", Source: " << QFileInfo(source.url.toLocalFile()).fileName() << "'";
- return Resolution::RemoveExisting;
- }
- if (q->isCompressedPackage() && match == 0 && source.priority == existingPackage->packageSource().priority) {
- //Same package with the same priority and version already exists
+ qCDebug(QInstaller::lcDeveloperBuild).nospace() << "Remove Package 'Name: " << name
+ << ", Priority: " << existingPackage->packageSource().priority
+ << ", Source: " << QFileInfo(existingPackage->packageSource().url.toLocalFile()).fileName()
+ << "' found a package with higher priority 'Name: "
+ << name << ", Priority: " << source.priority
+ << ", Source: " << QFileInfo(source.url.toLocalFile()).fileName() << "'";
return Resolution::RemoveExisting;
}
return Resolution::KeepExisting; // otherwise keep existing
@@ -446,113 +510,58 @@ UpdateFinder::Private::Resolution UpdateFinder::Private::checkPriorityAndVersion
return Resolution::AddPackage;
}
-//
-// UpdateFinder
-//
-
-/*!
- Constructs an update finder.
-*/
-UpdateFinder::UpdateFinder()
- : Task(QLatin1String("UpdateFinder"), Stoppable),
- m_compressedPackage(false),
- d(new Private(this))
-{
-}
-
-/*!
- Destructor
-*/
-UpdateFinder::~UpdateFinder()
-{
- delete d;
-}
-
-/*!
- Returns a list of KDUpdater::Update objects.
-*/
-QList<Update *> UpdateFinder::updates() const
-{
- return d->updates.values();
-}
-
-void UpdateFinder::setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub)
-{
- d->m_localPackageHub = std::move(hub);
-}
-
-/*!
- Sets the package sources information to use when searching for applicable packages.
-*/
-void UpdateFinder::setPackageSources(const QSet<PackageSource> &sources)
-{
- d->packageSources = sources;
-}
-
/*!
\internal
-
- Implemented from KDUpdater::Task::doRun().
*/
-void UpdateFinder::doRun()
+bool UpdateFinder::waitForJobToFinish(const int &currentCount, const int &totalsCount)
{
- d->computeUpdates();
-}
-
-/*!
- \internal
-
- Implemented from KDUpdater::Task::doStop().
-*/
-bool UpdateFinder::doStop()
-{
- d->cancelComputeUpdates();
+ while (true) {
+ QCoreApplication::processEvents();
+ if (m_cancel)
+ return false;
- // Wait until the cancel has actually happened, and then return.
- // Thinking of using QMutex for this. Frank/Till any suggestions?
+ if (currentCount == totalsCount)
+ break;
+ reportProgress(computePercent(currentCount, totalsCount),
+ tr("Downloading Updates.xml from update sources."));
+ }
return true;
}
-
/*!
\internal
-
- Implemented from KDUpdater::Task::doStop().
*/
-bool UpdateFinder::doPause()
+void UpdateFinder::parseUpdatesXmlTaskFinished()
{
- // Not a pausable task
- return false;
-}
+ ++m_updatesXmlTasks;
-/*!
- \internal
+ int pc = computePercent(m_updatesXmlTasks, m_updatesXmlTasksToComplete);
+ pc = computeProgressPercentage(0, 45, pc);
+ reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
- Implemented from KDUpdater::Task::doStop().
-*/
-bool UpdateFinder::doResume()
-{
- // Not a pausable task, hence it is not resumable as well
- return false;
+ QFutureWatcher<void> *watcher = static_cast<QFutureWatcher<void> *>(sender());
+ watcher->waitForFinished();
+ watcher->deleteLater();
}
+
/*!
\internal
*/
-void UpdateFinder::Private::slotDownloadDone()
+void UpdateFinder::slotDownloadDone()
{
- ++downloadCompleteCount;
+ ++m_downloadCompleteCount;
- int pc = computePercent(downloadCompleteCount, m_downloadsToComplete);
+ int pc = computePercent(m_downloadCompleteCount, m_downloadsToComplete);
pc = computeProgressPercentage(0, 45, pc);
- q->reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
+ reportProgress( pc, tr("Downloading Updates.xml from update sources.") );
}
/*!
\inmodule kdupdater
- This function compares two version strings \c v1 and \c v2 and returns
+ This function compares two version strings \a v1 and \a v2 and returns
-1, 0 or +1 based on the following rule
\list
@@ -591,8 +600,9 @@ int KDUpdater::compareVersion(const QString &v1, const QString &v2)
return 0;
// Split version components across ".", "-" or "_"
- QStringList v1_comps = v1.split(QRegExp(QLatin1String( "\\.|-|_")));
- QStringList v2_comps = v2.split(QRegExp(QLatin1String( "\\.|-|_")));
+ static const QRegularExpression regex(QLatin1String( "\\.|-|_"));
+ QStringList v1_comps = v1.split(regex);
+ QStringList v2_comps = v2.split(regex);
// Check each component of the version
int index = 0;
diff --git a/src/libs/kdtools/updatefinder.h b/src/libs/kdtools/updatefinder.h
index 47fa42c9a..626a700fd 100644
--- a/src/libs/kdtools/updatefinder.h
+++ b/src/libs/kdtools/updatefinder.h
@@ -1,8 +1,8 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
@@ -32,19 +32,68 @@
#include "task.h"
#include "packagesource.h"
+#include "filedownloader.h"
+#include "updatesinfo_p.h"
+#include "abstracttask.h"
#include <memory>
+using namespace QInstaller;
namespace KDUpdater {
class LocalPackageHub;
class Update;
+class ParseXmlFilesTask : public AbstractTask<void>
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ParseXmlFilesTask)
+
+public:
+ ParseXmlFilesTask(UpdatesInfo *info)
+ : m_info(info)
+ {}
+
+ void doTask(QFutureInterface<void> &fi) override
+ {
+ fi.reportStarted();
+ fi.setExpectedResultCount(1);
+
+ if (fi.isCanceled()) {
+ fi.reportFinished();
+ return; // ignore already canceled
+ }
+ m_info->parseFile();
+
+ fi.reportFinished();
+ }
+
+private:
+ UpdatesInfo *const m_info;
+};
+
+
class KDTOOLS_EXPORT UpdateFinder : public Task
{
Q_OBJECT
class Private;
+ struct Data {
+ Data()
+ : downloader(0) {}
+ explicit Data(const QInstaller::PackageSource &i, KDUpdater::FileDownloader *d = 0)
+ : info(i), downloader(d) {}
+
+ QInstaller::PackageSource info;
+ KDUpdater::FileDownloader *downloader;
+ };
+
+ enum struct Resolution {
+ AddPackage,
+ KeepExisting,
+ RemoveExisting
+ };
+
public:
UpdateFinder();
~UpdateFinder();
@@ -53,18 +102,41 @@ public:
void setLocalPackageHub(std::weak_ptr<LocalPackageHub> hub);
void setPackageSources(const QSet<QInstaller::PackageSource> &sources);
- void addCompressedPackage(bool add) { m_compressedPackage = add; }
- bool isCompressedPackage() { return m_compressedPackage; }
+
private:
- void doRun();
- bool doStop();
- bool doPause();
- bool doResume();
+ void doRun() override;
+ bool doStop() override;
+ bool doPause() override;
+ bool doResume() override;
+ void clear();
+ void computeUpdates();
+ void cancelComputeUpdates();
+ bool downloadUpdateXMLFiles();
+ bool parseUpdateXMLFiles();
+ bool removeInvalidObjects();
+ bool computeApplicableUpdates();
+
+ QList<UpdateInfo> applicableUpdates(UpdatesInfo *updatesInfo);
+ void createUpdateObjects(const PackageSource &source, const QList<UpdateInfo> &updateInfoList);
+ Resolution checkPriorityAndVersion(const QInstaller::PackageSource &source, const QVariantHash &data) const;
+ bool waitForJobToFinish(const int &currentCount, const int &totalsCount);
+
+private slots:
+ void parseUpdatesXmlTaskFinished();
+ void slotDownloadDone();
private:
- bool m_compressedPackage;
- Private *d;
- Q_PRIVATE_SLOT(d, void slotDownloadDone())
+ QSet<PackageSource> m_packageSources;
+ std::weak_ptr<LocalPackageHub> m_localPackageHub;
+ QHash<QString, Update *> m_updates;
+
+ bool m_cancel;
+ int m_downloadCompleteCount;
+ int m_downloadsToComplete;
+ QHash<UpdatesInfo *, Data> m_updatesInfoList;
+ int m_updatesXmlTasks;
+ int m_updatesXmlTasksToComplete;
+ QList<ParseXmlFilesTask*> m_xmlFileTasks;
};
} // namespace KDUpdater
diff --git a/src/libs/kdtools/updateoperation.cpp b/src/libs/kdtools/updateoperation.cpp
index 1dcb0014b..af89382a8 100644
--- a/src/libs/kdtools/updateoperation.cpp
+++ b/src/libs/kdtools/updateoperation.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -31,6 +32,7 @@
#include "constants.h"
#include "fileutils.h"
#include "packagemanagercore.h"
+#include "globals.h"
#include <QDataStream>
#include <QDebug>
@@ -67,6 +69,33 @@ using namespace KDUpdater;
to get the human-readable description of the error that occurred.
*/
+/*!
+ \enum UpdateOperation::OperationType
+ This enum code specifies the operation type.
+
+ \value Backup
+ Backup operation.
+ \value Perform
+ Perform operation.
+ \value Undo
+ Undo operation.
+*/
+
+/*!
+ \enum UpdateOperation::OperationGroup
+ This enum specifies the execution group of the operation.
+
+ \value Unpack
+ Operation should be run in the unpacking phase. Operations in
+ this group are run concurrently between all selected components.
+ \value Install
+ Operation should be run in the installation phase.
+ \value All
+ All available operation groups.
+ \value Default
+ The default group for operations, synonym for Install.
+*/
+
/*
\internal
Returns a filename for a temporary file based on \a templateName.
@@ -86,9 +115,13 @@ static QString backupFileName(const QString &templateName)
\internal
*/
UpdateOperation::UpdateOperation(QInstaller::PackageManagerCore *core)
- : m_error(0)
+ : m_group(OperationGroup::Default)
+ , m_error(0)
, m_core(core)
+ , m_requiresUnreplacedVariables(false)
{
+ qRegisterMetaType<UpdateOperation *>();
+
// Store the value for compatibility reasons.
m_values[QLatin1String("installer")] = QVariant::fromValue(core);
}
@@ -125,6 +158,16 @@ QString UpdateOperation::operationCommand() const
}
/*!
+ Returns the execution group this operation belongs to.
+
+ \sa setGroup()
+*/
+UpdateOperation::OperationGroup UpdateOperation::group() const
+{
+ return m_group;
+}
+
+/*!
Returns \c true if a value called \a name exists, otherwise returns \c false.
*/
bool UpdateOperation::hasValue(const QString &name) const
@@ -166,6 +209,17 @@ void UpdateOperation::setName(const QString &name)
}
/*!
+ Sets the execution group of the operation to \a group. Subclasses can change
+ the group to control which installation phase this operation should be run in.
+
+ The default group is \c Install.
+*/
+void UpdateOperation::setGroup(const OperationGroup &group)
+{
+ m_group = group;
+}
+
+/*!
Sets the arguments for the update operation to \a args.
*/
void UpdateOperation::setArguments(const QStringList &args)
@@ -181,10 +235,15 @@ QStringList UpdateOperation::arguments() const
return m_arguments;
}
+/*!
+ Returns \c true if the argument count is within limits of \a minArgCount
+ and \a maxArgCount. \a argDescription contains information about the
+ expected form.
+*/
bool UpdateOperation::checkArgumentCount(int minArgCount, int maxArgCount,
const QString &argDescription)
{
- const int argCount = arguments().count();
+ const int argCount = parsePerformOperationArguments().count();
if (argCount < minArgCount || argCount > maxArgCount) {
setError(InvalidArguments);
QString countRange;
@@ -212,14 +271,89 @@ bool UpdateOperation::checkArgumentCount(int minArgCount, int maxArgCount,
return true;
}
+/*!
+ Returns \c true if the argument count is exactly \a argCount.
+*/
bool UpdateOperation::checkArgumentCount(int argCount)
{
return checkArgumentCount(argCount, argCount);
}
+/*!
+ Returns operation argument list without
+ \c UNDOOOPERATION arguments.
+*/
+QStringList UpdateOperation::parsePerformOperationArguments()
+{
+ QStringList args;
+ int index = arguments().indexOf(QLatin1String("UNDOOPERATION"));
+ args = arguments().mid(0, index);
+ return args;
+}
+
+/*!
+ Returns \c true if operation undo should not be performed.
+ Returns \c false if the installation is cancelled or failed, or
+ \c UNDOOPERATION is not set in operation call.
+*/
+bool UpdateOperation::skipUndoOperation()
+{
+ //Install has failed, allow a normal undo
+ if (m_core && (m_core->status() == QInstaller::PackageManagerCore::Canceled
+ || m_core->status() == QInstaller::PackageManagerCore::Failure)) {
+ return false;
+ }
+ return arguments().contains(QLatin1String("UNDOOPERATION"));
+}
+
+/*!
+ Sets the requirement for unresolved variables to \a isRequired.
+
+ \sa requiresUnreplacedVariables()
+*/
+void UpdateOperation::setRequiresUnreplacedVariables(bool isRequired)
+{
+ m_requiresUnreplacedVariables = isRequired;
+}
+
+/*!
+ Replaces installer \c value \a variableValue with predefined variable.
+ If \c key is found for the \a variableValue and the \c key ends with string _OLD,
+ the initial \a variableValue is replaced with the \c value having a key
+ without _OLD ending. This way we can replace the hard coded values defined for operations,
+ if the value has for some reason changed. For example if we set following variables
+ in install script:
+ \badcode
+ installer.setValue("MY_OWN_EXECUTABLE", "C:/Qt/NewLocation/Tools.exe")
+ installer.setValue("MY_OWN_EXECUTABLE_OLD", "C:/Qt/OldLocation/Tools.exe")
+ \endcode
+ and we have moved the Tools.exe from OldLocation to NewLocation, the operation
+ continues to work and use the Tools.exe from NewLocation although original
+ installation has been made with Tools.exe in OldLocation.
+ Returns \c true if \a variableValue is replaced.
+*/
+bool UpdateOperation::variableReplacement(QString *variableValue)
+{
+ bool variableValueChanged = false;
+ const QString valueNormalized = QDir::cleanPath(*variableValue);
+ QString key = m_core->key(valueNormalized);
+ if (key.endsWith(QLatin1String("_OLD"))) {
+ key.chop(4);
+ if (m_core->containsValue(key)) {
+ key.prepend(QLatin1String("@"));
+ key.append(QLatin1String("@"));
+ *variableValue = m_core->replaceVariables(key);
+ qCDebug(QInstaller::lcInstallerInstallLog) << "Running above operation with replaced value: " << valueNormalized
+ << "has been replaced with" << *variableValue;
+ variableValueChanged = true;
+ }
+ }
+ return variableValueChanged;
+}
+
struct StartsWith
{
- StartsWith(const QString &searchTerm)
+ explicit StartsWith(const QString &searchTerm)
: m_searchTerm(searchTerm) {}
bool operator()(const QString &searchString)
@@ -248,9 +382,9 @@ QString UpdateOperation::argumentKeyValue(const QString &key, const QString &def
it = std::find_if(++it, tArguments.end(), StartsWith(keySeparater));
if (it != tArguments.end()) {
- qWarning().nospace() << "There are multiple keys in the arguments calling " << name() << ". "
- << "Only the first found " << key << " is used: "
- << arguments().join(QLatin1String("; "));
+ qCWarning(QInstaller::lcInstallerInstallLog).nospace() << "There are multiple keys in the arguments calling "
+ << name() << ". " << "Only the first found " << key << " is used: "
+ << arguments().join(QLatin1String("; "));
}
return value;
}
@@ -313,6 +447,15 @@ QStringList UpdateOperation::filesForDelayedDeletion() const
}
/*!
+ Returns true if installer saves the variables unresolved.
+ The variables are resolved right before operation is performed.
+*/
+bool UpdateOperation::requiresUnreplacedVariables() const
+{
+ return m_requiresUnreplacedVariables;
+}
+
+/*!
Returns the package manager core this operation belongs to.
*/
QInstaller::PackageManagerCore *UpdateOperation::packageManager() const
@@ -402,8 +545,15 @@ QDomDocument UpdateOperation::toXml() const
const QString target = m_core ? m_core->value(QInstaller::scTargetDir) : QString();
Q_FOREACH (const QString &s, arguments()) {
QDomElement arg = doc.createElement(QLatin1String("argument"));
- arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target,
- QLatin1String(QInstaller::scRelocatable))));
+ // Do not call cleanPath to Execute operations paths. The operation might require the
+ // exact separators that are set in the operation call.
+ if (name() == QLatin1String("Execute")) {
+ arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target,
+ QLatin1String(QInstaller::scRelocatable), false)));
+ } else {
+ arg.appendChild(doc.createTextNode(QInstaller::replacePath(s, target,
+ QLatin1String(QInstaller::scRelocatable))));
+ }
args.appendChild(arg);
}
root.appendChild(args);
@@ -422,14 +572,21 @@ QDomDocument UpdateOperation::toXml() const
value.setAttribute(QLatin1String("name"), it.key());
value.setAttribute(QLatin1String("type"), QLatin1String(variant.typeName()));
- if (variant.type() != QVariant::List && variant.type() != QVariant::StringList
- && variant.canConvert(QVariant::String)) {
- // it can convert to string? great!
- value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(),
- target, QLatin1String(QInstaller::scRelocatable))));
+ int variantType;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ variantType = variant.typeId();
+#else
+ variantType = variant.type();
+#endif
+
+ if (variantType != QMetaType::QStringList
+ && variant.canConvert<QString>()) {
+ // it can convert to string? great!
+ value.appendChild(doc.createTextNode(QInstaller::replacePath(variant.toString(),
+ target, QLatin1String(QInstaller::scRelocatable))));
} else {
// no? then we have to go the hard way...
- if (variant.type() == QVariant::StringList) {
+ if (variantType == QMetaType::QStringList) {
QStringList list = variant.toStringList();
for (int i = 0; i < list.count(); ++i) {
list[i] = QInstaller::replacePath(list.at(i), target,
@@ -455,6 +612,7 @@ QDomDocument UpdateOperation::toXml() const
bool UpdateOperation::fromXml(const QDomDocument &doc)
{
QString target = QCoreApplication::applicationDirPath();
+ static const QLatin1String relocatable = QLatin1String(QInstaller::scRelocatable);
// Does not change target on non macOS platforms.
if (QInstaller::isInBundle(target, &target))
target = QDir::cleanPath(target + QLatin1String("/.."));
@@ -466,8 +624,20 @@ bool UpdateOperation::fromXml(const QDomDocument &doc)
for (QDomNode n = argsElem.firstChild(); ! n.isNull(); n = n.nextSibling()) {
const QDomElement e = n.toElement();
if (!e.isNull() && e.tagName() == QLatin1String("argument")) {
- args << QInstaller::replacePath(e.text(), QLatin1String(QInstaller::scRelocatable),
- target);
+ // Sniff the Execute -operations file path separator. The operation might be
+ // strict with the used path separator
+ bool useCleanPath = true;
+ if (name() == QLatin1String("Execute")) {
+ if (e.text().startsWith(relocatable) && e.text().size() > relocatable.size()) {
+ const QChar separator = e.text().at(relocatable.size());
+ if (separator == QLatin1Char('\\')) {
+ target = QDir::toNativeSeparators(target);
+ useCleanPath = false;
+ }
+ }
+ }
+ args << QInstaller::replacePath(e.text(), relocatable,
+ target, useCleanPath);
}
}
setArguments(args);
@@ -483,21 +653,31 @@ bool UpdateOperation::fromXml(const QDomDocument &doc)
const QString type = v.attribute(QLatin1String("type"));
const QString value = v.text();
+ int variantType;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ const QMetaType t = QMetaType::fromName(type.toLatin1().data());
+ variantType = t.id();
+#else
const QVariant::Type t = QVariant::nameToType(type.toLatin1().data());
- QVariant var = qVariantFromValue(value);
- if (t == QVariant::List || t == QVariant::StringList || !var.convert(t)) {
- QDataStream stream(QByteArray::fromBase64( value.toLatin1()));
+ variantType = t;
+#endif
+ QVariant var = QVariant::fromValue(value);
+ if (variantType == QMetaType::QStringList || !var.canConvert(t)) {
+ QDataStream stream(QByteArray::fromBase64(value.toLatin1()));
stream >> var;
- if (t == QVariant::StringList) {
+ if (variantType == QMetaType::QStringList) {
QStringList list = var.toStringList();
for (int i = 0; i < list.count(); ++i) {
list[i] = QInstaller::replacePath(list.at(i),
- QLatin1String(QInstaller::scRelocatable), target);
+ relocatable, target);
}
var = QVariant::fromValue(list);
}
+ } else if (variantType == QMetaType::QString) {
+ const QString str = QInstaller::replacePath(value,
+ relocatable, target);
+ var = QVariant::fromValue(str);
}
-
m_values[name] = var;
}
@@ -505,6 +685,18 @@ bool UpdateOperation::fromXml(const QDomDocument &doc)
}
/*!
+ Returns a numerical representation of how this operation compares to
+ other operations in size, and in time it takes to perform the operation.
+
+ The default returned value is \c 1. Subclasses may override this method to
+ implement custom size hints.
+*/
+quint64 UpdateOperation::sizeHint()
+{
+ return 1;
+}
+
+/*!
\overload
Restores operation arguments and values from the XML file at path \a xml. Returns \c true on
@@ -517,7 +709,8 @@ bool UpdateOperation::fromXml(const QString &xml)
int errorLine;
int errorColumn;
if (!doc.setContent( xml, &errorMsg, &errorLine, &errorColumn)) {
- qWarning() << "Error parsing xml error=" << errorMsg << "line=" << errorLine << "column=" << errorColumn;
+ qCWarning(QInstaller::lcInstallerInstallLog) << "Error parsing xml error=" << errorMsg
+ << "line=" << errorLine << "column=" << errorColumn;
return false;
}
return fromXml(doc);
diff --git a/src/libs/kdtools/updateoperation.h b/src/libs/kdtools/updateoperation.h
index 45120ab95..e25846cd3 100644
--- a/src/libs/kdtools/updateoperation.h
+++ b/src/libs/kdtools/updateoperation.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -53,11 +54,26 @@ public:
UserDefinedError = 128
};
+ enum OperationType {
+ Backup,
+ Perform,
+ Undo
+ };
+
+ enum OperationGroup {
+ Unpack = 0x1,
+ Install = 0x2,
+ All = (Unpack | Install),
+ Default = Install
+ };
+ Q_DECLARE_FLAGS(OperationGroups, OperationGroup)
+
explicit UpdateOperation(QInstaller::PackageManagerCore *core);
virtual ~UpdateOperation();
QString name() const;
QString operationCommand() const;
+ OperationGroup group() const;
bool hasValue(const QString &name) const;
void clearValue(const QString &name);
@@ -71,6 +87,7 @@ public:
QString errorString() const;
int error() const;
QStringList filesForDelayedDeletion() const;
+ bool requiresUnreplacedVariables() const;
QInstaller::PackageManagerCore *packageManager() const;
@@ -83,25 +100,36 @@ public:
virtual bool fromXml(const QString &xml);
virtual bool fromXml(const QDomDocument &doc);
+ virtual quint64 sizeHint();
+
protected:
void setName(const QString &name);
+ void setGroup(const OperationGroup &group);
void setErrorString(const QString &errorString);
void setError(int error, const QString &errorString = QString());
void registerForDelayedDeletion(const QStringList &files);
bool deleteFileNowOrLater(const QString &file, QString *errorString = 0);
bool checkArgumentCount(int minArgCount, int maxArgCount, const QString &argDescription = QString());
bool checkArgumentCount(int argCount);
+ QStringList parsePerformOperationArguments();
+ bool skipUndoOperation();
+ void setRequiresUnreplacedVariables(bool isRequired);
+ bool variableReplacement(QString *variableValue);
private:
QString m_name;
+ OperationGroup m_group;
QStringList m_arguments;
QString m_errorString;
int m_error;
QVariantMap m_values;
QStringList m_delayedDeletionFiles;
QInstaller::PackageManagerCore *m_core;
+ bool m_requiresUnreplacedVariables;
};
} // namespace KDUpdater
+Q_DECLARE_METATYPE(KDUpdater::UpdateOperation *)
+
#endif // UPDATEOPERATION_H
diff --git a/src/libs/kdtools/updateoperations.cpp b/src/libs/kdtools/updateoperations.cpp
index 856431f84..5f6135103 100644
--- a/src/libs/kdtools/updateoperations.cpp
+++ b/src/libs/kdtools/updateoperations.cpp
@@ -54,7 +54,7 @@ static QString errnoToQString(int error)
#endif
}
-static bool removeDirectory(const QString &path, QString *errorString, bool force)
+static bool removeDirectory(const QString &path, QString *errorString, bool force, const QStringList &ignoreFiles = QStringList())
{
Q_ASSERT(errorString);
@@ -62,7 +62,9 @@ static bool removeDirectory(const QString &path, QString *errorString, bool forc
const QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
foreach (const QFileInfo &entry, entries) {
if (entry.isDir() && (!entry.isSymLink()))
- removeDirectory(entry.filePath(), errorString, force);
+ removeDirectory(entry.filePath(), errorString, force, ignoreFiles);
+ else if (ignoreFiles.contains(entry.absoluteFilePath()))
+ continue;
else if (force && (!QFile(entry.filePath()).remove()))
return false;
}
@@ -72,6 +74,12 @@ static bool removeDirectory(const QString &path, QString *errorString, bool forc
errno = 0;
const bool success = dir.rmdir(path);
+
+ if (!success && (dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden).count()
+ == ignoreFiles.count())){
+ return true;
+ }
+
if (errno)
*errorString = errnoToQString(errno);
return success;
@@ -95,6 +103,12 @@ static QString backupFileName(const QString &templateName)
// KDUpdater::CopyOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::CopyOperation
+ \internal
+*/
+
CopyOperation::CopyOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -113,7 +127,7 @@ QString CopyOperation::sourcePath()
QString CopyOperation::destinationPath()
{
- QString destination = arguments().last();
+ QString destination = arguments().at(1);
// if the target is a directory use the source filename to complete the destination path
if (QFileInfo(destination).isDir())
@@ -121,7 +135,6 @@ QString CopyOperation::destinationPath()
return destination;
}
-
void CopyOperation::backup()
{
QString destination = destinationPath();
@@ -142,8 +155,8 @@ void CopyOperation::backup()
bool CopyOperation::performOperation()
{
// We need two args to complete the copy operation. First arg provides the complete file name of source
- // Second arg provides the complete file name of dest
- if (!checkArgumentCount(2))
+ // Second arg provides the complete file name of dest. Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(2, 4, QLatin1String("<source filename> <destination filename> [UNDOOPERATION, \"\"]")))
return false;
QString source = sourcePath();
@@ -179,6 +192,8 @@ bool CopyOperation::performOperation()
bool CopyOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
QString source = sourcePath();
QString destination = destinationPath();
@@ -237,6 +252,12 @@ bool CopyOperation::testOperation()
// KDUpdater::MoveOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::MoveOperation
+ \internal
+*/
+
MoveOperation::MoveOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -250,7 +271,7 @@ MoveOperation::~MoveOperation()
void MoveOperation::backup()
{
- const QString dest = arguments().last();
+ const QString dest = arguments().at(1);
if (!QFile::exists(dest)) {
clearValue(QLatin1String("backupOfExistingDestination"));
return;
@@ -266,9 +287,10 @@ void MoveOperation::backup()
bool MoveOperation::performOperation()
{
- // We need two args to complete the copy operation. // First arg provides the complete file name of
- // source, second arg provides the complete file name of dest
- if (!checkArgumentCount(2))
+ // We need two args to complete the copy operation. First arg provides the complete file name of
+ // source, second arg provides the complete file name of dest. Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(2, 4, QLatin1String("<complete source file name> <complete destination "
+ "file name> [UNDOOPERATION, \"\"]")))
return false;
const QStringList args = arguments();
@@ -298,8 +320,10 @@ bool MoveOperation::performOperation()
bool MoveOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
const QStringList args = arguments();
- const QString dest = args.last();
+ const QString dest = args.at(1);
// first: copy back the destination to source
QFile destF(dest);
if (!destF.copy(args.first())) {
@@ -340,6 +364,12 @@ bool MoveOperation::testOperation()
// KDUpdater::DeleteOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::DeleteOperation
+ \internal
+*/
+
DeleteOperation::DeleteOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -365,7 +395,8 @@ void DeleteOperation::backup()
bool DeleteOperation::performOperation()
{
// Requires only one parameter. That is the name of the file to remove.
- if (!checkArgumentCount(1))
+ // Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]")))
return false;
return deleteFileNowOrLater(arguments().at(0));
@@ -373,7 +404,7 @@ bool DeleteOperation::performOperation()
bool DeleteOperation::undoOperation()
{
- if (!hasValue(QLatin1String("backupOfExistingFile")))
+ if (skipUndoOperation())
return true;
const QString fileName = arguments().first();
@@ -413,6 +444,12 @@ QDomDocument DeleteOperation::toXml() const
// KDUpdater::MkdirOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::MkdirOperation
+ \internal
+*/
+
MkdirOperation::MkdirOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -446,7 +483,8 @@ void MkdirOperation::backup()
bool MkdirOperation::performOperation()
{
// Requires only one parameter. That is the path which should be created
- if (!checkArgumentCount(1))
+ // Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]")))
return false;
const QString dirName = arguments().at(0);
@@ -461,7 +499,8 @@ bool MkdirOperation::performOperation()
bool MkdirOperation::undoOperation()
{
- Q_ASSERT(arguments().count() == 1);
+ if (skipUndoOperation())
+ return true;
QString createdDirValue = value(QLatin1String("createddir")).toString();
if (packageManager()) {
@@ -476,8 +515,16 @@ bool MkdirOperation::undoOperation()
// Since refactoring we know the mkdir operation which is creating the target path. If we do a full
// uninstall prevent removing the full path including target, instead remove the target only. (QTIFW-46)
- if (hasValue(QLatin1String("uninstall-only")) && value(QLatin1String("uninstall-only")).toBool())
+ QStringList excludeFiles;
+ if (hasValue(QLatin1String("uninstall-only")) && value(QLatin1String("uninstall-only")).toBool()) {
createdDir = QDir(arguments().first());
+ // Do not try to remove maintenancetool in Windows as it will fail as it is running.
+ // In Windows we use deferred delete for maintenancetool.
+ #ifdef Q_OS_WIN
+ if (packageManager())
+ excludeFiles << packageManager()->maintenanceToolName();
+ #endif
+ }
if (createdDir == QDir::root())
return true;
@@ -487,7 +534,7 @@ bool MkdirOperation::undoOperation()
QString errorString;
- const bool result = removeDirectory(createdDir.path(), &errorString, forceremoval);
+ const bool result = removeDirectory(createdDir.path(), &errorString, forceremoval, excludeFiles);
if (!result) {
if (errorString.isEmpty())
@@ -511,6 +558,12 @@ bool KDUpdater::MkdirOperation::testOperation()
// KDUpdater::RmdirOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::RmdirOperation
+ \internal
+*/
+
RmdirOperation::RmdirOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
@@ -526,7 +579,8 @@ void RmdirOperation::backup()
bool RmdirOperation::performOperation()
{
// Requires only one parameter. That is the name of the file to remove.
- if (!checkArgumentCount(1))
+ // Optionally UNDOOPERATION can be added as well
+ if (!checkArgumentCount(1, 3, QLatin1String("<file to remove> [UNDOOPERATION, \"\"]")))
return false;
const QString firstArg = arguments().at(0);
@@ -551,7 +605,7 @@ bool RmdirOperation::performOperation()
bool RmdirOperation::undoOperation()
{
- if (!value(QLatin1String("removed")).toBool())
+ if (!value(QLatin1String("removed")).toBool() || skipUndoOperation())
return true;
errno = 0;
@@ -575,12 +629,24 @@ bool RmdirOperation::testOperation()
// KDUpdater::AppendFileOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::AppendFileOperation
+ \internal
+*/
+
AppendFileOperation::AppendFileOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
setName(QLatin1String("AppendFile"));
}
+AppendFileOperation::~AppendFileOperation()
+{
+ if (skipUndoOperation())
+ deleteFileNowOrLater(value(QLatin1String("backupOfFile")).toString());
+}
+
void AppendFileOperation::backup()
{
const QString filename = arguments().first();
@@ -601,10 +667,10 @@ bool AppendFileOperation::performOperation()
{
// This operation takes two arguments. First argument is the name of the file into which a text has to be
// appended. Second argument is the text to append.
- if (!checkArgumentCount(2))
+ if (!checkArgumentCount(2, 4, QLatin1String("<filename> <text to apply> [UNDOOPERATION, \"\"]")))
return false;
- const QStringList args = this->arguments();
+ const QStringList args = arguments();
const QString fName = args.at(0);
QFile file(fName);
if (!file.open(QFile::Append)) {
@@ -643,6 +709,9 @@ bool AppendFileOperation::performOperation()
bool AppendFileOperation::undoOperation()
{
+ if (skipUndoOperation())
+ return true;
+
// backupOfFile being empty -> file didn't exist before -> no error
const QString filename = arguments().first();
const QString backupOfFile = value(QLatin1String("backupOfFile")).toString();
@@ -682,12 +751,24 @@ bool AppendFileOperation::testOperation()
// KDUpdater::PrependFileOperation
////////////////////////////////////////////////////////////////////////////
+/*!
+ \inmodule KDUpdater
+ \class KDUpdater::PrependFileOperation
+ \internal
+*/
+
PrependFileOperation::PrependFileOperation(QInstaller::PackageManagerCore *core)
: UpdateOperation(core)
{
setName(QLatin1String("PrependFile"));
}
+PrependFileOperation::~PrependFileOperation()
+{
+ if (skipUndoOperation())
+ deleteFileNowOrLater(value(QLatin1String("backupOfFile")).toString());
+}
+
void PrependFileOperation::backup()
{
const QString filename = arguments().first();
@@ -709,10 +790,10 @@ bool PrependFileOperation::performOperation()
// This operation takes two arguments. First argument is the name
// of the file into which a text has to be appended. Second argument
// is the text to append.
- if (!checkArgumentCount(2))
+ if (!checkArgumentCount(2, 4, QLatin1String("<filename> <text to prepend> [UNDOOPERATION, \"\"]")))
return false;
- const QStringList args = this->arguments();
+ const QStringList args = arguments();
const QString fName = args.at(0);
// Load the file first.
QFile file(fName);
@@ -752,7 +833,9 @@ bool PrependFileOperation::performOperation()
bool PrependFileOperation::undoOperation()
{
- // bockupOfFile being empty -> file didn't exist before -> no error
+ if (skipUndoOperation())
+ return true;
+
const QString filename = arguments().first();
const QString backupOfFile = value(QLatin1String("backupOfFile")).toString();
if (!backupOfFile.isEmpty() && !QFile::exists(backupOfFile)) {
diff --git a/src/libs/kdtools/updateoperations.h b/src/libs/kdtools/updateoperations.h
index b13a42559..adbfc7de1 100644
--- a/src/libs/kdtools/updateoperations.h
+++ b/src/libs/kdtools/updateoperations.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -40,12 +41,12 @@ public:
explicit CopyOperation(QInstaller::PackageManagerCore *core = 0);
~CopyOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
- QDomDocument toXml() const;
+ QDomDocument toXml() const override;
private:
QString sourcePath();
QString destinationPath();
@@ -58,10 +59,10 @@ public:
explicit MoveOperation(QInstaller::PackageManagerCore *core = 0);
~MoveOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
class KDTOOLS_EXPORT DeleteOperation : public UpdateOperation
@@ -71,12 +72,12 @@ public:
explicit DeleteOperation(QInstaller::PackageManagerCore *core = 0);
~DeleteOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
- QDomDocument toXml() const;
+ QDomDocument toXml() const override;
};
class KDTOOLS_EXPORT MkdirOperation : public UpdateOperation
@@ -85,10 +86,10 @@ class KDTOOLS_EXPORT MkdirOperation : public UpdateOperation
public:
explicit MkdirOperation(QInstaller::PackageManagerCore *core = 0);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
class KDTOOLS_EXPORT RmdirOperation : public UpdateOperation
@@ -97,10 +98,10 @@ class KDTOOLS_EXPORT RmdirOperation : public UpdateOperation
public:
RmdirOperation(QInstaller::PackageManagerCore *core = 0);
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
class KDTOOLS_EXPORT AppendFileOperation : public UpdateOperation
@@ -108,11 +109,12 @@ class KDTOOLS_EXPORT AppendFileOperation : public UpdateOperation
Q_DECLARE_TR_FUNCTIONS(KDUpdater::AppendFileOperation)
public:
explicit AppendFileOperation(QInstaller::PackageManagerCore *core = 0);
+ ~AppendFileOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
class KDTOOLS_EXPORT PrependFileOperation : public UpdateOperation
@@ -120,11 +122,12 @@ class KDTOOLS_EXPORT PrependFileOperation : public UpdateOperation
Q_DECLARE_TR_FUNCTIONS(KDUpdater::PrependFileOperation)
public:
explicit PrependFileOperation(QInstaller::PackageManagerCore *core = 0);
+ ~PrependFileOperation();
- void backup();
- bool performOperation();
- bool undoOperation();
- bool testOperation();
+ void backup() override;
+ bool performOperation() override;
+ bool undoOperation() override;
+ bool testOperation() override;
};
} // namespace KDUpdater
diff --git a/src/libs/kdtools/updater.cpp b/src/libs/kdtools/updater.cpp
new file mode 100644
index 000000000..a3ac7c768
--- /dev/null
+++ b/src/libs/kdtools/updater.cpp
@@ -0,0 +1,40 @@
+/**************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "updater.h"
+
+/*!
+ \enum KDUpdater::Error
+
+ \value ENoError
+ \value ECannotStartTask
+ \value ECannotPauseTask
+ \value ECannotResumeTask
+ \value ECannotStopTask
+ \value EUnknown
+*/
diff --git a/src/libs/kdtools/updatesinfo.cpp b/src/libs/kdtools/updatesinfo.cpp
index f01e60eb6..e82c1c1fb 100644
--- a/src/libs/kdtools/updatesinfo.cpp
+++ b/src/libs/kdtools/updatesinfo.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -28,18 +29,20 @@
#include "updatesinfo_p.h"
#include "utils.h"
+#include "constants.h"
-#include <QDomDocument>
#include <QFile>
#include <QLocale>
#include <QPair>
#include <QVector>
#include <QUrl>
+#include <QXmlStreamReader>
using namespace KDUpdater;
-UpdatesInfoData::UpdatesInfoData()
+UpdatesInfoData::UpdatesInfoData(const bool postLoadComponentScript)
: error(UpdatesInfo::NotYetReadError)
+ , m_postLoadComponentScript(postLoadComponentScript)
{
}
@@ -62,35 +65,26 @@ void UpdatesInfoData::parseFile(const QString &updateXmlFile)
return;
}
- QDomDocument doc;
- QString parseErrorMessage;
- int parseErrorLine, parseErrorColumn;
- if (!doc.setContent(&file, &parseErrorMessage, &parseErrorLine, &parseErrorColumn)) {
- error = UpdatesInfo::InvalidXmlError;
- errorMessage = tr("Parse error in %1 at %2, %3: %4").arg(updateXmlFile,
- QString::number(parseErrorLine), QString::number(parseErrorColumn), parseErrorMessage);
- return;
- }
-
- QDomElement rootE = doc.documentElement();
- if (rootE.tagName() != QLatin1String("Updates")) {
- setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(rootE.tagName()));
- return;
- }
-
- QDomNodeList childNodes = rootE.childNodes();
- for(int i = 0; i < childNodes.count(); i++) {
- const QDomElement childE = childNodes.at(i).toElement();
- if (childE.isNull())
- continue;
-
- if (childE.tagName() == QLatin1String("ApplicationName"))
- applicationName = childE.text();
- else if (childE.tagName() == QLatin1String("ApplicationVersion"))
- applicationVersion = childE.text();
- else if (childE.tagName() == QLatin1String("PackageUpdate")) {
- if (!parsePackageUpdateElement(childE))
- return; //error handled in subroutine
+ QXmlStreamReader reader(&file);
+ if (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("Updates")) {
+ while (reader.readNextStartElement()) {
+ if (reader.name() == QLatin1String("ApplicationName")) {
+ applicationName = reader.readElementText();
+ } else if (reader.name() == QLatin1String("ApplicationVersion")) {
+ applicationVersion = reader.readElementText();
+ } else if (reader.name() == QLatin1String("Checksum")) {
+ checkSha1CheckSum = (reader.readElementText());
+ } else if (reader.name() == QLatin1String("PackageUpdate")) {
+ if (!parsePackageUpdateElement(reader, checkSha1CheckSum))
+ return; //error handled in subroutine
+ } else {
+ reader.skipCurrentElement();
+ }
+ }
+ } else {
+ setInvalidContentError(tr("Root element %1 unexpected, should be \"Updates\".").arg(reader.name()));
+ return;
}
}
@@ -108,61 +102,58 @@ void UpdatesInfoData::parseFile(const QString &updateXmlFile)
error = UpdatesInfo::NoError;
}
-bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
+bool UpdatesInfoData::parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum)
{
- if (updateE.isNull())
- return false;
-
UpdateInfo info;
- QMap<QString, QString> localizedDescriptions;
- for (int i = 0; i < updateE.childNodes().count(); i++) {
- QDomElement childE = updateE.childNodes().at(i).toElement();
- if (childE.isNull())
+ QHash<QString, QVariant> scriptHash;
+ while (reader.readNext()) {
+ const QString elementName = reader.name().toString();
+ if ((reader.name() == QLatin1String("PackageUpdate"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (elementName.isEmpty() || reader.tokenType() == QXmlStreamReader::EndElement)
continue;
-
- if (childE.tagName() == QLatin1String("ReleaseNotes")) {
- info.data[childE.tagName()] = QUrl(childE.text());
- } else if (childE.tagName() == QLatin1String("Licenses")) {
- QHash<QString, QVariant> licenseHash;
- const QDomNodeList licenseNodes = childE.childNodes();
- for (int i = 0; i < licenseNodes.count(); ++i) {
- const QDomNode licenseNode = licenseNodes.at(i);
- if (licenseNode.nodeName() == QLatin1String("License")) {
- QDomElement element = licenseNode.toElement();
- licenseHash.insert(element.attributeNode(QLatin1String("name")).value(),
- element.attributeNode(QLatin1String("file")).value());
- }
- }
- if (!licenseHash.isEmpty())
- info.data.insert(QLatin1String("Licenses"), licenseHash);
- } else if (childE.tagName() == QLatin1String("Version")) {
+ if (elementName == QLatin1String("Licenses")) {
+ parseLicenses(reader, info.data);
+ } else if (elementName == QLatin1String("TreeName")) {
+ const QXmlStreamAttributes attr = reader.attributes();
+ const bool moveChildren = attr.value(QLatin1String("moveChildren")).toString().toLower() == QInstaller::scTrue ? true : false;
+ const QPair<QString, bool> treeNamePair(reader.readElementText(), moveChildren);
+ info.data.insert(QLatin1String("TreeName"), QVariant::fromValue(treeNamePair));
+ } else if (elementName == QLatin1String("Version")) {
+ const QXmlStreamAttributes attr = reader.attributes();
info.data.insert(QLatin1String("inheritVersionFrom"),
- childE.attribute(QLatin1String("inheritVersionFrom")));
- info.data[childE.tagName()] = childE.text();
- } else if (childE.tagName() == QLatin1String("DisplayName")) {
- processLocalizedTag(childE, info.data);
- } else if (childE.tagName() == QLatin1String("Description")) {
- if (!childE.hasAttribute(QLatin1String("xml:lang")))
- info.data[QLatin1String("Description")] = childE.text();
- QString languageAttribute = childE.attribute(QLatin1String("xml:lang"), QLatin1String("en"));
- localizedDescriptions.insert(languageAttribute.toLower(), childE.text());
- } else if (childE.tagName() == QLatin1String("UpdateFile")) {
- info.data[QLatin1String("CompressedSize")] = childE.attribute(QLatin1String("CompressedSize"));
- info.data[QLatin1String("UncompressedSize")] = childE.attribute(QLatin1String("UncompressedSize"));
+ attr.value(QLatin1String("inheritVersionFrom")).toString());
+ info.data[elementName] = reader.readElementText();
+ } else if (elementName == QLatin1String("DisplayName")
+ || elementName == QLatin1String("Description")) {
+ processLocalizedTag(reader, info.data);
+ } else if (elementName == QLatin1String("UpdateFile")) {
+ info.data[QLatin1String("CompressedSize")] = reader.attributes().value(QLatin1String("CompressedSize")).toString();
+ info.data[QLatin1String("UncompressedSize")] = reader.attributes().value(QLatin1String("UncompressedSize")).toString();
+ } else if (elementName == QLatin1String("Operations")) {
+ parseOperations(reader, info.data);
+ } else if (elementName == QLatin1String("Script")) {
+ const QXmlStreamAttributes attr = reader.attributes();
+ bool postLoad = false;
+ // postLoad can be set either to individual components or to whole repositories.
+ // If individual components has the postLoad attribute, it overwrites the repository value.
+ if (attr.hasAttribute(QLatin1String("postLoad")))
+ postLoad = attr.value(QLatin1String("postLoad")).toString().toLower() == QInstaller::scTrue ? true : false;
+ else if (m_postLoadComponentScript)
+ postLoad = true;
+
+ if (postLoad)
+ scriptHash.insert(QLatin1String("postLoadScript"), reader.readElementText());
+ else
+ scriptHash.insert(QLatin1String("installScript"), reader.readElementText());
} else {
- info.data[childE.tagName()] = childE.text();
- }
- }
-
- QStringList candidates;
- foreach (const QString &lang, QLocale().uiLanguages())
- candidates << QInstaller::localeCandidates(lang.toLower());
- foreach (const QString &candidate, candidates) {
- if (localizedDescriptions.contains(candidate)) {
- info.data[QLatin1String("Description")] = localizedDescriptions.value(candidate);
- break;
+ info.data[elementName] = reader.readElementText();
}
}
+ if (!scriptHash.isEmpty())
+ info.data.insert(QLatin1String("Script"), scriptHash);
if (!info.data.contains(QLatin1String("Name"))) {
setInvalidContentError(tr("PackageUpdate element without Name"));
@@ -176,28 +167,87 @@ bool UpdatesInfoData::parsePackageUpdateElement(const QDomElement &updateE)
setInvalidContentError(tr("PackageUpdate element without ReleaseDate"));
return false;
}
-
+ info.data[QLatin1String("CheckSha1CheckSum")] = checkSha1CheckSum;
updateInfoList.append(info);
return true;
}
-void UpdatesInfoData::processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const
+void UpdatesInfoData::processLocalizedTag(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
{
- QString languageAttribute = childE.attribute(QLatin1String("xml:lang")).toLower();
- if (!info.contains(childE.tagName()) && (languageAttribute.isEmpty()))
- info[childE.tagName()] = childE.text();
+ const QString languageAttribute = reader.attributes().value(QLatin1String("xml:lang")).toString().toLower();
+ const QString elementName = reader.name().toString();
+ if (!info.contains(elementName) && (languageAttribute.isEmpty()))
+ info[elementName] = reader.readElementText();
+ if (languageAttribute.isEmpty())
+ return;
// overwrite default if we have a language specific description
if (QLocale().name().startsWith(languageAttribute, Qt::CaseInsensitive))
- info[childE.tagName()] = childE.text();
+ info[elementName] = reader.readElementText();
}
+void UpdatesInfoData::parseOperations(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
+{
+ QList<QPair<QString, QVariant>> operationsList;
+ while (reader.readNext()) {
+ const QString subElementName = reader.name().toString();
+ // End of parsing operations
+ if ((subElementName == QLatin1String("Operations"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName != QLatin1String("Operation") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ QStringList attributes;
+ const QXmlStreamAttributes attr = reader.attributes();
+ while (reader.readNext()) {
+ const QString subElementName2 = reader.name().toString();
+ // End of parsing single operation
+ if ((subElementName2 == QLatin1String("Operation"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName2 != QLatin1String("Argument") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ attributes.append(reader.readElementText());
+ }
+ QPair<QString, QVariant> pair;
+ pair.first = attr.value(QLatin1String("name")).toString();
+ pair.second = attributes;
+ operationsList.append(pair);
+ }
+ info.insert(QLatin1String("Operations"), QVariant::fromValue(operationsList));
+}
+void UpdatesInfoData::parseLicenses(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const
+{
+ QHash<QString, QVariant> licenseHash;
+ while (reader.readNext()) {
+ const QString subElementName = reader.name().toString();
+ // End of parsing Licenses
+ if ((subElementName == QLatin1String("Licenses"))
+ && (reader.tokenType() == QXmlStreamReader::EndElement)) {
+ break;
+ }
+ if (subElementName != QLatin1String("License") || reader.tokenType() == QXmlStreamReader::EndElement)
+ continue;
+ const QXmlStreamAttributes attr = reader.attributes();
+ QVariantMap attributes;
+ attributes.insert(QLatin1String("file"), attr.value(QLatin1String("file")).toString());
+ if (!attr.value(QLatin1String("priority")).isNull())
+ attributes.insert(QLatin1String("priority"), attr.value(QLatin1String("priority")).toString());
+ else
+ attributes.insert(QLatin1String("priority"), QLatin1String("0"));
+ licenseHash.insert(attr.value(QLatin1String("name")).toString(), attributes);
+ }
+ if (!licenseHash.isEmpty())
+ info.insert(QLatin1String("Licenses"), licenseHash);
+}
//
// UpdatesInfo
//
-UpdatesInfo::UpdatesInfo()
- : d(new UpdatesInfoData)
+UpdatesInfo::UpdatesInfo(const bool postLoadComponentScript)
+ : d(new UpdatesInfoData(postLoadComponentScript))
{
}
@@ -225,6 +275,10 @@ void UpdatesInfo::setFileName(const QString &updateXmlFile)
d->updateInfoList.clear();
d->updateXmlFile = updateXmlFile;
+}
+
+void UpdatesInfo::parseFile()
+{
d->parseFile(d->updateXmlFile);
}
@@ -243,6 +297,11 @@ QString UpdatesInfo::applicationVersion() const
return d->applicationVersion;
}
+QString UpdatesInfo::checkSha1CheckSum() const
+{
+ return d->checkSha1CheckSum;
+}
+
int UpdatesInfo::updateInfoCount() const
{
return d->updateInfoList.count();
diff --git a/src/libs/kdtools/updatesinfo_p.h b/src/libs/kdtools/updatesinfo_p.h
index 93e2fe8c6..a3768232a 100644
--- a/src/libs/kdtools/updatesinfo_p.h
+++ b/src/libs/kdtools/updatesinfo_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Klaralvdalens Datakonsult AB (KDAB)
+** Copyright (C) 2023 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -58,7 +59,7 @@ public:
InvalidContentError
};
- UpdatesInfo();
+ UpdatesInfo(const bool postLoadComponentScript = false);
~UpdatesInfo();
bool isValid() const;
@@ -68,10 +69,13 @@ public:
QString fileName() const;
void setFileName(const QString &updateXmlFile);
+ void parseFile();
QString applicationName() const;
QString applicationVersion() const;
+ QString checkSha1CheckSum() const;
+
int updateInfoCount() const;
UpdateInfo updateInfo(int index) const;
QList<UpdateInfo> updatesInfo() const;
diff --git a/src/libs/kdtools/updatesinfodata_p.h b/src/libs/kdtools/updatesinfodata_p.h
index 69b004bc3..c71a85193 100644
--- a/src/libs/kdtools/updatesinfodata_p.h
+++ b/src/libs/kdtools/updatesinfodata_p.h
@@ -32,7 +32,7 @@
#include <QCoreApplication>
#include <QSharedData>
-QT_FORWARD_DECLARE_CLASS(QDomElement)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
namespace KDUpdater {
@@ -43,7 +43,7 @@ struct UpdatesInfoData : public QSharedData
Q_DECLARE_TR_FUNCTIONS(KDUpdater::UpdatesInfoData)
public:
- UpdatesInfoData();
+ UpdatesInfoData(const bool postLoadComponentScript);
~UpdatesInfoData();
int error;
@@ -51,15 +51,19 @@ public:
QString updateXmlFile;
QString applicationName;
QString applicationVersion;
+ QString checkSha1CheckSum;
QList<UpdateInfo> updateInfoList;
+ bool m_postLoadComponentScript;
void parseFile(const QString &updateXmlFile);
- bool parsePackageUpdateElement(const QDomElement &updateE);
+ bool parsePackageUpdateElement(QXmlStreamReader &reader, const QString &checkSha1CheckSum);
void setInvalidContentError(const QString &detail);
private:
- void processLocalizedTag(const QDomElement &childE, QHash<QString, QVariant> &info) const;
+ void processLocalizedTag(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
+ void parseOperations(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
+ void parseLicenses(QXmlStreamReader &reader, QHash<QString, QVariant> &info) const;
};
} // namespace KDUpdater
diff --git a/src/libs/libs.pro b/src/libs/libs.pro
index 3982bc8a1..076f83b5d 100644
--- a/src/libs/libs.pro
+++ b/src/libs/libs.pro
@@ -1,3 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS += 7zip installer
-installer.depends = 7zip
+
+SUBDIRS += 3rdparty installer
+installer.depends = 3rdparty
+