From 51b5f00f5cd4070f84fff6961b69dd53835128e1 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Mon, 10 Nov 2014 15:32:55 -0800 Subject: [PATCH] Large set of changes to restore CiderPress build. CiderPress and MDC now compile, and execute far enough to open their respective "about" boxes, but I doubt they'll do much more than that. * Switch from MBCS to UNICODE APIs Microsoft switched to UTF-16 (by way of UCS-2) a long time ago, and the support for MBCS seems to be getting phased out. So it's time to switch to wide strings. This is a bit awkward for CiderPress because it works with disk and file archives with 8-bit filenames, and I want NufxLib and DiskImgLib to continue to work on Linux (which has largely taken the UTF-8 approach to Unicode). The libraries will continue to work with 8-bit filenames, with CiderPress/MDC doing the conversion at the appropriate point. There were a couple of places where strings from a structure handed back by one of the libraries were used directly in the UI, or vice-versa, which is a problem because we have nowhere to store the result of the conversion. These currently have fixed place-holder "xyzzy" strings. All UI strings are now wide. Various format strings now use "%ls" and "%hs" to explicitly specify wide and narrow. This doesn't play well with gcc, so only the Windows-specific parts use those. * Various updates to vcxproj files The project-file conversion had some cruft that is now largely gone. The build now has a common output directory for the EXEs and libraries, avoiding the old post-build copy steps. * Added zlib 1.2.8 and nufxlib 2.2.2 source snapshots The old "prebuilts" directory is now gone. The libraries are now built as part of building the apps. I added a minimal set of files for zlib, and a full set for nufxlib. The Linux-specific nufxlib goodies are included for the benefit of the Linux utilities, which are currently broken (don't build). * Replace symbols used for include guards Symbols with a leading "__" are reserved. --- CP.sln | 23 +- app/ACUArchive.cpp | 72 +- app/ACUArchive.h | 16 +- app/AboutDialog.cpp | 20 +- app/AboutDialog.h | 6 +- app/ActionProgressDialog.cpp | 4 +- app/ActionProgressDialog.h | 10 +- app/Actions.cpp | 185 +- app/AddClashDialog.h | 6 +- app/AddFilesDialog.cpp | 2 +- app/AddFilesDialog.h | 10 +- app/ArchiveInfoDialog.cpp | 88 +- app/ArchiveInfoDialog.h | 8 +- app/BNYArchive.cpp | 61 +- app/BNYArchive.h | 16 +- app/BasicImport.cpp | 27 +- app/BasicImport.h | 8 +- app/CassImpTargetDialog.cpp | 22 +- app/CassImpTargetDialog.h | 6 +- app/CassetteDialog.cpp | 50 +- app/CassetteDialog.h | 6 +- app/ChooseAddTargetDialog.h | 6 +- app/ChooseDirDialog.cpp | 18 +- app/ChooseDirDialog.h | 12 +- app/CiderPress.rc | 1123 ++--- app/Clipboard.cpp | 69 +- app/ConfirmOverwriteDialog.cpp | 8 +- app/ConfirmOverwriteDialog.h | 6 +- app/ContentList.cpp | 134 +- app/ContentList.h | 20 +- app/ConvDiskOptionsDialog.cpp | 27 +- app/ConvDiskOptionsDialog.h | 10 +- app/ConvFileOptionsDialog.h | 6 +- app/CreateImageDialog.cpp | 56 +- app/CreateImageDialog.h | 20 +- app/CreateSubdirDialog.h | 6 +- app/DEFileDialog.cpp | 2 +- app/DEFileDialog.h | 8 +- app/DiskArchive.cpp | 373 +- app/DiskArchive.h | 55 +- app/DiskConvertDialog.cpp | 35 +- app/DiskConvertDialog.h | 6 +- app/DiskEditDialog.cpp | 95 +- app/DiskEditDialog.h | 26 +- app/DiskEditOpenDialog.h | 6 +- app/DiskFSTree.cpp | 8 +- app/DiskFSTree.h | 6 +- app/DoneOpenDialog.h | 5 + app/EOLScanDialog.cpp | 10 +- app/EOLScanDialog.h | 6 +- app/EditAssocDialog.cpp | 6 +- app/EditAssocDialog.h | 6 +- app/EditCommentDialog.h | 6 +- app/EditPropsDialog.cpp | 46 +- app/EditPropsDialog.h | 6 +- app/EnterRegDialog.cpp | 2 +- app/EnterRegDialog.h | 6 +- app/ExtractOptionsDialog.cpp | 4 +- app/ExtractOptionsDialog.h | 6 +- app/FileNameConv.cpp | 1211 +++--- app/FileNameConv.h | 50 +- app/GenericArchive.cpp | 162 +- app/GenericArchive.h | 110 +- app/HelpTopics.h | 6 +- app/ImageFormatDialog.cpp | 122 +- app/ImageFormatDialog.h | 8 +- app/Main.cpp | 295 +- app/Main.h | 79 +- app/MyApp.cpp | 48 +- app/MyApp.h | 16 +- app/NewDiskSize.cpp | 4 +- app/NewDiskSize.h | 6 +- app/NewFolderDialog.cpp | 14 +- app/NewFolderDialog.h | 10 +- app/NufxArchive.cpp | 347 +- app/NufxArchive.h | 24 +- app/OpenVolumeDialog.cpp | 100 +- app/OpenVolumeDialog.h | 8 +- app/PasteSpecialDialog.h | 6 +- app/Preferences.cpp | 240 +- app/Preferences.h | 32 +- app/PrefsDialog.cpp | 8 +- app/PrefsDialog.h | 6 +- app/Print.cpp | 35 +- app/Print.h | 20 +- app/ProgressCounterDialog.h | 10 +- app/RecompressOptionsDialog.cpp | 18 +- app/RecompressOptionsDialog.h | 8 +- app/Registry.cpp | 263 +- app/Registry.h | 28 +- app/RenameEntryDialog.h | 6 +- app/RenameVolumeDialog.h | 6 +- app/Squeeze.h | 6 +- app/StdAfx.h | 2 + app/SubVolumeDialog.cpp | 3 +- app/SubVolumeDialog.h | 6 +- app/Tools.cpp | 288 +- app/TwoImgPropsDialog.cpp | 22 +- app/TwoImgPropsDialog.h | 6 +- app/UseSelectionDialog.h | 7 +- app/ViewFilesDialog.cpp | 28 +- app/ViewFilesDialog.h | 12 +- app/VolumeCopyDialog.cpp | 121 +- app/VolumeCopyDialog.h | 6 +- app/app.vcxproj | 424 +- app/app.vcxproj.filters | 468 +-- app/targetver.h | 18 + diskimg/ASPI.cpp | 2 +- diskimg/ASPI.h | 5 +- diskimg/DOS33.cpp | 2 +- diskimg/DiskImg.h | 4 +- diskimg/DiskImgDetail.h | 13 +- diskimg/GenericFD.cpp | 10 +- diskimg/Global.cpp | 16 +- diskimg/HFS.cpp | 2 +- diskimg/Pascal.cpp | 2 +- diskimg/StdAfx.h | 6 + diskimg/Win32BlockIO.cpp | 24 +- diskimg/Win32BlockIO.h | 18 +- diskimg/Win32Extra.h | 6 +- diskimg/diskimg.vcxproj | 249 +- diskimg/diskimg.vcxproj.filters | 86 +- diskimg/diskimg.vcxproj.user | 4 + diskimg/libhfs/libhfs.vcxproj | 118 +- diskimg/libhfs/libhfs.vcxproj.filters | 76 +- mdc/AboutDlg.cpp | 2 +- mdc/ChooseFilesDlg.h | 10 +- mdc/Main.cpp | 188 +- mdc/Main.h | 16 +- mdc/ProgressDlg.cpp | 2 +- mdc/ProgressDlg.h | 2 +- mdc/StdAfx.h | 1 + mdc/mdc.cpp | 2 +- mdc/mdc.vcxproj | 126 +- mdc/mdc.vcxproj.filters | 40 +- mdc/mdc.vcxproj.user | 4 + nufxlib/Archive.c | 1218 ++++++ nufxlib/ArchiveIO.c | 425 ++ nufxlib/Bzip2.c | 299 ++ nufxlib/COPYING-LIB | 29 + nufxlib/ChangeLog.txt | 288 ++ nufxlib/Compress.c | 404 ++ nufxlib/Crc16.c | 110 + nufxlib/Debug.c | 395 ++ nufxlib/Deferred.c | 2578 ++++++++++++ nufxlib/Deflate.c | 299 ++ nufxlib/Entry.c | 864 ++++ nufxlib/Expand.c | 230 + nufxlib/FileIO.c | 1501 +++++++ nufxlib/Funnel.c | 925 ++++ nufxlib/INSTALL | 183 + nufxlib/Lzc.c | 1106 +++++ nufxlib/Lzw.c | 1635 ++++++++ nufxlib/Makefile.in | 122 + nufxlib/Makefile.msc | 155 + nufxlib/MiscStuff.c | 120 + nufxlib/MiscStuff.h | 109 + nufxlib/MiscUtils.c | 382 ++ nufxlib/NOTES.txt | 206 + {prebuilt => nufxlib}/NufxLib.h | 1688 ++++---- nufxlib/NufxLibPriv.h | 870 ++++ nufxlib/README.txt | 124 + nufxlib/Record.c | 2867 +++++++++++++ nufxlib/SourceSink.c | 889 ++++ nufxlib/Squeeze.c | 1146 +++++ nufxlib/SunOS4.h | 46 + nufxlib/SysDefs.h | 148 + nufxlib/Thread.c | 1370 ++++++ nufxlib/Value.c | 329 ++ nufxlib/Version.c | 42 + nufxlib/config.guess | 1466 +++++++ nufxlib/config.h.in | 146 + nufxlib/config.sub | 1579 +++++++ nufxlib/configure | 5552 +++++++++++++++++++++++++ nufxlib/configure.in | 228 + nufxlib/install-sh | 251 ++ nufxlib/mkinstalldirs | 34 + nufxlib/nufxlib.def | 67 + nufxlib/nufxlib.vcxproj | 116 + nufxlib/nufxlib.vcxproj.filters | 99 + nufxlib/samples/Common.h | 68 + nufxlib/samples/Exerciser.c | 1383 ++++++ nufxlib/samples/ImgConv.c | 658 +++ nufxlib/samples/Launder.c | 710 ++++ nufxlib/samples/Makefile.in | 91 + nufxlib/samples/Makefile.msc | 113 + nufxlib/samples/README-S.txt | 119 + nufxlib/samples/TestBasic.c | 1178 ++++++ nufxlib/samples/TestExtract.c | 492 +++ nufxlib/samples/TestSimple.c | 105 + nufxlib/samples/TestTwirl.c | 697 ++++ prebuilt/libnufx.a | Bin 191682 -> 0 bytes prebuilt/nufxlib2.dll | Bin 110592 -> 0 bytes prebuilt/nufxlib2.lib | Bin 13282 -> 0 bytes prebuilt/nufxlib2D.dll | Bin 331829 -> 0 bytes prebuilt/nufxlib2D.lib | Bin 13344 -> 0 bytes prebuilt/zdll.lib | Bin 10590 -> 0 bytes prebuilt/zlib.h | 1357 ------ prebuilt/zlib1.dll | Bin 59904 -> 0 bytes reformat/AWGS.cpp | 2 +- reformat/AWGS.h | 6 +- reformat/AppleWorks.h | 6 +- reformat/Asm.cpp | 2 +- reformat/Asm.h | 6 +- reformat/BASIC.h | 6 +- reformat/CPMFiles.h | 6 +- reformat/Directory.cpp | 10 +- reformat/Directory.h | 6 +- reformat/Disasm.cpp | 6 +- reformat/Disasm.h | 6 +- reformat/DisasmTable.cpp | 2 +- reformat/DoubleHiRes.h | 6 +- reformat/HiRes.h | 6 +- reformat/MacPaint.h | 6 +- reformat/NiftyList.cpp | 14 +- reformat/PascalFiles.h | 6 +- reformat/PrintShop.h | 6 +- reformat/Reformat.cpp | 125 +- reformat/Reformat.h | 26 +- reformat/ReformatBase.cpp | 10 +- reformat/ReformatBase.h | 6 +- reformat/ResourceFork.cpp | 4 +- reformat/ResourceFork.h | 6 +- reformat/Simple.h | 6 +- reformat/StdAfx.cpp | 1 - reformat/StdAfx.h | 5 + reformat/SuperHiRes.cpp | 11 +- reformat/SuperHiRes.h | 6 +- reformat/Teach.h | 6 +- reformat/Text8.h | 6 +- reformat/reformat.vcxproj | 179 +- reformat/reformat.vcxproj.filters | 124 +- util/CancelDialog.h | 6 +- util/FaddenStd.h | 7 +- util/Modeless.h | 6 +- util/MyBitmapButton.h | 6 +- util/MyDIBitmap.cpp | 30 +- util/MyDIBitmap.h | 12 +- util/MyDebug.h | 6 +- util/MyEdit.h | 6 +- util/MySpinCtrl.cpp | 18 +- util/MySpinCtrl.h | 8 +- util/PathName.cpp | 110 +- util/PathName.h | 44 +- util/Pidl.cpp | 68 +- util/Pidl.h | 8 +- util/ProgressCancelDialog.h | 6 +- util/SelectFilesDialog.cpp | 48 +- util/SelectFilesDialog.h | 27 +- util/ShellTree.cpp | 82 +- util/ShellTree.h | 6 +- util/SoundFile.cpp | 19 +- util/SoundFile.h | 10 +- util/StdAfx.cpp | 1 - util/StdAfx.h | 2 + util/Util.cpp | 94 +- util/Util.h | 16 +- util/UtilLib.h | 6 +- util/util.vcxproj | 107 +- util/util.vcxproj.filters | 76 +- zlib/ChangeLog | 1472 +++++++ zlib/FAQ | 368 ++ zlib/INDEX | 68 + zlib/README | 115 + zlib/adler32.c | 179 + zlib/compress.c | 80 + zlib/crc32.c | 425 ++ zlib/crc32.h | 441 ++ zlib/deflate.c | 1967 +++++++++ zlib/deflate.h | 346 ++ zlib/gzclose.c | 25 + zlib/gzguts.h | 209 + zlib/gzlib.c | 634 +++ zlib/gzread.c | 594 +++ zlib/gzwrite.c | 577 +++ zlib/infback.c | 640 +++ zlib/inffast.c | 340 ++ zlib/inffast.h | 11 + zlib/inffixed.h | 94 + zlib/inflate.c | 1512 +++++++ zlib/inflate.h | 122 + zlib/inftrees.c | 306 ++ zlib/inftrees.h | 62 + zlib/trees.c | 1226 ++++++ zlib/trees.h | 128 + zlib/uncompr.c | 59 + {prebuilt => zlib}/zconf.h | 843 ++-- zlib/zlib.h | 1768 ++++++++ zlib/zlib.vcxproj | 112 + zlib/zlib.vcxproj.filters | 99 + zlib/zutil.c | 324 ++ zlib/zutil.h | 253 ++ 292 files changed, 57242 insertions(+), 7887 deletions(-) create mode 100644 app/targetver.h create mode 100644 diskimg/diskimg.vcxproj.user create mode 100644 mdc/mdc.vcxproj.user create mode 100644 nufxlib/Archive.c create mode 100644 nufxlib/ArchiveIO.c create mode 100644 nufxlib/Bzip2.c create mode 100644 nufxlib/COPYING-LIB create mode 100644 nufxlib/ChangeLog.txt create mode 100644 nufxlib/Compress.c create mode 100644 nufxlib/Crc16.c create mode 100644 nufxlib/Debug.c create mode 100644 nufxlib/Deferred.c create mode 100644 nufxlib/Deflate.c create mode 100644 nufxlib/Entry.c create mode 100644 nufxlib/Expand.c create mode 100644 nufxlib/FileIO.c create mode 100644 nufxlib/Funnel.c create mode 100644 nufxlib/INSTALL create mode 100644 nufxlib/Lzc.c create mode 100644 nufxlib/Lzw.c create mode 100644 nufxlib/Makefile.in create mode 100644 nufxlib/Makefile.msc create mode 100644 nufxlib/MiscStuff.c create mode 100644 nufxlib/MiscStuff.h create mode 100644 nufxlib/MiscUtils.c create mode 100644 nufxlib/NOTES.txt rename {prebuilt => nufxlib}/NufxLib.h (97%) create mode 100644 nufxlib/NufxLibPriv.h create mode 100644 nufxlib/README.txt create mode 100644 nufxlib/Record.c create mode 100644 nufxlib/SourceSink.c create mode 100644 nufxlib/Squeeze.c create mode 100644 nufxlib/SunOS4.h create mode 100644 nufxlib/SysDefs.h create mode 100644 nufxlib/Thread.c create mode 100644 nufxlib/Value.c create mode 100644 nufxlib/Version.c create mode 100644 nufxlib/config.guess create mode 100644 nufxlib/config.h.in create mode 100644 nufxlib/config.sub create mode 100644 nufxlib/configure create mode 100644 nufxlib/configure.in create mode 100644 nufxlib/install-sh create mode 100644 nufxlib/mkinstalldirs create mode 100644 nufxlib/nufxlib.def create mode 100644 nufxlib/nufxlib.vcxproj create mode 100644 nufxlib/nufxlib.vcxproj.filters create mode 100644 nufxlib/samples/Common.h create mode 100644 nufxlib/samples/Exerciser.c create mode 100644 nufxlib/samples/ImgConv.c create mode 100644 nufxlib/samples/Launder.c create mode 100644 nufxlib/samples/Makefile.in create mode 100644 nufxlib/samples/Makefile.msc create mode 100644 nufxlib/samples/README-S.txt create mode 100644 nufxlib/samples/TestBasic.c create mode 100644 nufxlib/samples/TestExtract.c create mode 100644 nufxlib/samples/TestSimple.c create mode 100644 nufxlib/samples/TestTwirl.c delete mode 100644 prebuilt/libnufx.a delete mode 100644 prebuilt/nufxlib2.dll delete mode 100644 prebuilt/nufxlib2.lib delete mode 100644 prebuilt/nufxlib2D.dll delete mode 100644 prebuilt/nufxlib2D.lib delete mode 100644 prebuilt/zdll.lib delete mode 100644 prebuilt/zlib.h delete mode 100644 prebuilt/zlib1.dll create mode 100644 zlib/ChangeLog create mode 100644 zlib/FAQ create mode 100644 zlib/INDEX create mode 100644 zlib/README create mode 100644 zlib/adler32.c create mode 100644 zlib/compress.c create mode 100644 zlib/crc32.c create mode 100644 zlib/crc32.h create mode 100644 zlib/deflate.c create mode 100644 zlib/deflate.h create mode 100644 zlib/gzclose.c create mode 100644 zlib/gzguts.h create mode 100644 zlib/gzlib.c create mode 100644 zlib/gzread.c create mode 100644 zlib/gzwrite.c create mode 100644 zlib/infback.c create mode 100644 zlib/inffast.c create mode 100644 zlib/inffast.h create mode 100644 zlib/inffixed.h create mode 100644 zlib/inflate.c create mode 100644 zlib/inflate.h create mode 100644 zlib/inftrees.c create mode 100644 zlib/inftrees.h create mode 100644 zlib/trees.c create mode 100644 zlib/trees.h create mode 100644 zlib/uncompr.c rename {prebuilt => zlib}/zconf.h (54%) create mode 100644 zlib/zlib.h create mode 100644 zlib/zlib.vcxproj create mode 100644 zlib/zlib.vcxproj.filters create mode 100644 zlib/zutil.c create mode 100644 zlib/zutil.h diff --git a/CP.sln b/CP.sln index d5426c2..35d6367 100644 --- a/CP.sln +++ b/CP.sln @@ -1,12 +1,21 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30501.0 +VisualStudioVersion = 12.0.30723.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "app", "app\app.vcxproj", "{B023611B-7086-46E1-847B-3B21C4732384}" + ProjectSection(ProjectDependencies) = postProject + {B66109F4-217B-43C0-86AA-EB55657E5AC0} = {B66109F4-217B-43C0-86AA-EB55657E5AC0} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "diskimg", "diskimg\diskimg.vcxproj", "{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}" + ProjectSection(ProjectDependencies) = postProject + {B66109F4-217B-43C0-86AA-EB55657E5AC0} = {B66109F4-217B-43C0-86AA-EB55657E5AC0} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdc", "mdc\mdc.vcxproj", "{7DF41D71-C8DC-48AA-B372-4613210310A4}" + ProjectSection(ProjectDependencies) = postProject + {B66109F4-217B-43C0-86AA-EB55657E5AC0} = {B66109F4-217B-43C0-86AA-EB55657E5AC0} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util", "util\util.vcxproj", "{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}" EndProject @@ -14,6 +23,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhfs", "diskimg\libhfs\li EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reformat", "reformat\reformat.vcxproj", "{18BCF397-397E-460C-A1DC-3E26798966E4}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib\zlib.vcxproj", "{B66109F4-217B-43C0-86AA-EB55657E5AC0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nufxlib", "nufxlib\nufxlib.vcxproj", "{C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -44,6 +57,14 @@ Global {18BCF397-397E-460C-A1DC-3E26798966E4}.Debug|Win32.Build.0 = Debug|Win32 {18BCF397-397E-460C-A1DC-3E26798966E4}.Release|Win32.ActiveCfg = Release|Win32 {18BCF397-397E-460C-A1DC-3E26798966E4}.Release|Win32.Build.0 = Release|Win32 + {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Debug|Win32.Build.0 = Debug|Win32 + {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Release|Win32.ActiveCfg = Release|Win32 + {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Release|Win32.Build.0 = Release|Win32 + {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Debug|Win32.ActiveCfg = Debug|Win32 + {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Debug|Win32.Build.0 = Debug|Win32 + {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Release|Win32.ActiveCfg = Release|Win32 + {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/app/ACUArchive.cpp b/app/ACUArchive.cpp index 530b3ad..d1cba20 100644 --- a/app/ACUArchive.cpp +++ b/app/ACUArchive.cpp @@ -111,7 +111,7 @@ AcuEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, errno = 0; if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format("Unable to seek to offset %ld: %s", + pErrMsg->Format(L"Unable to seek to offset %ld: %hs", fOffset, strerror(errno)); goto bail; } @@ -120,7 +120,7 @@ AcuEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(), &expBuf, false, 0); if (nerr != kNuErrNone) { - pErrMsg->Format("File read failed: %s", NuStrError(nerr)); + pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); goto bail; } @@ -146,7 +146,7 @@ AcuEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, *pLength = unsqLen; } else { if (*pLength < unsqLen) { - pErrMsg->Format("buf size %ld too short (%ld)", + pErrMsg->Format(L"buf size %ld too short (%ld)", *pLength, unsqLen); delete[] unsqBuf; goto bail; @@ -162,19 +162,19 @@ AcuEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, if (needAlloc) { dataBuf = new char[len]; if (dataBuf == nil) { - pErrMsg->Format("allocation of %ld bytes failed", len); + pErrMsg->Format(L"allocation of %ld bytes failed", len); goto bail; } } else { if (*pLength < (long) len) { - pErrMsg->Format("buf size %ld too short (%ld)", + pErrMsg->Format(L"buf size %ld too short (%ld)", *pLength, len); goto bail; } dataBuf = *ppText; } if (fread(dataBuf, len, 1, fpArchive->fFp) != 1) { - pErrMsg->Format("File read failed: %s", strerror(errno)); + pErrMsg->Format(L"File read failed: %hs", strerror(errno)); goto bail; } @@ -217,7 +217,7 @@ AcuEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, ASSERT(IDOK != -1 && IDCANCEL != -1); if (which != kDataThread) { - *pErrMsg = "No such fork"; + *pErrMsg = L"No such fork"; goto bail; } @@ -230,7 +230,7 @@ AcuEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, errno = 0; if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format("Unable to seek to offset %ld: %s", + pErrMsg->Format(L"Unable to seek to offset %ld: %hs", fOffset, strerror(errno)); goto bail; } @@ -255,7 +255,7 @@ AcuEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(), &expBuf, false, 0); if (nerr != kNuErrNone) { - pErrMsg->Format("File read failed: %s", NuStrError(nerr)); + pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); goto bail; } @@ -273,7 +273,7 @@ AcuEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, int err = GenericEntry::WriteConvert(outfp, buf, uncLen, &conv, &convHA, &lastCR); if (err != 0) { - pErrMsg->Format("File write failed: %s", strerror(err)); + pErrMsg->Format(L"File write failed: %hs", strerror(err)); delete[] buf; goto bail; } @@ -283,7 +283,7 @@ AcuEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, nerr = CopyData(outfp, conv, convHA, pErrMsg); if (nerr != kNuErrNone) { if (pErrMsg->IsEmpty()) { - pErrMsg->Format("Failed while copying data: %s\n", + pErrMsg->Format(L"Failed while copying data: %hs\n", NuStrError(nerr)); } goto bail; @@ -329,7 +329,7 @@ AcuEntry::CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, /* read a chunk from the source file */ nerr = fpArchive->AcuRead(buf, chunkLen); if (nerr != kNuErrNone) { - pMsg->Format("File read failed: %s.", NuStrError(nerr)); + pMsg->Format(L"File read failed: %hs.", NuStrError(nerr)); goto bail; } @@ -337,7 +337,7 @@ AcuEntry::CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, &convHA, &lastCR); if (err != 0) { - pMsg->Format("File write failed: %s.", strerror(err)); + pMsg->Format(L"File write failed: %hs.", strerror(err)); nerr = kNuErrGeneric; goto bail; } @@ -372,7 +372,7 @@ AcuEntry::TestEntry(CWnd* pMsgWnd) errno = 0; if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { nerr = kNuErrGeneric; - errMsg.Format("Unable to seek to offset %ld: %s\n", + errMsg.Format(L"Unable to seek to offset %ld: %hs\n", fOffset, strerror(errno)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -382,7 +382,7 @@ AcuEntry::TestEntry(CWnd* pMsgWnd) nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(), nil, false, 0); if (nerr != kNuErrNone) { - errMsg.Format("Unsqueeze failed: %s.", NuStrError(nerr)); + errMsg.Format(L"Unsqueeze failed: %hs.", NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -390,7 +390,7 @@ AcuEntry::TestEntry(CWnd* pMsgWnd) errno = 0; if (fseek(fpArchive->fFp, fOffset + len, SEEK_SET) < 0) { nerr = kNuErrGeneric; - errMsg.Format("Unable to seek to offset %ld (file truncated?): %s\n", + errMsg.Format(L"Unable to seek to offset %ld (file truncated?): %hs\n", fOffset, strerror(errno)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -428,16 +428,16 @@ AcuArchive::AppInit(void) * Returns an error string on failure, or "" on success. */ GenericArchive::OpenResult -AcuArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) +AcuArchive::Open(const WCHAR* filename, bool readOnly, CString* pErrMsg) { CString errMsg; fIsReadOnly = true; // ignore "readOnly" errno = 0; - fFp = fopen(filename, "rb"); + fFp = _wfopen(filename, L"rb"); if (fFp == nil) { - errMsg.Format("Unable to open %s: %s.", filename, strerror(errno)); + errMsg.Format(L"Unable to open %ls: %hs.", filename, strerror(errno)); goto bail; } @@ -447,10 +447,10 @@ AcuArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) result = LoadContents(); if (result < 0) { - errMsg.Format("The file is not an ACU archive."); + errMsg.Format(L"The file is not an ACU archive."); goto bail; } else if (result > 0) { - errMsg.Format("Failed while reading data from ACU archive."); + errMsg.Format(L"Failed while reading data from ACU archive."); goto bail; } } @@ -471,10 +471,9 @@ bail: * Returns an error string on failure, or "" on success. */ CString -AcuArchive::New(const char* /*filename*/, const void* /*options*/) +AcuArchive::New(const WCHAR* /*filename*/, const void* /*options*/) { - CString retmsg("Sorry, AppleLink Compression Utility files can't be created."); - return retmsg; + return L"Sorry, AppleLink Compression Utility files can't be created."; } @@ -572,7 +571,7 @@ AcuArchive::Reload(void) DeleteEntries(); if (LoadContents() != 0) { - return "Reload failed."; + return L"Reload failed."; } return ""; @@ -703,14 +702,14 @@ AcuArchive::DumpFileHeader(const AcuFileEntry* pEntry) FormatDate(createWhen, &createStr); FormatDate(modWhen, &modStr); - WMSG1(" Header for file '%s':\n", pEntry->fileName); + WMSG1(" Header for file '%hs':\n", pEntry->fileName); WMSG4(" dataStorageLen=%d eof=%d blockCount=%d checksum=0x%04x\n", pEntry->dataStorageLen, pEntry->dataEof, pEntry->blockCount, pEntry->dataChecksum); WMSG4(" fileType=0x%02x auxType=0x%04x storageType=0x%02x access=0x%04x\n", pEntry->fileType, pEntry->auxType, pEntry->storageType, pEntry->access); - WMSG2(" created %s, modified %s\n", (const char*) createStr, - (const char*) modStr); + WMSG2(" created %ls, modified %ls\n", + (LPCWSTR) createStr, (LPCWSTR) modStr); WMSG2(" fileNameLen=%d headerChecksum=0x%04x\n", pEntry->fileNameLen, pEntry->headerChecksum); } @@ -730,7 +729,8 @@ AcuArchive::CreateEntry(const AcuFileEntry* pEntry) * Create the new entry. */ pNewEntry = new AcuEntry(this); - pNewEntry->SetPathName(pEntry->fileName); + CString fileName(pEntry->fileName); + pNewEntry->SetPathName(fileName); pNewEntry->SetFssep(kAcuFssep); pNewEntry->SetFileType(pEntry->fileType); pNewEntry->SetAuxType(pEntry->auxType); @@ -753,12 +753,12 @@ AcuArchive::CreateEntry(const AcuFileEntry* pEntry) pNewEntry->SetDataForkLen(pEntry->dataEof); if (pEntry->compressionType == kAcuCompNone) { - pNewEntry->SetFormatStr("Uncompr"); + pNewEntry->SetFormatStr(L"Uncompr"); } else if (pEntry->compressionType == kAcuCompSqueeze) { - pNewEntry->SetFormatStr("Squeeze"); + pNewEntry->SetFormatStr(L"Squeeze"); pNewEntry->SetSqueezed(true); } else { - pNewEntry->SetFormatStr("(unknown)"); + pNewEntry->SetFormatStr(L"(unknown)"); pNewEntry->SetSqueezed(false); } @@ -870,7 +870,7 @@ AcuArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) while (pSelEntry != nil) { pEntry = (AcuEntry*) pSelEntry->GetEntry(); - WMSG2(" Testing '%s' (offset=%ld)\n", pEntry->GetDisplayName(), + WMSG2(" Testing '%hs' (offset=%ld)\n", pEntry->GetDisplayName(), pEntry->GetOffset()); SET_PROGRESS_UPDATE2(0, pEntry->GetDisplayName(), nil); @@ -883,7 +883,7 @@ AcuArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) errMsg = "Cancelled."; pMsgWnd->MessageBox(errMsg, title, MB_OK); } else { - errMsg.Format("Failed while testing '%s': %s.", + errMsg.Format(L"Failed while testing '%hs': %hs.", pEntry->GetPathName(), NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); } @@ -894,9 +894,9 @@ AcuArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) } /* show success message */ - errMsg.Format("Tested %d file%s, no errors found.", + errMsg.Format(L"Tested %d file%ls, no errors found.", pSelSet->GetNumEntries(), - pSelSet->GetNumEntries() == 1 ? "" : "s"); + pSelSet->GetNumEntries() == 1 ? L"" : L"s"); pMsgWnd->MessageBox(errMsg); retVal = true; diff --git a/app/ACUArchive.h b/app/ACUArchive.h index 1fe0ba6..6d88a56 100644 --- a/app/ACUArchive.h +++ b/app/ACUArchive.h @@ -6,8 +6,8 @@ /* * AppleLink Compression Utility archive support. */ -#ifndef __ACU_ARCHIVE__ -#define __ACU_ARCHIVE__ +#ifndef APP_ACUARCHIVE_H +#define APP_ACUARCHIVE_H #include "GenericArchive.h" @@ -69,9 +69,9 @@ public: // One-time initialization; returns an error string. static CString AppInit(void); - virtual OpenResult Open(const char* filename, bool readOnly, + virtual OpenResult Open(const WCHAR* filename, bool readOnly, CString* pErrMsg); - virtual CString New(const char* filename, const void* options); + virtual CString New(const WCHAR* filename, const void* options); virtual CString Flush(void) { return ""; } virtual CString Reload(void); virtual bool IsReadOnly(void) const { return fIsReadOnly; }; @@ -84,7 +84,7 @@ public: const AddFilesDialog* pAddOpts) { ASSERT(false); return false; } virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const char* newName) + const WCHAR* newName) { ASSERT(false); return false; } virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet); virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) @@ -92,10 +92,10 @@ public: virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) { ASSERT(false); return false; } virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const char* newName) + const WCHAR* newName) { ASSERT(false); return false; } virtual CString TestVolumeName(const DiskFS* pDiskFS, - const char* newName) const + const WCHAR* newName) const { ASSERT(false); return "!"; } virtual CString TestPathName(const GenericEntry* pGenericEntry, const CString& basePath, const CString& newName, char newFssep) const @@ -219,4 +219,4 @@ private: bool fIsReadOnly; }; -#endif /*__ACU_ARCHIVE__*/ \ No newline at end of file +#endif /*APP_ACUARCHIVE_H*/ diff --git a/app/AboutDialog.cpp b/app/AboutDialog.cpp index f7407a1..925d863 100644 --- a/app/AboutDialog.cpp +++ b/app/AboutDialog.cpp @@ -12,10 +12,9 @@ #include "HelpTopics.h" #include "MyApp.h" #include "resource.h" -#include "../prebuilt/NufxLib.h" +#include "../nufxlib/NufxLib.h" #include "../diskimg/DiskImg.h" -#define ZLIB_DLL -#include "../prebuilt/zlib.h" +#include "../zlib/zlib.h" BEGIN_MESSAGE_MAP(AboutDialog, CDialog) @@ -23,16 +22,16 @@ BEGIN_MESSAGE_MAP(AboutDialog, CDialog) //ON_BN_CLICKED(IDC_ABOUT_ENTER_REG, OnEnterReg) END_MESSAGE_MAP() -static const char* kVersionExtra = +static const WCHAR kVersionExtra[] = #ifdef _DEBUG - " _DEBUG" + L" _DEBUG" #else - "" + L"" #endif #ifdef _DEBUG_LOG - " _LOG" + L" _LOG" #else - "" + L"" #endif ; @@ -80,7 +79,8 @@ AboutDialog::OnInitDialog(void) pStatic = (CStatic*) GetDlgItem(IDC_ZLIB_VERS_TEXT); ASSERT(pStatic != nil); pStatic->GetWindowText(tmpStr); - newVersion.Format(tmpStr, zlibVersion()); + CString zlibVersionStr(zlibVersion()); + newVersion.Format(tmpStr, zlibVersionStr); pStatic->SetWindowText(newVersion); /* and, finally, the ASPI version */ @@ -89,7 +89,7 @@ AboutDialog::OnInitDialog(void) if (DiskImgLib::Global::GetHasASPI()) { CString versionStr; DWORD version = DiskImgLib::Global::GetASPIVersion(); - versionStr.Format("%d.%d.%d.%d", + versionStr.Format(L"%d.%d.%d.%d", version & 0x0ff, (version >> 8) & 0xff, (version >> 16) & 0xff, diff --git a/app/AboutDialog.h b/app/AboutDialog.h index 148c738..2520395 100644 --- a/app/AboutDialog.h +++ b/app/AboutDialog.h @@ -6,8 +6,8 @@ /* * Class definition for About dialog. */ -#ifndef __ABOUT_DIALOG__ -#define __ABOUT_DIALOG__ +#ifndef APP_ABOUTDIALOG_H +#define APP_ABOUTDIALOG_H //#include #include "resource.h" @@ -34,4 +34,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__ABOUT_DIALOG__*/ \ No newline at end of file +#endif /*APP_ABOUTDIALOG_H*/ diff --git a/app/ActionProgressDialog.cpp b/app/ActionProgressDialog.cpp index 1ea8f6d..9698975 100644 --- a/app/ActionProgressDialog.cpp +++ b/app/ActionProgressDialog.cpp @@ -100,7 +100,7 @@ ActionProgressDialog::OnInitDialog(void) * Set the name of the file as it appears in the archive. */ void -ActionProgressDialog::SetArcName(const char* str) +ActionProgressDialog::SetArcName(const WCHAR* str) { CString oldStr; @@ -127,7 +127,7 @@ ActionProgressDialog::GetFileName(void) * Set the name of the file as it appears under Windows. */ void -ActionProgressDialog::SetFileName(const char* str) +ActionProgressDialog::SetFileName(const WCHAR* str) { CString oldStr; diff --git a/app/ActionProgressDialog.h b/app/ActionProgressDialog.h index 1c3d28e..561a7bf 100644 --- a/app/ActionProgressDialog.h +++ b/app/ActionProgressDialog.h @@ -6,8 +6,8 @@ /* * Show the progress of an action like "add" or "extract". */ -#ifndef __ACTIONPROGRESSDIALOG__ -#define __ACTIONPROGRESSDIALOG__ +#ifndef APP_ACTIONPROGRESSDIALOG_H +#define APP_ACTIONPROGRESSDIALOG_H #include "resource.h" @@ -48,8 +48,8 @@ public: DestroyWindow(); } - void SetArcName(const char* str); - void SetFileName(const char* str); + void SetArcName(const WCHAR* str); + void SetFileName(const WCHAR* str); const CString GetFileName(void); int SetProgress(int perc); @@ -62,4 +62,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__ACTIONPROGRESSDIALOG__*/ +#endif /*APP_ACTIONPROGRESSDIALOG_H*/ diff --git a/app/Actions.cpp b/app/Actions.cpp index de08b49..c16b5c0 100644 --- a/app/Actions.cpp +++ b/app/Actions.cpp @@ -82,8 +82,8 @@ MainWindow::HandleView(void) selSet.Dump(); if (selSet.GetNumEntries() == 0) { - MessageBox("Nothing viewable found.", - "No match", MB_OK | MB_ICONEXCLAMATION); + MessageBox(L"Nothing viewable found.", + L"No match", MB_OK | MB_ICONEXCLAMATION); return; } @@ -101,7 +101,7 @@ MainWindow::HandleView(void) // remember which font they used (sticky pref, not in registry) fPreferences.SetPrefString(kPrViewTextTypeFace, vfd.GetTextTypeFace()); fPreferences.SetPrefLong(kPrViewTextPointSize, vfd.GetTextPointSize()); - WMSG2("Preferences: saving view font %d-point '%s'\n", + WMSG2("Preferences: saving view font %d-point '%ls'\n", fPreferences.GetPrefLong(kPrViewTextPointSize), fPreferences.GetPrefString(kPrViewTextTypeFace)); } @@ -364,8 +364,8 @@ MainWindow::OnActionsAddDisks(void) openFilters = kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - dlg.m_ofn.lpstrTitle = "Add Disk Image"; + CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + dlg.m_ofn.lpstrTitle = L"Add Disk Image"; /* file is always opened read-only */ dlg.m_ofn.Flags |= OFN_HIDEREADONLY; @@ -381,14 +381,14 @@ MainWindow::OnActionsAddDisks(void) /* open the image file and analyze it */ dierr = img.OpenImage(dlg.GetPathName(), PathProposal::kLocalFssep, true); if (dierr != kDIErrNone) { - errMsg.Format("Unable to open disk image: %s.", + errMsg.Format(L"Unable to open disk image: %hs.", DiskImgLib::DIStrError(dierr)); MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); goto bail; } if (img.AnalyzeImage() != kDIErrNone) { - errMsg.Format("The file '%s' doesn't seem to hold a valid disk image.", + errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", dlg.GetPathName()); MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); goto bail; @@ -426,8 +426,8 @@ MainWindow::OnActionsAddDisks(void) dierr = img.OverrideFormat(img.GetPhysicalFormat(), imf.fFSFormat, imf.fSectorOrder); if (dierr != kDIErrNone) { - errMsg.Format("Unable to access disk image using selected" - " parameters. Error: %s.", + errMsg.Format(L"Unable to access disk image using selected" + L" parameters. Error: %hs.", DiskImgLib::DIStrError(dierr)); MessageBox(errMsg, failed, MB_OK | MB_ICONSTOP); goto bail; @@ -444,7 +444,7 @@ MainWindow::OnActionsAddDisks(void) dierr = img.OverrideFormat(img.GetPhysicalFormat(), DiskImg::kFormatGenericProDOSOrd, img.GetSectorOrder()); if (dierr != kDIErrNone) { - errMsg.Format("Internal error: couldn't switch to generic ProDOS: %s.", + errMsg.Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", DiskImgLib::DIStrError(dierr)); MessageBox(errMsg, failed, MB_OK | MB_ICONSTOP); goto bail; @@ -459,7 +459,7 @@ MainWindow::OnActionsAddDisks(void) */ ASSERT(dlg.m_ofn.nFileOffset > 0); int len; - len = strlen(dlg.m_ofn.lpstrFile) + 2; + len = wcslen(dlg.m_ofn.lpstrFile) + 2; dlg.m_ofn.lpstrFile[dlg.m_ofn.nFileOffset-1] = '\0'; addOpts.SetFileNames(dlg.m_ofn.lpstrFile, len, dlg.m_ofn.nFileOffset); addOpts.fStoragePrefix = ""; @@ -535,7 +535,7 @@ MainWindow::OnActionsCreateSubdir(void) return; } - WMSG1("Creating subdir in '%s'\n", pEntry->GetPathName()); + WMSG1("Creating subdir in '%ls'\n", pEntry->GetPathName()); csDialog.fBasePath = pEntry->GetPathName(); csDialog.fpArchive = fpOpenArchive; @@ -544,7 +544,7 @@ MainWindow::OnActionsCreateSubdir(void) if (csDialog.DoModal() != IDOK) return; - WMSG1("Creating '%s'\n", csDialog.fNewName); + WMSG1("Creating '%ls'\n", (LPCWSTR) csDialog.fNewName); fpOpenArchive->CreateSubdir(this, pEntry, csDialog.fNewName); fpContentList->Reload(); @@ -630,7 +630,7 @@ MainWindow::OnActionsExtract(void) fPreferences.SetPrefBool(kPrExtractStripFolderNames, extOpts.fStripFolderNames != 0); fPreferences.SetPrefBool(kPrExtractOverwriteExisting, extOpts.fOverwriteExisting != 0); - WMSG1("Requested extract path is '%s'\n", extOpts.fExtractPath); + WMSG1("Requested extract path is '%ls'\n", (LPCWSTR) extOpts.fExtractPath); /* * Create a "selection set" of things to display. @@ -652,8 +652,8 @@ MainWindow::OnActionsExtract(void) //selSet.Dump(); if (selSet.GetNumEntries() == 0) { - MessageBox("No files matched the selection criteria.", - "No match", MB_OK | MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK | MB_ICONEXCLAMATION); return; } @@ -711,7 +711,7 @@ MainWindow::DoBulkExtract(SelectionSet* pSelSet, GenericEntry* pEntry = pSelEntry->GetEntry(); if (pEntry->GetDamaged()) { - WMSG1("Skipping '%s' due to damage\n", pEntry->GetPathName()); + WMSG1("Skipping '%ls' due to damage\n", pEntry->GetPathName()); continue; } @@ -737,7 +737,7 @@ MainWindow::DoBulkExtract(SelectionSet* pSelSet, pEntry->GetRecordKind() == GenericEntry::kRecordKindForkedFile)) { fpActionProgress->SetArcName(pEntry->GetDisplayName()); - fpActionProgress->SetFileName(_T("-")); + fpActionProgress->SetFileName(L"-"); SET_PROGRESS_BEGIN(); if (GetFileParts(pEntry, &pHolder) == 0) { @@ -759,7 +759,7 @@ MainWindow::DoBulkExtract(SelectionSet* pSelSet, pEntry->GetFileType(), pEntry->GetAuxType(), ReformatterSourceFormat(pEntry->GetSourceFS()), - pEntry->GetFileNameExtension()); + pEntry->GetFileNameExtensionA()); pHolder->TestApplicability(); } } @@ -869,7 +869,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, { extractAs2MG = true; } else { - WMSG2("Not extracting funky image '%s' as 2MG (len=%ld)\n", + WMSG2("Not extracting funky image '%ls' as 2MG (len=%ld)\n", pEntry->GetPathName(), pEntry->GetUncompressedLen()); } } @@ -970,20 +970,20 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, */ switch (pOutput->GetOutputKind()) { case ReformatOutput::kOutputText: - if (lastFour.CompareNoCase(".txt") != 0) - tmpPath += ".txt"; + if (lastFour.CompareNoCase(L".txt") != 0) + tmpPath += L".txt"; break; case ReformatOutput::kOutputRTF: - if (lastFour.CompareNoCase(".rtf") != 0) - tmpPath += ".rtf"; + if (lastFour.CompareNoCase(L".rtf") != 0) + tmpPath += L".rtf"; break; case ReformatOutput::kOutputCSV: - if (lastFour.CompareNoCase(".csv") != 0) - tmpPath += ".csv"; + if (lastFour.CompareNoCase(L".csv") != 0) + tmpPath += L".csv"; break; case ReformatOutput::kOutputBitmap: - if (lastFour.CompareNoCase(".bmp") != 0) - tmpPath += ".bmp"; + if (lastFour.CompareNoCase(L".bmp") != 0) + tmpPath += L".bmp"; break; case ReformatOutput::kOutputRaw: noChangePath = true; @@ -1011,12 +1011,12 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, */ outputPath = adjustedExtractPath; outputPath += convName; - outputPath += ".2mg"; + outputPath += L".2mg"; } /* update the display in case we renamed it */ if (outputPath != fpActionProgress->GetFileName()) { - WMSG2(" Renamed our output, from '%s' to '%s'\n", + WMSG2(" Renamed our output, from '%ls' to '%ls'\n", (LPCTSTR) fpActionProgress->GetFileName(), outputPath); fpActionProgress->SetFileName(outputPath); } @@ -1026,7 +1026,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, */ fpActionProgress->SetArcName(pathProp.fStoredPathName); fpActionProgress->SetFileName(outputPath); - WMSG2("Extracting from '%s' to '%s'\n", + WMSG2("Extracting from '%ls' to '%ls'\n", pathProp.fStoredPathName, outputPath); SET_PROGRESS_BEGIN(); @@ -1048,8 +1048,8 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, /* update the display in case they renamed the file */ if (outputPath != fpActionProgress->GetFileName()) { - WMSG2(" Detected rename, from '%s' to '%s'\n", - (LPCTSTR) fpActionProgress->GetFileName(), outputPath); + WMSG2(" Detected rename, from '%ls' to '%ls'\n", + (LPCWSTR) fpActionProgress->GetFileName(), outputPath); fpActionProgress->SetFileName(outputPath); } @@ -1082,7 +1082,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, ASSERT(ftell(fp) == 0); err = header.WriteHeader(fp); if (err != 0) { - errMsg.Format("Unable to save 2MG file '%s': %s\n", + errMsg.Format(L"Unable to save 2MG file '%ls': %hs\n", outputPath, strerror(err)); fpActionProgress->MessageBox(errMsg, failed, MB_OK | MB_ICONERROR); @@ -1166,7 +1166,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, pOutput->GetTextLen(), 1, fp) != 1) err = errno; if (err != 0) { - errMsg.Format("Unable to save reformatted file '%s': %s\n", + errMsg.Format(L"Unable to save reformatted file '%ls': %hs\n", outputPath, strerror(err)); fpActionProgress->MessageBox(errMsg, failed, MB_OK | MB_ICONERROR); @@ -1179,7 +1179,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, ASSERT(pOutput->GetDIB() != nil); int err = pOutput->GetDIB()->WriteToFile(fp); if (err != 0) { - errMsg.Format("Unable to save bitmap '%s': %s\n", + errMsg.Format(L"Unable to save bitmap '%ls': %hs\n", outputPath, strerror(err)); fpActionProgress->MessageBox(errMsg, failed, MB_OK | MB_ICONERROR); @@ -1205,7 +1205,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, err = GenericEntry::WriteConvert(fp, pOutput->GetTextBuf(), pOutput->GetTextLen(), &thisConv, &thisConvHA, &lastCR); if (err != 0) { - errMsg.Format("Unable to write file '%s': %s\n", + errMsg.Format(L"Unable to write file '%ls': %hs\n", outputPath, strerror(err)); fpActionProgress->MessageBox(errMsg, failed, MB_OK | MB_ICONERROR); @@ -1230,7 +1230,7 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, CString msg; int result; ASSERT(fpActionProgress != nil); - WMSG3("Extracting '%s', requesting thisConv=%d, convHA=%d\n", + WMSG3("Extracting '%ls', requesting thisConv=%d, convHA=%d\n", outputPath, thisConv, convHA); result = pEntry->ExtractThreadToFile(thread, fp, thisConv, convHA, &msg); @@ -1239,11 +1239,11 @@ MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, CString msg; msg.LoadString(IDS_OPERATION_CANCELLED); fpActionProgress->MessageBox(msg, - "CiderPress", MB_OK | MB_ICONEXCLAMATION); + L"CiderPress", MB_OK | MB_ICONEXCLAMATION); } else { - WMSG2(" FAILED on '%s': %s\n", outputPath, msg); - errMsg.Format("Unable to extract file '%s': %s\n", - outputPath, (LPCTSTR) msg); + WMSG2(" FAILED on '%ls': %ls\n", outputPath, msg); + errMsg.Format(L"Unable to extract file '%ls': %ls\n", + outputPath, msg); fpActionProgress->MessageBox(errMsg, failed, MB_OK | MB_ICONERROR); } @@ -1309,11 +1309,11 @@ did_rename: if (*pOverwriteExisting) { do_overwrite: /* delete existing */ - WMSG1(" Deleting existing '%s'\n", (LPCTSTR) *pOutputPath); - if (::unlink(*pOutputPath) != 0) { + WMSG1(" Deleting existing '%ls'\n", (LPCWSTR) *pOutputPath); + if (::_wunlink(*pOutputPath) != 0) { err = errno; - WMSG2(" Failed deleting '%s', err=%d\n", - (LPCTSTR)*pOutputPath, err); + WMSG2(" Failed deleting '%ls', err=%d\n", + (LPCWSTR)*pOutputPath, err); if (err == ENOENT) { /* user might have removed it while dialog was up */ err = 0; @@ -1324,7 +1324,7 @@ do_overwrite: } } else if (*pOvwrForAll) { /* never overwrite */ - WMSG1(" Skipping '%s'\n", (LPCTSTR) *pOutputPath); + WMSG1(" Skipping '%ls'\n", (LPCWSTR) *pOutputPath); goto bail; } else { /* no firm policy, ask the user */ @@ -1360,7 +1360,7 @@ do_overwrite: if (err != 0) goto bail; - *pFp = fopen(*pOutputPath, "wb"); + *pFp = _wfopen(*pOutputPath, L"wb"); if (*pFp == nil) err = errno ? errno : -1; /* fall through with error */ @@ -1369,14 +1369,14 @@ bail: /* if we failed, tell the user why */ if (err == ENOTDIR) { /* part of the output path exists, but isn't a directory */ - msg.Format("Unable to create folders for '%s': part of the path " - "already exists but is not a folder.\n", + msg.Format(L"Unable to create folders for '%ls': part of the path " + L"already exists but is not a folder.\n", *pOutputPath); fpActionProgress->MessageBox(msg, failed, MB_OK | MB_ICONERROR); return IDCANCEL; } else if (err == EINVAL) { /* invalid argument; assume it's an invalid filename */ - msg.Format("Unable to create file '%s': invalid filename.\n", + msg.Format(L"Unable to create file '%ls': invalid filename.\n", *pOutputPath); fpActionProgress->MessageBox(msg, failed, MB_OK | MB_ICONERROR); return IDCANCEL; @@ -1385,7 +1385,7 @@ bail: WMSG0("Cancelling due to user request\n"); return IDCANCEL; } else if (err != 0) { - msg.Format("Unable to create file '%s': %s\n", + msg.Format(L"Unable to create file '%ls': %hs\n", *pOutputPath, strerror(err)); fpActionProgress->MessageBox(msg, failed, MB_OK | MB_ICONERROR); return IDCANCEL; @@ -1445,8 +1445,8 @@ MainWindow::OnActionsTest(void) if (selSet.GetNumEntries() == 0) { /* should be impossible */ - MessageBox("No files matched the selection criteria.", - "No match", MB_OK|MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK|MB_ICONEXCLAMATION); return; } @@ -1543,16 +1543,16 @@ MainWindow::OnActionsDelete(void) selSet.CreateFromSelection(fpContentList, threadMask); if (selSet.GetNumEntries() == 0) { /* can happen if they selected volume dir only */ - MessageBox("Nothing to delete.", - "No match", MB_OK | MB_ICONEXCLAMATION); + MessageBox(L"Nothing to delete.", + L"No match", MB_OK | MB_ICONEXCLAMATION); return; } CString appName, msg; appName.LoadString(IDS_MB_APP_NAME); - msg.Format("Delete %d file%s?", selSet.GetNumEntries(), - selSet.GetNumEntries() == 1 ? "" : "s"); + msg.Format(L"Delete %d file%ls?", selSet.GetNumEntries(), + selSet.GetNumEntries() == 1 ? L"" : L"s"); if (MessageBox(msg, appName, MB_OKCANCEL | MB_ICONQUESTION) != IDOK) return; @@ -1614,8 +1614,8 @@ MainWindow::OnActionsRename(void) if (selSet.GetNumEntries() == 0) { /* should be impossible */ - MessageBox("No files matched the selection criteria.", - "No match", MB_OK | MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK | MB_ICONEXCLAMATION); return; } @@ -1863,8 +1863,8 @@ MainWindow::OnActionsRecompress(void) if (selSet.GetNumEntries() == 0) { /* should be impossible */ - MessageBox("No files matched the selection criteria.", - "No match", MB_OK|MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK|MB_ICONEXCLAMATION); return; } @@ -1896,10 +1896,10 @@ MainWindow::OnActionsRecompress(void) ASSERT(beforeUncomp == afterUncomp); appName.LoadString(IDS_MB_APP_NAME); - msg.Format("Total uncompressed size of all files:\t%.1fK\r\n" - "Total size before recompress:\t\t%.1fK\r\n" - "Total size after recompress:\t\t%.1fK\r\n" - "Overall reduction:\t\t\t%.1fK", + msg.Format(L"Total uncompressed size of all files:\t%.1fK\r\n" + L"Total size before recompress:\t\t%.1fK\r\n" + L"Total size after recompress:\t\t%.1fK\r\n" + L"Overall reduction:\t\t\t%.1fK", beforeUncomp / 1024.0, beforeComp / 1024.0, afterComp / 1024.0, (beforeComp - afterComp) / 1024.0); MessageBox(msg, appName, MB_OK|MB_ICONINFORMATION); @@ -1993,8 +1993,8 @@ MainWindow::OnActionsConvDisk(void) if (selSet.GetNumEntries() == 0) { /* should be impossible */ - MessageBox("No files matched the selection criteria.", - "No match", MB_OK|MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK|MB_ICONEXCLAMATION); return; } @@ -2004,18 +2004,18 @@ MainWindow::OnActionsConvDisk(void) //xferOpts.fUseSparseBlocks = // fPreferences.GetPrefBool(kPrProDOSUseSparse) != 0; - WMSG1("New volume name will be '%s'\n", selOpts.fVolName); + WMSG1("New volume name will be '%ls'\n", (LPCWSTR) selOpts.fVolName); /* * Create a new disk image. */ CString filename, saveFolder, errStr; - CFileDialog dlg(FALSE, _T("po"), NULL, + CFileDialog dlg(FALSE, L"po", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "Disk Images (*.po)|*.po||", this); + L"Disk Images (*.po)|*.po||", this); - dlg.m_ofn.lpstrTitle = "New Disk Image (.PO)"; + dlg.m_ofn.lpstrTitle = L"New Disk Image (.PO)"; dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) { @@ -2028,7 +2028,7 @@ MainWindow::OnActionsConvDisk(void) fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); filename = dlg.GetPathName(); - WMSG1(" Will xfer to file '%s'\n", filename); + WMSG1(" Will xfer to file '%ls'\n", filename); /* remove file if it already exists */ CString errMsg; @@ -2149,8 +2149,8 @@ MainWindow::OnActionsConvFile(void) //selSet.Dump(); if (selSet.GetNumEntries() == 0) { - MessageBox("No files matched the selection criteria.", - "No match", MB_OK|MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK|MB_ICONEXCLAMATION); return; } @@ -2164,11 +2164,11 @@ MainWindow::OnActionsConvFile(void) */ CString filename, saveFolder, errStr; - CFileDialog dlg(FALSE, _T("shk"), NULL, + CFileDialog dlg(FALSE, L"shk", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "ShrinkIt Archives (*.shk)|*.shk||", this); + L"ShrinkIt Archives (*.shk)|*.shk||", this); - dlg.m_ofn.lpstrTitle = "New Archive"; + dlg.m_ofn.lpstrTitle = L"New Archive"; dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) { @@ -2181,7 +2181,7 @@ MainWindow::OnActionsConvFile(void) fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); filename = dlg.GetPathName(); - WMSG1(" Will xfer to file '%s'\n", filename); + WMSG1(" Will xfer to file '%ls'\n", filename); /* remove file if it already exists */ CString errMsg; @@ -2273,9 +2273,9 @@ MainWindow::OnActionsConvFromWav(void) CassetteDialog dlg; CString fileName, saveFolder; - CFileDialog fileDlg(TRUE, "wav", NULL, OFN_FILEMUSTEXIST|OFN_HIDEREADONLY, - "Sound Files (*.wav)|*.wav||", this); - fileDlg.m_ofn.lpstrTitle = "Open Sound File"; + CFileDialog fileDlg(TRUE, L"wav", NULL, OFN_FILEMUSTEXIST|OFN_HIDEREADONLY, + L"Sound Files (*.wav)|*.wav||", this); + fileDlg.m_ofn.lpstrTitle = L"Open Sound File"; fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenWAVFolder); if (fileDlg.DoModal() != IDOK) @@ -2286,7 +2286,7 @@ MainWindow::OnActionsConvFromWav(void) fPreferences.SetPrefString(kPrOpenWAVFolder, saveFolder); fileName = fileDlg.GetPathName(); - WMSG1("Opening WAV file '%s'\n", fileName); + WMSG1("Opening WAV file '%ls'\n", fileName); dlg.fFileName = fileName; // pass in fpOpenArchive? @@ -2340,7 +2340,7 @@ MainWindow::SaveToArchive(GenericArchive::FileDetails* pDetails, else dataBuf = new unsigned char[dataLen]; if (dataBuf == nil) { - errMsg.Format("Unable to allocate %ld bytes", dataLen); + errMsg.Format(L"Unable to allocate %ld bytes", dataLen); goto bail; } memcpy(dataBuf, dataBufIn, dataLen); @@ -2368,7 +2368,7 @@ MainWindow::SaveToArchive(GenericArchive::FileDetails* pDetails, } if (pTargetSubdir != nil) { storagePrefix = pTargetSubdir->GetPathName(); - WMSG1("--- using storagePrefix '%s'\n", (const char*) storagePrefix); + WMSG1("--- using storagePrefix '%ls'\n", storagePrefix); } if (!storagePrefix.IsEmpty()) { CString tmpStr, tmpFileName; @@ -2421,9 +2421,9 @@ MainWindow::OnActionsImportBAS(void) ImportBASDialog dlg; CString fileName, saveFolder; - CFileDialog fileDlg(TRUE, "txt", NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, - "Text files (*.txt)|*.txt||", this); - fileDlg.m_ofn.lpstrTitle = "Open Text File"; + CFileDialog fileDlg(TRUE, L"txt", NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, + L"Text files (*.txt)|*.txt||", this); + fileDlg.m_ofn.lpstrTitle = L"Open Text File"; fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrAddFileFolder); if (fileDlg.DoModal() != IDOK) @@ -2434,7 +2434,7 @@ MainWindow::OnActionsImportBAS(void) fPreferences.SetPrefString(kPrAddFileFolder, saveFolder); fileName = fileDlg.GetPathName(); - WMSG1("Opening TXT file '%s'\n", fileName); + WMSG1("Opening TXT file '%ls'\n", fileName); dlg.fFileName = fileName; // pass in fpOpenArchive? @@ -2530,7 +2530,7 @@ MainWindow::GetFilePart(const GenericEntry* pEntry, int whichThread, if (threadLen > fPreferences.GetPrefLong(kPrMaxViewFileSize)) { errMsg.Format( - "[File size (%I64d KBytes) exceeds file viewer maximum (%ld KBytes).]\n", + L"[File size (%I64d KBytes) exceeds file viewer maximum (%ld KBytes).]\n", ((LONGLONG) threadLen + 1023) / 1024, (fPreferences.GetPrefLong(kPrMaxViewFileSize) + 1023) / 1024); pHolder->SetErrorMsg(part, errMsg); @@ -2546,12 +2546,13 @@ MainWindow::GetFilePart(const GenericEntry* pEntry, int whichThread, pHolder->SetSourceBuf(part, (unsigned char*) buf, len); } else if (result == IDCANCEL) { /* not expected */ - errMsg = "Cancelled!"; + errMsg = L"Cancelled!"; pHolder->SetErrorMsg(part, errMsg); ASSERT(buf == nil); } else { /* transfer error message to ReformatHolder buffer */ - WMSG1("Got error message from ExtractThread: '%s'\n", errMsg); + WMSG1("Got error message from ExtractThread: '%ls'\n", + (LPCWSTR) errMsg); pHolder->SetErrorMsg(part, errMsg); ASSERT(buf == nil); } diff --git a/app/AddClashDialog.h b/app/AddClashDialog.h index 47c1a28..133fe13 100644 --- a/app/AddClashDialog.h +++ b/app/AddClashDialog.h @@ -6,8 +6,8 @@ /* * Resolve a filename clash when adding files. */ -#ifndef __ADDCLASHDIALOG__ -#define __ADDCLASHDIALOG__ +#ifndef APP_ADDCLASHDIALOG_H +#define APP_ADDCLASHDIALOG_H /* * @@ -36,4 +36,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__ADDCLASHDIALOG__*/ +#endif /*APP_ADDCLASHDIALOG_H*/ diff --git a/app/AddFilesDialog.cpp b/app/AddFilesDialog.cpp index 7a469a5..81e6004 100644 --- a/app/AddFilesDialog.cpp +++ b/app/AddFilesDialog.cpp @@ -125,7 +125,7 @@ AddFilesDialog::ValidateStoragePrefix(void) const char kFssep = PathProposal::kDefaultStoredFssep; if (fStoragePrefix[0] == kFssep || fStoragePrefix.Right(1) == kFssep) { CString errMsg; - errMsg.Format("The storage prefix may not start or end with '%c'.", + errMsg.Format(L"The storage prefix may not start or end with '%c'.", kFssep); MessageBox(errMsg, m_ofn.lpstrTitle, MB_OK | MB_ICONWARNING); return false; diff --git a/app/AddFilesDialog.h b/app/AddFilesDialog.h index ff13e5e..2ed5b29 100644 --- a/app/AddFilesDialog.h +++ b/app/AddFilesDialog.h @@ -7,8 +7,8 @@ * File selection dialog, a sub-class of "Open" that allows multiple selection * of both files and directories. */ -#ifndef __ADDFILESDIALOG__ -#define __ADDFILESDIALOG__ +#ifndef APP_ADDFILESDIALOG_H +#define APP_ADDFILESDIALOG_H #include "../diskimg/DiskImg.h" #include "../util/UtilLib.h" @@ -23,9 +23,9 @@ class AddFilesDialog : public SelectFilesDialog { public: AddFilesDialog(CWnd* pParentWnd = NULL) : - SelectFilesDialog("IDD_ADD_FILES", pParentWnd) + SelectFilesDialog(L"IDD_ADD_FILES", pParentWnd) { - SetWindowTitle(_T("Add Files...")); + SetWindowTitle(L"Add Files..."); fStoragePrefix = ""; fStoragePrefixEnable = true; fIncludeSubfolders = FALSE; @@ -77,4 +77,4 @@ private: //DECLARE_MESSAGE_MAP() }; -#endif /*__ADDFILESDIALOG__*/ \ No newline at end of file +#endif /*APP_ADDFILESDIALOG_H*/ diff --git a/app/ArchiveInfoDialog.cpp b/app/ArchiveInfoDialog.cpp index f73cfcb..ec43e18 100644 --- a/app/ArchiveInfoDialog.cpp +++ b/app/ArchiveInfoDialog.cpp @@ -9,7 +9,7 @@ #include "StdAfx.h" #include "HelpTopics.h" #include "ArchiveInfoDialog.h" -#include "../prebuilt/NufxLib.h" +#include "../nufxlib/NufxLib.h" /* * =========================================================================== @@ -60,12 +60,13 @@ NufxArchiveInfoDialog::OnInitDialog(void) ASSERT(pMasterHeader != nil); pWnd = GetDlgItem(IDC_AI_FILENAME); - pWnd->SetWindowText(fpArchive->GetPathName()); + CString pathName(fpArchive->GetPathName()); + pWnd->SetWindowText(pathName); pWnd = GetDlgItem(IDC_AINUFX_RECORDS); nerr = NuGetAttr(pNuArchive, kNuAttrNumRecords, &attr); if (nerr == kNuErrNone) - tmpStr.Format("%ld", attr); + tmpStr.Format(L"%ld", attr); else tmpStr = notAvailable; pWnd->SetWindowText(tmpStr); @@ -73,36 +74,36 @@ NufxArchiveInfoDialog::OnInitDialog(void) pWnd = GetDlgItem(IDC_AINUFX_FORMAT); nerr = NuGetAttr(pNuArchive, kNuAttrArchiveType, &attr); switch (attr) { - case kNuArchiveNuFX: tmpStr = "NuFX"; break; - case kNuArchiveNuFXInBNY: tmpStr = "NuFX in Binary II"; break; - case kNuArchiveNuFXSelfEx: tmpStr = "Self-extracting NuFX"; break; - case kNuArchiveNuFXSelfExInBNY: tmpStr = "Self-extracting NuFX in Binary II"; + case kNuArchiveNuFX: tmpStr = L"NuFX"; break; + case kNuArchiveNuFXInBNY: tmpStr = L"NuFX in Binary II"; break; + case kNuArchiveNuFXSelfEx: tmpStr = L"Self-extracting NuFX"; break; + case kNuArchiveNuFXSelfExInBNY: tmpStr = L"Self-extracting NuFX in Binary II"; break; - case kNuArchiveBNY: tmpStr = "Binary II"; break; + case kNuArchiveBNY: tmpStr = L"Binary II"; break; default: - tmpStr = "(unknown)"; + tmpStr = L"(unknown)"; break; }; pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_AINUFX_MASTERVERSION); - tmpStr.Format("%ld", pMasterHeader->mhMasterVersion); + tmpStr.Format(L"%ld", pMasterHeader->mhMasterVersion); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_AINUFX_CREATEWHEN); when = NufxArchive::DateTimeToSeconds(&pMasterHeader->mhArchiveCreateWhen); - tmpStr.Format("%.24s", ctime(&when)); + tmpStr.Format(L"%.24hs", ctime(&when)); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_AINUFX_MODIFYWHEN); when = NufxArchive::DateTimeToSeconds(&pMasterHeader->mhArchiveModWhen); - tmpStr.Format("%.24s", ctime(&when)); + tmpStr.Format(L"%.24hs", ctime(&when)); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_AINUFX_JUNKSKIPPED); nerr = NuGetAttr(pNuArchive, kNuAttrJunkOffset, &attr); if (nerr == kNuErrNone) - tmpStr.Format("%ld bytes", attr); + tmpStr.Format(L"%ld bytes", attr); else tmpStr = notAvailable; pWnd->SetWindowText(tmpStr); @@ -146,10 +147,12 @@ DiskArchiveInfoDialog::OnInitDialog(void) pWnd->SetWindowText(fpArchive->GetPathName()); pWnd = GetDlgItem(IDC_AIDISK_OUTERFORMAT); - pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetOuterFormat())); + CStringW outerFormat(DiskImg::ToString(pDiskImg->GetOuterFormat())); + pWnd->SetWindowText(outerFormat); pWnd = GetDlgItem(IDC_AIDISK_FILEFORMAT); - pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetFileFormat())); + CStringW fileFormat(DiskImg::ToString(pDiskImg->GetFileFormat())); + pWnd->SetWindowText(fileFormat); pWnd = GetDlgItem(IDC_AIDISK_PHYSICALFORMAT); DiskImg::PhysicalFormat physicalFormat = pDiskImg->GetPhysicalFormat(); @@ -160,13 +163,14 @@ DiskArchiveInfoDialog::OnInitDialog(void) CString tmpStr; const DiskImg::NibbleDescr* pNibbleDescr = pDiskImg->GetNibbleDescr(); if (pNibbleDescr != nil) - tmpStr.Format("%s, layout is \"%s\"", + tmpStr.Format(L"%hs, layout is \"%hs\"", DiskImg::ToString(physicalFormat), pNibbleDescr->description); else tmpStr = DiskImg::ToString(physicalFormat); // unexpected pWnd->SetWindowText(tmpStr); } else { - pWnd->SetWindowText(DiskImg::ToString(physicalFormat)); + CString physicalFormat(DiskImg::ToString(physicalFormat)); + pWnd->SetWindowText(physicalFormat); } FillInVolumeInfo(pDiskFS); @@ -178,7 +182,7 @@ DiskArchiveInfoDialog::OnInitDialog(void) CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL); int idx = 0; - AddSubVolumes(pDiskFS, "", &idx); + AddSubVolumes(pDiskFS, L"", &idx); ASSERT(idx > 0); // must have at least the top-level DiskFS pCombo->SetCurSel(0); @@ -192,7 +196,7 @@ DiskArchiveInfoDialog::OnInitDialog(void) * Recursively add sub-volumes to the list. */ void -DiskArchiveInfoDialog::AddSubVolumes(const DiskFS* pDiskFS, const char* prefix, +DiskArchiveInfoDialog::AddSubVolumes(const DiskFS* pDiskFS, const WCHAR* prefix, int* pIdx) { CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL); @@ -213,7 +217,7 @@ DiskArchiveInfoDialog::AddSubVolumes(const DiskFS* pDiskFS, const char* prefix, DiskFS::SubVolume* pSubVol; pSubVol = pDiskFS->GetNextSubVolume(nil); tmpStr = prefix; - tmpStr += " "; + tmpStr += L" "; while (pSubVol != nil) { AddSubVolumes(pSubVol->GetDiskFS(), tmpStr, pIdx); @@ -244,19 +248,21 @@ void DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) { const DiskImg* pDiskImg = pDiskFS->GetDiskImg(); - CString unknown = "(unknown)"; + CString unknown = L"(unknown)"; CString tmpStr; DIError dierr; CWnd* pWnd; pWnd = GetDlgItem(IDC_AIDISK_SECTORORDER); - pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetSectorOrder())); + CStringW sectorOrderW(DiskImg::ToString(pDiskImg->GetSectorOrder())); + pWnd->SetWindowText(sectorOrderW); pWnd = GetDlgItem(IDC_AIDISK_FSFORMAT); - pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetFSFormat())); + CStringW fsFormat(DiskImg::ToString(pDiskImg->GetFSFormat())); + pWnd->SetWindowText(fsFormat); pWnd = GetDlgItem(IDC_AIDISK_FILECOUNT); - tmpStr.Format("%ld", pDiskFS->GetFileCount()); + tmpStr.Format(L"%ld", pDiskFS->GetFileCount()); pWnd->SetWindowText(tmpStr); long totalUnits, freeUnits; @@ -270,11 +276,11 @@ DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) if (unitSize == DiskImgLib::kBlockSize) { pWnd = GetDlgItem(IDC_AIDISK_CAPACITY); GetReducedSize(totalUnits, unitSize, &reducedSize); - tmpStr.Format("%ld blocks (%s)", + tmpStr.Format(L"%ld blocks (%ls)", totalUnits, reducedSize); if (totalUnits != pDiskImg->GetNumBlocks()) { CString tmpStr2; - tmpStr2.Format(", image has room for %ld blocks", + tmpStr2.Format(L", image has room for %ld blocks", pDiskImg->GetNumBlocks()); tmpStr += tmpStr2; } @@ -282,7 +288,7 @@ DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) pWnd = GetDlgItem(IDC_AIDISK_FREESPACE); GetReducedSize(freeUnits, unitSize, &reducedSize); - tmpStr.Format("%ld blocks (%s)", + tmpStr.Format(L"%ld blocks (%ls)", freeUnits, reducedSize); pWnd->SetWindowText(tmpStr); } else { @@ -290,13 +296,13 @@ DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) pWnd = GetDlgItem(IDC_AIDISK_CAPACITY); GetReducedSize(totalUnits, unitSize, &reducedSize); - tmpStr.Format("%ld sectors (%s)", + tmpStr.Format(L"%ld sectors (%ls)", totalUnits, reducedSize); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_AIDISK_FREESPACE); GetReducedSize(freeUnits, unitSize, &reducedSize); - tmpStr.Format("%ld sectors (%s)", + tmpStr.Format(L"%ld sectors (%ls)", freeUnits, reducedSize); pWnd->SetWindowText(tmpStr); } @@ -306,10 +312,10 @@ DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) if (pDiskImg->GetHasBlocks()) { totalUnits = pDiskImg->GetNumBlocks(); GetReducedSize(totalUnits, DiskImgLib::kBlockSize, &reducedSize); - tmpStr.Format("%ld blocks (%s)", + tmpStr.Format(L"%ld blocks (%ls)", totalUnits, reducedSize); } else if (pDiskImg->GetHasSectors()) { - tmpStr.Format("%ld tracks, %d sectors per track", + tmpStr.Format(L"%ld tracks, %d sectors per track", pDiskImg->GetNumTracks(), pDiskImg->GetNumSectPerTrack()); } else { tmpStr = unknown; @@ -321,18 +327,20 @@ DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) } pWnd = GetDlgItem(IDC_AIDISK_WRITEABLE); - tmpStr = pDiskFS->GetReadWriteSupported() ? "Yes" : "No"; + tmpStr = pDiskFS->GetReadWriteSupported() ? L"Yes" : L"No"; pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_AIDISK_DAMAGED); - tmpStr = pDiskFS->GetFSDamaged() ? "Yes" : "No"; + tmpStr = pDiskFS->GetFSDamaged() ? L"Yes" : L"No"; pWnd->SetWindowText(tmpStr); const char* cp; - char* outp; + WCHAR* outp; pWnd = GetDlgItem(IDC_AIDISK_NOTES); cp = pDiskImg->GetNotes(); + // GetBuffer wants length in code units, which will be 2x since it's + // wide chars. The 2x mult below is for worst-case linefeed conversion. outp = tmpStr.GetBuffer(strlen(cp) * 2 +1); /* convert '\n' to '\r\n' */ while (*cp != '\0') { @@ -361,18 +369,18 @@ DiskArchiveInfoDialog::GetReducedSize(long numUnits, int unitSize, if (sizeInBytes < 0) { ASSERT(false); - pOut->Format(""); + pOut->Format(L""); return; } if (sizeInBytes >= 1024*1024*1024) { reducedSize = (long) (sizeInBytes / (1024*1024)); - pOut->Format("%.2fGB", reducedSize / 1024.0); + pOut->Format(L"%.2fGB", reducedSize / 1024.0); } else if (sizeInBytes >= 1024*1024) { reducedSize = (long) (sizeInBytes / 1024); - pOut->Format("%.2fMB", reducedSize / 1024.0); + pOut->Format(L"%.2fMB", reducedSize / 1024.0); } else { - pOut->Format("%.2fKB", ((long) sizeInBytes) / 1024.0); + pOut->Format(L"%.2fKB", ((long) sizeInBytes) / 1024.0); } } @@ -398,7 +406,7 @@ BnyArchiveInfoDialog::OnInitDialog(void) pWnd = GetDlgItem(IDC_AI_FILENAME); pWnd->SetWindowText(fpArchive->GetPathName()); - tmpStr.Format("%ld", fpArchive->GetNumEntries()); + tmpStr.Format(L"%ld", fpArchive->GetNumEntries()); pWnd = GetDlgItem(IDC_AIBNY_RECORDS); pWnd->SetWindowText(tmpStr); @@ -425,7 +433,7 @@ AcuArchiveInfoDialog::OnInitDialog(void) pWnd = GetDlgItem(IDC_AI_FILENAME); pWnd->SetWindowText(fpArchive->GetPathName()); - tmpStr.Format("%ld", fpArchive->GetNumEntries()); + tmpStr.Format(L"%ld", fpArchive->GetNumEntries()); pWnd = GetDlgItem(IDC_AIBNY_RECORDS); pWnd->SetWindowText(tmpStr); diff --git a/app/ArchiveInfoDialog.h b/app/ArchiveInfoDialog.h index 4eedd03..8dab0f1 100644 --- a/app/ArchiveInfoDialog.h +++ b/app/ArchiveInfoDialog.h @@ -6,8 +6,8 @@ /* * Definitions for the ArchiveInfo set of dialog classes. */ -#ifndef __ARCHIVEINFODIALOG__ -#define __ARCHIVEINFODIALOG__ +#ifndef APP_ARCHIVEINFODIALOG_H +#define APP_ARCHIVEINFODIALOG_H #include "resource.h" #include "GenericArchive.h" @@ -69,7 +69,7 @@ private: afx_msg void OnSubVolSelChange(void); void FillInVolumeInfo(const DiskFS* pDiskFS); - void AddSubVolumes(const DiskFS* pDiskFS, const char* prefix, + void AddSubVolumes(const DiskFS* pDiskFS, const WCHAR* prefix, int* pIdx); void GetReducedSize(long numUnits, int unitSize, CString* pOut) const; @@ -115,4 +115,4 @@ private: AcuArchive* fpArchive; }; -#endif /*__ARCHIVEINFODIALOG__*/ +#endif /*APP_ARCHIVEINFODIALOG_H*/ diff --git a/app/BNYArchive.cpp b/app/BNYArchive.cpp index 4414584..eb70a6c 100644 --- a/app/BNYArchive.cpp +++ b/app/BNYArchive.cpp @@ -74,7 +74,7 @@ BnyEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, errno = 0; if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format("Unable to seek to offset %ld: %s", + pErrMsg->Format(L"Unable to seek to offset %ld: %hs", fOffset, strerror(errno)); goto bail; } @@ -83,7 +83,7 @@ BnyEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(), &expBuf, true, kBNYBlockSize); if (nerr != kNuErrNone) { - pErrMsg->Format("File read failed: %s", NuStrError(nerr)); + pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); goto bail; } @@ -108,7 +108,7 @@ BnyEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, *pLength = unsqLen; } else { if (*pLength < unsqLen) { - pErrMsg->Format("buf size %ld too short (%ld)", + pErrMsg->Format(L"buf size %ld too short (%ld)", *pLength, unsqLen); delete[] unsqBuf; goto bail; @@ -124,19 +124,19 @@ BnyEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, if (needAlloc) { dataBuf = new char[len]; if (dataBuf == nil) { - pErrMsg->Format("allocation of %ld bytes failed", len); + pErrMsg->Format(L"allocation of %ld bytes failed", len); goto bail; } } else { if (*pLength < (long) len) { - pErrMsg->Format("buf size %ld too short (%ld)", + pErrMsg->Format(L"buf size %ld too short (%ld)", *pLength, len); goto bail; } dataBuf = *ppText; } if (fread(dataBuf, len, 1, fpArchive->fFp) != 1) { - pErrMsg->Format("File read failed: %s", strerror(errno)); + pErrMsg->Format(L"File read failed: %hs", strerror(errno)); goto bail; } @@ -179,7 +179,7 @@ BnyEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, ASSERT(IDOK != -1 && IDCANCEL != -1); if (which != kDataThread) { - *pErrMsg = "No such fork"; + *pErrMsg = L"No such fork"; goto bail; } @@ -192,7 +192,7 @@ BnyEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, errno = 0; if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format("Unable to seek to offset %ld: %s", + pErrMsg->Format(L"Unable to seek to offset %ld: %hs", fOffset, strerror(errno)); goto bail; } @@ -217,7 +217,7 @@ BnyEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(), &expBuf, true, kBNYBlockSize); if (nerr != kNuErrNone) { - pErrMsg->Format("File read failed: %s", NuStrError(nerr)); + pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); goto bail; } @@ -235,7 +235,7 @@ BnyEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, int err = GenericEntry::WriteConvert(outfp, buf, uncLen, &conv, &convHA, &lastCR); if (err != 0) { - pErrMsg->Format("File write failed: %s", strerror(err)); + pErrMsg->Format(L"File write failed: %hs", strerror(err)); delete[] buf; goto bail; } @@ -245,7 +245,7 @@ BnyEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, nerr = CopyData(outfp, conv, convHA, pErrMsg); if (nerr != kNuErrNone) { if (pErrMsg->IsEmpty()) { - pErrMsg->Format("Failed while copying data: %s\n", + pErrMsg->Format(L"Failed while copying data: %hs\n", NuStrError(nerr)); } goto bail; @@ -291,7 +291,7 @@ BnyEntry::CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, /* read a chunk from the source file */ nerr = fpArchive->BNYRead(buf, chunkLen); if (nerr != kNuErrNone) { - pMsg->Format("File read failed: %s.", NuStrError(nerr)); + pMsg->Format(L"File read failed: %hs.", NuStrError(nerr)); goto bail; } @@ -299,7 +299,7 @@ BnyEntry::CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, &convHA, &lastCR); if (err != 0) { - pMsg->Format("File write failed: %s.", strerror(err)); + pMsg->Format(L"File write failed: %hs.", strerror(err)); nerr = kNuErrGeneric; goto bail; } @@ -334,7 +334,7 @@ BnyEntry::TestEntry(CWnd* pMsgWnd) errno = 0; if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { nerr = kNuErrGeneric; - errMsg.Format("Unable to seek to offset %ld: %s\n", + errMsg.Format(L"Unable to seek to offset %ld: %hs\n", fOffset, strerror(errno)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -344,7 +344,7 @@ BnyEntry::TestEntry(CWnd* pMsgWnd) nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(), nil, true, kBNYBlockSize); if (nerr != kNuErrNone) { - errMsg.Format("Unsqueeze failed: %s.", NuStrError(nerr)); + errMsg.Format(L"Unsqueeze failed: %hs.", NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -352,7 +352,7 @@ BnyEntry::TestEntry(CWnd* pMsgWnd) errno = 0; if (fseek(fpArchive->fFp, fOffset + len, SEEK_SET) < 0) { nerr = kNuErrGeneric; - errMsg.Format("Unable to seek to offset %ld (file truncated?): %s\n", + errMsg.Format(L"Unable to seek to offset %ld (file truncated?): %hs\n", fOffset, strerror(errno)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -391,16 +391,16 @@ BnyArchive::AppInit(void) * Returns an error string on failure, or "" on success. */ GenericArchive::OpenResult -BnyArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) +BnyArchive::Open(const WCHAR* filename, bool readOnly, CString* pErrMsg) { CString errMsg; fIsReadOnly = true; // ignore "readOnly" errno = 0; - fFp = fopen(filename, "rb"); + fFp = _wfopen(filename, L"rb"); if (fFp == nil) { - errMsg.Format("Unable to open %s: %s.", filename, strerror(errno)); + errMsg.Format(L"Unable to open %ls: %hs.", filename, strerror(errno)); goto bail; } @@ -408,7 +408,7 @@ BnyArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) CWaitCursor waitc; if (LoadContents() != 0) { - errMsg.Format("Failed while loading contents of Binary II file."); + errMsg.Format(L"Failed while loading contents of Binary II file."); goto bail; } } @@ -429,9 +429,9 @@ bail: * Returns an error string on failure, or "" on success. */ CString -BnyArchive::New(const char* /*filename*/, const void* /*options*/) +BnyArchive::New(const WCHAR* /*filename*/, const void* /*options*/) { - CString retmsg("Sorry, Binary II files can't be created."); + CString retmsg(L"Sorry, Binary II files can't be created."); return retmsg; } @@ -503,7 +503,7 @@ BnyArchive::Reload(void) DeleteEntries(); if (LoadContents() != 0) { - return "Reload failed."; + return L"Reload failed."; } return ""; @@ -546,7 +546,8 @@ BnyArchive::LoadContentsCallback(BnyFileEntry* pEntry) * Create the new entry. */ pNewEntry = new BnyEntry(this); - pNewEntry->SetPathName(fileName); + CString fileNameW(fileName); + pNewEntry->SetPathName(fileNameW); pNewEntry->SetFssep(kBNYFssep); pNewEntry->SetFileType(pEntry->fileType); pNewEntry->SetAuxType(pEntry->auxType); @@ -570,9 +571,9 @@ BnyArchive::LoadContentsCallback(BnyFileEntry* pEntry) pNewEntry->SetDataForkLen(pEntry->realEOF); if (isSqueezed) - pNewEntry->SetFormatStr("Squeeze"); + pNewEntry->SetFormatStr(L"Squeeze"); else - pNewEntry->SetFormatStr("Uncompr"); + pNewEntry->SetFormatStr(L"Uncompr"); pNewEntry->SetSqueezed(isSqueezed); if (pEntry->realEOF != 0) @@ -962,7 +963,7 @@ BnyArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) while (pSelEntry != nil) { pEntry = (BnyEntry*) pSelEntry->GetEntry(); - WMSG2(" Testing '%s' (offset=%ld)\n", pEntry->GetDisplayName(), + WMSG2(" Testing '%ls' (offset=%ld)\n", pEntry->GetDisplayName(), pEntry->GetOffset()); SET_PROGRESS_UPDATE2(0, pEntry->GetDisplayName(), nil); @@ -975,7 +976,7 @@ BnyArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) errMsg = "Cancelled."; pMsgWnd->MessageBox(errMsg, title, MB_OK); } else { - errMsg.Format("Failed while testing '%s': %s.", + errMsg.Format(L"Failed while testing '%hs': %hs.", pEntry->GetPathName(), NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); } @@ -986,9 +987,9 @@ BnyArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) } /* show success message */ - errMsg.Format("Tested %d file%s, no errors found.", + errMsg.Format(L"Tested %d file%ls, no errors found.", pSelSet->GetNumEntries(), - pSelSet->GetNumEntries() == 1 ? "" : "s"); + pSelSet->GetNumEntries() == 1 ? L"" : L"s"); pMsgWnd->MessageBox(errMsg); retVal = true; diff --git a/app/BNYArchive.h b/app/BNYArchive.h index 34b89a9..fc101a3 100644 --- a/app/BNYArchive.h +++ b/app/BNYArchive.h @@ -6,8 +6,8 @@ /* * Binary II support. */ -#ifndef __BNY_ARCHIVE__ -#define __BNY_ARCHIVE__ +#ifndef APP_BNYARCHIVE_H +#define APP_BNYARCHIVE_H #include "GenericArchive.h" @@ -73,9 +73,9 @@ public: // One-time initialization; returns an error string. static CString AppInit(void); - virtual OpenResult Open(const char* filename, bool readOnly, + virtual OpenResult Open(const WCHAR* filename, bool readOnly, CString* pErrMsg); - virtual CString New(const char* filename, const void* options); + virtual CString New(const WCHAR* filename, const void* options); virtual CString Flush(void) { return ""; } virtual CString Reload(void); virtual bool IsReadOnly(void) const { return fIsReadOnly; }; @@ -88,7 +88,7 @@ public: const AddFilesDialog* pAddOpts) { ASSERT(false); return false; } virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const char* newName) + const WCHAR* newName) { ASSERT(false); return false; } virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet); virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) @@ -96,10 +96,10 @@ public: virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) { ASSERT(false); return false; } virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const char* newName) + const WCHAR* newName) { ASSERT(false); return false; } virtual CString TestVolumeName(const DiskFS* pDiskFS, - const char* newName) const + const WCHAR* newName) const { ASSERT(false); return "!"; } virtual CString TestPathName(const GenericEntry* pGenericEntry, const CString& basePath, const CString& newName, char newFssep) const @@ -215,4 +215,4 @@ private: bool fIsReadOnly; }; -#endif /*__BNY_ARCHIVE__*/ \ No newline at end of file +#endif /*APP_BNYARCHIVE_H*/ diff --git a/app/BasicImport.cpp b/app/BasicImport.cpp index f834aa1..c9c8d3b 100644 --- a/app/BasicImport.cpp +++ b/app/BasicImport.cpp @@ -97,8 +97,8 @@ ImportBASDialog::OnInitDialog(void) PathName path(fFileName); CString fileNameOnly(path.GetFileName()); CString ext(fileNameOnly.Right(4)); - if (ext.CompareNoCase(".txt") == 0) { - WMSG1("removing extension from '%s'\n", (const char*) fileNameOnly); + if (ext.CompareNoCase(L".txt") == 0) { + WMSG1("removing extension from '%ls'\n", (LPCWSTR) fileNameOnly); fileNameOnly = fileNameOnly.Left(fileNameOnly.GetLength() - 4); } @@ -126,7 +126,7 @@ static const char* kSuccess = "success!\r\n\r\n"; * Import an Applesoft BASIC program from the specified file. */ bool -ImportBASDialog::ImportBAS(const char* fileName) +ImportBASDialog::ImportBAS(const WCHAR* fileName) { FILE* fp = NULL; ExpandBuffer msgs(1024); @@ -135,8 +135,8 @@ ImportBASDialog::ImportBAS(const char* fileName) char* outBuf = nil; bool result = false; - msgs.Printf("Importing from '%s'...", fileName); - fp = fopen(fileName, "rb"); // EOL unknown, open as binary and deal + msgs.Printf("Importing from '%ls'...", fileName); + fp = _wfopen(fileName, L"rb"); // EOL unknown, open as binary and deal if (fp == NULL) { msgs.Printf("%sUnable to open file.", kFailed); goto bail; @@ -190,7 +190,8 @@ bail: char* msgBuf = nil; long msgLen; msgs.SeizeBuffer(&msgBuf, &msgLen); - pEdit->SetWindowText(msgBuf); + CString msgStr(msgBuf); + pEdit->SetWindowText(msgStr); delete[] msgBuf; return result; @@ -225,7 +226,7 @@ ImportBASDialog::ConvertTextToBAS(const char* buf, long fileLen, if (!ProcessBASLine(lineStart, lineEnd - lineStart, &output, /*ref*/ msg)) { - pMsgs->Printf("%sLine %d: %s", kFailed, lineNum, (const char*) msg); + pMsgs->Printf("%sLine %d: %ls", kFailed, lineNum, (LPCWSTR) msg); return false; } @@ -380,7 +381,7 @@ ImportBASDialog::ProcessBASLine(const char* buf, int len, return true; // blank lines with whitespace are okay else { // end of line reached while scanning line number is bad - msg = "found nothing except line number"; + msg = L"found nothing except line number"; return false; } } @@ -389,14 +390,14 @@ ImportBASDialog::ProcessBASLine(const char* buf, int len, if (!isdigit(ch)) break; if (tokenLen == 5) { // theoretical max is "65535" - msg = "line number has too many digits"; + msg = L"line number has too many digits"; return false; } tokenBuf[tokenLen++] = ch; } if (!tokenLen) { - msg = "line did not start with a line number"; + msg = L"line did not start with a line number"; return false; } tokenBuf[tokenLen] = '\0'; @@ -661,7 +662,7 @@ void ImportBASDialog::OnOK(void) CString appName; appName.LoadString(IDS_MB_APP_NAME); - MessageBox("You must specify a filename.", + MessageBox(L"You must specify a filename.", appName, MB_OK); } @@ -671,7 +672,7 @@ void ImportBASDialog::OnOK(void) GenericArchive::FileDetails details; details.entryKind = GenericArchive::FileDetails::kFileKindDataFork; - details.origName = "Imported BASIC"; + details.origName = L"Imported BASIC"; details.storageName = fileName; details.access = 0xe3; // unlocked, backup bit set details.fileType = kFileTypeBAS; @@ -697,7 +698,7 @@ void ImportBASDialog::OnOK(void) bail: if (!errMsg.IsEmpty()) { CString msg; - msg.Format("Unable to import file: %s.", (const char *) errMsg); + msg.Format(L"Unable to import file: %ls.", (LPCWSTR) errMsg); ShowFailureMsg(this, msg, IDS_FAILED); return; } diff --git a/app/BasicImport.h b/app/BasicImport.h index ae5b3ec..6d4e6bd 100644 --- a/app/BasicImport.h +++ b/app/BasicImport.h @@ -12,8 +12,8 @@ * neglected to "poke 33,33"). Have an optional "check syntax" box if we * want to get really fancy. */ -#ifndef __BASICIMPORT__ -#define __BASICIMPORT__ +#ifndef APP_BASICIMPORT_H +#define APP_BASICIMPORT_H /* * This is a helper class to scan for a token in the list. @@ -76,7 +76,7 @@ private: afx_msg void OnHelp(void); - bool ImportBAS(const char* fileName); + bool ImportBAS(const WCHAR* fileName); bool ConvertTextToBAS(const char* buf, long fileLen, char** pOutBuf, long* pOutLen, ExpandBuffer* pMsgs); bool ProcessBASLine(const char* buf, int len, @@ -101,4 +101,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__BASICIMPORT__*/ \ No newline at end of file +#endif /*APP_BASICIMPORT_H*/ diff --git a/app/CassImpTargetDialog.cpp b/app/CassImpTargetDialog.cpp index bdfc4ab..40a4000 100644 --- a/app/CassImpTargetDialog.cpp +++ b/app/CassImpTargetDialog.cpp @@ -60,8 +60,8 @@ CassImpTargetDialog::DoDataExchange(CDataExchange* pDX) if (fFileTypeIndex == kTypeBIN) { if (GetStartAddr() < 0) { - MessageBox("The address field must be a valid 4-digit " - " hexadecimal number.", + MessageBox(L"The address field must be a valid 4-digit " + L" hexadecimal number.", appName, MB_OK); pDX->Fail(); return; @@ -69,7 +69,7 @@ CassImpTargetDialog::DoDataExchange(CDataExchange* pDX) fStartAddr = (unsigned short) GetStartAddr(); } if (fFileName.IsEmpty()) { - MessageBox("You must enter a filename.", appName, MB_OK); + MessageBox(L"You must enter a filename.", appName, MB_OK); pDX->Fail(); return; } @@ -78,7 +78,7 @@ CassImpTargetDialog::DoDataExchange(CDataExchange* pDX) CString tmpStr; pWnd = GetDlgItem(IDC_CASSIMPTARG_BINADDR); - tmpStr.Format("%04X", fStartAddr); + tmpStr.Format(L"%04X", fStartAddr); pWnd->SetWindowText(tmpStr); } } @@ -113,7 +113,7 @@ CassImpTargetDialog::OnAddrChange(void) if (val < 0) val = 0; - tmpStr.Format(".%04X", val + fFileLength-1); + tmpStr.Format(L".%04X", val + fFileLength-1); pWnd = GetDlgItem(IDC_CASSIMPTARG_RANGE); pWnd->SetWindowText(tmpStr); @@ -134,18 +134,18 @@ CassImpTargetDialog::GetStartAddr(void) const CString aux; pWnd->GetWindowText(aux); - const char* str = aux; - char* end; + const WCHAR* str = aux; + WCHAR* end; long val; if (str[0] == '\0') { WMSG0(" HEY: blank addr, returning -1\n"); return -1; } - val = strtoul(aux, &end, 16); - if (end != str + strlen(str)) { - WMSG1(" HEY: found some garbage in addr '%s', returning -1\n", - (LPCTSTR) aux); + val = wcstoul(aux, &end, 16); + if (end != str + wcslen(str)) { + WMSG1(" HEY: found some garbage in addr '%ls', returning -1\n", + (LPCWSTR) aux); return -1; } return val; diff --git a/app/CassImpTargetDialog.h b/app/CassImpTargetDialog.h index 5c31d7a..b3c99a5 100644 --- a/app/CassImpTargetDialog.h +++ b/app/CassImpTargetDialog.h @@ -7,8 +7,8 @@ * Choose file name and characteristics for a file imported from an audio * cassette tape. */ -#ifndef __CASSIMPTARGETDIALOG__ -#define __CASSIMPTARGETDIALOG__ +#ifndef APP_CASSIMPTARGETDIALOG_H +#define APP_CASSIMPTARGETDIALOG_H #include "resource.h" @@ -49,4 +49,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CASSIMPTARGETDIALOG__*/ +#endif /*APP_CASSIMPTARGETDIALOG_H*/ diff --git a/app/CassetteDialog.cpp b/app/CassetteDialog.cpp index 457c7d4..6708173 100644 --- a/app/CassetteDialog.cpp +++ b/app/CassetteDialog.cpp @@ -314,19 +314,19 @@ CassetteDialog::OnInitDialog(void) int width0, width1, width2, width3, width4; pListView->GetClientRect(&rect); - width0 = pListView->GetStringWidth("XXIndexX"); - width1 = pListView->GetStringWidth("XXFormatXmmmmmmmmmmmmmm"); - width2 = pListView->GetStringWidth("XXLengthXm"); - width3 = pListView->GetStringWidth("XXChecksumXm"); - width4 = pListView->GetStringWidth("XXStart sampleX"); + width0 = pListView->GetStringWidth(L"XXIndexX"); + width1 = pListView->GetStringWidth(L"XXFormatXmmmmmmmmmmmmmm"); + width2 = pListView->GetStringWidth(L"XXLengthXm"); + width3 = pListView->GetStringWidth(L"XXChecksumXm"); + width4 = pListView->GetStringWidth(L"XXStart sampleX"); //width5 = pListView->GetStringWidth("XXEnd sampleX"); - pListView->InsertColumn(0, "Index", LVCFMT_LEFT, width0); - pListView->InsertColumn(1, "Format", LVCFMT_LEFT, width1); - pListView->InsertColumn(2, "Length", LVCFMT_LEFT, width2); - pListView->InsertColumn(3, "Checksum", LVCFMT_LEFT, width3); - pListView->InsertColumn(4, "Start sample", LVCFMT_LEFT, width4); - pListView->InsertColumn(5, "End sample", LVCFMT_LEFT, + pListView->InsertColumn(0, L"Index", LVCFMT_LEFT, width0); + pListView->InsertColumn(1, L"Format", LVCFMT_LEFT, width1); + pListView->InsertColumn(2, L"Length", LVCFMT_LEFT, width2); + pListView->InsertColumn(3, L"Checksum", LVCFMT_LEFT, width3); + pListView->InsertColumn(4, L"Start sample", LVCFMT_LEFT, width4); + pListView->InsertColumn(5, L"End sample", LVCFMT_LEFT, rect.Width() - (width0+width1+width2+width3+width4) /*- ::GetSystemMetrics(SM_CXVSCROLL)*/ ); @@ -480,7 +480,7 @@ CassetteDialog::OnImport(void) bail: if (!errMsg.IsEmpty()) { CString msg; - msg.Format("Unable to import file: %s.", (const char *) errMsg); + msg.Format(L"Unable to import file: %ls.", (LPCWSTR) errMsg); ShowFailureMsg(this, msg, IDS_FAILED); return; } @@ -511,13 +511,13 @@ CassetteDialog::AnalyzeWAV(void) if (pFormat->nChannels < 1 || pFormat->nChannels > 2 || (pFormat->wBitsPerSample != 8 && pFormat->wBitsPerSample != 16)) { - errMsg.Format("Unexpected PCM format (%d channels, %d bits/sample)", + errMsg.Format(L"Unexpected PCM format (%d channels, %d bits/sample)", pFormat->nChannels, pFormat->wBitsPerSample); ShowFailureMsg(this, errMsg, IDS_FAILED); return false; } if (soundFile.GetDataLen() % soundFile.GetBPS() != 0) { - errMsg.Format("Unexpected sound data length (%ld, samples are %d bytes)", + errMsg.Format(L"Unexpected sound data length (%ld, samples are %d bytes)", soundFile.GetDataLen(), soundFile.GetBPS()); ShowFailureMsg(this, errMsg, IDS_FAILED); return false; @@ -560,41 +560,41 @@ CassetteDialog::AddEntry(int idx, CListCtrl* pListCtrl, long* pFileType) ASSERT(pDataBuf != nil); - tmpStr.Format("%d", idx); + tmpStr.Format(L"%d", idx); pListCtrl->InsertItem(idx, tmpStr); *pFileType = kFileTypeBIN; if (pData->GetDataLen() == 2) { - tmpStr.Format("Integer header ($%04X)", + tmpStr.Format(L"Integer header ($%04X)", pDataBuf[0] | pDataBuf[1] << 8); } else if (pData->GetDataLen() == 3) { - tmpStr.Format("Applesoft header ($%04X $%02x)", + tmpStr.Format(L"Applesoft header ($%04X $%02x)", pDataBuf[0] | pDataBuf[1] << 8, pDataBuf[2]); } else if (pData->GetDataLen() > 3 && idx > 0 && fDataArray[idx-1].GetDataLen() == 2) { - tmpStr = "Integer BASIC"; + tmpStr = L"Integer BASIC"; *pFileType = kFileTypeINT; } else if (pData->GetDataLen() > 3 && idx > 0 && fDataArray[idx-1].GetDataLen() == 3) { - tmpStr = "Applesoft BASIC"; + tmpStr = L"Applesoft BASIC"; *pFileType = kFileTypeBAS; } else { - tmpStr = "Binary"; + tmpStr = L"Binary"; } pListCtrl->SetItemText(idx, 1, tmpStr); - tmpStr.Format("%d", pData->GetDataLen()); + tmpStr.Format(L"%d", pData->GetDataLen()); pListCtrl->SetItemText(idx, 2, tmpStr); if (pData->GetDataChkGood()) - tmpStr.Format("Good (0x%02x)", pData->GetDataChecksum()); + tmpStr.Format(L"Good (0x%02x)", pData->GetDataChecksum()); else - tmpStr.Format("BAD (0x%02x)", pData->GetDataChecksum()); + tmpStr.Format(L"BAD (0x%02x)", pData->GetDataChecksum()); pListCtrl->SetItemText(idx, 3, tmpStr); - tmpStr.Format("%ld", pData->GetDataOffset()); + tmpStr.Format(L"%ld", pData->GetDataOffset()); pListCtrl->SetItemText(idx, 4, tmpStr); - tmpStr.Format("%ld", pData->GetDataEndOffset()); + tmpStr.Format(L"%ld", pData->GetDataEndOffset()); pListCtrl->SetItemText(idx, 5, tmpStr); } diff --git a/app/CassetteDialog.h b/app/CassetteDialog.h index 98139a5..9656e37 100644 --- a/app/CassetteDialog.h +++ b/app/CassetteDialog.h @@ -6,8 +6,8 @@ /* * Apple II cassette I/O functions. */ -#ifndef __CASSETTEDIALOG__ -#define __CASSETTEDIALOG__ +#ifndef APP_CASSETTEDIALOG_H +#define APP_CASSETTEDIALOG_H /* * The dialog box is primarily concerned with extracting the original data @@ -157,4 +157,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CASSETTEDIALOG__*/ +#endif /*APP_CASSETTEDIALOG_H*/ diff --git a/app/ChooseAddTargetDialog.h b/app/ChooseAddTargetDialog.h index d0cbbe7..c236e47 100644 --- a/app/ChooseAddTargetDialog.h +++ b/app/ChooseAddTargetDialog.h @@ -6,8 +6,8 @@ /* * Choose the sub-volume and directory where added files will be put. */ -#ifndef __CHOOSE_ADD_TARGET_DIALOG__ -#define __CHOOSE_ADD_TARGET_DIALOG__ +#ifndef APP_CHOOSEADDTARGETDIALOG_H +#define APP_CHOOSEADDTARGETDIALOG_H #include "resource.h" #include "DiskFSTree.h" @@ -44,4 +44,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CHOOSE_ADD_TARGET_DIALOG__*/ +#endif /*APP_CHOOSEADDTARGETDIALOG_H*/ diff --git a/app/ChooseDirDialog.cpp b/app/ChooseDirDialog.cpp index 4055e74..4635700 100644 --- a/app/ChooseDirDialog.cpp +++ b/app/ChooseDirDialog.cpp @@ -53,8 +53,8 @@ ChooseDirDialog::OnInitDialog(void) fShellTree.TunnelTree(fPathName, &msg); if (!msg.IsEmpty()) { /* failed */ - WMSG2("TunnelTree failed on '%s' (%s), using MyComputer instead\n", - fPathName, msg); + WMSG2("TunnelTree failed on '%ls' (%ls), using MyComputer instead\n", + (LPCWSTR) fPathName, (LPCWSTR) msg); fShellTree.ExpandMyComputer(); } } @@ -117,7 +117,7 @@ ChooseDirDialog::OnSelChanged(NMHDR* pnmh, LRESULT* pResult) if (fShellTree.GetFolderPath(&path)) fPathName = path; else - fPathName = ""; + fPathName = L""; pWnd->SetWindowText(fPathName); // disable the "Select" button when there's no path ready @@ -128,7 +128,7 @@ ChooseDirDialog::OnSelChanged(NMHDR* pnmh, LRESULT* pResult) // It's confusing to have two different paths showing, so wipe out the // free entry field when the selection changes. pWnd = GetDlgItem(IDC_CHOOSEDIR_PATHEDIT); - pWnd->SetWindowText(""); + pWnd->SetWindowText(L""); *pResult = 0; } @@ -164,8 +164,8 @@ void ChooseDirDialog::OnNewFolder(void) { if (fPathName.IsEmpty()) { - MessageBox("You can't create a folder in this part of the tree.", - "Bad Location", MB_OK | MB_ICONERROR); + MessageBox(L"You can't create a folder in this part of the tree.", + L"Bad Location", MB_OK | MB_ICONERROR); return; } @@ -182,11 +182,11 @@ ChooseDirDialog::OnNewFolder(void) */ if (fShellTree.AddFolderAtSelection(newFolderDlg.fNewFolder)) { CString msg; - WMSG1("Success, tunneling to '%s'\n", - newFolderDlg.fNewFullPath); + WMSG1("Success, tunneling to '%ls'\n", + (LPCWSTR) newFolderDlg.fNewFullPath); fShellTree.TunnelTree(newFolderDlg.fNewFullPath, &msg); if (!msg.IsEmpty()) { - WMSG1("TunnelTree failed: %s\n", (LPCTSTR) msg); + WMSG1("TunnelTree failed: %ls\n", (LPCWSTR) msg); } } else { WMSG0("AddFolderAtSelection FAILED\n"); diff --git a/app/ChooseDirDialog.h b/app/ChooseDirDialog.h index cd4a140..ab9c762 100644 --- a/app/ChooseDirDialog.h +++ b/app/ChooseDirDialog.h @@ -6,8 +6,8 @@ /* * Dialog for choosing a directory. */ -#ifndef __CHOOSEDIRDIALOG__ -#define __CHOOSEDIRDIALOG__ +#ifndef APP_CHOOSEDIRDIALOG +#define APP_CHOOSEDIRDIALOG #include "../util/UtilLib.h" #include "resource.h" @@ -22,14 +22,14 @@ public: ChooseDirDialog(CWnd* pParent = NULL, int dialogID = IDD_CHOOSEDIR) : CDialog(dialogID, pParent) { - fPathName = ""; + fPathName = L""; } virtual ~ChooseDirDialog(void) {} - const char* GetPathName(void) const { return fPathName; } + const WCHAR* GetPathName(void) const { return fPathName; } // set the pathname; when DoModal is called this will tunnel in - void SetPathName(const char* str) { fPathName = str; } + void SetPathName(const WCHAR* str) { fPathName = str; } protected: virtual BOOL OnInitDialog(void); @@ -50,4 +50,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CHOOSEDIRDIALOG__*/ \ No newline at end of file +#endif /*APP_CHOOSEDIRDIALOG*/ diff --git a/app/CiderPress.rc b/app/CiderPress.rc index 2378fb7..811cc2f 100644 --- a/app/CiderPress.rc +++ b/app/CiderPress.rc @@ -1,4 +1,4 @@ -//Microsoft Developer Studio generated resource script. +// Microsoft Visual C++ generated resource script. // #include "resource.h" @@ -18,13 +18,11 @@ #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources +// English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) -#endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -32,18 +30,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // TEXTINCLUDE // -1 TEXTINCLUDE DISCARDABLE +1 TEXTINCLUDE BEGIN "resource.h\0" END -2 TEXTINCLUDE DISCARDABLE +2 TEXTINCLUDE BEGIN "#include ""afxres.h""\r\n" "#include ""Dlgs.h""\0" END -3 TEXTINCLUDE DISCARDABLE +3 TEXTINCLUDE BEGIN "\r\n" "\0" @@ -59,18 +57,18 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDR_MAINFRAME ICON DISCARDABLE "Graphics\\CiderPress.ico" -IDI_FILE_BINARY2 ICON DISCARDABLE "Graphics\\binary2.ico" -IDI_FILE_NUFX ICON DISCARDABLE "Graphics\\nufx.ico" -IDI_FILE_DISKIMAGE ICON DISCARDABLE "Graphics\\diskimage.ico" -IDI_FILE_VIEWER ICON DISCARDABLE "Graphics\\FileViewer.ico" +IDR_MAINFRAME ICON "Graphics\\CiderPress.ico" +IDI_FILE_BINARY2 ICON "Graphics\\binary2.ico" +IDI_FILE_NUFX ICON "Graphics\\nufx.ico" +IDI_FILE_DISKIMAGE ICON "Graphics\\diskimage.ico" +IDI_FILE_VIEWER ICON "Graphics\\FileViewer.ico" ///////////////////////////////////////////////////////////////////////////// // // Menu // -IDR_MAINFRAME MENU DISCARDABLE +IDR_MAINFRAME MENU BEGIN POPUP "&File" BEGIN @@ -144,9 +142,7 @@ BEGIN MENUITEM "Disk &image converter", IDM_TOOLS_DISKCONV MENUITEM "&Bulk disk image converter", IDM_TOOLS_BULKDISKCONV MENUITEM SEPARATOR - MENUITEM "&Volume copier (open volume)", - IDM_TOOLS_VOLUMECOPIER_VOLUME - + MENUITEM "&Volume copier (open volume)", IDM_TOOLS_VOLUMECOPIER_VOLUME MENUITEM "Volume copier (open file)", IDM_TOOLS_VOLUMECOPIER_FILE MENUITEM "&Merge SST images", IDM_TOOLS_SST_MERGE MENUITEM SEPARATOR @@ -162,7 +158,7 @@ BEGIN END END -IDR_RIGHTCLICKMENU MENU DISCARDABLE +IDR_RIGHTCLICKMENU MENU BEGIN POPUP "RightClickMenu" BEGIN @@ -191,7 +187,7 @@ END // Accelerator // -IDR_MAINFRAME ACCELERATORS DISCARDABLE +IDR_MAINFRAME ACCELERATORS BEGIN "A", IDM_EDIT_SELECT_ALL, VIRTKEY, CONTROL, NOINVERT "C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT @@ -199,8 +195,7 @@ BEGIN "I", IDM_FILE_ARCHIVEINFO, VIRTKEY, CONTROL, NOINVERT "N", IDM_FILE_NEW_ARCHIVE, VIRTKEY, CONTROL, NOINVERT "O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT - "O", IDM_FILE_OPEN_VOLUME, VIRTKEY, SHIFT, CONTROL, - NOINVERT + "O", IDM_FILE_OPEN_VOLUME, VIRTKEY, SHIFT, CONTROL, NOINVERT "P", IDM_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT "S", IDM_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT "V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT @@ -218,142 +213,97 @@ END // IDD_ABOUTDLG DIALOGEX 0, 0, 237, 154 -STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "About CiderPress" -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,7,132,50,14 - PUSHBUTTON "Credits",IDC_ABOUT_CREDITS,77,132,50,14,0,0, - HIDC_ABOUT_CREDITS - PUSHBUTTON "Enter registration code",IDC_ABOUT_ENTER_REG,144,132,86, - 14 - CONTROL 106,IDC_STATIC,"Static",SS_BITMAP,7,6,64,59 - LTEXT "CiderPress v%d.%d.%d%s%s",IDC_CIDERPRESS_VERS_TEXT,77,7, - 153,9 - LTEXT "Copyright © 2009 by CiderPress project authors\rAll Rights Reserved.", - IDC_STATIC,77,20,153,19 - ICON IDR_MAINFRAME,IDC_STATIC,77,45,21,20 - ICON IDI_FILE_NUFX,IDC_STATIC,106,45,21,20 - ICON IDI_FILE_BINARY2,IDC_STATIC,136,45,21,20 - ICON IDI_FILE_DISKIMAGE,IDC_STATIC,166,45,21,20 + PUSHBUTTON "Credits",IDC_ABOUT_CREDITS,77,132,50,14,0,0,HIDC_ABOUT_CREDITS + PUSHBUTTON "Enter registration code",IDC_ABOUT_ENTER_REG,144,132,86,14 + CONTROL IDB_FSLOGO,IDC_STATIC,"Static",SS_BITMAP,7,6,64,59 + LTEXT "CiderPress v%d.%d.%d%ls%ls",IDC_CIDERPRESS_VERS_TEXT,77,7,153,9 + LTEXT "Copyright © 2009 by CiderPress project authors\rAll Rights Reserved.",IDC_STATIC,77,20,153,19 + ICON IDR_MAINFRAME,IDC_STATIC,77,45,20,20 + ICON IDI_FILE_NUFX,IDC_STATIC,106,45,20,20 + ICON IDI_FILE_BINARY2,IDC_STATIC,136,45,20,20 + ICON IDI_FILE_DISKIMAGE,IDC_STATIC,166,45,20,20 GROUPBOX "Libraries",IDC_STATIC,7,71,223,56 - LTEXT "Using NufxLib DLL v%d.%d.%d",IDC_NUFXLIB_VERS_TEXT,16, - 82,170,10 - LTEXT "Using DiskImg DLL v%d.%d.%d",IDC_DISKIMG_VERS_TEXT,16, - 92,172,8 - LTEXT "Using zlib DLL v%s",IDC_ZLIB_VERS_TEXT,16,103,172,8 - LTEXT "Using ASPI DLL v%s",IDC_ASPI_VERS_TEXT,16,114,173,8 + LTEXT "Using NufxLib DLL v%d.%d.%d",IDC_NUFXLIB_VERS_TEXT,16,82,170,10 + LTEXT "Using DiskImg DLL v%d.%d.%d",IDC_DISKIMG_VERS_TEXT,16,92,172,8 + LTEXT "Using zlib DLL v%ls",IDC_ZLIB_VERS_TEXT,16,103,172,8 + LTEXT "Using ASPI DLL v%ls",IDC_ASPI_VERS_TEXT,16,114,173,8 END -IDD_PREF_GENERAL DIALOG DISCARDABLE 0, 0, 263, 180 -STYLE DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION +IDD_PREF_GENERAL DIALOG 0, 0, 263, 180 +STYLE DS_SETFONT | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "General" FONT 8, "MS Sans Serif" BEGIN GROUPBOX "Columns",IDC_STATIC,4,7,80,144 - CONTROL "Pathname",IDC_COL_PATHNAME,"Button",BS_AUTOCHECKBOX | - WS_DISABLED | WS_TABSTOP,12,19,65,10 - CONTROL "Type",IDC_COL_TYPE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,31,66,10 - CONTROL "Aux Type",IDC_COL_AUXTYPE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,43,65,10 - CONTROL "Mod Date",IDC_COL_MODDATE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,55,66,10 - CONTROL "Format",IDC_COL_FORMAT,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,67,68,10 - CONTROL "Size",IDC_COL_SIZE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,79,66,10 - CONTROL "Ratio",IDC_COL_RATIO,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,91,67,10 - CONTROL "Packed",IDC_COL_PACKED,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,103,66,10 - CONTROL "Access",IDC_COL_ACCESS,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,115,67,10 + CONTROL "Pathname",IDC_COL_PATHNAME,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,12,19,65,10 + CONTROL "Type",IDC_COL_TYPE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,31,66,10 + CONTROL "Aux Type",IDC_COL_AUXTYPE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,43,65,10 + CONTROL "Mod Date",IDC_COL_MODDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,55,66,10 + CONTROL "Format",IDC_COL_FORMAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,67,68,10 + CONTROL "Size",IDC_COL_SIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,79,66,10 + CONTROL "Ratio",IDC_COL_RATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,91,67,10 + CONTROL "Packed",IDC_COL_PACKED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,103,66,10 + CONTROL "Access",IDC_COL_ACCESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,115,67,10 PUSHBUTTON "&Defaults",IDC_COL_DEFAULTS,12,131,63,13 GROUPBOX "NuFX (ShrinkIt) archives",IDC_STATIC,96,7,162,51 - CONTROL "&Mimic ShrinkIt quirks",IDC_PREF_SHRINKIT_COMPAT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,104,19,142,10 + CONTROL "&Mimic ShrinkIt quirks",IDC_PREF_SHRINKIT_COMPAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,19,142,10 CONTROL "Handle ""&bad Mac"" archives",IDC_PREF_SHK_BAD_MAC, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,31,149,10 - CONTROL "Reduce error checking (not recommended)", - IDC_PREF_REDUCE_SHK_ERROR_CHECKS,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,104,43,151,10 + CONTROL "Reduce error checking (not recommended)",IDC_PREF_REDUCE_SHK_ERROR_CHECKS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,43,151,10 GROUPBOX "Filename munging",IDC_STATIC,96,60,162,39 - CONTROL "Display &DOS 3.3 filenames in lower case", - IDC_PREF_COERCE_DOS,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,104,72,144,8 + CONTROL "Display &DOS 3.3 filenames in lower case",IDC_PREF_COERCE_DOS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,72,144,8 CONTROL "Show spaces as &underscores",IDC_PREF_SPACES_TO_UNDER, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,84,145,10 GROUPBOX "System",IDC_STATIC,96,101,162,33 - PUSHBUTTON "File type &associations...",IDC_PREF_ASSOCIATIONS,104, - 113,92,14 + PUSHBUTTON "File type &associations...",IDC_PREF_ASSOCIATIONS,104,113,92,14 GROUPBOX "Miscellaneous",IDC_STATIC,96,136,162,38 - CONTROL "Strip pathnames when pasting files", - IDC_PREF_PASTE_JUNKPATHS,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,104,147,144,10 - CONTROL "Beep when actions complete successfully", - IDC_PREF_SUCCESS_BEEP,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,104,159,145,10 + CONTROL "Strip pathnames when pasting files",IDC_PREF_PASTE_JUNKPATHS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,147,144,10 + CONTROL "Beep when actions complete successfully",IDC_PREF_SUCCESS_BEEP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,159,145,10 END IDD_PREF_COMPRESSION DIALOGEX 0, 0, 212, 212 -STYLE DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION +STYLE DS_SETFONT | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Compression" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN LTEXT "Default compression method:",IDC_STATIC,4,6,184,10 - CONTROL "No compression",IDC_DEFC_UNCOMPRESSED,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,4,18,92,8 - CONTROL "Squeeze",IDC_DEFC_SQUEEZE,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,4,30,92,8 - CONTROL "Dynamic LZW/1",IDC_DEFC_LZW1,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,4,57,96,8 - CONTROL "Dynamic LZW/2 (recommended)",IDC_DEFC_LZW2,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,4,78,152,8 - CONTROL "12-bit LZC",IDC_DEFC_LZC12,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,4,99,92,8 - CONTROL "16-bit LZC",IDC_DEFC_LZC16,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,4,126,96,8 - CONTROL "Deflate",IDC_DEFC_DEFLATE,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,4,153,100,8 - CONTROL "Bzip2",IDC_DEFC_BZIP2,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,4,180,108,8 - LTEXT "Uses a combination of RLE and Huffman.\rNot compatible with ProDOS 8 ShrinkIt.", - IDC_STATIC,16,39,192,18 - LTEXT "The compression method used by ProDOS 8 ShrinkIt.", - IDC_STATIC,16,66,180,10,0,0,HIDC_STATIC - LTEXT "The compression method used by GS/ShrinkIt.",IDC_STATIC, - 16,87,185,8,0,0,HIDC_STATIC - LTEXT "Compression used by UNIX ""compress"" command.\rNot compatible with ProDOS 8 ShrinkIt.", - IDC_STATIC,16,108,190,18 - LTEXT "Compression used by UNIX ""compress"" command.\rNot compatible with ProDOS 8 ShrinkIt.", - IDC_STATIC,16,135,195,18 - LTEXT "Compression used by ZIP and gzip.\rNot compatible with any Apple II applications.", - IDC_STATIC,16,162,196,18 - LTEXT "Compression used by bzip2.\rNot compatible with any Apple II applications.", - IDC_STATIC,16,189,196,18 + CONTROL "No compression",IDC_DEFC_UNCOMPRESSED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,4,18,92,8 + CONTROL "Squeeze",IDC_DEFC_SQUEEZE,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,30,92,8 + CONTROL "Dynamic LZW/1",IDC_DEFC_LZW1,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,57,96,8 + CONTROL "Dynamic LZW/2 (recommended)",IDC_DEFC_LZW2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,78,152,8 + CONTROL "12-bit LZC",IDC_DEFC_LZC12,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,99,92,8 + CONTROL "16-bit LZC",IDC_DEFC_LZC16,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,126,96,8 + CONTROL "Deflate",IDC_DEFC_DEFLATE,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,153,100,8 + CONTROL "Bzip2",IDC_DEFC_BZIP2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,180,108,8 + LTEXT "Uses a combination of RLE and Huffman.\rNot compatible with ProDOS 8 ShrinkIt.",IDC_STATIC,16,39,192,18 + LTEXT "The compression method used by ProDOS 8 ShrinkIt.",IDC_STATIC,16,66,180,10,0,0,HIDC_STATIC + LTEXT "The compression method used by GS/ShrinkIt.",IDC_STATIC,16,87,185,8,0,0,HIDC_STATIC + LTEXT "Compression used by UNIX ""compress"" command.\rNot compatible with ProDOS 8 ShrinkIt.",IDC_STATIC,16,108,190,18 + LTEXT "Compression used by UNIX ""compress"" command.\rNot compatible with ProDOS 8 ShrinkIt.",IDC_STATIC,16,135,195,18 + LTEXT "Compression used by ZIP and gzip.\rNot compatible with any Apple II applications.",IDC_STATIC,16,162,196,18 + LTEXT "Compression used by bzip2.\rNot compatible with any Apple II applications.",IDC_STATIC,16,189,196,18 END IDD_FILE_VIEWER DIALOGEX 0, 0, 486, 257 -STYLE DS_3DLOOK | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | - WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +STYLE DS_SETFONT | DS_3DLOOK | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "File Viewer" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN DEFPUSHBUTTON "&Done",IDOK,432,224,50,14,WS_GROUP - CONTROL "",IDC_FVIEW_EDITBOX,"RICHEDIT",ES_MULTILINE | - ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | - WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP,4,3,478, - 217,WS_EX_CLIENTEDGE - COMBOBOX IDC_FVIEW_FORMATSEL,152,225,148,54,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - CONTROL "Data fork",IDC_FVIEW_DATA,"Button",BS_AUTORADIOBUTTON | - WS_GROUP | WS_TABSTOP,5,224,68,10 - CONTROL "Resource fork",IDC_FVIEW_RSRC,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,5,235,69,10 - CONTROL "Comment",IDC_FVIEW_CMMT,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,5,246,69,10 + CONTROL "",IDC_FVIEW_EDITBOX,"RICHEDIT",TCS_HOTTRACK | TCS_VERTICAL | TCS_RAGGEDRIGHT | TCS_MULTISELECT | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP,4,3,478,217,WS_EX_CLIENTEDGE + COMBOBOX IDC_FVIEW_FORMATSEL,152,225,148,54,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Data fork",IDC_FVIEW_DATA,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,5,224,68,10 + CONTROL "Resource fork",IDC_FVIEW_RSRC,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,5,235,69,10 + CONTROL "Comment",IDC_FVIEW_CMMT,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,5,246,69,10 PUSHBUTTON "&Next",IDC_FVIEW_NEXT,82,224,50,14,WS_GROUP PUSHBUTTON "&Prev",IDC_FVIEW_PREV,82,242,50,14 PUSHBUTTON "Best",IDC_FVIEW_FMT_BEST,151,242,32,14,WS_GROUP @@ -365,120 +315,73 @@ BEGIN PUSHBUTTON "Help",IDHELP,376,242,50,14 END -IDD_PREF_FVIEW DIALOG DISCARDABLE 0, 0, 216, 263 -STYLE DS_CONTEXTHELP | WS_CHILD | WS_CAPTION +IDD_PREF_FVIEW DIALOG 0, 0, 216, 263 +STYLE DS_SETFONT | DS_CONTEXTHELP | WS_CHILD | WS_CAPTION CAPTION "File Viewer" FONT 8, "MS Sans Serif" BEGIN GROUPBOX "Converters",IDC_STATIC,4,7,208,139 - CONTROL "High-ASCII &text",IDC_PVIEW_HITEXT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,19,88,10 - CONTROL "&CP/M text",IDC_PVIEW_CPMTEXT,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,30,92,10 - CONTROL "&Pascal text",IDC_PVIEW_PASCALTEXT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,41,87,10 - CONTROL "Pascal &code",IDC_PVIEW_PASCALCODE,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,52,89,10 - CONTROL "&Applesoft BASIC",IDC_PVIEW_APPLESOFT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,63,88,10 - CONTROL "&Integer BASIC",IDC_PVIEW_INTEGER,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,74,88,10 - CONTROL "Assembly source",IDC_PVIEW_SCASSEM,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,85,94,10 - CONTROL "ProDOS folders",IDC_PVIEW_PRODOSFOLDER,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,96,88,10 - CONTROL "Disassemble code",IDC_PVIEW_DISASM,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,107,91,10 - CONTROL "Resource forks",IDC_PVIEW_RESOURCES,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,12,118,90,10 - CONTROL "GWP (Teach, AWGS)",IDC_PVIEW_GWP,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,19,92,10 - CONTROL "8-bit word processor",IDC_PVIEW_TEXT8,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,30,94,10 - CONTROL "AppleWorks &WP",IDC_PVIEW_AWP,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,112,41,88,10 - CONTROL "AppleWorks &DB",IDC_PVIEW_ADB,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,112,52,94,10 - CONTROL "AppleWorks &SS",IDC_PVIEW_ASP,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,112,63,94,10 - CONTROL "&Hi-Res images",IDC_PVIEW_HIRES,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,74,90,10 - CONTROL "&Double Hi-Res images",IDC_PVIEW_DHR,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,85,86,10 - CONTROL "&Super Hi-Res images",IDC_PVIEW_SHR,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,96,89,10 - CONTROL "Print Shop graphics",IDC_PVIEW_PRINTSHOP,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,107,77,10 - CONTROL "MacPaint images",IDC_PVIEW_MACPAINT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,112,118,93,10 + CONTROL "High-ASCII &text",IDC_PVIEW_HITEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,19,88,10 + CONTROL "&CP/M text",IDC_PVIEW_CPMTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,30,92,10 + CONTROL "&Pascal text",IDC_PVIEW_PASCALTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,41,87,10 + CONTROL "Pascal &code",IDC_PVIEW_PASCALCODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,52,89,10 + CONTROL "&Applesoft BASIC",IDC_PVIEW_APPLESOFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,63,88,10 + CONTROL "&Integer BASIC",IDC_PVIEW_INTEGER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,74,88,10 + CONTROL "Assembly source",IDC_PVIEW_SCASSEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,85,94,10 + CONTROL "ProDOS folders",IDC_PVIEW_PRODOSFOLDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,96,88,10 + CONTROL "Disassemble code",IDC_PVIEW_DISASM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,107,91,10 + CONTROL "Resource forks",IDC_PVIEW_RESOURCES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,118,90,10 + CONTROL "GWP (Teach, AWGS)",IDC_PVIEW_GWP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,19,92,10 + CONTROL "8-bit word processor",IDC_PVIEW_TEXT8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,30,94,10 + CONTROL "AppleWorks &WP",IDC_PVIEW_AWP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,41,88,10 + CONTROL "AppleWorks &DB",IDC_PVIEW_ADB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,52,94,10 + CONTROL "AppleWorks &SS",IDC_PVIEW_ASP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,63,94,10 + CONTROL "&Hi-Res images",IDC_PVIEW_HIRES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,74,90,10 + CONTROL "&Double Hi-Res images",IDC_PVIEW_DHR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,85,86,10 + CONTROL "&Super Hi-Res images",IDC_PVIEW_SHR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,96,89,10 + CONTROL "Print Shop graphics",IDC_PVIEW_PRINTSHOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,107,77,10 + CONTROL "MacPaint images",IDC_PVIEW_MACPAINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,118,93,10 CONTROL "&Relax type-checking on graphics",IDC_PVIEW_RELAX_GFX, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,50,131,116,10 GROUPBOX "Conversion options",IDC_STATIC,4,150,208,90 - CONTROL "&Scroll horizontally instead of wrapping words", - IDC_PVIEW_NOWRAP_TEXT,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,162,192,10 - CONTROL "Highlight &hex dump columns (small files)", - IDC_PVIEW_BOLD_HEXDUMP,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,173,156,10 - CONTROL "Prefer syntax &highlighting on BASIC programs", - IDC_PVIEW_BOLD_BASIC,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,184,187,10 - CONTROL "Disassemble BRK/COP as single-byte instructions", - IDC_PVIEW_DISASM_ONEBYTEBRKCOP,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,12,195,189,10 + CONTROL "&Scroll horizontally instead of wrapping words",IDC_PVIEW_NOWRAP_TEXT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,162,192,10 + CONTROL "Highlight &hex dump columns (small files)",IDC_PVIEW_BOLD_HEXDUMP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,173,156,10 + CONTROL "Prefer syntax &highlighting on BASIC programs",IDC_PVIEW_BOLD_BASIC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,184,187,10 + CONTROL "Disassemble BRK/COP as single-byte instructions",IDC_PVIEW_DISASM_ONEBYTEBRKCOP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,195,189,10 CONTROL "Prefer &B&&W for hi-res images",IDC_PVIEW_HIRES_BW, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,206,148,10 LTEXT "Preferred DHR mode:",IDC_STATIC,12,223,69,8 - COMBOBOX IDC_PVIEW_DHR_CONV_COMBO,84,220,118,48,CBS_DROPDOWNLIST | - CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_PVIEW_DHR_CONV_COMBO,84,220,118,48,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP LTEXT "Viewer file size &limit:",IDC_STATIC,4,247,62,8 - EDITTEXT IDC_PVIEW_SIZE_EDIT,69,245,40,14,ES_AUTOHSCROLL | - ES_NUMBER - CONTROL "Spin2",IDC_PVIEW_SIZE_SPIN,"msctls_updown32", - UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | - UDS_ARROWKEYS | UDS_NOTHOUSANDS,112,247,11,14 + EDITTEXT IDC_PVIEW_SIZE_EDIT,69,245,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_PVIEW_SIZE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,112,247,11,14 LTEXT "KBytes",IDC_STATIC,110,247,36,8 END IDD_DISKEDIT DIALOGEX 0, 0, 458, 197 -STYLE DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | - WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Disk Edit" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN - CONTROL "",IDC_DISKEDIT_EDIT,"RICHEDIT",ES_MULTILINE | - ES_AUTOVSCROLL | WS_BORDER | WS_VSCROLL | WS_TABSTOP,4, - 12,384,164,WS_EX_CLIENTEDGE,HIDC_DISKEDIT_EDIT - PUSHBUTTON "&Done",IDC_DISKEDIT_DONE,4,180,50,14,0,0, - HIDC_DISKEDIT_DONE - PUSHBUTTON "&Open File",IDC_DISKEDIT_OPENFILE,60,180,50,14,0,0, - HIDC_DISKEDIT_OPENFILE - EDITTEXT IDC_DISKEDIT_TRACK,397,20,49,14,ES_AUTOHSCROLL,0, - HIDC_DISKEDIT_TRACK - CONTROL "Spin1",IDC_DISKEDIT_TRACKSPIN,"msctls_updown32", - UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,434,27, - 11,14 - EDITTEXT IDC_DISKEDIT_SECTOR,397,49,49,14,ES_AUTOHSCROLL,0, - HIDC_DISKEDIT_SECTOR - CONTROL "Spin2",IDC_DISKEDIT_SECTORSPIN,"msctls_updown32", - UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,434,58, - 11,14 - CONTROL "&Hex",IDC_DISKEDIT_HEX,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,399,68,44,10,0,HIDC_DISKEDIT_HEX - PUSHBUTTON "&Read",IDC_DISKEDIT_DOREAD,399,89,50,14,0,0, - HIDC_DISKEDIT_DOREAD - PUSHBUTTON "&Write",IDC_DISKEDIT_DOWRITE,399,105,50,14,0,0, - HIDC_DISKEDIT_DOWRITE - PUSHBUTTON "Read &Prev",IDC_DISKEDIT_PREV,399,132,50,14,0,0, - HIDC_DISKEDIT_PREV - PUSHBUTTON "Read &Next",IDC_DISKEDIT_NEXT,399,148,50,14,0,0, - HIDC_DISKEDIT_NEXT - LTEXT "Track:",IDC_STEXT_TRACK,396,12,58,8,0,0, - HIDC_STEXT_TRACK - LTEXT "Sector:",IDC_STEXT_SECTOR,396,41,52,8,0,0, - HIDC_STEXT_SECTOR - PUSHBUTTON "Sub &Volume",IDC_DISKEDIT_SUBVOLUME,116,180,50,14,0,0, - HIDC_DISKEDIT_SUBVOLUME + CONTROL "",IDC_DISKEDIT_EDIT,"RICHEDIT",TCS_HOTTRACK | TCS_MULTISELECT | WS_BORDER | WS_VSCROLL | WS_TABSTOP,4,12,384,164,WS_EX_CLIENTEDGE,HIDC_DISKEDIT_EDIT + PUSHBUTTON "&Done",IDC_DISKEDIT_DONE,4,180,50,14,0,0,HIDC_DISKEDIT_DONE + PUSHBUTTON "&Open File",IDC_DISKEDIT_OPENFILE,60,180,50,14,0,0,HIDC_DISKEDIT_OPENFILE + EDITTEXT IDC_DISKEDIT_TRACK,397,20,49,14,ES_AUTOHSCROLL,0,HIDC_DISKEDIT_TRACK + CONTROL "Spin1",IDC_DISKEDIT_TRACKSPIN,"msctls_updown32",UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,434,27,11,14 + EDITTEXT IDC_DISKEDIT_SECTOR,397,49,49,14,ES_AUTOHSCROLL,0,HIDC_DISKEDIT_SECTOR + CONTROL "Spin2",IDC_DISKEDIT_SECTORSPIN,"msctls_updown32",UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,434,58,11,14 + CONTROL "&Hex",IDC_DISKEDIT_HEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,399,68,44,10,0,HIDC_DISKEDIT_HEX + PUSHBUTTON "&Read",IDC_DISKEDIT_DOREAD,399,89,50,14,0,0,HIDC_DISKEDIT_DOREAD + PUSHBUTTON "&Write",IDC_DISKEDIT_DOWRITE,399,105,50,14,0,0,HIDC_DISKEDIT_DOWRITE + PUSHBUTTON "Read &Prev",IDC_DISKEDIT_PREV,399,132,50,14,0,0,HIDC_DISKEDIT_PREV + PUSHBUTTON "Read &Next",IDC_DISKEDIT_NEXT,399,148,50,14,0,0,HIDC_DISKEDIT_NEXT + LTEXT "Track:",IDC_STEXT_TRACK,396,12,58,8,0,0,HIDC_STEXT_TRACK + LTEXT "Sector:",IDC_STEXT_SECTOR,396,41,52,8,0,0,HIDC_STEXT_SECTOR + PUSHBUTTON "Sub &Volume",IDC_DISKEDIT_SUBVOLUME,116,180,50,14,0,0,HIDC_DISKEDIT_SUBVOLUME PUSHBUTTON "Help",IDHELP,172,180,50,14,0,0,HIDHELP LTEXT " 0",IDC_STATIC,32,4,8,8 LTEXT " 1",IDC_STATIC,48,4,8,8 @@ -496,13 +399,11 @@ BEGIN LTEXT " d",IDC_STATIC,240,4,8,8 LTEXT " e",IDC_STATIC,256,4,8,8 LTEXT " f",IDC_STATIC,272,4,8,8 - COMBOBOX IDC_DISKEDIT_NIBBLE_PARMS,276,182,112,60, - CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DISKEDIT_NIBBLE_PARMS,276,182,112,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END -IDD_DECONF DIALOG DISCARDABLE 0, 0, 188, 173 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | - WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU +IDD_DECONF DIALOG 0, 0, 188, 173 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU CAPTION "Disk Image Characteristics" FONT 8, "MS Sans Serif" BEGIN @@ -510,77 +411,62 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,68,152,50,14 PUSHBUTTON "Help",IDC_DECONF_HELP,128,152,50,14 LTEXT "File source:",IDC_STATIC,7,10,37,8 - EDITTEXT IDC_DECONF_SOURCE,68,8,112,14,ES_AUTOHSCROLL | - ES_READONLY + EDITTEXT IDC_DECONF_SOURCE,68,8,112,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "File Format:",IDC_STATIC,7,50,54,8 - COMBOBOX IDC_DECONF_FILEFORMAT,68,47,112,30,CBS_DROPDOWNLIST | - WS_DISABLED | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DECONF_FILEFORMAT,68,47,112,30,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP LTEXT "Physical format:",IDC_STATIC,7,69,56,8 - COMBOBOX IDC_DECONF_PHYSICAL,68,66,112,30,CBS_DROPDOWNLIST | - WS_DISABLED | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DECONF_PHYSICAL,68,66,112,30,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP LTEXT "Sector ordering:",IDC_STATIC,7,88,55,8 - COMBOBOX IDC_DECONF_SECTORORDER,68,85,112,53,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DECONF_SECTORORDER,68,85,112,53,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Filesystem format:",IDC_STATIC,7,107,56,8 - COMBOBOX IDC_DECONF_FSFORMAT,68,104,112,60,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - CONTROL "blocks",IDC_DECONF_VIEWASBLOCKS,"Button", - BS_AUTORADIOBUTTON,18,135,41,10 - CONTROL "sectors",IDC_DECONF_VIEWASSECTORS,"Button", - BS_AUTORADIOBUTTON,63,135,43,10 - CONTROL "track nibbles",IDC_DECONF_VIEWASNIBBLES,"Button", - BS_AUTORADIOBUTTON,109,135,59,10 - COMBOBOX IDC_DECONF_OUTERFORMAT,68,28,112,49,CBS_DROPDOWNLIST | - WS_DISABLED | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DECONF_FSFORMAT,68,104,112,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "blocks",IDC_DECONF_VIEWASBLOCKS,"Button",BS_AUTORADIOBUTTON,18,135,41,10 + CONTROL "sectors",IDC_DECONF_VIEWASSECTORS,"Button",BS_AUTORADIOBUTTON,63,135,43,10 + CONTROL "track nibbles",IDC_DECONF_VIEWASNIBBLES,"Button",BS_AUTORADIOBUTTON,109,135,59,10 + COMBOBOX IDC_DECONF_OUTERFORMAT,68,28,112,49,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP LTEXT "Outer Format:",IDC_STATIC,7,30,56,8 LTEXT "View data as...",IDC_DECONF_VIEWAS,7,123,48,8 END -IDD_SUBV DIALOG DISCARDABLE 0, 0, 153, 135 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +IDD_SUBV DIALOG 0, 0, 153, 135 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Select Sub-Volume" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,21,114,50,14 PUSHBUTTON "Cancel",IDCANCEL,81,114,50,14 - LISTBOX IDC_SUBV_LIST,7,19,139,87,LBS_USETABSTOPS | - LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_SUBV_LIST,7,19,139,87,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP LTEXT "Choose the sub-volume to open:",IDC_STATIC,7,7,103,8 END -IDD_DEFILE DIALOG DISCARDABLE 0, 0, 199, 97 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_DEFILE DIALOG 0, 0, 199, 97 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Open file on disk image..." FONT 8, "MS Sans Serif" BEGIN - LTEXT "Enter the name of the file to open:",IDC_STATIC,7,7,108, - 8 + LTEXT "Enter the name of the file to open:",IDC_STATIC,7,7,108,8 EDITTEXT IDC_DEFILE_FILENAME,7,19,185,14,ES_AUTOHSCROLL - CONTROL "Open &resource fork instead of data fork", - IDC_DEFILE_RSRC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7, - 39,185,10 + CONTROL "Open &resource fork instead of data fork",IDC_DEFILE_RSRC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,39,185,10 DEFPUSHBUTTON "OK",IDOK,45,76,50,14 PUSHBUTTON "Cancel",IDCANCEL,103,76,50,14 - LTEXT "To open a file in a sub-volume, first open the sub-volume.", - IDC_STATIC,7,59,180,8 + LTEXT "To open a file in a sub-volume, first open the sub-volume.",IDC_STATIC,7,59,180,8 END -IDD_PREF_FILES DIALOG DISCARDABLE 0, 0, 231, 89 -STYLE DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION +IDD_PREF_FILES DIALOG 0, 0, 231, 89 +STYLE DS_SETFONT | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Files" FONT 8, "MS Sans Serif" BEGIN LTEXT "Folder for temporary files:",IDC_STATIC,7,13,131,8 EDITTEXT IDC_PREF_TEMP_FOLDER,7,26,197,14,ES_AUTOHSCROLL - PUSHBUTTON "",IDC_PREF_CHOOSE_TEMP_FOLDER,208,26,16,14,BS_BITMAP | - BS_CENTER | BS_VCENTER - LTEXT "External file viewer extensions (separated with semicolons):", - IDC_STATIC,7,55,217,11 + PUSHBUTTON "",IDC_PREF_CHOOSE_TEMP_FOLDER,208,26,16,14,BS_BITMAP | BS_CENTER | BS_VCENTER + LTEXT "External file viewer extensions (separated with semicolons):",IDC_STATIC,7,55,217,11 EDITTEXT IDC_PREF_EXTVIEWER_EXTS,7,68,197,14,ES_AUTOHSCROLL END IDD_CHOOSEDIR DIALOGEX 0, 0, 255, 253 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_CONTEXTHELP CAPTION "Choose folder" FONT 8, "MS Sans Serif", 0, 0, 0x1 @@ -589,23 +475,16 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,103,232,50,14 PUSHBUTTON "Help",IDHELP,162,232,50,14 LTEXT "Select the folder to use:",IDC_STATIC,7,7,76,8 - CONTROL "Tree1",IDC_CHOOSEDIR_TREE,"SysTreeView32", - TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | - TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,17,241,155, - WS_EX_CLIENTEDGE - EDITTEXT IDC_CHOOSEDIR_PATH,7,174,221,14,ES_AUTOHSCROLL | - ES_READONLY - PUSHBUTTON "IDB_NEW_FOLDER",IDC_CHOOSEDIR_NEW_FOLDER,232,174,16,14, - BS_BITMAP | BS_CENTER | BS_VCENTER - LTEXT "Or type the folder name and hit ""Expand Tree"":", - IDC_STATIC,7,198,149,8 + CONTROL "Tree1",IDC_CHOOSEDIR_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,17,241,155,WS_EX_CLIENTEDGE + EDITTEXT IDC_CHOOSEDIR_PATH,7,174,221,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "IDB_NEW_FOLDER",IDC_CHOOSEDIR_NEW_FOLDER,232,174,16,14,BS_BITMAP | BS_CENTER | BS_VCENTER + LTEXT "Or type the folder name and hit ""Expand Tree"":",IDC_STATIC,7,198,149,8 EDITTEXT IDC_CHOOSEDIR_PATHEDIT,7,208,183,14,ES_AUTOHSCROLL PUSHBUTTON "Expand Tree",IDC_CHOOSEDIR_EXPAND_TREE,198,208,50,14 END -IDD_NEWFOLDER DIALOG DISCARDABLE 0, 0, 251, 69 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_NEWFOLDER DIALOG 0, 0, 251, 69 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Create New Folder" FONT 8, "MS Sans Serif" BEGIN @@ -613,81 +492,58 @@ BEGIN DEFPUSHBUTTON "OK",IDOK,193,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,193,24,50,14 LTEXT "Current folder:",IDC_STATIC,6,7,177,8 - EDITTEXT IDC_NEWFOLDER_CURDIR,6,16,177,14,ES_AUTOHSCROLL | - ES_READONLY - LTEXT "New folder name (will be created in current folder):", - IDC_STATIC,6,37,177,8 + EDITTEXT IDC_NEWFOLDER_CURDIR,6,16,177,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "New folder name (will be created in current folder):",IDC_STATIC,6,37,177,8 END -IDD_EXTRACT_FILES DIALOG DISCARDABLE 0, 0, 299, 242 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_EXTRACT_FILES DIALOG 0, 0, 299, 242 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Extract Files" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Extract",IDOK,68,220,50,14 PUSHBUTTON "Cancel",IDCANCEL,124,220,50,14 PUSHBUTTON "Help",IDHELP,180,220,50,14 - LTEXT "Folder where files will be extracted:",IDC_STATIC,8,8, - 143,8 + LTEXT "Folder where files will be extracted:",IDC_STATIC,8,8,143,8 EDITTEXT IDC_EXT_PATH,8,18,263,14,ES_AUTOHSCROLL | WS_GROUP - PUSHBUTTON "",IDC_EXT_CHOOSE_FOLDER,276,18,16,14,BS_BITMAP | - BS_CENTER | BS_VCENTER | WS_GROUP + PUSHBUTTON "",IDC_EXT_CHOOSE_FOLDER,276,18,16,14,BS_BITMAP | BS_CENTER | BS_VCENTER | WS_GROUP GROUPBOX "Files to extract",IDC_STATIC,8,40,140,35 - CONTROL ">Extract 65536 selected files<",IDC_EXT_SELECTED,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,50,124,10 - CONTROL "Extract &all files",IDC_EXT_ALL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,16,61,124,10 + CONTROL ">Extract 65536 selected files<",IDC_EXT_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,50,124,10 + CONTROL "Extract &all files",IDC_EXT_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,61,124,10 GROUPBOX "Parts to extract",IDC_STATIC,152,40,140,47,WS_GROUP - CONTROL "&Data forks",IDC_EXT_DATAFORK,"Button",BS_AUTOCHECKBOX | - WS_GROUP | WS_TABSTOP,160,50,128,10 - CONTROL "&Resource forks",IDC_EXT_RSRCFORK,"Button", - BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,61,128,10 - CONTROL "Disk &images",IDC_EXT_DISKIMAGE,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,160,72,128,10 + CONTROL "&Data forks",IDC_EXT_DATAFORK,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,50,128,10 + CONTROL "&Resource forks",IDC_EXT_RSRCFORK,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,61,128,10 + CONTROL "Disk &images",IDC_EXT_DISKIMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,160,72,128,10 GROUPBOX "Format conversion",IDC_STATIC,8,77,140,37 CONTROL "&Convert to non-Apple II formats",IDC_EXT_REFORMAT, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,16,88, - 124,10 - CONTROL "Extract disks as .&2MG",IDC_EXT_DISK_2MG,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,16,99,124,10 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,16,88,124,10 + CONTROL "Extract disks as .&2MG",IDC_EXT_DISK_2MG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,99,124,10 GROUPBOX "Filenames",IDC_STATIC,152,98,140,40 CONTROL "Add file attribute &preservation",IDC_EXT_ADD_PRESERVE, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,110, - 128,10 - CONTROL "Add type &extension",IDC_EXT_ADD_EXTEN,"Button", - BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,122,128,10 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,110,128,10 + CONTROL "Add type &extension",IDC_EXT_ADD_EXTEN,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,122,128,10 GROUPBOX "Text conversion",IDC_STATIC,8,117,140,70,WS_GROUP - CONTROL "&Don't convert text files",IDC_EXT_CONVEOLNONE,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,127,124,10 + CONTROL "&Don't convert text files",IDC_EXT_CONVEOLNONE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,127,124,10 CONTROL "Convert text files by file type",IDC_EXT_CONVEOLTYPE, "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,138,126,10 - CONTROL "Auto-detect && &convert files with text", - IDC_EXT_CONVEOLTEXT,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,16,149,126,10 - CONTROL "Convert &ALL files",IDC_EXT_CONVEOLALL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,16,160,124,10 + CONTROL "Auto-detect && &convert files with text",IDC_EXT_CONVEOLTEXT, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,149,126,10 + CONTROL "Convert &ALL files",IDC_EXT_CONVEOLALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,160,124,10 CONTROL "Strip ""&high ASCII"" files",IDC_EXT_CONVHIGHASCII, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,16,171, - 124,10 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,16,171,124,10 GROUPBOX "Miscellaneous",IDC_STATIC,152,151,140,36,WS_GROUP - CONTROL "&Strip folder names",IDC_EXT_STRIP_FOLDER,"Button", - BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,161,128,10 + CONTROL "&Strip folder names",IDC_EXT_STRIP_FOLDER,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,161,128,10 CONTROL "&Overwrite existing files",IDC_EXT_OVERWRITE_EXIST, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,172, - 128,10 - PUSHBUTTON "Configure to preserve Apple II formats", - IDC_EXT_CONFIG_PRESERVE,8,196,140,16 - PUSHBUTTON "Configure for easy access in Windows", - IDC_EXT_CONFIG_CONVERT,152,196,140,16 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,160,172,128,10 + PUSHBUTTON "Configure to preserve Apple II formats",IDC_EXT_CONFIG_PRESERVE,8,196,140,16 + PUSHBUTTON "Configure for easy access in Windows",IDC_EXT_CONFIG_CONVERT,152,196,140,16 END -IDD_ACTION_PROGRESS DIALOG DISCARDABLE 0, 0, 276, 111 -STYLE DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER +IDD_ACTION_PROGRESS DIALOG 0, 0, 276, 111 +STYLE DS_SETFONT | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER FONT 8, "MS Sans Serif" BEGIN - CONTROL "Progress1",IDC_PROG_PROGRESS,"msctls_progress32", - WS_BORDER,7,65,262,14 + CONTROL "Progress1",IDC_PROG_PROGRESS,"msctls_progress32",WS_BORDER,7,65,262,14 PUSHBUTTON "Cancel",IDCANCEL,112,90,50,14 LTEXT "Now extracting:",IDC_PROG_VERB,7,7,113,8 LTEXT ">source<",IDC_PROG_ARC_NAME,7,17,262,8 @@ -695,9 +551,8 @@ BEGIN LTEXT ">target<",IDC_PROG_FILE_NAME,7,47,262,8 END -IDD_CONFIRM_OVERWRITE DIALOG DISCARDABLE 0, 0, 292, 105 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_CONFIRM_OVERWRITE DIALOG 0, 0, 292, 105 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Confirm Overwrite" FONT 8, "MS Sans Serif" BEGIN @@ -715,8 +570,8 @@ BEGIN LTEXT ">new file info<",IDC_OVWR_NEW_INFO,52,48,228,8 END -IDD_RENAME_OVERWRITE DIALOG DISCARDABLE 0, 0, 316, 124 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_RENAME_OVERWRITE DIALOG 0, 0, 316, 124 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Rename File" FONT 8, "MS Sans Serif" BEGIN @@ -724,75 +579,59 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,162,103,50,14 LTEXT "Original name:",IDC_STATIC,8,8,46,8 LTEXT ">original name<",IDC_RENOVWR_SOURCE_NAME,8,20,301,8 - EDITTEXT IDC_RENOVWR_ORIG_NAME,8,48,302,14,ES_AUTOHSCROLL | - ES_READONLY + EDITTEXT IDC_RENOVWR_ORIG_NAME,8,48,302,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "Full pathname:",IDC_STATIC,8,36,47,8 LTEXT "New pathname:",IDC_STATIC,8,68,51,8 EDITTEXT IDC_RENOVWR_NEW_NAME,8,80,302,14,ES_AUTOHSCROLL END IDD_ADD_FILES DIALOGEX 0, 0, 292, 231 -STYLE DS_3DLOOK | DS_CONTROL | DS_CONTEXTHELP | WS_CHILD | WS_VISIBLE | - WS_CLIPSIBLINGS +STYLE DS_SETFONT | DS_3DLOOK | DS_CONTROL | DS_CONTEXTHELP | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN LTEXT "",1119,0,0,291,87,NOT WS_GROUP,WS_EX_STATICEDGE PUSHBUTTON "&Accept",IDC_SELECT_ACCEPT,241,88,50,14 PUSHBUTTON "Cancel",IDCANCEL,241,106,50,14 PUSHBUTTON "Help",IDHELP,241,124,50,14 - GROUPBOX "File attribute preservation",IDC_ADDFILES_STATIC1,4,112, - 169,47 - CONTROL "&Ignore file attribute preservation tags", - IDC_ADDFILES_NOPRESERVE,"Button",BS_AUTORADIOBUTTON | - WS_GROUP | WS_TABSTOP,8,122,157,10 - CONTROL "&Use file attribute preservation tags", - IDC_ADDFILES_PRESERVE,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,8,133,152,10 - CONTROL "Use tags and &guess type from extension", - IDC_ADDFILES_PRESERVEPLUS,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,8,144,150,10 + GROUPBOX "File attribute preservation",IDC_ADDFILES_STATIC1,4,112,169,47 + CONTROL "&Ignore file attribute preservation tags",IDC_ADDFILES_NOPRESERVE, + "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,8,122,157,10 + CONTROL "&Use file attribute preservation tags",IDC_ADDFILES_PRESERVE, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,133,152,10 + CONTROL "Use tags and &guess type from extension",IDC_ADDFILES_PRESERVEPLUS, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,144,150,10 GROUPBOX "Text conversion",IDC_ADDFILES_STATIC4,4,168,169,56 CONTROL "&Don't convert text files",IDC_ADDFILES_CONVEOLNONE, - "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,8, - 178,146,10 - CONTROL "Convert text files by file type", - IDC_ADDFILES_CONVEOLTYPE,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,8,189,153,10 - CONTROL "Auto-detect && &convert files with text", - IDC_ADDFILES_CONVEOLTEXT,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,8,200,148,10 - CONTROL "Convert &ALL files",IDC_ADDFILES_CONVEOLALL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,8,211,141,10 + "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,8,178,146,10 + CONTROL "Convert text files by file type",IDC_ADDFILES_CONVEOLTYPE, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,189,153,10 + CONTROL "Auto-detect && &convert files with text",IDC_ADDFILES_CONVEOLTEXT, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,200,148,10 + CONTROL "Convert &ALL files",IDC_ADDFILES_CONVEOLALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,211,141,10 GROUPBOX "Miscellaneous",IDC_ADDFILES_STATIC2,176,145,115,47 CONTROL "&Include subfolders",IDC_ADDFILES_INCLUDE_SUBFOLDERS, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,180,155, - 88,10 - CONTROL "&Strip folder names",IDC_ADDFILES_STRIP_FOLDER,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,180,166,88,10 + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,180,155,88,10 + CONTROL "&Strip folder names",IDC_ADDFILES_STRIP_FOLDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,180,166,88,10 CONTROL "&Overwrite existing files",IDC_ADDFILES_OVERWRITE, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,180,177,85,10 - LTEXT "Storage prefix (optional):",IDC_ADDFILES_STATIC3,177, - 198,77,8 + LTEXT "Storage prefix (optional):",IDC_ADDFILES_STATIC3,177,198,77,8 EDITTEXT IDC_ADDFILES_PREFIX,176,210,102,14,ES_AUTOHSCROLL END -IDD_USE_SELECTION DIALOG DISCARDABLE 0, 0, 139, 79 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_USE_SELECTION DIALOG 0, 0, 139, 79 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Action Files" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Action",IDOK,16,58,50,14,WS_GROUP PUSHBUTTON "Cancel",IDCANCEL,73,58,50,14 GROUPBOX "Files",IDC_STATIC,7,7,125,40 - CONTROL ">Action 65536 selected files<",IDC_USE_SELECTED,"Button", - BS_AUTORADIOBUTTON | WS_GROUP,14,19,113,10 - CONTROL "Action &all files",IDC_USE_ALL,"Button", - BS_AUTORADIOBUTTON,14,30,113,10 + CONTROL ">Action 65536 selected files<",IDC_USE_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,19,113,10 + CONTROL "Action &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON,14,30,113,10 END -IDD_RENAME_ENTRY DIALOG DISCARDABLE 0, 0, 284, 118 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_RENAME_ENTRY DIALOG 0, 0, 284, 118 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Rename" FONT 8, "MS Sans Serif" BEGIN @@ -805,25 +644,23 @@ BEGIN LTEXT "New name:",IDC_STATIC,7,42,125,8 EDITTEXT IDC_RENAME_NEW,7,52,270,14,ES_AUTOHSCROLL LTEXT "Path separator character:",IDC_STATIC,7,76,81,8 - EDITTEXT IDC_RENAME_PATHSEP,91,74,11,14,ES_AUTOHSCROLL | NOT - WS_TABSTOP + EDITTEXT IDC_RENAME_PATHSEP,91,74,11,14,ES_AUTOHSCROLL | NOT WS_TABSTOP END -IDD_COMMENT_EDIT DIALOG DISCARDABLE 0, 0, 362, 151 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_COMMENT_EDIT DIALOG 0, 0, 362, 151 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Edit Comment" FONT 8, "MS Sans Serif" BEGIN - EDITTEXT IDC_COMMENT_EDIT,7,7,290,138,ES_MULTILINE | - ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL + EDITTEXT IDC_COMMENT_EDIT,7,7,290,138,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL DEFPUSHBUTTON "OK",IDOK,305,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,305,25,50,14 PUSHBUTTON "Help",IDHELP,305,43,50,14 PUSHBUTTON "Delete",IDC_COMMENT_DELETE,305,71,50,14 END -IDD_RECOMPRESS_OPTS DIALOG DISCARDABLE 0, 0, 171, 149 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_RECOMPRESS_OPTS DIALOG 0, 0, 171, 149 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Recompress Files" FONT 8, "MS Sans Serif" BEGIN @@ -831,19 +668,15 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,88,128,50,14 GROUPBOX "Files",IDC_STATIC,7,7,157,40 CONTROL ">Recompress 65536 selected files<",IDC_USE_SELECTED, - "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14, - 19,130,10 - CONTROL "Action &all files",IDC_USE_ALL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 - COMBOBOX IDC_RECOMP_COMP,7,65,157,55,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP + "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 + CONTROL "Action &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 + COMBOBOX IDC_RECOMP_COMP,7,65,157,55,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "New compression type:",IDC_STATIC,7,53,157,8 - LTEXT "IMPORTANT: for broad compatibility, use only Dynamic LZW/2 compression. Other formats will not work with all software, and ""Deflate"" cannot be unpacked on an Apple II.", - IDC_STATIC,7,87,157,35 + LTEXT "IMPORTANT: for broad compatibility, use only Dynamic LZW/2 compression. Other formats will not work with all software, and ""Deflate"" cannot be unpacked on an Apple II.",IDC_STATIC,7,87,157,35 END -IDD_PRINT_CANCEL DIALOG DISCARDABLE 0, 0, 116, 46 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +IDD_PRINT_CANCEL DIALOG 0, 0, 116, 46 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Printing..." FONT 8, "MS Sans Serif" BEGIN @@ -851,52 +684,44 @@ BEGIN LTEXT "Sending file list to the printer.",IDC_STATIC,7,7,102,8 END -IDD_ASSOCIATIONS DIALOG DISCARDABLE 0, 0, 258, 281 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_ASSOCIATIONS DIALOG 0, 0, 258, 281 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "CiderPress Associations" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,201,70,50,14 PUSHBUTTON "Cancel",IDCANCEL,201,88,50,14 PUSHBUTTON "Help",IDHELP,201,106,50,14 - LTEXT "Place a checkmark next to the file extensions that you want associated with CiderPress. Opening files with these extensions will cause CiderPress to be launched automatically.", - IDC_STATIC,7,7,244,27 - LTEXT "Press OK in this screen to accept the changes, and hit OK or Apply in the Preferences screen to apply them.", - IDC_STATIC,7,36,244,21 + LTEXT "Place a checkmark next to the file extensions that you want associated with CiderPress. Opening files with these extensions will cause CiderPress to be launched automatically.",IDC_STATIC,7,7,244,27 + LTEXT "Press OK in this screen to accept the changes, and hit OK or Apply in the Preferences screen to apply them.",IDC_STATIC,7,36,244,21 LTEXT "Current file associations:",IDC_STATIC,7,59,244,8 - CONTROL "List4",IDC_ASSOCIATION_LIST,"SysListView32",LVS_REPORT | - LVS_SINGLESEL | LVS_NOSORTHEADER | WS_BORDER | - WS_TABSTOP,7,70,185,204 + CONTROL "List4",IDC_ASSOCIATION_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,70,185,204 END -IDD_REGISTRATION DIALOG DISCARDABLE 0, 0, 233, 191 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_REGISTRATION DIALOG 0, 0, 233, 191 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Registration Info" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,37,170,50,14 PUSHBUTTON "Cancel",IDCANCEL,92,170,50,14 PUSHBUTTON "Help",IDHELP,147,170,50,14 - LTEXT "Please enter your registration information. Fill in all fields exactly as they appear on the registration letter. Copying and pasting directly from the registration letter is the easiest way to do this.", - IDC_STATIC,7,7,219,27 + LTEXT "Please enter your registration information. Fill in all fields exactly as they appear on the registration letter. Copying and pasting directly from the registration letter is the easiest way to do this.",IDC_STATIC,7,7,219,27 LTEXT "Your name:",IDC_STATIC,7,70,122,8 EDITTEXT IDC_REGENTER_USER,7,81,181,14,ES_AUTOHSCROLL LTEXT "Your company:",IDC_STATIC,7,102,119,8 EDITTEXT IDC_REGENTER_COMPANY,7,113,181,14,ES_AUTOHSCROLL LTEXT "Registration key:",IDC_STATIC,7,134,128,8 EDITTEXT IDC_REGENTER_REG,7,145,180,14,ES_AUTOHSCROLL - LTEXT "When you have entered the data, make sure the checksum values match those in the registration letter. If they don't, re-check what you have entered. Punctuation and capitalization are important.", - IDC_STATIC,7,37,219,25 + LTEXT "When you have entered the data, make sure the checksum values match those in the registration letter. If they don't, re-check what you have entered. Punctuation and capitalization are important.",IDC_STATIC,7,37,219,25 LTEXT "Checksum",IDC_STATIC,192,70,34,8 LTEXT "CCCC",IDC_REGENTER_USERCRC,201,85,25,8 LTEXT "CCCC",IDC_REGENTER_COMPCRC,201,117,25,8 LTEXT "CCCC",IDC_REGENTER_REGCRC,201,149,25,8 END -IDD_DISKCONV DIALOG DISCARDABLE 0, 0, 174, 223 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_DISKCONV DIALOG 0, 0, 174, 223 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Convert Disk Image" FONT 8, "MS Sans Serif" BEGIN @@ -906,48 +731,37 @@ BEGIN LTEXT "Image size:",IDC_IMAGE_SIZE_TEXT,7,7,36,8 LTEXT ">800K floppy<",IDC_IMAGE_TYPE,47,7,120,8 GROUPBOX "Convert to:",IDC_STATIC,7,20,160,157 - CONTROL "Unadorned DOS-order (.DO)",IDC_DISKCONV_DOS,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,31,146,10 - CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_DOS2MG,"Button", - BS_AUTORADIOBUTTON,27,43,133,10 + CONTROL "Unadorned DOS-order (.DO)",IDC_DISKCONV_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,31,146,10 + CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_DOS2MG,"Button",BS_AUTORADIOBUTTON,27,43,133,10 CONTROL "Unadorned ProDOS-order (.PO)",IDC_DISKCONV_PRODOS, "Button",BS_AUTORADIOBUTTON,14,55,146,10 - CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_PRODOS2MG,"Button", - BS_AUTORADIOBUTTON,27,67,133,10 - CONTROL "Unadorned nibble (.NIB)",IDC_DISKCONV_NIB,"Button", - BS_AUTORADIOBUTTON,14,79,146,10 - CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_NIB2MG,"Button", - BS_AUTORADIOBUTTON,27,91,133,10 + CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_PRODOS2MG,"Button",BS_AUTORADIOBUTTON,27,67,133,10 + CONTROL "Unadorned nibble (.NIB)",IDC_DISKCONV_NIB,"Button",BS_AUTORADIOBUTTON,14,79,146,10 + CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_NIB2MG,"Button",BS_AUTORADIOBUTTON,27,91,133,10 CONTROL "Unadorned 13-sector disk (.D13)",IDC_DISKCONV_D13, "Button",BS_AUTORADIOBUTTON,14,103,146,10 CONTROL "DiskCopy 4.2 disk image (.DSK)",IDC_DISKCONV_DC42, "Button",BS_AUTORADIOBUTTON,14,115,146,10 - CONTROL "ShrinkIt disk archve (.SDK)",IDC_DISKCONV_SDK,"Button", - BS_AUTORADIOBUTTON,14,127,146,10 - CONTROL "TrackStar image (.APP)",IDC_DISKCONV_TRACKSTAR,"Button", - BS_AUTORADIOBUTTON,14,139,146,10 + CONTROL "ShrinkIt disk archve (.SDK)",IDC_DISKCONV_SDK,"Button",BS_AUTORADIOBUTTON,14,127,146,10 + CONTROL "TrackStar image (.APP)",IDC_DISKCONV_TRACKSTAR,"Button",BS_AUTORADIOBUTTON,14,139,146,10 CONTROL """Sim //e"" virtual hard drive (.HDV)",IDC_DISKCONV_HDV, "Button",BS_AUTORADIOBUTTON,14,151,146,10 - CONTROL "DDD Pro (.DDD)",IDC_DISKCONV_DDD,"Button", - BS_AUTORADIOBUTTON,14,163,146,10 - CONTROL "Compress with gzip (.gz)",IDC_DISKCONV_GZIP,"Button", - BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,184,91,10 + CONTROL "DDD Pro (.DDD)",IDC_DISKCONV_DDD,"Button",BS_AUTORADIOBUTTON,14,163,146,10 + CONTROL "Compress with gzip (.gz)",IDC_DISKCONV_GZIP,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,184,91,10 END -IDD_DONEOPEN DIALOG DISCARDABLE 0, 0, 119, 55 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_DONEOPEN DIALOG 0, 0, 119, 55 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Success" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Done",IDCANCEL,7,34,50,14 PUSHBUTTON "&Open Image",IDOK,62,34,50,14 - LTEXT "Would you like to open the disk image you just created?", - IDC_STATIC,7,7,105,25 + LTEXT "Would you like to open the disk image you just created?",IDC_STATIC,7,7,105,25 END IDD_PROPS_EDIT DIALOGEX 0, 0, 239, 157 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Edit Attributes" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN @@ -955,98 +769,73 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,94,135,50,14 PUSHBUTTON "Help",IDHELP,150,135,50,14 RTEXT "Pathname:",IDC_STATIC,7,9,65,8 - EDITTEXT IDC_PROPS_PATHNAME,79,7,153,14,ES_AUTOHSCROLL | - ES_READONLY + EDITTEXT IDC_PROPS_PATHNAME,79,7,153,14,ES_AUTOHSCROLL | ES_READONLY RTEXT "Creation date:",IDC_STATIC,7,26,65,8 LTEXT ">create date/time<",IDC_PROPS_CREATEWHEN,79,26,153,8 RTEXT "Modification date:",IDC_STATIC,7,37,65,8 LTEXT ">mod date/time<",IDC_PROPS_MODWHEN,79,37,153,8 RTEXT "File type:",IDC_STATIC,7,53,65,8 - COMBOBOX IDC_PROPS_FILETYPE,79,50,52,105,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_PROPS_FILETYPE,79,50,52,105,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP RTEXT "Aux type (hex):",IDC_STATIC,7,69,65,8 EDITTEXT IDC_PROPS_AUXTYPE,79,67,52,14,ES_AUTOHSCROLL RTEXT "Type description:",IDC_STATIC,7,86,65,8 LTEXT ">type description<",IDC_PROPS_TYPEDESCR,79,86,153,8 RTEXT "Access:",IDC_STATIC,7,102,65,8 - CONTROL "Read",IDC_PROPS_ACCESS_R,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,79,102,33,10 - CONTROL "Backup",IDC_PROPS_ACCESS_B,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,123,102,41,10 - CONTROL "Invisible",IDC_PROPS_ACCESS_I,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,175,102,41,10 - CONTROL "Write",IDC_PROPS_ACCESS_W,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,79,115,33,10 - CONTROL "Rename",IDC_PROPS_ACCESS_N,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,124,115,43,10 - CONTROL "Delete",IDC_PROPS_ACCESS_D,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,176,115,37,10 + CONTROL "Read",IDC_PROPS_ACCESS_R,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,102,33,10 + CONTROL "Backup",IDC_PROPS_ACCESS_B,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,123,102,41,10 + CONTROL "Invisible",IDC_PROPS_ACCESS_I,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,175,102,41,10 + CONTROL "Write",IDC_PROPS_ACCESS_W,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,115,33,10 + CONTROL "Rename",IDC_PROPS_ACCESS_N,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,124,115,43,10 + CONTROL "Delete",IDC_PROPS_ACCESS_D,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,115,37,10 EDITTEXT IDC_PROPS_HFS_FILETYPE,192,49,40,14,ES_AUTOHSCROLL EDITTEXT IDC_PROPS_HFS_AUXTYPE,192,67,40,14,ES_AUTOHSCROLL - CONTROL "HFS type:",IDC_PROPS_HFS_MODE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,145,51,45,10 + CONTROL "HFS type:",IDC_PROPS_HFS_MODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,145,51,45,10 LTEXT "Creator:",IDC_PROPS_HFS_LABEL,156,69,34,8,0,WS_EX_RIGHT END -IDD_CONVFILE_OPTS DIALOG DISCARDABLE 0, 0, 171, 93 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_CONVFILE_OPTS DIALOG 0, 0, 171, 93 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Convert to file archive" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Action",IDOK,33,72,50,14,WS_GROUP PUSHBUTTON "Cancel",IDCANCEL,89,72,50,14 GROUPBOX "Files",IDC_STATIC,7,7,157,40 - CONTROL ">Convert 65536 selected files<",IDC_USE_SELECTED,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 - CONTROL "Convert &all files",IDC_USE_ALL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 + CONTROL ">Convert 65536 selected files<",IDC_USE_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 + CONTROL "Convert &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 CONTROL "Preserve &empty folders",IDC_CONVFILE_PRESERVEDIR, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,157,10 END -IDD_CONVDISK_OPTS DIALOG DISCARDABLE 0, 0, 175, 225 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_CONVDISK_OPTS DIALOG 0, 0, 175, 225 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Convert to ProDOS disk" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Action",IDOK,34,203,50,14,WS_GROUP PUSHBUTTON "Cancel",IDCANCEL,90,203,50,14 GROUPBOX "Files",IDC_STATIC,7,7,161,40 - CONTROL ">Convert 65536 selected files<",IDC_USE_SELECTED,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 - CONTROL "Convert &all files",IDC_USE_ALL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 + CONTROL ">Convert 65536 selected files<",IDC_USE_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 + CONTROL "Convert &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 GROUPBOX "New disk size",IDC_STATIC,7,52,161,103,WS_GROUP - CONTROL "140KB (5.25"" floppy)",IDC_CONVDISK_140K,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,63,148,10 - CONTROL "800KB (3.5"" floppy)",IDC_CONVDISK_800K,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,74,148,10 - CONTROL "1.4MB (3.5"" PC floppy)",IDC_CONVDISK_1440K,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,85,88,10 - CONTROL "5MB",IDC_CONVDISK_5MB,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,96,148,10 - CONTROL "16MB",IDC_CONVDISK_16MB,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,107,148,10 - CONTROL "20MB (same as 20MB floptical)",IDC_CONVDISK_20MB,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,118,148,10 - CONTROL "32MB (largest ProDOS volume)",IDC_CONVDISK_32MB,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,129,148,10 - CONTROL "Specify size:",IDC_CONVDISK_SPECIFY,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,140,55,10 - EDITTEXT IDC_CONVDISK_SPECIFY_EDIT,72,139,43,14,ES_AUTOHSCROLL | - ES_NUMBER | WS_GROUP - LTEXT ">Space required: 1000KB<",IDC_CONVDISK_SPACEREQ,7,161, - 96,8 + CONTROL "140KB (5.25"" floppy)",IDC_CONVDISK_140K,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,63,148,10 + CONTROL "800KB (3.5"" floppy)",IDC_CONVDISK_800K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,74,148,10 + CONTROL "1.4MB (3.5"" PC floppy)",IDC_CONVDISK_1440K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,85,88,10 + CONTROL "5MB",IDC_CONVDISK_5MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,96,148,10 + CONTROL "16MB",IDC_CONVDISK_16MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,107,148,10 + CONTROL "20MB (same as 20MB floptical)",IDC_CONVDISK_20MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,118,148,10 + CONTROL "32MB (largest ProDOS volume)",IDC_CONVDISK_32MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,129,148,10 + CONTROL "Specify size:",IDC_CONVDISK_SPECIFY,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,140,55,10 + EDITTEXT IDC_CONVDISK_SPECIFY_EDIT,72,139,43,14,ES_AUTOHSCROLL | ES_NUMBER | WS_GROUP + LTEXT ">Space required: 1000KB<",IDC_CONVDISK_SPACEREQ,7,161,96,8 PUSHBUTTON "Compute",IDC_CONVDISK_COMPUTE,118,158,50,14 LTEXT "ProDOS volume name:",IDC_STATIC,7,182,73,8 EDITTEXT IDC_CONVDISK_VOLNAME,87,180,81,14,ES_AUTOHSCROLL LTEXT "blocks",IDC_STATIC,118,140,22,8 END -IDD_BULKCONV DIALOG DISCARDABLE 0, 0, 237, 57 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION +IDD_BULKCONV DIALOG 0, 0, 237, 57 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Bulk Conversion Progress" FONT 8, "MS Sans Serif" BEGIN @@ -1055,8 +844,8 @@ BEGIN LTEXT ">pathname<",IDC_BULKCONV_PATHNAME,7,18,223,8 END -IDD_OPENVOLUMEDLG DIALOG DISCARDABLE 0, 0, 300, 166 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_OPENVOLUMEDLG DIALOG 0, 0, 300, 166 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Select Volume" FONT 8, "MS Sans Serif" BEGIN @@ -1064,18 +853,14 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,64,145,50,14 PUSHBUTTON "Help",IDHELP,120,145,50,14 LTEXT "Show:",IDC_STATIC,7,9,27,8 - COMBOBOX IDC_VOLUME_FILTER,34,7,151,45,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - CONTROL "List1",IDC_VOLUME_LIST,"SysListView32",LVS_REPORT | - LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | - WS_BORDER | WS_TABSTOP,7,25,286,100 - CONTROL "Open as read-only (writing to the volume will be disabled)", - IDC_OPENVOL_READONLY,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,7,131,242,10 + COMBOBOX IDC_VOLUME_FILTER,34,7,151,45,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "List1",IDC_VOLUME_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,25,286,100 + CONTROL "Open as read-only (writing to the volume will be disabled)",IDC_OPENVOL_READONLY, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,131,242,10 END -IDD_VOLUMECOPYPROG DIALOG DISCARDABLE 0, 0, 276, 111 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION +IDD_VOLUMECOPYPROG DIALOG 0, 0, 276, 111 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Volume Copy Progress" FONT 8, "MS Sans Serif" BEGIN @@ -1088,45 +873,38 @@ BEGIN "msctls_progress32",PBS_SMOOTH | WS_BORDER,7,65,262,14 END -IDD_DISKEDIT_OPENWHICH DIALOG DISCARDABLE 0, 0, 134, 103 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE +IDD_DISKEDIT_OPENWHICH DIALOG 0, 0, 134, 103 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE FONT 8, "MS Sans Serif" BEGIN PUSHBUTTON "Open disk image file",IDC_DEOW_FILE,7,7,120,14 - PUSHBUTTON "Open logical or physical volume",IDC_DEOW_VOLUME,7,32, - 120,14 + PUSHBUTTON "Open logical or physical volume",IDC_DEOW_VOLUME,7,32,120,14 PUSHBUTTON "Open current archive",IDC_DEOW_CURRENT,7,57,120,14 PUSHBUTTON "Cancel",IDCANCEL,42,82,50,14 END -IDD_VOLUMECOPYSEL DIALOG DISCARDABLE 0, 0, 386, 138 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_VOLUMECOPYSEL DIALOG 0, 0, 386, 138 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Volume Copy" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Done",IDOK,316,117,63,14 - LTEXT "Choose volume or sub-volume to copy:",IDC_STATIC,7,7, - 293,8 - CONTROL "List2",IDC_VOLUMECOPYSEL_LIST,"SysListView32", - LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | - LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,22,297,109 + LTEXT "Choose volume or sub-volume to copy:",IDC_STATIC,7,7,293,8 + CONTROL "List2",IDC_VOLUMECOPYSEL_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,22,297,109 PUSHBUTTON "Copy &to file",IDC_VOLUEMCOPYSEL_TOFILE,316,22,63,14 - PUSHBUTTON "Load &from file",IDC_VOLUEMCOPYSEL_FROMFILE,316,42,63, - 14 + PUSHBUTTON "Load &from file",IDC_VOLUEMCOPYSEL_FROMFILE,316,42,63,14 PUSHBUTTON "Help",IDHELP,316,97,63,14 END -IDD_FORMATTING DIALOG DISCARDABLE 0, 0, 146, 38 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE +IDD_FORMATTING DIALOG 0, 0, 146, 38 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE FONT 8, "MS Sans Serif" BEGIN - CTEXT "Preparing disk image, please wait...",IDC_STATIC,7,15, - 132,8 + CTEXT "Preparing disk image, please wait...",IDC_STATIC,7,15,132,8 END -IDD_CREATEIMAGE DIALOG DISCARDABLE 0, 0, 342, 222 -STYLE DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | - WS_CAPTION | WS_SYSMENU +IDD_CREATEIMAGE DIALOG 0, 0, 342, 222 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Create Disk Image" FONT 8, "MS Sans Serif" BEGIN @@ -1134,77 +912,53 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,145,201,50,14 PUSHBUTTON "Help",IDHELP,202,201,50,14 GROUPBOX "Filesystem",IDC_STATIC,7,7,160,80 - CONTROL "DOS 3.2 (13-sector)",IDC_CREATEFS_DOS32,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,18,86,10 - CONTROL "DOS 3.3",IDC_CREATEFS_DOS33,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,29,86,10 - CONTROL "ProDOS",IDC_CREATEFS_PRODOS,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,40,86,10 - CONTROL "Pascal",IDC_CREATEFS_PASCAL,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,51,86,10 - CONTROL "HFS",IDC_CREATEFS_HFS,"Button",BS_AUTORADIOBUTTON,14,62, - 86,10 - CONTROL "Blank",IDC_CREATEFS_BLANK,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,73,86,10 + CONTROL "DOS 3.2 (13-sector)",IDC_CREATEFS_DOS32,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,18,86,10 + CONTROL "DOS 3.3",IDC_CREATEFS_DOS33,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,29,86,10 + CONTROL "ProDOS",IDC_CREATEFS_PRODOS,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,40,86,10 + CONTROL "Pascal",IDC_CREATEFS_PASCAL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,51,86,10 + CONTROL "HFS",IDC_CREATEFS_HFS,"Button",BS_AUTORADIOBUTTON,14,62,86,10 + CONTROL "Blank",IDC_CREATEFS_BLANK,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,73,86,10 GROUPBOX "New disk size",IDC_STATIC,7,93,160,104,WS_GROUP - CONTROL "140KB (5.25"" floppy)",IDC_CONVDISK_140K,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,104,148,10 - CONTROL "800KB (3.5"" floppy)",IDC_CONVDISK_800K,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,115,148,10 - CONTROL "1.4MB (3.5"" PC floppy)",IDC_CONVDISK_1440K,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,126,88,10 - CONTROL "5MB",IDC_CONVDISK_5MB,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,137,148,10 - CONTROL "16MB",IDC_CONVDISK_16MB,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,14,148,148,10 - CONTROL "20MB (same as 20MB floptical)",IDC_CONVDISK_20MB,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,159,148,10 - CONTROL "32MB (largest ProDOS volume)",IDC_CONVDISK_32MB,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,170,148,10 - CONTROL "Specify size:",IDC_CONVDISK_SPECIFY,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,181,55,10 - EDITTEXT IDC_CONVDISK_SPECIFY_EDIT,72,180,43,14,ES_AUTOHSCROLL | - ES_NUMBER | WS_GROUP + CONTROL "140KB (5.25"" floppy)",IDC_CONVDISK_140K,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,104,148,10 + CONTROL "800KB (3.5"" floppy)",IDC_CONVDISK_800K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,115,148,10 + CONTROL "1.4MB (3.5"" PC floppy)",IDC_CONVDISK_1440K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,126,88,10 + CONTROL "5MB",IDC_CONVDISK_5MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,137,148,10 + CONTROL "16MB",IDC_CONVDISK_16MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,148,148,10 + CONTROL "20MB (same as 20MB floptical)",IDC_CONVDISK_20MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,159,148,10 + CONTROL "32MB (largest ProDOS volume)",IDC_CONVDISK_32MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,170,148,10 + CONTROL "Specify size:",IDC_CONVDISK_SPECIFY,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,181,55,10 + EDITTEXT IDC_CONVDISK_SPECIFY_EDIT,72,180,43,14,ES_AUTOHSCROLL | ES_NUMBER | WS_GROUP LTEXT "blocks",IDC_STATIC,118,181,22,8 GROUPBOX "DOS options",IDC_STATIC,175,7,160,46 - CONTROL "Allocate DOS tracks",IDC_CREATEFSDOS_ALLOCDOS,"Button", - BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,184,20,143,10 + CONTROL "Allocate DOS tracks",IDC_CREATEFSDOS_ALLOCDOS,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,184,20,143,10 LTEXT "Disk volume:",IDC_STATIC,184,33,46,8 - EDITTEXT IDC_CREATEFSDOS_VOLNUM,235,32,40,14,ES_AUTOHSCROLL | - ES_NUMBER + EDITTEXT IDC_CREATEFSDOS_VOLNUM,235,32,40,14,ES_AUTOHSCROLL | ES_NUMBER GROUPBOX "ProDOS options",IDC_STATIC,175,56,160,45 LTEXT "ProDOS volume name (15 chars):",IDC_STATIC,184,69,143,8 EDITTEXT IDC_CREATEFSPRODOS_VOLNAME,184,80,115,14,ES_AUTOHSCROLL GROUPBOX "Pascal options",IDC_STATIC,175,104,160,45 LTEXT "Pascal volume name (7 chars):",IDC_STATIC,184,116,145,8 - EDITTEXT IDC_CREATEFSPASCAL_VOLNAME,184,127,115,14,ES_UPPERCASE | - ES_AUTOHSCROLL + EDITTEXT IDC_CREATEFSPASCAL_VOLNAME,184,127,115,14,ES_UPPERCASE | ES_AUTOHSCROLL GROUPBOX "HFS options",IDC_STATIC,175,152,160,45 EDITTEXT IDC_CREATEFSHFS_VOLNAME,184,178,145,14,ES_AUTOHSCROLL LTEXT "HFS volume name (27 chars):",IDC_STATIC,184,166,145,8 END -IDD_CHOOSE_ADD_TARGET DIALOG DISCARDABLE 0, 0, 220, 279 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +IDD_CHOOSE_ADD_TARGET DIALOG 0, 0, 220, 279 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Select location" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,26,258,50,14 PUSHBUTTON "Cancel",IDCANCEL,85,258,50,14 PUSHBUTTON "Help",IDHELP,144,258,50,14 - CONTROL "Tree1",IDC_ADD_TARGET_TREE,"SysTreeView32", - TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | - TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | - WS_TABSTOP,7,20,206,209 - LTEXT "Select location where files will be added:",IDC_STATIC, - 7,7,194,8 - LTEXT "Tip: you can skip this step on ProDOS disks by selecting a directory from the file list before using ""add files"".", - IDC_STATIC,7,235,206,17 + CONTROL "Tree1",IDC_ADD_TARGET_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,20,206,209 + LTEXT "Select location where files will be added:",IDC_STATIC,7,7,194,8 + LTEXT "Tip: you can skip this step on ProDOS disks by selecting a directory from the file list before using ""add files"".",IDC_STATIC,7,235,206,17 END IDD_ARCHIVEINFO_NUFX DIALOGEX 0, 0, 320, 127 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "NuFX (ShrinkIt) Archive" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN @@ -1228,7 +982,7 @@ BEGIN END IDD_ARCHIVEINFO_DISK DIALOGEX 0, 0, 320, 250 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Disk Image Info" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN @@ -1241,22 +995,18 @@ BEGIN LTEXT "Sector Ordering:",IDC_STATIC,7,95,68,8,0,WS_EX_RIGHT LTEXT "Filesystem Format:",IDC_STATIC,7,106,68,8,0,WS_EX_RIGHT LTEXT "Sub-Volume:",IDC_STATIC,7,81,68,8,0,WS_EX_RIGHT - COMBOBOX IDC_AIDISK_SUBVOLSEL,85,79,221,58,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_AIDISK_SUBVOLSEL,85,79,221,58,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Files+Directories:",IDC_STATIC,7,117,68,8,0,WS_EX_RIGHT LTEXT "Storage Capacity:",IDC_STATIC,7,128,68,8,0,WS_EX_RIGHT LTEXT "Free Space:",IDC_STATIC,7,139,68,8,0,WS_EX_RIGHT LTEXT "Damaged?",IDC_STATIC,7,161,68,8,0,WS_EX_RIGHT LTEXT "Notes:",IDC_STATIC,7,176,68,8,0,WS_EX_RIGHT - EDITTEXT IDC_AIDISK_NOTES,85,175,221,39,ES_MULTILINE | - ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | - WS_VSCROLL | WS_HSCROLL + EDITTEXT IDC_AIDISK_NOTES,85,175,221,39,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL GROUPBOX "Disk Characteristics",IDC_STATIC,6,68,307,153 LTEXT "",IDC_AI_FILENAME,85,18,221,8 LTEXT "",IDC_AIDISK_OUTERFORMAT,85,29,221,8 LTEXT "",IDC_AIDISK_FILEFORMAT,85,40,221,8 - LTEXT "",IDC_AIDISK_PHYSICALFORMAT,85,51,221, - 8 + LTEXT "",IDC_AIDISK_PHYSICALFORMAT,85,51,221,8 LTEXT "",IDC_AIDISK_SECTORORDER,85,95,221,8 LTEXT "",IDC_AIDISK_FSFORMAT,85,106,221,8 LTEXT "",IDC_AIDISK_FILECOUNT,85,117,221,8 @@ -1269,7 +1019,7 @@ BEGIN END IDD_ARCHIVEINFO_BNY DIALOGEX 0, 0, 320, 74 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Binary II Archive" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN @@ -1282,71 +1032,60 @@ BEGIN PUSHBUTTON "Help",IDHELP,105,53,50,14 END -IDD_PREF_DISKIMAGE DIALOG DISCARDABLE 0, 0, 219, 105 -STYLE DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION +IDD_PREF_DISKIMAGE DIALOG 0, 0, 219, 105 +STYLE DS_SETFONT | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION CAPTION "Disk Images" FONT 8, "MS Sans Serif" BEGIN GROUPBOX "General",IDC_STATIC,7,7,205,48 CONTROL "&Confirm disk image format",IDC_PDISK_CONFIRM_FORMAT, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,18,185,10 - CONTROL "Default to read-only when opening volumes", - IDC_PDISK_OPENVOL_RO,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,15,29,185,10 - CONTROL "Allow write access to physical disk 0 (not recommended)", - IDC_PDISK_OPENVOL_PHYS0,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,15,40,192,10 + CONTROL "Default to read-only when opening volumes",IDC_PDISK_OPENVOL_RO, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,29,185,10 + CONTROL "Allow write access to physical disk 0 (not recommended)",IDC_PDISK_OPENVOL_PHYS0, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,40,192,10 GROUPBOX "ProDOS",IDC_STATIC,7,61,205,38 - CONTROL "Allow &lower-case letters and spaces in filenames", - IDC_PDISK_PRODOS_ALLOWLOWER,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,15,72,186,10 - CONTROL "Use ""sparse"" allocation for empty blocks", - IDC_PDISK_PRODOS_USESPARSE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,15,83,186,10 + CONTROL "Allow &lower-case letters and spaces in filenames",IDC_PDISK_PRODOS_ALLOWLOWER, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,72,186,10 + CONTROL "Use ""sparse"" allocation for empty blocks",IDC_PDISK_PRODOS_USESPARSE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,83,186,10 END -IDD_CREATE_SUBDIR DIALOG DISCARDABLE 0, 0, 284, 98 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_CREATE_SUBDIR DIALOG 0, 0, 284, 98 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Create Subdirectory" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,87,77,50,14 PUSHBUTTON "Cancel",IDCANCEL,145,77,50,14 LTEXT "Parent directory:",IDC_STATIC,7,7,150,8 - EDITTEXT IDC_CREATESUBDIR_BASE,7,18,270,14,ES_AUTOHSCROLL | - ES_READONLY + EDITTEXT IDC_CREATESUBDIR_BASE,7,18,270,14,ES_AUTOHSCROLL | ES_READONLY LTEXT "New subdirectory name:",IDC_STATIC,7,42,151,8 EDITTEXT IDC_CREATESUBDIR_NEW,7,52,270,14,ES_AUTOHSCROLL END -IDD_RENAME_VOLUME DIALOG DISCARDABLE 0, 0, 218, 172 -STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_RENAME_VOLUME DIALOG 0, 0, 218, 172 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Rename volume" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,26,151,50,14 PUSHBUTTON "Cancel",IDCANCEL,83,151,50,14 LTEXT "Select volume to rename:",IDC_STATIC,7,7,175,8 - CONTROL "Tree1",IDC_RENAMEVOL_TREE,"SysTreeView32", - TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | - TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | - WS_TABSTOP,7,19,204,87 + CONTROL "Tree1",IDC_RENAMEVOL_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,19,204,87 LTEXT "New name:",IDC_STATIC,7,116,167,8 EDITTEXT IDC_RENAMEVOL_NEW,7,128,204,14,ES_AUTOHSCROLL PUSHBUTTON "Help",IDHELP,140,151,50,14 END -IDD_EOLSCAN DIALOG DISCARDABLE 0, 0, 173, 106 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_EOLSCAN DIALOG 0, 0, 173, 106 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "EOL Scanner" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,33,85,50,14 PUSHBUTTON "Help",IDHELP,90,85,50,14 - LTEXT "Results (click ""Help"" for explanation):",IDC_STATIC,7, - 7,159,8 + LTEXT "Results (click ""Help"" for explanation):",IDC_STATIC,7,7,159,8 RTEXT ">cr<",IDC_EOLSCAN_CR,7,45,61,8 LTEXT "carriage returns (0x0d)",IDC_STATIC,72,45,94,8 RTEXT ">lf<",IDC_EOLSCAN_LF,7,56,61,8 @@ -1359,9 +1098,8 @@ BEGIN LTEXT "high-ASCII characters",IDC_STATIC,72,34,94,8 END -IDD_TWOIMG_PROPS DIALOG DISCARDABLE 0, 0, 220, 198 -STYLE DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | - WS_CAPTION | WS_SYSMENU +IDD_TWOIMG_PROPS DIALOG 0, 0, 220, 198 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "2MG Disk Image Properties" FONT 8, "MS Sans Serif" BEGIN @@ -1376,27 +1114,23 @@ BEGIN LTEXT ">format<",IDC_TWOIMG_FORMAT,65,40,140,8 RTEXT "Blocks:",IDC_STATIC,9,51,50,8 LTEXT ">blocks<",IDC_TWOIMG_BLOCKS,65,51,140,8 - CONTROL "Locked",IDC_TWOIMG_LOCKED,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,7,68,120,10 - CONTROL "Specify disk volume number",IDC_TWOIMG_DOSVOLSET,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,79,118,10 + CONTROL "Locked",IDC_TWOIMG_LOCKED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,68,120,10 + CONTROL "Specify disk volume number",IDC_TWOIMG_DOSVOLSET,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,79,118,10 LTEXT "Volume number:",IDC_STATIC,7,92,52,8 - EDITTEXT IDC_TWOIMG_DOSVOLNUM,65,91,40,14,ES_AUTOHSCROLL | - ES_NUMBER + EDITTEXT IDC_TWOIMG_DOSVOLNUM,65,91,40,14,ES_AUTOHSCROLL | ES_NUMBER LTEXT "Comment:",IDC_STATIC,7,110,52,8 - EDITTEXT IDC_TWOIMG_COMMENT,7,121,206,48,ES_MULTILINE | - ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL + EDITTEXT IDC_TWOIMG_COMMENT,7,121,206,48,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL END -IDD_LOADING DIALOG DISCARDABLE 0, 0, 146, 38 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE +IDD_LOADING DIALOG 0, 0, 146, 38 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE FONT 8, "MS Sans Serif" BEGIN CTEXT "Loading data, please wait...",IDC_STATIC,7,15,132,8 END -IDD_IMPORTCASSETTE DIALOG DISCARDABLE 0, 0, 355, 157 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_IMPORTCASSETTE DIALOG 0, 0, 355, 157 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Import cassette WAV" FONT 8, "MS Sans Serif" BEGIN @@ -1406,16 +1140,13 @@ BEGIN LTEXT "Input file:",IDC_STATIC,7,7,30,8 LTEXT ">input-file<",IDC_CASSETTE_INPUT,43,7,305,8 LTEXT "Algorithm:",IDC_STATIC,7,21,34,8 - COMBOBOX IDC_CASSETTE_ALG,43,19,173,63,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CASSETTE_ALG,43,19,173,63,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT "Select the chunk to import:",IDC_STATIC,7,40,180,8 - CONTROL "List2",IDC_CASSETTE_LIST,"SysListView32",LVS_REPORT | - LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | - WS_BORDER | WS_TABSTOP,7,51,341,75 + CONTROL "List2",IDC_CASSETTE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,51,341,75 END -IDD_CASSIMPTARGET DIALOG DISCARDABLE 0, 0, 186, 127 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +IDD_CASSIMPTARGET DIALOG 0, 0, 186, 127 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Import..." FONT 8, "MS Sans Serif" BEGIN @@ -1424,26 +1155,21 @@ BEGIN LTEXT "Name of file to create?",IDC_STATIC,7,7,113,8 EDITTEXT IDC_CASSIMPTARG_FILENAME,7,18,172,14,ES_AUTOHSCROLL GROUPBOX "Static",IDC_STATIC,7,36,172,60 - CONTROL "Applesoft BASIC",IDC_CASSIMPTARG_BAS,"Button", - BS_AUTORADIOBUTTON | WS_GROUP,13,47,87,10 - CONTROL "Integer BASIC",IDC_CASSIMPTARG_INT,"Button", - BS_AUTORADIOBUTTON,13,58,94,10 - CONTROL "Binary",IDC_CASSIMPTARG_BIN,"Button",BS_AUTORADIOBUTTON, - 13,69,77,10 + CONTROL "Applesoft BASIC",IDC_CASSIMPTARG_BAS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,47,87,10 + CONTROL "Integer BASIC",IDC_CASSIMPTARG_INT,"Button",BS_AUTORADIOBUTTON,13,58,94,10 + CONTROL "Binary",IDC_CASSIMPTARG_BIN,"Button",BS_AUTORADIOBUTTON,13,69,77,10 LTEXT "Start address (hex):",IDC_STATIC,25,80,63,8 EDITTEXT IDC_CASSIMPTARG_BINADDR,93,78,31,14,ES_AUTOHSCROLL LTEXT ".XXXX",IDC_CASSIMPTARG_RANGE,126,80,29,8 END -IDD_ADD_CLASH DIALOG DISCARDABLE 0, 0, 307, 113 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +IDD_ADD_CLASH DIALOG 0, 0, 307, 113 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Filename Clash" FONT 8, "MS Sans Serif" BEGIN - LTEXT "A file being added to the archive has the same name as another file being added. This can happen when directory names or file attribute preservations strings are stripped from Windows filenames.", - IDC_STATIC,7,7,293,29 - LTEXT "You can rename this file, skip adding it, or cancel the entire operation.", - IDC_STATIC,7,39,290,8 + LTEXT "A file being added to the archive has the same name as another file being added. This can happen when directory names or file attribute preservations strings are stripped from Windows filenames.",IDC_STATIC,7,7,293,29 + LTEXT "You can rename this file, skip adding it, or cancel the entire operation.",IDC_STATIC,7,39,290,8 LTEXT "Windows name:",IDC_STATIC,7,57,58,8 LTEXT ">windows name<",IDC_CLASH_WINNAME,70,57,230,8 LTEXT "Storage name:",IDC_STATIC,7,72,58,8 @@ -1454,7 +1180,7 @@ BEGIN END IDD_ARCHIVEINFO_ACU DIALOGEX 0, 0, 320, 74 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "AppleLink ACU Archive" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN @@ -1467,40 +1193,35 @@ BEGIN PUSHBUTTON "Help",IDHELP,105,53,50,14 END -IDD_IMPORT_BAS DIALOG DISCARDABLE 0, 0, 247, 118 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | - WS_SYSMENU +IDD_IMPORT_BAS DIALOG 0, 0, 247, 118 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Import BAS from text file" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "Save",IDOK,44,97,50,14 PUSHBUTTON "Cancel",IDCANCEL,98,97,50,14 LTEXT "Results:",IDC_STATIC,7,7,40,8 - EDITTEXT IDC_IMPORT_BAS_RESULTS,7,19,233,42,ES_MULTILINE | - ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL + EDITTEXT IDC_IMPORT_BAS_RESULTS,7,19,233,42,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL LTEXT "Save as:",IDC_STATIC,7,74,34,8 EDITTEXT IDC_IMPORT_BAS_SAVEAS,39,72,201,14,ES_AUTOHSCROLL PUSHBUTTON "Help",IDHELP,153,97,50,14 END -IDD_PASTE_SPECIAL DIALOG DISCARDABLE 0, 0, 186, 78 -STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +IDD_PASTE_SPECIAL DIALOG 0, 0, 186, 78 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Paste..." FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,40,57,50,14 PUSHBUTTON "Cancel",IDCANCEL,95,57,50,14 - LTEXT "Choose how you would like to handle filenames:", - IDC_PASTE_SPECIAL_COUNT,7,7,172,8 - CONTROL "Keep full pathnames",IDC_PASTE_SPECIAL_PATHS,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,18,22,161,10 - CONTROL "Strip paths, keep filename only", - IDC_PASTE_SPECIAL_NOPATHS,"Button",BS_AUTORADIOBUTTON | - WS_TABSTOP,18,34,161,10 + LTEXT "Choose how you would like to handle filenames:",IDC_PASTE_SPECIAL_COUNT,7,7,172,8 + CONTROL "Keep full pathnames",IDC_PASTE_SPECIAL_PATHS,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,18,22,161,10 + CONTROL "Strip paths, keep filename only",IDC_PASTE_SPECIAL_NOPATHS, + "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,18,34,161,10 END -IDD_PROGRESS_COUNTER DIALOG DISCARDABLE 0, 0, 186, 57 -STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE +IDD_PROGRESS_COUNTER DIALOG 0, 0, 186, 57 +STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE FONT 8, "MS Sans Serif" BEGIN PUSHBUTTON "Cancel",IDCANCEL,67,36,50,14 @@ -1514,21 +1235,21 @@ END // Bitmap // -IDB_FSLOGO BITMAP DISCARDABLE "Graphics\\fslogo.bmp" -IDR_TOOLBAR1 BITMAP DISCARDABLE "Graphics\\toolbar1.bmp" -IDB_LIST_PICS BITMAP DISCARDABLE "Graphics\\list-pics.bmp" -IDB_HDRBAR BITMAP DISCARDABLE "Graphics\\hdrbar.bmp" -IDB_NEW_FOLDER BITMAP DISCARDABLE "graphics\\NewFolder.bmp" -IDB_CHOOSE_FOLDER BITMAP DISCARDABLE "Graphics\\ChooseFolder.bmp" -IDB_VOL_PICS BITMAP DISCARDABLE "graphics\\vol_pics.bmp" -IDB_TREE_PICS BITMAP DISCARDABLE "graphics\\tree_pics.bmp" +IDB_FSLOGO BITMAP "Graphics\\fslogo.bmp" +IDR_TOOLBAR1 BITMAP "Graphics\\toolbar1.bmp" +IDB_LIST_PICS BITMAP "Graphics\\list-pics.bmp" +IDB_HDRBAR BITMAP "Graphics\\hdrbar.bmp" +IDB_NEW_FOLDER BITMAP "graphics\\NewFolder.bmp" +IDB_CHOOSE_FOLDER BITMAP "Graphics\\ChooseFolder.bmp" +IDB_VOL_PICS BITMAP "graphics\\vol_pics.bmp" +IDB_TREE_PICS BITMAP "graphics\\tree_pics.bmp" ///////////////////////////////////////////////////////////////////////////// // // Toolbar // -IDR_TOOLBAR1 TOOLBAR DISCARDABLE 24, 23 +IDR_TOOLBAR1 TOOLBAR 24, 23 BEGIN BUTTON IDM_FILE_OPEN BUTTON IDM_FILE_OPEN_VOLUME @@ -1553,7 +1274,6 @@ BEGIN END -#ifndef _MAC ///////////////////////////////////////////////////////////////////////////// // // Version @@ -1576,18 +1296,15 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "Comments", "The end is nigh.\0" - VALUE "CompanyName", "CiderPress Project\0" - VALUE "FileDescription", "CiderPress\0" - VALUE "FileVersion", "3, 0, 1, 0\0" - VALUE "InternalName", "CiderPress\0" - VALUE "LegalCopyright", "Copyright © 2009 CiderPress project authors\0" - VALUE "LegalTrademarks", "\0" - VALUE "OriginalFilename", "CiderPress.exe\0" - VALUE "PrivateBuild", "\0" - VALUE "ProductName", "CiderPress\0" - VALUE "ProductVersion", "3, 0, 1, 0\0" - VALUE "SpecialBuild", "\0" + VALUE "Comments", "The end is nigh." + VALUE "CompanyName", "CiderPress Project" + VALUE "FileDescription", "CiderPress" + VALUE "FileVersion", "3, 0, 1, 0" + VALUE "InternalName", "CiderPress" + VALUE "LegalCopyright", "Copyright © 2009 CiderPress project authors" + VALUE "OriginalFilename", "CiderPress.exe" + VALUE "ProductName", "CiderPress" + VALUE "ProductVersion", "3, 0, 1, 0" END END BLOCK "VarFileInfo" @@ -1596,8 +1313,6 @@ BEGIN END END -#endif // !_MAC - ///////////////////////////////////////////////////////////////////////////// // @@ -1605,7 +1320,7 @@ END // #ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE +GUIDELINES DESIGNINFO BEGIN IDD_ABOUTDLG, DIALOG BEGIN @@ -2150,7 +1865,7 @@ END // String Table // -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_FILE_NEW_ARCHIVE "Create a new NuFX (ShrinkIt) archive\nNew archive (Ctrl-N)" IDM_FILE_OPEN "Open an archive or disk image\nOpen (Ctrl-O)" @@ -2168,7 +1883,7 @@ BEGIN "Select un-selected items, and un-select selected items" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_EDIT_PREFERENCES "Set application preferences" IDM_HELP_CONTENTS "Table of contents for on-line help" @@ -2184,7 +1899,7 @@ BEGIN IDM_SORT_RATIO "Sort by compression ratio" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_SORT_PACKED "Sort by compressed size" IDM_SORT_ACCESS "Sort by access flags" @@ -2196,7 +1911,7 @@ BEGIN IDM_HELP_WEBSITE "Visit the CiderPress web site\nGo to web site" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_EDIT_INVERT_SELECTION "Invert selection" IDM_ACTIONS_RENAME "Rename a file\nRename" @@ -2206,7 +1921,7 @@ BEGIN IDM_ACTIONS_OPENASDISK "Open the selected file as a disk image in a new window\nOpen as disk image" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_READONLY "(read only)" IDS_FAILED "Failed" @@ -2216,27 +1931,27 @@ BEGIN IDS_CANCELLED "Cancelled" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_SELECTED_COUNT "1 file selected" IDS_SELECTED_COUNTS_FMT "%d files selected" IDS_BLOCK "Block:" - IDS_DEFILE_FIND_FAILED "Unable to open %s: file not found." + IDS_DEFILE_FIND_FAILED "Unable to open %ls: file not found." END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN - IDS_DEFILE_OPEN_FAILED "Unable to open %s: %s." + IDS_DEFILE_OPEN_FAILED "Unable to open %ls: %hs." IDS_DISKEDIT_NOREADTS "Unable to read track %d sector %d." IDS_DISKEDIT_NOREADBLOCK "Unable to read block %d." - IDS_DISKEDIT_FIRDFAILED "Unable to read from file: %s." + IDS_DISKEDIT_FIRDFAILED "Unable to read from file: %hs." IDS_EXT_SELECTED_COUNT "Extract 1 selected file" IDS_EXT_SELECTED_COUNTS_FMT "Extract %d selected files" IDS_INDIC_RSRC " " IDS_INDIC_DISK " " END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_INDIC_COMMENT " " IDS_INDIC_DATA "" @@ -2256,12 +1971,12 @@ BEGIN IDS_MB_APP_NAME "CiderPress" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN AFX_IDS_IDLEMESSAGE "Ready" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_NO_COMMENT_ADD "This entry does not have a comment. Did you want to add one?" IDS_EDIT_COMMENT "Edit Comment" @@ -2281,7 +1996,7 @@ BEGIN IDS_REG_BAD_ENTRY "The information you have entered does not appear to be correct.\r\n\r\nPlease make sure that all values are entered exactly as they appear in the confirmation message." END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_OPEN_AS_NUFX "The file could not be opened as a disk image. However, it appears to be a valid NuFX file archive. Open it?" IDS_ABOUT_UNREGISTERED "Unregistered - register today!" @@ -2302,7 +2017,7 @@ BEGIN IDS_CONVFILE_SELECTED_COUNTS_FMT "Convert %d selected files" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_ACTIONS_EDIT_PROPS "Edit file type and access permissions\nEdit file attributes" IDM_ACTIONS_CONV_DISK "Convert part or all of this archive to a ProDOS disk image\nConvert to disk image" @@ -2317,7 +2032,7 @@ BEGIN IDM_FILE_ARCHIVEINFO "Show detailed information on this archive or disk image\nArchive Info (Ctrl-I)" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_CONVFILE_ALL_FILES "Convert all files" IDS_CONVFILE_OK "Go" @@ -2327,7 +2042,7 @@ BEGIN IDS_CONVDISK_ALL_FILES "Convert all files" IDS_CONVDISK_OK "Go" IDS_CONVDISK_TITLE "Convert to disk archive" - IDS_CONVDISK_SPACEREQ "Space required: %s" + IDS_CONVDISK_SPACEREQ "Space required: %ls" IDS_CDESC_40TRACK "40-track image (5.25"" floppy)" IDS_TRACKSTAR_TO_OTHER_WARNING "TrackStar images are stored as 40-track variable-length nibble images. No other supported format is directly compatible, so images are converted as formatted 35-track 16-sector disks. This probably won't work well for copy-protected disks.\n\nContinue anyway?" @@ -2339,7 +2054,7 @@ BEGIN IDS_VOLUME_NO_GENERIC "That type of drive is not supported." END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_VOLUME_NO_CDRIVE "For safety, access to C:\\ is not allowed." IDS_VOLUME_SELECT_ONE "Please select the volume to access." @@ -2363,7 +2078,7 @@ BEGIN IDS_CLIPBOARD_ALLOCFAILED "Unable to allocate memory for clipboard data." END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_ACTIONS_CREATE_SUBDIR "Create a new subdirectory on a ProDOS disk\nCreate subdirectory" @@ -2381,14 +2096,14 @@ BEGIN "Convert a WAV file recording of an Apple II cassette into an Apple II file\nImport file from WAV" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_CLIPBOARD_NOTFOUND "No CiderPress data found in the clipboard." IDS_CLIPBOARD_READFAILURE "Failed while reading data from the clipboard." IDS_CLIPBOARD_WRITEFAILURE "Failed while copying data to the clipboard." IDS_CLIPBOARD_WIN9XMAX "In Win98/ME the clipboard is limited to %ldMB of data. Your request includes %.3fMB. Please select less data." IDS_PRINTER_NOT_USABLE "Attempting to print to the requested printer failed." - IDS_NLIST_DATA_FAILED "Unable to load NList.Data from '%s' or '%s'." + IDS_NLIST_DATA_FAILED "Unable to load NList.Data from '%ls' or '%ls'." IDS_PROPS_DOS_TYPE_CHANGE "Changing the type of a DOS 3.2/3.3 file to BIN, INT, or BAS can cause problems. Consult the ""help"" file for details. Do you wish to continue?" IDS_FDI_TO_OTHER_WARNING @@ -2398,7 +2113,7 @@ BEGIN IDS_PASTE_SPECIAL_COUNT "%d files in clipboard" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDM_ACTIONS_IMPORT_BAS "Import Applesoft BASIC program from text file" IDM_FILE_REOPEN "Re-open the existing archive or disk image\nReopen" @@ -2406,12 +2121,12 @@ BEGIN IDM_EDIT_PASTE_SPECIAL "Choose how files on the clipboard will be pasted" END -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN ID_EDIT_FIND "Search for a " END -#endif // English (U.S.) resources +#endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/app/Clipboard.cpp b/app/Clipboard.cpp index d4ae7ac..1b33235 100644 --- a/app/Clipboard.cpp +++ b/app/Clipboard.cpp @@ -11,7 +11,7 @@ #include "PasteSpecialDialog.h" -static const char* kClipboardFmtName = "faddenSoft:CiderPress:v1"; +static const WCHAR kClipboardFmtName[] = L"faddenSoft:CiderPress:v1"; const int kClipVersion = 1; // should match "vN" in fmt name const unsigned short kEntrySignature = 0x4350; @@ -71,7 +71,7 @@ typedef enum EntryKind { typedef struct FileCollectionEntry { unsigned short signature; // let's be paranoid unsigned short dataOffset; // offset to start of data - unsigned short fileNameLen; // len of filename + unsigned short fileNameLen; // len of (8-bit) filename, in bytes unsigned long dataLen; // len of data fork unsigned long rsrcLen; // len of rsrc fork unsigned long cmmtLen; // len of comments @@ -138,7 +138,7 @@ MainWindow::OnEditCopy(void) GenericEntry::kAnyThread | GenericEntry::kAllowDirectory); if (selSet.GetNumEntries() == 0) { errStr.LoadString(IDS_CLIPBOARD_NOITEMS); - MessageBox(errStr, "No match", MB_OK | MB_ICONEXCLAMATION); + MessageBox(errStr, L"No match", MB_OK | MB_ICONEXCLAMATION); goto bail; } @@ -151,21 +151,21 @@ MainWindow::OnEditCopy(void) * Add the string to the clipboard. The clipboard will own the memory we * allocate. */ - hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, fileList.GetLength() +1); + size_t neededLen = (fileList.GetLength() + 1) * sizeof(WCHAR); + hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, neededLen); if (hGlobal == nil) { - WMSG1("Failed allocating %ld bytes\n", fileList.GetLength() +1); + WMSG1("Failed allocating %d bytes\n", neededLen); errStr.LoadString(IDS_CLIPBOARD_ALLOCFAILED); ShowFailureMsg(this, errStr, IDS_FAILED); goto bail; } - WMSG1(" Allocated %ld bytes for file list on clipboard\n", - fileList.GetLength() +1); + WMSG1(" Allocated %ld bytes for file list on clipboard\n", neededLen); pGlobal = ::GlobalLock(hGlobal); ASSERT(pGlobal != nil); - strcpy((char*) pGlobal, fileList); + wcscpy((WCHAR*) pGlobal, fileList); ::GlobalUnlock(hGlobal); - SetClipboardData(CF_TEXT, hGlobal); + SetClipboardData(CF_UNICODETEXT, hGlobal); /* * Create a (potentially very large) buffer with the contents of the @@ -201,8 +201,8 @@ MainWindow::CreateFileList(SelectionSet* pSelSet) SelectionEntry* pSelEntry; GenericEntry* pEntry; CString tmpStr, fullStr; - char fileTypeBuf[ContentList::kFileTypeBufLen]; - char auxTypeBuf[ContentList::kAuxTypeBufLen]; + WCHAR fileTypeBuf[ContentList::kFileTypeBufLen]; + WCHAR auxTypeBuf[ContentList::kAuxTypeBufLen]; CString fileName, subVol, fileType, auxType, modDate, format, length; pSelEntry = pSelSet->IterNext(); @@ -218,9 +218,9 @@ MainWindow::CreateFileList(SelectionSet* pSelSet) auxType = DblDblQuote(auxTypeBuf); FormatDate(pEntry->GetModWhen(), &modDate); format = pEntry->GetFormatStr(); - length.Format("%I64d", (LONGLONG) pEntry->GetUncompressedLen()); + length.Format(L"%I64d", (LONGLONG) pEntry->GetUncompressedLen()); - tmpStr.Format("\"%s\"\t%s\t\"%s\"\t\"%s\"\t%s\t%s\t%s\r\n", + tmpStr.Format(L"\"%hs\"\t%hs\t\"%hs\"\t\"%hs\"\t%hs\t%hs\t%hs\r\n", fileName, subVol, fileType, auxType, modDate, format, length); fullStr += tmpStr; @@ -234,12 +234,12 @@ MainWindow::CreateFileList(SelectionSet* pSelSet) * Double-up all double quotes. */ /*static*/ CString -MainWindow::DblDblQuote(const char* str) +MainWindow::DblDblQuote(const WCHAR* str) { CString result; - char* buf; + WCHAR* buf; - buf = result.GetBuffer(strlen(str) * 2 +1); + buf = result.GetBuffer(wcslen(str) * 2 +1); while (*str != '\0') { if (*str == '"') { *buf++ = *str; @@ -287,7 +287,7 @@ MainWindow::CreateFileCollection(SelectionSet* pSelSet) HGLOBAL hGlobal = nil; HGLOBAL hResult = nil; LPVOID pGlobal; - long totalLength, numFiles; + size_t totalLength, numFiles; long priorLength; /* get len of text version(s), with kluge to avoid close & reopen */ @@ -311,7 +311,7 @@ MainWindow::CreateFileCollection(SelectionSet* pSelSet) if (pEntry->GetRecordKind() != GenericEntry::kRecordKindVolumeDir) { totalLength += sizeof(FileCollectionEntry); - totalLength += strlen(pEntry->GetPathName()) +1; + totalLength += wcslen(pEntry->GetPathName()) +1; numFiles++; if (pEntry->GetRecordKind() != GenericEntry::kRecordKindDirectory) { totalLength += (long) pEntry->GetDataForkLen(); @@ -355,9 +355,9 @@ MainWindow::CreateFileCollection(SelectionSet* pSelSet) hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, totalLength); if (hGlobal == nil) { CString errMsg; - errMsg.Format("ERROR: unable to allocate %ld bytes for copy", + errMsg.Format(L"ERROR: unable to allocate %ld bytes for copy", totalLength); - WMSG1("%s\n", (const char*) errMsg); + WMSG1("%ls\n", (LPCWSTR) errMsg); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -374,7 +374,7 @@ MainWindow::CreateFileCollection(SelectionSet* pSelSet) ASSERT(fpActionProgress == nil); fpActionProgress = new ActionProgressDialog; fpActionProgress->Create(ActionProgressDialog::kActionExtract, this); - fpActionProgress->SetFileName("Clipboard"); + fpActionProgress->SetFileName(L"Clipboard"); /* * Extract the data into the buffer. @@ -392,7 +392,8 @@ MainWindow::CreateFileCollection(SelectionSet* pSelSet) pEntry = pSelEntry->GetEntry(); ASSERT(pEntry != nil); - fpActionProgress->SetArcName(pEntry->GetDisplayName()); + CString displayName(pEntry->GetDisplayName()); + fpActionProgress->SetArcName(displayName); errStr = CopyToCollection(pEntry, &buf, &remainingLen); if (!errStr.IsEmpty()) { @@ -488,7 +489,7 @@ MainWindow::CopyToCollection(GenericEntry* pEntry, void** pBuf, long* pBufLen) memset(&collEnt, 0x99, sizeof(collEnt)); collEnt.signature = kEntrySignature; collEnt.dataOffset = sizeof(collEnt); - collEnt.fileNameLen = strlen(pEntry->GetPathName()) +1; + collEnt.fileNameLen = wcslen(pEntry->GetPathName()) +1; if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { collEnt.dataLen = collEnt.rsrcLen = collEnt.cmmtLen = 0; } else { @@ -622,6 +623,7 @@ MainWindow::OnEditPaste(void) DoPaste(pasteJunkPaths); } + void MainWindow::OnUpdateEditPaste(CCmdUI* pCmdUI) { @@ -668,6 +670,7 @@ MainWindow::OnEditPasteSpecial(void) DoPaste(pasteJunkPaths); } + void MainWindow::OnUpdateEditPasteSpecial(CCmdUI* pCmdUI) { @@ -708,10 +711,10 @@ MainWindow::DoPaste(bool pasteJunkPaths) WMSG1("Found %d clipboard formats\n", CountClipboardFormats()); while ((format = EnumClipboardFormats(format)) != 0) { CString tmpStr; - tmpStr.Format(" %u", format); + tmpStr.Format(L" %u", format); buildStr += tmpStr; } - WMSG1(" %s\n", buildStr); + WMSG1(" %ls\n", (LPCWSTR) buildStr); #if 0 if (IsClipboardFormatAvailable(CF_HDROP)) { @@ -820,14 +823,14 @@ MainWindow::ProcessClipboard(const void* vbuf, long bufLen, bool pasteJunkPaths) */ if (fpOpenArchive->GetArchiveKind() == GenericArchive::kArchiveDiskImage) { if (!ChooseAddTarget(&pTargetSubdir, &xferOpts.fpTargetFS)) - return ""; + return L""; } fpOpenArchive->XferPrepare(&xferOpts); xferPrepped = true; if (pTargetSubdir != nil) { storagePrefix = pTargetSubdir->GetPathName(); - WMSG1("--- using storagePrefix '%s'\n", (const char*) storagePrefix); + WMSG1("--- using storagePrefix '%ls'\n", (LPCWSTR) storagePrefix); } /* @@ -836,7 +839,7 @@ MainWindow::ProcessClipboard(const void* vbuf, long bufLen, bool pasteJunkPaths) ASSERT(fpActionProgress == nil); fpActionProgress = new ActionProgressDialog; fpActionProgress->Create(ActionProgressDialog::kActionAdd, this); - fpActionProgress->SetArcName("Clipboard data"); + fpActionProgress->SetArcName(L"Clipboard data"); /* * Loop over all files. @@ -923,8 +926,8 @@ MainWindow::ProcessClipboard(const void* vbuf, long bufLen, bool pasteJunkPaths) */ processErrStr = ProcessClipboardEntry(&collEnt, fileName, buf, bufLen); if (!processErrStr.IsEmpty()) { - errMsg.Format("Unable to paste '%s': %s.", - (const char*) fileName, (const char*) processErrStr); + errMsg.Format(L"Unable to paste '%ls': %ls.", + (LPCWSTR) fileName, (LPCWSTR) processErrStr); goto bail; } @@ -959,7 +962,7 @@ bail: */ CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt, - const char* pathName, const unsigned char* buf, long remLen) + const WCHAR* pathName, const unsigned char* buf, long remLen) { GenericArchive::FileDetails::FileKind entryKind; GenericArchive::FileDetails details; @@ -969,10 +972,10 @@ MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt, CString errMsg; entryKind = (GenericArchive::FileDetails::FileKind) pCollEnt->entryKind; - WMSG2(" Processing '%s' (%d)\n", pathName, entryKind); + WMSG2(" Processing '%ls' (%d)\n", pathName, entryKind); details.entryKind = entryKind; - details.origName = "Clipboard"; + details.origName = L"Clipboard"; details.storageName = pathName; details.fileSysFmt = (DiskImg::FSFormat) pCollEnt->sourceFS; details.fileSysInfo = pCollEnt->fssep; diff --git a/app/ConfirmOverwriteDialog.cpp b/app/ConfirmOverwriteDialog.cpp index 673a75b..c3e885d 100644 --- a/app/ConfirmOverwriteDialog.cpp +++ b/app/ConfirmOverwriteDialog.cpp @@ -49,8 +49,8 @@ RenameOverwriteDialog::DoDataExchange(CDataExchange* pDX) /* validate the path field */ if (pDX->m_bSaveAndValidate) { if (fNewName.IsEmpty()) { - MessageBox("You must specify a new name.", - "CiderPress", MB_OK); + MessageBox(L"You must specify a new name.", + L"CiderPress", MB_OK); pDX->Fail(); } @@ -98,7 +98,7 @@ ConfirmOverwriteDialog::OnInitDialog(void) pWnd = GetDlgItem(IDC_OVWR_EXIST_INFO); ASSERT(pWnd != nil); FormatDate(fExistingFileModWhen, &dateStr); - tmpStr.Format("Modified %s", dateStr); + tmpStr.Format(L"Modified %ls", dateStr); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_OVWR_NEW_NAME); @@ -108,7 +108,7 @@ ConfirmOverwriteDialog::OnInitDialog(void) pWnd = GetDlgItem(IDC_OVWR_NEW_INFO); ASSERT(pWnd != nil); FormatDate(fNewFileModWhen, &dateStr); - tmpStr.Format("Modified %s", dateStr); + tmpStr.Format(L"Modified %ls", dateStr); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_OVWR_RENAME); diff --git a/app/ConfirmOverwriteDialog.h b/app/ConfirmOverwriteDialog.h index c7ec23d..7f4284b 100644 --- a/app/ConfirmOverwriteDialog.h +++ b/app/ConfirmOverwriteDialog.h @@ -6,8 +6,8 @@ /* * Ask for confirmation before overwriting a file. */ -#ifndef __CONFIRMOVERWRITEDIALOG__ -#define __CONFIRMOVERWRITEDIALOG__ +#ifndef APP_CONFIRMOVERWRITEDIALOG_H +#define APP_CONFIRMOVERWRITEDIALOG_H #include "resource.h" @@ -90,4 +90,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CONFIRMOVERWRITEDIALOG__*/ \ No newline at end of file +#endif /*APP_CONFIRMOVERWRITEDIALOG_H*/ diff --git a/app/ContentList.cpp b/app/ContentList.cpp index 1a74d78..be10092 100644 --- a/app/ContentList.cpp +++ b/app/ContentList.cpp @@ -110,7 +110,7 @@ ContentList::OnCreate(LPCREATESTRUCT lpcs) /* load the data and sort it */ if (LoadData() != 0) { - MessageBox("Not all entries were loaded.", "Error", + MessageBox(L"Not all entries were loaded.", L"Error", MB_OK | MB_ICONSTOP); /* keep going with what we've got; the error only affects display */ } @@ -368,7 +368,7 @@ ContentList::NewSortOrder(void) * Use kFileTypeBufLen. */ /*static*/ void -ContentList::MakeFileTypeDisplayString(const GenericEntry* pEntry, char* buf) +ContentList::MakeFileTypeDisplayString(const GenericEntry* pEntry, WCHAR* buf) { bool isDir = pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir || @@ -376,30 +376,30 @@ ContentList::MakeFileTypeDisplayString(const GenericEntry* pEntry, char* buf) if (pEntry->GetSourceFS() == DiskImg::kFormatMacHFS && isDir) { /* HFS directories don't have types; fake it */ - ::lstrcpy(buf, "DIR/"); + wcscpy(buf, L"DIR/"); } else if (!(pEntry->GetFileType() >= 0 && pEntry->GetFileType() <= 0xff)) { /* oversized type; assume it's HFS */ - char typeBuf[kFileTypeBufLen]; + WCHAR typeBuf[kFileTypeBufLen]; MakeMacTypeString(pEntry->GetFileType(), typeBuf); switch (pEntry->GetRecordKind()) { case GenericEntry::kRecordKindFile: - ::lstrcpy(buf, typeBuf); + wcscpy(buf, typeBuf); break; case GenericEntry::kRecordKindForkedFile: - ::sprintf(buf, "%s+", typeBuf); + wsprintf(buf, L"%ls+", typeBuf); break; case GenericEntry::kRecordKindUnknown: // shouldn't happen - ::sprintf(buf, "%s-", typeBuf); + wsprintf(buf, L"%ls-", typeBuf); break; case GenericEntry::kRecordKindVolumeDir: case GenericEntry::kRecordKindDirectory: case GenericEntry::kRecordKindDisk: default: ASSERT(FALSE); - ::lstrcpy(buf, "!!!"); + wcscpy(buf, L"!!!"); break; } } else { @@ -407,24 +407,24 @@ ContentList::MakeFileTypeDisplayString(const GenericEntry* pEntry, char* buf) switch (pEntry->GetRecordKind()) { case GenericEntry::kRecordKindVolumeDir: case GenericEntry::kRecordKindDirectory: - ::sprintf(buf, "%s/", pEntry->GetFileTypeString()); + wsprintf(buf, L"%ls/", pEntry->GetFileTypeString()); break; case GenericEntry::kRecordKindFile: - ::sprintf(buf, "%s", pEntry->GetFileTypeString()); + wsprintf(buf, L"%ls", pEntry->GetFileTypeString()); break; case GenericEntry::kRecordKindForkedFile: - ::sprintf(buf, "%s+", pEntry->GetFileTypeString()); + wsprintf(buf, L"%ls+", pEntry->GetFileTypeString()); break; case GenericEntry::kRecordKindDisk: - ::lstrcpy(buf, "Disk"); + wcscpy(buf, L"Disk"); break; case GenericEntry::kRecordKindUnknown: // usually a GSHK-archived empty data file does this - ::sprintf(buf, "%s-", pEntry->GetFileTypeString()); + wsprintf(buf, L"%ls-", pEntry->GetFileTypeString()); break; default: ASSERT(FALSE); - ::lstrcpy(buf, "!!!"); + wcscpy(buf, L"!!!"); break; } } @@ -437,7 +437,7 @@ ContentList::MakeFileTypeDisplayString(const GenericEntry* pEntry, char* buf) * kFileTypeBufLen. */ /*static*/ void -ContentList::MakeMacTypeString(unsigned long val, char* buf) +ContentList::MakeMacTypeString(unsigned long val, WCHAR* buf) { /* expand longword with ASCII type bytes */ buf[0] = (unsigned char) (val >> 24); @@ -460,7 +460,7 @@ ContentList::MakeMacTypeString(unsigned long val, char* buf) * Use kFileTypeBufLen. */ /*static*/ void -ContentList::MakeAuxTypeDisplayString(const GenericEntry* pEntry, char* buf) +ContentList::MakeAuxTypeDisplayString(const GenericEntry* pEntry, WCHAR* buf) { bool isDir = pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir || @@ -468,16 +468,16 @@ ContentList::MakeAuxTypeDisplayString(const GenericEntry* pEntry, char* buf) if (pEntry->GetSourceFS() == DiskImg::kFormatMacHFS && isDir) { /* HFS directories don't have types; fake it */ - ::lstrcpy(buf, " "); + wcscpy(buf, L" "); } else if (!(pEntry->GetFileType() >= 0 && pEntry->GetFileType() <= 0xff)) { /* oversized type; assume it's HFS */ MakeMacTypeString(pEntry->GetAuxType(), buf); } else { if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) - ::sprintf(buf, "%dk", pEntry->GetUncompressedLen() / 1024); + wsprintf(buf, L"%dk", pEntry->GetUncompressedLen() / 1024); else - ::sprintf(buf, "$%04lX", pEntry->GetAuxType()); + wsprintf(buf, L"$%04lX", pEntry->GetAuxType()); } } @@ -489,7 +489,7 @@ ContentList::MakeAuxTypeDisplayString(const GenericEntry* pEntry, char* buf) * "buf" must be able to hold at least 6 chars plus the NULL. */ void -ContentList::MakeRatioDisplayString(const GenericEntry* pEntry, char* buf, +ContentList::MakeRatioDisplayString(const GenericEntry* pEntry, WCHAR* buf, int* pPerc) { LONGLONG totalLen, totalCompLen; @@ -497,14 +497,14 @@ ContentList::MakeRatioDisplayString(const GenericEntry* pEntry, char* buf, totalCompLen = pEntry->GetCompressedLen(); if ((!totalLen && totalCompLen) || (totalLen && !totalCompLen)) { - ::lstrcpy(buf, "---"); /* weird */ + wcscpy(buf, L"---"); /* weird */ *pPerc = -1; } else if (totalLen < totalCompLen) { - ::lstrcpy(buf, ">100%"); /* compression failed? */ + wcscpy(buf, L">100%"); /* compression failed? */ *pPerc = 101; } else { *pPerc = ComputePercent(totalCompLen, totalLen); - ::sprintf(buf, "%d%%", *pPerc); + wsprintf(buf, L"%d%%", *pPerc); } } @@ -518,13 +518,12 @@ ContentList::MakeRatioDisplayString(const GenericEntry* pEntry, char* buf, void ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) { - //static const char kAccessBits[] = "DNB IWR"; - static const char kAccessBits[] = "dnb iwr"; + static const WCHAR kAccessBits[] = L"dnb iwr"; LV_DISPINFO* plvdi = (LV_DISPINFO*) pnmh; CString str; if (fpArchive->GetReloadFlag()) { - ::lstrcpy(plvdi->item.pszText, ""); + wcscpy(plvdi->item.pszText, L""); *pResult = 0; return; } @@ -537,13 +536,13 @@ ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) switch (plvdi->item.iSubItem) { case 0: // pathname - if ((int)strlen(pEntry->GetDisplayName()) > plvdi->item.cchTextMax) { + if (wcslen(pEntry->GetDisplayName()) > plvdi->item.cchTextMax) { // looks like current limit is 264 chars, which we could hit - ::strncpy(plvdi->item.pszText, pEntry->GetDisplayName(), + wcsncpy(plvdi->item.pszText, pEntry->GetDisplayName(), plvdi->item.cchTextMax); plvdi->item.pszText[plvdi->item.cchTextMax-1] = '\0'; } else { - ::lstrcpy(plvdi->item.pszText, pEntry->GetDisplayName()); + wcscpy(plvdi->item.pszText, pEntry->GetDisplayName()); } /* @@ -553,10 +552,10 @@ ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) * it a little. */ { - unsigned char* str = (unsigned char*) plvdi->item.pszText; + WCHAR* str = plvdi->item.pszText; while (*str != '\0') { - *str = DiskImg::MacToASCII(*str); + *str = DiskImg::MacToASCII((unsigned char) (*str)); str++; } } @@ -576,20 +575,20 @@ ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) break; case 4: // format ASSERT(pEntry->GetFormatStr() != nil); - ::lstrcpy(plvdi->item.pszText, pEntry->GetFormatStr()); + wcscpy(plvdi->item.pszText, pEntry->GetFormatStr()); break; case 5: // size - ::sprintf(plvdi->item.pszText, "%ld", pEntry->GetUncompressedLen()); + wsprintf(plvdi->item.pszText, L"%ld", pEntry->GetUncompressedLen()); break; case 6: // ratio int crud; MakeRatioDisplayString(pEntry, plvdi->item.pszText, &crud); break; case 7: // packed - ::sprintf(plvdi->item.pszText, "%ld", pEntry->GetCompressedLen()); + wsprintf(plvdi->item.pszText, L"%ld", pEntry->GetCompressedLen()); break; case 8: // access - char bitLabels[sizeof(kAccessBits)]; + WCHAR bitLabels[sizeof(kAccessBits)]; int i, j, mask; for (i = 0, j = 0, mask = 0x80; i < 8; i++, mask >>= 1) { @@ -599,7 +598,7 @@ ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) bitLabels[j] = '\0'; ASSERT(j < sizeof(bitLabels)); //::sprintf(plvdi->item.pszText, "0x%02x", pEntry->GetAccess()); - ::lstrcpy(plvdi->item.pszText, bitLabels); + wcscpy(plvdi->item.pszText, bitLabels); break; case 9: // NuRecordIdx [hidden] break; @@ -621,7 +620,7 @@ ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) /* * Helper functions for sort routine. */ -static inline +static inline int CompareUnsignedLong(unsigned long u1, unsigned long u2) { if (u1 < u2) @@ -631,7 +630,7 @@ CompareUnsignedLong(unsigned long u1, unsigned long u2) else return 0; } -static inline +static inline int CompareLONGLONG(LONGLONG u1, LONGLONG u2) { if (u1 < u2) @@ -650,8 +649,8 @@ ContentList::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { const GenericEntry* pEntry1 = (const GenericEntry*) lParam1; const GenericEntry* pEntry2 = (const GenericEntry*) lParam2; - char tmpBuf1[16]; // needs >= 5 for file type compare, and - char tmpBuf2[16]; // >= 7 for ratio string + WCHAR tmpBuf1[16]; // needs >= 5 for file type compare, and + WCHAR tmpBuf2[16]; // >= 7 for ratio string int result; /* for descending order, flip the parameters */ @@ -665,12 +664,12 @@ ContentList::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) switch (lParamSort) { case 0: // pathname - result = ::stricmp(pEntry1->GetDisplayName(), pEntry2->GetDisplayName()); + result = wcsicmp(pEntry1->GetDisplayName(), pEntry2->GetDisplayName()); break; case 1: // file type MakeFileTypeDisplayString(pEntry1, tmpBuf1); MakeFileTypeDisplayString(pEntry2, tmpBuf2); - result = ::stricmp(tmpBuf1, tmpBuf2); + result = wcsicmp(tmpBuf1, tmpBuf2); if (result != 0) break; /* else fall through to case 2 */ @@ -786,30 +785,28 @@ ContentList::GetDefaultWidth(int col) retval = 200; break; case 1: // type (need "$XY" and long HFS types) - //retval = MaxVal(GetStringWidth("XXMMM+"), GetStringWidth("XXType")); - retval = MaxVal(GetStringWidth("XXMMMM+"), GetStringWidth("XXType")); + retval = MaxVal(GetStringWidth(L"XXMMMM+"), GetStringWidth(L"XXType")); break; case 2: // auxtype (hex or long HFS type) - //retval = MaxVal(GetStringWidth("XX$8888"), GetStringWidth("XXAux")); - retval = MaxVal(GetStringWidth("XX$CCCC"), GetStringWidth("XXAux")); + retval = MaxVal(GetStringWidth(L"XX$CCCC"), GetStringWidth(L"XXAux")); break; case 3: // mod date - retval = GetStringWidth("XX88-MMM-88 88:88"); + retval = GetStringWidth(L"XX88-MMM-88 88:88"); break; case 4: // format - retval = GetStringWidth("XXUncompr"); + retval = GetStringWidth(L"XXUncompr"); break; case 5: // uncompressed size - retval = GetStringWidth("XX88888888"); + retval = GetStringWidth(L"XX88888888"); break; case 6: // ratio - retval = MaxVal(GetStringWidth("XXRatio"), GetStringWidth("XX100%")); + retval = MaxVal(GetStringWidth(L"XXRatio"), GetStringWidth(L"XX100%")); break; case 7: // packed - retval = GetStringWidth("XX88888888"); + retval = GetStringWidth(L"XX88888888"); break; case 8: // access - retval = MaxVal(GetStringWidth("XXAccess"), GetStringWidth("XXdnbiwr")); + retval = MaxVal(GetStringWidth(L"XXAccess"), GetStringWidth(L"XXdnbiwr")); break; default: ASSERT(false); @@ -869,7 +866,7 @@ ContentList::OnDoubleClick(NMHDR*, LRESULT* pResult) int idx = HitTest(point); if (idx != -1) { CString str = GetItemText(idx, 0); - WMSG1("%s was double-clicked\n", str); + WMSG1("%ls was double-clicked\n", (LPCWSTR) str); } ((MainWindow*) ::AfxGetMainWnd())->HandleDoubleClick(); @@ -898,8 +895,7 @@ ContentList::OnRightClick(NMHDR*, LRESULT* pResult) int idx = HitTest(point); if (idx != -1) { CString str = GetItemText(idx, 0); - //TRACE1("%s was right-clicked\n", str); - WMSG1("%s was right-clicked\n", str); + WMSG1("%ls was right-clicked\n", (LPCWSTR) str); //fRightClickItem = idx; #else @@ -1016,15 +1012,15 @@ ContentList::SelectSubdirContents(void) * Select every entry whose display name has "displayPrefix" as a prefix. */ void -ContentList::SelectSubdir(const char* displayPrefix) +ContentList::SelectSubdir(const WCHAR* displayPrefix) { - WMSG1(" ContentList selecting all in '%s'\n", displayPrefix); - int len = strlen(displayPrefix); + WMSG1(" ContentList selecting all in '%ls'\n", displayPrefix); + int len = wcslen(displayPrefix); for (int i = GetItemCount()-1; i >= 0; i--) { GenericEntry* pEntry = (GenericEntry*) GetItemData(i); - if (strncasecmp(displayPrefix, pEntry->GetDisplayName(), len) == 0) + if (wcsnicmp(displayPrefix, pEntry->GetDisplayName(), len) == 0) SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); } } @@ -1044,14 +1040,14 @@ ContentList::ClearSelection(void) * If we find a matching entry, we clear the current selection and select it. */ void -ContentList::FindNext(const char* str, bool down, bool matchCase, +ContentList::FindNext(const WCHAR* str, bool down, bool matchCase, bool wholeWord) { POSITION posn; int i, num; bool found = false; - WMSG4("FindNext '%s' d=%d c=%d w=%d\n", str, down, matchCase, wholeWord); + WMSG4("FindNext '%ls' d=%d c=%d w=%d\n", str, down, matchCase, wholeWord); posn = GetFirstSelectedItemPosition(); num = GetNextSelectedItem(/*ref*/ posn); @@ -1108,26 +1104,26 @@ ContentList::FindNext(const char* str, bool down, bool matchCase, * Compare "str" against the contents of entry "num". */ bool -ContentList::CompareFindString(int num, const char* str, bool matchCase, +ContentList::CompareFindString(int num, const WCHAR* str, bool matchCase, bool wholeWord) { GenericEntry* pEntry = (GenericEntry*) GetItemData(num); char fssep = pEntry->GetFssep(); - char* (*pSubCompare)(const char* str, const char* subStr) = nil; + const WCHAR* (*pSubCompare)(const WCHAR* str, const WCHAR* subStr) = nil; if (matchCase) - pSubCompare = strstr; + pSubCompare = wcsstr; else - pSubCompare = stristr; + pSubCompare = Stristr; if (wholeWord) { - const char* src = pEntry->GetDisplayName(); - const char* start = src; - int strLen = strlen(str); + const WCHAR* src = pEntry->GetDisplayName(); + const WCHAR* start = src; + size_t strLen = wcslen(str); /* scan forward, looking for a match that starts & ends on fssep */ while (*start != '\0') { - const char* match; + const WCHAR* match; match = (*pSubCompare)(start, str); diff --git a/app/ContentList.h b/app/ContentList.h index 3b25947..17c9f7e 100644 --- a/app/ContentList.h +++ b/app/ContentList.h @@ -6,8 +6,8 @@ /* * Class declaration for a list control showing archive contents. */ -#ifndef __CONTENT_LIST__ -#define __CONTENT_LIST__ +#ifndef APP_CONTENTLIST_H +#define APP_CONTENTLIST_H #include "GenericArchive.h" #include "Preferences.h" @@ -59,8 +59,8 @@ public: void SelectSubdirContents(void); - void FindNext(const char* str, bool down, bool matchCase, bool wholeWord); - bool CompareFindString(int num, const char* str, bool matchCase, + void FindNext(const WCHAR* str, bool down, bool matchCase, bool wholeWord); + bool CompareFindString(int num, const WCHAR* str, bool matchCase, bool wholeWord); //int GetRightClickItem(void) const { return fRightClickItem; } @@ -68,9 +68,9 @@ public: enum { kFileTypeBufLen = 5, kAuxTypeBufLen = 6 }; static void MakeFileTypeDisplayString(const GenericEntry* pEntry, - char* buf); + WCHAR* buf); static void MakeAuxTypeDisplayString(const GenericEntry* pEntry, - char* buf); + WCHAR* buf); protected: // overridden functions @@ -111,8 +111,8 @@ private: int GetDefaultWidth(int col); - static void MakeMacTypeString(unsigned long val, char* buf); - static void MakeRatioDisplayString(const GenericEntry* pEntry, char* buf, + static void MakeMacTypeString(unsigned long val, WCHAR* buf); + static void MakeRatioDisplayString(const GenericEntry* pEntry, WCHAR* buf, int* pPerc); void SetSortIcon(void); @@ -121,7 +121,7 @@ private: void OnDoubleClick(NMHDR* pnmh, LRESULT* pResult); void OnRightClick(NMHDR* pnmh, LRESULT* pResult); - void SelectSubdir(const char* displayPrefix); + void SelectSubdir(const WCHAR* displayPrefix); CImageList fHdrImageList; CImageList fListImageList; @@ -133,4 +133,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CONTENT_LIST__*/ \ No newline at end of file +#endif /*APP_CONTENTLIST_H*/ diff --git a/app/ConvDiskOptionsDialog.cpp b/app/ConvDiskOptionsDialog.cpp index f5a0001..2b840cd 100644 --- a/app/ConvDiskOptionsDialog.cpp +++ b/app/ConvDiskOptionsDialog.cpp @@ -119,9 +119,10 @@ ConvDiskOptionsDialog::OnRadioChangeRange(UINT nID) * Test a ProDOS filename for validity. */ bool -ConvDiskOptionsDialog::IsValidVolumeName_ProDOS(const char* name) +ConvDiskOptionsDialog::IsValidVolumeName_ProDOS(const WCHAR* name) { - return DiskImgLib::DiskFSProDOS::IsValidVolumeName(name); + CStringA nameA(name); + return DiskImgLib::DiskFSProDOS::IsValidVolumeName(nameA); } @@ -172,7 +173,7 @@ ConvDiskOptionsDialog::LimitSizeControls(long totalBlocks, long blocksUsed) blocksUsed - NewDiskSize::GetNumBitmapBlocks_ProDOS(totalBlocks); long sizeInK = usedWithoutBitmap / 2; CString sizeStr, spaceReq; - sizeStr.Format("%dK", sizeInK); + sizeStr.Format(L"%dK", sizeInK); spaceReq.Format(IDS_CONVDISK_SPACEREQ, sizeStr); pWnd = GetDlgItem(IDC_CONVDISK_SPACEREQ); @@ -242,8 +243,8 @@ ConvDiskOptionsDialog::OnCompute(void) if (selSet.GetNumEntries() == 0) { /* should be impossible */ - MessageBox("No files matched the selection criteria.", - "No match", MB_OK|MB_ICONEXCLAMATION); + MessageBox(L"No files matched the selection criteria.", + L"No match", MB_OK|MB_ICONEXCLAMATION); return; } @@ -253,26 +254,26 @@ ConvDiskOptionsDialog::OnCompute(void) //xferOpts.fUseSparseBlocks = // pPreferences->GetPrefBool(kPrProDOSUseSparse) != 0; - WMSG1("New volume name will be '%s'\n", fVolName); + WMSG1("New volume name will be '%ls'\n", fVolName); /* * Create a new disk image file. */ CString errStr; - char nameBuf[MAX_PATH]; + WCHAR nameBuf[MAX_PATH]; UINT unique; unique = GetTempFileName(pMain->GetPreferences()->GetPrefString(kPrTempPath), - "CPdisk", 0, nameBuf); + L"CPdisk", 0, nameBuf); if (unique == 0) { DWORD dwerr = ::GetLastError(); - errStr.Format("GetTempFileName failed on '%s' (err=0x%08lx)\n", + errStr.Format(L"GetTempFileName failed on '%ls' (err=0x%08lx)\n", pMain->GetPreferences()->GetPrefString(kPrTempPath), dwerr); ShowFailureMsg(this, errStr, IDS_FAILED); return; } - WMSG1(" Will xfer to file '%s'\n", nameBuf); + WMSG1(" Will xfer to file '%ls'\n", nameBuf); // annoying -- DiskArchive insists on creating it - (void) unlink(nameBuf); + (void) _wunlink(nameBuf); DiskArchive::NewOptions options; memset(&options, 0, sizeof(options)); @@ -318,7 +319,7 @@ ConvDiskOptionsDialog::OnCompute(void) dierr = pDiskFS->GetFreeSpaceCount(&totalBlocks, &freeBlocks, &unitSize); if (dierr != kDIErrNone) { - errStr.Format("Unable to get free space count: %s.\n", + errStr.Format(L"Unable to get free space count: %hs.\n", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errStr, IDS_FAILED); } else { @@ -340,5 +341,5 @@ ConvDiskOptionsDialog::OnCompute(void) /* clean up */ delete xferOpts.fTarget; - (void) unlink(nameBuf); + (void) _wunlink(nameBuf); } diff --git a/app/ConvDiskOptionsDialog.h b/app/ConvDiskOptionsDialog.h index 687dbd9..a01bd37 100644 --- a/app/ConvDiskOptionsDialog.h +++ b/app/ConvDiskOptionsDialog.h @@ -6,8 +6,8 @@ /* * Options for converting a disk image to a file archive. */ -#ifndef __CONVDISK_OPTIONS_DIALOG__ -#define __CONVDISK_OPTIONS_DIALOG__ +#ifndef APP_CONVDISKOPTIONSDIALOG_H +#define APP_CONVDISKOPTIONSDIALOG_H #include "UseSelectionDialog.h" #include "resource.h" @@ -22,7 +22,7 @@ public: { fDiskSizeIdx = 0; //fAllowLower = fSparseAlloc = FALSE; - fVolName = "NEW.DISK"; + fVolName = L"NEW.DISK"; fNumBlocks = -1; } virtual ~ConvDiskOptionsDialog(void) {} @@ -45,9 +45,9 @@ private: afx_msg void OnRadioChangeRange(UINT nID); void LimitSizeControls(long totalBlocks, long blocksUsed); - bool IsValidVolumeName_ProDOS(const char* name); + bool IsValidVolumeName_ProDOS(const WCHAR* name); DECLARE_MESSAGE_MAP() }; -#endif /*__CONVDISK_OPTIONS_DIALOG__*/ \ No newline at end of file +#endif /*APP_CONVDISKOPTIONSDIALOG_H*/ diff --git a/app/ConvFileOptionsDialog.h b/app/ConvFileOptionsDialog.h index dceef38..6a0c72e 100644 --- a/app/ConvFileOptionsDialog.h +++ b/app/ConvFileOptionsDialog.h @@ -6,8 +6,8 @@ /* * Options for converting a disk image to a file archive. */ -#ifndef __CONVFILE_OPTIONS_DIALOG__ -#define __CONVFILE_OPTIONS_DIALOG__ +#ifndef APP_CONFFILEOPTIONSDIALOG_H +#define APP_CONFFILEOPTIONSDIALOG_H #include "UseSelectionDialog.h" #include "resource.h" @@ -35,4 +35,4 @@ private: //DECLARE_MESSAGE_MAP() }; -#endif /*__CONVFILE_OPTIONS_DIALOG__*/ \ No newline at end of file +#endif /*APP_CONFFILEOPTIONSDIALOG_H*/ diff --git a/app/CreateImageDialog.cpp b/app/CreateImageDialog.cpp index 8e3ab91..5aa9603 100644 --- a/app/CreateImageDialog.cpp +++ b/app/CreateImageDialog.cpp @@ -90,12 +90,12 @@ CreateImageDialog::DoDataExchange(CDataExchange* pDX) if (fDiskFormatIdx == kFmtDOS32) { CString tmpStr; - tmpStr.Format("%d", fDOSVolumeNum); + tmpStr.Format(L"%d", fDOSVolumeNum); if (!IsValidVolumeName_DOS(tmpStr)) errMsg.LoadString(IDS_VALID_VOLNAME_DOS); } else if (fDiskFormatIdx == kFmtDOS33) { CString tmpStr; - tmpStr.Format("%d", fDOSVolumeNum); + tmpStr.Format(L"%d", fDOSVolumeNum); if (!IsValidVolumeName_DOS(tmpStr)) errMsg.LoadString(IDS_VALID_VOLNAME_DOS); @@ -105,24 +105,24 @@ CreateImageDialog::DoDataExchange(CDataExchange* pDX) (fNumBlocks <= 400 && (fNumBlocks % 8) != 0) || (fNumBlocks > 400 && (fNumBlocks % 16) != 0)) { - errMsg = "Specify a size between 144 blocks (18 tracks) and" - " 800 blocks (50 tracks/32 sectors). The block count" - " must be a multiple of 8 for 16-sector disks, or a" - " multiple of 16 for 32-sector disks. 32 sector" - " formatting starts at 400 blocks. Disks larger than" - " 400 blocks but less than 800 aren't recognized by" - " CiderPress."; + errMsg = L"Specify a size between 144 blocks (18 tracks) and" + L" 800 blocks (50 tracks/32 sectors). The block count" + L" must be a multiple of 8 for 16-sector disks, or a" + L" multiple of 16 for 32-sector disks. 32 sector" + L" formatting starts at 400 blocks. Disks larger than" + L" 400 blocks but less than 800 aren't recognized by" + L" CiderPress."; } } else if (fDiskFormatIdx == kFmtProDOS) { // Max is really 65535, but we allow 65536 for creation of volumes // that can be copied to CFFA cards. if (fNumBlocks < 16 || fNumBlocks > 65536) { - errMsg = "Specify a size of at least 16 blocks and no more" - " than 65536 blocks."; + errMsg = L"Specify a size of at least 16 blocks and no more" + L" than 65536 blocks."; } else if (fVolName_ProDOS.IsEmpty() || fVolName_ProDOS.GetLength() > kProDOSVolNameMax) { - errMsg = "You must specify a volume name 1-15 characters long."; + errMsg = L"You must specify a volume name 1-15 characters long."; } else { if (!IsValidVolumeName_ProDOS(fVolName_ProDOS)) errMsg.LoadString(IDS_VALID_VOLNAME_PRODOS); @@ -131,27 +131,27 @@ CreateImageDialog::DoDataExchange(CDataExchange* pDX) if (fVolName_Pascal.IsEmpty() || fVolName_Pascal.GetLength() > kPascalVolNameMax) { - errMsg = "You must specify a volume name 1-7 characters long."; + errMsg = L"You must specify a volume name 1-7 characters long."; } else { if (!IsValidVolumeName_Pascal(fVolName_Pascal)) errMsg.LoadString(IDS_VALID_VOLNAME_PASCAL); } } else if (fDiskFormatIdx == kFmtHFS) { if (fNumBlocks < 1600 || fNumBlocks > 4194303) { - errMsg = "Specify a size of at least 1600 blocks and no more" - " than 4194303 blocks."; + errMsg = L"Specify a size of at least 1600 blocks and no more" + L" than 4194303 blocks."; } else if (fVolName_HFS.IsEmpty() || fVolName_HFS.GetLength() > kHFSVolNameMax) { - errMsg = "You must specify a volume name 1-27 characters long."; + errMsg = L"You must specify a volume name 1-27 characters long."; } else { if (!IsValidVolumeName_HFS(fVolName_HFS)) errMsg.LoadString(IDS_VALID_VOLNAME_HFS); } } else if (fDiskFormatIdx == kFmtBlank) { if (fNumBlocks < 1 || fNumBlocks > kMaxBlankBlocks) - errMsg = "Specify a size of at least 1 block and no more" - " than 16777216 blocks."; + errMsg = L"Specify a size of at least 1 block and no more" + L" than 16777216 blocks."; } else { ASSERT(false); } @@ -294,36 +294,40 @@ CreateImageDialog::OnSizeChangeRange(UINT nID) * Test a DOS filename for validity. */ bool -CreateImageDialog::IsValidVolumeName_DOS(const char* name) +CreateImageDialog::IsValidVolumeName_DOS(const WCHAR* name) { - return DiskImgLib::DiskFSDOS33::IsValidVolumeName(name); + CStringA nameStr(name); + return DiskImgLib::DiskFSDOS33::IsValidVolumeName(nameStr); } /* * Test a ProDOS filename for validity. */ bool -CreateImageDialog::IsValidVolumeName_ProDOS(const char* name) +CreateImageDialog::IsValidVolumeName_ProDOS(const WCHAR* name) { - return DiskImgLib::DiskFSProDOS::IsValidVolumeName(name); + CStringA nameStr(name); + return DiskImgLib::DiskFSProDOS::IsValidVolumeName(nameStr); } /* * Test a Pascal filename for validity. */ bool -CreateImageDialog::IsValidVolumeName_Pascal(const char* name) +CreateImageDialog::IsValidVolumeName_Pascal(const WCHAR* name) { - return DiskImgLib::DiskFSPascal::IsValidVolumeName(name); + CStringA nameStr(name); + return DiskImgLib::DiskFSPascal::IsValidVolumeName(nameStr); } /* * Test an HFS filename for validity. */ bool -CreateImageDialog::IsValidVolumeName_HFS(const char* name) +CreateImageDialog::IsValidVolumeName_HFS(const WCHAR* name) { - return DiskImgLib::DiskFSHFS::IsValidVolumeName(name); + CStringA nameStr(name); + return DiskImgLib::DiskFSHFS::IsValidVolumeName(nameStr); } diff --git a/app/CreateImageDialog.h b/app/CreateImageDialog.h index 3e6bb56..ea88cd6 100644 --- a/app/CreateImageDialog.h +++ b/app/CreateImageDialog.h @@ -6,8 +6,8 @@ /* * Options for creating a blank disk image. */ -#ifndef __CREATE_IMAGE_DIALOG__ -#define __CREATE_IMAGE_DIALOG__ +#ifndef APP_CREATEIMAGEDIALOG_H +#define APP_CREATEIMAGEDIALOG_H #include "resource.h" @@ -33,9 +33,9 @@ public: fDiskFormatIdx = kFmtProDOS; fAllocTracks_DOS = TRUE; fDOSVolumeNum = 254; - fVolName_ProDOS = "NEW.DISK"; - fVolName_Pascal = "BLANK"; - fVolName_HFS = "New Disk"; + fVolName_ProDOS = L"NEW.DISK"; + fVolName_Pascal = L"BLANK"; + fVolName_HFS = L"New Disk"; fNumBlocks = -2; // -1 has special meaning fExtendedOpts = false; } @@ -62,14 +62,14 @@ private: afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo); afx_msg void OnHelp(void); - bool IsValidVolumeName_DOS(const char* name); - bool IsValidVolumeName_ProDOS(const char* name); - bool IsValidVolumeName_Pascal(const char* name); - bool IsValidVolumeName_HFS(const char* name); + bool IsValidVolumeName_DOS(const WCHAR* name); + bool IsValidVolumeName_ProDOS(const WCHAR* name); + bool IsValidVolumeName_Pascal(const WCHAR* name); + bool IsValidVolumeName_HFS(const WCHAR* name); bool fExtendedOpts; DECLARE_MESSAGE_MAP() }; -#endif /*__CREATE_IMAGE_DIALOG__*/ \ No newline at end of file +#endif /*APP_CREATEIMAGEDIALOG_H*/ diff --git a/app/CreateSubdirDialog.h b/app/CreateSubdirDialog.h index ab12ecc..0e2ba97 100644 --- a/app/CreateSubdirDialog.h +++ b/app/CreateSubdirDialog.h @@ -6,8 +6,8 @@ /* * Create a subdirectory (e.g. on a ProDOS disk image). */ -#ifndef __CREATESUBDIRDIALOG__ -#define __CREATESUBDIRDIALOG__ +#ifndef APP_CREATESUBDIRDIALOG_H +#define APP_CREATESUBDIRDIALOG_H #include "GenericArchive.h" #include "resource.h" @@ -42,4 +42,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__CREATESUBDIRDIALOG__*/ +#endif /*APP_CREATESUBDIRDIALOG_H*/ diff --git a/app/DEFileDialog.cpp b/app/DEFileDialog.cpp index 554bfd6..d013867 100644 --- a/app/DEFileDialog.cpp +++ b/app/DEFileDialog.cpp @@ -51,7 +51,7 @@ DEFileDialog::OnChange(void) CString str; pEdit->GetWindowText(str); - //WMSG2("STR is '%s' (%d)\n", str, str.GetLength()); + //WMSG2("STR is '%ls' (%d)\n", str, str.GetLength()); CWnd* pWnd = GetDlgItem(IDOK); ASSERT(pWnd != nil); diff --git a/app/DEFileDialog.h b/app/DEFileDialog.h index ae43bb9..d94e1dd 100644 --- a/app/DEFileDialog.h +++ b/app/DEFileDialog.h @@ -16,8 +16,8 @@ * state of the sector editor. The read-only state of the underlying FS * doesn't matter, since we're writing sectors, not really editing files. */ -#ifndef __DEFILEDIALOG__ -#define __DEFILEDIALOG__ +#ifndef APP_DEFILEDIALOG_H +#define APP_DEFILEDIALOG_H #include "resource.h" #include "../diskimg/DiskImg.h" @@ -32,7 +32,7 @@ public: DEFileDialog(CWnd* pParentWnd = NULL) : CDialog(IDD_DEFILE, pParentWnd) { fOpenRsrcFork = false; - fName = ""; + fName = L""; } virtual ~DEFileDialog(void) {} @@ -57,4 +57,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__DEFILEDIALOG__*/ \ No newline at end of file +#endif /*APP_DEFILEDIALOG_H*/ diff --git a/app/DiskArchive.cpp b/app/DiskArchive.cpp index 6879116..89a359f 100644 --- a/app/DiskArchive.cpp +++ b/app/DiskArchive.cpp @@ -90,13 +90,13 @@ DiskEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, goto bail; } else if (len < 0) { assert(rsrcFork); // forked files always have a data fork - *pErrMsg = "That fork doesn't exist"; + *pErrMsg = L"That fork doesn't exist"; goto bail; } dierr = fpFile->Open(&pOpenFile, true, rsrcFork); if (dierr != kDIErrNone) { - *pErrMsg = "File open failed"; + *pErrMsg = L"File open failed"; goto bail; } @@ -106,12 +106,12 @@ DiskEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, if (needAlloc) { dataBuf = new char[(int) len]; if (dataBuf == nil) { - pErrMsg->Format("ERROR: allocation of %ld bytes failed", len); + pErrMsg->Format(L"ERROR: allocation of %ld bytes failed", len); goto bail; } } else { if (*pLength < (long) len) { - pErrMsg->Format("ERROR: buf size %ld too short (%ld)", + pErrMsg->Format(L"ERROR: buf size %ld too short (%ld)", *pLength, (long) len); goto bail; } @@ -123,7 +123,7 @@ DiskEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, if (dierr == kDIErrCancelled) { result = IDCANCEL; } else { - pErrMsg->Format("File read failed: %s", + pErrMsg->Format(L"File read failed: %hs", DiskImgLib::DIStrError(dierr)); } goto bail; @@ -175,7 +175,7 @@ DiskEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, rsrcFork = true; else { /* if we handle disk images, make sure we disable "conv" */ - *pErrMsg = "No such fork"; + *pErrMsg = L"No such fork"; goto bail; } @@ -191,21 +191,21 @@ DiskEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, goto bail; } else if (len < 0) { assert(rsrcFork); // forked files always have a data fork - *pErrMsg = "That fork doesn't exist"; + *pErrMsg = L"That fork doesn't exist"; goto bail; } DIError dierr; dierr = fpFile->Open(&pOpenFile, true, rsrcFork); if (dierr != kDIErrNone) { - *pErrMsg = "Unable to open file on disk image"; + *pErrMsg = L"Unable to open file on disk image"; goto bail; } dierr = CopyData(pOpenFile, outfp, conv, convHA, pErrMsg); if (dierr != kDIErrNone) { if (pErrMsg->IsEmpty()) { - pErrMsg->Format("Failed while copying data: %s\n", + pErrMsg->Format(L"Failed while copying data: %hs\n", DiskImgLib::DIStrError(dierr)); } goto bail; @@ -263,7 +263,7 @@ DiskEntry::CopyData(A2FileDescr* pOpenFile, FILE* outfp, ConvertEOL conv, /* read a chunk from the source file */ dierr = pOpenFile->Read(buf, chunkLen); if (dierr != kDIErrNone) { - pMsg->Format("File read failed: %s", + pMsg->Format(L"File read failed: %hs", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -272,7 +272,7 @@ DiskEntry::CopyData(A2FileDescr* pOpenFile, FILE* outfp, ConvertEOL conv, int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, &convHA, &lastCR); if (err != 0) { - pMsg->Format("File write failed: %s", strerror(err)); + pMsg->Format(L"File write failed: %hs", strerror(err)); dierr = kDIErrGeneric; goto bail; } @@ -409,15 +409,15 @@ DiskArchive::AppInit(void) dierr = DiskImgLib::Global::AppInit(); if (dierr != kDIErrNone) { - result.Format("DiskImg DLL failed to initialize: %s\n", + result.Format(L"DiskImg DLL failed to initialize: %hs\n", DiskImgLib::DIStrError(dierr)); goto bail; } DiskImgLib::Global::GetVersion(&major, &minor, &bug); if (major != kDiskImgVersionMajor || minor < kDiskImgVersionMinor) { - result.Format("Older or incompatible version of DiskImg DLL found.\r\r" - "Wanted v%d.%d.x, found %ld.%ld.%ld.", + result.Format(L"Older or incompatible version of DiskImg DLL found.\r\r" + L"Wanted v%d.%d.x, found %ld.%ld.%ld.", kDiskImgVersionMajor, kDiskImgVersionMinor, major, minor, bug); goto bail; @@ -482,7 +482,9 @@ DiskArchive::ProgressCallback(DiskImgLib::A2FileDescr* pFile, * Progress update callback, called from DiskImgLib while scanning a volume * during Open(). * - * Returns "true" if we should continue; + * "str" must not contain a '%'. (TODO: fix that) + * + * Returns "true" if we should continue. */ /*static*/ bool DiskArchive::ScanProgressCallback(void* cookie, const char* str, int count) @@ -493,7 +495,7 @@ DiskArchive::ScanProgressCallback(void* cookie, const char* str, int count) if (count == 0) fmt = str; else - fmt.Format("%s (%%d)", str); + fmt.Format(L"%hs (%%d)", str); cont = SET_PROGRESS_COUNTER_2(fmt, count); if (!cont) { @@ -508,7 +510,7 @@ DiskArchive::ScanProgressCallback(void* cookie, const char* str, int count) * Finish instantiating a DiskArchive object by opening an existing file. */ GenericArchive::OpenResult -DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) +DiskArchive::Open(const WCHAR* filename, bool readOnly, CString* pErrMsg) { DIError dierr; CString errMsg; @@ -552,7 +554,7 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) result = kResultFileArchive; else { result = kResultFailure; - errMsg.Format("Unable to open '%s': %s.", filename, + errMsg.Format(L"Unable to open '%ls': %hs.", filename, DiskImgLib::DIStrError(dierr)); } goto bail; @@ -562,7 +564,7 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) dierr = fDiskImg.AnalyzeImage(); if (dierr != kDIErrNone) { result = kResultFailure; - errMsg.Format("Analysis of '%s' failed: %s", filename, + errMsg.Format(L"Analysis of '%ls' failed: %hs", filename, DiskImgLib::DIStrError(dierr)); goto bail; } @@ -590,8 +592,8 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) imf.fFSFormat, imf.fSectorOrder); if (dierr != kDIErrNone) { result = kResultFailure; - errMsg.Format("Unable to access disk image using selected" - " parameters. Error: %s.", + errMsg.Format(L"Unable to access disk image using selected" + L" parameters. Error: %hs.", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -602,7 +604,7 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) fDiskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { result = kResultFailure; - errMsg.Format("Unable to identify filesystem on '%s'", filename); + errMsg.Format(L"Unable to identify filesystem on '%ls'", filename); goto bail; } @@ -612,7 +614,7 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) /* unknown FS should've been caught above! */ ASSERT(false); result = kResultFailure; - errMsg.Format("Format of '%s' not recognized.", filename); + errMsg.Format(L"Format of '%ls' not recognized.", filename); goto bail; } @@ -633,8 +635,8 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) ProgressCounterDialog* pProgress; pProgress = new ProgressCounterDialog; - pProgress->Create(_T("Examining contents, please wait..."), pMain); - pProgress->SetCounterFormat("Scanning..."); + pProgress->Create(L"Examining contents, please wait...", pMain); + pProgress->SetCounterFormat(L"Scanning..."); pProgress->CenterWindow(); //pMain->PeekAndPump(); // redraw CWaitCursor waitc; @@ -654,7 +656,7 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) result = kResultCancel; } else { result = kResultFailure; - errMsg.Format("Error reading list of files from disk: %s", + errMsg.Format(L"Error reading list of files from disk: %hs", DiskImgLib::DIStrError(dierr)); } goto bail; @@ -663,7 +665,7 @@ DiskArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) if (LoadContents() != 0) { result = kResultFailure; - errMsg.Format("Failed while loading contents of disk image."); + errMsg = L"Failed while loading contents of disk image."; goto bail; } @@ -727,11 +729,12 @@ bail: * Returns an error string on failure, or "" on success. */ CString -DiskArchive::New(const char* fileName, const void* vOptions) +DiskArchive::New(const WCHAR* fileName, const void* vOptions) { const Preferences* pPreferences = GET_PREFERENCES(); NewOptions* pOptions = (NewOptions*) vOptions; CString volName; + CStringA volNameA, fileNameA; long numBlocks = -1; long numTracks = -1; int numSectors; @@ -767,15 +770,15 @@ DiskArchive::New(const char* fileName, const void* vOptions) if (numTracks < DiskFSDOS33::kMinTracks || numTracks > DiskFSDOS33::kMaxTracks) { - retmsg.Format("Invalid DOS32 track count"); + retmsg = L"Invalid DOS32 track count"; goto bail; } if (numSectors != 13) { - retmsg.Format("Invalid DOS32 sector count"); + retmsg = L"Invalid DOS32 sector count"; goto bail; } if (pOptions->dos.allocDOSTracks) - volName = "DOS"; + volName = L"DOS"; break; case DiskImg::kFormatDOS33: numTracks = pOptions->dos.numTracks; @@ -784,23 +787,23 @@ DiskArchive::New(const char* fileName, const void* vOptions) if (numTracks < DiskFSDOS33::kMinTracks || numTracks > DiskFSDOS33::kMaxTracks) { - retmsg.Format("Invalid DOS33 track count"); + retmsg = L"Invalid DOS33 track count"; goto bail; } if (numSectors != 16 && numSectors != 32) { // no 13-sector (yet) - retmsg.Format("Invalid DOS33 sector count"); + retmsg = L"Invalid DOS33 sector count"; goto bail; } if (pOptions->dos.allocDOSTracks) - volName = "DOS"; + volName = L"DOS"; break; default: - retmsg.Format("Unsupported disk format"); + retmsg = L"Unsupported disk format"; goto bail; } - WMSG4("DiskArchive: new '%s' %ld %s in '%s'\n", - (const char*)volName, numBlocks, + WMSG4("DiskArchive: new '%ls' %ld %hs in '%ls'\n", + (LPCWSTR) volName, numBlocks, DiskImg::ToString(pOptions->base.format), fileName); bool canSkipFormat; @@ -819,8 +822,9 @@ DiskArchive::New(const char* fileName, const void* vOptions) * want to do it under Win2K/XP because it can be slow for larger * volumes. */ + fileNameA = fileName; if (numBlocks > 0) { - dierr = fDiskImg.CreateImage(fileName, nil, + dierr = fDiskImg.CreateImage(fileNameA, nil, DiskImg::kOuterFormatNone, DiskImg::kFileFormatUnadorned, DiskImg::kPhysicalFormatSectors, @@ -831,7 +835,7 @@ DiskArchive::New(const char* fileName, const void* vOptions) canSkipFormat); } else { ASSERT(numTracks > 0); - dierr = fDiskImg.CreateImage(fileName, nil, + dierr = fDiskImg.CreateImage(fileNameA, nil, DiskImg::kOuterFormatNone, DiskImg::kFileFormatUnadorned, DiskImg::kPhysicalFormatSectors, @@ -842,7 +846,7 @@ DiskArchive::New(const char* fileName, const void* vOptions) canSkipFormat); } if (dierr != kDIErrNone) { - retmsg.Format("Unable to create disk image: %s.", + retmsg.Format(L"Unable to create disk image: %hs.", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -867,29 +871,30 @@ DiskArchive::New(const char* fileName, const void* vOptions) volName.MakeUpper(); /* format it */ - dierr = fDiskImg.FormatImage(pOptions->base.format, volName); + volNameA = volName; + dierr = fDiskImg.FormatImage(pOptions->base.format, volNameA); if (dierr != kDIErrNone) { - retmsg.Format("Unable to format disk image: %s.", + retmsg.Format(L"Unable to format disk image: %hs.", DiskImgLib::DIStrError(dierr)); goto bail; } fpPrimaryDiskFS = fDiskImg.OpenAppropriateDiskFS(false); if (fpPrimaryDiskFS == nil) { - retmsg.Format("Unable to create DiskFS."); + retmsg = L"Unable to create DiskFS."; goto bail; } /* prep it */ dierr = fpPrimaryDiskFS->Initialize(&fDiskImg, DiskFS::kInitFull); if (dierr != kDIErrNone) { - retmsg.Format("Error reading list of files from disk: %s", + retmsg.Format(L"Error reading list of files from disk: %hs", DiskImgLib::DIStrError(dierr)); goto bail; } /* this is pretty meaningless, but do it to ensure we're initialized */ if (LoadContents() != 0) { - retmsg.Format("Failed while loading contents of disk image."); + retmsg = L"Failed while loading contents of disk image."; goto bail; } @@ -921,15 +926,15 @@ DiskArchive::Close(void) MainWindow* pMainWin = (MainWindow*)::AfxGetMainWnd(); CString msg, failed; - msg.Format("Failed while closing disk image: %s.", + msg.Format(L"Failed while closing disk image: %hs.", DiskImgLib::DIStrError(dierr)); failed.LoadString(IDS_FAILED); - WMSG1("During close: %s\n", (const char*) msg); + WMSG1("During close: %ls\n", msg); pMainWin->MessageBox(msg, failed, MB_OK); } - return ""; + return L""; } /* @@ -953,12 +958,12 @@ DiskArchive::Flush(void) if (dierr != kDIErrNone) { CString errMsg; - errMsg.Format("Attempt to flush the current archive failed: %s.", + errMsg.Format(L"Attempt to flush the current archive failed: %hs.", DiskImgLib::DIStrError(dierr)); return errMsg; } - return ""; + return L""; } /* @@ -982,8 +987,9 @@ DiskArchive::GetDescription(CString* pStr) const if (fpPrimaryDiskFS == nil) return; - if (fpPrimaryDiskFS->GetVolumeID() != nil) - pStr->Format("Disk Image - %s", fpPrimaryDiskFS->GetVolumeID()); + if (fpPrimaryDiskFS->GetVolumeID() != nil) { + pStr->Format(L"Disk Image - %hs", fpPrimaryDiskFS->GetVolumeID()); + } } @@ -1008,7 +1014,7 @@ DiskArchive::LoadContents(void) pMain->PeekAndPump(); // redraw CWaitCursor waitc; - result = LoadDiskFSContents(fpPrimaryDiskFS, ""); + result = LoadDiskFSContents(fpPrimaryDiskFS, L""); SET_PROGRESS_COUNTER(-1); @@ -1076,9 +1082,9 @@ DiskArchive::InternalReload(CWnd* pMsgWnd) * sub-volume as it should appear in the list. */ int -DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName) +DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const WCHAR* volName) { - static const char* kBlankFileName = ""; + static const WCHAR* kBlankFileName = L""; A2File* pFile; DiskEntry* pNewEntry; DiskFS::SubVolume* pSubVol; @@ -1088,7 +1094,7 @@ DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName) wantCoerceDOSFilenames = pPreferences->GetPrefBool(kPrCoerceDOSFilenames); - WMSG2("Notes for disk image '%s':\n%s", + WMSG2("Notes for disk image '%ls':\n%hs", volName, pDiskFS->GetDiskImg()->GetNotes()); ASSERT(pDiskFS != nil); @@ -1160,30 +1166,30 @@ DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName) case DiskImg::kFormatDOS32: case DiskImg::kFormatUNIDOS: case DiskImg::kFormatGutenberg: - pNewEntry->SetFormatStr("DOS"); + pNewEntry->SetFormatStr(L"DOS"); break; case DiskImg::kFormatProDOS: - pNewEntry->SetFormatStr("ProDOS"); + pNewEntry->SetFormatStr(L"ProDOS"); break; case DiskImg::kFormatPascal: - pNewEntry->SetFormatStr("Pascal"); + pNewEntry->SetFormatStr(L"Pascal"); break; case DiskImg::kFormatCPM: - pNewEntry->SetFormatStr("CP/M"); + pNewEntry->SetFormatStr(L"CP/M"); break; case DiskImg::kFormatMSDOS: - pNewEntry->SetFormatStr("MS-DOS"); + pNewEntry->SetFormatStr(L"MS-DOS"); break; case DiskImg::kFormatRDOS33: case DiskImg::kFormatRDOS32: case DiskImg::kFormatRDOS3: - pNewEntry->SetFormatStr("RDOS"); + pNewEntry->SetFormatStr(L"RDOS"); break; case DiskImg::kFormatMacHFS: - pNewEntry->SetFormatStr("HFS"); + pNewEntry->SetFormatStr(L"HFS"); break; default: - pNewEntry->SetFormatStr("???"); + pNewEntry->SetFormatStr(L"???"); break; } @@ -1220,9 +1226,9 @@ DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName) subVolName = "+++"; // call it *something* if (volName[0] == '\0') - concatSubVolName.Format("_%s", subVolName); + concatSubVolName.Format(L"_%hs", subVolName); else - concatSubVolName.Format("%s_%s", volName, subVolName); + concatSubVolName.Format(L"%ls_%hs", volName, subVolName); ret = LoadDiskFSContents(pSubVol->GetDiskFS(), concatSubVolName); if (ret != 0) return ret; @@ -1309,11 +1315,11 @@ DiskArchive::BulkAdd(ActionProgressDialog* pActionProgress, { NuError nerr; CString errMsg; - char curDir[MAX_PATH] = ""; + WCHAR curDir[MAX_PATH] = L""; bool retVal = false; - WMSG2("Opts: '%s' typePres=%d\n", - pAddOpts->fStoragePrefix, pAddOpts->fTypePreservation); + WMSG2("Opts: '%ls' typePres=%d\n", (LPCWSTR) pAddOpts->fStoragePrefix, + pAddOpts->fTypePreservation); WMSG3(" sub=%d strip=%d ovwr=%d\n", pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, pAddOpts->fOverwriteExisting); @@ -1331,44 +1337,44 @@ DiskArchive::BulkAdd(ActionProgressDialog* pActionProgress, /* * Save the current directory and change to the one from the file dialog. */ - const char* buf = pAddOpts->GetFileNames(); - WMSG2("Selected path = '%s' (offset=%d)\n", buf, + const WCHAR* buf = pAddOpts->GetFileNames(); + WMSG2("Selected path = '%ls' (offset=%d)\n", buf, pAddOpts->GetFileNameOffset()); - if (GetCurrentDirectory(sizeof(curDir), curDir) == 0) { - errMsg = "Unable to get current directory.\n"; + if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { + errMsg = L"Unable to get current directory.\n"; ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); goto bail; } if (SetCurrentDirectory(buf) == false) { - errMsg.Format("Unable to set current directory to '%s'.\n", buf); + errMsg.Format(L"Unable to set current directory to '%ls'.\n", buf); ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); goto bail; } buf += pAddOpts->GetFileNameOffset(); while (*buf != '\0') { - WMSG1(" file '%s'\n", buf); + WMSG1(" file '%ls'\n", buf); /* add the file, calling DoAddFile via the generic AddFile */ nerr = AddFile(pAddOpts, buf, &errMsg); if (nerr != kNuErrNone) { if (errMsg.IsEmpty()) - errMsg.Format("Failed while adding file '%s': %s.", - (LPCTSTR) buf, NuStrError(nerr)); + errMsg.Format(L"Failed while adding file '%ls': %hs.", + (LPCWSTR) buf, NuStrError(nerr)); if (nerr != kNuErrAborted) { ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); } goto bail; } - buf += strlen(buf)+1; + buf += wcslen(buf)+1; } if (fpAddDataHead == nil) { CString title; title.LoadString(IDS_MB_APP_NAME); - errMsg = "No files added.\n"; + errMsg = L"No files added.\n"; pActionProgress->MessageBox(errMsg, title, MB_OK | MB_ICONWARNING); } else { /* add all pending files */ @@ -1390,7 +1396,7 @@ DiskArchive::BulkAdd(ActionProgressDialog* pActionProgress, bail: FreeAddDataList(); if (SetCurrentDirectory(curDir) == false) { - errMsg.Format("Unable to reset current directory to '%s'.\n", buf); + errMsg.Format(L"Unable to reset current directory to '%ls'.\n", buf); ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); // bummer, but don't signal failure } @@ -1439,10 +1445,10 @@ DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts, DIError dierr; int neededLen = 64; // reasonable guess - char* fsNormalBuf = nil; + char* fsNormalBuf = nil; // name as it will appear on disk image - WMSG2(" +++ ADD file: orig='%s' stor='%s'\n", - pDetails->origName, pDetails->storageName); + WMSG2(" +++ ADD file: orig='%ls' stor='%ls'\n", + (LPCWSTR) pDetails->origName, (LPCWSTR) pDetails->storageName); retry: /* @@ -1450,13 +1456,14 @@ retry: */ delete[] fsNormalBuf; fsNormalBuf = new char[neededLen]; - dierr = pDiskFS->NormalizePath(pDetails->storageName, + CStringA storageNameA(pDetails->storageName); + dierr = pDiskFS->NormalizePath(storageNameA, PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen); if (dierr == kDIErrDataOverrun) { /* not long enough, try again *once* */ delete[] fsNormalBuf; fsNormalBuf = new char[neededLen]; - dierr = pDiskFS->NormalizePath(pDetails->storageName, + dierr = pDiskFS->NormalizePath(storageNameA, PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen); } if (dierr != kDIErrNone) { @@ -1491,7 +1498,7 @@ retry: goto retry; } else if (result == kNuOverwrite) { /* delete the existing file immediately */ - WMSG1(" Deleting existing file '%s'\n", fsNormalBuf); + WMSG1(" Deleting existing file '%hs'\n", fsNormalBuf); dierr = pDiskFS->DeleteFile(pExisting); if (dierr != kDIErrNone) { // Would be nice to show a dialog and explain *why*, but @@ -1518,7 +1525,7 @@ retry: goto bail; } - WMSG1("FSNormalized is '%s'\n", pAddData->GetFSNormalPath()); + WMSG1("FSNormalized is '%hs'\n", pAddData->GetFSNormalPath()); AddToAddDataList(pAddData); @@ -1583,7 +1590,7 @@ DiskArchive::HandleReplaceExisting(const A2File* pExisting, * allow the FS normalizer to force the filename to be valid. */ pDetails->storageName = confOvwr.fExistingFile; - WMSG1("Trying rename to '%s'\n", pDetails->storageName); + WMSG1("Trying rename to '%ls'\n", (LPCWSTR) pDetails->storageName); return kNuRename; } @@ -1649,7 +1656,7 @@ DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL) const FileDetails* pDataDetails = nil; const FileDetails* pRsrcDetails = nil; const FileDetails* pDetails = pData->GetDetails(); - const char* typeStr = "????"; + const char* typeStr = "????"; // for debug msg only switch (pDetails->entryKind) { case FileDetails::kFileKindDataFork: @@ -1668,7 +1675,7 @@ DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL) case FileDetails::kFileKindDirectory: default: assert(false); - return "internal error"; + return L"internal error"; } if (pData->GetOtherFork() != nil) { @@ -1686,17 +1693,17 @@ DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL) break; case FileDetails::kFileKindDiskImage: assert(false); - return "(internal) add other disk error"; + return L"(internal) add other disk error"; case FileDetails::kFileKindBothForks: case FileDetails::kFileKindDirectory: default: assert(false); - return "internal error"; + return L"internal error"; } } - WMSG2("Adding file '%s' (%s)\n", - pDetails->storageName, typeStr); + WMSG2("Adding file '%ls' (%hs)\n", + (LPCWSTR) pDetails->storageName, typeStr); ASSERT(pDataDetails != nil || pRsrcDetails != nil); /* @@ -1712,6 +1719,7 @@ DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL) else parms.storageType = kNuStorageSeedling; /* use the FS-normalized path here */ + /* (do we have to? do we want to?) */ parms.pathName = pData->GetFSNormalPath(); dataLen = rsrcLen = -1; @@ -1749,15 +1757,15 @@ DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL) /* really ought to do this separately for each thread */ SET_PROGRESS_BEGIN(); - SET_PROGRESS_UPDATE2(0, pDetails->origName, - parms.pathName); + CString pathNameW(parms.pathName); + SET_PROGRESS_UPDATE2(0, pDetails->origName, pathNameW); DIError dierr; dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen, rsrcBuf, rsrcLen); SET_PROGRESS_END(); if (dierr != kDIErrNone) { - errMsg.Format("Unable to add '%s' to image: %s.", + errMsg.Format(L"Unable to add '%hs' to image: %hs.", parms.pathName, DiskImgLib::DIStrError(dierr)); goto bail; } @@ -1790,7 +1798,7 @@ bail: * really large files. */ CString -DiskArchive::LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, +DiskArchive::LoadFile(const WCHAR* pathName, BYTE** pBuf, long* pLen, GenericEntry::ConvertEOL conv, GenericEntry::ConvertHighASCII convHA) const { CString errMsg; @@ -1806,21 +1814,21 @@ DiskArchive::LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, ASSERT(pBuf != nil); ASSERT(pLen != nil); - fp = fopen(pathName, "rb"); + fp = _wfopen(pathName, L"rb"); if (fp == nil) { - errMsg.Format("Unable to open '%s': %s.", pathName, + errMsg.Format(L"Unable to open '%ls': %hs.", pathName, strerror(errno)); goto bail; } if (fseek(fp, 0, SEEK_END) != 0) { - errMsg.Format("Unable to seek to end of '%s': %s", pathName, + errMsg.Format(L"Unable to seek to end of '%ls': %hs", pathName, strerror(errno)); goto bail; } fileLen = ftell(fp); if (fileLen < 0) { - errMsg.Format("Unable to determine length of '%s': %s", pathName, + errMsg.Format(L"Unable to determine length of '%ls': %hs", pathName, strerror(errno)); goto bail; } @@ -1831,13 +1839,13 @@ DiskArchive::LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, *pLen = 0; goto bail; } else if (fileLen > 0x00ffffff) { - errMsg = "Cannot add files larger than 16MB to a disk image."; + errMsg = L"Cannot add files larger than 16MB to a disk image."; goto bail; } - *pBuf = new unsigned char[fileLen]; + *pBuf = new BYTE[fileLen]; if (*pBuf == nil) { - errMsg.Format("Unable to allocate %ld bytes for '%s'.", + errMsg.Format(L"Unable to allocate %ld bytes for '%ls'.", fileLen, pathName); goto bail; } @@ -1860,7 +1868,7 @@ DiskArchive::LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, chunkLen = fileLen; if (fread(*pBuf, chunkLen, 1, fp) != 1) { - errMsg.Format("Unable to read initial chunk of '%s': %s.", + errMsg.Format(L"Unable to read initial chunk of '%ls': %hs.", pathName, strerror(errno)); delete[] *pBuf; *pBuf = nil; @@ -1890,9 +1898,9 @@ DiskArchive::LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, */ if (conv == GenericEntry::kConvertEOLOff) { /* fast path */ - WMSG1(" +++ NOT converting text '%s'\n", pathName); + WMSG1(" +++ NOT converting text '%ls'\n", pathName); if (fread(*pBuf, fileLen, 1, fp) != 1) { - errMsg.Format("Unable to read '%s': %s.", pathName, strerror(errno)); + errMsg.Format(L"Unable to read '%ls': %hs.", pathName, strerror(errno)); delete[] *pBuf; *pBuf = nil; goto bail; @@ -1918,7 +1926,7 @@ DiskArchive::LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, else mask = 0x00; - WMSG2(" +++ Converting text '%s', mask=0x%02x\n", pathName, mask); + WMSG2(" +++ Converting text '%ls', mask=0x%02x\n", pathName, mask); while (count--) { ic = getc(fp); @@ -1963,7 +1971,6 @@ DiskArchive::AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms, const unsigned char* rsrcBuf, long rsrcLen) const { DIError dierr = kDIErrNone; - CString replacementFileName; const int kFileTypeBIN = 0x06; const int kFileTypeINT = 0xfa; const int kFileTypeBAS = 0xfc; @@ -2002,7 +2009,7 @@ DiskArchive::AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms, if (strcmp(cp+1, kEmptyFolderMarker) == 0 && dataLen == 0) { /* drop the junk on the end */ parmCopy.storageType = kNuStorageDirectory; - replacementFileName = parmCopy.pathName; + CStringA replacementFileName(parmCopy.pathName);; replacementFileName = replacementFileName.Left(cp - parmCopy.pathName -1); parmCopy.pathName = replacementFileName; @@ -2041,7 +2048,7 @@ DiskArchive::AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms, if (dataLen < 0) { /* this was a resource-fork-only file */ - WMSG1("--- nothing left to write for '%s'\n", + WMSG1("--- nothing left to write for '%hs'\n", parmCopy.pathName); goto bail; } @@ -2075,7 +2082,7 @@ DiskArchive::AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms, */ dierr = pDiskFS->CreateFile(&parmCopy, &pNewFile); if (dierr != kDIErrNone) { - WMSG1(" CreateFile failed: %s\n", DiskImgLib::DIStrError(dierr)); + WMSG1(" CreateFile failed: %hs\n", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -2143,7 +2150,7 @@ bail: * erase any subdirectories that were created to contain this file. * Not worth worrying about. */ - WMSG1(" Deleting newly-created file '%s'\n", parmCopy.pathName); + WMSG1(" Deleting newly-created file '%hs'\n", parmCopy.pathName); (void) pDiskFS->DeleteFile(pNewFile); } return dierr; @@ -2159,7 +2166,8 @@ void DiskArchive::ConvertFDToCP(const FileDetails* pDetails, DiskFS::CreateParms* pCreateParms) { - pCreateParms->pathName = pDetails->storageName; + // TODO(xyzzy): need to store 8-bit form + pCreateParms->pathName = "XYZZY-DiskArchive"; // pDetails->storageName; pCreateParms->fssep = (char) pDetails->fileSysInfo; pCreateParms->storageType = pDetails->storageType; pCreateParms->fileType = pDetails->fileType; @@ -2173,7 +2181,7 @@ DiskArchive::ConvertFDToCP(const FileDetails* pDetails, /* * Add an entry to the end of the FileAddData list. * - * If "storeName" (the Windows filename with type goodies stripped, but + * If "storageName" (the Windows filename with type goodies stripped, but * without filesystem normalization) matches an entry already in the list, * we check to see if these are forks of the same file. If they are * different forks and we don't already have both forks, we put the @@ -2199,7 +2207,7 @@ DiskArchive::AddToAddDataList(FileAddData* pData) dataKind = pData->GetDetails()->entryKind; while (pSearch != nil) { if (pSearch->GetOtherFork() == nil && - strcmp(pSearch->GetDetails()->storageName, + wcscmp(pSearch->GetDetails()->storageName, pData->GetDetails()->storageName) == 0) { //NuThreadID dataID = pData->GetDetails()->threadID; @@ -2213,7 +2221,7 @@ DiskArchive::AddToAddDataList(FileAddData* pData) (listKind == FileDetails::kFileKindDataFork || listKind == FileDetails::kFileKindRsrcFork)) { /* looks good, hook it in here instead of the list */ - WMSG2("--- connecting forks of '%s' and '%s'\n", + WMSG2("--- connecting forks of '%ls' and '%ls'\n", pData->GetDetails()->origName, pSearch->GetDetails()->origName); pSearch->SetOtherFork(pData); @@ -2265,9 +2273,9 @@ DiskArchive::FreeAddDataList(void) */ bool DiskArchive::CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const char* newName) + const WCHAR* newName) { - ASSERT(newName != nil && strlen(newName) > 0); + ASSERT(newName != nil && wcslen(newName) > 0); DiskEntry* pEntry = (DiskEntry*) pParentEntry; ASSERT(pEntry != nil); A2File* pFile = pEntry->GetA2File(); @@ -2283,7 +2291,7 @@ DiskArchive::CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, DIError dierr; A2File* pNewFile = nil; DiskFS::CreateParms parms; - CString pathName; + CStringA pathName; time_t now = time(nil); /* @@ -2296,7 +2304,7 @@ DiskArchive::CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, pathName += pParentEntry->GetFssep(); pathName += newName; } - ASSERT(strchr(newName, pParentEntry->GetFssep()) == nil); + ASSERT(wcschr(newName, pParentEntry->GetFssep()) == nil); /* using NufxLib constants; they match with ProDOS */ memset(&parms, 0, sizeof(parms)); @@ -2312,7 +2320,7 @@ DiskArchive::CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, dierr = pDiskFS->CreateFile(&parms, &pNewFile); if (dierr != kDIErrNone) { CString errMsg; - errMsg.Format("Unable to create subdirectory: %s.\n", + errMsg.Format(L"Unable to create subdirectory: %hs.\n", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); return false; @@ -2340,7 +2348,7 @@ DiskArchive::CompareDisplayNamesDesc(const void* ventry1, const void* ventry2) const DiskEntry* pEntry1 = *((const DiskEntry**) ventry1); const DiskEntry* pEntry2 = *((const DiskEntry**) ventry2); - return strcasecmp(pEntry2->GetDisplayName(), pEntry1->GetDisplayName()); + return wcsicmp(pEntry2->GetDisplayName(), pEntry1->GetDisplayName()); } /* @@ -2383,8 +2391,8 @@ DiskArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) ASSERT(pEntry != nil); entryArray[idx++] = pEntry; - WMSG2("Added 0x%08lx '%s'\n", (long) entryArray[idx-1], - entryArray[idx-1]->GetDisplayName()); + WMSG2("Added 0x%08lx '%ls'\n", (long) entryArray[idx-1], + (LPCWSTR) entryArray[idx-1]->GetDisplayName()); pSelEntry = pSelSet->IterNext(); } @@ -2411,14 +2419,15 @@ DiskArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) * support write access to the filesystem). */ if (!pFile->GetDiskFS()->GetReadWriteSupported()) { - errMsg.Format("Unable to delete '%s' on '%s': operation not supported.", - pEntry->GetDisplayName(), pFile->GetDiskFS()->GetVolumeName()); + errMsg.Format(L"Unable to delete '%ls' on '%hs': operation not supported.", + (LPCWSTR) pEntry->GetDisplayName(), + (LPCSTR) pFile->GetDiskFS()->GetVolumeName()); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } - WMSG2(" Deleting '%s' from '%s'\n", pEntry->GetPathName(), - pFile->GetDiskFS()->GetVolumeName()); + WMSG2(" Deleting '%ls' from '%hs'\n", (LPCWSTR) pEntry->GetPathName(), + (LPCSTR) pFile->GetDiskFS()->GetVolumeName()); SET_PROGRESS_UPDATE2(0, pEntry->GetPathName(), nil); /* @@ -2427,8 +2436,9 @@ DiskArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) */ dierr = pFile->GetDiskFS()->DeleteFile(pFile); if (dierr != kDIErrNone) { - errMsg.Format("Unable to delete '%s' on '%s': %s.", - pEntry->GetDisplayName(), pFile->GetDiskFS()->GetVolumeName(), + errMsg.Format(L"Unable to delete '%ls' on '%hs': %hs.", + (LPCWSTR) pEntry->GetDisplayName(), + (LPCSTR) pFile->GetDiskFS()->GetVolumeName(), DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -2497,7 +2507,7 @@ DiskArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) RenameEntryDialog renameDlg(pMsgWnd); DiskEntry* pEntry = (DiskEntry*) pSelEntry->GetEntry(); - WMSG1(" Renaming '%s'\n", pEntry->GetPathName()); + WMSG1(" Renaming '%ls'\n", pEntry->GetPathName()); if (!SetRenameFields(pMsgWnd, pEntry, &renameDlg)) break; @@ -2513,22 +2523,23 @@ DiskArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) pFile = pEntry->GetA2File(); pDiskFS = pFile->GetDiskFS(); - dierr = pDiskFS->RenameFile(pFile, renameDlg.fNewName); + CStringA newNameA(renameDlg.fNewName); + dierr = pDiskFS->RenameFile(pFile, newNameA); if (dierr != kDIErrNone) { - errMsg.Format("Unable to rename '%s' to '%s': %s.", + errMsg.Format(L"Unable to rename '%ls' to '%ls': %hs.", pEntry->GetPathName(), renameDlg.fNewName, DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } - WMSG2("Rename of '%s' to '%s' succeeded\n", + WMSG2("Rename of '%ls' to '%ls' succeeded\n", pEntry->GetDisplayName(), renameDlg.fNewName); } else if (result == IDCANCEL) { WMSG0("Canceling out of remaining renames\n"); break; } else { /* 3rd possibility is IDIGNORE, i.e. skip this entry */ - WMSG1("Skipping rename of '%s'\n", pEntry->GetDisplayName()); + WMSG1("Skipping rename of '%ls'\n", pEntry->GetDisplayName()); } pSelEntry = pSelSet->IterNext(); @@ -2572,14 +2583,14 @@ DiskArchive::SetRenameFields(CWnd* pMsgWnd, DiskEntry* pEntry, */ if (!pDiskFS->GetReadWriteSupported()) { CString errMsg; - errMsg.Format("Unable to rename '%s': operation not supported.", + errMsg.Format(L"Unable to rename '%ls': operation not supported.", pEntry->GetPathName()); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); return false; } if (pDiskFS->GetFSDamaged()) { CString errMsg; - errMsg.Format("Unable to rename '%s': the disk it's on appears to be damaged.", + errMsg.Format(L"Unable to rename '%ls': the disk it's on appears to be damaged.", pEntry->GetPathName()); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); return false; @@ -2608,7 +2619,7 @@ DiskArchive::TestPathName(const GenericEntry* pGenericEntry, { const DiskEntry* pEntry = (DiskEntry*) pGenericEntry; DiskImg::FSFormat format; - CString pathName, errMsg; + CStringA pathName, errMsg, newNameA; DiskFS* pDiskFS; if (basePath.IsEmpty()) { @@ -2624,32 +2635,34 @@ DiskArchive::TestPathName(const GenericEntry* pGenericEntry, /* look for an existing file, but don't compare against self */ A2File* existingFile; - existingFile = pDiskFS->GetFileByName(pathName); + CStringA pathNameA(pathName); + existingFile = pDiskFS->GetFileByName(pathNameA); if (existingFile != nil && existingFile != pEntry->GetA2File()) { errMsg = "A file with that name already exists."; goto bail; } + newNameA = newName; switch (format) { case DiskImg::kFormatProDOS: - if (!DiskFSProDOS::IsValidFileName(newName)) + if (!DiskFSProDOS::IsValidFileName(newNameA)) errMsg.LoadString(IDS_VALID_FILENAME_PRODOS); break; case DiskImg::kFormatDOS33: case DiskImg::kFormatDOS32: - if (!DiskFSDOS33::IsValidFileName(newName)) + if (!DiskFSDOS33::IsValidFileName(newNameA)) errMsg.LoadString(IDS_VALID_FILENAME_DOS); break; case DiskImg::kFormatPascal: - if (!DiskFSPascal::IsValidFileName(newName)) + if (!DiskFSPascal::IsValidFileName(newNameA)) errMsg.LoadString(IDS_VALID_FILENAME_PASCAL); break; case DiskImg::kFormatMacHFS: - if (!DiskFSHFS::IsValidFileName(newName)) + if (!DiskFSHFS::IsValidFileName(newNameA)) errMsg.LoadString(IDS_VALID_FILENAME_HFS); break; default: - errMsg = "Not supported by TestPathName!"; + errMsg = L"Not supported by TestPathName!"; } bail: @@ -2670,15 +2683,16 @@ bail: */ bool DiskArchive::RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const char* newName) + const WCHAR* newName) { DIError dierr; CString errMsg; bool retVal = true; - dierr = pDiskFS->RenameVolume(newName); + CStringA newNameA(newName); + dierr = pDiskFS->RenameVolume(newNameA); if (dierr != kDIErrNone) { - errMsg.Format("Unable to rename volume: %s.\n", + errMsg.Format(L"Unable to rename volume: %hs.\n", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); retVal = false; @@ -2697,7 +2711,7 @@ DiskArchive::RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, */ CString DiskArchive::TestVolumeName(const DiskFS* pDiskFS, - const char* newName) const + const WCHAR* newName) const { DiskImg::FSFormat format; CString errMsg; @@ -2707,26 +2721,27 @@ DiskArchive::TestVolumeName(const DiskFS* pDiskFS, format = pDiskFS->GetDiskImg()->GetFSFormat(); + CStringA newNameA(newName); switch (format) { case DiskImg::kFormatProDOS: - if (!DiskFSProDOS::IsValidVolumeName(newName)) + if (!DiskFSProDOS::IsValidVolumeName(newNameA)) errMsg.LoadString(IDS_VALID_VOLNAME_PRODOS); break; case DiskImg::kFormatDOS33: case DiskImg::kFormatDOS32: - if (!DiskFSDOS33::IsValidVolumeName(newName)) + if (!DiskFSDOS33::IsValidVolumeName(newNameA)) errMsg.LoadString(IDS_VALID_VOLNAME_DOS); break; case DiskImg::kFormatPascal: - if (!DiskFSPascal::IsValidVolumeName(newName)) + if (!DiskFSPascal::IsValidVolumeName(newNameA)) errMsg.LoadString(IDS_VALID_VOLNAME_PASCAL); break; case DiskImg::kFormatMacHFS: - if (!DiskFSHFS::IsValidVolumeName(newName)) + if (!DiskFSHFS::IsValidVolumeName(newNameA)) errMsg.LoadString(IDS_VALID_VOLNAME_HFS); break; default: - errMsg = "Not supported by TestVolumeName!"; + errMsg = L"Not supported by TestVolumeName!"; } return errMsg; @@ -2759,7 +2774,7 @@ DiskArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pGenericEntry, pProps->auxType, pProps->access); if (dierr != kDIErrNone) { CString errMsg; - errMsg.Format("Unable to set file info: %s.\n", + errMsg.Format(L"Unable to set file info: %hs.\n", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); return false; @@ -2828,7 +2843,7 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, ASSERT(rsrcBuf == nil); if (pEntry->GetDamaged()) { - WMSG1(" XFER skipping damaged entry '%s'\n", + WMSG1(" XFER skipping damaged entry '%ls'\n", pEntry->GetDisplayName()); continue; } @@ -2852,8 +2867,8 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) { /* this is the volume dir */ - WMSG1(" XFER not transferring volume dir '%s'\n", - fixedPathName); + WMSG1(" XFER not transferring volume dir '%ls'\n", + (LPCWSTR) fixedPathName); continue; } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { if (pXferOpts->fPreserveEmptyFolders) { @@ -2862,7 +2877,7 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, cmpStr += (char)PathProposal::kDefaultStoredFssep; if (pSelSet->CountMatchingPrefix(cmpStr) == 0) { - WMSG1("FOUND empty dir '%s'\n", fixedPathName); + WMSG1("FOUND empty dir '%ls'\n", (LPCWSTR) fixedPathName); cmpStr += kEmptyFolderMarker; dataBuf = new unsigned char[1]; dataLen = 0; @@ -2873,17 +2888,17 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, pEntry->GetAccess() | GenericEntry::kAccessInvisible; goto have_stuff2; } else { - WMSG1("NOT empty dir '%s'\n", fixedPathName); + WMSG1("NOT empty dir '%ls'\n", (LPCWSTR) fixedPathName); } } - WMSG1(" XFER not transferring directory '%s'\n", - fixedPathName); + WMSG1(" XFER not transferring directory '%ls'\n", + (LPCWSTR) fixedPathName); continue; } - WMSG3(" Xfer '%s' (data=%d rsrc=%d)\n", - fixedPathName, pEntry->GetHasDataFork(), + WMSG3(" Xfer '%ls' (data=%d rsrc=%d)\n", + (LPCWSTR) fixedPathName, pEntry->GetHasDataFork(), pEntry->GetHasRsrcFork()); dataBuf = nil; @@ -2894,7 +2909,7 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, WMSG0("Cancelled during data extract!\n"); goto bail; /* abort anything that was pending */ } else if (result != IDOK) { - errMsg.Format("Failed while extracting '%s': %s.", + errMsg.Format(L"Failed while extracting '%ls': %ls.", fixedPathName, extractErrMsg); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -2911,7 +2926,7 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, long len; unsigned char* ucp; - WMSG1(" Converting DOS text in '%s'\n", fixedPathName); + WMSG1(" Converting DOS text in '%ls'\n", fixedPathName); for (ucp = dataBuf, len = dataLen; len > 0; len--, ucp++) *ucp = *ucp & 0x7f; } @@ -2922,7 +2937,7 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, pEntry->GetFSFormat() == DiskImg::kFormatPascal && pEntry->GetFileType() == kFileTypePTX) { - WMSG1("WOULD CONVERT ptx '%s'\n", fixedPathName); + WMSG1("WOULD CONVERT ptx '%ls'\n", fixedPathName); } #endif @@ -2935,7 +2950,7 @@ DiskArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, WMSG0("Cancelled during rsrc extract!\n"); goto bail; /* abort anything that was pending */ } else if (result != IDOK) { - errMsg.Format("Failed while extracting '%s': %s.", + errMsg.Format(L"Failed while extracting '%ls': %ls.", fixedPathName, extractErrMsg); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -2985,8 +3000,8 @@ have_stuff2: &rsrcBuf, rsrcLen); if (!errMsg.IsEmpty()) { WMSG0("XferFile failed!\n"); - errMsg.Format("Failed while transferring '%s': %s.", - pEntry->GetDisplayName(), (const char*) errMsg); + errMsg.Format(L"Failed while transferring '%ls': %ls.", + (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -3047,8 +3062,8 @@ DiskArchive::XferPrepare(const XferFileOptions* pXferOpts) * version just tucks the pointers into NufxLib structures.) */ CString -DiskArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, - long dataLen, unsigned char** pRsrcBuf, long rsrcLen) +DiskArchive::XferFile(FileDetails* pDetails, BYTE** pDataBuf, + long dataLen, BYTE** pRsrcBuf, long rsrcLen) { //const int kFileTypeTXT = 0x04; DiskFS::CreateParms createParms; @@ -3056,7 +3071,7 @@ DiskArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, CString errMsg; DIError dierr = kDIErrNone; - WMSG3(" XFER: transfer '%s' (dataLen=%ld rsrcLen=%ld)\n", + WMSG3(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)\n", pDetails->storageName, dataLen, rsrcLen); ASSERT(pDataBuf != nil); @@ -3089,12 +3104,12 @@ DiskArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, long len = dataLen; if (srcIsDOS && !dstIsDOS) { - WMSG1(" Stripping high ASCII from '%s'\n", pDetails->storageName); + WMSG1(" Stripping high ASCII from '%ls'\n", pDetails->storageName); while (len--) *ucp++ &= 0x7f; } else if (!srcIsDOS && dstIsDOS) { - WMSG1(" Adding high ASCII to '%s'\n", pDetails->storageName); + WMSG1(" Adding high ASCII to '%ls'\n", pDetails->storageName); while (len--) { if (*ucp != '\0') @@ -3102,10 +3117,10 @@ DiskArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, ucp++; } } else if (srcIsDOS && dstIsDOS) { - WMSG1(" --- not altering DOS-to-DOS text '%s'\n", + WMSG1(" --- not altering DOS-to-DOS text '%ls'\n", pDetails->storageName); } else { - WMSG1(" --- non-DOS transfer '%s'\n", pDetails->storageName); + WMSG1(" --- non-DOS transfer '%ls'\n", pDetails->storageName); } } @@ -3120,7 +3135,7 @@ DiskArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, dierr = AddForksToDisk(pDiskFS, &createParms, *pDataBuf, dataLen, *pRsrcBuf, rsrcLen); if (dierr != kDIErrNone) { - errMsg.Format("%s", DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"%hs", DiskImgLib::DIStrError(dierr)); goto bail; } diff --git a/app/DiskArchive.h b/app/DiskArchive.h index f3456c7..d3f5580 100644 --- a/app/DiskArchive.h +++ b/app/DiskArchive.h @@ -6,8 +6,8 @@ /* * Disk image "archive" support. */ -#ifndef __DISK_ARCHIVE__ -#define __DISK_ARCHIVE__ +#ifndef APP_DISKARCHIVE_H +#define APP_DISKARCHIVE_H #include "GenericArchive.h" #include "../diskimg/DiskImg.h" @@ -74,17 +74,17 @@ public: } blank; struct { NewOptionsBase base; - const char* volName; + const WCHAR* volName; long numBlocks; } prodos; struct { NewOptionsBase base; - const char* volName; + const WCHAR* volName; long numBlocks; } pascalfs; // "pascal" is reserved token in MSVC++ struct { NewOptionsBase base; - const char* volName; + const WCHAR* volName; long numBlocks; } hfs; struct { @@ -101,8 +101,8 @@ public: // one-time cleanup at app shutdown time static void AppCleanup(void); - virtual OpenResult Open(const char* filename, bool readOnly, CString* pErrMsg); - virtual CString New(const char* filename, const void* options); + virtual OpenResult Open(const WCHAR* filename, bool readOnly, CString* pErrMsg); + virtual CString New(const WCHAR* filename, const void* options); virtual CString Flush(void); virtual CString Reload(void); virtual bool IsReadOnly(void) const { return fIsReadOnly; }; @@ -114,7 +114,7 @@ public: const AddFilesDialog* pAddOpts) { ASSERT(false); return false; } virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const char* newName); + const WCHAR* newName); virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) { ASSERT(false); return false; } virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet); @@ -122,9 +122,9 @@ public: virtual CString TestPathName(const GenericEntry* pGenericEntry, const CString& basePath, const CString& newName, char newFssep) const; virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const char* newName); + const WCHAR* newName); virtual CString TestVolumeName(const DiskFS* pDiskFS, - const char* newName) const; + const WCHAR* newName) const; virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, const RecompressOptionsDialog* pRecompOpts) { ASSERT(false); return false; } @@ -153,12 +153,12 @@ public: private: virtual CString Close(void); virtual void XferPrepare(const XferFileOptions* pXferOpts); - virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf, - long dataLen, unsigned char** pRsrcBuf, long rsrcLen); + virtual CString XferFile(FileDetails* pDetails, BYTE** pDataBuf, + long dataLen, BYTE** pRsrcBuf, long rsrcLen); virtual void XferAbort(CWnd* pMsgWnd); virtual void XferFinish(CWnd* pMsgWnd); - /* internal function, used during initial scan of volume */ + /* DiskImg callback, used during initial scan of volume */ static bool ScanProgressCallback(void* cookie, const char* str, int count); @@ -168,7 +168,7 @@ private: */ class FileAddData { public: - FileAddData(const FileDetails* pDetails, const char* fsNormalPath) { + FileAddData(const FileDetails* pDetails, char* fsNormalPath) { fDetails = *pDetails; fFSNormalPath = fsNormalPath; @@ -183,15 +183,24 @@ private: void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; } const FileDetails* GetDetails(void) const { return &fDetails; } + + /* + * Get the "FS-normal" path, i.e. exactly what we want to appear + * on the disk image. This has the result of any conversions, so + * we need to store it as a narrow string. + */ const char* GetFSNormalPath(void) const { return fFSNormalPath; } private: - // Three filenames stored here: + // Three filenames stored inside FileDetails: // fDetails.origName -- the name of the Windows file - // fDetails.storageName -- the normalized Windows name - // fFSNormalPath -- the FS-normalized version of "storageName" + // fDetails.storageName -- origName with type-preservation goodies + // stripped out + // fFSNormalPath -- the FS-normalized version of "storageName", i.e. + // the name as it will appear on the Apple II disk image + FileDetails fDetails; - CString fFSNormalPath; + CStringA fFSNormalPath; FileAddData* fpOtherFork; FileAddData* fpNext; @@ -205,7 +214,7 @@ private: static int CompareDisplayNamesDesc(const void* ventry1, const void* ventry2); int LoadContents(void); - int LoadDiskFSContents(DiskFS* pDiskFS, const char* volName); + int LoadDiskFSContents(DiskFS* pDiskFS, const WCHAR* volName); void DowncaseSubstring(CString* pStr, int startPos, int endPos, bool prevWasSpace); static void DebugMsgHandler(const char* file, int line, const char* msg); @@ -213,11 +222,11 @@ private: NuResult HandleReplaceExisting(const A2File* pExisting, FileDetails* pDetails); CString ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL); - CString LoadFile(const char* pathName, unsigned char** pBuf, long* pLen, + CString LoadFile(const WCHAR* pathName, BYTE** pBuf, long* pLen, GenericEntry::ConvertEOL conv, GenericEntry::ConvertHighASCII convHA) const; DIError AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms, - const unsigned char* dataBuf, long dataLen, - const unsigned char* rsrcBuf, long rsrcLen) const; + const BYTE* dataBuf, long dataLen, + const BYTE* rsrcBuf, long rsrcLen) const; void AddToAddDataList(FileAddData* pData); void FreeAddDataList(void); void ConvertFDToCP(const FileDetails* pDetails, @@ -241,4 +250,4 @@ private: DiskFS* fpXferTargetFS; }; -#endif /*__DISK_ARCHIVE__*/ \ No newline at end of file +#endif /*APP_DISKARCHIVE_H*/ diff --git a/app/DiskConvertDialog.cpp b/app/DiskConvertDialog.cpp index f289897..94ce8a3 100644 --- a/app/DiskConvertDialog.cpp +++ b/app/DiskConvertDialog.cpp @@ -141,7 +141,7 @@ DiskConvertDialog::Init(int fileCount) fAllowNuFX = fAllowTrackStar = fAllowSim2eHDV = fAllowDDD = true; fConvertIdx = kConvDOSRaw; // default choice == first in list fBulkFileCount = fileCount; - fDiskDescription.Format("%d images selected", fBulkFileCount); + fDiskDescription.Format(L"%d images selected", fBulkFileCount); } @@ -238,28 +238,29 @@ DiskConvertDialog::DoDataExchange(CDataExchange* pDX) if (pDX->m_bSaveAndValidate) { switch (fConvertIdx) { - case kConvDOSRaw: fExtension = "do"; break; - case kConvDOS2MG: fExtension = "2mg"; break; - case kConvProDOSRaw: fExtension = "po"; break; - case kConvProDOS2MG: fExtension = "2mg"; break; - case kConvNibbleRaw: fExtension = "nib"; break; - case kConvNibble2MG: fExtension = "2mg"; break; - case kConvD13: fExtension = "d13"; break; - case kConvDiskCopy42: fExtension = "dsk"; break; - case kConvNuFX: fExtension = "sdk"; break; - case kConvTrackStar: fExtension = "app"; break; - case kConvSim2eHDV: fExtension = "hdv"; break; - case kConvDDD: fExtension = "ddd"; break; + case kConvDOSRaw: fExtension = L"do"; break; + case kConvDOS2MG: fExtension = L"2mg"; break; + case kConvProDOSRaw: fExtension = L"po"; break; + case kConvProDOS2MG: fExtension = L"2mg"; break; + case kConvNibbleRaw: fExtension = L"nib"; break; + case kConvNibble2MG: fExtension = L"2mg"; break; + case kConvD13: fExtension = L"d13"; break; + case kConvDiskCopy42: fExtension = L"dsk"; break; + case kConvNuFX: fExtension = L"sdk"; break; + case kConvTrackStar: fExtension = L"app"; break; + case kConvSim2eHDV: fExtension = L"hdv"; break; + case kConvDDD: fExtension = L"ddd"; break; default: - fExtension = "???"; + fExtension = L"???"; ASSERT(false); break; } - if (fAddGzip && fConvertIdx != kConvNuFX) - fExtension += ".gz"; + if (fAddGzip && fConvertIdx != kConvNuFX) { + fExtension += L".gz"; + } - WMSG1(" DCD recommending extension '%s'\n", (LPCTSTR) fExtension); + WMSG1(" DCD recommending extension '%ls'\n", (LPCWSTR) fExtension); } } diff --git a/app/DiskConvertDialog.h b/app/DiskConvertDialog.h index 5b526ae..6b81059 100644 --- a/app/DiskConvertDialog.h +++ b/app/DiskConvertDialog.h @@ -6,8 +6,8 @@ /* * Let the user choose how they want to convert a disk image. */ -#ifndef __DISKCONVERTDIALOG__ -#define __DISKCONVERTDIALOG__ +#ifndef APP_DISKCONVERTDIALOG_H +#define APP_DISKCONVERTDIALOG_H #include "resource.h" #include "../diskimg/DiskImg.h" @@ -82,4 +82,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__DISKCONVERTDIALOG__*/ \ No newline at end of file +#endif /*APP_DISKCONVERTDIALOG_H*/ diff --git a/app/DiskEditDialog.cpp b/app/DiskEditDialog.cpp index 2dbc3f5..471aeb5 100644 --- a/app/DiskEditDialog.cpp +++ b/app/DiskEditDialog.cpp @@ -83,7 +83,7 @@ DiskEditDialog::OnInitDialog(void) CHARFORMAT cf; cf.cbSize = sizeof(CHARFORMAT); cf.dwMask = CFM_FACE | CFM_SIZE; - ::lstrcpy(cf.szFaceName, "Courier New"); + wcscpy(cf.szFaceName, L"Courier New"); cf.yHeight = 10 * 20; // point size in twips BOOL cc = pEdit->SetDefaultCharFormat(cf); if (cc == FALSE) { @@ -188,16 +188,17 @@ DiskEditDialog::InitNibbleParmList(void) } for (i = 0; i < count; i++) { - if (pTable[i].numSectors > 0) - pCombo->AddString(pTable[i].description); - else { + if (pTable[i].numSectors > 0) { + CString description(pTable[i].description); + pCombo->AddString(description); + } else { /* only expecting this on the last, "custom" entry */ ASSERT(i == count-1); } } pCombo->SetCurSel(dflt); } else { - pCombo->AddString("Nibble Parms"); + pCombo->AddString(L"Nibble Parms"); pCombo->SetCurSel(0); pCombo->EnableWindow(FALSE); } @@ -345,7 +346,8 @@ DiskEditDialog::OnSubVolume(void) pEditDialog = &blockEdit; else pEditDialog = §orEdit; - pEditDialog->Setup(pSubVol->GetDiskFS(), fpDiskFS->GetVolumeID()); + CString volumeID(fpDiskFS->GetVolumeID()); + pEditDialog->Setup(pSubVol->GetDiskFS(), volumeID); pEditDialog->SetPositionShift(8); (void) pEditDialog->DoModal(); } @@ -378,9 +380,9 @@ DiskEditDialog::SetSpinMode(int id, int base) } if (base == 10) - valStr.Format("%d", val); + valStr.Format(L"%d", val); else - valStr.Format("%X", val); + valStr.Format(L"%X", val); pSpin->SetBase(base); pSpin->GetBuddy()->SetWindowText(valStr); @@ -407,7 +409,7 @@ DiskEditDialog::ReadSpinner(int id, long* pVal) err.LoadString(IDS_ERROR); int lower, upper; pSpin->GetRange32(lower, upper); - msg.Format("Please enter a value between %d and %d (0x%x and 0x%x).", + msg.Format(L"Please enter a value between %d and %d (0x%x and 0x%x).", lower, upper, lower, upper); MessageBox(msg, err, MB_OK|MB_ICONEXCLAMATION); return -1; @@ -438,10 +440,10 @@ DiskEditDialog::SetSpinner(int id, long val) * Convert a chunk of data into a hex dump, and stuff it into the edit control. */ void -DiskEditDialog::DisplayData(const unsigned char* srcBuf, int size) +DiskEditDialog::DisplayData(const BYTE* srcBuf, int size) { - char textBuf[80 * 16 * 2]; - char* cp; + WCHAR textBuf[80 * 16 * 2]; + WCHAR* cp; int i, j; ASSERT(srcBuf != nil); @@ -459,8 +461,8 @@ DiskEditDialog::DisplayData(const unsigned char* srcBuf, int size) if (indent < 0) indent = 0; - CString msg = " " - " "; + CString msg = L" " + L" "; ASSERT(msg.GetLength() == kWidth); msg = msg.Left(indent); msg += fAlertMsg; @@ -468,7 +470,7 @@ DiskEditDialog::DisplayData(const unsigned char* srcBuf, int size) textBuf[i] = '\r'; textBuf[i+1] = '\n'; } - strcpy(&textBuf[i], msg); + wcscpy(&textBuf[i], msg); pEdit->SetWindowText(textBuf); return; @@ -481,8 +483,8 @@ DiskEditDialog::DisplayData(const unsigned char* srcBuf, int size) for (i = 0; i < size/16; i++) { if (size == kSectorSize) { /* two-nybble addr */ - sprintf(cp, " %02x: %02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x ", + wsprintf(cp, L" %02x: %02x %02x %02x %02x %02x %02x %02x %02x " + L"%02x %02x %02x %02x %02x %02x %02x %02x ", i * 16, srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], @@ -490,15 +492,15 @@ DiskEditDialog::DisplayData(const unsigned char* srcBuf, int size) srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); } else { /* three-nybble addr */ - sprintf(cp, "%03x: %02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x ", + wsprintf(cp, L"%03x: %02x %02x %02x %02x %02x %02x %02x %02x " + L"%02x %02x %02x %02x %02x %02x %02x %02x ", i * 16, srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); } - ASSERT(strlen(cp) == 54); + ASSERT(wcslen(cp) == 54); cp += 54; // strlen(cp) for (j = 0; j < 16; j++) *cp++ = PrintableChar(srcBuf[j]); @@ -526,8 +528,8 @@ DiskEditDialog::DisplayNibbleData(const unsigned char* srcBuf, int size) ASSERT(fAlertMsg.IsEmpty()); int bufSize = ((size+15) / 16) * 80; - char* textBuf = new char[bufSize]; - char* cp; + WCHAR* textBuf = new WCHAR[bufSize]; + WCHAR* cp; int i; if (textBuf == nil) @@ -536,20 +538,20 @@ DiskEditDialog::DisplayNibbleData(const unsigned char* srcBuf, int size) cp = textBuf; for (i = 0; size > 0; i++) { if (size >= 16) { - sprintf(cp, "%04x: %02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x", + wsprintf(cp, L"%04x: %02x %02x %02x %02x %02x %02x %02x %02x " + L"%02x %02x %02x %02x %02x %02x %02x %02x", i * 16, srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); - ASSERT(strlen(cp) == 53); + ASSERT(wcslen(cp) == 53); cp += 53; // strlen(cp) } else { - sprintf(cp, "%04x:", i * 16); + wsprintf(cp, L"%04x:", i * 16); cp += 5; for (int j = 0; j < size; j++) { - sprintf(cp, " %02x", srcBuf[j]); + wsprintf(cp, L" %02x", srcBuf[j]); cp += 3; } } @@ -602,14 +604,15 @@ DiskEditDialog::DisplayNibbleData(const unsigned char* srcBuf, int size) * its Close function. */ DIError -DiskEditDialog::OpenFile(const char* fileName, bool openRsrc, A2File** ppFile, +DiskEditDialog::OpenFile(const WCHAR* fileName, bool openRsrc, A2File** ppFile, A2FileDescr** ppOpenFile) { A2File* pFile; A2FileDescr* pOpenFile = nil; - WMSG2(" OpenFile '%s' rsrc=%d\n", fileName, openRsrc); - pFile = fpDiskFS->GetFileByName(fileName); + WMSG2(" OpenFile '%ls' rsrc=%d\n", fileName, openRsrc); + CStringA fileNameA(fileName); + pFile = fpDiskFS->GetFileByName(fileNameA); if (pFile == nil) { CString msg, failed; @@ -716,11 +719,11 @@ SectorEditDialog::OnInitDialog(void) */ CString trackStr; CWnd* pWnd; - trackStr.Format("Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); + trackStr.Format(L"Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); pWnd = GetDlgItem(IDC_STEXT_TRACK); ASSERT(pWnd != nil); pWnd->SetWindowText(trackStr); - trackStr.Format("Sector (%d):", fpDiskFS->GetDiskImg()->GetNumSectPerTrack()); + trackStr.Format(L"Sector (%d):", fpDiskFS->GetDiskImg()->GetNumSectPerTrack()); pWnd = GetDlgItem(IDC_STEXT_SECTOR); ASSERT(pWnd != nil); pWnd->SetWindowText(trackStr); @@ -767,7 +770,7 @@ SectorEditDialog::LoadData(void) DIError dierr; dierr = fpDiskFS->GetDiskImg()->ReadTrackSector(fTrack, fSector, fSectorData); if (dierr != kDIErrNone) { - WMSG1("SED sector read failed: %s\n", DiskImgLib::DIStrError(dierr)); + WMSG1("SED sector read failed: %hs\n", DiskImgLib::DIStrError(dierr)); //CString msg; //CString err; //err.LoadString(IDS_ERROR); @@ -797,7 +800,7 @@ SectorEditDialog::OnDoRead(void) void SectorEditDialog::OnDoWrite(void) { - MessageBox("Write!"); + MessageBox(L"Write!"); } /* @@ -922,9 +925,9 @@ SectorFileEditDialog::OnInitDialog(void) CString title; CString rsrcIndic; rsrcIndic.LoadString(IDS_INDIC_RSRC); - title.Format("Disk Viewer - %s%s (%ld bytes)", - fpFile->GetPathName(), // use fpFile version to get case - fOpenRsrcFork ? (LPCTSTR)rsrcIndic : "", fLength); + title.Format(L"Disk Viewer - %hs%ls (%ld bytes)", + (LPCSTR) fpFile->GetPathName(), // use fpFile version to get case + fOpenRsrcFork ? (LPCWSTR)rsrcIndic : L"", fLength); SetWindowText(title); return retval; @@ -1091,7 +1094,7 @@ BlockEditDialog::OnInitDialog(void) CString blockStr; //blockStr.LoadString(IDS_BLOCK); - blockStr.Format("Block (%d):", fpDiskFS->GetDiskImg()->GetNumBlocks()); + blockStr.Format(L"Block (%d):", fpDiskFS->GetDiskImg()->GetNumBlocks()); pWnd = GetDlgItem(IDC_STEXT_TRACK); ASSERT(pWnd != nil); pWnd->SetWindowText(blockStr); @@ -1189,7 +1192,7 @@ BlockEditDialog::LoadData(void) DIError dierr; dierr = fpDiskFS->GetDiskImg()->ReadBlock(fBlock, fBlockData); if (dierr != kDIErrNone) { - WMSG1("BED block read failed: %s\n", DiskImgLib::DIStrError(dierr)); + WMSG1("BED block read failed: %hs\n", DiskImgLib::DIStrError(dierr)); //CString msg; //CString err; //err.LoadString(IDS_ERROR); @@ -1219,7 +1222,7 @@ BlockEditDialog::OnDoRead(void) void BlockEditDialog::OnDoWrite(void) { - MessageBox("Write!"); + MessageBox(L"Write!"); } /* @@ -1320,9 +1323,9 @@ BlockFileEditDialog::OnInitDialog(void) CString title; CString rsrcIndic; rsrcIndic.LoadString(IDS_INDIC_RSRC); - title.Format("Disk Viewer - %s%s (%ld bytes)", - fpFile->GetPathName(), // use fpFile version to get case - fOpenRsrcFork ? (LPCTSTR)rsrcIndic : "", fLength); + title.Format(L"Disk Viewer - %hs%ls (%ld bytes)", + (LPCSTR) fpFile->GetPathName(), // use fpFile version to get case + fOpenRsrcFork ? (LPCWSTR)rsrcIndic : L"", fLength); SetWindowText(title); return retval; @@ -1484,7 +1487,7 @@ NibbleEditDialog::OnInitDialog(void) pWnd->DestroyWindow(); CString trackStr; - trackStr.Format("Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); + trackStr.Format(L"Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); pWnd = GetDlgItem(IDC_STEXT_TRACK); ASSERT(pWnd != nil); pWnd->SetWindowText(trackStr); @@ -1583,7 +1586,7 @@ NibbleEditDialog::LoadData(void) dierr = fpDiskFS->GetDiskImg()->ReadNibbleTrack(fTrack, fNibbleData, &fNibbleDataLen); if (dierr != kDIErrNone) { - WMSG1("NED track read failed: %s\n", DiskImgLib::DIStrError(dierr)); + WMSG1("NED track read failed: %hs\n", DiskImgLib::DIStrError(dierr)); fAlertMsg.LoadString(IDS_DISKEDITMSG_BADTRACK); } @@ -1607,7 +1610,7 @@ NibbleEditDialog::OnDoRead(void) void NibbleEditDialog::OnDoWrite(void) { - MessageBox("Write!"); + MessageBox(L"Write!"); } /* diff --git a/app/DiskEditDialog.h b/app/DiskEditDialog.h index 2ef12ad..3b47369 100644 --- a/app/DiskEditDialog.h +++ b/app/DiskEditDialog.h @@ -6,8 +6,8 @@ /* * Class definition for DiskEdit dialog. */ -#ifndef __DISK_EDIT_DIALOG__ -#define __DISK_EDIT_DIALOG__ +#ifndef APP_DISKEDITDIALOG_H +#define APP_DISKEDITDIALOG_H #include "../diskimg/DiskImg.h" #include "../util/UtilLib.h" @@ -37,7 +37,7 @@ public: } virtual ~DiskEditDialog() {} - void Setup(DiskFS* pDiskFS, const char* fileName) { + void Setup(DiskFS* pDiskFS, const WCHAR* fileName) { ASSERT(pDiskFS != nil); ASSERT(fileName != nil); fpDiskFS = pDiskFS; @@ -49,15 +49,15 @@ public: virtual int LoadData(void) = 0; virtual void DisplayData(void) = 0; - virtual void DisplayData(const unsigned char* buf, int size); - virtual void DisplayNibbleData(const unsigned char* srcBuf, int size); + virtual void DisplayData(const BYTE* buf, int size); + virtual void DisplayNibbleData(const BYTE* srcBuf, int size); bool GetReadOnly(void) const { return fReadOnly; } void SetReadOnly(bool val) { fReadOnly = val; } int GetPositionShift(void) const { return fPositionShift; } void SetPositionShift(int val) { fPositionShift = val; } DiskFS* GetDiskFS(void) const { return fpDiskFS; } - const char* GetFileName(void) const { return fFileName; } + const WCHAR* GetFileName(void) const { return fFileName; } protected: // return a low-ASCII character so we can read high-ASCII files @@ -94,7 +94,7 @@ protected: void SetSpinner(int id, long val); //void FillWithPattern(unsigned char* buf, int size, const char* pattern); - DIError OpenFile(const char* fileName, bool openRsrc, A2File** ppFile, + DIError OpenFile(const WCHAR* fileName, bool openRsrc, A2File** ppFile, A2FileDescr** ppOpenFile); DiskFS* fpDiskFS; @@ -149,7 +149,7 @@ protected: long fTrack; long fSector; - unsigned char fSectorData[kSectorSize]; + BYTE fSectorData[kSectorSize]; }; /* @@ -167,7 +167,7 @@ public: virtual ~SectorFileEditDialog() {} /* we do NOT own pOpenFile, and should not delete it */ - void SetupFile(const char* fileName, bool rsrcFork, A2File* pFile, + void SetupFile(const WCHAR* fileName, bool rsrcFork, A2File* pFile, A2FileDescr* pOpenFile) { fOpenFileName = fileName; @@ -229,7 +229,7 @@ protected: afx_msg virtual void OnOpenFile(void); long fBlock; - unsigned char fBlockData[kBlockSize]; + BYTE fBlockData[kBlockSize]; }; @@ -248,7 +248,7 @@ public: virtual ~BlockFileEditDialog() {} /* we do NOT own pOpenFile, and should not delete it */ - void SetupFile(const char* fileName, bool rsrcFork, A2File* pFile, + void SetupFile(const WCHAR* fileName, bool rsrcFork, A2File* pFile, A2FileDescr* pOpenFile) { fOpenFileName = fileName; @@ -308,8 +308,8 @@ protected: afx_msg virtual void OnNibbleParms(void) { ASSERT(false); } long fTrack; - unsigned char fNibbleData[DiskImgLib::kTrackAllocSize]; + BYTE fNibbleData[DiskImgLib::kTrackAllocSize]; long fNibbleDataLen; }; -#endif /*__DISK_EDIT_DIALOG__*/ \ No newline at end of file +#endif /*APP_DISKEDITDIALOG_H*/ diff --git a/app/DiskEditOpenDialog.h b/app/DiskEditOpenDialog.h index 76dc21a..3d86bc4 100644 --- a/app/DiskEditOpenDialog.h +++ b/app/DiskEditOpenDialog.h @@ -6,8 +6,8 @@ /* * Decide how to open the disk editor. */ -#ifndef __DISKEDITOPENDIALOG__ -#define __DISKEDITOPENDIALOG__ +#ifndef APP_DISKEDITOPENDIALOG_H +#define APP_DISKEDITOPENDIALOG_H #include #include "resource.h" @@ -46,4 +46,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__DISKEDITOPENDIALOG__*/ +#endif /*APP_DISKEDITOPENDIALOG_H*/ diff --git a/app/DiskFSTree.cpp b/app/DiskFSTree.cpp index be7e461..063f0eb 100644 --- a/app/DiskFSTree.cpp +++ b/app/DiskFSTree.cpp @@ -51,7 +51,8 @@ DiskFSTree::AddDiskFS(CTreeCtrl* pTree, HTREEITEM parent, pTarget->pDiskFS = pDiskFS; pTarget->pFile = nil; // could also use volume dir for ProDOS tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.pszText = const_cast(pDiskFS->GetVolumeID()); + // TODO(xyzzy): need storage for wide-char version + tvi.pszText = L"XYZZY-DiskFSTree1"; // pDiskFS->GetVolumeID(); tvi.cchTextMax = 0; // not needed for insertitem // tvi.iImage = kTreeImageFolderClosed; // tvi.iSelectedImage = kTreeImageFolderOpen; @@ -170,7 +171,8 @@ DiskFSTree::AddSubdir(CTreeCtrl* pTree, HTREEITEM parent, pTarget->pDiskFS = pDiskFS; pTarget->pFile = pParentFile; tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - tvi.pszText = const_cast(pParentFile->GetFileName()); + // TODO(xyzzy): need storage for wide-char version + tvi.pszText = L"XYZZY-DiskFSTree2"; // pParentFile->GetFileName(); tvi.cchTextMax = 0; // not needed for insertitem tvi.iImage = kTreeImageFolderClosed; tvi.iSelectedImage = kTreeImageFolderOpen; @@ -180,7 +182,7 @@ DiskFSTree::AddSubdir(CTreeCtrl* pTree, HTREEITEM parent, tvins.hParent = parent; hLocalRoot = pTree->InsertItem(&tvins); if (hLocalRoot == nil) { - WMSG1("Tree insert '%s' failed\n", tvi.pszText); + WMSG1("Tree insert '%ls' failed\n", tvi.pszText); return nil; } } diff --git a/app/DiskFSTree.h b/app/DiskFSTree.h index e286edc..90753a8 100644 --- a/app/DiskFSTree.h +++ b/app/DiskFSTree.h @@ -7,8 +7,8 @@ * Fill out a CTreeCtrl with the results of a tree search through a DiskFS and * its sub-volumes. */ -#ifndef __DISKFSTREE__ -#define __DISKFSTREE__ +#ifndef APP_DISKFSTREE_H +#define APP_DISKFSTREE_H #include "resource.h" #include "../diskimg/DiskImg.h" @@ -78,4 +78,4 @@ private: TargetData* fpTargetData; }; -#endif /*__DISKFSTREE__*/ +#endif /*APP_DISKFSTREE_H*/ diff --git a/app/DoneOpenDialog.h b/app/DoneOpenDialog.h index de58d4a..eb97dc9 100644 --- a/app/DoneOpenDialog.h +++ b/app/DoneOpenDialog.h @@ -6,6 +6,9 @@ /* * Simple dialog to offer the opportunity to open the file we just created. */ +#ifndef APP_DONEOPENDIALOG_H +#define APP_DONEOPENDIALOG_H + #include "resource.h" class DoneOpenDialog : public CDialog { @@ -14,3 +17,5 @@ public: {} virtual ~DoneOpenDialog(void) {} }; + +#endif /*APP_DONEOPENDIALOG_H*/ diff --git a/app/EOLScanDialog.cpp b/app/EOLScanDialog.cpp index 14d077d..e48cfdd 100644 --- a/app/EOLScanDialog.cpp +++ b/app/EOLScanDialog.cpp @@ -26,23 +26,23 @@ EOLScanDialog::OnInitDialog(void) CWnd* pWnd; CString fmt; - fmt.Format("%ld", fCountChars); + fmt.Format(L"%ld", fCountChars); pWnd = GetDlgItem(IDC_EOLSCAN_CHARS); pWnd->SetWindowText(fmt); - fmt.Format("%ld", fCountCR); + fmt.Format(L"%ld", fCountCR); pWnd = GetDlgItem(IDC_EOLSCAN_CR); pWnd->SetWindowText(fmt); - fmt.Format("%ld", fCountLF); + fmt.Format(L"%ld", fCountLF); pWnd = GetDlgItem(IDC_EOLSCAN_LF); pWnd->SetWindowText(fmt); - fmt.Format("%ld", fCountCRLF); + fmt.Format(L"%ld", fCountCRLF); pWnd = GetDlgItem(IDC_EOLSCAN_CRLF); pWnd->SetWindowText(fmt); - fmt.Format("%ld", fCountHighASCII); + fmt.Format(L"%ld", fCountHighASCII); pWnd = GetDlgItem(IDC_EOLSCAN_HIGHASCII); pWnd->SetWindowText(fmt); diff --git a/app/EOLScanDialog.h b/app/EOLScanDialog.h index b14f5c5..2fd6992 100644 --- a/app/EOLScanDialog.h +++ b/app/EOLScanDialog.h @@ -6,8 +6,8 @@ /* * A simple dialog to display the results of an EOL scan. */ -#ifndef __EOLSCANDIALOG__ -#define __EOLSCANDIALOG__ +#ifndef APP_EOLSCANDIALOG_H +#define APP_EOLSCANDIALOG_H #include "resource.h" @@ -34,4 +34,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__EOLSCANDIALOG__*/ +#endif /*APP_EOLSCANDIALOG_H*/ diff --git a/app/EditAssocDialog.cpp b/app/EditAssocDialog.cpp index 0ddcc9a..782d73e 100644 --- a/app/EditAssocDialog.cpp +++ b/app/EditAssocDialog.cpp @@ -82,9 +82,9 @@ EditAssocDialog::Setup(bool loadAssoc) pListView->GetClientRect(&rect); int width; - width = pListView->GetStringWidth("XXExtensionXX"); - pListView->InsertColumn(0, "Extension", LVCFMT_LEFT, width); - pListView->InsertColumn(1, "Association", LVCFMT_LEFT, + width = pListView->GetStringWidth(L"XXExtensionXX"); + pListView->InsertColumn(0, L"Extension", LVCFMT_LEFT, width); + pListView->InsertColumn(1, L"Association", LVCFMT_LEFT, rect.Width() - width); int num = gMyApp.fRegistry.GetNumFileAssocs(); diff --git a/app/EditAssocDialog.h b/app/EditAssocDialog.h index c4bd790..3766f74 100644 --- a/app/EditAssocDialog.h +++ b/app/EditAssocDialog.h @@ -6,8 +6,8 @@ /* * File associations edit dialog. */ -#ifndef __EDITASSOCDIALOG__ -#define __EDITASSOCDIALOG__ +#ifndef APP_EDITASSOCDIALOG_H +#define APP_EDITASSOCDIALOG_H #include "resource.h" @@ -42,4 +42,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__EDITASSOCDIALOG__*/ \ No newline at end of file +#endif /*APP_EDITASSOCDIALOG_H*/ diff --git a/app/EditCommentDialog.h b/app/EditCommentDialog.h index 69b89aa..5952b85 100644 --- a/app/EditCommentDialog.h +++ b/app/EditCommentDialog.h @@ -6,8 +6,8 @@ /* * Edit a comment. */ -#ifndef __EDITCOMMENTDIALOG__ -#define __EDITCOMMENTDIALOG__ +#ifndef APP_EDITCOMMENTDIALOG_H +#define APP_EDITCOMMENTDIALOG_H #include "GenericArchive.h" #include "resource.h" @@ -44,4 +44,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__EDITCOMMENTDIALOG__*/ \ No newline at end of file +#endif /*APP_EDITCOMMENTDIALOG_H*/ diff --git a/app/EditPropsDialog.cpp b/app/EditPropsDialog.cpp index e9d279a..880ce02 100644 --- a/app/EditPropsDialog.cpp +++ b/app/EditPropsDialog.cpp @@ -88,19 +88,21 @@ EditPropsDialog::OnInitDialog(void) pCombo->InitStorage(256, 256 * 8); for (int type = 0; type < 256; type++) { - const char* str; - char buf[10]; + const WCHAR* str; + WCHAR buf[10]; if (fAllowedTypes == kAllowedPascal) { /* not the most efficient way, but it'll do */ - for (int j = 0; j < NELEM(kPascalTypes); j++) { + int j; + for (j = 0; j < NELEM(kPascalTypes); j++) { if (kPascalTypes[j] == type) break; } if (j == NELEM(kPascalTypes)) continue; } else if (fAllowedTypes == kAllowedDOS) { - for (int j = 0; j < NELEM(kDOSTypes); j++) { + int j; + for (j = 0; j < NELEM(kDOSTypes); j++) { if (kDOSTypes[j] == type) break; } @@ -110,9 +112,9 @@ EditPropsDialog::OnInitDialog(void) str = PathProposal::FileTypeString(type); if (str[0] == '$') - sprintf(buf, "??? $%02X", type); + wsprintf(buf, L"??? $%02X", type); else - sprintf(buf, "%s $%02X", str, type); + wsprintf(buf, L"%ls $%02X", str, type); comboIdx = pCombo->AddString(buf); pCombo->SetItemData(comboIdx, type); @@ -124,7 +126,7 @@ EditPropsDialog::OnInitDialog(void) pCombo->SetCurSel(0); } else { // unexpected -- bogus data out of DiskFS? - comboIdx = pCombo->AddString("???"); + comboIdx = pCombo->AddString(L"???"); pCombo->SetCurSel(comboIdx); pCombo->SetItemData(comboIdx, 256); } @@ -140,7 +142,7 @@ EditPropsDialog::OnInitDialog(void) ASSERT(pWnd != nil); FormatDate(fProps.modWhen, &dateStr); pWnd->SetWindowText(dateStr); - //WMSG2("USING DATE '%s' from 0x%08lx\n", dateStr, fProps.modWhen); + //WMSG2("USING DATE '%ls' from 0x%08lx\n", dateStr, fProps.modWhen); CEdit* pEdit = (CEdit*) GetDlgItem(IDC_PROPS_AUXTYPE); ASSERT(pEdit != nil); @@ -219,8 +221,8 @@ EditPropsDialog::DoDataExchange(CDataExchange* pDX) DDX_Text(pDX, IDC_PROPS_HFS_FILETYPE, type); DDX_Text(pDX, IDC_PROPS_HFS_AUXTYPE, creator); if (type.GetLength() != 4 || creator.GetLength() != 4) { - MessageBox("The file and creator types must be exactly" - " 4 characters each.", + MessageBox(L"The file and creator types must be exactly" + L" 4 characters each.", appName, MB_OK); pDX->Fail(); return; @@ -236,8 +238,8 @@ EditPropsDialog::DoDataExchange(CDataExchange* pDX) } else { /* ProDOS mode */ if (GetAuxType() < 0) { - MessageBox("The AuxType field must be a valid 4-digit" - " hexadecimal number.", + MessageBox(L"The AuxType field must be a valid 4-digit" + L" hexadecimal number.", appName, MB_OK); pDX->Fail(); return; @@ -329,7 +331,7 @@ EditPropsDialog::DoDataExchange(CDataExchange* pDX) //DDX_CBIndex(pDX, IDC_PROPS_FILETYPE, fileTypeIdx); /* write the aux type as a hex string */ - fAuxType.Format("%04X", fProps.auxType); + fAuxType.Format(L"%04X", fProps.auxType); DDX_Text(pDX, IDC_PROPS_AUXTYPE, fAuxType); } OnTypeChange(); // set the description field @@ -352,12 +354,12 @@ EditPropsDialog::DoDataExchange(CDataExchange* pDX) void EditPropsDialog::OnTypeChange(void) { - static const char* kUnknownFileType = "Unknown file type"; + static const WCHAR kUnknownFileType[] = L"Unknown file type"; CComboBox* pCombo; CWnd* pWnd; int fileType, fileTypeIdx; long auxType; - const char* descr = nil; + const WCHAR* descr = nil; pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE); ASSERT(pCombo != nil); @@ -426,7 +428,7 @@ EditPropsDialog::UpdateHFSMode(void) pWnd = GetDlgItem(IDC_PROPS_TYPEDESCR); ASSERT(pWnd != nil); - pWnd->SetWindowText("(HFS type)"); + pWnd->SetWindowText(L"(HFS type)"); OnHFSTypeChange(); } else { /* switch to ProDOS mode */ @@ -486,18 +488,18 @@ EditPropsDialog::GetAuxType(void) CString aux; pWnd->GetWindowText(aux); - const char* str = aux; - char* end; + const WCHAR* str = aux; + WCHAR* end; long val; if (str[0] == '\0') { WMSG0(" HEY: blank aux type, returning -1\n"); return -1; } - val = strtoul(aux, &end, 16); - if (end != str + strlen(str)) { - WMSG1(" HEY: found some garbage in aux type '%s', returning -1\n", - (LPCTSTR) aux); + val = wcstoul(aux, &end, 16); + if (end != str + wcslen(str)) { + WMSG1(" HEY: found some garbage in aux type '%ls', returning -1\n", + (LPCWSTR) aux); return -1; } return val; diff --git a/app/EditPropsDialog.h b/app/EditPropsDialog.h index f98f1aa..0f336ea 100644 --- a/app/EditPropsDialog.h +++ b/app/EditPropsDialog.h @@ -6,8 +6,8 @@ /* * Edit file properties. */ -#ifndef __EDITPROPSDIALOG__ -#define __EDITPROPSDIALOG__ +#ifndef APP_EDITPROPSDIALOG_H +#define APP_EDITPROPSDIALOG_H #include "GenericArchive.h" #include "resource.h" @@ -85,4 +85,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__EDITPROPSDIALOG__*/ \ No newline at end of file +#endif /*APP_EDITPROPSDIALOG_H*/ diff --git a/app/EnterRegDialog.cpp b/app/EnterRegDialog.cpp index aff8f55..822eaef 100644 --- a/app/EnterRegDialog.cpp +++ b/app/EnterRegDialog.cpp @@ -68,7 +68,7 @@ EnterRegDialog::DoDataExchange(CDataExchange* pDX) if (gMyApp.fRegistry.IsValidRegistrationKey(fUserName, fCompanyName, fRegKey)) { - WMSG3("Correct key entered: '%s' '%s' '%s'\n", + WMSG3("Correct key entered: '%ls' '%ls' '%ls'\n", (LPCTSTR)fUserName, (LPCTSTR)fCompanyName, (LPCTSTR)fRegKey); } else { WMSG0("Incorrect key entered, rejecting\n"); diff --git a/app/EnterRegDialog.h b/app/EnterRegDialog.h index 5153cf3..868bcaa 100644 --- a/app/EnterRegDialog.h +++ b/app/EnterRegDialog.h @@ -6,8 +6,8 @@ /* * Dialog allowing the user to enter registration data. */ -#ifndef __ENTERREGDIALOG__ -#define __ENTERREGDIALOG__ +#ifndef APP_ENTERREGDIALOG_H +#define APP_ENTERREGDIALOG_H #include "../util/UtilLib.h" #include "resource.h" @@ -47,4 +47,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__ENTERREGDIALOG__*/ \ No newline at end of file +#endif /*APP_ENTERREGDIALOG_H*/ diff --git a/app/ExtractOptionsDialog.cpp b/app/ExtractOptionsDialog.cpp index 2a63baf..f593ea3 100644 --- a/app/ExtractOptionsDialog.cpp +++ b/app/ExtractOptionsDialog.cpp @@ -181,8 +181,8 @@ ExtractOptionsDialog::OnChooseFolder(void) chooseDir.SetPathName(editPath); if (chooseDir.DoModal() == IDOK) { - const char* ccp = chooseDir.GetPathName(); - WMSG1("New extract path chosen = '%s'\n", ccp); + const WCHAR* ccp = chooseDir.GetPathName(); + WMSG1("New extract path chosen = '%ls'\n", ccp); pEditWnd->SetWindowText(ccp); } diff --git a/app/ExtractOptionsDialog.h b/app/ExtractOptionsDialog.h index 87dd3f8..c004ff9 100644 --- a/app/ExtractOptionsDialog.h +++ b/app/ExtractOptionsDialog.h @@ -6,8 +6,8 @@ /* * Choose options related to file extraction. */ -#ifndef __EXTRACT_OPTIONS_DIALOG__ -#define __EXTRACT_OPTIONS_DIALOG__ +#ifndef APP_EXTRACTOPTIONSDIALOG_H +#define APP_EXTRACTOPTIONSDIALOG_H #include "../util/UtilLib.h" #include "resource.h" @@ -84,4 +84,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__EXTRACT_OPTIONS_DIALOG__*/ \ No newline at end of file +#endif /*APP_EXTRACTOPTIONSDIALOG_H*/ diff --git a/app/FileNameConv.cpp b/app/FileNameConv.cpp index c2196df..de2519b 100644 --- a/app/FileNameConv.cpp +++ b/app/FileNameConv.cpp @@ -5,8 +5,7 @@ */ /* * Filename manipulation, including file type preservation. This is - * substantially ripped from NuLib2, which would be a GPL violation if - * it weren't my code to begin with. + * substantially ripped from NuLib2. */ #include "stdafx.h" #include "FileNameConv.h" @@ -39,46 +38,46 @@ #define kResourceFlag 'r' #define kDiskImageFlag 'i' #define kMaxExtLen 5 /* ".1234" */ -#define kResourceStr _T("_rsrc_") +#define kResourceStr L"_rsrc_" /* must be longer then strlen(kResourceStr)... no problem there */ -#define kMaxPathGrowth (sizeof("#XXXXXXXXYYYYYYYYZ")-1 + kMaxExtLen+1) +#define kMaxPathGrowth (sizeof(L"#XXXXXXXXYYYYYYYYZ")-1 + kMaxExtLen+1) /* ProDOS file type names; must be entirely in upper case */ -static const char gFileTypeNames[256][4] = { - "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", - "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", - "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", - "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", - "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", - "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", - "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", - "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", - "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", - "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", - "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", - "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", - "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", - "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", - "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", - "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", - "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", - "$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F", - "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", - "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", - "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", - "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", - "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", - "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", - "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", - "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", - "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", - "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", - "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", - "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", - "CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", - "$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS" +static const WCHAR gFileTypeNames[256][4] = { + L"NON", L"BAD", L"PCD", L"PTX", L"TXT", L"PDA", L"BIN", L"FNT", + L"FOT", L"BA3", L"DA3", L"WPF", L"SOS", L"$0D", L"$0E", L"DIR", + L"RPD", L"RPI", L"AFD", L"AFM", L"AFR", L"SCL", L"PFS", L"$17", + L"$18", L"ADB", L"AWP", L"ASP", L"$1C", L"$1D", L"$1E", L"$1F", + L"TDM", L"$21", L"$22", L"$23", L"$24", L"$25", L"$26", L"$27", + L"$28", L"$29", L"8SC", L"8OB", L"8IC", L"8LD", L"P8C", L"$2F", + L"$30", L"$31", L"$32", L"$33", L"$34", L"$35", L"$36", L"$37", + L"$38", L"$39", L"$3A", L"$3B", L"$3C", L"$3D", L"$3E", L"$3F", + L"DIC", L"OCR", L"FTD", L"$43", L"$44", L"$45", L"$46", L"$47", + L"$48", L"$49", L"$4A", L"$4B", L"$4C", L"$4D", L"$4E", L"$4F", + L"GWP", L"GSS", L"GDB", L"DRW", L"GDP", L"HMD", L"EDU", L"STN", + L"HLP", L"COM", L"CFG", L"ANM", L"MUM", L"ENT", L"DVU", L"FIN", + L"$60", L"$61", L"$62", L"$63", L"$64", L"$65", L"$66", L"$67", + L"$68", L"$69", L"$6A", L"BIO", L"$6C", L"TDR", L"PRE", L"HDV", + L"$70", L"$71", L"$72", L"$73", L"$74", L"$75", L"$76", L"$77", + L"$78", L"$79", L"$7A", L"$7B", L"$7C", L"$7D", L"$7E", L"$7F", + L"$80", L"$81", L"$82", L"$83", L"$84", L"$85", L"$86", L"$87", + L"$88", L"$89", L"$8A", L"$8B", L"$8C", L"$8D", L"$8E", L"$8F", + L"$90", L"$91", L"$92", L"$93", L"$94", L"$95", L"$96", L"$97", + L"$98", L"$99", L"$9A", L"$9B", L"$9C", L"$9D", L"$9E", L"$9F", + L"WP ", L"$A1", L"$A2", L"$A3", L"$A4", L"$A5", L"$A6", L"$A7", + L"$A8", L"$A9", L"$AA", L"GSB", L"TDF", L"BDF", L"$AE", L"$AF", + L"SRC", L"OBJ", L"LIB", L"S16", L"RTL", L"EXE", L"PIF", L"TIF", + L"NDA", L"CDA", L"TOL", L"DVR", L"LDF", L"FST", L"$BE", L"DOC", + L"PNT", L"PIC", L"ANI", L"PAL", L"$C4", L"OOG", L"SCR", L"CDV", + L"FON", L"FND", L"ICN", L"$CB", L"$CC", L"$CD", L"$CE", L"$CF", + L"$D0", L"$D1", L"$D2", L"$D3", L"$D4", L"MUS", L"INS", L"MDI", + L"SND", L"$D9", L"$DA", L"DBM", L"$DC", L"DDD", L"$DE", L"$DF", + L"LBR", L"$E1", L"ATK", L"$E3", L"$E4", L"$E5", L"$E6", L"$E7", + L"$E8", L"$E9", L"$EA", L"$EB", L"$EC", L"$ED", L"R16", L"PAS", + L"CMD", L"$F1", L"$F2", L"$F3", L"$F4", L"$F5", L"$F6", L"$F7", + L"$F8", L"OS ", L"INT", L"IVR", L"BAS", L"VAR", L"REL", L"SYS" }; /* @@ -90,26 +89,26 @@ static const char gFileTypeNames[256][4] = { * file rather than a hard-coded table. Ought to fix that someday. */ static const struct { - const char* label; + const WCHAR* label; unsigned short fileType; unsigned long auxType; unsigned char flags; } gRecognizedExtensions[] = { - { "ASM", 0xb0, 0x0003, 0 }, /* APW assembly source */ - { "C", 0xb0, 0x000a, 0 }, /* APW C source */ - { "H", 0xb0, 0x000a, 0 }, /* APW C header */ - { "CPP", 0xb0, 0x0000, 0 }, /* generic source file */ - { "BNY", 0xe0, 0x8000, 0 }, /* Binary II lib */ - { "BQY", 0xe0, 0x8000, 0 }, /* Binary II lib, w/ compress */ - { "BXY", 0xe0, 0x8000, 0 }, /* Binary II wrap around SHK */ - { "BSE", 0xe0, 0x8000, 0 }, /* Binary II wrap around SEA */ - { "SEA", 0xb3, 0xdb07, 0 }, /* GSHK SEA */ - { "TEXT", 0x04, 0x0000, 0 }, /* ASCII Text */ - { "GIF", 0xc0, 0x8006, 0 }, /* GIF image */ - { "JPG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */ - { "JPEG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */ - //{ "ACU", 0xe0, 0x8001, 0 }, /* ACU archive */ - { "SHK", 0xe0, 0x8002, 0 }, /* ShrinkIt archive */ + { L"ASM", 0xb0, 0x0003, 0 }, /* APW assembly source */ + { L"C", 0xb0, 0x000a, 0 }, /* APW C source */ + { L"H", 0xb0, 0x000a, 0 }, /* APW C header */ + { L"CPP", 0xb0, 0x0000, 0 }, /* generic source file */ + { L"BNY", 0xe0, 0x8000, 0 }, /* Binary II lib */ + { L"BQY", 0xe0, 0x8000, 0 }, /* Binary II lib, w/ compress */ + { L"BXY", 0xe0, 0x8000, 0 }, /* Binary II wrap around SHK */ + { L"BSE", 0xe0, 0x8000, 0 }, /* Binary II wrap around SEA */ + { L"SEA", 0xb3, 0xdb07, 0 }, /* GSHK SEA */ + { L"TEXT", 0x04, 0x0000, 0 }, /* ASCII Text */ + { L"GIF", 0xc0, 0x8006, 0 }, /* GIF image */ + { L"JPG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */ + { L"JPEG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */ + //{ L"ACU", 0xe0, 0x8001, 0 }, /* ACU archive */ + { L"SHK", 0xe0, 0x8002, 0 }, /* ShrinkIt archive */ }; @@ -118,7 +117,7 @@ static const struct { * * Note to self: code down below tests first char for '?'. */ -/*static*/ const char* +/*static*/ const WCHAR* PathProposal::FileTypeString(unsigned long fileType) { if (fileType < NELEM(gFileTypeNames)) @@ -142,459 +141,459 @@ static const struct { unsigned short fileType; unsigned short minAuxType; // start of range for which this applies unsigned short maxAuxType; // end of range - const char* descr; + const WCHAR* descr; } gTypeDescriptions[] = { - /*NON*/ { 0x00, 0x0000, 0xffff, "Untyped file" }, - /*BAD*/ { 0x01, 0x0000, 0xffff, "Bad blocks" }, - /*PCD*/ { 0x02, 0x0000, 0xffff, "Pascal code" }, - /*PTX*/ { 0x03, 0x0000, 0xffff, "Pascal text" }, - /*TXT*/ { 0x04, 0x0000, 0xffff, "ASCII text" }, - /*PDA*/ { 0x05, 0x0000, 0xffff, "Pascal data" }, - /*BIN*/ { 0x06, 0x0000, 0xffff, "Binary" }, - /*FNT*/ { 0x07, 0x0000, 0xffff, "Apple /// font" }, - /*FOT*/ { 0x08, 0x0000, 0xffff, "Apple II or /// graphics" }, - /* */ { 0x08, 0x0000, 0x3fff, "Apple II graphics" }, - /* */ { 0x08, 0x4000, 0x4000, "Packed hi-res image" }, - /* */ { 0x08, 0x4001, 0x4001, "Packed double hi-res image" }, - /* */ { 0x08, 0x8001, 0x8001, "Printographer packed HGR file" }, - /* */ { 0x08, 0x8002, 0x8002, "Printographer packed DHGR file" }, - /* */ { 0x08, 0x8003, 0x8003, "Softdisk hi-res image" }, - /* */ { 0x08, 0x8004, 0x8004, "Softdisk double hi-res image" }, - /*BA3*/ { 0x09, 0x0000, 0xffff, "Apple /// BASIC program" }, - /*DA3*/ { 0x0a, 0x0000, 0xffff, "Apple /// BASIC data" }, - /*WPF*/ { 0x0b, 0x0000, 0xffff, "Apple II or /// word processor" }, - /* */ { 0x0b, 0x8001, 0x8001, "Write This Way document" }, - /* */ { 0x0b, 0x8002, 0x8002, "Writing & Publishing document" }, - /*SOS*/ { 0x0c, 0x0000, 0xffff, "Apple /// SOS system" }, - /*DIR*/ { 0x0f, 0x0000, 0xffff, "Folder" }, - /*RPD*/ { 0x10, 0x0000, 0xffff, "Apple /// RPS data" }, - /*RPI*/ { 0x11, 0x0000, 0xffff, "Apple /// RPS index" }, - /*AFD*/ { 0x12, 0x0000, 0xffff, "Apple /// AppleFile discard" }, - /*AFM*/ { 0x13, 0x0000, 0xffff, "Apple /// AppleFile model" }, - /*AFR*/ { 0x14, 0x0000, 0xffff, "Apple /// AppleFile report format" }, - /*SCL*/ { 0x15, 0x0000, 0xffff, "Apple /// screen library" }, - /*PFS*/ { 0x16, 0x0000, 0xffff, "PFS document" }, - /* */ { 0x16, 0x0001, 0x0001, "PFS:File document" }, - /* */ { 0x16, 0x0002, 0x0002, "PFS:Write document" }, - /* */ { 0x16, 0x0003, 0x0003, "PFS:Graph document" }, - /* */ { 0x16, 0x0004, 0x0004, "PFS:Plan document" }, - /* */ { 0x16, 0x0016, 0x0016, "PFS internal data" }, - /*ADB*/ { 0x19, 0x0000, 0xffff, "AppleWorks data base" }, - /*AWP*/ { 0x1a, 0x0000, 0xffff, "AppleWorks word processor" }, - /*ASP*/ { 0x1b, 0x0000, 0xffff, "AppleWorks spreadsheet" }, - /*TDM*/ { 0x20, 0x0000, 0xffff, "Desktop Manager document" }, - /*???*/ { 0x21, 0x0000, 0xffff, "Instant Pascal source" }, - /*???*/ { 0x22, 0x0000, 0xffff, "UCSD Pascal volume" }, - /*???*/ { 0x29, 0x0000, 0xffff, "Apple /// SOS dictionary" }, - /*8SC*/ { 0x2a, 0x0000, 0xffff, "Apple II source code" }, - /* */ { 0x2a, 0x8001, 0x8001, "EBBS command script" }, - /*8OB*/ { 0x2b, 0x0000, 0xffff, "Apple II object code" }, - /* */ { 0x2b, 0x8001, 0x8001, "GBBS Pro object Code" }, - /*8IC*/ { 0x2c, 0x0000, 0xffff, "Apple II interpreted code" }, - /* */ { 0x2c, 0x8003, 0x8003, "APEX Program File" }, - /* */ { 0x2c, 0x8005, 0x8005, "EBBS tokenized command script" }, - /*8LD*/ { 0x2d, 0x0000, 0xffff, "Apple II language data" }, - /* */ { 0x2d, 0x8006, 0x8005, "EBBS message bundle" }, - /* */ { 0x2d, 0x8007, 0x8007, "EBBS compressed message bundle" }, - /*P8C*/ { 0x2e, 0x0000, 0xffff, "ProDOS 8 code module" }, - /* */ { 0x2e, 0x8001, 0x8001, "Davex 8 Command" }, - /*PTP*/ { 0x2e, 0x8002, 0x8002, "Point-to-Point drivers" }, - /*PTP*/ { 0x2e, 0x8003, 0x8003, "Point-to-Point code" }, - /* */ { 0x2e, 0x8004, 0x8004, "Softdisk printer driver" }, - /*DIC*/ { 0x40, 0x0000, 0xffff, "Dictionary file" }, - /*???*/ { 0x41, 0x0000, 0xffff, "OCR data" }, - /* */ { 0x41, 0x8001, 0x8001, "InWords OCR font table" }, - /*FTD*/ { 0x42, 0x0000, 0xffff, "File type names" }, - /*???*/ { 0x43, 0x0000, 0xffff, "Peripheral data" }, - /* */ { 0x43, 0x8001, 0x8001, "Express document" }, - /*???*/ { 0x44, 0x0000, 0xffff, "Personal information" }, - /* */ { 0x44, 0x8001, 0x8001, "ResuMaker personal information" }, - /* */ { 0x44, 0x8002, 0x8002, "ResuMaker resume" }, - /* */ { 0x44, 0x8003, 0x8003, "II Notes document" }, - /* */ { 0x44, 0x8004, 0x8004, "Softdisk scrapbook document" }, - /* */ { 0x44, 0x8005, 0x8005, "Don't Forget document" }, - /* */ { 0x44, 0x80ff, 0x80ff, "What To Do data" }, - /* */ { 0x44, 0xbeef, 0xbeef, "Table Scraps scrapbook" }, - /*???*/ { 0x45, 0x0000, 0xffff, "Mathematical document" }, - /* */ { 0x45, 0x8001, 0x8001, "GSymbolix 3D graph document" }, - /* */ { 0x45, 0x8002, 0x8002, "GSymbolix formula document" }, - /*???*/ { 0x46, 0x0000, 0xffff, "AutoSave profiles" }, - /* */ { 0x46, 0x8001, 0x8001, "AutoSave profiles" }, - /*GWP*/ { 0x50, 0x0000, 0xffff, "Apple IIgs Word Processor" }, - /* */ { 0x50, 0x8001, 0x8001, "DeluxeWrite document" }, - /* */ { 0x50, 0x8003, 0x8003, "Personal Journal document" }, - /* */ { 0x50, 0x8010, 0x8010, "AppleWorks GS word processor" }, - /* */ { 0x50, 0x8011, 0x8011, "Softdisk issue text" }, - /* */ { 0x50, 0x5445, 0x5445, "Teach document" }, - /*GSS*/ { 0x51, 0x0000, 0xffff, "Apple IIgs spreadsheet" }, - /* */ { 0x51, 0x8010, 0x8010, "AppleWorks GS spreadsheet" }, - /* */ { 0x51, 0x2358, 0x2358, "QC Calc spreadsheet " }, - /*GDB*/ { 0x52, 0x0000, 0xffff, "Apple IIgs data base" }, - /* */ { 0x52, 0x8001, 0x8001, "GTv database" }, - /* */ { 0x52, 0x8010, 0x8010, "AppleWorks GS data base" }, - /* */ { 0x52, 0x8011, 0x8011, "AppleWorks GS DB template" }, - /* */ { 0x52, 0x8013, 0x8013, "GSAS database" }, - /* */ { 0x52, 0x8014, 0x8014, "GSAS accounting journals" }, - /* */ { 0x52, 0x8015, 0x8015, "Address Manager document" }, - /* */ { 0x52, 0x8016, 0x8016, "Address Manager defaults" }, - /* */ { 0x52, 0x8017, 0x8017, "Address Manager index" }, - /*DRW*/ { 0x53, 0x0000, 0xffff, "Drawing" }, - /* */ { 0x53, 0x8002, 0x8002, "Graphic Disk Labeler document" }, - /* */ { 0x53, 0x8010, 0x8010, "AppleWorks GS graphics" }, - /*GDP*/ { 0x54, 0x0000, 0xffff, "Desktop publishing" }, - /* */ { 0x54, 0x8002, 0x8002, "GraphicWriter document" }, - /* */ { 0x54, 0x8003, 0x8003, "Label It document" }, - /* */ { 0x54, 0x8010, 0x8010, "AppleWorks GS Page Layout" }, - /* */ { 0x54, 0xdd3e, 0xdd3e, "Medley document" }, - /*HMD*/ { 0x55, 0x0000, 0xffff, "Hypermedia" }, - /* */ { 0x55, 0x0001, 0x0001, "HyperCard IIgs stack" }, - /* */ { 0x55, 0x8001, 0x8001, "Tutor-Tech document" }, - /* */ { 0x55, 0x8002, 0x8002, "HyperStudio document" }, - /* */ { 0x55, 0x8003, 0x8003, "Nexus document" }, - /* */ { 0x55, 0x8004, 0x8004, "HyperSoft stack" }, - /* */ { 0x55, 0x8005, 0x8005, "HyperSoft card" }, - /* */ { 0x55, 0x8006, 0x8006, "HyperSoft external command" }, - /*EDU*/ { 0x56, 0x0000, 0xffff, "Educational Data" }, - /* */ { 0x56, 0x8001, 0x8001, "Tutor-Tech scores" }, - /* */ { 0x56, 0x8007, 0x8007, "GradeBook data" }, - /*STN*/ { 0x57, 0x0000, 0xffff, "Stationery" }, - /* */ { 0x57, 0x8003, 0x8003, "Music Writer format" }, - /*HLP*/ { 0x58, 0x0000, 0xffff, "Help file" }, - /* */ { 0x58, 0x8002, 0x8002, "Davex 8 help file" }, - /* */ { 0x58, 0x8005, 0x8005, "Micol Advanced Basic help file" }, - /* */ { 0x58, 0x8006, 0x8006, "Locator help document" }, - /* */ { 0x58, 0x8007, 0x8007, "Personal Journal help" }, - /* */ { 0x58, 0x8008, 0x8008, "Home Refinancer help" }, - /* */ { 0x58, 0x8009, 0x8009, "The Optimizer help" }, - /* */ { 0x58, 0x800a, 0x800a, "Text Wizard help" }, - /* */ { 0x58, 0x800b, 0x800b, "WordWorks Pro help system" }, - /* */ { 0x58, 0x800c, 0x800c, "Sound Wizard help" }, - /* */ { 0x58, 0x800d, 0x800d, "SeeHear help system" }, - /* */ { 0x58, 0x800e, 0x800e, "QuickForms help system" }, - /* */ { 0x58, 0x800f, 0x800f, "Don't Forget help system" }, - /*COM*/ { 0x59, 0x0000, 0xffff, "Communications file" }, - /* */ { 0x59, 0x8002, 0x8002, "AppleWorks GS communications" }, - /*CFG*/ { 0x5a, 0x0000, 0xffff, "Configuration file" }, - /* */ { 0x5a, 0x0000, 0x0000, "Sound settings files" }, - /* */ { 0x5a, 0x0002, 0x0002, "Battery RAM configuration" }, - /* */ { 0x5a, 0x0003, 0x0003, "AutoLaunch preferences" }, - /* */ { 0x5a, 0x0004, 0x0004, "SetStart preferences" }, - /* */ { 0x5a, 0x0005, 0x0005, "GSBug configuration" }, - /* */ { 0x5a, 0x0006, 0x0006, "Archiver preferences" }, - /* */ { 0x5a, 0x0007, 0x0007, "Archiver table of contents" }, - /* */ { 0x5a, 0x0008, 0x0008, "Font Manager data" }, - /* */ { 0x5a, 0x0009, 0x0009, "Print Manager data" }, - /* */ { 0x5a, 0x000a, 0x000a, "IR preferences" }, - /* */ { 0x5a, 0x8001, 0x8001, "Master Tracks Jr. preferences" }, - /* */ { 0x5a, 0x8002, 0x8002, "GraphicWriter preferences" }, - /* */ { 0x5a, 0x8003, 0x8003, "Z-Link configuration" }, - /* */ { 0x5a, 0x8004, 0x8004, "JumpStart configuration" }, - /* */ { 0x5a, 0x8005, 0x8005, "Davex 8 configuration" }, - /* */ { 0x5a, 0x8006, 0x8006, "Nifty List configuration" }, - /* */ { 0x5a, 0x8007, 0x8007, "GTv videodisc configuration" }, - /* */ { 0x5a, 0x8008, 0x8008, "GTv Workshop configuration" }, - /*PTP*/ { 0x5a, 0x8009, 0x8009, "Point-to-Point preferences" }, - /* */ { 0x5a, 0x800a, 0x800a, "ORCA/Disassembler preferences" }, - /* */ { 0x5a, 0x800b, 0x800b, "SnowTerm preferences" }, - /* */ { 0x5a, 0x800c, 0x800c, "My Word! preferences" }, - /* */ { 0x5a, 0x800d, 0x800d, "Chipmunk configuration" }, - /* */ { 0x5a, 0x8010, 0x8010, "AppleWorks GS configuration" }, - /* */ { 0x5a, 0x8011, 0x8011, "SDE Shell preferences" }, - /* */ { 0x5a, 0x8012, 0x8012, "SDE Editor preferences" }, - /* */ { 0x5a, 0x8013, 0x8013, "SDE system tab ruler" }, - /* */ { 0x5a, 0x8014, 0x8014, "Nexus configuration" }, - /* */ { 0x5a, 0x8015, 0x8015, "DesignMaster preferences" }, - /* */ { 0x5a, 0x801a, 0x801a, "MAX/Edit keyboard template" }, - /* */ { 0x5a, 0x801b, 0x801b, "MAX/Edit tab ruler set" }, - /* */ { 0x5a, 0x801c, 0x801c, "Platinum Paint preferences" }, - /* */ { 0x5a, 0x801d, 0x801d, "Sea Scan 1000" }, - /* */ { 0x5a, 0x801e, 0x801e, "Allison preferences" }, - /* */ { 0x5a, 0x801f, 0x801f, "Gold of the Americas options" }, - /* */ { 0x5a, 0x8021, 0x8021, "GSAS accounting setup" }, - /* */ { 0x5a, 0x8022, 0x8022, "GSAS accounting document" }, - /* */ { 0x5a, 0x8023, 0x8023, "UtilityLaunch preferences" }, - /* */ { 0x5a, 0x8024, 0x8024, "Softdisk configuration" }, - /* */ { 0x5a, 0x8025, 0x8025, "Quit-To configuration" }, - /* */ { 0x5a, 0x8026, 0x8026, "Big Edit Thing" }, - /* */ { 0x5a, 0x8027, 0x8027, "ZMaker preferences" }, - /* */ { 0x5a, 0x8028, 0x8028, "Minstrel configuration" }, - /* */ { 0x5a, 0x8029, 0x8029, "WordWorks Pro preferences" }, - /* */ { 0x5a, 0x802b, 0x802b, "Pointless preferences" }, - /* */ { 0x5a, 0x802c, 0x802c, "Micol Advanced Basic config" }, - /* */ { 0x5a, 0x802e, 0x802e, "Label It configuration" }, - /* */ { 0x5a, 0x802f, 0x802f, "Cool Cursor document" }, - /* */ { 0x5a, 0x8030, 0x8030, "Locator preferences" }, - /* */ { 0x5a, 0x8031, 0x8031, "Replicator preferences" }, - /* */ { 0x5a, 0x8032, 0x8032, "Kangaroo configuration" }, - /* */ { 0x5a, 0x8033, 0x8033, "Kangaroo data" }, - /* */ { 0x5a, 0x8034, 0x8034, "TransProg III configuration" }, - /* */ { 0x5a, 0x8035, 0x8035, "Home Refinancer preferences" }, - /* */ { 0x5a, 0x8036, 0x8036, "Easy Eyes settings" }, - /* */ { 0x5a, 0x8037, 0x8037, "The Optimizer settings" }, - /* */ { 0x5a, 0x8038, 0x8038, "Text Wizard settings" }, - /* */ { 0x5a, 0x803b, 0x803b, "Disk Access II preferences" }, - /* */ { 0x5a, 0x803d, 0x803d, "Quick DA configuration" }, - /* */ { 0x5a, 0x803e, 0x803e, "Crazy 8s preferences" }, - /* */ { 0x5a, 0x803f, 0x803f, "Sound Wizard settings" }, - /* */ { 0x5a, 0x8041, 0x8041, "Quick Window configuration" }, - /* */ { 0x5a, 0x8044, 0x8044, "Universe Master disk map" }, - /* */ { 0x5a, 0x8046, 0x8046, "Autopilot configuration" }, - /* */ { 0x5a, 0x8047, 0x8047, "EGOed preferences" }, - /* */ { 0x5a, 0x8049, 0x8049, "Quick DA preferences" }, - /* */ { 0x5a, 0x804b, 0x804b, "HardPressed volume preferences" }, - /* */ { 0x5a, 0x804c, 0x804c, "HardPressed global preferences" }, - /* */ { 0x5a, 0x804d, 0x804d, "HardPressed profile" }, - /* */ { 0x5a, 0x8050, 0x8050, "Don't Forget settings" }, - /* */ { 0x5a, 0x8052, 0x8052, "ProBOOT preferences" }, - /* */ { 0x5a, 0x8054, 0x8054, "Battery Brain preferences" }, - /* */ { 0x5a, 0x8055, 0x8055, "Rainbow configuration" }, - /* */ { 0x5a, 0x8061, 0x8061, "TypeSet preferences" }, - /* */ { 0x5a, 0x8063, 0x8063, "Cool Cursor preferences" }, - /* */ { 0x5a, 0x806e, 0x806e, "Balloon preferences" }, - /* */ { 0x5a, 0x80fe, 0x80fe, "Special Edition configuration" }, - /* */ { 0x5a, 0x80ff, 0x80ff, "Sun Dial preferences" }, - /*ANM*/ { 0x5b, 0x0000, 0xffff, "Animation file" }, - /* */ { 0x5b, 0x8001, 0x8001, "Cartooners movie" }, - /* */ { 0x5b, 0x8002, 0x8002, "Cartooners actors" }, - /* */ { 0x5b, 0x8005, 0x8005, "Arcade King Super document" }, - /* */ { 0x5b, 0x8006, 0x8006, "Arcade King DHRG document" }, - /* */ { 0x5b, 0x8007, 0x8007, "DreamVision movie" }, - /*MUM*/ { 0x5c, 0x0000, 0xffff, "Multimedia document" }, - /* */ { 0x5c, 0x8001, 0x8001, "GTv multimedia playlist" }, - /*ENT*/ { 0x5d, 0x0000, 0xffff, "Game/Entertainment document" }, - /* */ { 0x5d, 0x8001, 0x8001, "Solitaire Royale document" }, - /* */ { 0x5d, 0x8002, 0x8002, "BattleFront scenario" }, - /* */ { 0x5d, 0x8003, 0x8003, "BattleFront saved game" }, - /* */ { 0x5d, 0x8004, 0x8004, "Gold of the Americas game" }, - /* */ { 0x5d, 0x8006, 0x8006, "Blackjack Tutor document" }, - /* */ { 0x5d, 0x8008, 0x8008, "Canasta document" }, - /* */ { 0x5d, 0x800b, 0x800b, "Word Search document" }, - /* */ { 0x5d, 0x800c, 0x800c, "Tarot deal" }, - /* */ { 0x5d, 0x800d, 0x800d, "Tarot tournament" }, - /* */ { 0x5d, 0x800e, 0x800e, "Full Metal Planet game" }, - /* */ { 0x5d, 0x800f, 0x800f, "Full Metal Planet player" }, - /* */ { 0x5d, 0x8010, 0x8010, "Quizzical high scores" }, - /* */ { 0x5d, 0x8011, 0x8011, "Meltdown high scores" }, - /* */ { 0x5d, 0x8012, 0x8012, "BlockWords high scores" }, - /* */ { 0x5d, 0x8013, 0x8013, "Lift-A-Gon scores" }, - /* */ { 0x5d, 0x8014, 0x8014, "Softdisk Adventure" }, - /* */ { 0x5d, 0x8015, 0x8015, "Blankety Blank document" }, - /* */ { 0x5d, 0x8016, 0x8016, "Son of Star Axe champion" }, - /* */ { 0x5d, 0x8017, 0x8017, "Digit Fidget high scores" }, - /* */ { 0x5d, 0x8018, 0x8018, "Eddie map" }, - /* */ { 0x5d, 0x8019, 0x8019, "Eddie tile set" }, - /* */ { 0x5d, 0x8122, 0x8122, "Wolfenstein 3D scenario" }, - /* */ { 0x5d, 0x8123, 0x8123, "Wolfenstein 3D saved game" }, - /*DVU*/ { 0x5e, 0x0000, 0xffff, "Development utility document" }, - /* */ { 0x5e, 0x0001, 0x0001, "Resource file" }, - /* */ { 0x5e, 0x8001, 0x8001, "ORCA/Disassembler template" }, - /* */ { 0x5e, 0x8003, 0x8003, "DesignMaster document" }, - /* */ { 0x5e, 0x8008, 0x8008, "ORCA/C symbol file" }, - /*FIN*/ { 0x5f, 0x0000, 0xffff, "Financial document" }, - /* */ { 0x5f, 0x8001, 0x8001, "Your Money Matters document" }, - /* */ { 0x5f, 0x8002, 0x8002, "Home Refinancer document" }, - /*BIO*/ { 0x6b, 0x0000, 0xffff, "PC Transporter BIOS" }, - /*TDR*/ { 0x6d, 0x0000, 0xffff, "PC Transporter driver" }, - /*PRE*/ { 0x6e, 0x0000, 0xffff, "PC Transporter pre-boot" }, - /*HDV*/ { 0x6f, 0x0000, 0xffff, "PC Transporter volume" }, - /*WP */ { 0xa0, 0x0000, 0xffff, "WordPerfect document" }, - /*GSB*/ { 0xab, 0x0000, 0xffff, "Apple IIgs BASIC program" }, - /*TDF*/ { 0xac, 0x0000, 0xffff, "Apple IIgs BASIC TDF" }, - /*BDF*/ { 0xad, 0x0000, 0xffff, "Apple IIgs BASIC data" }, - /*SRC*/ { 0xb0, 0x0000, 0xffff, "Apple IIgs source code" }, - /* */ { 0xb0, 0x0001, 0x0001, "APW Text file" }, - /* */ { 0xb0, 0x0003, 0x0003, "APW 65816 Assembly source code" }, - /* */ { 0xb0, 0x0005, 0x0005, "ORCA/Pascal source code" }, - /* */ { 0xb0, 0x0006, 0x0006, "APW command file" }, - /* */ { 0xb0, 0x0008, 0x0008, "ORCA/C source code" }, - /* */ { 0xb0, 0x0009, 0x0009, "APW Linker command file" }, - /* */ { 0xb0, 0x000a, 0x000a, "APW C source code" }, - /* */ { 0xb0, 0x000c, 0x000c, "ORCA/Desktop command file" }, - /* */ { 0xb0, 0x0015, 0x0015, "APW Rez source file" }, - /* */ { 0xb0, 0x0017, 0x0017, "Installer script" }, - /* */ { 0xb0, 0x001e, 0x001e, "TML Pascal source code" }, - /* */ { 0xb0, 0x0116, 0x0116, "ORCA/Disassembler script" }, - /* */ { 0xb0, 0x0503, 0x0503, "SDE Assembler source code" }, - /* */ { 0xb0, 0x0506, 0x0506, "SDE command script" }, - /* */ { 0xb0, 0x0601, 0x0601, "Nifty List data" }, - /* */ { 0xb0, 0x0719, 0x0719, "PostScript file" }, - /*OBJ*/ { 0xb1, 0x0000, 0xffff, "Apple IIgs object code" }, - /*LIB*/ { 0xb2, 0x0000, 0xffff, "Apple IIgs Library file" }, - /*S16*/ { 0xb3, 0x0000, 0xffff, "GS/OS application" }, - /*RTL*/ { 0xb4, 0x0000, 0xffff, "GS/OS run-time library" }, - /*EXE*/ { 0xb5, 0x0000, 0xffff, "GS/OS shell application" }, - /*PIF*/ { 0xb6, 0x0000, 0xffff, "Permanent initialization file" }, - /*TIF*/ { 0xb7, 0x0000, 0xffff, "Temporary initialization file" }, - /*NDA*/ { 0xb8, 0x0000, 0xffff, "New desk accessory" }, - /*CDA*/ { 0xb9, 0x0000, 0xffff, "Classic desk accessory" }, - /*TOL*/ { 0xba, 0x0000, 0xffff, "Tool" }, - /*DVR*/ { 0xbb, 0x0000, 0xffff, "Apple IIgs device driver file" }, - /* */ { 0xbb, 0x7e01, 0x7e01, "GNO/ME terminal device driver" }, - /* */ { 0xbb, 0x7f01, 0x7f01, "GTv videodisc serial driver" }, - /* */ { 0xbb, 0x7f02, 0x7f02, "GTv videodisc game port driver" }, - /*LDF*/ { 0xbc, 0x0000, 0xffff, "Load file (generic)" }, - /* */ { 0xbc, 0x4001, 0x4001, "Nifty List module" }, - /* */ { 0xbc, 0xc001, 0xc001, "Nifty List module" }, - /* */ { 0xbc, 0x4002, 0x4002, "Super Info module" }, - /* */ { 0xbc, 0xc002, 0xc002, "Super Info module" }, - /* */ { 0xbc, 0x4004, 0x4004, "Twilight document" }, - /* */ { 0xbc, 0xc004, 0xc004, "Twilight document" }, - /* */ { 0xbc, 0x4006, 0x4006, "Foundation resource editor" }, - /* */ { 0xbc, 0xc006, 0xc006, "Foundation resource editor" }, - /* */ { 0xbc, 0x4007, 0x4007, "HyperStudio new button action" }, - /* */ { 0xbc, 0xc007, 0xc007, "HyperStudio new button action" }, - /* */ { 0xbc, 0x4008, 0x4008, "HyperStudio screen transition" }, - /* */ { 0xbc, 0xc008, 0xc008, "HyperStudio screen transition" }, - /* */ { 0xbc, 0x4009, 0x4009, "DreamGrafix module" }, - /* */ { 0xbc, 0xc009, 0xc009, "DreamGrafix module" }, - /* */ { 0xbc, 0x400a, 0x400a, "HyperStudio Extra utility" }, - /* */ { 0xbc, 0xc00a, 0xc00a, "HyperStudio Extra utility" }, - /* */ { 0xbc, 0x400f, 0x400f, "HardPressed module" }, - /* */ { 0xbc, 0xc00f, 0xc00f, "HardPressed module" }, - /* */ { 0xbc, 0x4010, 0x4010, "Graphic Exchange translator" }, - /* */ { 0xbc, 0xc010, 0xc010, "Graphic Exchange translator" }, - /* */ { 0xbc, 0x4011, 0x4011, "Desktop Enhancer blanker" }, - /* */ { 0xbc, 0xc011, 0xc011, "Desktop Enhancer blanker" }, - /* */ { 0xbc, 0x4083, 0x4083, "Marinetti link layer module" }, - /* */ { 0xbc, 0xc083, 0xc083, "Marinetti link layer module" }, - /*FST*/ { 0xbd, 0x0000, 0xffff, "GS/OS File System Translator" }, - /*DOC*/ { 0xbf, 0x0000, 0xffff, "GS/OS document" }, - /*PNT*/ { 0xc0, 0x0000, 0xffff, "Packed super hi-res picture" }, - /* */ { 0xc0, 0x0000, 0x0000, "Paintworks packed picture" }, - /* */ { 0xc0, 0x0001, 0x0001, "Packed super hi-res image" }, - /* */ { 0xc0, 0x0002, 0x0002, "Apple Preferred Format picture" }, - /* */ { 0xc0, 0x0003, 0x0003, "Packed QuickDraw II PICT file" }, - /* */ { 0xc0, 0x0080, 0x0080, "TIFF document" }, - /* */ { 0xc0, 0x0081, 0x0081, "JFIF (JPEG) document" }, - /* */ { 0xc0, 0x8001, 0x8001, "GTv background image" }, - /* */ { 0xc0, 0x8005, 0x8005, "DreamGrafix document" }, - /* */ { 0xc0, 0x8006, 0x8006, "GIF document" }, - /*PIC*/ { 0xc1, 0x0000, 0xffff, "Super hi-res picture" }, - /* */ { 0xc1, 0x0000, 0x0000, "Super hi-res screen image" }, - /* */ { 0xc1, 0x0001, 0x0001, "QuickDraw PICT file" }, - /* */ { 0xc1, 0x0002, 0x0002, "Super hi-res 3200-color screen image" }, - /* */ { 0xc1, 0x8001, 0x8001, "Allison raw image doc" }, - /* */ { 0xc1, 0x8002, 0x8002, "ThunderScan image doc" }, - /* */ { 0xc1, 0x8003, 0x8003, "DreamGrafix document" }, - /*ANI*/ { 0xc2, 0x0000, 0xffff, "Paintworks animation" }, - /*PAL*/ { 0xc3, 0x0000, 0xffff, "Paintworks palette" }, - /*OOG*/ { 0xc5, 0x0000, 0xffff, "Object-oriented graphics" }, - /* */ { 0xc5, 0x8000, 0x8000, "Draw Plus document" }, - /* */ { 0xc5, 0xc000, 0xc000, "DYOH architecture doc" }, - /* */ { 0xc5, 0xc001, 0xc001, "DYOH predrawn objects" }, - /* */ { 0xc5, 0xc002, 0xc002, "DYOH custom objects" }, - /* */ { 0xc5, 0xc003, 0xc003, "DYOH clipboard" }, - /* */ { 0xc5, 0xc004, 0xc004, "DYOH interiors document" }, - /* */ { 0xc5, 0xc005, 0xc005, "DYOH patterns" }, - /* */ { 0xc5, 0xc006, 0xc006, "DYOH landscape document" }, - /* */ { 0xc5, 0xc007, 0xc007, "PyWare Document" }, - /*SCR*/ { 0xc6, 0x0000, 0xffff, "Script" }, - /* */ { 0xc6, 0x8001, 0x8001, "Davex 8 script" }, - /* */ { 0xc6, 0x8002, 0x8002, "Universe Master backup script" }, - /* */ { 0xc6, 0x8003, 0x8003, "Universe Master Chain script" }, - /*CDV*/ { 0xc7, 0x0000, 0xffff, "Control Panel document" }, - /*FON*/ { 0xc8, 0x0000, 0xffff, "Font" }, - /* */ { 0xc8, 0x0000, 0x0000, "Font (Standard Apple IIgs QuickDraw II Font)" }, - /* */ { 0xc8, 0x0001, 0x0001, "TrueType font resource" }, - /* */ { 0xc8, 0x0008, 0x0008, "Postscript font resource" }, - /* */ { 0xc8, 0x0081, 0x0081, "TrueType font file" }, - /* */ { 0xc8, 0x0088, 0x0088, "Postscript font file" }, - /*FND*/ { 0xc9, 0x0000, 0xffff, "Finder data" }, - /*ICN*/ { 0xca, 0x0000, 0xffff, "Icons" }, - /*MUS*/ { 0xd5, 0x0000, 0xffff, "Music sequence" }, - /* */ { 0xd5, 0x0000, 0x0000, "Music Construction Set song" }, - /* */ { 0xd5, 0x0001, 0x0001, "MIDI Synth sequence" }, - /* */ { 0xd5, 0x0007, 0x0007, "SoundSmith document" }, - /* */ { 0xd5, 0x8002, 0x8002, "Diversi-Tune sequence" }, - /* */ { 0xd5, 0x8003, 0x8003, "Master Tracks Jr. sequence" }, - /* */ { 0xd5, 0x8004, 0x8004, "Music Writer document" }, - /* */ { 0xd5, 0x8005, 0x8005, "Arcade King Super music" }, - /* */ { 0xd5, 0x8006, 0x8006, "Music Composer file" }, - /*INS*/ { 0xd6, 0x0000, 0xffff, "Instrument" }, - /* */ { 0xd6, 0x0000, 0x0000, "Music Construction Set instrument" }, - /* */ { 0xd6, 0x0001, 0x0001, "MIDI Synth instrument" }, - /* */ { 0xd6, 0x8002, 0x8002, "Diversi-Tune instrument" }, - /*MDI*/ { 0xd7, 0x0000, 0xffff, "MIDI data" }, - /* */ { 0xd7, 0x0000, 0x0000, "MIDI standard data" }, - /* */ { 0xd7, 0x0080, 0x0080, "MIDI System Exclusive data" }, - /* */ { 0xd7, 0x8001, 0x8001, "MasterTracks Pro Sysex file" }, - /*SND*/ { 0xd8, 0x0000, 0xffff, "Sampled sound" }, - /* */ { 0xd8, 0x0000, 0x0000, "Audio IFF document" }, - /* */ { 0xd8, 0x0001, 0x0001, "AIFF-C document" }, - /* */ { 0xd8, 0x0002, 0x0002, "ASIF instrument" }, - /* */ { 0xd8, 0x0003, 0x0003, "Sound resource file" }, - /* */ { 0xd8, 0x0004, 0x0004, "MIDI Synth wave data" }, - /* */ { 0xd8, 0x8001, 0x8001, "HyperStudio sound" }, - /* */ { 0xd8, 0x8002, 0x8002, "Arcade King Super sound" }, - /* */ { 0xd8, 0x8003, 0x8003, "SoundOff! sound bank" }, - /*DBM*/ { 0xdb, 0x0000, 0xffff, "DB Master document" }, - /* */ { 0xdb, 0x0001, 0x0001, "DB Master document" }, - /*???*/ { 0xdd, 0x0000, 0xffff, "DDD Deluxe archive" }, // unofficial - /*LBR*/ { 0xe0, 0x0000, 0xffff, "Archival library" }, - /* */ { 0xe0, 0x0000, 0x0000, "ALU library" }, - /* */ { 0xe0, 0x0001, 0x0001, "AppleSingle file" }, - /* */ { 0xe0, 0x0002, 0x0002, "AppleDouble header file" }, - /* */ { 0xe0, 0x0003, 0x0003, "AppleDouble data file" }, - /* */ { 0xe0, 0x0004, 0x0004, "Archiver archive" }, - /* */ { 0xe0, 0x0005, 0x0005, "DiskCopy 4.2 disk image" }, - /* */ { 0xe0, 0x0100, 0x0100, "Apple 5.25 disk image" }, - /* */ { 0xe0, 0x0101, 0x0101, "Profile 5MB disk image" }, - /* */ { 0xe0, 0x0102, 0x0102, "Profile 10MB disk image" }, - /* */ { 0xe0, 0x0103, 0x0103, "Apple 3.5 disk image" }, - /* */ { 0xe0, 0x0104, 0x0104, "SCSI device image" }, - /* */ { 0xe0, 0x0105, 0x0105, "SCSI hard disk image" }, - /* */ { 0xe0, 0x0106, 0x0106, "SCSI tape image" }, - /* */ { 0xe0, 0x0107, 0x0107, "SCSI CD-ROM image" }, - /* */ { 0xe0, 0x010e, 0x010e, "RAM disk image" }, - /* */ { 0xe0, 0x010f, 0x010f, "ROM disk image" }, - /* */ { 0xe0, 0x0110, 0x0110, "File server image" }, - /* */ { 0xe0, 0x0113, 0x0113, "Hard disk image" }, - /* */ { 0xe0, 0x0114, 0x0114, "Floppy disk image" }, - /* */ { 0xe0, 0x0115, 0x0115, "Tape image" }, - /* */ { 0xe0, 0x011e, 0x011e, "AppleTalk file server image" }, - /* */ { 0xe0, 0x0120, 0x0120, "DiskCopy 6 disk image" }, - /* */ { 0xe0, 0x0130, 0x0130, "Universal Disk Image file" }, - /* */ { 0xe0, 0x8000, 0x8000, "Binary II file" }, - /* */ { 0xe0, 0x8001, 0x8001, "AppleLink ACU document" }, - /* */ { 0xe0, 0x8002, 0x8002, "ShrinkIt (NuFX) document" }, - /* */ { 0xe0, 0x8003, 0x8003, "Universal Disk Image file" }, - /* */ { 0xe0, 0x8004, 0x8004, "Davex archived volume" }, - /* */ { 0xe0, 0x8006, 0x8006, "EZ Backup Saveset doc" }, - /* */ { 0xe0, 0x8007, 0x8007, "ELS DOS 3.3 volume" }, - /* */ { 0xe0, 0x8008, 0x8008, "UtilityWorks document" }, - /* */ { 0xe0, 0x800a, 0x800a, "Replicator document" }, - /* */ { 0xe0, 0x800b, 0x800b, "AutoArk compressed document" }, - /* */ { 0xe0, 0x800d, 0x800d, "HardPressed compressed data (data fork)" }, - /* */ { 0xe0, 0x800e, 0x800e, "HardPressed compressed data (rsrc fork)" }, - /* */ { 0xe0, 0x800f, 0x800f, "HardPressed compressed data (both forks)" }, - /* */ { 0xe0, 0x8010, 0x8010, "LHA archive" }, - /*ATK*/ { 0xe2, 0x0000, 0xffff, "AppleTalk data" }, - /* */ { 0xe2, 0xffff, 0xffff, "EasyMount document" }, - /*R16*/ { 0xee, 0x0000, 0xffff, "EDASM 816 relocatable file" }, - /*PAS*/ { 0xef, 0x0000, 0xffff, "Pascal area" }, - /*CMD*/ { 0xf0, 0x0000, 0xffff, "BASIC command" }, - /*???*/ { 0xf1, 0x0000, 0xffff, "User type #1" }, - /*???*/ { 0xf2, 0x0000, 0xffff, "User type #2" }, - /*???*/ { 0xf3, 0x0000, 0xffff, "User type #3" }, - /*???*/ { 0xf4, 0x0000, 0xffff, "User type #4" }, - /*???*/ { 0xf5, 0x0000, 0xffff, "User type #5" }, - /*???*/ { 0xf6, 0x0000, 0xffff, "User type #6" }, - /*???*/ { 0xf7, 0x0000, 0xffff, "User type #7" }, - /*???*/ { 0xf8, 0x0000, 0xffff, "User type #8" }, - /*OS */ { 0xf9, 0x0000, 0xffff, "GS/OS system file" }, - /*OS */ { 0xfa, 0x0000, 0xffff, "Integer BASIC program" }, - /*OS */ { 0xfb, 0x0000, 0xffff, "Integer BASIC variables" }, - /*OS */ { 0xfc, 0x0000, 0xffff, "AppleSoft BASIC program" }, - /*OS */ { 0xfd, 0x0000, 0xffff, "AppleSoft BASIC variables" }, - /*OS */ { 0xfe, 0x0000, 0xffff, "Relocatable code" }, - /*OS */ { 0xff, 0x0000, 0xffff, "ProDOS 8 application" }, + /*NON*/ { 0x00, 0x0000, 0xffff, L"Untyped file" }, + /*BAD*/ { 0x01, 0x0000, 0xffff, L"Bad blocks" }, + /*PCD*/ { 0x02, 0x0000, 0xffff, L"Pascal code" }, + /*PTX*/ { 0x03, 0x0000, 0xffff, L"Pascal text" }, + /*TXT*/ { 0x04, 0x0000, 0xffff, L"ASCII text" }, + /*PDA*/ { 0x05, 0x0000, 0xffff, L"Pascal data" }, + /*BIN*/ { 0x06, 0x0000, 0xffff, L"Binary" }, + /*FNT*/ { 0x07, 0x0000, 0xffff, L"Apple /// font" }, + /*FOT*/ { 0x08, 0x0000, 0xffff, L"Apple II or /// graphics" }, + /* */ { 0x08, 0x0000, 0x3fff, L"Apple II graphics" }, + /* */ { 0x08, 0x4000, 0x4000, L"Packed hi-res image" }, + /* */ { 0x08, 0x4001, 0x4001, L"Packed double hi-res image" }, + /* */ { 0x08, 0x8001, 0x8001, L"Printographer packed HGR file" }, + /* */ { 0x08, 0x8002, 0x8002, L"Printographer packed DHGR file" }, + /* */ { 0x08, 0x8003, 0x8003, L"Softdisk hi-res image" }, + /* */ { 0x08, 0x8004, 0x8004, L"Softdisk double hi-res image" }, + /*BA3*/ { 0x09, 0x0000, 0xffff, L"Apple /// BASIC program" }, + /*DA3*/ { 0x0a, 0x0000, 0xffff, L"Apple /// BASIC data" }, + /*WPF*/ { 0x0b, 0x0000, 0xffff, L"Apple II or /// word processor" }, + /* */ { 0x0b, 0x8001, 0x8001, L"Write This Way document" }, + /* */ { 0x0b, 0x8002, 0x8002, L"Writing & Publishing document" }, + /*SOS*/ { 0x0c, 0x0000, 0xffff, L"Apple /// SOS system" }, + /*DIR*/ { 0x0f, 0x0000, 0xffff, L"Folder" }, + /*RPD*/ { 0x10, 0x0000, 0xffff, L"Apple /// RPS data" }, + /*RPI*/ { 0x11, 0x0000, 0xffff, L"Apple /// RPS index" }, + /*AFD*/ { 0x12, 0x0000, 0xffff, L"Apple /// AppleFile discard" }, + /*AFM*/ { 0x13, 0x0000, 0xffff, L"Apple /// AppleFile model" }, + /*AFR*/ { 0x14, 0x0000, 0xffff, L"Apple /// AppleFile report format" }, + /*SCL*/ { 0x15, 0x0000, 0xffff, L"Apple /// screen library" }, + /*PFS*/ { 0x16, 0x0000, 0xffff, L"PFS document" }, + /* */ { 0x16, 0x0001, 0x0001, L"PFS:File document" }, + /* */ { 0x16, 0x0002, 0x0002, L"PFS:Write document" }, + /* */ { 0x16, 0x0003, 0x0003, L"PFS:Graph document" }, + /* */ { 0x16, 0x0004, 0x0004, L"PFS:Plan document" }, + /* */ { 0x16, 0x0016, 0x0016, L"PFS internal data" }, + /*ADB*/ { 0x19, 0x0000, 0xffff, L"AppleWorks data base" }, + /*AWP*/ { 0x1a, 0x0000, 0xffff, L"AppleWorks word processor" }, + /*ASP*/ { 0x1b, 0x0000, 0xffff, L"AppleWorks spreadsheet" }, + /*TDM*/ { 0x20, 0x0000, 0xffff, L"Desktop Manager document" }, + /*???*/ { 0x21, 0x0000, 0xffff, L"Instant Pascal source" }, + /*???*/ { 0x22, 0x0000, 0xffff, L"UCSD Pascal volume" }, + /*???*/ { 0x29, 0x0000, 0xffff, L"Apple /// SOS dictionary" }, + /*8SC*/ { 0x2a, 0x0000, 0xffff, L"Apple II source code" }, + /* */ { 0x2a, 0x8001, 0x8001, L"EBBS command script" }, + /*8OB*/ { 0x2b, 0x0000, 0xffff, L"Apple II object code" }, + /* */ { 0x2b, 0x8001, 0x8001, L"GBBS Pro object Code" }, + /*8IC*/ { 0x2c, 0x0000, 0xffff, L"Apple II interpreted code" }, + /* */ { 0x2c, 0x8003, 0x8003, L"APEX Program File" }, + /* */ { 0x2c, 0x8005, 0x8005, L"EBBS tokenized command script" }, + /*8LD*/ { 0x2d, 0x0000, 0xffff, L"Apple II language data" }, + /* */ { 0x2d, 0x8006, 0x8005, L"EBBS message bundle" }, + /* */ { 0x2d, 0x8007, 0x8007, L"EBBS compressed message bundle" }, + /*P8C*/ { 0x2e, 0x0000, 0xffff, L"ProDOS 8 code module" }, + /* */ { 0x2e, 0x8001, 0x8001, L"Davex 8 Command" }, + /*PTP*/ { 0x2e, 0x8002, 0x8002, L"Point-to-Point drivers" }, + /*PTP*/ { 0x2e, 0x8003, 0x8003, L"Point-to-Point code" }, + /* */ { 0x2e, 0x8004, 0x8004, L"Softdisk printer driver" }, + /*DIC*/ { 0x40, 0x0000, 0xffff, L"Dictionary file" }, + /*???*/ { 0x41, 0x0000, 0xffff, L"OCR data" }, + /* */ { 0x41, 0x8001, 0x8001, L"InWords OCR font table" }, + /*FTD*/ { 0x42, 0x0000, 0xffff, L"File type names" }, + /*???*/ { 0x43, 0x0000, 0xffff, L"Peripheral data" }, + /* */ { 0x43, 0x8001, 0x8001, L"Express document" }, + /*???*/ { 0x44, 0x0000, 0xffff, L"Personal information" }, + /* */ { 0x44, 0x8001, 0x8001, L"ResuMaker personal information" }, + /* */ { 0x44, 0x8002, 0x8002, L"ResuMaker resume" }, + /* */ { 0x44, 0x8003, 0x8003, L"II Notes document" }, + /* */ { 0x44, 0x8004, 0x8004, L"Softdisk scrapbook document" }, + /* */ { 0x44, 0x8005, 0x8005, L"Don't Forget document" }, + /* */ { 0x44, 0x80ff, 0x80ff, L"What To Do data" }, + /* */ { 0x44, 0xbeef, 0xbeef, L"Table Scraps scrapbook" }, + /*???*/ { 0x45, 0x0000, 0xffff, L"Mathematical document" }, + /* */ { 0x45, 0x8001, 0x8001, L"GSymbolix 3D graph document" }, + /* */ { 0x45, 0x8002, 0x8002, L"GSymbolix formula document" }, + /*???*/ { 0x46, 0x0000, 0xffff, L"AutoSave profiles" }, + /* */ { 0x46, 0x8001, 0x8001, L"AutoSave profiles" }, + /*GWP*/ { 0x50, 0x0000, 0xffff, L"Apple IIgs Word Processor" }, + /* */ { 0x50, 0x8001, 0x8001, L"DeluxeWrite document" }, + /* */ { 0x50, 0x8003, 0x8003, L"Personal Journal document" }, + /* */ { 0x50, 0x8010, 0x8010, L"AppleWorks GS word processor" }, + /* */ { 0x50, 0x8011, 0x8011, L"Softdisk issue text" }, + /* */ { 0x50, 0x5445, 0x5445, L"Teach document" }, + /*GSS*/ { 0x51, 0x0000, 0xffff, L"Apple IIgs spreadsheet" }, + /* */ { 0x51, 0x8010, 0x8010, L"AppleWorks GS spreadsheet" }, + /* */ { 0x51, 0x2358, 0x2358, L"QC Calc spreadsheet " }, + /*GDB*/ { 0x52, 0x0000, 0xffff, L"Apple IIgs data base" }, + /* */ { 0x52, 0x8001, 0x8001, L"GTv database" }, + /* */ { 0x52, 0x8010, 0x8010, L"AppleWorks GS data base" }, + /* */ { 0x52, 0x8011, 0x8011, L"AppleWorks GS DB template" }, + /* */ { 0x52, 0x8013, 0x8013, L"GSAS database" }, + /* */ { 0x52, 0x8014, 0x8014, L"GSAS accounting journals" }, + /* */ { 0x52, 0x8015, 0x8015, L"Address Manager document" }, + /* */ { 0x52, 0x8016, 0x8016, L"Address Manager defaults" }, + /* */ { 0x52, 0x8017, 0x8017, L"Address Manager index" }, + /*DRW*/ { 0x53, 0x0000, 0xffff, L"Drawing" }, + /* */ { 0x53, 0x8002, 0x8002, L"Graphic Disk Labeler document" }, + /* */ { 0x53, 0x8010, 0x8010, L"AppleWorks GS graphics" }, + /*GDP*/ { 0x54, 0x0000, 0xffff, L"Desktop publishing" }, + /* */ { 0x54, 0x8002, 0x8002, L"GraphicWriter document" }, + /* */ { 0x54, 0x8003, 0x8003, L"Label It document" }, + /* */ { 0x54, 0x8010, 0x8010, L"AppleWorks GS Page Layout" }, + /* */ { 0x54, 0xdd3e, 0xdd3e, L"Medley document" }, + /*HMD*/ { 0x55, 0x0000, 0xffff, L"Hypermedia" }, + /* */ { 0x55, 0x0001, 0x0001, L"HyperCard IIgs stack" }, + /* */ { 0x55, 0x8001, 0x8001, L"Tutor-Tech document" }, + /* */ { 0x55, 0x8002, 0x8002, L"HyperStudio document" }, + /* */ { 0x55, 0x8003, 0x8003, L"Nexus document" }, + /* */ { 0x55, 0x8004, 0x8004, L"HyperSoft stack" }, + /* */ { 0x55, 0x8005, 0x8005, L"HyperSoft card" }, + /* */ { 0x55, 0x8006, 0x8006, L"HyperSoft external command" }, + /*EDU*/ { 0x56, 0x0000, 0xffff, L"Educational Data" }, + /* */ { 0x56, 0x8001, 0x8001, L"Tutor-Tech scores" }, + /* */ { 0x56, 0x8007, 0x8007, L"GradeBook data" }, + /*STN*/ { 0x57, 0x0000, 0xffff, L"Stationery" }, + /* */ { 0x57, 0x8003, 0x8003, L"Music Writer format" }, + /*HLP*/ { 0x58, 0x0000, 0xffff, L"Help file" }, + /* */ { 0x58, 0x8002, 0x8002, L"Davex 8 help file" }, + /* */ { 0x58, 0x8005, 0x8005, L"Micol Advanced Basic help file" }, + /* */ { 0x58, 0x8006, 0x8006, L"Locator help document" }, + /* */ { 0x58, 0x8007, 0x8007, L"Personal Journal help" }, + /* */ { 0x58, 0x8008, 0x8008, L"Home Refinancer help" }, + /* */ { 0x58, 0x8009, 0x8009, L"The Optimizer help" }, + /* */ { 0x58, 0x800a, 0x800a, L"Text Wizard help" }, + /* */ { 0x58, 0x800b, 0x800b, L"WordWorks Pro help system" }, + /* */ { 0x58, 0x800c, 0x800c, L"Sound Wizard help" }, + /* */ { 0x58, 0x800d, 0x800d, L"SeeHear help system" }, + /* */ { 0x58, 0x800e, 0x800e, L"QuickForms help system" }, + /* */ { 0x58, 0x800f, 0x800f, L"Don't Forget help system" }, + /*COM*/ { 0x59, 0x0000, 0xffff, L"Communications file" }, + /* */ { 0x59, 0x8002, 0x8002, L"AppleWorks GS communications" }, + /*CFG*/ { 0x5a, 0x0000, 0xffff, L"Configuration file" }, + /* */ { 0x5a, 0x0000, 0x0000, L"Sound settings files" }, + /* */ { 0x5a, 0x0002, 0x0002, L"Battery RAM configuration" }, + /* */ { 0x5a, 0x0003, 0x0003, L"AutoLaunch preferences" }, + /* */ { 0x5a, 0x0004, 0x0004, L"SetStart preferences" }, + /* */ { 0x5a, 0x0005, 0x0005, L"GSBug configuration" }, + /* */ { 0x5a, 0x0006, 0x0006, L"Archiver preferences" }, + /* */ { 0x5a, 0x0007, 0x0007, L"Archiver table of contents" }, + /* */ { 0x5a, 0x0008, 0x0008, L"Font Manager data" }, + /* */ { 0x5a, 0x0009, 0x0009, L"Print Manager data" }, + /* */ { 0x5a, 0x000a, 0x000a, L"IR preferences" }, + /* */ { 0x5a, 0x8001, 0x8001, L"Master Tracks Jr. preferences" }, + /* */ { 0x5a, 0x8002, 0x8002, L"GraphicWriter preferences" }, + /* */ { 0x5a, 0x8003, 0x8003, L"Z-Link configuration" }, + /* */ { 0x5a, 0x8004, 0x8004, L"JumpStart configuration" }, + /* */ { 0x5a, 0x8005, 0x8005, L"Davex 8 configuration" }, + /* */ { 0x5a, 0x8006, 0x8006, L"Nifty List configuration" }, + /* */ { 0x5a, 0x8007, 0x8007, L"GTv videodisc configuration" }, + /* */ { 0x5a, 0x8008, 0x8008, L"GTv Workshop configuration" }, + /*PTP*/ { 0x5a, 0x8009, 0x8009, L"Point-to-Point preferences" }, + /* */ { 0x5a, 0x800a, 0x800a, L"ORCA/Disassembler preferences" }, + /* */ { 0x5a, 0x800b, 0x800b, L"SnowTerm preferences" }, + /* */ { 0x5a, 0x800c, 0x800c, L"My Word! preferences" }, + /* */ { 0x5a, 0x800d, 0x800d, L"Chipmunk configuration" }, + /* */ { 0x5a, 0x8010, 0x8010, L"AppleWorks GS configuration" }, + /* */ { 0x5a, 0x8011, 0x8011, L"SDE Shell preferences" }, + /* */ { 0x5a, 0x8012, 0x8012, L"SDE Editor preferences" }, + /* */ { 0x5a, 0x8013, 0x8013, L"SDE system tab ruler" }, + /* */ { 0x5a, 0x8014, 0x8014, L"Nexus configuration" }, + /* */ { 0x5a, 0x8015, 0x8015, L"DesignMaster preferences" }, + /* */ { 0x5a, 0x801a, 0x801a, L"MAX/Edit keyboard template" }, + /* */ { 0x5a, 0x801b, 0x801b, L"MAX/Edit tab ruler set" }, + /* */ { 0x5a, 0x801c, 0x801c, L"Platinum Paint preferences" }, + /* */ { 0x5a, 0x801d, 0x801d, L"Sea Scan 1000" }, + /* */ { 0x5a, 0x801e, 0x801e, L"Allison preferences" }, + /* */ { 0x5a, 0x801f, 0x801f, L"Gold of the Americas options" }, + /* */ { 0x5a, 0x8021, 0x8021, L"GSAS accounting setup" }, + /* */ { 0x5a, 0x8022, 0x8022, L"GSAS accounting document" }, + /* */ { 0x5a, 0x8023, 0x8023, L"UtilityLaunch preferences" }, + /* */ { 0x5a, 0x8024, 0x8024, L"Softdisk configuration" }, + /* */ { 0x5a, 0x8025, 0x8025, L"Quit-To configuration" }, + /* */ { 0x5a, 0x8026, 0x8026, L"Big Edit Thing" }, + /* */ { 0x5a, 0x8027, 0x8027, L"ZMaker preferences" }, + /* */ { 0x5a, 0x8028, 0x8028, L"Minstrel configuration" }, + /* */ { 0x5a, 0x8029, 0x8029, L"WordWorks Pro preferences" }, + /* */ { 0x5a, 0x802b, 0x802b, L"Pointless preferences" }, + /* */ { 0x5a, 0x802c, 0x802c, L"Micol Advanced Basic config" }, + /* */ { 0x5a, 0x802e, 0x802e, L"Label It configuration" }, + /* */ { 0x5a, 0x802f, 0x802f, L"Cool Cursor document" }, + /* */ { 0x5a, 0x8030, 0x8030, L"Locator preferences" }, + /* */ { 0x5a, 0x8031, 0x8031, L"Replicator preferences" }, + /* */ { 0x5a, 0x8032, 0x8032, L"Kangaroo configuration" }, + /* */ { 0x5a, 0x8033, 0x8033, L"Kangaroo data" }, + /* */ { 0x5a, 0x8034, 0x8034, L"TransProg III configuration" }, + /* */ { 0x5a, 0x8035, 0x8035, L"Home Refinancer preferences" }, + /* */ { 0x5a, 0x8036, 0x8036, L"Easy Eyes settings" }, + /* */ { 0x5a, 0x8037, 0x8037, L"The Optimizer settings" }, + /* */ { 0x5a, 0x8038, 0x8038, L"Text Wizard settings" }, + /* */ { 0x5a, 0x803b, 0x803b, L"Disk Access II preferences" }, + /* */ { 0x5a, 0x803d, 0x803d, L"Quick DA configuration" }, + /* */ { 0x5a, 0x803e, 0x803e, L"Crazy 8s preferences" }, + /* */ { 0x5a, 0x803f, 0x803f, L"Sound Wizard settings" }, + /* */ { 0x5a, 0x8041, 0x8041, L"Quick Window configuration" }, + /* */ { 0x5a, 0x8044, 0x8044, L"Universe Master disk map" }, + /* */ { 0x5a, 0x8046, 0x8046, L"Autopilot configuration" }, + /* */ { 0x5a, 0x8047, 0x8047, L"EGOed preferences" }, + /* */ { 0x5a, 0x8049, 0x8049, L"Quick DA preferences" }, + /* */ { 0x5a, 0x804b, 0x804b, L"HardPressed volume preferences" }, + /* */ { 0x5a, 0x804c, 0x804c, L"HardPressed global preferences" }, + /* */ { 0x5a, 0x804d, 0x804d, L"HardPressed profile" }, + /* */ { 0x5a, 0x8050, 0x8050, L"Don't Forget settings" }, + /* */ { 0x5a, 0x8052, 0x8052, L"ProBOOT preferences" }, + /* */ { 0x5a, 0x8054, 0x8054, L"Battery Brain preferences" }, + /* */ { 0x5a, 0x8055, 0x8055, L"Rainbow configuration" }, + /* */ { 0x5a, 0x8061, 0x8061, L"TypeSet preferences" }, + /* */ { 0x5a, 0x8063, 0x8063, L"Cool Cursor preferences" }, + /* */ { 0x5a, 0x806e, 0x806e, L"Balloon preferences" }, + /* */ { 0x5a, 0x80fe, 0x80fe, L"Special Edition configuration" }, + /* */ { 0x5a, 0x80ff, 0x80ff, L"Sun Dial preferences" }, + /*ANM*/ { 0x5b, 0x0000, 0xffff, L"Animation file" }, + /* */ { 0x5b, 0x8001, 0x8001, L"Cartooners movie" }, + /* */ { 0x5b, 0x8002, 0x8002, L"Cartooners actors" }, + /* */ { 0x5b, 0x8005, 0x8005, L"Arcade King Super document" }, + /* */ { 0x5b, 0x8006, 0x8006, L"Arcade King DHRG document" }, + /* */ { 0x5b, 0x8007, 0x8007, L"DreamVision movie" }, + /*MUM*/ { 0x5c, 0x0000, 0xffff, L"Multimedia document" }, + /* */ { 0x5c, 0x8001, 0x8001, L"GTv multimedia playlist" }, + /*ENT*/ { 0x5d, 0x0000, 0xffff, L"Game/Entertainment document" }, + /* */ { 0x5d, 0x8001, 0x8001, L"Solitaire Royale document" }, + /* */ { 0x5d, 0x8002, 0x8002, L"BattleFront scenario" }, + /* */ { 0x5d, 0x8003, 0x8003, L"BattleFront saved game" }, + /* */ { 0x5d, 0x8004, 0x8004, L"Gold of the Americas game" }, + /* */ { 0x5d, 0x8006, 0x8006, L"Blackjack Tutor document" }, + /* */ { 0x5d, 0x8008, 0x8008, L"Canasta document" }, + /* */ { 0x5d, 0x800b, 0x800b, L"Word Search document" }, + /* */ { 0x5d, 0x800c, 0x800c, L"Tarot deal" }, + /* */ { 0x5d, 0x800d, 0x800d, L"Tarot tournament" }, + /* */ { 0x5d, 0x800e, 0x800e, L"Full Metal Planet game" }, + /* */ { 0x5d, 0x800f, 0x800f, L"Full Metal Planet player" }, + /* */ { 0x5d, 0x8010, 0x8010, L"Quizzical high scores" }, + /* */ { 0x5d, 0x8011, 0x8011, L"Meltdown high scores" }, + /* */ { 0x5d, 0x8012, 0x8012, L"BlockWords high scores" }, + /* */ { 0x5d, 0x8013, 0x8013, L"Lift-A-Gon scores" }, + /* */ { 0x5d, 0x8014, 0x8014, L"Softdisk Adventure" }, + /* */ { 0x5d, 0x8015, 0x8015, L"Blankety Blank document" }, + /* */ { 0x5d, 0x8016, 0x8016, L"Son of Star Axe champion" }, + /* */ { 0x5d, 0x8017, 0x8017, L"Digit Fidget high scores" }, + /* */ { 0x5d, 0x8018, 0x8018, L"Eddie map" }, + /* */ { 0x5d, 0x8019, 0x8019, L"Eddie tile set" }, + /* */ { 0x5d, 0x8122, 0x8122, L"Wolfenstein 3D scenario" }, + /* */ { 0x5d, 0x8123, 0x8123, L"Wolfenstein 3D saved game" }, + /*DVU*/ { 0x5e, 0x0000, 0xffff, L"Development utility document" }, + /* */ { 0x5e, 0x0001, 0x0001, L"Resource file" }, + /* */ { 0x5e, 0x8001, 0x8001, L"ORCA/Disassembler template" }, + /* */ { 0x5e, 0x8003, 0x8003, L"DesignMaster document" }, + /* */ { 0x5e, 0x8008, 0x8008, L"ORCA/C symbol file" }, + /*FIN*/ { 0x5f, 0x0000, 0xffff, L"Financial document" }, + /* */ { 0x5f, 0x8001, 0x8001, L"Your Money Matters document" }, + /* */ { 0x5f, 0x8002, 0x8002, L"Home Refinancer document" }, + /*BIO*/ { 0x6b, 0x0000, 0xffff, L"PC Transporter BIOS" }, + /*TDR*/ { 0x6d, 0x0000, 0xffff, L"PC Transporter driver" }, + /*PRE*/ { 0x6e, 0x0000, 0xffff, L"PC Transporter pre-boot" }, + /*HDV*/ { 0x6f, 0x0000, 0xffff, L"PC Transporter volume" }, + /*WP */ { 0xa0, 0x0000, 0xffff, L"WordPerfect document" }, + /*GSB*/ { 0xab, 0x0000, 0xffff, L"Apple IIgs BASIC program" }, + /*TDF*/ { 0xac, 0x0000, 0xffff, L"Apple IIgs BASIC TDF" }, + /*BDF*/ { 0xad, 0x0000, 0xffff, L"Apple IIgs BASIC data" }, + /*SRC*/ { 0xb0, 0x0000, 0xffff, L"Apple IIgs source code" }, + /* */ { 0xb0, 0x0001, 0x0001, L"APW Text file" }, + /* */ { 0xb0, 0x0003, 0x0003, L"APW 65816 Assembly source code" }, + /* */ { 0xb0, 0x0005, 0x0005, L"ORCA/Pascal source code" }, + /* */ { 0xb0, 0x0006, 0x0006, L"APW command file" }, + /* */ { 0xb0, 0x0008, 0x0008, L"ORCA/C source code" }, + /* */ { 0xb0, 0x0009, 0x0009, L"APW Linker command file" }, + /* */ { 0xb0, 0x000a, 0x000a, L"APW C source code" }, + /* */ { 0xb0, 0x000c, 0x000c, L"ORCA/Desktop command file" }, + /* */ { 0xb0, 0x0015, 0x0015, L"APW Rez source file" }, + /* */ { 0xb0, 0x0017, 0x0017, L"Installer script" }, + /* */ { 0xb0, 0x001e, 0x001e, L"TML Pascal source code" }, + /* */ { 0xb0, 0x0116, 0x0116, L"ORCA/Disassembler script" }, + /* */ { 0xb0, 0x0503, 0x0503, L"SDE Assembler source code" }, + /* */ { 0xb0, 0x0506, 0x0506, L"SDE command script" }, + /* */ { 0xb0, 0x0601, 0x0601, L"Nifty List data" }, + /* */ { 0xb0, 0x0719, 0x0719, L"PostScript file" }, + /*OBJ*/ { 0xb1, 0x0000, 0xffff, L"Apple IIgs object code" }, + /*LIB*/ { 0xb2, 0x0000, 0xffff, L"Apple IIgs Library file" }, + /*S16*/ { 0xb3, 0x0000, 0xffff, L"GS/OS application" }, + /*RTL*/ { 0xb4, 0x0000, 0xffff, L"GS/OS run-time library" }, + /*EXE*/ { 0xb5, 0x0000, 0xffff, L"GS/OS shell application" }, + /*PIF*/ { 0xb6, 0x0000, 0xffff, L"Permanent initialization file" }, + /*TIF*/ { 0xb7, 0x0000, 0xffff, L"Temporary initialization file" }, + /*NDA*/ { 0xb8, 0x0000, 0xffff, L"New desk accessory" }, + /*CDA*/ { 0xb9, 0x0000, 0xffff, L"Classic desk accessory" }, + /*TOL*/ { 0xba, 0x0000, 0xffff, L"Tool" }, + /*DVR*/ { 0xbb, 0x0000, 0xffff, L"Apple IIgs device driver file" }, + /* */ { 0xbb, 0x7e01, 0x7e01, L"GNO/ME terminal device driver" }, + /* */ { 0xbb, 0x7f01, 0x7f01, L"GTv videodisc serial driver" }, + /* */ { 0xbb, 0x7f02, 0x7f02, L"GTv videodisc game port driver" }, + /*LDF*/ { 0xbc, 0x0000, 0xffff, L"Load file (generic)" }, + /* */ { 0xbc, 0x4001, 0x4001, L"Nifty List module" }, + /* */ { 0xbc, 0xc001, 0xc001, L"Nifty List module" }, + /* */ { 0xbc, 0x4002, 0x4002, L"Super Info module" }, + /* */ { 0xbc, 0xc002, 0xc002, L"Super Info module" }, + /* */ { 0xbc, 0x4004, 0x4004, L"Twilight document" }, + /* */ { 0xbc, 0xc004, 0xc004, L"Twilight document" }, + /* */ { 0xbc, 0x4006, 0x4006, L"Foundation resource editor" }, + /* */ { 0xbc, 0xc006, 0xc006, L"Foundation resource editor" }, + /* */ { 0xbc, 0x4007, 0x4007, L"HyperStudio new button action" }, + /* */ { 0xbc, 0xc007, 0xc007, L"HyperStudio new button action" }, + /* */ { 0xbc, 0x4008, 0x4008, L"HyperStudio screen transition" }, + /* */ { 0xbc, 0xc008, 0xc008, L"HyperStudio screen transition" }, + /* */ { 0xbc, 0x4009, 0x4009, L"DreamGrafix module" }, + /* */ { 0xbc, 0xc009, 0xc009, L"DreamGrafix module" }, + /* */ { 0xbc, 0x400a, 0x400a, L"HyperStudio Extra utility" }, + /* */ { 0xbc, 0xc00a, 0xc00a, L"HyperStudio Extra utility" }, + /* */ { 0xbc, 0x400f, 0x400f, L"HardPressed module" }, + /* */ { 0xbc, 0xc00f, 0xc00f, L"HardPressed module" }, + /* */ { 0xbc, 0x4010, 0x4010, L"Graphic Exchange translator" }, + /* */ { 0xbc, 0xc010, 0xc010, L"Graphic Exchange translator" }, + /* */ { 0xbc, 0x4011, 0x4011, L"Desktop Enhancer blanker" }, + /* */ { 0xbc, 0xc011, 0xc011, L"Desktop Enhancer blanker" }, + /* */ { 0xbc, 0x4083, 0x4083, L"Marinetti link layer module" }, + /* */ { 0xbc, 0xc083, 0xc083, L"Marinetti link layer module" }, + /*FST*/ { 0xbd, 0x0000, 0xffff, L"GS/OS File System Translator" }, + /*DOC*/ { 0xbf, 0x0000, 0xffff, L"GS/OS document" }, + /*PNT*/ { 0xc0, 0x0000, 0xffff, L"Packed super hi-res picture" }, + /* */ { 0xc0, 0x0000, 0x0000, L"Paintworks packed picture" }, + /* */ { 0xc0, 0x0001, 0x0001, L"Packed super hi-res image" }, + /* */ { 0xc0, 0x0002, 0x0002, L"Apple Preferred Format picture" }, + /* */ { 0xc0, 0x0003, 0x0003, L"Packed QuickDraw II PICT file" }, + /* */ { 0xc0, 0x0080, 0x0080, L"TIFF document" }, + /* */ { 0xc0, 0x0081, 0x0081, L"JFIF (JPEG) document" }, + /* */ { 0xc0, 0x8001, 0x8001, L"GTv background image" }, + /* */ { 0xc0, 0x8005, 0x8005, L"DreamGrafix document" }, + /* */ { 0xc0, 0x8006, 0x8006, L"GIF document" }, + /*PIC*/ { 0xc1, 0x0000, 0xffff, L"Super hi-res picture" }, + /* */ { 0xc1, 0x0000, 0x0000, L"Super hi-res screen image" }, + /* */ { 0xc1, 0x0001, 0x0001, L"QuickDraw PICT file" }, + /* */ { 0xc1, 0x0002, 0x0002, L"Super hi-res 3200-color screen image" }, + /* */ { 0xc1, 0x8001, 0x8001, L"Allison raw image doc" }, + /* */ { 0xc1, 0x8002, 0x8002, L"ThunderScan image doc" }, + /* */ { 0xc1, 0x8003, 0x8003, L"DreamGrafix document" }, + /*ANI*/ { 0xc2, 0x0000, 0xffff, L"Paintworks animation" }, + /*PAL*/ { 0xc3, 0x0000, 0xffff, L"Paintworks palette" }, + /*OOG*/ { 0xc5, 0x0000, 0xffff, L"Object-oriented graphics" }, + /* */ { 0xc5, 0x8000, 0x8000, L"Draw Plus document" }, + /* */ { 0xc5, 0xc000, 0xc000, L"DYOH architecture doc" }, + /* */ { 0xc5, 0xc001, 0xc001, L"DYOH predrawn objects" }, + /* */ { 0xc5, 0xc002, 0xc002, L"DYOH custom objects" }, + /* */ { 0xc5, 0xc003, 0xc003, L"DYOH clipboard" }, + /* */ { 0xc5, 0xc004, 0xc004, L"DYOH interiors document" }, + /* */ { 0xc5, 0xc005, 0xc005, L"DYOH patterns" }, + /* */ { 0xc5, 0xc006, 0xc006, L"DYOH landscape document" }, + /* */ { 0xc5, 0xc007, 0xc007, L"PyWare Document" }, + /*SCR*/ { 0xc6, 0x0000, 0xffff, L"Script" }, + /* */ { 0xc6, 0x8001, 0x8001, L"Davex 8 script" }, + /* */ { 0xc6, 0x8002, 0x8002, L"Universe Master backup script" }, + /* */ { 0xc6, 0x8003, 0x8003, L"Universe Master Chain script" }, + /*CDV*/ { 0xc7, 0x0000, 0xffff, L"Control Panel document" }, + /*FON*/ { 0xc8, 0x0000, 0xffff, L"Font" }, + /* */ { 0xc8, 0x0000, 0x0000, L"Font (Standard Apple IIgs QuickDraw II Font)" }, + /* */ { 0xc8, 0x0001, 0x0001, L"TrueType font resource" }, + /* */ { 0xc8, 0x0008, 0x0008, L"Postscript font resource" }, + /* */ { 0xc8, 0x0081, 0x0081, L"TrueType font file" }, + /* */ { 0xc8, 0x0088, 0x0088, L"Postscript font file" }, + /*FND*/ { 0xc9, 0x0000, 0xffff, L"Finder data" }, + /*ICN*/ { 0xca, 0x0000, 0xffff, L"Icons" }, + /*MUS*/ { 0xd5, 0x0000, 0xffff, L"Music sequence" }, + /* */ { 0xd5, 0x0000, 0x0000, L"Music Construction Set song" }, + /* */ { 0xd5, 0x0001, 0x0001, L"MIDI Synth sequence" }, + /* */ { 0xd5, 0x0007, 0x0007, L"SoundSmith document" }, + /* */ { 0xd5, 0x8002, 0x8002, L"Diversi-Tune sequence" }, + /* */ { 0xd5, 0x8003, 0x8003, L"Master Tracks Jr. sequence" }, + /* */ { 0xd5, 0x8004, 0x8004, L"Music Writer document" }, + /* */ { 0xd5, 0x8005, 0x8005, L"Arcade King Super music" }, + /* */ { 0xd5, 0x8006, 0x8006, L"Music Composer file" }, + /*INS*/ { 0xd6, 0x0000, 0xffff, L"Instrument" }, + /* */ { 0xd6, 0x0000, 0x0000, L"Music Construction Set instrument" }, + /* */ { 0xd6, 0x0001, 0x0001, L"MIDI Synth instrument" }, + /* */ { 0xd6, 0x8002, 0x8002, L"Diversi-Tune instrument" }, + /*MDI*/ { 0xd7, 0x0000, 0xffff, L"MIDI data" }, + /* */ { 0xd7, 0x0000, 0x0000, L"MIDI standard data" }, + /* */ { 0xd7, 0x0080, 0x0080, L"MIDI System Exclusive data" }, + /* */ { 0xd7, 0x8001, 0x8001, L"MasterTracks Pro Sysex file" }, + /*SND*/ { 0xd8, 0x0000, 0xffff, L"Sampled sound" }, + /* */ { 0xd8, 0x0000, 0x0000, L"Audio IFF document" }, + /* */ { 0xd8, 0x0001, 0x0001, L"AIFF-C document" }, + /* */ { 0xd8, 0x0002, 0x0002, L"ASIF instrument" }, + /* */ { 0xd8, 0x0003, 0x0003, L"Sound resource file" }, + /* */ { 0xd8, 0x0004, 0x0004, L"MIDI Synth wave data" }, + /* */ { 0xd8, 0x8001, 0x8001, L"HyperStudio sound" }, + /* */ { 0xd8, 0x8002, 0x8002, L"Arcade King Super sound" }, + /* */ { 0xd8, 0x8003, 0x8003, L"SoundOff! sound bank" }, + /*DBM*/ { 0xdb, 0x0000, 0xffff, L"DB Master document" }, + /* */ { 0xdb, 0x0001, 0x0001, L"DB Master document" }, + /*???*/ { 0xdd, 0x0000, 0xffff, L"DDD Deluxe archive" }, // unofficial + /*LBR*/ { 0xe0, 0x0000, 0xffff, L"Archival library" }, + /* */ { 0xe0, 0x0000, 0x0000, L"ALU library" }, + /* */ { 0xe0, 0x0001, 0x0001, L"AppleSingle file" }, + /* */ { 0xe0, 0x0002, 0x0002, L"AppleDouble header file" }, + /* */ { 0xe0, 0x0003, 0x0003, L"AppleDouble data file" }, + /* */ { 0xe0, 0x0004, 0x0004, L"Archiver archive" }, + /* */ { 0xe0, 0x0005, 0x0005, L"DiskCopy 4.2 disk image" }, + /* */ { 0xe0, 0x0100, 0x0100, L"Apple 5.25 disk image" }, + /* */ { 0xe0, 0x0101, 0x0101, L"Profile 5MB disk image" }, + /* */ { 0xe0, 0x0102, 0x0102, L"Profile 10MB disk image" }, + /* */ { 0xe0, 0x0103, 0x0103, L"Apple 3.5 disk image" }, + /* */ { 0xe0, 0x0104, 0x0104, L"SCSI device image" }, + /* */ { 0xe0, 0x0105, 0x0105, L"SCSI hard disk image" }, + /* */ { 0xe0, 0x0106, 0x0106, L"SCSI tape image" }, + /* */ { 0xe0, 0x0107, 0x0107, L"SCSI CD-ROM image" }, + /* */ { 0xe0, 0x010e, 0x010e, L"RAM disk image" }, + /* */ { 0xe0, 0x010f, 0x010f, L"ROM disk image" }, + /* */ { 0xe0, 0x0110, 0x0110, L"File server image" }, + /* */ { 0xe0, 0x0113, 0x0113, L"Hard disk image" }, + /* */ { 0xe0, 0x0114, 0x0114, L"Floppy disk image" }, + /* */ { 0xe0, 0x0115, 0x0115, L"Tape image" }, + /* */ { 0xe0, 0x011e, 0x011e, L"AppleTalk file server image" }, + /* */ { 0xe0, 0x0120, 0x0120, L"DiskCopy 6 disk image" }, + /* */ { 0xe0, 0x0130, 0x0130, L"Universal Disk Image file" }, + /* */ { 0xe0, 0x8000, 0x8000, L"Binary II file" }, + /* */ { 0xe0, 0x8001, 0x8001, L"AppleLink ACU document" }, + /* */ { 0xe0, 0x8002, 0x8002, L"ShrinkIt (NuFX) document" }, + /* */ { 0xe0, 0x8003, 0x8003, L"Universal Disk Image file" }, + /* */ { 0xe0, 0x8004, 0x8004, L"Davex archived volume" }, + /* */ { 0xe0, 0x8006, 0x8006, L"EZ Backup Saveset doc" }, + /* */ { 0xe0, 0x8007, 0x8007, L"ELS DOS 3.3 volume" }, + /* */ { 0xe0, 0x8008, 0x8008, L"UtilityWorks document" }, + /* */ { 0xe0, 0x800a, 0x800a, L"Replicator document" }, + /* */ { 0xe0, 0x800b, 0x800b, L"AutoArk compressed document" }, + /* */ { 0xe0, 0x800d, 0x800d, L"HardPressed compressed data (data fork)" }, + /* */ { 0xe0, 0x800e, 0x800e, L"HardPressed compressed data (rsrc fork)" }, + /* */ { 0xe0, 0x800f, 0x800f, L"HardPressed compressed data (both forks)" }, + /* */ { 0xe0, 0x8010, 0x8010, L"LHA archive" }, + /*ATK*/ { 0xe2, 0x0000, 0xffff, L"AppleTalk data" }, + /* */ { 0xe2, 0xffff, 0xffff, L"EasyMount document" }, + /*R16*/ { 0xee, 0x0000, 0xffff, L"EDASM 816 relocatable file" }, + /*PAS*/ { 0xef, 0x0000, 0xffff, L"Pascal area" }, + /*CMD*/ { 0xf0, 0x0000, 0xffff, L"BASIC command" }, + /*???*/ { 0xf1, 0x0000, 0xffff, L"User type #1" }, + /*???*/ { 0xf2, 0x0000, 0xffff, L"User type #2" }, + /*???*/ { 0xf3, 0x0000, 0xffff, L"User type #3" }, + /*???*/ { 0xf4, 0x0000, 0xffff, L"User type #4" }, + /*???*/ { 0xf5, 0x0000, 0xffff, L"User type #5" }, + /*???*/ { 0xf6, 0x0000, 0xffff, L"User type #6" }, + /*???*/ { 0xf7, 0x0000, 0xffff, L"User type #7" }, + /*???*/ { 0xf8, 0x0000, 0xffff, L"User type #8" }, + /*OS */ { 0xf9, 0x0000, 0xffff, L"GS/OS system file" }, + /*OS */ { 0xfa, 0x0000, 0xffff, L"Integer BASIC program" }, + /*OS */ { 0xfb, 0x0000, 0xffff, L"Integer BASIC variables" }, + /*OS */ { 0xfc, 0x0000, 0xffff, L"AppleSoft BASIC program" }, + /*OS */ { 0xfd, 0x0000, 0xffff, L"AppleSoft BASIC variables" }, + /*OS */ { 0xfe, 0x0000, 0xffff, L"Relocatable code" }, + /*OS */ { 0xff, 0x0000, 0xffff, L"ProDOS 8 application" }, }; /* * Find an entry in the type description table that matches both file type and * aux type. If no match is found, nil is returned. */ -/*static*/ const char* +/*static*/ const WCHAR* PathProposal::FileTypeDescription(long fileType, long auxType) { int i; @@ -628,15 +627,15 @@ PathProposal::FileTypeDescription(long fileType, long auxType) void PathProposal::ArchiveToLocal(void) { - char* pathBuf; - const char* startp; - const char* endp; - char* dstp; + WCHAR* pathBuf; + const WCHAR* startp; + const WCHAR* endp; + WCHAR* dstp; int newBufLen; /* init output fields */ fLocalFssep = kLocalFssep; - fLocalPathName = ""; + fLocalPathName = L""; /* * Set up temporary buffer space. The maximum possible expansion @@ -658,10 +657,10 @@ PathProposal::ArchiveToLocal(void) while (startp != nil) { endp = nil; if (fStoredFssep != '\0') - endp = strchr(startp, fStoredFssep); + endp = wcschr(startp, fStoredFssep); if (endp != nil && endp == startp) { /* zero-length subdir component */ - WMSG1("WARNING: zero-length subdir component in '%s'\n", startp); + WMSG1("WARNING: zero-length subdir component in '%ls'\n", startp); startp++; continue; } @@ -675,23 +674,23 @@ PathProposal::ArchiveToLocal(void) startp = endp +1; } else { /* normalize filename component */ - NormalizeFileName(startp, strlen(startp), + NormalizeFileName(startp, wcslen(startp), fStoredFssep, &dstp, newBufLen); *dstp++ = '\0'; /* add/replace extension if necessary */ - char extBuf[kMaxPathGrowth +1] = ""; + WCHAR extBuf[kMaxPathGrowth +1] = L""; if (fPreservation) { AddPreservationString(pathBuf, extBuf); } else if (fThreadKind == GenericEntry::kRsrcThread) { /* add this in lieu of the preservation extension */ - strcat(pathBuf, kResourceStr); + wcscat(pathBuf, kResourceStr); } if (fAddExtension) { AddTypeExtension(pathBuf, extBuf); } - ASSERT(strlen(extBuf) <= kMaxPathGrowth); - strcat(pathBuf, extBuf); + ASSERT(wcslen(extBuf) <= kMaxPathGrowth); + wcscat(pathBuf, extBuf); startp = nil; /* we're done, break out of loop */ } @@ -704,11 +703,12 @@ PathProposal::ArchiveToLocal(void) * If "junk paths" is set, drop everything but the last component. */ if (fJunkPaths) { - char* lastFssep; - lastFssep = strrchr(pathBuf, fLocalFssep); + WCHAR* lastFssep; + lastFssep = wcsrchr(pathBuf, fLocalFssep); if (lastFssep != nil) { ASSERT(*(lastFssep+1) != '\0'); /* should already have been caught*/ - memmove(pathBuf, lastFssep+1, strlen(lastFssep+1)+1); + memmove(pathBuf, lastFssep+1, + (wcslen(lastFssep+1)+1) * sizeof(WCHAR)); } } @@ -720,39 +720,46 @@ PathProposal::ArchiveToLocal(void) * You can't create files or directories with these names on a FAT filesystem, * because they're MS-DOS "device special files". * - * The list comes from the Linux kernel's fs/msdos/namei.c. + * The list originally came from the Linux kernel's fs/msdos/namei.c; a + * better reference is "Naming Files, Paths, and Namespaces": + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx * * The trick is that the name can't start with any of these. That could mean * that the name is just "aux", or it could be "aux.this.txt". */ -static const char* gFatReservedNames3[] = { - "CON", "PRN", "NUL", "AUX", nil +static const WCHAR* gFatReservedNames3[] = { + L"CON", L"PRN", L"NUL", L"AUX", nil }; -static const char* gFatReservedNames4[] = { - "LPT1", "LPT2", "LPT3", "LPT4", "COM1", "COM2", "COM3", "COM4", nil +static const WCHAR* gFatReservedNames4[] = { + L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9", + L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", + nil }; /* * Filename normalization for Win32 filesystems. You can't use [ \/:*?"<>| ] - * or control characters, and it's probably unwise to use high-ASCII stuff. + * or control characters, and we're currently avoiding high-ASCII stuff. + * TODO: consider supporting the "Mac Roman" characters + * + * TODO: don't allow the filename to end with a space or period */ void -PathProposal::Win32NormalizeFileName(const char* srcp, long srcLen, - char fssep, char** pDstp, long dstLen) +PathProposal::Win32NormalizeFileName(const WCHAR* srcp, long srcLen, + char fssep, WCHAR** pDstp, long dstLen) { - char* dstp = *pDstp; - const char* startp = srcp; - static const char* kInvalid = "\\/:*?\"<>|"; + WCHAR* dstp = *pDstp; + const WCHAR* startp = srcp; + static const WCHAR* kInvalid = L"\\/:*?\"<>|"; /* match on "aux" or "aux.blah" */ if (srcLen >= 3) { - const char** ppcch; + const WCHAR** ppcch; for (ppcch = gFatReservedNames3; *ppcch != nil; ppcch++) { - if (strncasecmp(srcp, *ppcch, 3) == 0 && + if (wcsnicmp(srcp, *ppcch, 3) == 0 && (srcp[3] == '.' || srcLen == 3)) { - WMSG1("--- fixing '%s'\n", *ppcch); + WMSG1("--- fixing '%ls'\n", *ppcch); if (fPreservation) { *dstp++ = kForeignIndic; *dstp++ = '0'; @@ -764,13 +771,13 @@ PathProposal::Win32NormalizeFileName(const char* srcp, long srcLen, } } if (srcLen >= 4) { - const char** ppcch; + const WCHAR** ppcch; for (ppcch = gFatReservedNames4; *ppcch != nil; ppcch++) { - if (strncasecmp(srcp, *ppcch, 4) == 0 && + if (wcsnicmp(srcp, *ppcch, 4) == 0 && (srcp[4] == '.' || srcLen == 4)) { - WMSG1("--- fixing '%s'\n", *ppcch); + WMSG1("--- fixing '%ls'\n", *ppcch); if (fPreservation) { *dstp++ = kForeignIndic; *dstp++ = '0'; @@ -791,10 +798,11 @@ PathProposal::Win32NormalizeFileName(const char* srcp, long srcLen, if (fPreservation) *dstp++ = *srcp; *dstp++ = *srcp++; - } else if (strchr(kInvalid, *srcp) != nil || + } else if (wcschr(kInvalid, *srcp) != nil || *srcp < 0x20 || *srcp >= 0x7f) { /* change invalid char to "%2f" or '_' */ + // TODO: this assumes 8-bit input; should convert to UTF-8 if (fPreservation) { *dstp++ = kForeignIndic; *dstp++ = HexConv(*srcp >> 4 & 0x0f); @@ -826,8 +834,8 @@ PathProposal::Win32NormalizeFileName(const char* srcp, long srcLen, * The output buffer must be able to hold 3x the original string length. */ void -PathProposal::NormalizeFileName(const char* srcp, long srcLen, - char fssep, char** pDstp, long dstLen) +PathProposal::NormalizeFileName(const WCHAR* srcp, long srcLen, + char fssep, WCHAR** pDstp, long dstLen) { ASSERT(srcp != nil); ASSERT(srcLen > 0); @@ -849,8 +857,8 @@ PathProposal::NormalizeFileName(const char* srcp, long srcLen, * Normalize a directory name to local filesystem conventions. */ void -PathProposal::NormalizeDirectoryName(const char* srcp, long srcLen, - char fssep, char** pDstp, long dstLen) +PathProposal::NormalizeDirectoryName(const WCHAR* srcp, long srcLen, + char fssep, WCHAR** pDstp, long dstLen) { /* in general, directories and filenames are the same */ ASSERT(fssep > ' ' && fssep < 0x7f); @@ -865,25 +873,25 @@ PathProposal::NormalizeDirectoryName(const char* srcp, long srcLen, * plus kMaxPathGrowth more. It will be modified in place. */ void -PathProposal::AddPreservationString(const char* pathBuf, char* extBuf) +PathProposal::AddPreservationString(const WCHAR* pathBuf, WCHAR* extBuf) { - char* cp; + WCHAR* cp; ASSERT(pathBuf != nil); ASSERT(extBuf != nil); ASSERT(fPreservation); - cp = extBuf + strlen(extBuf); + cp = extBuf + wcslen(extBuf); /* * Cons up a preservation string. On some platforms "sprintf" doesn't * return the #of characters written, so we add it up manually. */ if (fFileType < 0x100 && fAuxType < 0x10000) { - sprintf(cp, "%c%02lx%04lx", kPreserveIndic, fFileType, fAuxType); + wsprintf(cp, L"%c%02lx%04lx", kPreserveIndic, fFileType, fAuxType); cp += 7; } else { - sprintf(cp, "%c%08lx%08lx", kPreserveIndic, fFileType, fAuxType); + wsprintf(cp, L"%c%08lx%08lx", kPreserveIndic, fFileType, fAuxType); cp += 17; } @@ -904,21 +912,21 @@ PathProposal::AddPreservationString(const char* pathBuf, char* extBuf) * type preservation) or append an extension based on the ProDOS file type. */ void -PathProposal::AddTypeExtension(const char* pathBuf, char* extBuf) +PathProposal::AddTypeExtension(const WCHAR* pathBuf, WCHAR* extBuf) { - const char* pPathExt = nil; - const char* pWantedExt = nil; - const char* pTypeExt = nil; - char* end; - char* cp; + const WCHAR* pPathExt = nil; + const WCHAR* pWantedExt = nil; + const WCHAR* pTypeExt = nil; + WCHAR* end; + WCHAR* cp; - cp = extBuf + strlen(extBuf); + cp = extBuf + wcslen(extBuf); /* * Find extension in the local filename that we've prepared so far. * Note FindExtension guarantees there's at least one char after '.'. */ - pPathExt = FindExtension(pathBuf, fLocalFssep); + pPathExt = PathName::FindExtension(pathBuf, fLocalFssep); if (pPathExt == nil) { /* * There's no extension on the filename. Use the standard @@ -941,9 +949,9 @@ PathProposal::AddTypeExtension(const char* pathBuf, char* extBuf) * original extension, or generate one for it from the ProDOS file type. */ if (fFileType == 0x04) - pWantedExt = "TXT"; + pWantedExt = L"TXT"; else if (fThreadKind == GenericEntry::kDiskImageThread) - pWantedExt = "PO"; + pWantedExt = L"PO"; else { /* * We want to use the extension currently on the file, if it has one. @@ -963,29 +971,29 @@ PathProposal::AddTypeExtension(const char* pathBuf, char* extBuf) */ if (pWantedExt != nil) { if (extBuf[0] == '\0' && pPathExt != nil && - strcasecmp(pPathExt, pWantedExt) == 0) + wcsicmp(pPathExt, pWantedExt) == 0) { /* don't add an extension that's already there */ pWantedExt = nil; goto know_ext; } - if (strlen(pWantedExt) >= kMaxExtLen) { + if (wcslen(pWantedExt) >= kMaxExtLen) { /* too long, forget it */ pWantedExt = nil; goto know_ext; } /* if it's strictly decimal-numeric, don't use it (.1, .2, etc) */ - (void) strtoul(pWantedExt, &end, 10); + (void) wcstoul(pWantedExt, &end, 10); if (*end == '\0') { pWantedExt = nil; goto know_ext; } /* if '#' appears in it, don't use it -- it'll confuse us */ - //WMSG2("LOOKING FOR '%c' in '%s'\n", kPreserveIndic, ccp); - const char* ccp = pWantedExt; + //WMSG2("LOOKING FOR '%c' in '%ls'\n", kPreserveIndic, ccp); + const WCHAR* ccp = pWantedExt; while (*ccp != '\0') { if (*ccp == kPreserveIndic) { pWantedExt = nil; @@ -1002,7 +1010,7 @@ know_ext: */ if (pWantedExt != nil) { *cp++ = kFilenameExtDelim; - strcpy(cp, pWantedExt); + wcscpy(cp, pWantedExt); //cp += strlen(pWantedExt); } } @@ -1032,10 +1040,10 @@ PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) Boolean wasPreserved; Boolean doJunk = false; Boolean adjusted; - char slashDotDotSlash[5] = "_.._"; + WCHAR slashDotDotSlash[5] = L"_.._"; fStoredPathName = fLocalPathName; - char* livePathStr = fStoredPathName.GetBuffer(0); + WCHAR* livePathStr = fStoredPathName.GetBuffer(0); fStoredFssep = kDefaultStoredFssep; @@ -1097,7 +1105,8 @@ PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) ASSERT(kLocalFssep != '\0'); while (livePathStr[0] == kLocalFssep) { /* slide it down, len is (strlen +1), -1 (dropping first char)*/ - memmove(livePathStr, livePathStr+1, strlen(livePathStr)); + memmove(livePathStr, livePathStr+1, + wcslen(livePathStr) * sizeof(WCHAR)); adjusted = true; } @@ -1107,7 +1116,8 @@ PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) while (livePathStr[0] == '.' && livePathStr[1] == kLocalFssep) { /* slide it down, len is (strlen +1) -2 (dropping two chars) */ - memmove(livePathStr, livePathStr+2, strlen(livePathStr)-1); + memmove(livePathStr, livePathStr+2, + (wcslen(livePathStr)-1) * sizeof(WCHAR)); adjusted = true; } } while (adjusted); @@ -1122,9 +1132,9 @@ PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) slashDotDotSlash[0] = kLocalFssep; slashDotDotSlash[3] = kLocalFssep; if ((livePathStr[0] == '.' && livePathStr[1] == '.') || - (strstr(livePathStr, slashDotDotSlash) != nil)) + (wcsstr(livePathStr, slashDotDotSlash) != nil)) { - WMSG1("Found dot dot in '%s', keeping only filename\n", livePathStr); + WMSG1("Found dot dot in '%ls', keeping only filename\n", livePathStr); doJunk = true; } @@ -1139,11 +1149,12 @@ PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) * If "junk paths" is set, drop everything before the last fssep char. */ if (pAddOpts->fStripFolderNames || doJunk) { - char* lastFssep; - lastFssep = strrchr(livePathStr, kLocalFssep); + WCHAR* lastFssep; + lastFssep = wcsrchr(livePathStr, kLocalFssep); if (lastFssep != nil) { ASSERT(*(lastFssep+1) != '\0'); /* should already have been caught*/ - memmove(livePathStr, lastFssep+1, strlen(lastFssep+1)+1); + memmove(livePathStr, lastFssep+1, + (wcslen(lastFssep+1)+1) * sizeof(WCHAR)); } } @@ -1167,7 +1178,7 @@ PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) * in the string, replace it with "newSubst". */ void -PathProposal::ReplaceFssep(char* str, char oldc, char newc, char newSubst) +PathProposal::ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst) { while (*str != '\0') { if (*str == oldc) @@ -1186,19 +1197,19 @@ PathProposal::ReplaceFssep(char* str, char oldc, char newc, char newSubst) * like "TXT" and "BIN") and the separate list of recognized extensions. */ void -PathProposal::LookupExtension(const char* ext) +PathProposal::LookupExtension(const WCHAR* ext) { - char uext3[4]; + WCHAR uext3[4]; int i, extLen; - extLen = strlen(ext); + extLen = wcslen(ext); ASSERT(extLen > 0); /* * First step is to try to find it in the recognized types list. */ for (i = 0; i < NELEM(gRecognizedExtensions); i++) { - if (strcasecmp(ext, gRecognizedExtensions[i].label) == 0) { + if (wcsicmp(ext, gRecognizedExtensions[i].label) == 0) { fFileType = gRecognizedExtensions[i].fileType; fAuxType = gRecognizedExtensions[i].auxType; goto bail; @@ -1219,10 +1230,8 @@ PathProposal::LookupExtension(const char* ext) uext3[i] = toupper(ext[i]); uext3[3] = '\0'; - /*printf("### converted '%s' to '%s'\n", ext, uext3);*/ - for (i = 0; i < NELEM(gFileTypeNames); i++) { - if (strcmp(uext3, gFileTypeNames[i]) == 0) { + if (wcscmp(uext3, gFileTypeNames[i]) == 0) { fFileType = i; goto bail; } @@ -1237,13 +1246,13 @@ bail: * Try to associate some meaning with the file extension. */ void -PathProposal::InterpretExtension(const char* pathName) +PathProposal::InterpretExtension(const WCHAR* pathName) { - const char* pExt; + const WCHAR* pExt; ASSERT(pathName != nil); - pExt = FindExtension(pathName, fLocalFssep); + pExt = PathName::FindExtension(pathName, fLocalFssep); if (pExt != nil) LookupExtension(pExt+1); } @@ -1258,18 +1267,18 @@ PathProposal::InterpretExtension(const char* pathName) * in the filename. */ Boolean -PathProposal::ExtractPreservationString(char* pathname) +PathProposal::ExtractPreservationString(WCHAR* pathname) { - char numBuf[9]; + WCHAR numBuf[9]; unsigned long fileType, auxType; int threadMask; - char* pPreserve; - char* cp; + WCHAR* pPreserve; + WCHAR* cp; int digitCount; ASSERT(pathname != nil); - pPreserve = strrchr(pathname, kPreserveIndic); + pPreserve = wcsrchr(pathname, kPreserveIndic); if (pPreserve == nil) return false; @@ -1282,22 +1291,22 @@ PathProposal::ExtractPreservationString(char* pathname) switch (digitCount) { case 6: /* ProDOS 1-byte type and 2-byte aux */ - memcpy(numBuf, pPreserve+1, 2); + memcpy(numBuf, pPreserve+1, 2 * sizeof(WCHAR)); numBuf[2] = 0; - fileType = strtoul(numBuf, &cp, 16); + fileType = wcstoul(numBuf, &cp, 16); ASSERT(cp == numBuf + 2); - auxType = strtoul(pPreserve+3, &cp, 16); + auxType = wcstoul(pPreserve+3, &cp, 16); ASSERT(cp == pPreserve + 7); break; case 16: /* HFS 4-byte type and 4-byte creator */ - memcpy(numBuf, pPreserve+1, 8); + memcpy(numBuf, pPreserve+1, 8 * sizeof(WCHAR)); numBuf[8] = 0; - fileType = strtoul(numBuf, &cp, 16); + fileType = wcstoul(numBuf, &cp, 16); ASSERT(cp == numBuf + 8); - auxType = strtoul(pPreserve+9, &cp, 16); + auxType = wcstoul(pPreserve+9, &cp, 16); ASSERT(cp == pPreserve + 17); break; default: @@ -1351,11 +1360,11 @@ PathProposal::ExtractPreservationString(char* pathname) * smaller, so we can do it in place in the buffer. */ void -PathProposal::DenormalizePath(char* pathBuf) +PathProposal::DenormalizePath(WCHAR* pathBuf) { - const char* srcp; - char* dstp; - char ch; + const WCHAR* srcp; + WCHAR* dstp; + WCHAR ch; srcp = pathBuf; dstp = pathBuf; @@ -1400,22 +1409,22 @@ PathProposal::DenormalizePath(char* pathBuf) * don't want them to retain their original suffix. */ void -PathProposal::StripDiskImageSuffix(char* pathName) +PathProposal::StripDiskImageSuffix(WCHAR* pathName) { - static const char diskExt[][4] = { - "SHK", "SDK", "IMG", "PO", "DO", "2MG", "DSK" + static const WCHAR diskExt[][4] = { + L"SHK", L"SDK", L"IMG", L"PO", L"DO", L"2MG", L"DSK" }; - char* pExt; + const WCHAR* pExt; int i; - pExt = (char*)FindExtension(pathName, fLocalFssep); + pExt = PathName::FindExtension(pathName, fLocalFssep); if (pExt == nil || pExt == pathName) return; for (i = 0; i < NELEM(diskExt); i++) { - if (strcasecmp(pExt+1, diskExt[i]) == 0) { - WMSG2("Dropping '%s' from '%s'\n", pExt, pathName); - *pExt = '\0'; + if (wcsicmp(pExt+1, diskExt[i]) == 0) { + WMSG2("Dropping '%ls' from '%ls'\n", pExt, pathName); + *const_cast(pExt) = '\0'; return; } } diff --git a/app/FileNameConv.h b/app/FileNameConv.h index 60dc946..a960fbb 100644 --- a/app/FileNameConv.h +++ b/app/FileNameConv.h @@ -6,12 +6,12 @@ /* * File name conversion. */ -#ifndef __FILENAMECONV__ -#define __FILENAMECONV__ +#ifndef APP_FILENAMECONV_H +#define APP_FILENAMECONV_H #include "GenericArchive.h" -#define kUnknownTypeStr "???" +#define kUnknownTypeStr L"???" /* * Proposal for an output pathname, based on the contents of a GenericEntry. @@ -26,13 +26,13 @@ public: }; PathProposal(void) { - fStoredPathName = ":BOGUS:"; + fStoredPathName = L":BOGUS:"; fStoredFssep = '['; fFileType = 256; fAuxType = 65536; fThreadKind = 0; - fLocalPathName = ":HOSED:"; + fLocalPathName = L":HOSED:"; fLocalFssep = ']'; fPreservation = false; @@ -52,7 +52,7 @@ public: fAuxType = pEntry->GetAuxType(); //fThreadKind set from SelectionEntry // reset the "output" fields - fLocalPathName = ":HOSED:"; + fLocalPathName = L":HOSED:"; fLocalFssep = ']'; // I expect these to be as-yet unset; check it ASSERT(!fPreservation); @@ -61,13 +61,13 @@ public: } // init the "add to archive" side - void Init(const char* localPathName) { + void Init(const WCHAR* localPathName) { //ASSERT(basePathName[strlen(basePathName)-1] != kLocalFssep); //fLocalPathName = localPathName + strlen(basePathName)+1; fLocalPathName = localPathName; fLocalFssep = kLocalFssep; // reset the "output" fields - fStoredPathName = ":HOSED:"; + fStoredPathName = L":HOSED:"; fStoredFssep = '['; fFileType = 0; fAuxType = 0; @@ -115,25 +115,25 @@ public: /* * Misc utility functions. */ - static const char* FileTypeString(unsigned long fileType); - static const char* FileTypeDescription(long fileType, long auxType); + static const WCHAR* FileTypeString(unsigned long fileType); + static const WCHAR* FileTypeDescription(long fileType, long auxType); private: - void Win32NormalizeFileName(const char* srcp, long srcLen, - char fssep, char** pDstp, long dstLen); - void NormalizeFileName(const char* srcp, long srcLen, - char fssep, char** pDstp, long dstLen); - void NormalizeDirectoryName(const char* srcp, long srcLen, - char fssep, char** pDstp, long dstLen); - void AddPreservationString(const char* pathBuf, char* extBuf); - void AddTypeExtension(const char* pathBuf, char* extBuf); + void Win32NormalizeFileName(const WCHAR* srcp, long srcLen, + char fssep, WCHAR** pDstp, long dstLen); + void NormalizeFileName(const WCHAR* srcp, long srcLen, + char fssep, WCHAR** pDstp, long dstLen); + void NormalizeDirectoryName(const WCHAR* srcp, long srcLen, + char fssep, WCHAR** pDstp, long dstLen); + void AddPreservationString(const WCHAR* pathBuf, WCHAR* extBuf); + void AddTypeExtension(const WCHAR* pathBuf, WCHAR* extBuf); - void ReplaceFssep(char* str, char oldc, char newc, char newSubst); - void LookupExtension(const char* ext); - bool ExtractPreservationString(char* pathName); - void InterpretExtension(const char* pathName); - void DenormalizePath(char* pathBuf); - void StripDiskImageSuffix(char* pathName); + void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst); + void LookupExtension(const WCHAR* ext); + bool ExtractPreservationString(WCHAR* pathName); + void InterpretExtension(const WCHAR* pathName); + void DenormalizePath(WCHAR* pathBuf); + void StripDiskImageSuffix(WCHAR* pathName); }; -#endif /*__FILENAMECONV__*/ \ No newline at end of file +#endif /*APP_FILENAMECONV_H*/ diff --git a/app/GenericArchive.cpp b/app/GenericArchive.cpp index 0586a59..1b688c2 100644 --- a/app/GenericArchive.cpp +++ b/app/GenericArchive.cpp @@ -61,7 +61,7 @@ GenericEntry::GenericEntry(void) fModWhen = kDateNone; fCreateWhen = kDateNone; fRecordKind = kRecordKindUnknown; - fFormatStr = "Unknown"; + fFormatStr = L"Unknown"; fCompressedLen = 0; //fUncompressedLen = 0; fDataForkLen = fRsrcForkLen = 0; @@ -95,13 +95,12 @@ GenericEntry::~GenericEntry(void) * Pathname getters and setters. */ void -GenericEntry::SetPathName(const char* path) +GenericEntry::SetPathName(const WCHAR* path) { - ASSERT(path != nil && strlen(path) > 0); + ASSERT(path != nil && wcslen(path) > 0); if (fPathName != nil) delete fPathName; - fPathName = new char[strlen(path)+1]; - strcpy(fPathName, path); + fPathName = wcsdup(path); // nuke the derived fields fFileName = nil; fFileNameExtension = nil; @@ -118,59 +117,65 @@ GenericEntry::SetPathName(const char* path) if (pPreferences->GetPrefBool(kPrSpacesToUnder)) SpacesToUnderscores(fPathName); } -const char* +const WCHAR* GenericEntry::GetFileName(void) { ASSERT(fPathName != nil); if (fFileName == nil) - fFileName = FilenameOnly(fPathName, fFssep); + fFileName = PathName::FilenameOnly(fPathName, fFssep); return fFileName; } -const char* +const WCHAR* GenericEntry::GetFileNameExtension(void) { ASSERT(fPathName != nil); if (fFileNameExtension == nil) - fFileNameExtension = FindExtension(fPathName, fFssep); + fFileNameExtension = PathName::FindExtension(fPathName, fFssep); return fFileNameExtension; } +CStringA +GenericEntry::GetFileNameExtensionA(void) +{ + return GetFileNameExtension(); +} void -GenericEntry::SetSubVolName(const char* name) +GenericEntry::SetSubVolName(const WCHAR* name) { delete[] fSubVolName; fSubVolName = nil; if (name != nil) { - fSubVolName = new char[strlen(name)+1]; - strcpy(fSubVolName, name); + fSubVolName = wcsdup(name); } } -const char* +const WCHAR* GenericEntry::GetDisplayName(void) const { ASSERT(fPathName != nil); if (fDisplayName != nil) return fDisplayName; + // TODO: hmm... GenericEntry* pThis = const_cast(this); - int len = strlen(fPathName) +1; + int len = wcslen(fPathName) +1; if (fSubVolName != nil) - len += strlen(fSubVolName) +1; - pThis->fDisplayName = new char[len]; + len += wcslen(fSubVolName) +1; + pThis->fDisplayName = new WCHAR[len]; if (fSubVolName != nil) { - char xtra[2] = { DiskFS::kDIFssep, '\0' }; - strcpy(pThis->fDisplayName, fSubVolName); - strcat(pThis->fDisplayName, xtra); - } else + WCHAR xtra[2] = { DiskFS::kDIFssep, '\0' }; + wcscpy(pThis->fDisplayName, fSubVolName); + wcscat(pThis->fDisplayName, xtra); + } else { pThis->fDisplayName[0] = '\0'; - strcat(pThis->fDisplayName, fPathName); + } + wcscat(pThis->fDisplayName, fPathName); return pThis->fDisplayName; } /* * Get a string for this entry's filetype. */ -const char* +const WCHAR* GenericEntry::GetFileTypeString(void) const { return PathProposal::FileTypeString(fFileType); @@ -180,7 +185,7 @@ GenericEntry::GetFileTypeString(void) const * Convert spaces to underscores. */ /*static*/ void -GenericEntry::SpacesToUnderscores(char* buf) +GenericEntry::SpacesToUnderscores(WCHAR* buf) { while (*buf != '\0') { if (*buf == ' ') @@ -574,9 +579,9 @@ GenericArchive::CreateIndex(void) * in which bad things could happen, but it should be okay. */ /*static*/ CString -GenericArchive::GenDerivedTempName(const char* filename) +GenericArchive::GenDerivedTempName(const WCHAR* filename) { - static const char* kTmpTemplate = "CPtmp_XXXXXX"; + static const WCHAR kTmpTemplate[] = L"CPtmp_XXXXXX"; CString mangle(filename); int idx, len; @@ -592,7 +597,7 @@ GenericArchive::GenDerivedTempName(const char* filename) mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */ mangle += kTmpTemplate; } - WMSG2("GenDerived: passed '%s' returned '%s'\n", filename, mangle); + WMSG2("GenDerived: passed '%ls' returned '%ls'\n", filename, (LPCWSTR) mangle); return mangle; } @@ -616,8 +621,8 @@ GenericArchive::GenDerivedTempName(const char* filename) GenericArchive::ComparePaths(const CString& name1, char fssep1, const CString& name2, char fssep2) { - const char* cp1 = name1; - const char* cp2 = name2; + const WCHAR* cp1 = name1; + const WCHAR* cp2 = name2; while (*cp1 != '\0' && *cp2 != '\0') { if (*cp1 == fssep1) { @@ -700,7 +705,7 @@ GenericArchive::UNIXTimeToDateTime(const time_t* pWhen, NuDateTime* pDateTime) */ NuError GenericArchive::GetFileDetails(const AddFilesDialog* pAddOpts, - const char* pathname, struct stat* psb, FileDetails* pDetails) + const WCHAR* pathname, struct _stat* psb, FileDetails* pDetails) { //char* livePathStr; time_t now; @@ -728,7 +733,7 @@ GenericArchive::GetFileDetails(const AddFilesDialog* pAddOpts, if (NState_GetModAddAsDisk(pState)) { if ((psb->st_size & 0x1ff) != 0) { /* reject anything whose size isn't a multiple of 512 bytes */ - printf("NOT storing odd-sized (%ld) file as disk image: %s\n", + printf("NOT storing odd-sized (%ld) file as disk image: %ls\n", (long)psb->st_size, livePathStr); } else { /* set fields; note the "preserve" stuff can override this */ @@ -797,12 +802,12 @@ GenericArchive::GetFileDetails(const AddFilesDialog* pAddOpts, */ typedef struct Win32dirent { char d_attr; - char d_name[MAX_PATH]; + WCHAR d_name[MAX_PATH]; int d_first; HANDLE d_hFindFile; } Win32dirent; -static const char* kWildMatchAll = "*.*"; +static const WCHAR kWildMatchAll[] = L"*.*"; /* * Prepare a directory for reading. @@ -810,36 +815,36 @@ static const char* kWildMatchAll = "*.*"; * Allocates a Win32dirent struct that must be freed by the caller. */ Win32dirent* -GenericArchive::OpenDir(const char* name) +GenericArchive::OpenDir(const WCHAR* name) { Win32dirent* dir = nil; - char* tmpStr = nil; - char* cp; + WCHAR* tmpStr = nil; + WCHAR* cp; WIN32_FIND_DATA fnd; dir = (Win32dirent*) malloc(sizeof(*dir)); - tmpStr = (char*) malloc(strlen(name) + (2 + sizeof(kWildMatchAll))); + tmpStr = (WCHAR*) malloc((wcslen(name) + 2 + wcslen(kWildMatchAll)) * sizeof(WCHAR)); if (dir == nil || tmpStr == nil) goto failed; - strcpy(tmpStr, name); - cp = tmpStr + strlen(tmpStr); + wcscpy(tmpStr, name); + cp = tmpStr + wcslen(tmpStr); /* don't end in a colon (e.g. "C:") */ - if ((cp - tmpStr) > 0 && strrchr(tmpStr, ':') == (cp - 1)) + if ((cp - tmpStr) > 0 && wcsrchr(tmpStr, ':') == (cp - 1)) *cp++ = '.'; /* must end in a slash */ if ((cp - tmpStr) > 0 && - strrchr(tmpStr, PathProposal::kLocalFssep) != (cp - 1)) + wcsrchr(tmpStr, PathProposal::kLocalFssep) != (cp - 1)) *cp++ = PathProposal::kLocalFssep; - strcpy(cp, kWildMatchAll); + wcscpy(cp, kWildMatchAll); dir->d_hFindFile = FindFirstFile(tmpStr, &fnd); if (dir->d_hFindFile == INVALID_HANDLE_VALUE) goto failed; - strcpy(dir->d_name, fnd.cFileName); + wcscpy(dir->d_name, fnd.cFileName); dir->d_attr = (unsigned char) fnd.dwFileAttributes; dir->d_first = 1; @@ -868,7 +873,7 @@ GenericArchive::ReadDir(Win32dirent* dir) if (!FindNextFile(dir->d_hFindFile, &fnd)) return nil; - strcpy(dir->d_name, fnd.cFileName); + wcscpy(dir->d_name, fnd.cFileName); dir->d_attr = (unsigned char) fnd.dwFileAttributes; } @@ -888,14 +893,6 @@ GenericArchive::CloseDir(Win32dirent* dir) free(dir); } - -/* might as well blend in with the UNIX version */ -#define DIR_NAME_LEN(dirent) ((int)strlen((dirent)->d_name)) - -//static NuError Win32AddFile(NulibState* pState, NuArchive* pArchive, -// const char* pathname); - - /* * Win32 recursive directory descent. Scan the contents of a directory. * If a subdirectory is found, follow it; otherwise, call Win32AddFile to @@ -903,19 +900,19 @@ GenericArchive::CloseDir(Win32dirent* dir) */ NuError GenericArchive::Win32AddDirectory(const AddFilesDialog* pAddOpts, - const char* dirName, CString* pErrMsg) + const WCHAR* dirName, CString* pErrMsg) { NuError err = kNuErrNone; Win32dirent* dirp = nil; Win32dirent* entry; - char nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ + WCHAR nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ char fssep; int len; ASSERT(pAddOpts != nil); ASSERT(dirName != nil); - WMSG1("+++ DESCEND: '%s'\n", dirName); + WMSG1("+++ DESCEND: '%ls'\n", dirName); dirp = OpenDir(dirName); if (dirp == nil) { @@ -924,7 +921,7 @@ GenericArchive::Win32AddDirectory(const AddFilesDialog* pAddOpts, else err = errno ? (NuError)errno : kNuErrOpenDir; - pErrMsg->Format("Failed on '%s': %s.", dirName, NuStrError(err)); + pErrMsg->Format(L"Failed on '%ls': %hs.", dirName, NuStrError(err)); goto bail; } @@ -933,22 +930,25 @@ GenericArchive::Win32AddDirectory(const AddFilesDialog* pAddOpts, /* could use readdir_r, but we don't care about reentrancy here */ while ((entry = ReadDir(dirp)) != nil) { /* skip the dotsies */ - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + if (wcscmp(entry->d_name, L".") == 0 || + wcscmp(entry->d_name, L"..") == 0) + { continue; + } - len = strlen(dirName); - if (len + DIR_NAME_LEN(entry) +2 > MAX_PATH) { + len = wcslen(dirName); + if (len + wcslen(entry->d_name) +2 > MAX_PATH) { err = kNuErrInternal; - WMSG4("ERROR: Filename exceeds %d bytes: %s%c%s", + WMSG4("ERROR: Filename exceeds %d bytes: %ls%c%ls", MAX_PATH, dirName, fssep, entry->d_name); goto bail; } /* form the new name, inserting an fssep if needed */ - strcpy(nbuf, dirName); + wcscpy(nbuf, dirName); if (dirName[len-1] != fssep) nbuf[len++] = fssep; - strcpy(nbuf+len, entry->d_name); + wcscpy(nbuf+len, entry->d_name); err = Win32AddFile(pAddOpts, nbuf, pErrMsg); if (err != kNuErrNone) @@ -970,12 +970,12 @@ bail: */ NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts, - const char* pathname, CString* pErrMsg) + const WCHAR* pathname, CString* pErrMsg) { NuError err = kNuErrNone; Boolean exists, isDir, isReadable; FileDetails details; - struct stat sb; + struct _stat sb; ASSERT(pAddOpts != nil); ASSERT(pathname != nil); @@ -984,19 +984,19 @@ GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts, int ierr = checkPath.CheckFileStatus(&sb, &exists, &isReadable, &isDir); if (ierr != 0) { err = kNuErrGeneric; - pErrMsg->Format("Unexpected error while examining '%s': %s.", pathname, - NuStrError((NuError) ierr)); + pErrMsg->Format(L"Unexpected error while examining '%ls': %hs.", + pathname, NuStrError((NuError) ierr)); goto bail; } if (!exists) { err = kNuErrFileNotFound; - pErrMsg->Format("Couldn't find '%s'", pathname); + pErrMsg->Format(L"Couldn't find '%ls'", pathname); goto bail; } if (!isReadable) { err = kNuErrFileNotReadable; - pErrMsg->Format("File '%s' isn't readable.", pathname); + pErrMsg->Format(L"File '%ls' isn't readable.", pathname); goto bail; } if (isDir) { @@ -1010,7 +1010,7 @@ GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts, * filetype and auxtype it has, and whether or not it's actually the * resource fork of another file. */ - WMSG1("+++ ADD '%s'\n", pathname); + WMSG1("+++ ADD '%ls'\n", pathname); /* * Fill out the "details" structure. The class has an automatic @@ -1021,7 +1021,7 @@ GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts, if (err != kNuErrNone) goto bail; - assert(strcmp(pathname, details.origName) == 0); + assert(wcscmp(pathname, details.origName) == 0); err = DoAddFile(pAddOpts, &details); if (err == kNuErrSkipped) // ignore "skipped" result err = kNuErrNone; @@ -1030,7 +1030,7 @@ GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts, bail: if (err != kNuErrNone && pErrMsg->IsEmpty()) { - pErrMsg->Format("Unable to add file '%s': %s.", + pErrMsg->Format(L"Unable to add file '%ls': %hs.", pathname, NuStrError(err)); } return err; @@ -1047,7 +1047,7 @@ bail: * GSOSAddFile. ] */ NuError -GenericArchive::AddFile(const AddFilesDialog* pAddOpts, const char* pathname, +GenericArchive::AddFile(const AddFilesDialog* pAddOpts, const WCHAR* pathname, CString* pErrMsg) { *pErrMsg = ""; @@ -1106,8 +1106,10 @@ GenericArchive::FileDetails::operator const NuFileDetails() const break; } - details.origName = origName; // CString to char* - details.storageName = storageName; // CString to char* + // TODO(xyzzy): need narrow-string versions of origName and storageName + // (probably need to re-think this automatic-cast-conversion stuff) + details.origName = "XYZZY-GenericArchive1"; // origName; + details.storageName = "XYZZY-GenericArchive2"; // storageName; //details.fileSysID = fileSysID; details.fileSysInfo = fileSysInfo; details.access = access; @@ -1234,13 +1236,13 @@ SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask) { SelectionEntry* pSelEntry; - //WMSG1(" Sel '%s'\n", pEntry->GetPathName()); + //WMSG1(" Sel '%ls'\n", pEntry->GetPathName()); if (!(threadMask & GenericEntry::kAllowVolumeDir) && pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) { /* only include volume dir if specifically requested */ - //WMSG1(" Excluding volume dir '%s' from set\n", pEntry->GetPathName()); + //WMSG1(" Excluding volume dir '%ls' from set\n", pEntry->GetPathName()); return; } @@ -1248,7 +1250,7 @@ SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask) pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { /* only include directories if specifically requested */ - //WMSG1(" Excluding folder '%s' from set\n", pEntry->GetPathName()); + //WMSG1(" Excluding folder '%ls' from set\n", pEntry->GetPathName()); return; } @@ -1325,18 +1327,18 @@ SelectionSet::DeleteEntries(void) * Count the #of entries whose display name matches the prefix string. */ int -SelectionSet::CountMatchingPrefix(const char* prefix) +SelectionSet::CountMatchingPrefix(const WCHAR* prefix) { SelectionEntry* pEntry; int count = 0; - int len = strlen(prefix); + int len = wcslen(prefix); ASSERT(len > 0); pEntry = GetEntries(); while (pEntry != nil) { GenericEntry* pGeneric = pEntry->GetEntry(); - if (strncasecmp(prefix, pGeneric->GetDisplayName(), len) == 0) + if (wcsnicmp(prefix, pGeneric->GetDisplayName(), len) == 0) count++; pEntry = pEntry->GetNext(); } @@ -1356,7 +1358,7 @@ SelectionSet::Dump(void) pEntry = fEntryHead; while (pEntry != nil) { - WMSG1(" : name='%s'\n", pEntry->GetEntry()->GetPathName()); + WMSG1(" : name='%ls'\n", pEntry->GetEntry()->GetPathName()); pEntry = pEntry->GetNext(); } } diff --git a/app/GenericArchive.h b/app/GenericArchive.h index 61b62ed..f14d6ac 100644 --- a/app/GenericArchive.h +++ b/app/GenericArchive.h @@ -8,13 +8,13 @@ * * These are abstract base classes. */ -#ifndef __GENERIC_ARCHIVE__ -#define __GENERIC_ARCHIVE__ +#ifndef APP_GENERICARCHIVE_H +#define APP_GENERICARCHIVE_H #include "Preferences.h" #include "../util/UtilLib.h" #include "../diskimg/DiskImg.h" -#include "../prebuilt/NufxLib.h" +#include "../nufxlib/NufxLib.h" #include "../reformat/Reformat.h" #include #include @@ -166,13 +166,14 @@ public: long GetIndex(void) const { return fIndex; } void SetIndex(long idx) { fIndex = idx; } - const char* GetPathName(void) const { return fPathName; } - void SetPathName(const char* path); - const char* GetFileName(void); - const char* GetFileNameExtension(void); // returns e.g. ".SHK" - void SetSubVolName(const char* name); - const char* GetSubVolName(void) const { return fSubVolName; } - const char* GetDisplayName(void) const; // not really "const" + const WCHAR* GetPathName(void) const { return fPathName; } + void SetPathName(const WCHAR* path); + const WCHAR* GetFileName(void); + const WCHAR* GetFileNameExtension(void); // returns e.g. ".SHK" + CStringA GetFileNameExtensionA(void); + void SetSubVolName(const WCHAR* name); + const WCHAR* GetSubVolName(void) const { return fSubVolName; } + const WCHAR* GetDisplayName(void) const; // not really "const" char GetFssep(void) const { return fFssep; } void SetFssep(char fssep) { fFssep = fssep; } @@ -188,8 +189,8 @@ public: void SetModWhen(time_t when) { fModWhen = when; } RecordKind GetRecordKind(void) const { return fRecordKind; } void SetRecordKind(RecordKind recordKind) { fRecordKind = recordKind; } - const char* GetFormatStr(void) const { return fFormatStr; } - void SetFormatStr(const char* str) { fFormatStr = str; } // arg not copied, must be static! + const WCHAR* GetFormatStr(void) const { return fFormatStr; } + void SetFormatStr(const WCHAR* str) { fFormatStr = str; } // arg not copied, must be static! LONGLONG GetCompressedLen(void) const { return fCompressedLen; } void SetCompressedLen(LONGLONG len) { fCompressedLen = len; } LONGLONG GetUncompressedLen(void) const { @@ -226,33 +227,33 @@ public: void SetNext(GenericEntry* pEntry) { fpNext = pEntry; } // Utility functions. - const char* GetFileTypeString(void) const; - static bool CheckHighASCII(const unsigned char* buffer, + const WCHAR* GetFileTypeString(void) const; + static bool CheckHighASCII(const BYTE* buffer, unsigned long count); - static ConvertEOL DetermineConversion(const unsigned char* buffer, + static ConvertEOL DetermineConversion(const BYTE* buffer, long count, EOLType* pSourceType, ConvertHighASCII* pConvHA); static int GenericEntry::WriteConvert(FILE* fp, const char* buf, size_t len, ConvertEOL* pConv, ConvertHighASCII* pConvHA, bool* pLastCR); protected: - static void SpacesToUnderscores(char* buf); + static void SpacesToUnderscores(WCHAR* buf); private: - char* fPathName; - const char* fFileName; // points within fPathName - const char* fFileNameExtension; // points within fPathName + WCHAR* fPathName; + const WCHAR* fFileName; // points within fPathName + const WCHAR* fFileNameExtension; // points within fPathName char fFssep; - char* fSubVolName; // sub-volume prefix, or nil if none - char* fDisplayName; // combination of sub-vol and path + WCHAR* fSubVolName; // sub-volume prefix, or nil if none + WCHAR* fDisplayName; // combination of sub-vol and path long fFileType; long fAuxType; long fAccess; time_t fCreateWhen; time_t fModWhen; RecordKind fRecordKind; // forked file, disk image, ?? - const char* fFormatStr; // static str; compression or fs format + const WCHAR* fFormatStr; // static str; compression or fs format //LONGLONG fUncompressedLen; LONGLONG fDataForkLen; // also for disk images LONGLONG fRsrcForkLen; // set to 0 when nonexistent @@ -318,10 +319,10 @@ public: } OpenResult; // Open an archive and do fun things with the innards. - virtual OpenResult Open(const char* filename, bool readOnly, + virtual OpenResult Open(const WCHAR* filename, bool readOnly, CString* pErrMsg) = 0; // Create a new archive with the specified name. - virtual CString New(const char* filename, const void* options) = 0; + virtual CString New(const WCHAR* filename, const void* options) = 0; // Flush any unwritten data to disk virtual CString Flush(void) = 0; // Force a re-read from the underlying storage. @@ -357,7 +358,7 @@ public: const AddFilesDialog* pAddOpts) = 0; // Create a subdirectory. virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const char* newName) = 0; + const WCHAR* newName) = 0; // Test a set of files. virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0; @@ -372,9 +373,9 @@ public: // Rename a volume (or sub-volume) virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const char* newName) = 0; + const WCHAR* newName) = 0; virtual CString TestVolumeName(const DiskFS* pDiskFS, - const char* newName) const = 0; + const WCHAR* newName) const = 0; // Recompress a set of files. virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, @@ -419,10 +420,10 @@ public: virtual long GetCapability(Capability cap) = 0; // Get the pathname of the file we opened. - const char* GetPathName(void) const { return fPathName; } + const WCHAR* GetPathName(void) const { return fPathName; } // Generic utility function. - static CString GenDerivedTempName(const char* filename); + static CString GenDerivedTempName(const WCHAR* filename); static int ComparePaths(const CString& name1, char fssep1, const CString& name2, char fssep2); @@ -433,6 +434,9 @@ public: * * It's based on the NuFileDetails class from NufxLib (which used to be * used everywhere). + * + * TODO: xyzzy: NuFileDetails cast requires us to store pathnames with + * narrow strings. */ class FileDetails { public: @@ -482,11 +486,23 @@ public: * Data fields. While transitioning from general use of NuFileDetails * (v1.2.x to v2.0) I'm just going to leave these public. */ + //NuThreadID threadID; /* data, rsrc, disk img? */ FileKind entryKind; + + /* + * Original full pathname as found on Windows. + */ CString origName; - CString storageName; /* normalized (NOT FS-normalized) */ + /* + * "Normalized" pathname. This is the full path with any of our + * added bits removed (e.g. file type & fork identifiers). It has + * not been sanitized for any specific target filesystem. See also + * PathProposal::LocalToArchive(). + */ + CString storageName; + //NuFileSysID fileSysID; DiskImg::FSFormat fileSysFmt; unsigned short fileSysInfo; /* fssep lurks here */ @@ -504,8 +520,8 @@ public: // Transfer files, one at a time, into this archive from another. virtual void XferPrepare(const XferFileOptions* pXferOpts) = 0; - virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf, - long dataLen, unsigned char** pRsrcBuf, long rsrcLen) = 0; + virtual CString XferFile(FileDetails* pDetails, BYTE** pDataBuf, + long dataLen, BYTE** pRsrcBuf, long rsrcLen) = 0; virtual void XferAbort(CWnd* pMsgWnd) = 0; virtual void XferFinish(CWnd* pMsgWnd) = 0; static void UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime); @@ -514,17 +530,17 @@ protected: virtual void DeleteEntries(void); /* NuLib2-derived recursive directory add functions */ - void ReplaceFssep(char* str, char oldc, char newc, char newSubst); - NuError GetFileDetails(const AddFilesDialog* pAddOpts, const char* pathname, - struct stat* psb, FileDetails* pDetails); - Win32dirent* OpenDir(const char* name); + void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst); + NuError GetFileDetails(const AddFilesDialog* pAddOpts, const WCHAR* pathname, + struct _stat* psb, FileDetails* pDetails); + Win32dirent* OpenDir(const WCHAR* name); Win32dirent* ReadDir(Win32dirent* dir); void CloseDir(Win32dirent* dir); NuError Win32AddDirectory(const AddFilesDialog* pAddOpts, - const char* dirName, CString* pErrMsg); + const WCHAR* dirName, CString* pErrMsg); NuError Win32AddFile(const AddFilesDialog* pAddOpts, - const char* pathname, CString* pErrMsg); - NuError AddFile(const AddFilesDialog* pAddOpts, const char* pathname, + const WCHAR* pathname, CString* pErrMsg); + NuError AddFile(const AddFilesDialog* pAddOpts, const WCHAR* pathname, CString* pErrMsg); /* @@ -545,13 +561,13 @@ protected: virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, FileDetails* pDetails) = 0; - void SetPathName(const char* pathName) { - delete fPathName; + void SetPathName(const WCHAR* pathName) { + free(fPathName); if (pathName != nil) { - fPathName = new char[strlen(pathName)+1]; - strcpy(fPathName, pathName); - } else + fPathName = _wcsdup(pathName); + } else { fPathName = nil; + } } bool fReloadFlag; // set after Reload called @@ -562,7 +578,7 @@ private: //CString fNewPathHolder; //CString fOrigPathHolder; - char* fPathName; + WCHAR* fPathName; long fNumEntries; GenericEntry* fEntryHead; GenericEntry* fEntryTail; @@ -669,7 +685,7 @@ public: int GetNumEntries(void) const { return fNumEntries; } // count the #of entries whose display name matches "prefix" - int CountMatchingPrefix(const char* prefix); + int CountMatchingPrefix(const WCHAR* prefix); // debug dump void Dump(void); @@ -687,4 +703,4 @@ private: SelectionEntry* fEntryTail; }; -#endif /*__GENERIC_ARCHIVE__*/ \ No newline at end of file +#endif /*APP_GENERICARCHIVE_H*/ diff --git a/app/HelpTopics.h b/app/HelpTopics.h index 14f649f..96d39df 100644 --- a/app/HelpTopics.h +++ b/app/HelpTopics.h @@ -6,8 +6,8 @@ /* * Constants for help topics. */ -#ifndef __HELP_TOPICS__ -#define __HELP_TOPICS__ +#ifndef APP_HELP_TOPICS_H +#define APP_HELP_TOPICS_H #define HELP_TOPIC_WELCOME 10 #define HELP_TOPIC_DISKEDIT 13 @@ -40,4 +40,4 @@ #define HELP_TOPIC_RENAME_VOLUME 268 #define HELP_TOPIC_EOL_SCAN 272 -#endif /*__HELP_TOPICS__*/ +#endif /*APP_HELP_TOPICS_H*/ diff --git a/app/ImageFormatDialog.cpp b/app/ImageFormatDialog.cpp index 3d8f14a..f7816b6 100644 --- a/app/ImageFormatDialog.cpp +++ b/app/ImageFormatDialog.cpp @@ -30,83 +30,83 @@ END_MESSAGE_MAP() * to get the text string. That way we'd be consistent. */ typedef struct ImageFormatDialog::ConvTable { - int enumval; // a DiskImg::enum type - const char* name; + int enumval; // a DiskImg::enum type + const WCHAR* name; } ConvTable; const int kLastEntry = -1; /* DiskImg::OuterFormat */ static const ConvTable gOuterFormats[] = { - { DiskImg::kOuterFormatUnknown, "Unknown format" }, - { DiskImg::kOuterFormatNone, "(none)" }, -// { DiskImg::kOuterFormatCompress, "UNIX compress" }, - { DiskImg::kOuterFormatGzip, "gzip" }, -// { DiskImg::kOuterFormatBzip2, "bzip2" }, - { DiskImg::kOuterFormatZip, "Zip archive" }, + { DiskImg::kOuterFormatUnknown, L"Unknown format" }, + { DiskImg::kOuterFormatNone, L"(none)" }, +// { DiskImg::kOuterFormatCompress, L"UNIX compress" }, + { DiskImg::kOuterFormatGzip, L"gzip" }, +// { DiskImg::kOuterFormatBzip2, L"bzip2" }, + { DiskImg::kOuterFormatZip, L"Zip archive" }, { kLastEntry, nil } }; /* DiskImg::FileFormat */ static const ConvTable gFileFormats[] = { - { DiskImg::kFileFormatUnknown, "Unknown format" }, - { DiskImg::kFileFormatUnadorned, "Unadorned raw data" }, - { DiskImg::kFileFormat2MG, "2MG" }, - { DiskImg::kFileFormatNuFX, "NuFX (ShrinkIt)" }, - { DiskImg::kFileFormatDiskCopy42, "DiskCopy 4.2" }, -// { DiskImg::kFileFormatDiskCopy60, "DiskCopy 6.0" }, -// { DiskImg::kFileFormatDavex, "Davex volume image" }, - { DiskImg::kFileFormatSim2eHDV, "Sim //e HDV" }, - { DiskImg::kFileFormatDDD, "DDD" }, - { DiskImg::kFileFormatTrackStar, "TrackStar image" }, - { DiskImg::kFileFormatFDI, "FDI image" }, -// { DiskImg::kFileFormatDDDDeluxe, "DDDDeluxe" }, + { DiskImg::kFileFormatUnknown, L"Unknown format" }, + { DiskImg::kFileFormatUnadorned, L"Unadorned raw data" }, + { DiskImg::kFileFormat2MG, L"2MG" }, + { DiskImg::kFileFormatNuFX, L"NuFX (ShrinkIt)" }, + { DiskImg::kFileFormatDiskCopy42, L"DiskCopy 4.2" }, +// { DiskImg::kFileFormatDiskCopy60, L"DiskCopy 6.0" }, +// { DiskImg::kFileFormatDavex, L"Davex volume image" }, + { DiskImg::kFileFormatSim2eHDV, L"Sim //e HDV" }, + { DiskImg::kFileFormatDDD, L"DDD" }, + { DiskImg::kFileFormatTrackStar, L"TrackStar image" }, + { DiskImg::kFileFormatFDI, L"FDI image" }, +// { DiskImg::kFileFormatDDDDeluxe, L"DDDDeluxe" }, { kLastEntry, nil } }; /* DiskImg::PhysicalFormat */ static const ConvTable gPhysicalFormats[] = { - { DiskImg::kPhysicalFormatUnknown, "Unknown format" }, - { DiskImg::kPhysicalFormatSectors, "Sectors" }, - { DiskImg::kPhysicalFormatNib525_6656, "Raw nibbles (6656-byte)" }, - { DiskImg::kPhysicalFormatNib525_6384, "Raw nibbles (6384-byte)" }, - { DiskImg::kPhysicalFormatNib525_Var, "Raw nibbles (variable len)" }, + { DiskImg::kPhysicalFormatUnknown, L"Unknown format" }, + { DiskImg::kPhysicalFormatSectors, L"Sectors" }, + { DiskImg::kPhysicalFormatNib525_6656, L"Raw nibbles (6656-byte)" }, + { DiskImg::kPhysicalFormatNib525_6384, L"Raw nibbles (6384-byte)" }, + { DiskImg::kPhysicalFormatNib525_Var, L"Raw nibbles (variable len)" }, { kLastEntry, nil } }; /* DiskImg::SectorOrder */ static const ConvTable gSectorOrders[] = { - { DiskImg::kSectorOrderUnknown, "Unknown ordering" }, - { DiskImg::kSectorOrderProDOS, "ProDOS block ordering" }, - { DiskImg::kSectorOrderDOS, "DOS sector ordering" }, - { DiskImg::kSectorOrderCPM, "CP/M block ordering" }, - { DiskImg::kSectorOrderPhysical, "Physical sector ordering" }, + { DiskImg::kSectorOrderUnknown, L"Unknown ordering" }, + { DiskImg::kSectorOrderProDOS, L"ProDOS block ordering" }, + { DiskImg::kSectorOrderDOS, L"DOS sector ordering" }, + { DiskImg::kSectorOrderCPM, L"CP/M block ordering" }, + { DiskImg::kSectorOrderPhysical, L"Physical sector ordering" }, { kLastEntry, nil } }; /* DiskImg::FSFormat */ static const ConvTable gFSFormats[] = { - { DiskImg::kFormatUnknown, "Unknown filesystem" }, - { DiskImg::kFormatGenericDOSOrd, "Generic DOS sectors" }, - { DiskImg::kFormatGenericProDOSOrd, "Generic ProDOS blocks" }, - { DiskImg::kFormatGenericPhysicalOrd, "Generic raw sectors" }, - { DiskImg::kFormatGenericCPMOrd, "Generic CP/M blocks" }, - { DiskImg::kFormatProDOS, "ProDOS" }, - { DiskImg::kFormatDOS33, "DOS 3.3" }, - { DiskImg::kFormatDOS32, "DOS 3.2" }, - { DiskImg::kFormatPascal, "Pascal" }, - { DiskImg::kFormatMacHFS, "HFS" }, -// { DiskImg::kFormatMacMFS, "MFS" }, -// { DiskImg::kFormatLisa, "Lisa" }, - { DiskImg::kFormatCPM, "CP/M" }, - { DiskImg::kFormatMSDOS, "MS-DOS FAT" }, -// { DiskImg::kFormatISO9660, "ISO-9660" }, - { DiskImg::kFormatUNIDOS, "UNIDOS (400K DOS x2)" }, - { DiskImg::kFormatOzDOS, "OzDOS (400K DOS x2)" }, - { DiskImg::kFormatCFFA4, "CFFA (4 or 6 partitions)" }, - { DiskImg::kFormatCFFA8, "CFFA (8 partitions)" }, - { DiskImg::kFormatMacPart, "Macintosh partitioned disk" }, - { DiskImg::kFormatMicroDrive, "MicroDrive partitioned disk" }, - { DiskImg::kFormatFocusDrive, "FocusDrive partitioned disk" }, - { DiskImg::kFormatRDOS33, "RDOS 3.3 (16-sector)" }, - { DiskImg::kFormatRDOS32, "RDOS 3.2 (13-sector)" }, - { DiskImg::kFormatRDOS3, "RDOS 3 (cracked 13-sector)" }, + { DiskImg::kFormatUnknown, L"Unknown filesystem" }, + { DiskImg::kFormatGenericDOSOrd, L"Generic DOS sectors" }, + { DiskImg::kFormatGenericProDOSOrd, L"Generic ProDOS blocks" }, + { DiskImg::kFormatGenericPhysicalOrd, L"Generic raw sectors" }, + { DiskImg::kFormatGenericCPMOrd, L"Generic CP/M blocks" }, + { DiskImg::kFormatProDOS, L"ProDOS" }, + { DiskImg::kFormatDOS33, L"DOS 3.3" }, + { DiskImg::kFormatDOS32, L"DOS 3.2" }, + { DiskImg::kFormatPascal, L"Pascal" }, + { DiskImg::kFormatMacHFS, L"HFS" }, +// { DiskImg::kFormatMacMFS, L"MFS" }, +// { DiskImg::kFormatLisa, L"Lisa" }, + { DiskImg::kFormatCPM, L"CP/M" }, + { DiskImg::kFormatMSDOS, L"MS-DOS FAT" }, +// { DiskImg::kFormatISO9660, L"ISO-9660" }, + { DiskImg::kFormatUNIDOS, L"UNIDOS (400K DOS x2)" }, + { DiskImg::kFormatOzDOS, L"OzDOS (400K DOS x2)" }, + { DiskImg::kFormatCFFA4, L"CFFA (4 or 6 partitions)" }, + { DiskImg::kFormatCFFA8, L"CFFA (8 partitions)" }, + { DiskImg::kFormatMacPart, L"Macintosh partitioned disk" }, + { DiskImg::kFormatMicroDrive, L"MicroDrive partitioned disk" }, + { DiskImg::kFormatFocusDrive, L"FocusDrive partitioned disk" }, + { DiskImg::kFormatRDOS33, L"RDOS 3.3 (16-sector)" }, + { DiskImg::kFormatRDOS32, L"RDOS 3.2 (13-sector)" }, + { DiskImg::kFormatRDOS3, L"RDOS 3 (cracked 13-sector)" }, { kLastEntry, nil } }; @@ -230,7 +230,7 @@ ImageFormatDialog::LoadComboBox(int boxID, const ConvTable* pTable, int dflt) if (pTable == gFSFormats && !fAllowGenericFormats && DiskImg::IsGenericFormat((DiskImg::FSFormat)pTable[idx].enumval)) { - WMSG1("LoadComboBox skipping '%s'\n", pTable[idx].name); + WMSG1("LoadComboBox skipping '%ls'\n", pTable[idx].name); idxShift++; } else { // Note to self: AddString returns the combo box item ID; @@ -281,7 +281,7 @@ ImageFormatDialog::ConvComboSel(int boxID, const ConvTable* pTable) ASSERT(enumval == pTable[idx].enumval); } - WMSG3(" Returning ev=%d for %d entry '%s'\n", + WMSG3(" Returning ev=%d for %d entry '%ls'\n", enumval, boxID, pTable[idx].name); return enumval; @@ -321,7 +321,7 @@ ImageFormatDialog::OnOK(void) ConvComboSel(IDC_DECONF_FSFORMAT, gFSFormats); if (fSectorOrder == DiskImg::kSectorOrderUnknown) { - MessageBox("You must choose a sector ordering.", "Error", + MessageBox(L"You must choose a sector ordering.", L"Error", MB_OK | MB_ICONEXCLAMATION); return; } @@ -329,9 +329,9 @@ ImageFormatDialog::OnOK(void) if (fFSFormat == DiskImg::kFormatUnknown && !fAllowUnknown) { - MessageBox("You must choose a filesystem format. If not known," - " use one of the 'generic' entries.", - "Error", MB_OK | MB_ICONEXCLAMATION); + MessageBox(L"You must choose a filesystem format. If not known," + L" use one of the 'generic' entries.", + L"Error", MB_OK | MB_ICONEXCLAMATION); return; } diff --git a/app/ImageFormatDialog.h b/app/ImageFormatDialog.h index 58febaf..7552d54 100644 --- a/app/ImageFormatDialog.h +++ b/app/ImageFormatDialog.h @@ -6,8 +6,8 @@ /* * Dialog asking the user to confirm certain details of a disk image. */ -#ifndef __IMAGEFORMATDIALOG__ -#define __IMAGEFORMATDIALOG__ +#ifndef APP_IMAGEFORMATDIALOG_H +#define APP_IMAGEFORMATDIALOG_H //#include #include "resource.h" @@ -24,7 +24,7 @@ public: CDialog(IDD_DECONF, pParentWnd) { fInitialized = false; - fFileSource = ""; + fFileSource = L""; fAllowUnknown = false; fOuterFormat = DiskImg::kOuterFormatUnknown; fFileFormat = DiskImg::kFileFormatUnknown; @@ -78,4 +78,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__IMAGEFORMATDIALOG__*/ \ No newline at end of file +#endif /*APP_IMAGEFORMATDIALOG_H*/ diff --git a/app/Main.cpp b/app/Main.cpp index 239c80f..64e7d08 100644 --- a/app/Main.cpp +++ b/app/Main.cpp @@ -26,59 +26,58 @@ /* use MFC's fancy version of new for debugging */ //#define new DEBUG_NEW -static const char* kWebSiteURL = "http://www.faddensoft.com/"; +static const WCHAR kWebSiteURL[] = L"http://www.faddensoft.com/"; /* * Filters for the "open file" command. In some cases a file may be opened * in more than one format, so it's necessary to keep track of what the * file filter was set to when the file was opened. */ -const char MainWindow::kOpenNuFX[] = - "ShrinkIt Archives (.shk .sdk .bxy .sea .bse)|*.shk;*.sdk;*.bxy;*.sea;*.bse|"; -const char MainWindow::kOpenBinaryII[] = - "Binary II Archives (.bny .bqy .bxy)|*.bny;*.bqy;*.bxy|"; -const char MainWindow::kOpenACU[] = - "ACU Archives (.acu)|*.acu|"; -const char MainWindow::kOpenDiskImage[] = - "Disk Images (.shk .sdk .dsk .po .do .d13 .2mg .img .nib .nb2 .raw .hdv .dc .dc6 .ddd .app .fdi .iso .gz .zip)|" - "*.shk;*.sdk;*.dsk;*.po;*.do;*.d13;*.2mg;*.img;*.nib;*.nb2;*.raw;*.hdv;*.dc;*.dc6;*.ddd;*.app;*.fdi;*.iso;*.gz;*.zip|"; -const char MainWindow::kOpenAll[] = - "All Files (*.*)|*.*|"; -const char MainWindow::kOpenEnd[] = - "|"; +const WCHAR MainWindow::kOpenNuFX[] = + L"ShrinkIt Archives (.shk .sdk .bxy .sea .bse)|*.shk;*.sdk;*.bxy;*.sea;*.bse|"; +const WCHAR MainWindow::kOpenBinaryII[] = + L"Binary II Archives (.bny .bqy .bxy)|*.bny;*.bqy;*.bxy|"; +const WCHAR MainWindow::kOpenACU[] = + L"ACU Archives (.acu)|*.acu|"; +const WCHAR MainWindow::kOpenDiskImage[] = + L"Disk Images (.shk .sdk .dsk .po .do .d13 .2mg .img .nib .nb2 .raw .hdv .dc .dc6 .ddd .app .fdi .iso .gz .zip)|" + L"*.shk;*.sdk;*.dsk;*.po;*.do;*.d13;*.2mg;*.img;*.nib;*.nb2;*.raw;*.hdv;*.dc;*.dc6;*.ddd;*.app;*.fdi;*.iso;*.gz;*.zip|"; +const WCHAR MainWindow::kOpenAll[] = + L"All Files (*.*)|*.*|"; +const WCHAR MainWindow::kOpenEnd[] = + L"|"; static const struct { - //const char* extension; - char extension[4]; + WCHAR extension[4]; FilterIndex idx; } gExtensionToIndex[] = { - { "shk", kFilterIndexNuFX }, - { "bxy", kFilterIndexNuFX }, - { "bse", kFilterIndexNuFX }, - { "sea", kFilterIndexNuFX }, - { "bny", kFilterIndexBinaryII }, - { "bqy", kFilterIndexBinaryII }, - { "acu", kFilterIndexACU }, - { "dsk", kFilterIndexDiskImage }, - { "po", kFilterIndexDiskImage }, - { "do", kFilterIndexDiskImage }, - { "d13", kFilterIndexDiskImage }, - { "2mg", kFilterIndexDiskImage }, - { "img", kFilterIndexDiskImage }, - { "sdk", kFilterIndexDiskImage }, - { "raw", kFilterIndexDiskImage }, - { "ddd", kFilterIndexDiskImage }, - { "app", kFilterIndexDiskImage }, - { "fdi", kFilterIndexDiskImage }, - { "iso", kFilterIndexDiskImage }, - { "gz", kFilterIndexDiskImage }, // assume disk image inside - { "zip", kFilterIndexDiskImage }, // assume disk image inside + { L"shk", kFilterIndexNuFX }, + { L"bxy", kFilterIndexNuFX }, + { L"bse", kFilterIndexNuFX }, + { L"sea", kFilterIndexNuFX }, + { L"bny", kFilterIndexBinaryII }, + { L"bqy", kFilterIndexBinaryII }, + { L"acu", kFilterIndexACU }, + { L"dsk", kFilterIndexDiskImage }, + { L"po", kFilterIndexDiskImage }, + { L"do", kFilterIndexDiskImage }, + { L"d13", kFilterIndexDiskImage }, + { L"2mg", kFilterIndexDiskImage }, + { L"img", kFilterIndexDiskImage }, + { L"sdk", kFilterIndexDiskImage }, + { L"raw", kFilterIndexDiskImage }, + { L"ddd", kFilterIndexDiskImage }, + { L"app", kFilterIndexDiskImage }, + { L"fdi", kFilterIndexDiskImage }, + { L"iso", kFilterIndexDiskImage }, + { L"gz", kFilterIndexDiskImage }, // assume disk image inside + { L"zip", kFilterIndexDiskImage }, // assume disk image inside }; -const char* MainWindow::kModeNuFX = _T("nufx"); -const char* MainWindow::kModeBinaryII = _T("bin2"); -const char* MainWindow::kModeACU = _T("acu"); -const char* MainWindow::kModeDiskImage = _T("disk"); +const WCHAR MainWindow::kModeNuFX[] = L"nufx"; +const WCHAR MainWindow::kModeBinaryII[] = L"bin2"; +const WCHAR MainWindow::kModeACU[] = L"acu"; +const WCHAR MainWindow::kModeDiskImage[] = L"disk"; /* @@ -198,7 +197,7 @@ END_MESSAGE_MAP() */ MainWindow::MainWindow() { - static const char* kAppName = _T("CiderPress"); + static const WCHAR kAppName[] = L"CiderPress"; fpContentList = nil; fpOpenArchive = nil; @@ -368,71 +367,71 @@ MainWindow::ProcessCommandLine(void) /* * Get the command line and break it down into an argument vector. */ - const char* cmdLine = ::GetCommandLine(); - if (cmdLine == nil || strlen(cmdLine) == 0) + const WCHAR* cmdLine = ::GetCommandLine(); + if (cmdLine == nil || wcslen(cmdLine) == 0) return; - char* mangle = strdup(cmdLine); + WCHAR* mangle = wcsdup(cmdLine); if (mangle == nil) return; - WMSG1("Mangling '%s'\n", mangle); - char* argv[8]; + WMSG1("Mangling '%ls'\n", mangle); + WCHAR* argv[8]; int argc = 8; VectorizeString(mangle, argv, &argc); WMSG0("Args:\n"); for (int i = 0; i < argc; i++) { - WMSG2(" %d '%s'\n", i, argv[i]); + WMSG2(" %d '%ls'\n", i, argv[i]); } /* * Figure out what the arguments are. */ - const char* filename = nil; - const char* dispName = nil; + const WCHAR* filename = nil; + const WCHAR* dispName = nil; int filterIndex = kFilterIndexGeneric; bool temp = false; - for (i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { - if (strcasecmp(argv[i], "-mode") == 0) { + if (wcsicmp(argv[i], L"-mode") == 0) { if (i == argc-1) { WMSG0("WARNING: -mode specified without mode\n"); } else i++; - if (strcasecmp(argv[i], kModeNuFX) == 0) + if (wcsicmp(argv[i], kModeNuFX) == 0) filterIndex = kFilterIndexNuFX; - else if (strcasecmp(argv[i], kModeBinaryII) == 0) + else if (wcsicmp(argv[i], kModeBinaryII) == 0) filterIndex = kFilterIndexBinaryII; - else if (strcasecmp(argv[i], kModeACU) == 0) + else if (wcsicmp(argv[i], kModeACU) == 0) filterIndex = kFilterIndexACU; - else if (strcasecmp(argv[i], kModeDiskImage) == 0) + else if (wcsicmp(argv[i], kModeDiskImage) == 0) filterIndex = kFilterIndexDiskImage; else { - WMSG1("WARNING: unrecognized mode '%s'\n", argv[i]); + WMSG1("WARNING: unrecognized mode '%ls'\n", argv[i]); } - } else if (strcasecmp(argv[i], "-dispname") == 0) { + } else if (wcsicmp(argv[i], L"-dispname") == 0) { if (i == argc-1) { WMSG0("WARNING: -dispname specified without name\n"); } else i++; dispName = argv[i]; - } else if (strcasecmp(argv[i], "-temparc") == 0) { + } else if (wcsicmp(argv[i], L"-temparc") == 0) { temp = true; - } else if (strcasecmp(argv[i], "-install") == 0) { + } else if (wcsicmp(argv[i], L"-install") == 0) { // see MyApp::InitInstance WMSG0("Got '-install' flag, doing nothing\n"); - } else if (strcasecmp(argv[i], "-uninstall") == 0) { + } else if (wcsicmp(argv[i], L"-uninstall") == 0) { // see MyApp::InitInstance WMSG0("Got '-uninstall' flag, doing nothing\n"); } else { - WMSG1("WARNING: unrecognized flag '%s'\n", argv[i]); + WMSG1("WARNING: unrecognized flag '%ls'\n", argv[i]); } } else { /* must be the filename */ if (i != argc-1) { - WMSG1("WARNING: ignoring extra arguments (e.g. '%s')\n", + WMSG1("WARNING: ignoring extra arguments (e.g. '%ls')\n", argv[i+1]); } filename = argv[i]; @@ -444,8 +443,8 @@ MainWindow::ProcessCommandLine(void) } WMSG0("Argument handling:\n"); - WMSG3(" index=%d temp=%d filename='%s'\n", - filterIndex, temp, filename == nil ? "(nil)" : filename); + WMSG3(" index=%d temp=%d filename='%ls'\n", + filterIndex, temp, filename == nil ? L"(null)" : filename); if (filename != nil) { PathName path(filename); @@ -469,12 +468,12 @@ MainWindow::ProcessCommandLine(void) /* if it's a temporary file, arrange to have it deleted before exit */ if (temp) { - int len = strlen(filename); + int len = wcslen(filename); - if (len > 4 && strcasecmp(filename + (len-4), ".tmp") == 0) { + if (len > 4 && wcsicmp(filename + (len-4), L".tmp") == 0) { fDeleteList.Add(filename); } else { - WMSG1("NOT adding '%s' to DeleteList -- does not end in '.tmp'\n", + WMSG1("NOT adding '%ls' to DeleteList -- does not end in '.tmp'\n", filename); } } @@ -525,7 +524,7 @@ MainWindow::OnCreate(LPCREATESTRUCT lpcs) fStatusBar.SetIndicators(indicators, NELEM(indicators)); //fStatusBar.SetPaneInfo(0, ID_SEPARATOR, SBPS_NOBORDERS | SBPS_STRETCH, 0); - fStatusBar.SetPaneText(kProgressPane, ""); + fStatusBar.SetPaneText(kProgressPane, L""); return 0; } @@ -618,7 +617,7 @@ MainWindow::OnLateInit(UINT, LONG) default: ASSERT(false); CString confused; - confused.Format("Registration check failed. %s", (LPCTSTR) result); + confused.Format(L"Registration check failed. %ls", (LPCWSTR) result); result = confused; goto fail; } @@ -992,7 +991,7 @@ MainWindow::ApplyNow(PrefsSheet* pPS) fPreferences.SetPrefBool(kPrConvResources, pPS->fFviewPage.fConvResources != 0); fPreferences.SetPrefString(kPrTempPath, pPS->fFilesPage.fTempPath); - WMSG1("--- Temp path now '%s'\n", fPreferences.GetPrefString(kPrTempPath)); + WMSG1("--- Temp path now '%ls'\n", fPreferences.GetPrefString(kPrTempPath)); fPreferences.SetPrefString(kPrExtViewerExts, pPS->fFilesPage.fExtViewerExts); @@ -1122,17 +1121,17 @@ MainWindow::OnHelpWebSite(void) { int err; - err = (int) ::ShellExecute(m_hWnd, _T("open"), kWebSiteURL, NULL, NULL, + err = (int) ::ShellExecute(m_hWnd, L"open", kWebSiteURL, NULL, NULL, SW_SHOWNORMAL); if (err <= 32) { CString msg; if (err == ERROR_FILE_NOT_FOUND) { - msg = "Windows call failed: web browser not found. (Sometimes" - " it mistakenly reports this when IE is not the default" - " browser.)"; + msg = L"Windows call failed: web browser not found. (Sometimes" + L" it mistakenly reports this when IE is not the default" + L" browser.)"; ShowFailureMsg(this, msg, IDS_FAILED); } else { - msg.Format("Unable to launch web browser (err=%d).", err); + msg.Format(L"Unable to launch web browser (err=%d).", err); ShowFailureMsg(this, msg, IDS_FAILED); } } @@ -1178,11 +1177,11 @@ MainWindow::OnFileNewArchive(void) GenericArchive* pOpenArchive; CString errMsg; - CFileDialog dlg(FALSE, _T("shk"), NULL, + CFileDialog dlg(FALSE, L"shk", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "ShrinkIt Archives (*.shk)|*.shk||", this); + L"ShrinkIt Archives (*.shk)|*.shk||", this); - dlg.m_ofn.lpstrTitle = "New Archive"; + dlg.m_ofn.lpstrTitle = L"New Archive"; dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) @@ -1193,7 +1192,7 @@ MainWindow::OnFileNewArchive(void) fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); filename = dlg.GetPathName(); - WMSG1("NEW FILE '%s'\n", filename); + WMSG1("NEW FILE '%ls'\n", (LPCWSTR) filename); /* remove file if it already exists */ errMsg = RemoveFile(filename); @@ -1237,7 +1236,7 @@ MainWindow::OnFileOpen(void) openFilters += kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "shk", NULL, + CFileDialog dlg(TRUE, L"shk", NULL, OFN_FILEMUSTEXIST, openFilters, this); dlg.m_ofn.nFilterIndex = fPreferences.GetPrefLong(kPrLastOpenFilterIndex); @@ -1291,7 +1290,7 @@ MainWindow::OnUpdateFileOpenVolume(CCmdUI* pCmdUI) * Open an archive. */ void -MainWindow::DoOpenArchive(const char* pathName, const char* ext, +MainWindow::DoOpenArchive(const WCHAR* pathName, const WCHAR* ext, int filterIndex, bool readOnly) { if (LoadArchive(pathName, ext, filterIndex, readOnly, false) == 0) { @@ -1564,11 +1563,11 @@ MainWindow::HandleDoubleClick(void) if (pEntry == nil) return; - WMSG1(" Double-click GOT '%s'\n", pEntry->GetPathName()); - const char* ext; + WMSG1(" Double-click GOT '%ls'\n", pEntry->GetPathName()); + const WCHAR* ext; long fileType, auxType; - ext = FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); + ext = PathName::FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); fileType = pEntry->GetFileType(); auxType = pEntry->GetAuxType(); @@ -1595,14 +1594,14 @@ MainWindow::HandleDoubleClick(void) CString extViewerExts; extViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); if (ext != nil && MatchSemicolonList(extViewerExts, ext+1)) { - WMSG1(" Launching external viewer for '%s'\n", ext); + WMSG1(" Launching external viewer for '%ls'\n", ext); TmpExtractForExternal(pEntry); handled = true; } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindFile) { if ((ext != nil && ( - stricmp(ext, ".shk") == 0 || - stricmp(ext, ".sdk") == 0 || - stricmp(ext, ".bxy") == 0 )) || + wcsicmp(ext, L".shk") == 0 || + wcsicmp(ext, L".sdk") == 0 || + wcsicmp(ext, L".bxy") == 0)) || (fileType == 0xe0 && auxType == 0x8002)) { WMSG0(" Guessing NuFX\n"); @@ -1610,8 +1609,8 @@ MainWindow::HandleDoubleClick(void) handled = true; } else if ((ext != nil && ( - stricmp(ext, ".bny") == 0 || - stricmp(ext, ".bqy") == 0 )) || + wcsicmp(ext, L".bny") == 0 || + wcsicmp(ext, L".bqy") == 0)) || (fileType == 0xe0 && auxType == 0x8000)) { WMSG0(" Guessing Binary II\n"); @@ -1619,7 +1618,7 @@ MainWindow::HandleDoubleClick(void) handled = true; } else if ((ext != nil && ( - stricmp(ext, ".acu") == 0 )) || + wcsicmp(ext, L".acu") == 0)) || (fileType == 0xe0 && auxType == 0x8001)) { WMSG0(" Guessing ACU\n"); @@ -1661,7 +1660,7 @@ MainWindow::HandleDoubleClick(void) */ int MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, - const char* modeStr) + const WCHAR* modeStr) { CString dispName; bool mustDelete = false; @@ -1674,13 +1673,13 @@ MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, dispName = pEntry->GetFileName(); dispName.Replace('"', '_'); - char nameBuf[MAX_PATH]; + WCHAR nameBuf[MAX_PATH]; UINT unique; unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), - "CPfile", 0, nameBuf); + L"CPfile", 0, nameBuf); if (unique == 0) { DWORD dwerr = ::GetLastError(); - WMSG2("GetTempFileName failed on '%s' (err=%ld)\n", + WMSG2("GetTempFileName failed on '%ls' (err=%ld)\n", fPreferences.GetPrefString(kPrTempPath), dwerr); return dwerr; } @@ -1693,9 +1692,9 @@ MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, int result; FILE* fp; - fp = fopen(nameBuf, "wb"); + fp = _wfopen(nameBuf, L"wb"); if (fp != nil) { - WMSG2("Extracting to '%s' (unique=%d)\n", nameBuf, unique); + WMSG2("Extracting to '%ls' (unique=%d)\n", nameBuf, unique); result = pEntry->ExtractThreadToFile(threadKind, fp, GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, &errMsg); @@ -1704,16 +1703,16 @@ MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, /* success */ CString parameters; - parameters.Format("-mode %s -dispname \"%s\" -temparc \"%s\"", + parameters.Format(L"-mode %ls -dispname \"%ls\" -temparc \"%ls\"", modeStr, dispName, nameBuf); int err; - err = (int) ::ShellExecute(m_hWnd, _T("open"), + err = (int) ::ShellExecute(m_hWnd, L"open", gMyApp.GetExeFileName(), parameters, NULL, SW_SHOWNORMAL); if (err <= 32) { CString msg; - msg.Format("Unable to launch CiderPress (err=%d).", err); + msg.Format(L"Unable to launch CiderPress (err=%d).", err); ShowFailureMsg(this, msg, IDS_FAILED); } else { /* during dev, "missing DLL" causes false-positive success */ @@ -1725,13 +1724,13 @@ MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, } } else { CString msg; - msg.Format("Unable to open temp file '%s'.", nameBuf); + msg.Format(L"Unable to open temp file '%ls'.", nameBuf); ::ShowFailureMsg(this, msg, IDS_FAILED); } if (mustDelete) { - WMSG1("Deleting '%s'\n", nameBuf); - unlink(nameBuf); + WMSG1("Deleting '%ls'\n", nameBuf); + _wunlink(nameBuf); } return 0; @@ -1758,23 +1757,23 @@ MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, int MainWindow::TmpExtractForExternal(GenericEntry* pEntry) { - const char* ext; + const WCHAR* ext; - ext = FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); + ext = PathName::FindExtension(pEntry->GetPathName(), pEntry->GetFssep()); - char nameBuf[MAX_PATH]; + WCHAR nameBuf[MAX_PATH]; UINT unique; unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), - "CPfile", 0, nameBuf); + L"CPfile", 0, nameBuf); if (unique == 0) { DWORD dwerr = ::GetLastError(); - WMSG2("GetTempFileName failed on '%s' (err=%ld)\n", + WMSG2("GetTempFileName failed on '%ls' (err=%ld)\n", fPreferences.GetPrefString(kPrTempPath), dwerr); return dwerr; } fDeleteList.Add(nameBuf); // file is created by GetTempFileName - strcat(nameBuf, ext); + wcscat(nameBuf, ext); /* * Open the temp file and extract the data into it. @@ -1783,10 +1782,10 @@ MainWindow::TmpExtractForExternal(GenericEntry* pEntry) int result; FILE* fp; - fp = fopen(nameBuf, "wb"); + fp = _wfopen(nameBuf, L"wb"); if (fp != nil) { fDeleteList.Add(nameBuf); // second file created by fopen - WMSG2("Extracting to '%s' (unique=%d)\n", nameBuf, unique); + WMSG2("Extracting to '%ls' (unique=%d)\n", nameBuf, unique); result = pEntry->ExtractThreadToFile(GenericEntry::kDataThread, fp, GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, &errMsg); @@ -1795,11 +1794,11 @@ MainWindow::TmpExtractForExternal(GenericEntry* pEntry) /* success */ int err; - err = (int) ::ShellExecute(m_hWnd, _T("open"), nameBuf, NULL, + err = (int) ::ShellExecute(m_hWnd, L"open", nameBuf, NULL, NULL, SW_SHOWNORMAL); if (err <= 32) { CString msg; - msg.Format("Unable to launch external viewer (err=%d).", err); + msg.Format(L"Unable to launch external viewer (err=%d).", err); ShowFailureMsg(this, msg, IDS_FAILED); } else { WMSG0("Successfully launched external viewer\n"); @@ -1809,7 +1808,7 @@ MainWindow::TmpExtractForExternal(GenericEntry* pEntry) } } else { CString msg; - msg.Format("Unable to open temp file '%s'.", nameBuf); + msg.Format(L"Unable to open temp file '%ls'.", nameBuf); ShowFailureMsg(this, msg, IDS_FAILED); } @@ -1858,7 +1857,7 @@ MainWindow::SetProgressBegin(void) if (fpActionProgress != nil) fpActionProgress->SetProgress(0); else - fStatusBar.SetPaneText(kProgressPane, "--%"); + fStatusBar.SetPaneText(kProgressPane, L"--%"); //WMSG0(" Complete: BEGIN\n"); /* redraw stuff with the changes */ @@ -1866,8 +1865,8 @@ MainWindow::SetProgressBegin(void) } int -MainWindow::SetProgressUpdate(int percent, const char* oldName, - const char* newName) +MainWindow::SetProgressUpdate(int percent, const WCHAR* oldName, + const WCHAR* newName) { int status = IDOK; @@ -1878,10 +1877,10 @@ MainWindow::SetProgressUpdate(int percent, const char* oldName, if (newName != nil) fpActionProgress->SetFileName(newName); } else { - char buf[8]; - sprintf(buf, "%d%%", percent); + WCHAR buf[8]; + wsprintf(buf, L"%d%%", percent); fStatusBar.SetPaneText(kProgressPane, buf); - //WMSG1(" Complete: %s\n", buf); + //WMSG1(" Complete: %ls\n", buf); } if (!PeekAndPump()) { @@ -1898,7 +1897,7 @@ MainWindow::SetProgressEnd(void) if (fpActionProgress != nil) fpActionProgress->SetProgress(100); else - fStatusBar.SetPaneText(kProgressPane, ""); + fStatusBar.SetPaneText(kProgressPane, L""); // EventPause(100); // DEBUG DEBUG //WMSG0(" Complete: END\n"); } @@ -1914,13 +1913,13 @@ MainWindow::SetProgressEnd(void) * Returns "true" if we'd like things to continue. */ bool -MainWindow::SetProgressCounter(const char* str, long val) +MainWindow::SetProgressCounter(const WCHAR* str, long val) { /* if the main window is enabled, user could activate menus */ ASSERT(!IsWindowEnabled()); if (fpProgressCounter != nil) { - //WMSG2("SetProgressCounter '%s' %d\n", str, val); + //WMSG2("SetProgressCounter '%ls' %d\n", str, val); CString msg; if (str != nil) @@ -1928,10 +1927,10 @@ MainWindow::SetProgressCounter(const char* str, long val) fpProgressCounter->SetCount((int) val); } else { if (val < 0) { - fStatusBar.SetPaneText(kProgressPane, ""); + fStatusBar.SetPaneText(kProgressPane, L""); } else { CString tmpStr; - tmpStr.Format("%ld", val); + tmpStr.Format(L"%ld", val); fStatusBar.SetPaneText(kProgressPane, tmpStr); } } @@ -2073,7 +2072,7 @@ MainWindow::DrawEmptyClientArea(CDC* pDC, const CRect& clientRect) * Returns 0 on success, nonzero on failure. */ int -MainWindow::LoadArchive(const char* fileName, const char* extension, +MainWindow::LoadArchive(const WCHAR* fileName, const WCHAR* extension, int filterIndex, bool readOnly, bool createFile) { GenericArchive::OpenResult openResult; @@ -2084,7 +2083,7 @@ MainWindow::LoadArchive(const char* fileName, const char* extension, appName.LoadString(IDS_MB_APP_NAME); - WMSG3("LoadArchive: '%s' ro=%d idx=%d\n", fileName, readOnly, filterIndex); + WMSG3("LoadArchive: '%ls' ro=%d idx=%d\n", fileName, readOnly, filterIndex); /* close any existing archive to avoid weirdness from re-open */ CloseArchive(); @@ -2104,7 +2103,7 @@ MainWindow::LoadArchive(const char* fileName, const char* extension, int i; for (i = 0; i < NELEM(gExtensionToIndex); i++) { - if (strcasecmp(extension, gExtensionToIndex[i].extension) == 0) { + if (wcsicmp(extension, gExtensionToIndex[i].extension) == 0) { filterIndex = gExtensionToIndex[i].idx; break; } @@ -2155,7 +2154,7 @@ try_again: delete pOpenArchive; pOpenArchive = nil; - if (strcasecmp(extension, "zip") == 0) { + if (wcsicmp(extension, L"zip") == 0) { errStr = "ZIP archives with multiple files are not supported."; MessageBox(errStr, appName, MB_OK|MB_ICONINFORMATION); result = -1; @@ -2172,8 +2171,8 @@ try_again: /* * Kluge: assume we guessed disk image and were wrong. */ - errStr = "File doesn't appear to be a valid archive" - " or disk image."; + errStr = L"File doesn't appear to be a valid archive" + L" or disk image."; } if (!errStr.IsEmpty()) ShowFailureMsg(this, errStr, IDS_FAILED); @@ -2234,7 +2233,7 @@ MainWindow::DoOpenVolume(CString drive, bool readOnly) //char filename[4] = "_:\\"; //filename[0] = driveLetter; - WMSG2("FileOpenVolume '%s' %d\n", (const char*)drive, readOnly); + WMSG2("FileOpenVolume '%ls' %d\n", (LPCWSTR)drive, readOnly); /* close existing archive */ CloseArchive(); @@ -2296,7 +2295,8 @@ MainWindow::ReopenArchive(void) CString errStr; /* if the open fails we *don't* want to leave the previous content up */ - WMSG3("Reopening '%s' ro=%d kind=%d\n", pathName, readOnly, archiveKind); + WMSG3("Reopening '%ls' ro=%d kind=%d\n", + (LPCWSTR) pathName, readOnly, archiveKind); CloseArchive(); switch (archiveKind) { @@ -2337,12 +2337,12 @@ bail: * Determine whether "path" matches the pathname of the currently open archive. */ bool -MainWindow::IsOpenPathName(const char* path) +MainWindow::IsOpenPathName(const WCHAR* path) { if (fpOpenArchive == nil) return false; - if (stricmp(path, fpOpenArchive->GetPathName()) == 0) + if (wcsicmp(path, fpOpenArchive->GetPathName()) == 0) return true; return false; @@ -2450,22 +2450,23 @@ MainWindow::CloseArchive(void) * temp file whose name we're trying to conceal. */ void -MainWindow::SetCPTitle(const char* pathname, GenericArchive* pOpenArchive) +MainWindow::SetCPTitle(const WCHAR* pathname, GenericArchive* pOpenArchive) { ASSERT(pathname != nil); CString title; - CString archiveDescription; CString appName; + CString archiveDescription; appName.LoadString(IDS_MB_APP_NAME); pOpenArchive->GetDescription(&archiveDescription); - title.Format(_T("%s - %s (%s)"), appName, pathname, archiveDescription); + title.Format(L"%ls - %ls (%ls)", (LPCWSTR) appName, pathname, + (LPCWSTR) archiveDescription); if (fpOpenArchive->IsReadOnly()) { CString readOnly; readOnly.LoadString(IDS_READONLY); - title += _T(" "); + title += L" "; title += readOnly; } @@ -2520,8 +2521,8 @@ MainWindow::GetPrintTitle(void) appName.LoadString(IDS_MB_APP_NAME); fpOpenArchive->GetDescription(&archiveDescription); - title.Format(_T("%s - %s (%s)"), - appName, fOpenArchivePathName, archiveDescription); + title.Format(L"%ls - %ls (%ls)", (LPCWSTR) appName, + (LPCWSTR) fOpenArchivePathName, (LPCWSTR) archiveDescription); return title; } @@ -2562,19 +2563,19 @@ MainWindow::FailureBeep(void) * The absence of the file is not considered an error. */ CString -MainWindow::RemoveFile(const char* fileName) +MainWindow::RemoveFile(const WCHAR* fileName) { CString errMsg; int cc; - cc = unlink(fileName); + cc = _wunlink(fileName); if (cc < 0 && errno != ENOENT) { int err = errno; - WMSG2("Failed removing file '%s', errno=%d\n", fileName, err); - errMsg.Format("Unable to remove '%s': %s.", + WMSG2("Failed removing file '%ls', errno=%d\n", fileName, err); + errMsg.Format(L"Unable to remove '%ls': %hs.", fileName, strerror(err)); if (err == EACCES) - errMsg += "\n\n(Make sure the file isn't open.)"; + errMsg += L"\n\n(Make sure the file isn't open.)"; } return errMsg; diff --git a/app/Main.h b/app/Main.h index 10a8770..0af7375 100644 --- a/app/Main.h +++ b/app/Main.h @@ -6,8 +6,8 @@ /* * Application UI classes. */ -#ifndef __MAIN__ -#define __MAIN__ +#ifndef APP_MAIN_H +#define APP_MAIN_H #include "ContentList.h" #include "GenericArchive.h" @@ -61,12 +61,12 @@ public: // update the progress meter void SetProgressBegin(void); - int SetProgressUpdate(int percent, const char* oldName, - const char* newName); + int SetProgressUpdate(int percent, const WCHAR* oldName, + const WCHAR* newName); void SetProgressEnd(void); // update the progress counter - bool SetProgressCounter(const char* fmt, long val); + bool SetProgressCounter(const WCHAR* fmt, long val); // handle a double-click in the content view void HandleDoubleClick(void); @@ -115,14 +115,14 @@ public: void FailureBeep(void); // remove a file, returning a helpful message on failure - CString RemoveFile(const char* fileName); + CString RemoveFile(const WCHAR* fileName); // choose the place to put a file bool ChooseAddTarget(DiskImgLib::A2File** ppTargetSubdir, DiskImgLib::DiskFS** ppDiskFS); // try a disk image override dialog - int TryDiskImgOverride(DiskImg* pImg, const char* fileSource, + int TryDiskImgOverride(DiskImg* pImg, const WCHAR* fileSource, DiskImg::FSFormat defaultFormat, int* pDisplayFormat, bool allowUnknown, CString* pErrMsg); // copy all blocks from one disk image to another @@ -130,7 +130,7 @@ public: bool partial, ProgressCancelDialog* pPCDialog); // does the currently open archive pathname match? - bool IsOpenPathName(const char* path); + bool IsOpenPathName(const WCHAR* path); // raise a flag to cause a full reload of the open file void SetReopenFlag(void) { fNeedReopen = true; } @@ -139,22 +139,22 @@ public: // save a buffer of data as a file in a disk image or file archive static bool SaveToArchive(GenericArchive::FileDetails* pDetails, - const unsigned char* dataBuf, long dataLen, - const unsigned char* rsrcBuf, long rsrcLen, + const BYTE* dataBuf, long dataLen, + const BYTE* rsrcBuf, long rsrcLen, CString& errMsg, CWnd* pDialog); - static const char kOpenNuFX[]; - static const char kOpenBinaryII[]; - static const char kOpenACU[]; - static const char kOpenDiskImage[]; - static const char kOpenAll[]; - static const char kOpenEnd[]; + static const WCHAR kOpenNuFX[]; + static const WCHAR kOpenBinaryII[]; + static const WCHAR kOpenACU[]; + static const WCHAR kOpenDiskImage[]; + static const WCHAR kOpenAll[]; + static const WCHAR kOpenEnd[]; private: - static const char* kModeNuFX; - static const char* kModeBinaryII; - static const char* kModeACU; - static const char* kModeDiskImage; + static const WCHAR kModeNuFX[]; + static const WCHAR kModeBinaryII[]; + static const WCHAR kModeACU[]; + static const WCHAR kModeDiskImage[]; // Command handlers afx_msg int OnCreate(LPCREATESTRUCT lpcs); @@ -255,22 +255,22 @@ private: void ResizeClientArea(void); void DrawEmptyClientArea(CDC* pDC, const CRect& clientRect); int TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, - const char* modeStr); + const WCHAR* modeStr); int TmpExtractForExternal(GenericEntry* pEntry); - void DoOpenArchive(const char* pathName, const char* ext, + void DoOpenArchive(const WCHAR* pathName, const WCHAR* ext, int filterIndex, bool readOnly); - int LoadArchive(const char* filename, const char* extension, + int LoadArchive(const WCHAR* filename, const WCHAR* extension, int filterIndex, bool readOnly, bool createFile); int DoOpenVolume(CString drive, bool readOnly); void SwitchContentList(GenericArchive* pOpenArchive); void CloseArchiveWOControls(void); void CloseArchive(void); - void SetCPTitle(const char* pathname, GenericArchive* pArchive); + void SetCPTitle(const WCHAR* pathname, GenericArchive* pArchive); void SetCPTitle(void); GenericEntry* GetSelectedItem(ContentList* pContentList); void HandleView(void); - void DeleteFileOnExit(const char* name); + void DeleteFileOnExit(const WCHAR* name); void ReopenArchive(void); @@ -280,6 +280,11 @@ private: void GetFilePart(const GenericEntry* pEntry, int whichThread, ReformatHolder* pHolder) const; + /** + * this is a test + * of whatever the hell this does + * whee. + */ void DoBulkExtract(SelectionSet* pSelSet, const ExtractOptionsDialog* pExtOpts); bool ExtractEntry(GenericEntry* pEntry, int thread, @@ -294,7 +299,7 @@ private: /* some stuff from Clipboard.cpp */ CString CreateFileList(SelectionSet* pSelSet); - static CString DblDblQuote(const char* str); + static CString DblDblQuote(const WCHAR* str); long GetClipboardContentLen(void); HGLOBAL CreateFileCollection(SelectionSet* pSelSet); CString CopyToCollection(GenericEntry* pEntry, void** pBuf, long* pBufLen); @@ -302,23 +307,23 @@ private: CString ProcessClipboard(const void* vbuf, long bufLen, bool pasteJunkPaths); CString ProcessClipboardEntry(const FileCollectionEntry* pCollEnt, - const char* pathName, const unsigned char* buf, long remLen); + const WCHAR* pathName, const unsigned char* buf, long remLen); /* some stuff from Tools.cpp */ int DetermineImageSettings(int convertIdx, bool addGzip, DiskImg::OuterFormat* pOuterFormat, DiskImg::FileFormat* pFileFormat, DiskImg::PhysicalFormat* pPhysicalFormat, DiskImg::SectorOrder* pSectorOrder); - void BulkConvertImage(const char* pathName, const char* targetDir, + void BulkConvertImage(const WCHAR* pathName, const WCHAR* targetDir, const DiskConvertDialog& convDlg, CString* pErrMsg); int SSTOpenImage(int seqNum, DiskImg* pDiskImg); - int SSTLoadData(int seqNum, DiskImg* pDiskImg, unsigned char* trackBuf, + int SSTLoadData(int seqNum, DiskImg* pDiskImg, BYTE* trackBuf, long* pBadCount); long SSTGetBufOffset(int track); - long SSTCountBadBytes(const unsigned char* sctBuf, int count); - void SSTProcessTrackData(unsigned char* trackBuf); + long SSTCountBadBytes(const BYTE* sctBuf, int count); + void SSTProcessTrackData(BYTE* trackBuf); void VolumeCopier(bool openFile); - bool EditTwoImgProps(const char* fileName); + bool EditTwoImgProps(const WCHAR* fileName); void PrintListing(const ContentList* pContentList); @@ -386,11 +391,11 @@ private: while (pNode != nil) { pNext = pNode->fNext; - if (unlink(pNode->fName) != 0) { - WMSG2(" WARNING: delete of '%s' failed, err=%d\n", + if (_wunlink(pNode->fName) != 0) { + WMSG2(" WARNING: delete of '%ls' failed, err=%d\n", pNode->fName, errno); } else { - WMSG1(" Deleted '%s'\n", pNode->fName); + WMSG1(" Deleted '%ls'\n", pNode->fName); } delete pNode; pNode = pNext; @@ -405,7 +410,7 @@ private: pNode->fNext = fHead; } fHead = pNode; - WMSG1("Delete-on-exit '%s'\n", (LPCTSTR) name); + WMSG1("Delete-on-exit '%ls'\n", (LPCWSTR) name); } DeleteListNode* fHead; @@ -432,4 +437,4 @@ private: #define GET_PREFERENCES() ((MainWindow*)::AfxGetMainWnd())->GetPreferences() #define GET_PREFERENCES_WR() ((MainWindow*)::AfxGetMainWnd())->GetPreferencesWr() -#endif /*__MAIN__*/ \ No newline at end of file +#endif /*APP_MAIN_H*/ diff --git a/app/MyApp.cpp b/app/MyApp.cpp index 48fc652..eef0dc0 100644 --- a/app/MyApp.cpp +++ b/app/MyApp.cpp @@ -57,7 +57,7 @@ MyApp::MyApp(LPCTSTR lpszAppName) : CWinApp(lpszAppName) } #endif - WMSG5("CiderPress v%d.%d.%d%s started at %.24s\n", + WMSG5("CiderPress v%d.%d.%d%s started at %.24hs\n", kAppMajorVersion, kAppMinorVersion, kAppBugVersion, kAppDevString, ctime(&now)); @@ -105,14 +105,14 @@ MyApp::InitInstance(void) /* find our .EXE file */ //HMODULE hModule = ::GetModuleHandle(NULL); - char buf[MAX_PATH]; - if (::GetModuleFileName(nil /*hModule*/, buf, sizeof(buf)) != 0) { - WMSG1("Module name is '%s'\n", buf); + WCHAR buf[MAX_PATH]; + if (::GetModuleFileName(nil /*hModule*/, buf, NELEM(buf)) != 0) { + WMSG1("Module name is '%ls'\n", buf); fExeFileName = buf; - char* cp = strrchr(buf, '\\'); + WCHAR* cp = wcsrchr(buf, '\\'); if (cp == nil) - fExeBaseName = ""; + fExeBaseName = L""; else fExeBaseName = fExeFileName.Left(cp - buf +1); } else { @@ -120,9 +120,9 @@ MyApp::InitInstance(void) ::GetLastError()); } - LogModuleLocation("riched.dll"); - LogModuleLocation("riched20.dll"); - LogModuleLocation("riched32.dll"); + LogModuleLocation(L"riched.dll"); + LogModuleLocation(L"riched20.dll"); + LogModuleLocation(L"riched32.dll"); #if 0 /* find our .INI file by tweaking the EXE path */ @@ -137,7 +137,7 @@ MyApp::InitInstance(void) free((void*)m_pszProfileName); m_pszProfileName = strdup(buf); - WMSG1("Profile name is '%s'\n", m_pszProfileName); + WMSG1("Profile name is '%ls'\n", m_pszProfileName); if (!WriteProfileString("SectionOne", "MyEntry", "test")) WMSG0("WriteProfileString failed\n"); @@ -145,13 +145,13 @@ MyApp::InitInstance(void) SetRegistryKey(fRegistry.GetAppRegistryKey()); - //WMSG1("Registry key is '%s'\n", m_pszRegistryKey); - //WMSG1("Profile name is '%s'\n", m_pszProfileName); - WMSG1("Short command line is '%s'\n", m_lpCmdLine); - //WMSG1("CP app name is '%s'\n", m_pszAppName); - //WMSG1("CP exe name is '%s'\n", m_pszExeName); - WMSG1("CP help file is '%s'\n", m_pszHelpFilePath); - WMSG1("Command line is '%s'\n", ::GetCommandLine()); + //WMSG1("Registry key is '%ls'\n", m_pszRegistryKey); + //WMSG1("Profile name is '%ls'\n", m_pszProfileName); + WMSG1("Short command line is '%ls'\n", m_lpCmdLine); + //WMSG1("CP app name is '%ls'\n", m_pszAppName); + //WMSG1("CP exe name is '%ls'\n", m_pszExeName); + WMSG1("CP help file is '%ls'\n", m_pszHelpFilePath); + WMSG1("Command line is '%ls'\n", ::GetCommandLine()); //if (!WriteProfileString("SectionOne", "MyEntry", "test")) // WMSG0("WriteProfileString failed\n"); @@ -161,11 +161,11 @@ MyApp::InitInstance(void) * bail immediately. This will hemorrhage memory, but I'm sure the * incredibly robust Windows environment will take it in stride. */ - if (strcmp(m_lpCmdLine, _T("-install")) == 0) { + if (wcscmp(m_lpCmdLine, L"-install") == 0) { WMSG0("Invoked with INSTALL flag\n"); fRegistry.OneTimeInstall(); exit(0); - } else if (strcmp(m_lpCmdLine, _T("-uninstall")) == 0) { + } else if (wcscmp(m_lpCmdLine, L"-uninstall") == 0) { WMSG0("Invoked with UNINSTALL flag\n"); fRegistry.OneTimeUninstall(); exit(1); // tell DeployMaster to continue with uninstall @@ -182,18 +182,18 @@ MyApp::InitInstance(void) * If "name" is nil, we show the EXE info. */ void -MyApp::LogModuleLocation(const char* name) +MyApp::LogModuleLocation(const WCHAR* name) { HMODULE hModule; - char fileNameBuf[256]; + WCHAR fileNameBuf[256]; hModule = ::GetModuleHandle(name); if (hModule != nil && - ::GetModuleFileName(hModule, fileNameBuf, sizeof(fileNameBuf)) != 0) + ::GetModuleFileName(hModule, fileNameBuf, NELEM(fileNameBuf)) != 0) { // GetModuleHandle does not increase ref count, so no need to release - WMSG2("Module '%s' loaded from '%s'\n", name, fileNameBuf); + WMSG2("Module '%ls' loaded from '%ls'\n", name, fileNameBuf); } else { - WMSG1("Module '%s' not loaded\n", name); + WMSG1("Module '%ls' not loaded\n", name); } } diff --git a/app/MyApp.h b/app/MyApp.h index 2191da1..665242a 100644 --- a/app/MyApp.h +++ b/app/MyApp.h @@ -6,21 +6,21 @@ /* * The application object. */ -#ifndef __MYAPP__ -#define __MYAPP__ +#ifndef APP_MYAPP_H +#define APP_MYAPP_H #include "Registry.h" #if defined(_DEBUG_LOG) //#define kDebugLog "C:\\test\\cplog.txt" -#define kDebugLog "C:\\cplog.txt" +#define kDebugLog L"C:\\cplog.txt" #endif /* CiderPress version numbers */ #define kAppMajorVersion 3 #define kAppMinorVersion 0 #define kAppBugVersion 1 -#define kAppDevString "" +#define kAppDevString L"" /* * Windows application object. @@ -33,15 +33,15 @@ public: MyRegistry fRegistry; - const char* GetExeFileName(void) const { return fExeFileName; } - const char* GetExeBaseName(void) const { return fExeBaseName; } + const WCHAR* GetExeFileName(void) const { return fExeFileName; } + const WCHAR* GetExeBaseName(void) const { return fExeBaseName; } private: // Overridden functions virtual BOOL InitInstance(void); virtual BOOL OnIdle(LONG lCount); - void LogModuleLocation(const char* name); + void LogModuleLocation(const WCHAR* name); CString fExeFileName; CString fExeBaseName; @@ -49,4 +49,4 @@ private: extern MyApp gMyApp; -#endif /*__MYAPP__*/ \ No newline at end of file +#endif /*APP_MYAPP_H*/ diff --git a/app/NewDiskSize.cpp b/app/NewDiskSize.cpp index 6d27090..84314fe 100644 --- a/app/NewDiskSize.cpp +++ b/app/NewDiskSize.cpp @@ -25,7 +25,7 @@ { IDC_CONVDISK_32MB, 65535 }, { IDC_CONVDISK_SPECIFY, kSpecified }, }; -static const kEditBoxID = IDC_CONVDISK_SPECIFY_EDIT; +static const int kEditBoxID = IDC_CONVDISK_SPECIFY_EDIT; /* * Return the #of entries in the table. @@ -161,6 +161,6 @@ NewDiskSize::UpdateSpecifyEdit(CDialog* pDialog) } CString fmt; - fmt.Format("%ld", kCtrlMap[i].blocks); + fmt.Format(L"%ld", kCtrlMap[i].blocks); pEdit->SetWindowText(fmt); } diff --git a/app/NewDiskSize.h b/app/NewDiskSize.h index 5b59869..4325dc5 100644 --- a/app/NewDiskSize.h +++ b/app/NewDiskSize.h @@ -6,8 +6,8 @@ /* * Functions to manage the "new disk size" radio button set in dialogs. */ -#ifndef __NEWDISKSIZE__ -#define __NEWDISKSIZE__ +#ifndef APP_NEWDISKSIZE_H +#define APP_NEWDISKSIZE_H /* * All members are static. Don't instantiate the class. @@ -35,4 +35,4 @@ private: static const RadioCtrlMap kCtrlMap[]; }; -#endif /*__NEWDISKSIZE__*/ \ No newline at end of file +#endif /*APP_NEWDISKSIZE_H*/ diff --git a/app/NewFolderDialog.cpp b/app/NewFolderDialog.cpp index c52de47..cd54868 100644 --- a/app/NewFolderDialog.cpp +++ b/app/NewFolderDialog.cpp @@ -39,28 +39,28 @@ NewFolderDialog::DoDataExchange(CDataExchange* pDX) /* validate the new folder by creating it */ if (pDX->m_bSaveAndValidate) { if (fNewFolder.IsEmpty()) { - MessageBox("No name entered, not creating new folder.", - "CiderPress", MB_OK); + MessageBox(L"No name entered, not creating new folder.", + L"CiderPress", MB_OK); // fall out of DoModal with fFolderCreated==false } else if (fNewFolder.Find('\\') >= 0 || fNewFolder.Find('/') >= 0) { - MessageBox("Folder names may not contain '/' or '\\'.", - "CiderPress", MB_OK); + MessageBox(L"Folder names may not contain '/' or '\\'.", + L"CiderPress", MB_OK); pDX->Fail(); } else { fNewFullPath = fCurrentFolder; if (fNewFullPath.Right(1) != "\\") fNewFullPath += "\\"; fNewFullPath += fNewFolder; - WMSG1("CREATING '%s'\n", fNewFullPath); + WMSG1("CREATING '%ls'\n", (LPCWSTR) fNewFullPath); if (!::CreateDirectory(fNewFullPath, nil)) { /* show the sometimes-bizarre Windows error string */ CString msg, errStr, failed; DWORD dwerr = ::GetLastError(); GetWin32ErrorString(dwerr, &errStr); - msg.Format("Unable to create folder '%s': %s", - fNewFolder, errStr); + msg.Format(L"Unable to create folder '%ls': %ls", + (LPCWSTR) fNewFolder, (LPCWSTR) errStr); failed.LoadString(IDS_FAILED); MessageBox(msg, failed, MB_OK | MB_ICONERROR); pDX->Fail(); diff --git a/app/NewFolderDialog.h b/app/NewFolderDialog.h index 3da0f0e..53e6bfa 100644 --- a/app/NewFolderDialog.h +++ b/app/NewFolderDialog.h @@ -6,8 +6,8 @@ /* * Allow the user to create a new folder. */ -#ifndef __NEWFOLDERDIALOG__ -#define __NEWFOLDERDIALOG__ +#ifndef APP_NEWFOLDERDIALOG_H +#define APP_NEWFOLDERDIALOG_H #include "resource.h" @@ -20,8 +20,8 @@ class NewFolderDialog : public CDialog { public: NewFolderDialog(CWnd* pParent = NULL) : CDialog(IDD_NEWFOLDER, pParent) { - fCurrentFolder = ""; - fNewFolder = ""; + fCurrentFolder = L""; + fNewFolder = L""; fFolderCreated = false; } virtual ~NewFolderDialog(void) {} @@ -47,4 +47,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__NEWFOLDERDIALOG__*/ \ No newline at end of file +#endif /*APP_NEWFOLDERDIALOG_H*/ diff --git a/app/NufxArchive.cpp b/app/NufxArchive.cpp index fa4965d..98646d1 100644 --- a/app/NufxArchive.cpp +++ b/app/NufxArchive.cpp @@ -13,7 +13,7 @@ #include "RecompressOptionsDialog.h" #include "AddClashDialog.h" #include "Main.h" -#include "../prebuilt/NufxLib.h" +#include "../nufxlib/NufxLib.h" /* * NufxLib doesn't currently allow an fssep of '\0', so we use this instead @@ -89,13 +89,13 @@ NufxEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, if (needAlloc) { dataBuf = new char[actualThreadEOF]; if (dataBuf == nil) { - pErrMsg->Format("allocation of %ld bytes failed", + pErrMsg->Format(L"allocation of %ld bytes failed", actualThreadEOF); goto bail; } } else { if (*pLength < (long) actualThreadEOF) { - pErrMsg->Format("buf size %ld too short (%ld)", + pErrMsg->Format(L"buf size %ld too short (%ld)", *pLength, actualThreadEOF); goto bail; } @@ -104,7 +104,7 @@ NufxEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, nerr = NuCreateDataSinkForBuffer(true, kNuConvertOff, (unsigned char*)dataBuf, actualThreadEOF, &pDataSink); if (nerr != kNuErrNone) { - pErrMsg->Format("unable to create buffer data sink: %s", + pErrMsg->Format(L"unable to create buffer data sink: %hs", NuStrError(nerr)); goto bail; } @@ -116,12 +116,12 @@ NufxEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, result = IDCANCEL; //::sprintf(errorBuf, "Cancelled.\n"); } else if (nerr == kNuErrBadFormat) { - pErrMsg->Format("The compression method used on this file is not supported " - "by your copy of \"nufxlib2.dll\". For more information, " - "please visit us on the web at " - "http://www.faddensoft.com/ciderpress/"); + pErrMsg->Format(L"The compression method used on this file is not supported " + L"by your copy of \"nufxlib.dll\". For more information, " + L"please visit us on the web at " + L"http://www.faddensoft.com/ciderpress/"); } else { - pErrMsg->Format("unable to extract thread %ld: %s", + pErrMsg->Format(L"unable to extract thread %ld: %hs", threadIdx, NuStrError(nerr)); } goto bail; @@ -191,7 +191,7 @@ NufxEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, case kConvertEOLAuto: nuConv = kNuConvertAuto; break; default: ASSERT(false); - pErrMsg->Format("internal error: bad conv flag %d", conv); + pErrMsg->Format(L"internal error: bad conv flag %d", conv); goto bail; } if (which == kDiskImageThread) { @@ -209,21 +209,21 @@ NufxEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, break; default: ASSERT(false); - pErrMsg->Format("internal error: bad convHA flag %d", convHA); + pErrMsg->Format(L"internal error: bad convHA flag %d", convHA); goto bail; } /* make sure we convert to CRLF */ nerr = NuSetValue(fpArchive, kNuValueEOL, kNuEOLCRLF); // for Win32 if (nerr != kNuErrNone) { - pErrMsg->Format("failed setting EOL value: %s", NuStrError(nerr)); + pErrMsg->Format(L"failed setting EOL value: %hs", NuStrError(nerr)); goto bail; } /* create a data sink for "outfp" */ nerr = NuCreateDataSinkForFP(true, nuConv, outfp, &pDataSink); if (nerr != kNuErrNone) { - pErrMsg->Format("unable to create FP data sink: %s", + pErrMsg->Format(L"unable to create FP data sink: %hs", NuStrError(nerr)); goto bail; } @@ -234,15 +234,15 @@ NufxEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, if (nerr != kNuErrNone) { if (nerr == kNuErrAborted) { /* user hit the "cancel" button */ - *pErrMsg = _T("cancelled"); + *pErrMsg = L"cancelled"; result = IDCANCEL; } else if (nerr == kNuErrBadFormat) { - pErrMsg->Format("The compression method used on this file is not supported " - "by your copy of \"nufxlib2.dll\". For more information, " - "please visit us on the web at " - "http://www.faddensoft.com/ciderpress/"); + pErrMsg->Format(L"The compression method used on this file is not supported " + L"by your copy of \"nufxlib.dll\". For more information, " + L"please visit us on the web at " + L"http://www.faddensoft.com/ciderpress/"); } else { - pErrMsg->Format("unable to extract thread %ld: %s", + pErrMsg->Format(L"unable to extract thread %ld: %hs", threadIdx, NuStrError(nerr)); } goto bail; @@ -283,7 +283,7 @@ NufxEntry::FindThreadInfo(int which, NuThread* pRetThread, const NuRecord* pRecord; nerr = NuGetRecord(fpArchive, fRecordIdx, &pRecord); if (nerr != kNuErrNone) { - pErrMsg->Format("NufxLib unable to locate record %ld: %s", + pErrMsg->Format(L"NufxLib unable to locate record %ld: %hs", fRecordIdx, NuStrError(nerr)); goto bail; } @@ -299,7 +299,7 @@ NufxEntry::FindThreadInfo(int which, NuThread* pRetThread, case kDiskImageThread: wantedThreadID = kNuThreadIDDiskImage; break; case kCommentThread: wantedThreadID = kNuThreadIDComment; break; default: - pErrMsg->Format("looking for bogus thread 0x%02x", which); + pErrMsg->Format(L"looking for bogus thread 0x%02x", which); goto bail; } @@ -312,7 +312,7 @@ NufxEntry::FindThreadInfo(int which, NuThread* pRetThread, } if (i == (int)NuRecordGetNumThreads(pRecord)) { /* didn't find the thread we wanted */ - pErrMsg->Format("searched %d threads but couldn't find 0x%02x", + pErrMsg->Format(L"searched %d threads but couldn't find 0x%02x", NuRecordGetNumThreads(pRecord), which); goto bail; } @@ -327,9 +327,9 @@ bail: //static const char* gShortFormatNames[] = { // "unc", "squ", "lz1", "lz2", "u12", "u16", "dfl", "bzp" //}; -static const char* gFormatNames[] = { - "Uncompr", "Squeeze", "LZW/1", "LZW/2", "LZC-12", - "LZC-16", "Deflate", "Bzip2" +static const WCHAR* gFormatNames[] = { + L"Uncompr", L"Squeeze", L"LZW/1", L"LZW/2", L"LZC-12", + L"LZC-16", L"Deflate", L"Bzip2" }; /* @@ -430,7 +430,7 @@ NufxEntry::AnalyzeRecord(const NuRecord* pRecord) if (format >= 0 && format < NELEM(gFormatNames)) SetFormatStr(gFormatNames[format]); else - SetFormatStr("Unknown"); + SetFormatStr(L"Unknown"); } @@ -463,8 +463,8 @@ NufxArchive::AppInit(void) } if (major != kNuVersionMajor || minor < kNuVersionMinor) { - result.Format("Older or incompatible version of NufxLib DLL found.\r\r" - "Wanted v%d.%d.x, found %ld.%ld.%ld.", + result.Format(L"Older or incompatible version of NufxLib DLL found.\r\r" + L"Wanted v%d.%d.x, found %ld.%ld.%ld.", kNuVersionMajor, kNuVersionMinor, major, minor, bug); goto bail; @@ -535,25 +535,25 @@ NufxArchive::NufxErrorMsgHandler(NuArchive* /*pArchive*/, void* vErrorMessage) { #if defined(_DEBUG_LOG) const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - CString msg(pErrorMessage->message); + CStringA msg(pErrorMessage->message); msg += "\n"; if (pErrorMessage->isDebug) msg = "[D] " + msg; - fprintf(gLog, "%05u NufxLib %s(%d) : %s", + fprintf(gLog, "%05u NufxLib %hs(%d) : %hs", gPid, pErrorMessage->file, pErrorMessage->line, msg); #elif defined(_DEBUG) const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - CString msg(pErrorMessage->message); + CStringA msg(pErrorMessage->message); msg += "\n"; if (pErrorMessage->isDebug) msg = "[D] " + msg; _CrtDbgReport(_CRT_WARN, pErrorMessage->file, pErrorMessage->line, - pErrorMessage->function, msg); + pErrorMessage->function, "%hs", msg); #endif return kNuOK; @@ -601,12 +601,14 @@ NufxArchive::ProgressUpdater(NuArchive* pArchive, void* vpProgress) if (pProgress->state == kNuProgressDone) perc = 100; - //WMSG3("Progress: %d%% '%s' '%s'\n", perc, + //WMSG3("Progress: %d%% '%hs' '%hs'\n", perc, // oldName == nil ? "(nil)" : oldName, // newName == nil ? "(nil)" : newName); //status = pMainWin->SetProgressUpdate(perc, oldName, newName); - status = SET_PROGRESS_UPDATE2(perc, oldName, newName); + CString oldNameW(oldName); + CString newNameW(newName); + status = SET_PROGRESS_UPDATE2(perc, oldNameW, newNameW); /* check to see if user hit the "cancel" button on the progress dialog */ if (pProgress->state == kNuProgressAborted) { @@ -628,33 +630,32 @@ NufxArchive::ProgressUpdater(NuArchive* pArchive, void* vpProgress) * Returns an error string on failure, or nil on success. */ GenericArchive::OpenResult -NufxArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) +NufxArchive::Open(const WCHAR* filename, bool readOnly, CString* pErrMsg) { NuError nerr; CString errMsg; ASSERT(fpArchive == nil); + CStringA filenameA(filename); if (!readOnly) { CString tmpname = GenDerivedTempName(filename); - WMSG2("Opening file '%s' rw (tmp='%s')\n", filename, tmpname); + WMSG2("Opening file '%ls' rw (tmp='%ls')\n", filename, (LPCWSTR) tmpname); fIsReadOnly = false; - nerr = NuOpenRW(filename, tmpname, 0, &fpArchive); + CStringA tmpnameA(tmpname); + nerr = NuOpenRW(filenameA, tmpnameA, 0, &fpArchive); } if (nerr == kNuErrFileAccessDenied || nerr == EACCES) { WMSG0("Read-write failed with access denied, trying read-only\n"); readOnly = true; } if (readOnly) { - WMSG1("Opening file '%s' ro\n", filename); + WMSG1("Opening file '%ls' ro\n", (LPCWSTR) filename); fIsReadOnly = true; - nerr = NuOpenRO(filename, &fpArchive); + nerr = NuOpenRO(filenameA, &fpArchive); } if (nerr != kNuErrNone) { - errMsg = "Unable to open '"; - errMsg += filename; - errMsg += "': "; - errMsg += NuStrError(nerr); + errMsg.Format(L"Unable to open '%ls': %hs", filename, NuStrError(nerr)); goto bail; } else { //WMSG0("FILE OPEN SUCCESS\n"); @@ -662,13 +663,13 @@ NufxArchive::Open(const char* filename, bool readOnly, CString* pErrMsg) nerr = SetCallbacks(); if (nerr != kNuErrNone) { - errMsg = "Callback init failed"; + errMsg = L"Callback init failed"; goto bail; } nerr = LoadContents(); if (nerr != kNuErrNone) { - errMsg = "Failed reading archive contents: "; + errMsg = L"Failed reading archive contents: "; errMsg += NuStrError(nerr); } @@ -689,23 +690,22 @@ bail: * Returns an error string on failure, or "" on success. */ CString -NufxArchive::New(const char* filename, const void* options) +NufxArchive::New(const WCHAR* filename, const void* options) { NuError nerr; - CString retmsg(""); + CString retmsg; ASSERT(fpArchive == nil); ASSERT(options == nil); CString tmpname = GenDerivedTempName(filename); - WMSG2("Creating file '%s' (tmp='%s')\n", filename, tmpname); + WMSG2("Creating file '%ls' (tmp='%ls')\n", filename, (LPCWSTR) tmpname); fIsReadOnly = false; - nerr = NuOpenRW(filename, tmpname, kNuOpenCreat | kNuOpenExcl, &fpArchive); + CStringA filenameA(filename); + CStringA tmpnameA(tmpname); + nerr = NuOpenRW(filenameA, tmpnameA, kNuOpenCreat | kNuOpenExcl, &fpArchive); if (nerr != kNuErrNone) { - retmsg = "Unable to open '"; - retmsg += filename; - retmsg += "': "; - retmsg += NuStrError(nerr); + retmsg.Format(L"Unable to open '%ls': %hs", filename, NuStrError(nerr)); goto bail; } else { WMSG0("NEW FILE SUCCESS\n"); @@ -714,7 +714,7 @@ NufxArchive::New(const char* filename, const void* options) nerr = SetCallbacks(); if (nerr != kNuErrNone) { - retmsg = "Callback init failed"; + retmsg = L"Callback init failed"; goto bail; } @@ -875,7 +875,7 @@ NufxArchive::Reload(void) nerr = LoadContents(); if (nerr != kNuErrNone) { - errMsg.Format("ERROR: unable to reload archive contents: %s.", + errMsg.Format(L"ERROR: unable to reload archive contents: %hs.", NuStrError(nerr)); DeleteEntries(); @@ -921,7 +921,8 @@ NufxArchive::ContentFunc(NuArchive* pArchive, void* vpRecord) pNewEntry = new NufxEntry(pArchive); - pNewEntry->SetPathName(pRecord->filename); + CStringW filenameW(pRecord->filename); + pNewEntry->SetPathName(filenameW); pNewEntry->SetFssep(NuGetSepFromSysInfo(pRecord->recFileSysInfo)); pNewEntry->SetFileType(pRecord->recFileType); pNewEntry->SetAuxType(pRecord->recExtraType); @@ -1029,55 +1030,55 @@ NufxArchive::BulkAdd(ActionProgressDialog* pActionProgress, { NuError nerr; CString errMsg; - char curDir[MAX_PATH] = ""; + WCHAR curDir[MAX_PATH] = L""; bool retVal = false; - WMSG2("Opts: '%s' typePres=%d\n", - pAddOpts->fStoragePrefix, pAddOpts->fTypePreservation); + WMSG2("Opts: '%ls' typePres=%d\n", (LPCWSTR) pAddOpts->fStoragePrefix, + pAddOpts->fTypePreservation); WMSG3(" sub=%d strip=%d ovwr=%d\n", pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, pAddOpts->fOverwriteExisting); AddPrep(pActionProgress, pAddOpts); - pActionProgress->SetArcName("(Scanning files to be added...)"); - pActionProgress->SetFileName(""); + pActionProgress->SetArcName(L"(Scanning files to be added...)"); + pActionProgress->SetFileName(L""); /* initialize count */ fNumAdded = 0; - const char* buf = pAddOpts->GetFileNames(); - WMSG2("Selected path = '%s' (offset=%d)\n", buf, + const WCHAR* buf = pAddOpts->GetFileNames(); + WMSG2("Selected path = '%ls' (offset=%d)\n", buf, pAddOpts->GetFileNameOffset()); - if (GetCurrentDirectory(sizeof(curDir), curDir) == 0) { - errMsg = "Unable to get current directory.\n"; + if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { + errMsg = L"Unable to get current directory.\n"; ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } if (SetCurrentDirectory(buf) == false) { - errMsg.Format("Unable to set current directory to '%s'.\n", buf); + errMsg.Format(L"Unable to set current directory to '%ls'.\n", buf); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } buf += pAddOpts->GetFileNameOffset(); while (*buf != '\0') { - WMSG1(" file '%s'\n", buf); + WMSG1(" file '%ls'\n", buf); /* this just provides the list of files to NufxLib */ nerr = AddFile(pAddOpts, buf, &errMsg); if (nerr != kNuErrNone) { if (errMsg.IsEmpty()) - errMsg.Format("Failed while adding file '%s': %s.", - (LPCTSTR) buf, NuStrError(nerr)); + errMsg.Format(L"Failed while adding file '%ls': %hs.", + buf, NuStrError(nerr)); if (nerr != kNuErrAborted) { ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } goto bail; } - buf += strlen(buf)+1; + buf += wcslen(buf)+1; } /* actually do the work */ @@ -1085,7 +1086,7 @@ NufxArchive::BulkAdd(ActionProgressDialog* pActionProgress, nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Unable to add files: %s.", NuStrError(nerr)); + errMsg.Format(L"Unable to add files: %hs.", NuStrError(nerr)); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } @@ -1096,19 +1097,19 @@ NufxArchive::BulkAdd(ActionProgressDialog* pActionProgress, } if (!fNumAdded) { - errMsg = "No files added.\n"; - fpMsgWnd->MessageBox(errMsg, "CiderPress", MB_OK | MB_ICONWARNING); + errMsg = L"No files added.\n"; + fpMsgWnd->MessageBox(errMsg, L"CiderPress", MB_OK | MB_ICONWARNING); } else { if (InternalReload(fpMsgWnd) == kNuErrNone) retVal = true; else - errMsg = "Reload failed."; + errMsg = L"Reload failed."; } bail: NuAbort(fpArchive); // abort anything that didn't get flushed if (SetCurrentDirectory(curDir) == false) { - errMsg.Format("Unable to reset current directory to '%s'.\n", buf); + errMsg.Format(L"Unable to reset current directory to '%ls'.\n", buf); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); // bummer, but don't signal failure } @@ -1131,13 +1132,14 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, DiskImg* pDiskImg; NuDataSource* pSource = nil; unsigned char* diskData = nil; - char curDir[MAX_PATH] = "\\"; + WCHAR curDir[MAX_PATH] = L"\\"; bool retVal = false; + CStringA storageNameA, origNameA; - WMSG2("AddDisk: '%s' %d\n", pAddOpts->GetFileNames(), + WMSG2("AddDisk: '%ls' %d\n", pAddOpts->GetFileNames(), pAddOpts->GetFileNameOffset()); - WMSG2("Opts: '%s' type=%d\n", - pAddOpts->fStoragePrefix, pAddOpts->fTypePreservation); + WMSG2("Opts: '%ls' type=%d\n", (LPCWSTR) pAddOpts->fStoragePrefix, + pAddOpts->fTypePreservation); WMSG3(" sub=%d strip=%d ovwr=%d\n", pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, pAddOpts->fOverwriteExisting); @@ -1145,10 +1147,10 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, pDiskImg = pAddOpts->fpDiskImg; ASSERT(pDiskImg != nil); - /* allocate storage for the disk */ - diskData = new unsigned char[pDiskImg->GetNumBlocks() * kBlockSize]; + /* allocate storage for the entire disk */ + diskData = new BYTE[pDiskImg->GetNumBlocks() * kBlockSize]; if (diskData == nil) { - errMsg.Format("Unable to allocate %d bytes.", + errMsg.Format(L"Unable to allocate %d bytes.", pDiskImg->GetNumBlocks() * kBlockSize); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -1157,24 +1159,24 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, /* prepare to add */ AddPrep(pActionProgress, pAddOpts); - const char* buf; + const WCHAR* buf; buf = pAddOpts->GetFileNames(); - WMSG2("Selected path = '%s' (offset=%d)\n", buf, + WMSG2("Selected path = '%ls' (offset=%d)\n", buf, pAddOpts->GetFileNameOffset()); - if (GetCurrentDirectory(sizeof(curDir), curDir) == 0) { - errMsg = "Unable to get current directory.\n"; + if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { + errMsg = L"Unable to get current directory.\n"; ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } if (SetCurrentDirectory(buf) == false) { - errMsg.Format("Unable to set current directory to '%s'.\n", buf); + errMsg.Format(L"Unable to set current directory to '%ls'.\n", buf); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } buf += pAddOpts->GetFileNameOffset(); - WMSG1(" file '%s'\n", buf); + WMSG1(" file '%ls'\n", buf); /* strip off preservation stuff, and ignore it */ pathProp.Init(buf); @@ -1188,8 +1190,10 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, details.storageType = kBlockSize; details.access = kNuAccessUnlocked; details.extraType = pAddOpts->fpDiskImg->GetNumBlocks(); - details.origName = buf; - details.storageName = pathProp.fStoredPathName; + origNameA = buf; + storageNameA = pathProp.fStoredPathName; + details.origName = origNameA; + details.storageName = storageNameA; details.fileSysID = kNuFileSysUnknown; details.fileSysInfo = PathProposal::kDefaultStoredFssep; @@ -1203,8 +1207,8 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, UNIXTimeToDateTime(&then, &details.createWhen); /* set up the progress updater */ - pActionProgress->SetArcName(details.storageName); - pActionProgress->SetFileName(details.origName); + pActionProgress->SetArcName(pathProp.fStoredPathName); + pActionProgress->SetFileName(buf); /* read the disk now that we have progress update titles in place */ int block, numBadBlocks; @@ -1221,8 +1225,8 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, if (numBadBlocks > 0) { CString appName, msg; appName.LoadString(IDS_MB_APP_NAME); - msg.Format("Skipped %ld unreadable block%s.", numBadBlocks, - numBadBlocks == 1 ? "" : "s"); + msg.Format(L"Skipped %ld unreadable block%ls.", numBadBlocks, + numBadBlocks == 1 ? L"" : L"s"); fpMsgWnd->MessageBox(msg, appName, MB_OK | MB_ICONWARNING); // keep going -- just a warning } @@ -1242,7 +1246,7 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, nerr = NuAddRecord(fpArchive, &details, &recordIdx); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Failed adding record: %s.", NuStrError(nerr)); + errMsg.Format(L"Failed adding record: %hs.", NuStrError(nerr)); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } goto bail; @@ -1252,7 +1256,7 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, nerr = NuAddThread(fpArchive, recordIdx, kNuThreadIDDiskImage, pSource, nil); if (nerr != kNuErrNone) { - errMsg.Format("Failed adding thread: %s.", NuStrError(nerr)); + errMsg.Format(L"Failed adding thread: %hs.", NuStrError(nerr)); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -1263,7 +1267,7 @@ NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Unable to add disk: %s.", NuStrError(nerr)); + errMsg.Format(L"Unable to add disk: %hs.", NuStrError(nerr)); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } @@ -1281,7 +1285,7 @@ bail: NuAbort(fpArchive); // abort anything that didn't get flushed NuFreeDataSource(pSource); if (SetCurrentDirectory(curDir) == false) { - errMsg.Format("Unable to reset current directory to '%s'.\n", buf); + errMsg.Format(L"Unable to reset current directory to '%hs'.\n", buf); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); // bummer } @@ -1304,7 +1308,8 @@ NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts, retry: nuFileDetails = *pDetails; // stuff class contents into struct - err = NuAddFile(fpArchive, pDetails->origName /*pathname*/, + CStringA origNameA(pDetails->origName); + err = NuAddFile(fpArchive, origNameA /*pathname*/, &nuFileDetails, false, &recordIdx); if (err == kNuErrNone) { @@ -1312,7 +1317,7 @@ retry: } else if (err == kNuErrSkipped) { /* "maybe overwrite" UI causes this if user declines */ // fall through with the error - WMSG1("DoAddFile: skipped '%s'\n", pDetails->origName); + WMSG1("DoAddFile: skipped '%ls'\n", (LPCWSTR) pDetails->origName); } else if (err == kNuErrRecordExists) { AddClashDialog dlg; @@ -1323,7 +1328,7 @@ retry: goto bail_quiet; } if (dlg.fDoRename) { - WMSG1("add clash: rename to '%s'\n", (const char*) dlg.fNewName); + WMSG1("add clash: rename to '%ls'\n", (LPCWSTR) dlg.fNewName); pDetails->storageName = dlg.fNewName; goto retry; } else { @@ -1338,8 +1343,8 @@ retry: //bail: if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) { CString msg; - msg.Format("Unable to add file '%s': %s.", pDetails->origName, - NuStrError(err)); + msg.Format(L"Unable to add file '%ls': %hs.", + (LPCWSTR) pDetails->origName, NuStrError(err)); ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED); } bail_quiet: @@ -1517,7 +1522,7 @@ NufxArchive::HandleAddNotFound(const NuErrorStatus* pErrorStatus) { CString errMsg; - errMsg.Format("Failed while adding '%s': file no longer exists.", + errMsg.Format(L"Failed while adding '%hs': file no longer exists.", pErrorStatus->pathname); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); @@ -1550,7 +1555,7 @@ NufxArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) while (pSelEntry != nil) { pEntry = (NufxEntry*) pSelEntry->GetEntry(); - WMSG2(" Testing %ld '%s'\n", pEntry->GetRecordIdx(), + WMSG2(" Testing %ld '%ls'\n", pEntry->GetRecordIdx(), pEntry->GetPathName()); nerr = NuTestRecord(fpArchive, pEntry->GetRecordIdx()); if (nerr != kNuErrNone) { @@ -1560,7 +1565,7 @@ NufxArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) errMsg = "Cancelled."; pMsgWnd->MessageBox(errMsg, title, MB_OK); } else { - errMsg.Format("Failed while testing '%s': %s.", + errMsg.Format(L"Failed while testing '%ls': %hs.", pEntry->GetPathName(), NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); } @@ -1571,9 +1576,9 @@ NufxArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) } /* show success message */ - errMsg.Format("Tested %d file%s, no errors found.", + errMsg.Format(L"Tested %d file%ls, no errors found.", pSelSet->GetNumEntries(), - pSelSet->GetNumEntries() == 1 ? "" : "s"); + pSelSet->GetNumEntries() == 1 ? L"" : L"s"); pMsgWnd->MessageBox(errMsg); retVal = true; @@ -1608,11 +1613,11 @@ NufxArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) while (pSelEntry != nil) { pEntry = (NufxEntry*) pSelEntry->GetEntry(); - WMSG2(" Deleting %ld '%s'\n", pEntry->GetRecordIdx(), + WMSG2(" Deleting %ld '%ls'\n", pEntry->GetRecordIdx(), pEntry->GetPathName()); nerr = NuDeleteRecord(fpArchive, pEntry->GetRecordIdx()); if (nerr != kNuErrNone) { - errMsg.Format("Unable to delete record %d: %s.", + errMsg.Format(L"Unable to delete record %d: %hs.", pEntry->GetRecordIdx(), NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; @@ -1625,7 +1630,7 @@ NufxArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) long statusFlags; nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { - errMsg.Format("Unable to delete all files: %s.", NuStrError(nerr)); + errMsg.Format(L"Unable to delete all files: %hs.", NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); /* see if it got converted to read-only status */ @@ -1682,7 +1687,7 @@ NufxArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) SelectionEntry* pSelEntry = pSelSet->IterNext(); while (pSelEntry != nil) { NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry(); - WMSG1(" Renaming '%s'\n", pEntry->GetPathName()); + WMSG1(" Renaming '%ls'\n", pEntry->GetPathName()); RenameEntryDialog renameDlg(pMsgWnd); renameDlg.SetCanRenameFullPath(renameFullPath); @@ -1696,22 +1701,23 @@ NufxArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) if (result == IDOK) { if (renameDlg.fFssep == '\0') renameDlg.fFssep = kNufxNoFssep; + CStringA newNameA(renameDlg.fNewName); nerr = NuRename(fpArchive, pEntry->GetRecordIdx(), - renameDlg.fNewName, renameDlg.fFssep); + newNameA, renameDlg.fFssep); if (nerr != kNuErrNone) { - errMsg.Format("Unable to rename '%s': %s.", pEntry->GetPathName(), + errMsg.Format(L"Unable to rename '%ls': %hs.", pEntry->GetPathName(), NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); break; } - WMSG2("Rename of '%s' to '%s' succeeded\n", - pEntry->GetDisplayName(), renameDlg.fNewName); + WMSG2("Rename of '%ls' to '%ls' succeeded\n", + pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName); } else if (result == IDCANCEL) { WMSG0("Canceling out of remaining renames\n"); break; } else { /* 3rd possibility is IDIGNORE, i.e. skip this entry */ - WMSG1("Skipping rename of '%s'\n", pEntry->GetDisplayName()); + WMSG1("Skipping rename of '%ls'\n", pEntry->GetDisplayName()); } pSelEntry = pSelSet->IterNext(); @@ -1724,7 +1730,7 @@ NufxArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) long statusFlags; nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { - errMsg.Format("Unable to rename all files: %s.", + errMsg.Format(L"Unable to rename all files: %hs.", NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); @@ -1753,15 +1759,15 @@ CString NufxArchive::TestPathName(const GenericEntry* pGenericEntry, const CString& basePath, const CString& newName, char newFssep) const { - CString errMsg(""); + CString errMsg; ASSERT(pGenericEntry != nil); ASSERT(basePath.IsEmpty()); /* can't start or end with fssep */ if (newName.Left(1) == newFssep || newName.Right(1) == newFssep) { - errMsg.Format("Names in NuFX archives may not start or end with a " - "path separator character (%c).", + errMsg.Format(L"Names in NuFX archives may not start or end with a " + L"path separator character (%c).", newFssep); goto bail; } @@ -1769,8 +1775,8 @@ NufxArchive::TestPathName(const GenericEntry* pGenericEntry, /* if it's a disk image, don't allow complex paths */ if (pGenericEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) { if (newName.Find(newFssep) != -1) { - errMsg.Format("Disk image names may not contain a path separator " - "character (%c).", + errMsg.Format(L"Disk image names may not contain a path separator " + L"character (%c).", newFssep); goto bail; } @@ -1787,7 +1793,7 @@ NufxArchive::TestPathName(const GenericEntry* pGenericEntry, ComparePaths(pEntry->GetPathName(), pEntry->GetFssep(), newName, newFssep) == 0) { - errMsg.Format("An entry with that name already exists."); + errMsg = L"An entry with that name already exists."; } pEntry = pEntry->GetNext(); @@ -1884,7 +1890,7 @@ NufxArchive::RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Unable to recompress all files: %s.", + errMsg.Format(L"Unable to recompress all files: %hs.", NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); } else { @@ -1906,8 +1912,8 @@ NufxArchive::RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, if (!result) { ASSERT(pEntry != nil); CString dispStr; - dispStr.Format("Failed while recompressing '%s': %s.", - pEntry->GetDisplayName(), errMsg); + dispStr.Format(L"Failed while recompressing '%ls': %ls.", + pEntry->GetDisplayName(), (LPCWSTR) errMsg); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -1918,7 +1924,7 @@ NufxArchive::RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Unable to recompress all files: %s.", + errMsg.Format(L"Unable to recompress all files: %hs.", NuStrError(nerr)); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); } else { @@ -1961,13 +1967,13 @@ NufxArchive::RecompressThread(NufxEntry* pEntry, int threadKind, char* buf = nil; long len = 0; - WMSG2(" Recompressing %ld '%s'\n", pEntry->GetRecordIdx(), + WMSG2(" Recompressing %ld '%ls'\n", pEntry->GetRecordIdx(), pEntry->GetDisplayName()); /* get a copy of the thread header */ pEntry->FindThreadInfo(threadKind, &thread, pErrMsg); if (!pErrMsg->IsEmpty()) { - pErrMsg->Format("Unable to locate thread for %s (type %d)", + pErrMsg->Format(L"Unable to locate thread for %ls (type %d)", pEntry->GetDisplayName(), threadKind); goto bail; } @@ -1975,7 +1981,7 @@ NufxArchive::RecompressThread(NufxEntry* pEntry, int threadKind, /* if it's already in the target format, skip it */ if (thread.thThreadFormat == pRecompOpts->fCompressionType) { - WMSG2("Skipping (fmt=%d) '%s'\n", + WMSG2("Skipping (fmt=%d) '%ls'\n", pRecompOpts->fCompressionType, pEntry->GetDisplayName()); return true; } @@ -1988,18 +1994,18 @@ NufxArchive::RecompressThread(NufxEntry* pEntry, int threadKind, ASSERT(buf == nil); goto bail; /* abort anything that was pending */ } else if (result != IDOK) { - pErrMsg->Format("Failed while extracting '%s': %s", - pEntry->GetDisplayName(), subErrMsg); + pErrMsg->Format(L"Failed while extracting '%ls': %ls", + pEntry->GetDisplayName(), (LPCWSTR) subErrMsg); goto bail; } *pSizeInMemory += len; /* create a data source for it */ nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, (const unsigned char*)buf, 0, len, ArrayDeleteHandler, + 0, (const BYTE*)buf, 0, len, ArrayDeleteHandler, &pSource); if (nerr != kNuErrNone) { - pErrMsg->Format("Unable to create NufxLib data source (len=%d).", + pErrMsg->Format(L"Unable to create NufxLib data source (len=%d).", len); goto bail; } @@ -2009,7 +2015,7 @@ NufxArchive::RecompressThread(NufxEntry* pEntry, int threadKind, //WMSG1("+++ DELETE threadIdx=%d\n", thread.threadIdx); nerr = NuDeleteThread(fpArchive, thread.threadIdx); if (nerr != kNuErrNone) { - pErrMsg->Format("Unable to delete thread %d: %s", + pErrMsg->Format(L"Unable to delete thread %d: %hs", pEntry->GetRecordIdx(), NuStrError(nerr)); goto bail; } @@ -2019,7 +2025,7 @@ NufxArchive::RecompressThread(NufxEntry* pEntry, int threadKind, nerr = NuAddThread(fpArchive, pEntry->GetRecordIdx(), threadID, pSource, nil); if (nerr != kNuErrNone) { - pErrMsg->Format("Unable to add thread type %d: %s", + pErrMsg->Format(L"Unable to add thread type %d: %hs", threadID, NuStrError(nerr)); goto bail; } @@ -2072,12 +2078,12 @@ NufxArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, /* in case we start handling CRC errors better */ if (pEntry->GetDamaged()) { - WMSG1(" XFER skipping damaged entry '%s'\n", + WMSG1(" XFER skipping damaged entry '%ls'\n", pEntry->GetDisplayName()); continue; } - WMSG1(" XFER converting '%s'\n", pEntry->GetDisplayName()); + WMSG1(" XFER converting '%ls'\n", pEntry->GetDisplayName()); fileDetails.storageName = pEntry->GetDisplayName(); fileDetails.fileType = pEntry->GetFileType(); @@ -2119,8 +2125,8 @@ NufxArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, retval = kXferCancelled; goto bail; /* abort anything that was pending */ } else if (result != IDOK) { - dispMsg.Format("Failed while extracting '%s': %s.", - pEntry->GetDisplayName(), errMsg); + dispMsg.Format(L"Failed while extracting '%ls': %ls.", + pEntry->GetDisplayName(), (LPCWSTR) errMsg); ShowFailureMsg(pMsgWnd, dispMsg, IDS_FAILED); goto bail; } @@ -2140,8 +2146,8 @@ NufxArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, WMSG0("Cancelled during data extract!\n"); goto bail; /* abort anything that was pending */ } else if (result != IDOK) { - dispMsg.Format("Failed while extracting '%s': %s.", - pEntry->GetDisplayName(), errMsg); + dispMsg.Format(L"Failed while extracting '%ls': %ls.", + pEntry->GetDisplayName(), (LPCWSTR) errMsg); ShowFailureMsg(pMsgWnd, dispMsg, IDS_FAILED); goto bail; } @@ -2163,8 +2169,8 @@ NufxArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, WMSG0("Cancelled during rsrc extract!\n"); goto bail; /* abort anything that was pending */ } else if (result != IDOK) { - dispMsg.Format("Failed while extracting '%s': %s.", - pEntry->GetDisplayName(), errMsg); + dispMsg.Format(L"Failed while extracting '%ls': %ls.", + pEntry->GetDisplayName(), (LPCWSTR) errMsg); ShowFailureMsg(pMsgWnd, dispMsg, IDS_FAILED); goto bail; } @@ -2175,7 +2181,7 @@ NufxArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, } if (dataLen < 0 && rsrcLen < 0) { - WMSG1(" XFER: WARNING: nothing worth transferring in '%s'\n", + WMSG1(" XFER: WARNING: nothing worth transferring in '%ls'\n", pEntry->GetDisplayName()); continue; } @@ -2184,8 +2190,8 @@ NufxArchive::XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, &rsrcBuf, rsrcLen); if (!errMsg.IsEmpty()) { WMSG0("XferFile failed!\n"); - errMsg.Format("Failed while transferring '%s': %s.", - pEntry->GetDisplayName(), (const char*) errMsg); + errMsg.Format(L"Failed while transferring '%ls': %ls.", + pEntry->GetDisplayName(), (LPCWSTR) errMsg); ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -2242,7 +2248,7 @@ NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, NuDataSource* pSource = nil; CString errMsg; - WMSG1(" NufxArchive::XferFile '%s'\n", pDetails->storageName); + WMSG1(" NufxArchive::XferFile '%ls'\n", (LPCWSTR) pDetails->storageName); WMSG4(" dataBuf=0x%08lx dataLen=%ld rsrcBuf=0x%08lx rsrcLen=%ld\n", *pDataBuf, dataLen, *pRsrcBuf, rsrcLen); ASSERT(pDataBuf != nil); @@ -2289,7 +2295,7 @@ NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Failed adding record: %s", NuStrError(nerr)); + errMsg.Format(L"Failed adding record: %hs", NuStrError(nerr)); //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } // else the add was cancelled @@ -2304,7 +2310,8 @@ NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, pDetails->fileType == kFileTypeTXT && DiskImg::UsesDOSFileStructure(pDetails->fileSysFmt)) { - WMSG1(" Stripping high ASCII from '%s'\n", pDetails->storageName); + WMSG1(" Stripping high ASCII from '%ls'\n", + (LPCWSTR) pDetails->storageName); unsigned char* ucp = *pDataBuf; long len = dataLen; @@ -2331,7 +2338,7 @@ NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, nerr = NuAddThread(fpArchive, recordIdx, targetID, pSource, nil); if (nerr != kNuErrNone) { - errMsg.Format("Failed adding thread: %s.", NuStrError(nerr)); + errMsg.Format(L"Failed adding thread: %hs.", NuStrError(nerr)); //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -2345,7 +2352,7 @@ NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, *pRsrcBuf, 0, rsrcLen, ArrayDeleteHandler, &pSource); if (nerr != kNuErrNone) { - errMsg = "Unable to create NufxLib data source."; + errMsg = L"Unable to create NufxLib data source."; //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -2355,7 +2362,7 @@ NufxArchive::XferFile(FileDetails* pDetails, unsigned char** pDataBuf, nerr = NuAddThread(fpArchive, recordIdx, kNuThreadIDRsrcFork, pSource, nil); if (nerr != kNuErrNone) { - errMsg.Format("Failed adding thread: %s.", NuStrError(nerr)); + errMsg.Format(L"Failed adding thread: %hs.", NuStrError(nerr)); //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); goto bail; } @@ -2383,7 +2390,7 @@ NufxArchive::XferAbort(CWnd* pMsgWnd) nerr = NuAbort(fpArchive); if (nerr != kNuErrNone) { - errMsg.Format("Failed while aborting procedure: %s.", NuStrError(nerr)); + errMsg.Format(L"Failed while aborting procedure: %hs.", NuStrError(nerr)); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } } @@ -2404,7 +2411,7 @@ NufxArchive::XferFinish(CWnd* pMsgWnd) nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { if (nerr != kNuErrAborted) { - errMsg.Format("Unable to add file: %s.", NuStrError(nerr)); + errMsg.Format(L"Unable to add file: %hs.", NuStrError(nerr)); ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); } @@ -2451,7 +2458,7 @@ NufxArchive::GetComment(CWnd* pMsgWnd, const GenericEntry* pGenericEntry, result = pEntry->ExtractThreadToBuffer(GenericEntry::kCommentThread, &buf, &len, &errMsg); if (result != IDOK) { - WMSG1("Failed getting comment: %s\n", buf); + WMSG1("Failed getting comment: %hs\n", buf); ASSERT(buf == nil); return false; } @@ -2503,7 +2510,7 @@ NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, bool retVal = false; /* convert CRLF to CR */ - CString newStr(str); + CStringA newStr(str); char* srcp; char* dstp; srcp = dstp = newStr.GetBuffer(0); @@ -2531,7 +2538,7 @@ NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, /* delete existing thread */ nerr = NuDeleteThread(fpArchive, threadIdx); if (nerr != kNuErrNone) { - errMsg.Format("Unable to delete thread: %s.", NuStrError(nerr)); + errMsg.Format(L"Unable to delete thread: %hs.", NuStrError(nerr)); goto bail; } } @@ -2545,10 +2552,10 @@ NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, /* create a data source to write from */ nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - maxLen, (const unsigned char*)(const char*)newStr, 0, + maxLen, (const BYTE*)(LPCSTR)newStr, 0, newStr.GetLength(), nil, &pSource); if (nerr != kNuErrNone) { - errMsg.Format("Unable to create NufxLib data source (len=%d, maxLen=%d).", + errMsg.Format(L"Unable to create NufxLib data source (len=%d, maxLen=%d).", newStr.GetLength(), maxLen); goto bail; } @@ -2557,7 +2564,7 @@ NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, nerr = NuAddThread(fpArchive, pEntry->GetRecordIdx(), kNuThreadIDComment, pSource, nil); if (nerr != kNuErrNone) { - errMsg.Format("Unable to add comment thread: %s.", + errMsg.Format(L"Unable to add comment thread: %hs.", NuStrError(nerr)); goto bail; } @@ -2567,7 +2574,7 @@ NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, long statusFlags; nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { - errMsg.Format("Unable to flush comment changes: %s.", + errMsg.Format(L"Unable to flush comment changes: %hs.", NuStrError(nerr)); goto bail; } @@ -2579,7 +2586,7 @@ NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, bail: NuFreeDataSource(pSource); if (!retVal) { - WMSG1("FAILED: %s\n", (LPCTSTR) errMsg); + WMSG1("FAILED: %ls\n", (LPCWSTR) errMsg); NuAbort(fpArchive); } return retVal; @@ -2607,7 +2614,7 @@ NufxArchive::DeleteComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry) nerr = NuDeleteThread(fpArchive, threadIdx); if (nerr != kNuErrNone) { - errMsg.Format("Unable to delete thread: %s.", NuStrError(nerr)); + errMsg.Format(L"Unable to delete thread: %hs.", NuStrError(nerr)); goto bail; } @@ -2615,7 +2622,7 @@ NufxArchive::DeleteComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry) long statusFlags; nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { - errMsg.Format("Unable to flush comment deletion: %s.", + errMsg.Format(L"Unable to flush comment deletion: %hs.", NuStrError(nerr)); goto bail; } @@ -2626,7 +2633,7 @@ NufxArchive::DeleteComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry) bail: if (retVal != 0) { - WMSG1("FAILED: %s\n", (LPCTSTR) errMsg); + WMSG1("FAILED: %ls\n", (LPCWSTR) errMsg); NuAbort(fpArchive); } return retVal; @@ -2658,7 +2665,7 @@ NufxArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, nerr = NuGetRecord(fpArchive, pNufxEntry->GetRecordIdx(), &pRecord); if (nerr != kNuErrNone) { - WMSG2("ERROR: couldn't find recordIdx %ld: %s\n", + WMSG2("ERROR: couldn't find recordIdx %ld: %hs\n", pNufxEntry->GetRecordIdx(), NuStrError(nerr)); return false; } @@ -2670,7 +2677,7 @@ NufxArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, nerr = NuSetRecordAttr(fpArchive, pNufxEntry->GetRecordIdx(), &recordAttr); if (nerr != kNuErrNone) { - WMSG2("ERROR: couldn't set recordAttr %ld: %s\n", + WMSG2("ERROR: couldn't set recordAttr %ld: %hs\n", pNufxEntry->GetRecordIdx(), NuStrError(nerr)); return false; } @@ -2678,7 +2685,7 @@ NufxArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, long statusFlags; nerr = NuFlush(fpArchive, &statusFlags); if (nerr != kNuErrNone) { - WMSG1("ERROR: NuFlush failed: %s\n", NuStrError(nerr)); + WMSG1("ERROR: NuFlush failed: %hs\n", NuStrError(nerr)); /* see if it got converted to read-only status */ if (statusFlags & kNuFlushReadOnly) diff --git a/app/NufxArchive.h b/app/NufxArchive.h index fc846d8..ec9c851 100644 --- a/app/NufxArchive.h +++ b/app/NufxArchive.h @@ -6,11 +6,11 @@ /* * NuFX archive support. */ -#ifndef __NUFX_ARCHIVE__ -#define __NUFX_ARCHIVE__ +#ifndef APP_NUFXARCHIVE_H +#define APP_NUFXARCHIVE_H #include "GenericArchive.h" -#include "../prebuilt/NufxLib.h" // ideally this wouldn't be here, only in .cpp +#include "../nufxlib/NufxLib.h" // ideally this wouldn't be here, only in .cpp /* @@ -72,20 +72,20 @@ public: // One-time initialization; returns an error string. static CString AppInit(void); - virtual OpenResult Open(const char* filename, bool readOnly, + virtual OpenResult Open(const WCHAR* filename, bool readOnly, CString* pErrMsg); - virtual CString New(const char* filename, const void* options); + virtual CString New(const WCHAR* filename, const void* options); virtual CString Flush(void) { return ""; } virtual CString Reload(void); virtual bool IsReadOnly(void) const { return fIsReadOnly; }; virtual bool IsModified(void) const { return false; } - virtual void GetDescription(CString* pStr) const { *pStr = "NuFX"; } + virtual void GetDescription(CString* pStr) const { *pStr = L"NuFX"; } virtual bool BulkAdd(ActionProgressDialog* pActionProgress, const AddFilesDialog* pAddOpts); virtual bool AddDisk(ActionProgressDialog* pActionProgress, const AddFilesDialog* pAddOpts); virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const char* newName) + const WCHAR* newName) { ASSERT(false); return false; } virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet); virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet); @@ -93,11 +93,11 @@ public: virtual CString TestPathName(const GenericEntry* pGenericEntry, const CString& basePath, const CString& newName, char newFssep) const; virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const char* newName) + const WCHAR* newName) { ASSERT(false); return false; } virtual CString TestVolumeName(const DiskFS* pDiskFS, - const char* newName) const - { ASSERT(false); return "!"; } + const WCHAR* newName) const + { ASSERT(false); return L"!"; } virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, const RecompressOptionsDialog* pRecompOpts); virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, @@ -129,7 +129,7 @@ private: NuClose(fpArchive); fpArchive = nil; } - return ""; + return L""; } bool RecompressThread(NufxEntry* pEntry, int threadKind, const RecompressOptionsDialog* pRecompOpts, long* pSizeInMemory, @@ -178,4 +178,4 @@ private: const AddFilesDialog* fpAddOpts; }; -#endif /*__NUFX_ARCHIVE__*/ \ No newline at end of file +#endif /*APP_NUFXARCHIVE_H*/ diff --git a/app/OpenVolumeDialog.cpp b/app/OpenVolumeDialog.cpp index 0941e9b..f35d01b 100644 --- a/app/OpenVolumeDialog.cpp +++ b/app/OpenVolumeDialog.cpp @@ -71,9 +71,9 @@ OpenVolumeDialog::OnInitDialog(void) pListView->GetClientRect(&rect); int width; - width = pListView->GetStringWidth("XXVolume or Device NameXXmmmmmm"); - pListView->InsertColumn(0, "Volume or Device Name", LVCFMT_LEFT, width); - pListView->InsertColumn(1, "Remarks", LVCFMT_LEFT, + width = pListView->GetStringWidth(L"XXVolume or Device NameXXmmmmmm"); + pListView->InsertColumn(0, L"Volume or Device Name", LVCFMT_LEFT, width); + pListView->InsertColumn(1, L"Remarks", LVCFMT_LEFT, rect.Width() - width - ::GetSystemMetrics(SM_CXVSCROLL)); // Load the drive list. @@ -153,11 +153,11 @@ OpenVolumeDialog::LoadLogicalDriveList(CListCtrl* pListView, int* pItemIndex) fVolumeInfo[i].driveType = DRIVE_UNKNOWN; if ((drivesAvailable >> i) & 0x01) { - char driveName[] = "_:\\"; + WCHAR driveName[] = L"_:\\"; driveName[0] = 'A' + i; unsigned int driveType; - const char* driveTypeComment = nil; + const WCHAR* driveTypeComment = nil; BOOL result; driveType = fVolumeInfo[i].driveType = GetDriveType(driveName); @@ -170,19 +170,19 @@ OpenVolumeDialog::LoadLogicalDriveList(CListCtrl* pListView, int* pItemIndex) break; case DRIVE_REMOVABLE: // The disk can be removed from the drive. - driveTypeComment = "Removable"; + driveTypeComment = L"Removable"; break; case DRIVE_FIXED: // The disk cannot be removed from the drive. - driveTypeComment = "Local Disk"; + driveTypeComment = L"Local Disk"; break; case DRIVE_REMOTE: // The drive is a remote (network) drive. - driveTypeComment = "Network"; + driveTypeComment = L"Network"; break; case DRIVE_CDROM: // The drive is a CD-ROM drive. - driveTypeComment = "CD-ROM"; + driveTypeComment = L"CD-ROM"; break; case DRIVE_RAMDISK: // The drive is a RAM disk. @@ -194,79 +194,79 @@ OpenVolumeDialog::LoadLogicalDriveList(CListCtrl* pListView, int* pItemIndex) if (driveType == DRIVE_CDROM && !DiskImgLib::Global::GetHasSPTI()) { /* use "physical" device via ASPI instead */ - WMSG1("Not including CD-ROM '%s' in logical drive list\n", + WMSG1("Not including CD-ROM '%ls' in logical drive list\n", driveName); continue; } - char volNameBuf[256]; - char fsNameBuf[64]; - const char* errorComment = nil; + WCHAR volNameBuf[256]; + WCHAR fsNameBuf[64]; + const WCHAR* errorComment = nil; //DWORD fsFlags; CString entryName, entryRemarks; result = ::GetVolumeInformation(driveName, volNameBuf, - sizeof(volNameBuf), NULL, NULL, NULL /*&fsFlags*/, fsNameBuf, - sizeof(fsNameBuf)); + NELEM(volNameBuf), NULL, NULL, NULL /*&fsFlags*/, fsNameBuf, + NELEM(fsNameBuf)); if (result == FALSE) { DWORD err = GetLastError(); if (err == ERROR_UNRECOGNIZED_VOLUME) { // Win2K: media exists but format not recognized - errorComment = "Non-Windows format"; + errorComment = L"Non-Windows format"; } else if (err == ERROR_NOT_READY) { // Win2K: device exists but no media loaded if (isWin9x) { - WMSG1("Not showing drive '%s': not ready\n", + WMSG1("Not showing drive '%ls': not ready\n", driveName); continue; // safer not to show it } else - errorComment = "Not ready"; + errorComment = L"Not ready"; } else if (err == ERROR_PATH_NOT_FOUND /*Win2K*/ || err == ERROR_INVALID_DATA /*Win98*/) { // Win2K/Win98: device letter not in use - WMSG1("GetVolumeInformation '%s': nothing there\n", + WMSG1("GetVolumeInformation '%ls': nothing there\n", driveName); continue; } else if (err == ERROR_INVALID_PARAMETER) { // Win2K: device is already open - //WMSG1("GetVolumeInformation '%s': currently open??\n", + //WMSG1("GetVolumeInformation '%ls': currently open??\n", // driveName); - errorComment = "(currently open?)"; + errorComment = L"(currently open?)"; //continue; } else if (err == ERROR_ACCESS_DENIED) { // Win2K: disk is open no-read-sharing elsewhere - errorComment = "(already open read-write)"; + errorComment = L"(already open read-write)"; } else if (err == ERROR_GEN_FAILURE) { // Win98: floppy format not recognzied // --> we don't want to access ProDOS floppies via A: in // Win98, so we skip it here - WMSG1("GetVolumeInformation '%s': general failure\n", + WMSG1("GetVolumeInformation '%ls': general failure\n", driveName); continue; } else if (err == ERROR_INVALID_FUNCTION) { // Win2K: CD-ROM with HFS if (driveType == DRIVE_CDROM) - errorComment = "Non-Windows format"; + errorComment = L"Non-Windows format"; else - errorComment = "(invalid disc?)"; + errorComment = L"(invalid disc?)"; } else { - WMSG2("GetVolumeInformation '%s' failed: %ld\n", + WMSG2("GetVolumeInformation '%ls' failed: %ld\n", driveName, GetLastError()); continue; } ASSERT(errorComment != nil); - entryName.Format("(%c:)", 'A' + i); + entryName.Format(L"(%c:)", 'A' + i); if (driveTypeComment != nil) - entryRemarks.Format("%s - %s", driveTypeComment, + entryRemarks.Format(L"%ls - %ls", driveTypeComment, errorComment); else - entryRemarks.Format("%s", errorComment); + entryRemarks.Format(L"%ls", errorComment); } else { - entryName.Format("%s (%c:)", volNameBuf, 'A' + i); + entryName.Format(L"%ls (%c:)", volNameBuf, 'A' + i); if (driveTypeComment != nil) - entryRemarks.Format("%s", driveTypeComment); + entryRemarks.Format(L"%ls", driveTypeComment); else entryRemarks = ""; } @@ -313,7 +313,7 @@ OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) result = HasPhysicalDriveWin9x(i, &remark); if (result) { - driveName.Format("Floppy disk %d", i); + driveName.Format(L"Floppy disk %d", i); pListView->InsertItem(itemIndex, driveName); pListView->SetItemText(itemIndex, 1, remark); pListView->SetItemData(itemIndex, (DWORD) i); @@ -327,7 +327,7 @@ OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) result = HasPhysicalDriveWin9x(i + 128, &remark); if (result) { - driveName.Format("Hard drive %d", i); + driveName.Format(L"Hard drive %d", i); pListView->InsertItem(itemIndex, driveName); pListView->SetItemText(itemIndex, 1, remark); pListView->SetItemData(itemIndex, (DWORD) i + 128); @@ -342,7 +342,7 @@ OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) result = HasPhysicalDriveWin2K(i + 128, &remark); if (result) { - driveName.Format("Physical disk %d", i); + driveName.Format(L"Physical disk %d", i); pListView->InsertItem(itemIndex, driveName); pListView->SetItemText(itemIndex, 1, remark); pListView->SetItemData(itemIndex, (DWORD) i + 128); // HD volume @@ -352,6 +352,8 @@ OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) } if (DiskImgLib::Global::GetHasASPI()) { + WMSG0("IGNORING ASPI"); +#if 0 // can we remove this? DIError dierr; DiskImgLib::ASPI* pASPI = DiskImgLib::Global::GetASPI(); ASPIDevice* deviceArray = nil; @@ -399,6 +401,7 @@ OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) } delete[] deviceArray; +#endif } *pItemIndex = itemIndex; @@ -410,6 +413,8 @@ OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) * * Pass in the Int13 unit number, i.e. 0x00 for the first floppy drive. Win9x * makes direct access to the hard drive very difficult, so we don't even try. + * + * TODO: remove this entirely? */ bool OpenVolumeDialog::HasPhysicalDriveWin9x(int unit, CString* pRemark) @@ -434,7 +439,7 @@ OpenVolumeDialog::HasPhysicalDriveWin9x(int unit, CString* pRemark) if (unit > 4) return false; // floppy drives only - handle = CreateFile("\\\\.\\vwin32", 0, 0, NULL, + handle = CreateFile(L"\\\\.\\vwin32", 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (handle == INVALID_HANDLE_VALUE) { WMSG1(" Unable to open vwin32: %ld\n", ::GetLastError()); @@ -456,7 +461,7 @@ OpenVolumeDialog::HasPhysicalDriveWin9x(int unit, CString* pRemark) reg.reg_EDX = MAKEWORD(unit, 0); // head result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, ®, - sizeof(reg), ®, sizeof(reg), &cb, 0); + sizeof(reg) /*bytes*/, ®, sizeof(reg) /*bytes*/, &cb, 0); lastError = GetLastError(); ::CloseHandle(handle); @@ -501,9 +506,9 @@ OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark) * See if the drive is there. */ ASSERT(unit >= 128 && unit < 160); // arbitrary max - fileName.Format("\\\\.\\PhysicalDrive%d", unit - 128); + fileName.Format(L"\\\\.\\PhysicalDrive%d", unit - 128); - hDevice = ::CreateFile((const char*) fileName, // drive to open + hDevice = ::CreateFile(fileName, // drive to open 0, // no access to the drive FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode NULL, // default security attributes @@ -521,7 +526,7 @@ OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark) result = ::DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, // input buffer - &dge, sizeof(dge), // output buffer + &dge, sizeof(dge), // output buffer + size in bytes &junk, // # bytes returned (LPOVERLAPPED) NULL); // synchronous I/O if (result) { @@ -530,10 +535,10 @@ OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark) WMSG2(" Disk size = %I64d (bytes) = %I64d (MB)\n", diskSize, diskSize / (1024*1024)); if (diskSize > 1024*1024*1024) - pRemark->Format("Size is %.2fGB", + pRemark->Format(L"Size is %.2fGB", (double) diskSize / (1024.0 * 1024.0 * 1024.0)); else - pRemark->Format("Size is %.2fMB", + pRemark->Format(L"Size is %.2fMB", (double) diskSize / (1024.0 * 1024.0)); } else { // Win2K shows ERROR_INVALID_FUNCTION or ERROR_NOT_SUPPORTED @@ -542,7 +547,7 @@ OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark) result = ::DeviceIoControl(hDevice, // device to be queried IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform NULL, 0, // no input buffer - &dg, sizeof(dg), // output buffer + &dg, sizeof(dg), // output buffer + size in bytes &junk, // # bytes returned (LPOVERLAPPED) NULL); // synchronous I/O @@ -558,10 +563,10 @@ OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark) WMSG2("Disk size = %I64d (bytes) = %I64d (MB)\n", diskSize, diskSize / (1024 * 1024)); if (diskSize > 1024*1024*1024) - pRemark->Format("Size is %.2fGB", + pRemark->Format(L"Size is %.2fGB", (double) diskSize / (1024.0 * 1024.0 * 1024.0)); else - pRemark->Format("Size is %.2fMB", + pRemark->Format(L"Size is %.2fMB", (double) diskSize / (1024.0 * 1024.0)); } else { err = GetLastError(); @@ -654,7 +659,8 @@ OpenVolumeDialog::OnOK(void) UINT formatID = 0; if (HIBYTE(HIWORD(driveID)) == 0xaa) { - fChosenDrive.Format("%s%d:%d:%d\\", + // TODO: remove this? + fChosenDrive.Format(L"%hs%d:%d:%d\\", DiskImgLib::kASPIDev, LOBYTE(HIWORD(driveID)), HIBYTE(LOWORD(driveID)), @@ -682,11 +688,11 @@ OpenVolumeDialog::OnOK(void) break; } - fChosenDrive.Format("%c:\\", driveID); + fChosenDrive.Format(L"%c:\\", driveID); } else if ((driveID >= 0 && driveID < 4) || (driveID >= 0x80 && driveID < 0x88)) { - fChosenDrive.Format("%02x:\\", driveID); + fChosenDrive.Format(L"%02x:\\", driveID); } else { ASSERT(false); return; diff --git a/app/OpenVolumeDialog.h b/app/OpenVolumeDialog.h index 776ec5a..f6672d6 100644 --- a/app/OpenVolumeDialog.h +++ b/app/OpenVolumeDialog.h @@ -6,8 +6,8 @@ /* * Class definition for Open Volume dialog. */ -#ifndef __OPEN_VOLUME_DIALOG__ -#define __OPEN_VOLUME_DIALOG__ +#ifndef APP_OPENVOLUMEDIALOG_H +#define APP_OPENVOLUMEDIALOG_H #include #include "resource.h" @@ -20,7 +20,7 @@ class OpenVolumeDialog : public CDialog { public: OpenVolumeDialog(CWnd* pParentWnd = NULL) : CDialog(IDD_OPENVOLUMEDLG, pParentWnd), - fChosenDrive(""), + fChosenDrive(L""), fAllowROChange(true) { Preferences* pPreferences = GET_PREFERENCES_WR(); @@ -67,4 +67,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__OPEN_VOLUME_DIALOG__*/ \ No newline at end of file +#endif /*APP_OPENVOLUMEDIALOG_H*/ diff --git a/app/PasteSpecialDialog.h b/app/PasteSpecialDialog.h index be26493..1867bb4 100644 --- a/app/PasteSpecialDialog.h +++ b/app/PasteSpecialDialog.h @@ -6,8 +6,8 @@ /* * Paste Special dialog. */ -#ifndef __PASTESPECIALDIALOG__ -#define __PASTESPECIALDIALOG__ +#ifndef APP_PASTESPECIALDIALOG_H +#define APP_PASTESPECIALDIALOG_H #include "resource.h" @@ -38,4 +38,4 @@ protected: //DECLARE_MESSAGE_MAP() }; -#endif /*__PASTESPECIALDIALOG__*/ \ No newline at end of file +#endif /*APP_PASTESPECIALDIALOG_H*/ diff --git a/app/Preferences.cpp b/app/Preferences.cpp index f9f5e51..87b3f20 100644 --- a/app/Preferences.cpp +++ b/app/Preferences.cpp @@ -15,25 +15,25 @@ static const char* kDefaultTempPath = "."; /* registry section for columns */ -static const char* kColumnSect = _T("columns"); +static const WCHAR kColumnSect[] = L"columns"; /* registry section for file add options */ -static const char* kAddSect = _T("add"); +static const WCHAR kAddSect[] = L"add"; /* registry section for extraction options */ -static const char* kExtractSect = _T("extract"); +static const WCHAR kExtractSect[] = L"extract"; /* registry section for view options */ -static const char* kViewSect = _T("view"); +static const WCHAR kViewSect[] = L"view"; /* registry section for logical/physical volume operations */ -static const char* kVolumeSect = _T("volume"); +static const WCHAR kVolumeSect[] = L"volume"; /* registry section for file-to-disk options */ -//static const char* kConvDiskSect = _T("conv-disk"); +//static const WCHAR kConvDiskSect[] = L"conv-disk"; /* registry section for disk-to-file options */ -static const char* kConvFileSect = _T("conv-file"); +static const WCHAR kConvFileSect[] = L"conv-file"; /* registry section for folders */ -static const char* kFolderSect = _T("folders"); +static const WCHAR kFolderSect[] = L"folders"; /* registry section for preferences on property pages */ -static const char* kPrefsSect = _T("prefs"); +static const WCHAR kPrefsSect[] = L"prefs"; /* registry section for miscellaneous settings */ -static const char* kMiscSect = _T("misc"); +static const WCHAR kMiscSect[] = L"misc"; /* @@ -46,96 +46,96 @@ static const char* kMiscSect = _T("misc"); const Preferences::PrefMap Preferences::fPrefMaps[kPrefNumLastEntry] = { /**/ { kPrefNumUnknown, kPTNone, nil, nil }, - { kPrAddIncludeSubFolders, kBool, kAddSect, _T("include-sub-folders") }, - { kPrAddStripFolderNames, kBool, kAddSect, _T("strip-folder-names") }, - { kPrAddOverwriteExisting, kBool, kAddSect, _T("overwrite-existing") }, - { kPrAddTypePreservation, kLong, kAddSect, _T("type-preservation") }, - { kPrAddConvEOL, kLong, kAddSect, _T("conv-eol") }, + { kPrAddIncludeSubFolders, kBool, kAddSect, L"include-sub-folders" }, + { kPrAddStripFolderNames, kBool, kAddSect, L"strip-folder-names" }, + { kPrAddOverwriteExisting, kBool, kAddSect, L"overwrite-existing" }, + { kPrAddTypePreservation, kLong, kAddSect, L"type-preservation" }, + { kPrAddConvEOL, kLong, kAddSect, L"conv-eol" }, -// { kPrExtractPath, kString, kExtractSect, _T("path") }, - { kPrExtractConvEOL, kLong, kExtractSect, _T("conv-eol") }, - { kPrExtractConvHighASCII, kBool, kExtractSect, _T("conv-high-ascii") }, - { kPrExtractIncludeData, kBool, kExtractSect, _T("include-data") }, - { kPrExtractIncludeRsrc, kBool, kExtractSect, _T("include-rsrc") }, - { kPrExtractIncludeDisk, kBool, kExtractSect, _T("include-disk") }, - { kPrExtractEnableReformat, kBool, kExtractSect, _T("enable-reformat") }, - { kPrExtractDiskTo2MG, kBool, kExtractSect, _T("disk-to-2mg") }, - { kPrExtractAddTypePreservation, kBool, kExtractSect, _T("add-type-preservation") }, - { kPrExtractAddExtension, kBool, kExtractSect, _T("add-extension") }, - { kPrExtractStripFolderNames, kBool, kExtractSect, _T("strip-folder-names") }, - { kPrExtractOverwriteExisting, kBool, kExtractSect, _T("overwrite-existing") }, +// { kPrExtractPath, kString, kExtractSect, L"path" }, + { kPrExtractConvEOL, kLong, kExtractSect, L"conv-eol" }, + { kPrExtractConvHighASCII, kBool, kExtractSect, L"conv-high-ascii" }, + { kPrExtractIncludeData, kBool, kExtractSect, L"include-data" }, + { kPrExtractIncludeRsrc, kBool, kExtractSect, L"include-rsrc" }, + { kPrExtractIncludeDisk, kBool, kExtractSect, L"include-disk" }, + { kPrExtractEnableReformat, kBool, kExtractSect, L"enable-reformat" }, + { kPrExtractDiskTo2MG, kBool, kExtractSect, L"disk-to-2mg" }, + { kPrExtractAddTypePreservation, kBool, kExtractSect, L"add-type-preservation" }, + { kPrExtractAddExtension, kBool, kExtractSect, L"add-extension" }, + { kPrExtractStripFolderNames, kBool, kExtractSect, L"strip-folder-names" }, + { kPrExtractOverwriteExisting, kBool, kExtractSect, L"overwrite-existing" }, -// { kPrViewIncludeDataForks, kBool, kViewSect, _T("include-data-forks") }, -// { kPrViewIncludeRsrcForks, kBool, kViewSect, _T("include-rsrc-forks") }, -// { kPrViewIncludeDiskImages, kBool, kViewSect, _T("include-disk-images") }, -// { kPrViewIncludeComments, kBool, kViewSect, _T("include-comments") }, +// { kPrViewIncludeDataForks, kBool, kViewSect, L"include-data-forks" }, +// { kPrViewIncludeRsrcForks, kBool, kViewSect, L"include-rsrc-forks" }, +// { kPrViewIncludeDiskImages, kBool, kViewSect, L"include-disk-images" }, +// { kPrViewIncludeComments, kBool, kViewSect, L"include-comments" }, - { kPrConvFileEmptyFolders, kBool, kConvFileSect, _T("preserve-empty-folders") }, + { kPrConvFileEmptyFolders, kBool, kConvFileSect, L"preserve-empty-folders" }, - { kPrOpenArchiveFolder, kString, kFolderSect, _T("open-archive") }, - { kPrConvertArchiveFolder, kString, kFolderSect, _T("convert-archive") }, - { kPrAddFileFolder, kString, kFolderSect, _T("add-file") }, - { kPrExtractFileFolder, kString, kFolderSect, _T("extract-file") }, + { kPrOpenArchiveFolder, kString, kFolderSect, L"open-archive" }, + { kPrConvertArchiveFolder, kString, kFolderSect, L"convert-archive" }, + { kPrAddFileFolder, kString, kFolderSect, L"add-file" }, + { kPrExtractFileFolder, kString, kFolderSect, L"extract-file" }, - { kPrVolumeFilter, kLong, kVolumeSect, _T("open-filter") }, - //{ kPrVolumeReadOnly, kBool, kVolumeSect, _T("read-only") }, + { kPrVolumeFilter, kLong, kVolumeSect, L"open-filter" }, + //{ kPrVolumeReadOnly, kBool, kVolumeSect, L"read-only" }, - { kPrCassetteAlgorithm, kLong, kVolumeSect, _T("cassette-algorithm") }, - { kPrOpenWAVFolder, kString, kFolderSect, _T("open-wav") }, + { kPrCassetteAlgorithm, kLong, kVolumeSect, L"cassette-algorithm" }, + { kPrOpenWAVFolder, kString, kFolderSect, L"open-wav" }, - { kPrMimicShrinkIt, kBool, kPrefsSect, _T("mimic-shrinkit") }, - { kPrBadMacSHK, kBool, kPrefsSect, _T("bad-mac-shk") }, - { kPrReduceSHKErrorChecks, kBool, kPrefsSect, _T("reduce-shk-error-checks") }, - { kPrCoerceDOSFilenames, kBool, kPrefsSect, _T("coerce-dos-filenames") }, - { kPrSpacesToUnder, kBool, kPrefsSect, _T("spaces-to-under") }, - { kPrPasteJunkPaths, kBool, kPrefsSect, _T("paste-junk-paths") }, - { kPrBeepOnSuccess, kBool, kPrefsSect, _T("beep-on-success") }, + { kPrMimicShrinkIt, kBool, kPrefsSect, L"mimic-shrinkit" }, + { kPrBadMacSHK, kBool, kPrefsSect, L"bad-mac-shk" }, + { kPrReduceSHKErrorChecks, kBool, kPrefsSect, L"reduce-shk-error-checks" }, + { kPrCoerceDOSFilenames, kBool, kPrefsSect, L"coerce-dos-filenames" }, + { kPrSpacesToUnder, kBool, kPrefsSect, L"spaces-to-under" }, + { kPrPasteJunkPaths, kBool, kPrefsSect, L"paste-junk-paths" }, + { kPrBeepOnSuccess, kBool, kPrefsSect, L"beep-on-success" }, - { kPrQueryImageFormat, kBool, kPrefsSect, _T("query-image-format") }, - { kPrOpenVolumeRO, kBool, kPrefsSect, _T("open-volume-ro") }, - { kPrOpenVolumePhys0, kBool, kPrefsSect, _T("open-volume-phys0") }, - { kPrProDOSAllowLower, kBool, kPrefsSect, _T("prodos-allow-lower") }, - { kPrProDOSUseSparse, kBool, kPrefsSect, _T("prodos-use-sparse") }, + { kPrQueryImageFormat, kBool, kPrefsSect, L"query-image-format" }, + { kPrOpenVolumeRO, kBool, kPrefsSect, L"open-volume-ro" }, + { kPrOpenVolumePhys0, kBool, kPrefsSect, L"open-volume-phys0" }, + { kPrProDOSAllowLower, kBool, kPrefsSect, L"prodos-allow-lower" }, + { kPrProDOSUseSparse, kBool, kPrefsSect, L"prodos-use-sparse" }, - { kPrCompressionType, kLong, kPrefsSect, _T("compression-type") }, + { kPrCompressionType, kLong, kPrefsSect, L"compression-type" }, - { kPrMaxViewFileSize, kLong, kPrefsSect, _T("max-view-file-size") }, - { kPrNoWrapText, kBool, kPrefsSect, _T("no-wrap-text") }, + { kPrMaxViewFileSize, kLong, kPrefsSect, L"max-view-file-size" }, + { kPrNoWrapText, kBool, kPrefsSect, L"no-wrap-text" }, - { kPrHighlightHexDump, kBool, kPrefsSect, _T("highlight-hex-dump") }, - { kPrHighlightBASIC, kBool, kPrefsSect, _T("highlight-basic") }, - { kPrConvHiResBlackWhite, kBool, kPrefsSect, _T("conv-hi-res-black-white") }, - { kPrConvDHRAlgorithm, kLong, kPrefsSect, _T("dhr-algorithm") }, - { kPrRelaxGfxTypeCheck, kBool, kPrefsSect, _T("relax-gfx-type-check") }, - { kPrDisasmOneByteBrkCop, kBool, kPrefsSect, _T("disasm-onebytebrkcop") }, - //{ kPrEOLConvRaw, kBool, kPrefsSect, _T("eol-conv-raw") }, - { kPrConvTextEOL_HA, kBool, kPrefsSect, _T("conv-eol-ha") }, - { kPrConvPascalText, kBool, kPrefsSect, _T("conv-pascal-text") }, - { kPrConvPascalCode, kBool, kPrefsSect, _T("conv-pascal-code") }, - { kPrConvCPMText, kBool, kPrefsSect, _T("conv-cpm-text") }, - { kPrConvApplesoft, kBool, kPrefsSect, _T("conv-applesoft") }, - { kPrConvInteger, kBool, kPrefsSect, _T("conv-integer") }, - { kPrConvBusiness, kBool, kPrefsSect, _T("conv-business") }, - { kPrConvGWP, kBool, kPrefsSect, _T("conv-gwp") }, - { kPrConvText8, kBool, kPrefsSect, _T("conv-text8") }, - { kPrConvGutenberg, kBool, kPrefsSect, _T("conv-gutenberg") }, - { kPrConvAWP, kBool, kPrefsSect, _T("conv-awp") }, - { kPrConvADB, kBool, kPrefsSect, _T("conv-adb") }, - { kPrConvASP, kBool, kPrefsSect, _T("conv-asp") }, - { kPrConvSCAssem, kBool, kPrefsSect, _T("conv-scassem") }, - { kPrConvDisasm, kBool, kPrefsSect, _T("conv-disasm") }, - { kPrConvHiRes, kBool, kPrefsSect, _T("conv-hi-res") }, - { kPrConvDHR, kBool, kPrefsSect, _T("conv-dhr") }, - { kPrConvSHR, kBool, kPrefsSect, _T("conv-shr") }, - { kPrConvPrintShop, kBool, kPrefsSect, _T("conv-print-shop") }, - { kPrConvMacPaint, kBool, kPrefsSect, _T("conv-mac-paint") }, - { kPrConvProDOSFolder, kBool, kPrefsSect, _T("conv-prodos-folder") }, - { kPrConvResources, kBool, kPrefsSect, _T("conv-resources") }, + { kPrHighlightHexDump, kBool, kPrefsSect, L"highlight-hex-dump" }, + { kPrHighlightBASIC, kBool, kPrefsSect, L"highlight-basic" }, + { kPrConvHiResBlackWhite, kBool, kPrefsSect, L"conv-hi-res-black-white" }, + { kPrConvDHRAlgorithm, kLong, kPrefsSect, L"dhr-algorithm" }, + { kPrRelaxGfxTypeCheck, kBool, kPrefsSect, L"relax-gfx-type-check" }, + { kPrDisasmOneByteBrkCop, kBool, kPrefsSect, L"disasm-onebytebrkcop" }, + //{ kPrEOLConvRaw, kBool, kPrefsSect, L"eol-conv-raw" }, + { kPrConvTextEOL_HA, kBool, kPrefsSect, L"conv-eol-ha" }, + { kPrConvPascalText, kBool, kPrefsSect, L"conv-pascal-text" }, + { kPrConvPascalCode, kBool, kPrefsSect, L"conv-pascal-code" }, + { kPrConvCPMText, kBool, kPrefsSect, L"conv-cpm-text" }, + { kPrConvApplesoft, kBool, kPrefsSect, L"conv-applesoft" }, + { kPrConvInteger, kBool, kPrefsSect, L"conv-integer" }, + { kPrConvBusiness, kBool, kPrefsSect, L"conv-business" }, + { kPrConvGWP, kBool, kPrefsSect, L"conv-gwp" }, + { kPrConvText8, kBool, kPrefsSect, L"conv-text8" }, + { kPrConvGutenberg, kBool, kPrefsSect, L"conv-gutenberg" }, + { kPrConvAWP, kBool, kPrefsSect, L"conv-awp" }, + { kPrConvADB, kBool, kPrefsSect, L"conv-adb" }, + { kPrConvASP, kBool, kPrefsSect, L"conv-asp" }, + { kPrConvSCAssem, kBool, kPrefsSect, L"conv-scassem" }, + { kPrConvDisasm, kBool, kPrefsSect, L"conv-disasm" }, + { kPrConvHiRes, kBool, kPrefsSect, L"conv-hi-res" }, + { kPrConvDHR, kBool, kPrefsSect, L"conv-dhr" }, + { kPrConvSHR, kBool, kPrefsSect, L"conv-shr" }, + { kPrConvPrintShop, kBool, kPrefsSect, L"conv-print-shop" }, + { kPrConvMacPaint, kBool, kPrefsSect, L"conv-mac-paint" }, + { kPrConvProDOSFolder, kBool, kPrefsSect, L"conv-prodos-folder" }, + { kPrConvResources, kBool, kPrefsSect, L"conv-resources" }, - { kPrTempPath, kString, kPrefsSect, _T("temp-path") }, - { kPrExtViewerExts, kString, kPrefsSect, _T("extviewer-exts") }, + { kPrTempPath, kString, kPrefsSect, L"temp-path" }, + { kPrExtViewerExts, kString, kPrefsSect, L"extviewer-exts" }, - { kPrLastOpenFilterIndex, kLong, kMiscSect, _T("open-filter-index") }, + { kPrLastOpenFilterIndex, kLong, kMiscSect, L"open-filter-index" }, /**/ { kPrefNumLastRegistry, kPTNone, nil, nil }, @@ -243,11 +243,11 @@ Preferences::Preferences(void) SetPrefBool(kPrConvResources, true); InitTempPath(); // set default for kPrTempPath - SetPrefString(kPrExtViewerExts, "gif; jpg; jpeg"); + SetPrefString(kPrExtViewerExts, L"gif; jpg; jpeg"); SetPrefLong(kPrLastOpenFilterIndex, 0); - SetPrefString(kPrViewTextTypeFace, "Courier New"); + SetPrefString(kPrViewTextTypeFace, L"Courier New"); SetPrefLong(kPrViewTextPointSize, 10); long width = 680; /* exact width for 80-column text */ long height = 510; /* exact height for file viewer to show IIgs graphic */ @@ -272,39 +272,39 @@ Preferences::Preferences(void) * Restore column widths. */ void -ColumnLayout::LoadFromRegistry(const char* section) +ColumnLayout::LoadFromRegistry(const WCHAR* section) { - char numBuf[8]; + WCHAR numBuf[8]; int i; for (i = 0; i < kNumVisibleColumns; i++) { - sprintf(numBuf, "%d", i); + wsprintf(numBuf, L"%d", i); fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, fColumnWidth[i]); fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, fColumnWidth[i]); } - fSortColumn = gMyApp.GetProfileInt(section, _T("sort-column"), fSortColumn); - fAscending = (gMyApp.GetProfileInt(section, _T("ascending"), fAscending) != 0); + fSortColumn = gMyApp.GetProfileInt(section, L"sort-column", fSortColumn); + fAscending = (gMyApp.GetProfileInt(section, L"ascending", fAscending) != 0); } /* * Store column widths. */ void -ColumnLayout::SaveToRegistry(const char* section) +ColumnLayout::SaveToRegistry(const WCHAR* section) { - char numBuf[8]; + WCHAR numBuf[8]; int i; for (i = 0; i < kNumVisibleColumns; i++) { - sprintf(numBuf, "%d", i); + wsprintf(numBuf, L"%d", i); gMyApp.WriteProfileInt(section, numBuf, fColumnWidth[i]); } - gMyApp.WriteProfileInt(section, _T("sort-column"), fSortColumn); - gMyApp.WriteProfileInt(section, _T("ascending"), fAscending); + gMyApp.WriteProfileInt(section, L"sort-column", fSortColumn); + gMyApp.WriteProfileInt(section, L"ascending", fAscending); } @@ -320,29 +320,29 @@ ColumnLayout::SaveToRegistry(const char* section) void Preferences::InitTempPath(void) { - char buf[MAX_PATH]; + WCHAR buf[MAX_PATH]; DWORD len; CString tempPath; - len = ::GetTempPath(sizeof(buf), buf); + len = ::GetTempPath(NELEM(buf), buf); if (len == 0) { DWORD err = ::GetLastError(); WMSG1("GetTempPath failed, err=%d\n", err); tempPath = kDefaultTempPath; - } else if (len >= sizeof(buf)) { + } else if (len >= NELEM(buf)) { /* sheesh! */ - WMSG1("GetTempPath wants a %d-byte buffer\n", len); + WMSG1("GetTempPath wants a %d-unit buffer\n", len); tempPath = kDefaultTempPath; } else { tempPath = buf; } PathName path(tempPath); - WMSG1("Temp path is '%s'\n", tempPath); + WMSG1("Temp path is '%ls'\n", tempPath); path.SFNToLFN(); tempPath = path.GetPathName(); - WMSG1("Temp path (long form) is '%s'\n", tempPath); + WMSG1("Temp path (long form) is '%ls'\n", tempPath); SetPrefString(kPrTempPath, tempPath); @@ -366,8 +366,8 @@ Preferences::InitFolders(void) SetPrefString(kPrExtractFileFolder, path); SetPrefString(kPrOpenWAVFolder, path); } else { - char buf[MAX_PATH]; - ::GetCurrentDirectory(sizeof(buf), buf); + WCHAR buf[MAX_PATH]; + ::GetCurrentDirectory(NELEM(buf), buf); SetPrefString(kPrOpenArchiveFolder, buf); SetPrefString(kPrConvertArchiveFolder, buf); SetPrefString(kPrAddFileFolder, buf); @@ -375,7 +375,7 @@ Preferences::InitFolders(void) SetPrefString(kPrOpenWAVFolder, buf); } - WMSG1("Default folder is '%s'\n", GetPrefString(kPrExtractFileFolder)); + WMSG1("Default folder is '%ls'\n", GetPrefString(kPrExtractFileFolder)); } /* @@ -459,25 +459,23 @@ Preferences::SetPrefLong(PrefNum num, long val) return; fValues[num] = (void*) val; } -const char* +const WCHAR* Preferences::GetPrefString(PrefNum num) const { if (!ValidateEntry(num, kString)) return nil; - return (const char*) fValues[num]; + return (const WCHAR*) fValues[num]; } void -Preferences::SetPrefString(PrefNum num, const char* str) +Preferences::SetPrefString(PrefNum num, const WCHAR* str) { if (!ValidateEntry(num, kString)) return; free(fValues[num]); - if (str == nil) + if (str == nil) { fValues[num] = nil; - else { - fValues[num] = new char[strlen(str) +1]; - if (fValues[num] != nil) - strcpy((char*)fValues[num], str); + } else { + fValues[num] = wcsdup(str); } } @@ -523,12 +521,12 @@ Preferences::ScanPrefMaps(void) { continue; } - if (strcasecmp(fPrefMaps[i].registryKey, - fPrefMaps[j].registryKey) == 0 && - strcasecmp(fPrefMaps[i].registrySection, - fPrefMaps[j].registrySection) == 0) + if (wcsicmp(fPrefMaps[i].registryKey, + fPrefMaps[j].registryKey) == 0 && + wcsicmp(fPrefMaps[i].registrySection, + fPrefMaps[j].registrySection) == 0) { - WMSG4("HEY: PrefMaps[%d] and [%d] both have '%s'/'%s'\n", + WMSG4("HEY: PrefMaps[%d] and [%d] both have '%ls'/'%ls'\n", i, j, fPrefMaps[i].registrySection, fPrefMaps[i].registryKey); ASSERT(false); diff --git a/app/Preferences.h b/app/Preferences.h index 6609b6c..78b9054 100644 --- a/app/Preferences.h +++ b/app/Preferences.h @@ -13,8 +13,8 @@ * - Add a default value to Preferences::Preferences. If not specified, * strings will be nil and numeric values will be zero. */ -#ifndef __PREFERENCES__ -#define __PREFERENCES__ +#ifndef APP_PREFERENCES_H +#define APP_PREFERENCES_H #include "MyApp.h" @@ -42,8 +42,8 @@ public: } ~ColumnLayout(void) {} - void LoadFromRegistry(const char* section); - void SaveToRegistry(const char* section); + void LoadFromRegistry(const WCHAR* section); + void SaveToRegistry(const WCHAR* section); int GetColumnWidth(int col) const { ASSERT(col >= 0 && col < kNumVisibleColumns); @@ -229,8 +229,8 @@ public: void SetPrefBool(PrefNum num, bool val); long GetPrefLong(PrefNum num) const; void SetPrefLong(PrefNum num, long val); - const char* GetPrefString(PrefNum num) const; - void SetPrefString(PrefNum num, const char* str); + const WCHAR* GetPrefString(PrefNum num) const; + void SetPrefString(PrefNum num, const WCHAR* str); private: @@ -247,8 +247,8 @@ private: typedef struct PrefMap { PrefNum num; PrefType type; - const char* registrySection; - const char* registryKey; + const WCHAR* registrySection; + const WCHAR* registryKey; } PrefMap; static const PrefMap fPrefMaps[kPrefNumLastEntry]; void ScanPrefMaps(void); @@ -275,26 +275,26 @@ private: /* * Registry helpers. */ - UINT GetInt(const char* section, const char* key, int dflt) { + UINT GetInt(const WCHAR* section, const WCHAR* key, int dflt) { return gMyApp.GetProfileInt(section, key, dflt); } - bool GetBool(const char* section, const char* key, bool dflt) { + bool GetBool(const WCHAR* section, const WCHAR* key, bool dflt) { return (gMyApp.GetProfileInt(section, key, dflt) != 0); } - CString GetString(const char* section, const char* key, - const char* dflt) + CString GetString(const WCHAR* section, const WCHAR* key, + const WCHAR* dflt) { return gMyApp.GetProfileString(section, key, dflt); } - BOOL WriteInt(const char* section, const char* key, int value) { + BOOL WriteInt(const WCHAR* section, const WCHAR* key, int value) { return gMyApp.WriteProfileInt(section, key, value); } - BOOL WriteBool(const char* section, const char* key, bool value) { + BOOL WriteBool(const WCHAR* section, const WCHAR* key, bool value) { return gMyApp.WriteProfileInt(section, key, value); } - BOOL WriteString(const char* section, const char* key, const char* value) { + BOOL WriteString(const WCHAR* section, const WCHAR* key, const WCHAR* value) { return gMyApp.WriteProfileString(section, key, value); } }; -#endif /*__PREFERENCES__*/ \ No newline at end of file +#endif /*APP_PREFERENCES_H*/ diff --git a/app/PrefsDialog.cpp b/app/PrefsDialog.cpp index 89f3823..6c0ec7c 100644 --- a/app/PrefsDialog.cpp +++ b/app/PrefsDialog.cpp @@ -520,7 +520,7 @@ PrefsFilesPage::DoDataExchange(CDataExchange* pDX) if (fTempPath.IsEmpty()) { CString appName; appName.LoadString(IDS_MB_APP_NAME); - MessageBox("You must specify a path for temp files", + MessageBox(L"You must specify a path for temp files", appName, MB_OK); pDX->Fail(); } @@ -546,8 +546,8 @@ PrefsFilesPage::OnChooseFolder(void) chooseDir.SetPathName(editPath); if (chooseDir.DoModal() == IDOK) { - const char* ccp = chooseDir.GetPathName(); - WMSG1("New temp path chosen = '%s'\n", ccp); + const WCHAR* ccp = chooseDir.GetPathName(); + WMSG1("New temp path chosen = '%ls'\n", ccp); pEditWnd->SetWindowText(ccp); @@ -593,7 +593,7 @@ END_MESSAGE_MAP() * Construct the preferences dialog from the individual pages. */ PrefsSheet::PrefsSheet(CWnd* pParentWnd) : - CPropertySheet("Preferences", pParentWnd) + CPropertySheet(L"Preferences", pParentWnd) { AddPage(&fGeneralPage); AddPage(&fDiskImagePage); diff --git a/app/PrefsDialog.h b/app/PrefsDialog.h index cf95eae..fbee91b 100644 --- a/app/PrefsDialog.h +++ b/app/PrefsDialog.h @@ -6,8 +6,8 @@ /* * Classes to support the Preferences property pages. */ -#ifndef __PREFSDIALOG__ -#define __PREFSDIALOG__ +#ifndef APP_PREFSDIALOG_H +#define APP_PREFSDIALOG_H #include "Preferences.h" #include "../util/UtilLib.h" @@ -239,4 +239,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__PREFSDIALOG__*/ \ No newline at end of file +#endif /*APP_PREFSDIALOG_H*/ diff --git a/app/Print.cpp b/app/Print.cpp index 3501f58..46a2040 100644 --- a/app/Print.cpp +++ b/app/Print.cpp @@ -18,8 +18,8 @@ * ========================================================================== */ -/*static*/ const char* PrintStuff::kCourierNew = _T("Courier New"); -/*static*/ const char* PrintStuff::kTimesNewRoman = _T("Times New Roman"); +/*static*/ const WCHAR PrintStuff::kCourierNew[] = L"Courier New"; +/*static*/ const WCHAR PrintStuff::kTimesNewRoman[] = L"Times New Roman"; /* * Set up various values. @@ -122,7 +122,7 @@ PrintStuff::TrimString(CString* pStr, int width, bool addOnLeft) } if (!addOnLeft) { - WMSG1("Now trying '%s'\n", (LPCTSTR) newStr); + WMSG1("Now trying '%ls'\n", (LPCWSTR) newStr); } strWidth = StringWidth(newStr); } @@ -367,7 +367,7 @@ PrintContentList::DoPrintPage(int page) */ fpDC->TextOut(0, 1 * fCharHeight, fDocTitle); CString pageNum; - pageNum.Format("Page %d/%d", page, fNumPages); + pageNum.Format(L"Page %d/%d", page, fNumPages); int pageNumWidth = StringWidth(pageNum); fpDC->TextOut(fHorzRes - pageNumWidth, 1 * fCharHeight, pageNum); @@ -472,14 +472,14 @@ PrintRichEdit::PrintPreflight(CRichEditCtrl* pREC, int* pNumPages) fEndChar = -1; fStartPage = 0; fEndPage = -1; - return StartPrint(pREC, "(test)", pNumPages, false); + return StartPrint(pREC, L"(test)", pNumPages, false); } /* * Print all pages. */ int -PrintRichEdit::PrintAll(CRichEditCtrl* pREC, const char* title) +PrintRichEdit::PrintAll(CRichEditCtrl* pREC, const WCHAR* title) { fStartChar = 0; fEndChar = -1; @@ -492,7 +492,7 @@ PrintRichEdit::PrintAll(CRichEditCtrl* pREC, const char* title) * Print a range of pages. */ int -PrintRichEdit::PrintPages(CRichEditCtrl* pREC, const char* title, +PrintRichEdit::PrintPages(CRichEditCtrl* pREC, const WCHAR* title, int startPage, int endPage) { fStartChar = 0; @@ -506,7 +506,7 @@ PrintRichEdit::PrintPages(CRichEditCtrl* pREC, const char* title, * Print the selected area. */ int -PrintRichEdit::PrintSelection(CRichEditCtrl* pREC, const char* title, +PrintRichEdit::PrintSelection(CRichEditCtrl* pREC, const WCHAR* title, long startChar, long endChar) { fStartChar = startChar; @@ -520,7 +520,7 @@ PrintRichEdit::PrintSelection(CRichEditCtrl* pREC, const char* title, * Start the printing process by posting a print-cancel dialog. */ int -PrintRichEdit::StartPrint(CRichEditCtrl* pREC, const char* title, +PrintRichEdit::StartPrint(CRichEditCtrl* pREC, const WCHAR* title, int* pNumPages, bool doPrint) { CancelDialog* pPCD = nil; @@ -630,10 +630,10 @@ PrintRichEdit::ComputeMargins(void) pOldFont = fpDC->SelectObject(&tmpFont); // in theory we could compute one 'X' * 80; this seems more reliable - char str[81]; + WCHAR str[81]; for (int i = 0; i < 80; i++) str[i] = 'X'; - str[i] = '\0'; + str[80] = '\0'; char80width = StringWidth(str); fpDC->SelectObject(pOldFont); @@ -668,7 +668,7 @@ PrintRichEdit::ComputeMargins(void) * This was derived from Microsft KB article 129860. */ int -PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const char* title, +PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const WCHAR* title, int* pNumPages, bool doPrint) { FORMATRANGE fr; @@ -676,7 +676,7 @@ PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const char* title, long textLength, textPrinted, lastTextPrinted; int pageNum; - WMSG2("DoPrint: title='%s' doPrint=%d\n", title, doPrint); + WMSG2("DoPrint: title='%ls' doPrint=%d\n", title, doPrint); WMSG4(" startChar=%d endChar=%d startPage=%d endPage=%d\n", fStartChar, fEndChar, fStartPage, fEndPage); @@ -712,14 +712,15 @@ PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const char* title, * GetTextLengthEx is part of "riched20.dll". Win9x uses "riched32.dll", * which doesn't support the call. */ -#ifdef _UNICODE -# error "should be code page 1200, not CP_ACP" -#endif GETTEXTLENGTHEX exLenReq; long basicTextLength, extdTextLength; basicTextLength = pREC->GetTextLength(); exLenReq.flags = GTL_PRECISE | GTL_NUMCHARS; +#ifdef _UNICODE + exLenReq.codepage = 1200; // UTF-16 little-endian +#else exLenReq.codepage = CP_ACP; +#endif extdTextLength = (long)::SendMessage(pREC->m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM) &exLenReq, (LPARAM) NULL); WMSG2("RichEdit text length: std=%ld extd=%ld\n", @@ -754,7 +755,7 @@ PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const char* title, CFont* pOldFont = fpDC->SelectObject(&fTitleFont); fpDC->TextOut(0, 0 * fCharHeight, title); CString pageNumStr; - pageNumStr.Format("Page %d", pageNum); + pageNumStr.Format(L"Page %d", pageNum); int pageNumWidth = StringWidth(pageNumStr); fpDC->TextOut(fHorzRes - pageNumWidth, 0 * fCharHeight, pageNumStr); fpDC->SelectObject(pOldFont); diff --git a/app/Print.h b/app/Print.h index 13a4482..7672da0 100644 --- a/app/Print.h +++ b/app/Print.h @@ -6,8 +6,8 @@ /* * Goodies needed for printing. */ -#ifndef __PRINT__ -#define __PRINT__ +#ifndef APP_PRINT_H +#define APP_PRINT_H #include "ContentList.h" #include "resource.h" @@ -33,8 +33,8 @@ protected: int StringWidth(const CString& str); - static const char* kCourierNew; - static const char* kTimesNewRoman; + static const WCHAR kCourierNew[]; + static const WCHAR kTimesNewRoman[]; enum { kTwipsPerInch = 1440 @@ -113,18 +113,18 @@ public: * Commence printing. */ int PrintPreflight(CRichEditCtrl* pREC, int* pNumPages); - int PrintAll(CRichEditCtrl* pREC, const char* title); - int PrintPages(CRichEditCtrl* pREC, const char* title, int startPage, + int PrintAll(CRichEditCtrl* pREC, const WCHAR* title); + int PrintPages(CRichEditCtrl* pREC, const WCHAR* title, int startPage, int endPage); - int PrintSelection(CRichEditCtrl* pREC, const char* title, long startChar, + int PrintSelection(CRichEditCtrl* pREC, const WCHAR* title, long startChar, long endChar); private: - int StartPrint(CRichEditCtrl* pREC, const char* title, + int StartPrint(CRichEditCtrl* pREC, const WCHAR* title, int* pNumPages, bool doPrint); void PrintPrep(FORMATRANGE* pFR); void ComputeMargins(void); - int DoPrint(CRichEditCtrl* pREC, const char* title, int* pNumPages, + int DoPrint(CRichEditCtrl* pREC, const WCHAR* title, int* pNumPages, bool doPrint); bool fInitialized; @@ -140,4 +140,4 @@ private: int fEndPage; }; -#endif /*__PRINT__*/ \ No newline at end of file +#endif /*APP_PRINT_H*/ diff --git a/app/ProgressCounterDialog.h b/app/ProgressCounterDialog.h index e021e7f..902a812 100644 --- a/app/ProgressCounterDialog.h +++ b/app/ProgressCounterDialog.h @@ -7,8 +7,8 @@ * Show the progress of something that has no definite bound. Because we * don't know when we need to stop, we just count upward. */ -#ifndef __PROGRESSCOUNTERDIALOG__ -#define __PROGRESSCOUNTERDIALOG__ +#ifndef APP_PROGRESSCOUNTERDIALOG_H +#define APP_PROGRESSCOUNTERDIALOG_H #include "resource.h" @@ -20,7 +20,7 @@ public: BOOL Create(const CString& descr, CWnd* pParentWnd = NULL) { fpParentWnd = pParentWnd; fDescr = descr; - fCountFormat = "%d"; + fCountFormat = L"%d"; fCancel = false; /* disable the parent window before we're created */ @@ -56,7 +56,7 @@ private: CWnd* pWnd = GetDlgItem(IDC_PROGRESS_COUNTER_DESC); pWnd->SetWindowText(fDescr); pWnd = GetDlgItem(IDC_PROGRESS_COUNTER_COUNT); - pWnd->SetWindowText(""); + pWnd->SetWindowText(L""); pWnd->SetFocus(); // get focus off of the Cancel button return FALSE; // accept our focus } @@ -67,4 +67,4 @@ private: bool fCancel; }; -#endif /*__PROGRESSCOUNTERDIALOG__*/ +#endif /*APP_PROGRESSCOUNTERDIALOG_H*/ diff --git a/app/RecompressOptionsDialog.cpp b/app/RecompressOptionsDialog.cpp index dbb47fd..44bff0d 100644 --- a/app/RecompressOptionsDialog.cpp +++ b/app/RecompressOptionsDialog.cpp @@ -39,16 +39,16 @@ RecompressOptionsDialog::LoadComboBox(NuThreadFormat fmt) { static const struct { NuThreadFormat format; - const char* name; + const WCHAR* name; } kComboStrings[] = { - { kNuThreadFormatUncompressed, "No compression" }, - { kNuThreadFormatHuffmanSQ, "Squeeze" }, - { kNuThreadFormatLZW1, "Dynamic LZW/1" }, - { kNuThreadFormatLZW2, "Dynamic LZW/2" }, - { kNuThreadFormatLZC12, "12-bit LZC" }, - { kNuThreadFormatLZC16, "16-bit LZC" }, - { kNuThreadFormatDeflate, "Deflate" }, - { kNuThreadFormatBzip2, "Bzip2" }, + { kNuThreadFormatUncompressed, L"No compression" }, + { kNuThreadFormatHuffmanSQ, L"Squeeze" }, + { kNuThreadFormatLZW1, L"Dynamic LZW/1" }, + { kNuThreadFormatLZW2, L"Dynamic LZW/2" }, + { kNuThreadFormatLZC12, L"12-bit LZC" }, + { kNuThreadFormatLZC16, L"16-bit LZC" }, + { kNuThreadFormatDeflate, L"Deflate" }, + { kNuThreadFormatBzip2, L"Bzip2" }, }; CComboBox* pCombo; diff --git a/app/RecompressOptionsDialog.h b/app/RecompressOptionsDialog.h index 18aaf8c..116497c 100644 --- a/app/RecompressOptionsDialog.h +++ b/app/RecompressOptionsDialog.h @@ -7,11 +7,11 @@ * Options for recompressing files. This is derived from the "use selection" * dialog. */ -#ifndef __RECOMPRESS_OPTIONS_DIALOG__ -#define __RECOMPRESS_OPTIONS_DIALOG__ +#ifndef APP_RECOMPESSOPTIONSDIALOG_H +#define APP_RECOMPESSOPTIONSDIALOG_H #include "UseSelectionDialog.h" -#include "../prebuilt/NufxLib.h" +#include "../nufxlib/NufxLib.h" #include "resource.h" /* @@ -40,4 +40,4 @@ private: //DECLARE_MESSAGE_MAP() }; -#endif /*__RECOMPRESS_OPTIONS_DIALOG__*/ \ No newline at end of file +#endif /*APP_RECOMPESSOPTIONSDIALOG_H*/ diff --git a/app/Registry.cpp b/app/Registry.cpp index 1840cfd..d72270e 100644 --- a/app/Registry.cpp +++ b/app/Registry.cpp @@ -11,12 +11,12 @@ #include "Main.h" #include "MyApp.h" -#define kRegAppName "CiderPress" -#define kRegExeName "CiderPress.exe" -#define kCompanyName "faddenSoft" +#define kRegAppName L"CiderPress" +#define kRegExeName L"CiderPress.exe" +#define kCompanyName L"faddenSoft" -static const char* kRegKeyCPKVersions = _T("vrs"); -static const char* kRegKeyCPKExpire = _T("epr"); +static const WCHAR kRegKeyCPKVersions[] = L"vrs"; +static const WCHAR kRegKeyCPKExpire[] = L"epr"; /* * Application path. Add two keys: @@ -27,23 +27,23 @@ static const char* kRegKeyCPKExpire = _T("epr"); * The $PATH that will be in effect when the program starts (but only if * launched from the Windows explorer). */ -static const char* kAppKeyBase = - _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" kRegExeName); +static const WCHAR kAppKeyBase[] = + L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" kRegExeName; /* * Local settings. App stuff goes in the per-user key, registration info is * in the per-machine key. */ -static const char* kMachineSettingsBaseKey = - _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\" kCompanyName "\\" kRegAppName); -static const char* kUserSettingsBaseKey = - _T("HKEY_CURRENT_USER\\Software\\" kCompanyName "\\" kRegAppName); +static const WCHAR kMachineSettingsBaseKey[] = + L"HKEY_LOCAL_MACHINE\\SOFTWARE\\" kCompanyName L"\\" kRegAppName; +static const WCHAR kUserSettingsBaseKey[] = + L"HKEY_CURRENT_USER\\Software\\" kCompanyName L"\\" kRegAppName; /* * Set this key + ".XXX" to (Default)=AppID. This associates the file * type with kRegAppID. */ -//static const char* kFileExtensionBase = _T("HKEY_CLASSES_ROOT"); +//static const char* kFileExtensionBase = L"HKEY_CLASSES_ROOT"; /* * Description of data files. Set this key + AppID to 40-char string, e.g. @@ -51,48 +51,48 @@ static const char* kUserSettingsBaseKey = * * Can also set DefaultIcon = Pathname [,Index] */ -//static const char* kAppIDBase = _T("HKEY_CLASSES_ROOT"); +//static const char* kAppIDBase = L"HKEY_CLASSES_ROOT"; /* * Put one of these under the AppID to specify the icon for a file type. */ -static const char* kDefaultIcon = _T("DefaultIcon"); +static const WCHAR* kDefaultIcon = L"DefaultIcon"; -static const char* kRegKeyCPKStr = "CPK"; +static const WCHAR* kRegKeyCPKStr = L"CPK"; /* * Table of file type associations. They will appear in the UI in the same * order that they appear here, so try to maintain alphabetical order. */ -#define kAppIDNuFX _T("CiderPress.NuFX") -#define kAppIDDiskImage _T("CiderPress.DiskImage") -#define kAppIDBinaryII _T("CiderPress.BinaryII") -#define kNoAssociation _T("(no association)") +#define kAppIDNuFX L"CiderPress.NuFX" +#define kAppIDDiskImage L"CiderPress.DiskImage" +#define kAppIDBinaryII L"CiderPress.BinaryII" +#define kNoAssociation L"(no association)" const MyRegistry::FileTypeAssoc MyRegistry::kFileTypeAssoc[] = { - { _T(".2MG"), kAppIDDiskImage }, - { _T(".APP"), kAppIDDiskImage }, - { _T(".BNY"), kAppIDBinaryII }, - { _T(".BQY"), kAppIDBinaryII }, - { _T(".BSE"), kAppIDNuFX }, - { _T(".BXY"), kAppIDNuFX }, - { _T(".D13"), kAppIDDiskImage }, - { _T(".DDD"), kAppIDDiskImage }, - { _T(".DO"), kAppIDDiskImage }, - { _T(".DSK"), kAppIDDiskImage }, - { _T(".FDI"), kAppIDDiskImage }, - { _T(".HDV"), kAppIDDiskImage }, - { _T(".IMG"), kAppIDDiskImage }, - { _T(".NIB"), kAppIDDiskImage }, - { _T(".PO"), kAppIDDiskImage }, - { _T(".SDK"), kAppIDDiskImage }, - { _T(".SEA"), kAppIDNuFX }, - { _T(".SHK"), kAppIDNuFX }, -// { _T(".DC"), kAppIDDiskImage }, -// { _T(".DC6"), kAppIDDiskImage }, -// { _T(".GZ"), kAppIDDiskImage }, -// { _T(".NB2"), kAppIDDiskImage }, -// { _T(".RAW"), kAppIDDiskImage }, -// { _T(".ZIP"), kAppIDDiskImage }, + { L".2MG", kAppIDDiskImage }, + { L".APP", kAppIDDiskImage }, + { L".BNY", kAppIDBinaryII }, + { L".BQY", kAppIDBinaryII }, + { L".BSE", kAppIDNuFX }, + { L".BXY", kAppIDNuFX }, + { L".D13", kAppIDDiskImage }, + { L".DDD", kAppIDDiskImage }, + { L".DO", kAppIDDiskImage }, + { L".DSK", kAppIDDiskImage }, + { L".FDI", kAppIDDiskImage }, + { L".HDV", kAppIDDiskImage }, + { L".IMG", kAppIDDiskImage }, + { L".NIB", kAppIDDiskImage }, + { L".PO", kAppIDDiskImage }, + { L".SDK", kAppIDDiskImage }, + { L".SEA", kAppIDNuFX }, + { L".SHK", kAppIDNuFX }, +// { L".DC", kAppIDDiskImage }, +// { L".DC6", kAppIDDiskImage }, +// { L".GZ", kAppIDDiskImage }, +// { L".NB2", kAppIDDiskImage }, +// { L".RAW", kAppIDDiskImage }, +// { L".ZIP", kAppIDDiskImage }, }; static const struct { @@ -137,13 +137,13 @@ MyRegistry::OneTimeInstall(void) const res = RegOpenKeyEx(HKEY_CLASSES_ROOT, kFileTypeAssoc[i].ext, 0, KEY_READ, &hExtKey); if (res == ERROR_SUCCESS) { - WMSG1(" Found existing HKCR\\'%s', leaving alone\n", + WMSG1(" Found existing HKCR\\'%ls', leaving alone\n", kFileTypeAssoc[i].ext); RegCloseKey(hExtKey); } else if (res == ERROR_FILE_NOT_FOUND) { OwnExtension(kFileTypeAssoc[i].ext, kFileTypeAssoc[i].appID); } else { - WMSG2(" Got error %ld opening HKCR\\'%s', leaving alone\n", + WMSG2(" Got error %ld opening HKCR\\'%ls', leaving alone\n", res, kFileTypeAssoc[i].ext); } } @@ -201,7 +201,7 @@ MyRegistry::OneTimeUninstall(void) const * (in m_pszRegistryKey) with the app name (in m_pszProfileName) and prepend * "HKEY_CURRENT_USER\Software\". */ -const char* +const WCHAR* MyRegistry::GetAppRegistryKey(void) const { return kCompanyName; @@ -211,11 +211,11 @@ MyRegistry::GetAppRegistryKey(void) const * See if an AppID is one we recognize. */ bool -MyRegistry::IsOurAppID(const char* id) const +MyRegistry::IsOurAppID(const WCHAR* id) const { - return (strcasecmp(id, kAppIDNuFX) == 0 || - strcasecmp(id, kAppIDDiskImage) == 0 || - strcasecmp(id, kAppIDBinaryII) == 0); + return (wcsicmp(id, kAppIDNuFX) == 0 || + wcsicmp(id, kAppIDDiskImage) == 0 || + wcsicmp(id, kAppIDBinaryII) == 0); } /* @@ -229,24 +229,24 @@ MyRegistry::IsOurAppID(const char* id) const void MyRegistry::FixBasicSettings(void) const { - const char* exeName = gMyApp.GetExeFileName(); - ASSERT(exeName != nil && strlen(exeName) > 0); + const WCHAR* exeName = gMyApp.GetExeFileName(); + ASSERT(exeName != nil && wcslen(exeName) > 0); WMSG0("Fixing any missing file type AppID entries in registry\n"); - ConfigureAppID(kAppIDNuFX, "NuFX Archive (CiderPress)", exeName, 1); - ConfigureAppID(kAppIDBinaryII, "Binary II (CiderPress)", exeName, 2); - ConfigureAppID(kAppIDDiskImage, "Disk Image (CiderPress)", exeName, 3); + ConfigureAppID(kAppIDNuFX, L"NuFX Archive (CiderPress)", exeName, 1); + ConfigureAppID(kAppIDBinaryII, L"Binary II (CiderPress)", exeName, 2); + ConfigureAppID(kAppIDDiskImage, L"Disk Image (CiderPress)", exeName, 3); } /* * Set up the registry goodies for one appID. */ void -MyRegistry::ConfigureAppID(const char* appID, const char* descr, - const char* exeName, int iconIdx) const +MyRegistry::ConfigureAppID(const WCHAR* appID, const WCHAR* descr, + const WCHAR* exeName, int iconIdx) const { - WMSG2(" Configuring '%s' for '%s'\n", appID, exeName); + WMSG2(" Configuring '%ls' for '%ls'\n", appID, exeName); HKEY hAppKey = nil; HKEY hIconKey = nil; @@ -263,31 +263,33 @@ MyRegistry::ConfigureAppID(const char* appID, const char* descr, &hIconKey, &dw) == ERROR_SUCCESS) { DWORD type, size; - unsigned char buf[256]; + unsigned char buf[512]; long res; - size = sizeof(buf); - res = RegQueryValueEx(hIconKey, "", nil, &type, buf, &size); + size = sizeof(buf); // size in bytes + res = RegQueryValueEx(hIconKey, L"", nil, &type, buf, &size); if (res == ERROR_SUCCESS && size > 1) { - WMSG1(" Icon for '%s' already exists, not altering\n", appID); + WMSG1(" Icon for '%ls' already exists, not altering\n", appID); } else { CString iconStr; - iconStr.Format("%s,%d", exeName, iconIdx); + iconStr.Format(L"%ls,%d", exeName, iconIdx); - if (RegSetValueEx(hIconKey, "", 0, REG_SZ, (const unsigned char*) - (const char*) iconStr, strlen(iconStr)) == ERROR_SUCCESS) + if (RegSetValueEx(hIconKey, L"", 0, REG_SZ, + (const BYTE*)(LPCTSTR) iconStr, + wcslen(iconStr) * sizeof(WCHAR)) == ERROR_SUCCESS) { - WMSG2(" Set icon for '%s' to '%s'\n", appID, (LPCTSTR) iconStr); + WMSG2(" Set icon for '%ls' to '%ls'\n", appID, + (LPCWSTR) iconStr); } else { - WMSG2(" WARNING: unable to set DefaultIcon for '%s' to '%s'\n", - appID, (LPCTSTR) iconStr); + WMSG2(" WARNING: unable to set DefaultIcon for '%ls' to '%ls'\n", + appID, (LPCWSTR) iconStr); } } } else { - WMSG1("WARNING: couldn't set up DefaultIcon for '%s'\n", appID); + WMSG1("WARNING: couldn't set up DefaultIcon for '%ls'\n", appID); } } else { - WMSG1("WARNING: couldn't create AppID='%s'\n", appID); + WMSG1("WARNING: couldn't create AppID='%ls'\n", appID); } RegCloseKey(hIconKey); @@ -299,8 +301,8 @@ MyRegistry::ConfigureAppID(const char* appID, const char* descr, * description) and put the "Open" command in "...\shell\open\command". */ void -MyRegistry::ConfigureAppIDSubFields(HKEY hAppKey, const char* descr, - const char* exeName) const +MyRegistry::ConfigureAppIDSubFields(HKEY hAppKey, const WCHAR* descr, + const WCHAR* exeName) const { HKEY hShellKey, hOpenKey, hCommandKey; DWORD dw; @@ -310,43 +312,44 @@ MyRegistry::ConfigureAppIDSubFields(HKEY hAppKey, const char* descr, ASSERT(exeName != nil); hShellKey = hOpenKey = hCommandKey = nil; - if (RegSetValueEx(hAppKey, "", 0, REG_SZ, (const unsigned char*) descr, - strlen(descr)) != ERROR_SUCCESS) + if (RegSetValueEx(hAppKey, L"", 0, REG_SZ, (const BYTE*) descr, + wcslen(descr) * sizeof(WCHAR)) != ERROR_SUCCESS) { - WMSG1(" WARNING: unable to set description to '%s'\n", descr); + WMSG1(" WARNING: unable to set description to '%ls'\n", descr); } - if (RegCreateKeyEx(hAppKey, _T("shell"), 0, REG_NONE, + if (RegCreateKeyEx(hAppKey, L"shell", 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, &hShellKey, &dw) == ERROR_SUCCESS) { - if (RegCreateKeyEx(hShellKey, _T("open"), 0, REG_NONE, + if (RegCreateKeyEx(hShellKey, L"open", 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, &hOpenKey, &dw) == ERROR_SUCCESS) { - if (RegCreateKeyEx(hOpenKey, _T("command"), 0, REG_NONE, + if (RegCreateKeyEx(hOpenKey, L"command", 0, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, &hCommandKey, &dw) == ERROR_SUCCESS) { DWORD type, size; - unsigned char buf[MAX_PATH+8]; + WCHAR buf[MAX_PATH+8]; long res; - size = sizeof(buf); - res = RegQueryValueEx(hCommandKey, "", nil, &type, buf, &size); + size = sizeof(buf); // size in bytes + res = RegQueryValueEx(hCommandKey, L"", nil, &type, (LPBYTE) buf, + &size); if (res == ERROR_SUCCESS && size > 1) { - WMSG1(" Command already exists, not altering ('%s')\n", buf); + WMSG1(" Command already exists, not altering ('%ls')\n", buf); } else { CString openCmd; - openCmd.Format("\"%s\" \"%%1\"", exeName); - if (RegSetValueEx(hCommandKey, "", 0, REG_SZ, - (const unsigned char*) (const char*) openCmd, - strlen(openCmd)) == ERROR_SUCCESS) + openCmd.Format(L"\"%ls\" \"%%1\"", exeName); + if (RegSetValueEx(hCommandKey, L"", 0, REG_SZ, + (LPBYTE)(LPCWSTR) openCmd, + wcslen(openCmd) * sizeof(WCHAR)) == ERROR_SUCCESS) { - WMSG1(" Set command to '%s'\n", openCmd); + WMSG1(" Set command to '%ls'\n", openCmd); } else { - WMSG1(" WARNING: unable to set open cmd '%s'\n", openCmd); + WMSG1(" WARNING: unable to set open cmd '%ls'\n", openCmd); } } } @@ -424,22 +427,22 @@ MyRegistry::GetFileAssoc(int idx, CString* pExt, CString* pHandler, res = RegOpenKeyEx(HKEY_CLASSES_ROOT, *pExt, 0, KEY_READ, &hExtKey); if (res == ERROR_SUCCESS) { - unsigned char buf[260]; - DWORD type, size; + WCHAR buf[260]; + DWORD type; + DWORD size = sizeof(buf); // size in bytes - size = sizeof(buf); - res = RegQueryValueEx(hExtKey, "", nil, &type, buf, &size); + res = RegQueryValueEx(hExtKey, L"", nil, &type, (LPBYTE)buf, &size); if (res == ERROR_SUCCESS) { - WMSG1(" Got '%s'\n", buf); + WMSG1(" Got '%ls'\n", buf); appID = buf; if (GetAssocAppName(appID, pHandler) != 0) *pHandler = appID; } else { - WMSG1("RegQueryValueEx failed on '%s'\n", (LPCTSTR) *pExt); + WMSG1("RegQueryValueEx failed on '%ls'\n", (LPCWSTR) *pExt); } } else { - WMSG1(" RegOpenKeyEx failed on '%s'\n", *pExt); + WMSG1(" RegOpenKeyEx failed on '%ls'\n", *pExt); } *pOurs = false; @@ -461,17 +464,19 @@ int MyRegistry::GetAssocAppName(const CString& appID, CString* pCmd) const { CString keyName; - unsigned char buf[260]; - DWORD type, size = sizeof(buf); + WCHAR buf[260]; HKEY hAppKey = nil; long res; int result = -1; - keyName = appID + "\\shell\\open\\command"; + keyName = appID + L"\\shell\\open\\command"; res = RegOpenKeyEx(HKEY_CLASSES_ROOT, keyName, 0, KEY_READ, &hAppKey); if (res == ERROR_SUCCESS) { - res = RegQueryValueEx(hAppKey, "", nil, &type, buf, &size); + DWORD type; + DWORD size = sizeof(buf); // size in bytes + + res = RegQueryValueEx(hAppKey, L"", nil, &type, (LPBYTE) buf, &size); if (res == ERROR_SUCCESS) { CString cmd(buf); int pos; @@ -487,14 +492,14 @@ MyRegistry::GetAssocAppName(const CString& appID, CString* pCmd) const *pCmd = cmd; result = 0; } else { - WMSG1("Unable to open shell\\open\\command for '%s'\n", appID); + WMSG1("Unable to open shell\\open\\command for '%ls'\n", appID); } } else { CString errBuf; GetWin32ErrorString(res, &errBuf); - WMSG2("Unable to open AppID key '%s' (%s)\n", - keyName, (LPCTSTR) errBuf); + WMSG2("Unable to open AppID key '%ls' (%ls)\n", + keyName, (LPCWSTR) errBuf); } RegCloseKey(hAppKey); @@ -507,9 +512,9 @@ MyRegistry::GetAssocAppName(const CString& appID, CString* pCmd) const void MyRegistry::ReduceToToken(CString* pStr) const { - char* argv[1]; + WCHAR* argv[1]; int argc = 1; - char* mangle = strdup(*pStr); + WCHAR* mangle = wcsdup(*pStr); VectorizeString(mangle, argv, &argc); @@ -532,7 +537,7 @@ MyRegistry::ReduceToToken(CString* pStr) const int MyRegistry::SetFileAssoc(int idx, bool wantIt) const { - const char* ext; + const WCHAR* ext; bool weOwnIt; int result = 0; @@ -540,18 +545,18 @@ MyRegistry::SetFileAssoc(int idx, bool wantIt) const ext = kFileTypeAssoc[idx].ext; weOwnIt = GetAssocState(ext); - WMSG3("SetFileAssoc: ext='%s' own=%d want=%d\n", ext, weOwnIt, wantIt); + WMSG3("SetFileAssoc: ext='%ls' own=%d want=%d\n", ext, weOwnIt, wantIt); if (weOwnIt && !wantIt) { /* reset it */ - WMSG1(" SetFileAssoc: clearing '%s'\n", ext); + WMSG1(" SetFileAssoc: clearing '%ls'\n", ext); result = DisownExtension(ext); } else if (!weOwnIt && wantIt) { /* take it */ - WMSG1(" SetFileAssoc: taking '%s'\n", ext); + WMSG1(" SetFileAssoc: taking '%ls'\n", ext); result = OwnExtension(ext, kFileTypeAssoc[idx].appID); } else { - WMSG1(" SetFileAssoc: do nothing with '%s'\n", ext); + WMSG1(" SetFileAssoc: do nothing with '%ls'\n", ext); /* do nothing */ } @@ -566,22 +571,22 @@ MyRegistry::SetFileAssoc(int idx, bool wantIt) const * encountered. */ bool -MyRegistry::GetAssocState(const char* ext) const +MyRegistry::GetAssocState(const WCHAR* ext) const { - unsigned char buf[260]; + WCHAR buf[260]; HKEY hExtKey = nil; - DWORD type, size; int res; bool result = false; res = RegOpenKeyEx(HKEY_CLASSES_ROOT, ext, 0, KEY_READ, &hExtKey); if (res == ERROR_SUCCESS) { - size = sizeof(buf); - res = RegQueryValueEx(hExtKey, "", nil, &type, buf, &size); + DWORD type; + DWORD size = sizeof(buf); // size in bytes + res = RegQueryValueEx(hExtKey, L"", nil, &type, (LPBYTE) buf, &size); if (res == ERROR_SUCCESS && type == REG_SZ) { /* compare it to known appID values */ - WMSG2(" Found '%s', testing '%s'\n", ext, buf); - if (IsOurAppID((char*)buf)) + WMSG2(" Found '%ls', testing '%ls'\n", ext, buf); + if (IsOurAppID((WCHAR*)buf)) result = true; } } @@ -598,17 +603,17 @@ MyRegistry::GetAssocState(const char* ext) const * Returns 0 on success, -1 on error. */ int -MyRegistry::DisownExtension(const char* ext) const +MyRegistry::DisownExtension(const WCHAR* ext) const { ASSERT(ext != nil); ASSERT(ext[0] == '.'); - if (ext == nil || strlen(ext) < 2) + if (ext == nil || wcslen(ext) < 2) return -1; if (RegDeleteKeyNT(HKEY_CLASSES_ROOT, ext) == ERROR_SUCCESS) { - WMSG1(" HKCR\\%s subtree deleted\n", ext); + WMSG1(" HKCR\\%ls subtree deleted\n", ext); } else { - WMSG1(" Failed deleting HKCR\\'%s'\n", ext); + WMSG1(" Failed deleting HKCR\\'%ls'\n", ext); return -1; } @@ -621,11 +626,11 @@ MyRegistry::DisownExtension(const char* ext) const * Returns 0 on success, -1 on error. */ int -MyRegistry::OwnExtension(const char* ext, const char* appID) const +MyRegistry::OwnExtension(const WCHAR* ext, const WCHAR* appID) const { ASSERT(ext != nil); ASSERT(ext[0] == '.'); - if (ext == nil || strlen(ext) < 2) + if (ext == nil || wcslen(ext) < 2) return -1; HKEY hExtKey = nil; @@ -635,11 +640,11 @@ MyRegistry::OwnExtension(const char* ext, const char* appID) const /* delete the old key (which might be a hierarchy) */ res = RegDeleteKeyNT(HKEY_CLASSES_ROOT, ext); if (res == ERROR_SUCCESS) { - WMSG1(" HKCR\\%s subtree deleted\n", ext); + WMSG1(" HKCR\\%ls subtree deleted\n", ext); } else if (res == ERROR_FILE_NOT_FOUND) { - WMSG1(" No HKCR\\%s subtree to delete\n", ext); + WMSG1(" No HKCR\\%ls subtree to delete\n", ext); } else { - WMSG1(" Failed deleting HKCR\\'%s'\n", ext); + WMSG1(" Failed deleting HKCR\\'%ls'\n", ext); goto bail; } @@ -648,13 +653,13 @@ MyRegistry::OwnExtension(const char* ext, const char* appID) const REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, &hExtKey, &dw) == ERROR_SUCCESS) { - res = RegSetValueEx(hExtKey, "", 0, REG_SZ, - (const unsigned char*) appID, strlen(appID)); + res = RegSetValueEx(hExtKey, L"", 0, REG_SZ, + (LPBYTE) appID, wcslen(appID) * sizeof(WCHAR)); if (res == ERROR_SUCCESS) { - WMSG2(" Set '%s' to '%s'\n", ext, appID); + WMSG2(" Set '%ls' to '%ls'\n", ext, appID); result = 0; } else { - WMSG3("Failed setting '%s' to '%s' (res=%d)\n", ext, appID, res); + WMSG3("Failed setting '%ls' to '%ls' (res=%d)\n", ext, appID, res); goto bail; } } diff --git a/app/Registry.h b/app/Registry.h index c83b90c..3ab9e1b 100644 --- a/app/Registry.h +++ b/app/Registry.h @@ -6,8 +6,8 @@ /* * A class representing the system registry. */ -#ifndef __REGISTRY__ -#define __REGISTRY__ +#ifndef APP_REGISTRY_H +#define APP_REGISTRY_H /* @@ -42,7 +42,7 @@ public: */ // Get the registry key to be used for our application. - const char* GetAppRegistryKey(void) const; + const WCHAR* GetAppRegistryKey(void) const; // Fix basic settings, e.g. HKCR AppID classes. void FixBasicSettings(void) const; @@ -56,22 +56,22 @@ public: private: typedef struct FileTypeAssoc { - const char* ext; // e.g. ".SHK" - const char* appID; // e.g. "CiderPress.NuFX" + const WCHAR* ext; // e.g. ".SHK" + const WCHAR* appID; // e.g. "CiderPress.NuFX" } FileTypeAssoc; static const FileTypeAssoc kFileTypeAssoc[]; - bool IsOurAppID(const char* id) const; - void ConfigureAppID(const char* appID, const char* descr, - const char* exeName, int iconIdx) const; - void ConfigureAppIDSubFields(HKEY hAppKey, const char* descr, - const char* exeName) const; + bool IsOurAppID(const WCHAR* id) const; + void ConfigureAppID(const WCHAR* appID, const WCHAR* descr, + const WCHAR* exeName, int iconIdx) const; + void ConfigureAppIDSubFields(HKEY hAppKey, const WCHAR* descr, + const WCHAR* exeName) const; int GetAssocAppName(const CString& appID, CString* pCmd) const; void ReduceToToken(CString* pStr) const; - bool GetAssocState(const char* ext) const; - int DisownExtension(const char* ext) const; - int OwnExtension(const char* ext, const char* appID) const; + bool GetAssocState(const WCHAR* ext) const; + int DisownExtension(const WCHAR* ext) const; + int OwnExtension(const WCHAR* ext, const WCHAR* appID) const; DWORD RegDeleteKeyNT(HKEY hStartKey, LPCTSTR pKeyName) const; /* key validation */ @@ -82,4 +82,4 @@ private: int VerifyKey(const char* user, const char* company, const char* key); }; -#endif /*__REGISTRY__*/ \ No newline at end of file +#endif /*APP_REGISTRY_H*/ diff --git a/app/RenameEntryDialog.h b/app/RenameEntryDialog.h index e414a2b..888658a 100644 --- a/app/RenameEntryDialog.h +++ b/app/RenameEntryDialog.h @@ -6,8 +6,8 @@ /* * Rename an archive entry. */ -#ifndef __RENAMEENTRYDIALOG__ -#define __RENAMEENTRYDIALOG__ +#ifndef APP_RENAMEENTRYDIALOG_H +#define APP_RENAMEENTRYDIALOG_H #include "GenericArchive.h" #include "resource.h" @@ -67,4 +67,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__RENAMEENTRYDIALOG__*/ +#endif /*APP_RENAMEENTRYDIALOG_H*/ diff --git a/app/RenameVolumeDialog.h b/app/RenameVolumeDialog.h index 9f13bf5..0b7ed1b 100644 --- a/app/RenameVolumeDialog.h +++ b/app/RenameVolumeDialog.h @@ -6,8 +6,8 @@ /* * Declarations for "rename volume" dialog. */ -#ifndef __RENAMEVOLUME__ -#define __RENAMEVOLUME__ +#ifndef APP_RENAMEVOLUME_H +#define APP_RENAMEVOLUME_H #include "DiskFSTree.h" #include "resource.h" @@ -47,4 +47,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__RENAMEVOLUME__*/ +#endif /*APP_RENAMEVOLUME_H*/ diff --git a/app/Squeeze.h b/app/Squeeze.h index 9e53435..b98e12e 100644 --- a/app/Squeeze.h +++ b/app/Squeeze.h @@ -6,10 +6,10 @@ /* * Implementation of SQueeze compression. */ -#ifndef __SQUEEZE__ -#define __SQUEEZE__ +#ifndef APP_SQUEEZE_H +#define APP_SQUEEZE_H NuError UnSqueeze(FILE* fp, unsigned long realEOF, ExpandBuffer* outExp, bool fullSqHeader, int blockSize); -#endif /*__SQUEEZE__*/ +#endif /*APP_SQUEEZE_H*/ diff --git a/app/StdAfx.h b/app/StdAfx.h index d258492..17a5ff5 100644 --- a/app/StdAfx.h +++ b/app/StdAfx.h @@ -19,6 +19,8 @@ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #define VC_EXTRALEAN +#include "targetver.h" + #include #include #include diff --git a/app/SubVolumeDialog.cpp b/app/SubVolumeDialog.cpp index 2b92eb4..a90f94a 100644 --- a/app/SubVolumeDialog.cpp +++ b/app/SubVolumeDialog.cpp @@ -37,7 +37,8 @@ SubVolumeDialog::OnInitDialog(void) DiskFS::SubVolume* pSubVol = fpDiskFS->GetNextSubVolume(nil); ASSERT(pSubVol != nil); // shouldn't be here otherwise while (pSubVol != nil) { - pListBox->AddString(pSubVol->GetDiskFS()->GetVolumeID()); + CString volumeIdW(pSubVol->GetDiskFS()->GetVolumeID()); + pListBox->AddString(volumeIdW); // makes a copy of the string pSubVol = fpDiskFS->GetNextSubVolume(pSubVol); } diff --git a/app/SubVolumeDialog.h b/app/SubVolumeDialog.h index c816ecb..5317ba7 100644 --- a/app/SubVolumeDialog.h +++ b/app/SubVolumeDialog.h @@ -6,8 +6,8 @@ /* * Sub-volume selection dialog. */ -#ifndef __SUBVOLUMEDIALOG__ -#define __SUBVOLUMEDIALOG__ +#ifndef APP_SUBVOLUMEDIALOG_H +#define APP_SUBVOLUMEDIALOG_H #include "resource.h" #include "../diskimg/DiskImg.h" @@ -44,4 +44,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__SUBVOLUMEDIALOG__*/ \ No newline at end of file +#endif /*APP_SUBVOLUMEDIALOG_H*/ diff --git a/app/Tools.cpp b/app/Tools.cpp index 6bfcd97..30d60ba 100644 --- a/app/Tools.cpp +++ b/app/Tools.cpp @@ -37,7 +37,7 @@ * On error, "*pErrMsg" will be non-empty. */ int -MainWindow::TryDiskImgOverride(DiskImg* pImg, const char* fileSource, +MainWindow::TryDiskImgOverride(DiskImg* pImg, const WCHAR* fileSource, DiskImg::FSFormat defaultFormat, int* pDisplayFormat, bool allowUnknown, CString* pErrMsg) { @@ -78,8 +78,8 @@ MainWindow::TryDiskImgOverride(DiskImg* pImg, const char* fileSource, dierr = pImg->OverrideFormat(pImg->GetPhysicalFormat(), imf.fFSFormat, imf.fSectorOrder); if (dierr != kDIErrNone) { - pErrMsg->Format("Unable to access disk image using selected" - " parameters. Error: %s.", + pErrMsg->Format(L"Unable to access disk image using selected" + L" parameters. Error: %hs.", DiskImgLib::DIStrError(dierr)); // fall through to "return IDOK" } @@ -138,7 +138,7 @@ MainWindow::OnToolsDiskEdit(void) openFilters = kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); /* for now, everything is read-only */ dlg.m_ofn.Flags |= OFN_HIDEREADONLY; @@ -173,8 +173,8 @@ MainWindow::OnToolsDiskEdit(void) goto bail; } - WMSG3("Disk editor what=%d name='%s' ro=%d\n", - diskEditOpen.fOpenWhat, loadName, readOnly); + WMSG3("Disk editor what=%d name='%ls' ro=%d\n", + diskEditOpen.fOpenWhat, (LPCWSTR) loadName, readOnly); #if 1 @@ -203,7 +203,7 @@ MainWindow::OnToolsDiskEdit(void) #endif if (dierr != kDIErrNone) { - errMsg.Format("Unable to open disk image: %s.", + errMsg.Format(L"Unable to open disk image: %hs.", DiskImgLib::DIStrError(dierr)); MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); goto bail; @@ -258,8 +258,8 @@ MainWindow::OnToolsDiskEdit(void) if (img.AnalyzeImage() != kDIErrNone) { - errMsg.Format("The file '%s' doesn't seem to hold a valid disk image.", - loadName); + errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", + (LPCWSTR) loadName); MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); goto bail; } @@ -319,7 +319,7 @@ MainWindow::OnToolsDiskEdit(void) dierr = pDiskFS->Initialize(&img, DiskFS::kInitFull); } if (dierr != kDIErrNone) { - errMsg.Format("Warning: error during disk scan: %s.", + errMsg.Format(L"Warning: error during disk scan: %hs.", DiskImgLib::DIStrError(dierr)); MessageBox(errMsg, failed, MB_OK | MB_ICONEXCLAMATION); /* keep going */ @@ -361,6 +361,7 @@ MainWindow::OnToolsDiskConv(void) DiskImg srcImg, dstImg; DiskConvertDialog convDlg(this); CString storageName; + CStringA saveNameA, storageNameA; /* flush current archive in case that's what we're planning to convert */ OnFileSave(); @@ -373,11 +374,11 @@ MainWindow::OnToolsDiskConv(void) openFilters = kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); /* for now, everything is read-only */ dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - dlg.m_ofn.lpstrTitle = "Select image to convert"; + dlg.m_ofn.lpstrTitle = L"Select image to convert"; dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) @@ -391,15 +392,15 @@ MainWindow::OnToolsDiskConv(void) /* open the image file and analyze it */ dierr = srcImg.OpenImage(loadName, PathProposal::kLocalFssep, true); if (dierr != kDIErrNone) { - errMsg.Format("Unable to open disk image: %s.", + errMsg.Format(L"Unable to open disk image: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (srcImg.AnalyzeImage() != kDIErrNone) { - errMsg.Format("The file '%s' doesn't seem to hold a valid disk image.", - loadName); + errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", + (LPCWSTR) loadName); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -437,9 +438,9 @@ MainWindow::OnToolsDiskConv(void) delete pDiskFS; } else { /* use filename as storageName (exception for DiskCopy42 later) */ - storageName = FilenameOnly(loadName, '\\'); + storageName = PathName::FilenameOnly(loadName, '\\'); } - WMSG1(" Using '%s' as storageName\n", storageName); + WMSG1(" Using '%ls' as storageName\n", (LPCWSTR) storageName); /* transfer the DOS volume num, if one was set */ dstImg.SetDOSVolumeNum(srcImg.GetDOSVolumeNum()); @@ -457,7 +458,7 @@ MainWindow::OnToolsDiskConv(void) dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); if (dierr != kDIErrNone) { - errMsg.Format("Internal error: couldn't switch to generic ProDOS: %s.", + errMsg.Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -506,7 +507,7 @@ MainWindow::OnToolsDiskConv(void) DiskImg::kNibbleDescrDOS33Std); } } - WMSG2(" NibbleDescr is 0x%08lx (%s)\n", (long) pNibbleDescr, + WMSG2(" NibbleDescr is 0x%08lx (%hs)\n", (long) pNibbleDescr, pNibbleDescr != nil ? pNibbleDescr->description : "---"); if (srcImg.GetFileFormat() == DiskImg::kFileFormatTrackStar && @@ -571,7 +572,7 @@ MainWindow::OnToolsDiskConv(void) fileFormat == DiskImg::kFileFormatDiskCopy42) { WMSG0(" Nuking storage name for non-ProDOS DiskCopy42 image"); - storageName = ""; // want to use "-not a mac disk" for non-ProDOS + storageName = L""; // want to use "-not a mac disk" for non-ProDOS } /* @@ -580,12 +581,12 @@ MainWindow::OnToolsDiskConv(void) { CFileDialog saveDlg(FALSE, convDlg.fExtension, NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "All Files (*.*)|*.*||", this); + L"All Files (*.*)|*.*||", this); CString saveFolder; - CString title = "New disk image (."; + CString title = L"New disk image (."; title += convDlg.fExtension; - title += ")"; + title += L")"; saveDlg.m_ofn.lpstrTitle = title; saveDlg.m_ofn.lpstrInitialDir = @@ -602,7 +603,7 @@ MainWindow::OnToolsDiskConv(void) saveName = saveDlg.GetPathName(); } - WMSG1("File will be saved to '%s'\n", saveName); + WMSG1("File will be saved to '%ls'\n", (LPCWSTR) saveName); /* DiskImgLib does not like it if file already exists */ errMsg = RemoveFile(saveName); @@ -649,6 +650,9 @@ MainWindow::OnToolsDiskConv(void) isPartial = true; } + saveNameA = saveName; + storageNameA = storageName; + if (srcImg.GetHasNibbles() && DiskImg::IsNibbleFormat(physicalFormat) && physicalFormat == srcImg.GetPhysicalFormat()) @@ -657,7 +661,8 @@ MainWindow::OnToolsDiskConv(void) * For nibble-to-nibble with the same track format, copy it as * a collection of tracks. */ - dierr = dstImg.CreateImage(saveName, storageName, + dierr = dstImg.CreateImage((LPCSTR) saveNameA, + (LPCSTR) storageNameA, outerFormat, fileFormat, physicalFormat, @@ -671,7 +676,8 @@ MainWindow::OnToolsDiskConv(void) * For general case, copy as a block image, converting in and out of * nibbles as needed. */ - dierr = dstImg.CreateImage(saveName, storageName, + dierr = dstImg.CreateImage((LPCSTR) saveNameA, + (LPCSTR) storageNameA, outerFormat, fileFormat, physicalFormat, @@ -687,7 +693,8 @@ MainWindow::OnToolsDiskConv(void) * block copying as the lowest common denominator. D13 screwed * everything up. :-) */ - dierr = dstImg.CreateImage(saveName, storageName, + dierr = dstImg.CreateImage((LPCSTR) saveNameA, + (LPCSTR) storageNameA, outerFormat, fileFormat, physicalFormat, @@ -707,7 +714,7 @@ MainWindow::OnToolsDiskConv(void) dierr = kDIErrInternal; } if (dierr != kDIErrNone) { - errMsg.Format("Couldn't create disk image: %s.", + errMsg.Format(L"Couldn't create disk image: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -718,21 +725,21 @@ MainWindow::OnToolsDiskConv(void) */ dierr = CopyDiskImage(&dstImg, &srcImg, false, isPartial, nil); if (dierr != kDIErrNone) { - errMsg.Format("Copy failed: %s.", DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"Copy failed: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } dierr = srcImg.CloseImage(); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: srcImg close failed (err=%d)\n", dierr); + errMsg.Format(L"ERROR: srcImg close failed (err=%d)\n", dierr); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } dierr = dstImg.CloseImage(); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: dstImg close failed (err=%d)\n", dierr); + errMsg.Format(L"ERROR: dstImg close failed (err=%d)\n", dierr); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -747,7 +754,7 @@ MainWindow::OnToolsDiskConv(void) DoneOpenDialog doneOpen(this); if (doneOpen.DoModal() == IDOK) { - WMSG1(" At user request, opening '%s'\n", saveName); + WMSG1(" At user request, opening '%ls'\n", (LPCWSTR) saveName); DoOpenArchive(saveName, convDlg.fExtension, kFilterIndexDiskImage, false); @@ -895,14 +902,14 @@ MainWindow::CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, for (int track = 0; track < numTracks; track++) { dierr = pSrcImg->ReadNibbleTrack(track, dataBuf, &trackLen); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: read on track %d failed (err=%d)\n", + errMsg.Format(L"ERROR: read on track %d failed (err=%d)\n", track, dierr); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } dierr = pDstImg->WriteNibbleTrack(track, dataBuf, trackLen); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: write on track %d failed (err=%d)\n", + errMsg.Format(L"ERROR: write on track %d failed (err=%d)\n", track, dierr); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -944,7 +951,7 @@ MainWindow::CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, } dierr = pDstImg->WriteTrackSector(track, sector, dataBuf); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: write of T=%d S=%d failed (err=%d)\n", + errMsg.Format(L"ERROR: write of T=%d S=%d failed (err=%d)\n", track, sector, dierr); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -957,8 +964,8 @@ MainWindow::CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, if (!bulk && numBadSectors != 0) { CString appName; appName.LoadString(IDS_MB_APP_NAME); - errMsg.Format("Skipped %ld unreadable sector%s.", numBadSectors, - numBadSectors == 1 ? "" : "s"); + errMsg.Format(L"Skipped %ld unreadable sector%ls.", numBadSectors, + numBadSectors == 1 ? L"" : L"s"); MessageBox(errMsg, appName, MB_OK | MB_ICONWARNING); } } else { @@ -1011,7 +1018,7 @@ MainWindow::CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, dierr = pDstImg->WriteBlocks(block, blocksThisTime, dataBuf); if (dierr != kDIErrNone) { if (dierr != kDIErrWriteProtected) { - errMsg.Format("ERROR: write of block %ld failed (%s)\n", + errMsg.Format(L"ERROR: write of block %ld failed: %hs\n", block, DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); } @@ -1039,8 +1046,8 @@ MainWindow::CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, if (!bulk && numBadBlocks != 0) { CString appName; appName.LoadString(IDS_MB_APP_NAME); - errMsg.Format("Skipped %ld unreadable block%s.", numBadBlocks, - numBadBlocks == 1 ? "" : "s"); + errMsg.Format(L"Skipped %ld unreadable block%ls.", numBadBlocks, + numBadBlocks == 1 ? L"" : L"s"); MessageBox(errMsg, appName, MB_OK | MB_ICONWARNING); } } @@ -1068,7 +1075,7 @@ public: IDD_BULKCONV, pParentWnd); } - void SetCurrentFile(const char* fileName) { + void SetCurrentFile(const WCHAR* fileName) { CWnd* pWnd = GetDlgItem(IDC_BULKCONV_PATHNAME); ASSERT(pWnd != nil); pWnd->SetWindowText(fileName); @@ -1109,14 +1116,14 @@ MainWindow::OnToolsBulkDiskConv(void) openFilters = kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - dlg.m_ofn.lpstrFile = new char[kFileNameBufSize]; + dlg.m_ofn.lpstrFile = new WCHAR[kFileNameBufSize]; dlg.m_ofn.lpstrFile[0] = dlg.m_ofn.lpstrFile[1] = '\0'; dlg.m_ofn.nMaxFile = kFileNameBufSize; dlg.m_ofn.Flags |= OFN_HIDEREADONLY; // open all images as read-only dlg.m_ofn.Flags |= OFN_ALLOWMULTISELECT; - dlg.m_ofn.lpstrTitle = "Select images to convert"; + dlg.m_ofn.lpstrTitle = L"Select images to convert"; dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) @@ -1177,9 +1184,9 @@ MainWindow::OnToolsBulkDiskConv(void) while (posn != nil) { CString pathName; pathName = dlg.GetNextPathName(posn); - WMSG1(" BulkConv: source path='%s'\n", pathName); + WMSG1(" BulkConv: source path='%ls'\n", (LPCWSTR) pathName); - pCancelDialog->SetCurrentFile(FilenameOnly(pathName, '\\')); + pCancelDialog->SetCurrentFile(PathName::FilenameOnly(pathName, '\\')); PeekAndPump(); if (pCancelDialog->fAbortOperation) break; @@ -1228,7 +1235,7 @@ bail: * On failure, the reason for failure is stuffed into "*pErrMsg". */ void -MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, +MainWindow::BulkConvertImage(const WCHAR* pathName, const WCHAR* targetDir, const DiskConvertDialog& convDlg, CString* pErrMsg) { DIError dierr; @@ -1237,8 +1244,9 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, CString storageName; PathName srcPath(pathName); CString fileName, ext; + CStringA saveNameA, storageNameA; - *pErrMsg = ""; + *pErrMsg = L""; dstImg.SetNuFXCompressionType( fPreferences.GetPrefLong(kPrCompressionType)); @@ -1246,13 +1254,13 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, /* open the image file and analyze it */ dierr = srcImg.OpenImage(pathName, PathProposal::kLocalFssep, true); if (dierr != kDIErrNone) { - pErrMsg->Format("Unable to open disk image: %s.", + pErrMsg->Format(L"Unable to open disk image: %hs.", DiskImgLib::DIStrError(dierr)); goto bail; } if (srcImg.AnalyzeImage() != kDIErrNone) { - pErrMsg->Format("The file doesn't seem to hold a valid disk image."); + pErrMsg->Format(L"The file doesn't seem to hold a valid disk image."); goto bail; } @@ -1274,8 +1282,8 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, } #else if (srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - *pErrMsg = "Could not determine the disk image sector ordering. You " - "may need to change the file extension."; + *pErrMsg = L"Could not determine the disk image sector ordering. You " + L"may need to change the file extension."; goto bail; } #endif @@ -1296,7 +1304,7 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); if (dierr != kDIErrNone) { - pErrMsg->Format("Internal error: couldn't switch to generic ProDOS: %s.", + pErrMsg->Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -1312,7 +1320,7 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, if (DetermineImageSettings(convDlg.fConvertIdx, (convDlg.fAddGzip != 0), &outerFormat, &fileFormat, &physicalFormat, §orOrder) != 0) { - *pErrMsg = "Odd: couldn't configure image settings"; + *pErrMsg = L"Odd: couldn't configure image settings"; goto bail; } @@ -1333,7 +1341,7 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, DiskImg::kNibbleDescrDOS33Std); } } - WMSG2(" NibbleDescr is 0x%08lx (%s)\n", (long) pNibbleDescr, + WMSG2(" NibbleDescr is 0x%08lx (%hs)\n", (long) pNibbleDescr, pNibbleDescr != nil ? pNibbleDescr->description : "---"); /* @@ -1344,7 +1352,7 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, saveName += '\\'; fileName = srcPath.GetFileName(); ext = srcPath.GetExtension(); // extension, including '.' - if (ext.CompareNoCase(".gz") == 0) { + if (ext.CompareNoCase(L".gz") == 0) { /* got a .gz, see if there's anything else in front of it */ CString tmpName, ext2; tmpName = srcPath.GetPathName(); @@ -1363,10 +1371,10 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, saveName += fileName.Left(fileName.GetLength() - ext.GetLength()); } } - storageName = FilenameOnly(saveName, '\\'); // grab this for SHK name + storageName = PathName::FilenameOnly(saveName, '\\'); // grab this for SHK name saveName += '.'; saveName += convDlg.fExtension; - WMSG2(" Bulk converting '%s' to '%s'\n", pathName, saveName); + WMSG2(" Bulk converting '%ls' to '%ls'\n", pathName, (LPCWSTR) saveName); /* * If this is a ProDOS volume, use the disk volume name as the default @@ -1384,9 +1392,9 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, } else { /* just use storageName as set earlier, unless target is DiskCopy42 */ if (fileFormat == DiskImg::kFileFormatDiskCopy42) - storageName = ""; // want to use "not a mac disk" for non-ProDOS + storageName = L""; // want to use "not a mac disk" for non-ProDOS } - WMSG1(" Using '%s' as storageName\n", storageName); + WMSG1(" Using '%ls' as storageName\n", (LPCWSTR) storageName); /* * If the source is a UNIDOS volume and the target format is DiskCopy 4.2, @@ -1438,13 +1446,16 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, isPartial = true; } + saveNameA = saveName; + storageNameA = storageName; if (srcImg.GetHasNibbles() && DiskImg::IsNibbleFormat(physicalFormat) && physicalFormat == srcImg.GetPhysicalFormat()) { /* for nibble-to-nibble with the same track format, copy it as collection of tracks */ - dierr = dstImg.CreateImage(saveName, storageName, + dierr = dstImg.CreateImage((LPCSTR) saveNameA, + (LPCSTR) storageNameA, outerFormat, fileFormat, physicalFormat, @@ -1456,7 +1467,8 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, } else if (srcImg.GetHasBlocks()) { /* for general case, create as a block image */ ASSERT(srcImg.GetHasBlocks()); - dierr = dstImg.CreateImage(saveName, storageName, + dierr = dstImg.CreateImage((LPCSTR) saveNameA, + (LPCSTR) storageNameA, outerFormat, fileFormat, physicalFormat, @@ -1472,7 +1484,8 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, * block copying as the lowest common denominator. D13 screwed * everything up. :-) */ - dierr = dstImg.CreateImage(saveName, storageName, + dierr = dstImg.CreateImage((LPCSTR) saveNameA, + (LPCSTR) storageNameA, outerFormat, fileFormat, physicalFormat, @@ -1483,14 +1496,14 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, false /* only need for dest=nibble? */); } else { /* e.g. unrecognizeable nibble to blocks */ - *pErrMsg = "Could not convert to requested format."; + *pErrMsg = L"Could not convert to requested format."; goto bail; } if (dierr != kDIErrNone) { if (dierr == kDIErrInvalidCreateReq) - *pErrMsg = "Could not convert to requested format."; + *pErrMsg = L"Could not convert to requested format."; else - pErrMsg->Format("Couldn't construct disk image: %s.", + pErrMsg->Format(L"Couldn't construct disk image: %hs.", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -1504,13 +1517,13 @@ MainWindow::BulkConvertImage(const char* pathName, const char* targetDir, dierr = dstImg.CloseImage(); if (dierr != kDIErrNone) { - pErrMsg->Format("ERROR: dstImg close failed (err=%d)\n", dierr); + pErrMsg->Format(L"ERROR: dstImg close failed (err=%d)\n", dierr); goto bail; } dierr = srcImg.CloseImage(); if (dierr != kDIErrNone) { - pErrMsg->Format("ERROR: srcImg close failed (err=%d)\n", dierr); + pErrMsg->Format(L"ERROR: srcImg close failed (err=%d)\n", dierr); goto bail; } @@ -1538,18 +1551,18 @@ MainWindow::OnToolsSSTMerge(void) const int kBadCountThreshold = 3072; DiskImg srcImg0, srcImg1; CString appName, saveName, saveFolder, errMsg; - unsigned char* trackBuf = nil; + BYTE* trackBuf = nil; long badCount; // no need to flush -- can't really open raw SST images - CFileDialog saveDlg(FALSE, _T("nib"), NULL, + CFileDialog saveDlg(FALSE, L"nib", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "All Files (*.*)|*.*||", this); + L"All Files (*.*)|*.*||", this); appName.LoadString(IDS_MB_APP_NAME); - trackBuf = new unsigned char[kSSTNumTracks * kSSTTrackLen]; + trackBuf = new BYTE[kSSTNumTracks * kSSTTrackLen]; if (trackBuf == nil) goto bail; @@ -1588,7 +1601,7 @@ MainWindow::OnToolsSSTMerge(void) /* * Pick the output file and write the buffer to it. */ - saveDlg.m_ofn.lpstrTitle = _T("Save .NIB disk image as..."); + saveDlg.m_ofn.lpstrTitle = L"Save .NIB disk image as..."; saveDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (saveDlg.DoModal() != IDOK) { WMSG0(" User bailed out of image save dialog\n"); @@ -1599,7 +1612,7 @@ MainWindow::OnToolsSSTMerge(void) fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); saveName = saveDlg.GetPathName(); - WMSG1("File will be saved to '%s'\n", saveName); + WMSG1("File will be saved to '%ls'\n", (LPCWSTR) saveName); /* remove the file if it exists */ errMsg = RemoveFile(saveName); @@ -1609,16 +1622,16 @@ MainWindow::OnToolsSSTMerge(void) } FILE* fp; - fp = fopen(saveName, "wb"); + fp = _wfopen(saveName, L"wb"); if (fp == nil) { - errMsg.Format("Unable to create '%s': %s.", - saveName, strerror(errno)); + errMsg.Format(L"Unable to create '%ls': %hs.", + (LPCWSTR) saveName, strerror(errno)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (fwrite(trackBuf, kSSTNumTracks * kSSTTrackLen, 1, fp) != 1) { - errMsg.Format("Failed while writing to new image file: %s.", + errMsg.Format(L"Failed while writing to new image file: %hs.", strerror(errno)); ShowFailureMsg(this, errMsg, IDS_FAILED); fclose(fp); @@ -1637,9 +1650,9 @@ MainWindow::OnToolsSSTMerge(void) DoneOpenDialog doneOpen(this); if (doneOpen.DoModal() == IDOK) { - WMSG1(" At user request, opening '%s'\n", saveName); + WMSG1(" At user request, opening '%ls'\n", (LPCWSTR) saveName); - DoOpenArchive(saveName, "nib", kFilterIndexDiskImage, false); + DoOpenArchive(saveName, L"nib", kFilterIndexDiskImage, false); } } @@ -1669,13 +1682,13 @@ MainWindow::SSTOpenImage(int seqNum, DiskImg* pDiskImg) openFilters = kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog dlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); dlg.m_ofn.Flags |= OFN_HIDEREADONLY; if (seqNum == 0) - dlg.m_ofn.lpstrTitle = "Select first SST image"; + dlg.m_ofn.lpstrTitle = L"Select first SST image"; else - dlg.m_ofn.lpstrTitle = "Select second SST image"; + dlg.m_ofn.lpstrTitle = L"Select second SST image"; dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) @@ -1689,15 +1702,15 @@ MainWindow::SSTOpenImage(int seqNum, DiskImg* pDiskImg) /* open the image file and analyze it */ dierr = pDiskImg->OpenImage(loadName, PathProposal::kLocalFssep, true); if (dierr != kDIErrNone) { - errMsg.Format("Unable to open disk image: %s.", + errMsg.Format(L"Unable to open disk image: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (pDiskImg->AnalyzeImage() != kDIErrNone) { - errMsg.Format("The file '%s' doesn't seem to hold a valid disk image.", - loadName); + errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", + (LPCWSTR) loadName); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -1723,15 +1736,15 @@ MainWindow::SSTOpenImage(int seqNum, DiskImg* pDiskImg) if (pDiskImg->GetFSFormat() != DiskImg::kFormatUnknown && !DiskImg::IsGenericFormat(pDiskImg->GetFSFormat())) { - errMsg = "This disk image appears to have a valid filesystem. SST" - " images are just raw track dumps."; + errMsg = L"This disk image appears to have a valid filesystem. SST" + L" images are just raw track dumps."; ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (pDiskImg->GetNumTracks() != kSSTNumTracks || pDiskImg->GetNumSectPerTrack() != kSSTNumSectPerTrack) { - errMsg = "ERROR: only 5.25\" floppy disk images can be SST inputs."; + errMsg = L"ERROR: only 5.25\" floppy disk images can be SST inputs."; ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -1740,7 +1753,7 @@ MainWindow::SSTOpenImage(int seqNum, DiskImg* pDiskImg) dierr = pDiskImg->OverrideFormat(pDiskImg->GetPhysicalFormat(), DiskImg::kFormatGenericDOSOrd, pDiskImg->GetSectorOrder()); if (dierr != kDIErrNone) { - errMsg = "ERROR: internal failure: format override failed."; + errMsg = L"ERROR: internal failure: format override failed."; ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -1760,11 +1773,11 @@ bail: * Returns 0 on success, -1 on failure. */ int -MainWindow::SSTLoadData(int seqNum, DiskImg* pDiskImg, unsigned char* trackBuf, +MainWindow::SSTLoadData(int seqNum, DiskImg* pDiskImg, BYTE* trackBuf, long* pBadCount) { DIError dierr; - unsigned char sctBuf[256]; + BYTE sctBuf[256]; int track, sector; long bufOffset; @@ -1987,11 +2000,11 @@ MainWindow::VolumeCopier(bool openFile) openFilters = kOpenDiskImage; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog fileDlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog fileDlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); //dlg.m_ofn.Flags |= OFN_HIDEREADONLY; fileDlg.m_ofn.Flags &= ~(OFN_READONLY); - fileDlg.m_ofn.lpstrTitle = "Select disk image file"; + fileDlg.m_ofn.lpstrTitle = L"Select disk image file"; fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (fileDlg.DoModal() != IDOK) @@ -2011,21 +2024,21 @@ MainWindow::VolumeCopier(bool openFile) dierr = srcImg.OpenImage(deviceName, '\0', readOnly); if (dierr == kDIErrAccessDenied) { if (openFile) { - errMsg.Format("Unable to open '%s': %s (try opening the file" - " with 'Read Only' checked).", deviceName, - DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"Unable to open '%ls': %hs (try opening the file" + L" with 'Read Only' checked).", + (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); } else if (!IsWin9x() && !openFile) { - errMsg.Format("Unable to open '%s': %s (make sure you have" - " administrator privileges).", deviceName, - DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"Unable to open '%ls': %hs (make sure you have" + L" administrator privileges).", + (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); } else { - errMsg.Format("Unable to open '%s': %s.", deviceName, - DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"Unable to open '%ls': %hs.", + (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); } ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } else if (dierr != kDIErrNone) { - errMsg.Format("Unable to open '%s': %s.", deviceName, + errMsg.Format(L"Unable to open '%ls': %hs.", (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -2033,7 +2046,7 @@ MainWindow::VolumeCopier(bool openFile) /* analyze it to get #of blocks and determine the FS */ if (srcImg.AnalyzeImage() != kDIErrNone) { - errMsg.Format("There isn't a valid disk image here?!?"); + errMsg.Format(L"There isn't a valid disk image here?!?"); MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); goto bail; } @@ -2174,20 +2187,20 @@ MainWindow::OnToolsDiskImageCreator(void) CString formats; if (createDlg.fDiskFormatIdx == CreateImageDialog::kFmtDOS32) { - formats = "13-sector disk (*.d13)|*.d13|"; + formats = L"13-sector disk (*.d13)|*.d13|"; } else { - formats = "ProDOS-ordered image (*.po)|*.po|"; + formats = L"ProDOS-ordered image (*.po)|*.po|"; if (createDlg.fNumBlocks == 280) { - formats += "DOS-ordered image (*.do)|*.do|"; + formats += L"DOS-ordered image (*.do)|*.do|"; filterIndex = 2; } } - formats += "|"; + formats += L"|"; - CFileDialog saveDlg(FALSE, _T("po"), NULL, + CFileDialog saveDlg(FALSE, L"po", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, formats, this); - saveDlg.m_ofn.lpstrTitle = "New Disk Image"; + saveDlg.m_ofn.lpstrTitle = L"New Disk Image"; saveDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); saveDlg.m_ofn.nFilterIndex = filterIndex; @@ -2201,8 +2214,8 @@ MainWindow::OnToolsDiskImageCreator(void) fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); filename = saveDlg.GetPathName(); - WMSG2(" Will xfer to file '%s' (filterIndex=%d)\n", - filename, saveDlg.m_ofn.nFilterIndex); + WMSG2(" Will xfer to file '%ls' (filterIndex=%d)\n", + (LPCWSTR) filename, saveDlg.m_ofn.nFilterIndex); if (createDlg.fDiskFormatIdx == CreateImageDialog::kFmtDOS32) { options.base.sectorOrder = DiskImg::kSectorOrderDOS; @@ -2240,7 +2253,7 @@ MainWindow::OnToolsDiskImageCreator(void) delete pNewArchive; // close it, either way if (!errStr.IsEmpty()) { ShowFailureMsg(this, errStr, IDS_FAILED); - (void) unlink(filename); + (void) _wunlink(filename); } else { WMSG0("Disk image created successfully\n"); #if 0 @@ -2250,13 +2263,13 @@ MainWindow::OnToolsDiskImageCreator(void) DoneOpenDialog doneOpen(this); if (doneOpen.DoModal() == IDOK) { - WMSG1(" At user request, opening '%s'\n", filename); + WMSG1(" At user request, opening '%ls'\n", filename); DoOpenArchive(filename, "dsk", kFilterIndexDiskImage, false); } #else if (createDlg.fDiskFormatIdx != CreateImageDialog::kFmtBlank) - DoOpenArchive(filename, "dsk", kFilterIndexDiskImage, false); + DoOpenArchive(filename, L"dsk", kFilterIndexDiskImage, false); #endif } } @@ -2281,11 +2294,11 @@ MainWindow::OnToolsEOLScanner(void) CString openFilters; openFilters = kOpenAll; openFilters += kOpenEnd; - CFileDialog fileDlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog fileDlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); fileDlg.m_ofn.Flags |= OFN_HIDEREADONLY; //fileDlg.m_ofn.Flags &= ~(OFN_READONLY); - fileDlg.m_ofn.lpstrTitle = "Select file to scan"; + fileDlg.m_ofn.lpstrTitle = L"Select file to scan"; fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (fileDlg.DoModal() != IDOK) @@ -2296,12 +2309,12 @@ MainWindow::OnToolsEOLScanner(void) saveFolder = saveFolder.Left(fileDlg.m_ofn.nFileOffset); fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - WMSG1("Scanning '%s'\n", (const char*) fileName); + WMSG1("Scanning '%ls'\n", (LPCWSTR) fileName); - FILE* fp; - fp = fopen(fileName, "rb"); + FILE* fp = _wfopen(fileName, L"rb"); if (fp == nil) { - errMsg.Format("Unable to open '%s': %s.", fileName, strerror(errno)); + errMsg.Format(L"Unable to open '%ls': %hs.", + (LPCWSTR) fileName, strerror(errno)); ShowFailureMsg(this, errMsg, IDS_FAILED); return; } @@ -2375,14 +2388,14 @@ MainWindow::OnToolsTwoImgProps(void) /* * Select the file to open. */ - openFilters = "2MG Disk Images (.2mg .2img)|*.2mg;*.2img|"; + openFilters = L"2MG Disk Images (.2mg .2img)|*.2mg;*.2img|"; openFilters += kOpenAll; openFilters += kOpenEnd; - CFileDialog fileDlg(TRUE, "2mg", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog fileDlg(TRUE, L"2mg", NULL, OFN_FILEMUSTEXIST, openFilters, this); fileDlg.m_ofn.Flags |= OFN_HIDEREADONLY; //fileDlg.m_ofn.Flags &= ~(OFN_READONLY); - fileDlg.m_ofn.lpstrTitle = "Select file to edit"; + fileDlg.m_ofn.lpstrTitle = L"Select file to edit"; fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); if (fileDlg.DoModal() != IDOK) @@ -2411,7 +2424,7 @@ MainWindow::OnToolsTwoImgProps(void) * Returns "true" if the file was modified, "false" if not. */ bool -MainWindow::EditTwoImgProps(const char* fileName) +MainWindow::EditTwoImgProps(const WCHAR* fileName) { TwoImgPropsDialog dialog; TwoImgHeader header; @@ -2421,17 +2434,18 @@ MainWindow::EditTwoImgProps(const char* fileName) long totalLength; bool readOnly = false; - WMSG1("EditTwoImgProps '%s'\n", fileName); - fp = fopen(fileName, "r+b"); + WMSG1("EditTwoImgProps '%ls'\n", fileName); + fp = _wfopen(fileName, L"r+b"); if (fp == nil) { int firstError = errno; - fp = fopen(fileName, "rb"); + fp = _wfopen(fileName, L"rb"); if (fp == nil) { - errMsg.Format("Unable to open '%s': %s.", + errMsg.Format(L"Unable to open '%ls': %hs.", fileName, strerror(firstError)); goto bail; - } else + } else { readOnly = true; + } } fseek(fp, 0, SEEK_END); @@ -2439,9 +2453,9 @@ MainWindow::EditTwoImgProps(const char* fileName) rewind(fp); if (header.ReadHeader(fp, totalLength) != 0) { - errMsg.Format("Unable to process 2MG header in '%s'" - " (are you sure this is in 2MG format?).", - fileName); + errMsg.Format(L"Unable to process 2MG header in '%ls'" + L" (are you sure this is in 2MG format?).", + (LPCWSTR) fileName); goto bail; } @@ -2453,7 +2467,7 @@ MainWindow::EditTwoImgProps(const char* fileName) rewind(fp); if (header.WriteHeader(fp) != 0) { - errMsg = "Unable to write 2MG header"; + errMsg = L"Unable to write 2MG header"; goto bail; } @@ -2464,19 +2478,19 @@ MainWindow::EditTwoImgProps(const char* fileName) */ result = fseek(fp, header.fDataOffset + header.fDataLen, SEEK_SET); if (result < 0) { - errMsg = "Unable to seek to end of 2MG file"; + errMsg = L"Unable to seek to end of 2MG file"; goto bail; } dirty = true; if (::chsize(fileno(fp), ftell(fp)) != 0) { - errMsg = "Unable to truncate 2MG file before writing footer"; + errMsg = L"Unable to truncate 2MG file before writing footer"; goto bail; } if (header.fCmtLen || header.fCreatorLen) { if (header.WriteFooter(fp) != 0) { - errMsg = "Unable to write 2MG footer"; + errMsg = L"Unable to write 2MG footer"; goto bail; } } diff --git a/app/TwoImgPropsDialog.cpp b/app/TwoImgPropsDialog.cpp index 631091a..a3342c1 100644 --- a/app/TwoImgPropsDialog.cpp +++ b/app/TwoImgPropsDialog.cpp @@ -34,24 +34,24 @@ TwoImgPropsDialog::OnInitDialog(void) * Set up the static fields. */ pWnd = GetDlgItem(IDC_TWOIMG_CREATOR); - tmpStr.Format("'%s'", fpHeader->GetCreatorStr()); + tmpStr.Format(L"'%hs'", fpHeader->GetCreatorStr()); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_TWOIMG_VERSION); - tmpStr.Format("%d", fpHeader->fVersion); + tmpStr.Format(L"%d", fpHeader->fVersion); pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_TWOIMG_FORMAT); switch (fpHeader->fImageFormat) { - case TwoImgHeader::kImageFormatDOS: tmpStr = "DOS order sectors"; break; - case TwoImgHeader::kImageFormatProDOS: tmpStr = "ProDOS order sectors"; break; - case TwoImgHeader::kImageFormatNibble: tmpStr = "Raw nibbles"; break; - default: tmpStr = "Unknown"; break; + case TwoImgHeader::kImageFormatDOS: tmpStr = L"DOS order sectors"; break; + case TwoImgHeader::kImageFormatProDOS: tmpStr = L"ProDOS order sectors"; break; + case TwoImgHeader::kImageFormatNibble: tmpStr = L"Raw nibbles"; break; + default: tmpStr = L"Unknown"; break; } pWnd->SetWindowText(tmpStr); pWnd = GetDlgItem(IDC_TWOIMG_BLOCKS); - tmpStr.Format("%d", fpHeader->fNumBlocks); + tmpStr.Format(L"%d", fpHeader->fNumBlocks); pWnd->SetWindowText(tmpStr); /* @@ -121,10 +121,12 @@ TwoImgPropsDialog::DoDataExchange(CDataExchange* pDX) } - if (!comment.IsEmpty()) - fpHeader->SetComment(comment); - else + if (!comment.IsEmpty()) { + CStringA commentA(comment); + fpHeader->SetComment(commentA); + } else { fpHeader->SetComment(nil); + } } else { CWnd* pWnd; diff --git a/app/TwoImgPropsDialog.h b/app/TwoImgPropsDialog.h index af10693..30a41f7 100644 --- a/app/TwoImgPropsDialog.h +++ b/app/TwoImgPropsDialog.h @@ -6,8 +6,8 @@ /* * Class definition for TwoImg properties edit dialog. */ -#ifndef __TWOIMG_PROPS_DIALOG__ -#define __TWOIMG_PROPS_DIALOG__ +#ifndef APP_TWOIMGPROPSDIALOG_H +#define APP_TWOIMGPROPSDIALOG_H #include "resource.h" #include "../diskimg/TwoImg.h" @@ -46,4 +46,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__TWOIMG_PROPS_DIALOG__*/ \ No newline at end of file +#endif /*APP_TWOIMGPROPSDIALOG_H*/ diff --git a/app/UseSelectionDialog.h b/app/UseSelectionDialog.h index d286e31..e96419e 100644 --- a/app/UseSelectionDialog.h +++ b/app/UseSelectionDialog.h @@ -6,10 +6,9 @@ /* * Acknowledge and clarify a request to delete files. */ -#ifndef __USE_SELECTION_DIALOG__ -#define __USE_SELECTION_DIALOG__ +#ifndef APP_USESELECTIONDIALOG_H +#define APP_USESELECTIONDIALOG_H -//#include "../util/UtilLib.h" #include "resource.h" /* @@ -59,4 +58,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__USE_SELECTION_DIALOG__*/ \ No newline at end of file +#endif /*APP_USESELECTIONDIALOG_H*/ diff --git a/app/ViewFilesDialog.cpp b/app/ViewFilesDialog.cpp index 11f24d3..fa5bb74 100644 --- a/app/ViewFilesDialog.cpp +++ b/app/ViewFilesDialog.cpp @@ -314,9 +314,11 @@ DumpBitmapInfo(HBITMAP hBitmap) * * The RichEdit dialog will hold its own copy of the data, so "pHolder" can * be safely destroyed after this returns. + * + * "fileName" is for display only. */ void -ViewFilesDialog::DisplayText(const char* fileName) +ViewFilesDialog::DisplayText(const WCHAR* fileName) { CWaitCursor wait; // streaming of big files can take a little while bool errFlg; @@ -418,14 +420,14 @@ ViewFilesDialog::DisplayText(const char* fileName) hBitmap = fpOutput->GetDIB()->ConvertToDDB(dcScreen.m_hDC); if (hBitmap == nil) { WMSG0("ConvertToDDB failed!\n"); - pEdit->SetWindowText("Internal error."); + pEdit->SetWindowText(L"Internal error."); errFlg = true; } else { //DumpBitmapInfo(hBitmap); //DumpBitmapInfo(pDib->GetHandle()); WMSG0("Inserting bitmap\n"); - pEdit->SetWindowText(""); + pEdit->SetWindowText(L""); CImageDataObject::InsertBitmap(fpRichEditOle, hBitmap); /* RichEditCtrl has it now */ @@ -451,7 +453,7 @@ ViewFilesDialog::DisplayText(const char* fileName) if (fpOutput->GetOutputKind() == ReformatOutput::kOutputRTF) streamFormat = SF_RTF; if (fpOutput->GetTextLen() == 0) { - textBuf = _T("(file is empty)"); + textBuf = "(file is empty)"; textLen = strlen(textBuf); emptyFlg = true; EnableFormatSelection(FALSE); @@ -846,7 +848,7 @@ ViewFilesDialog::ReformatPrep(GenericEntry* pEntry) pEntry->GetFileType(), pEntry->GetAuxType(), MainWindow::ReformatterSourceFormat(pEntry->GetSourceFS()), - pEntry->GetFileNameExtension()); + pEntry->GetFileNameExtensionA()); /* figure out which reformatters apply to this file */ WMSG0("Testing reformatters\n"); @@ -895,7 +897,7 @@ ViewFilesDialog::Reformat(const GenericEntry* pEntry, return 0; } else { /* shouldn't get here; handle it if we do */ - static const char* kFailMsg = _T("Internal error\r\n"); + static const char* kFailMsg = "Internal error\r\n"; fpOutput = new ReformatOutput; fpOutput->SetTextBuf((char*) kFailMsg, strlen(kFailMsg), false); fpOutput->SetOutputKind(ReformatOutput::kOutputErrorMsg); @@ -970,8 +972,8 @@ ViewFilesDialog::ConfigureFormatSel(ReformatHolder::ReformatPart part) /* match! */ CString str; //WMSG2("MATCH at %d (0x%02x)\n", idIdx, testApplies); - str.Format("%s", - ReformatHolder::GetReformatName((ReformatHolder::ReformatID) idIdx)); + str.Format(L"%ls", ReformatHolder::GetReformatName( + (ReformatHolder::ReformatID) idIdx)); comboIdx = pCombo->AddString(str); pCombo->SetItemData(comboIdx, idIdx); @@ -1160,7 +1162,7 @@ ViewFilesDialog::OnFviewFont(void) //fontDlg.GetCurrentFont(&logFont); fTypeFace = fontDlg.GetFaceName(); fPointSize = fontDlg.GetSize() / 10; - WMSG2("Now using %d-point '%s'\n", fPointSize, (const char*)fTypeFace); + WMSG2("Now using %d-point '%ls'\n", fPointSize, (LPCWSTR) fTypeFace); NewFontSelected(false); } @@ -1347,10 +1349,10 @@ ViewFilesDialog::OnFindDialogMessage(WPARAM wParam, LPARAM lParam) * Find the next ocurrence of the specified string. */ void -ViewFilesDialog::FindNext(const char* str, bool down, bool matchCase, +ViewFilesDialog::FindNext(const WCHAR* str, bool down, bool matchCase, bool wholeWord) { - WMSG4("FindText '%s' d=%d c=%d w=%d\n", str, down, matchCase, wholeWord); + WMSG4("FindText '%ls' d=%d c=%d w=%d\n", str, down, matchCase, wholeWord); FINDTEXTEX findTextEx = { 0 }; CHARRANGE selChrg; @@ -1372,7 +1374,7 @@ ViewFilesDialog::FindNext(const char* str, bool down, bool matchCase, findTextEx.chrg.cpMin = start; findTextEx.chrg.cpMax = -1; - findTextEx.lpstrText = const_cast(str); + findTextEx.lpstrText = str; /* MSVC++6 claims FindText doesn't exist, even though it's in the header */ //result = fEditCtrl.FindText(flags, &findTextEx); @@ -1383,7 +1385,7 @@ ViewFilesDialog::FindNext(const char* str, bool down, bool matchCase, /* didn't find it, wrap around to start */ findTextEx.chrg.cpMin = 0; findTextEx.chrg.cpMax = -1; - findTextEx.lpstrText = const_cast(str); + findTextEx.lpstrText = str; result = fEditCtrl.SendMessage(EM_FINDTEXTEX, (WPARAM) flags, (LPARAM) &findTextEx); } diff --git a/app/ViewFilesDialog.h b/app/ViewFilesDialog.h index 38f5ac7..e80c280 100644 --- a/app/ViewFilesDialog.h +++ b/app/ViewFilesDialog.h @@ -6,8 +6,8 @@ /* * Class for the "view files" dialog box. */ -#ifndef __VIEWFILESDIALOG__ -#define __VIEWFILESDIALOG__ +#ifndef APP_VIEWFILESDIALOG_H +#define APP_VIEWFILESDIALOG_H #include "GenericArchive.h" #include "resource.h" @@ -50,7 +50,7 @@ public: void SetSelectionSet(SelectionSet* pSelSet) { fpSelSet = pSelSet; } CString GetTextTypeFace(void) const { return fTypeFace; } - void SetTextTypeFace(const char* name) { fTypeFace = name; } + void SetTextTypeFace(const WCHAR* name) { fTypeFace = name; } int GetTextPointSize(void) const { return fPointSize; } void SetTextPointSize(int size) { fPointSize = size; } //bool GetNoWrapText(void) const { return fNoWrapText; } @@ -89,7 +89,7 @@ private: //void MoveControl(int id, int deltaX, int deltaY); //void StretchControl(int id, int deltaX, int deltaY); void NewFontSelected(bool resetBold); - void DisplayText(const char* fileName); + void DisplayText(const WCHAR* fileName); int ReformatPrep(GenericEntry* pEntry); int Reformat(const GenericEntry* pEntry, ReformatHolder::ReformatPart part, ReformatHolder::ReformatID id); @@ -100,7 +100,7 @@ private: ReformatHolder::ReformatPart part); int FindByVal(CComboBox* pCombo, DWORD val); void EnableFormatSelection(BOOL enable); - void FindNext(const char* str, bool down, bool matchCase, + void FindNext(const WCHAR* str, bool down, bool matchCase, bool wholeWord); // pointer to main window, so we can ask for text to view @@ -153,4 +153,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__VIEWFILESDIALOG__*/ \ No newline at end of file +#endif /*APP_VIEWFILESDIALOG_H*/ diff --git a/app/VolumeCopyDialog.cpp b/app/VolumeCopyDialog.cpp index 8f862d0..00bf690 100644 --- a/app/VolumeCopyDialog.cpp +++ b/app/VolumeCopyDialog.cpp @@ -37,7 +37,7 @@ public: IDD_VOLUMECOPYPROG, IDC_VOLUMECOPYPROG_PROGRESS, pParentWnd); } - void SetCurrentFiles(const char* fromName, const char* toName) { + void SetCurrentFiles(const WCHAR* fromName, const WCHAR* toName) { CWnd* pWnd = GetDlgItem(IDC_VOLUMECOPYPROG_FROM); ASSERT(pWnd != nil); pWnd->SetWindowText(fromName); @@ -102,15 +102,15 @@ VolumeCopyDialog::OnInitDialog(void) //CRect rect; pListView->GetClientRect(&rect); - width1 = pListView->GetStringWidth("XXVolume NameXXmmmmm"); - width2 = pListView->GetStringWidth("XXFormatXXmmmmmmmmmm"); - width3 = pListView->GetStringWidth("XXSizeXXmmm"); + width1 = pListView->GetStringWidth(L"XXVolume NameXXmmmmm"); + width2 = pListView->GetStringWidth(L"XXFormatXXmmmmmmmmmm"); + width3 = pListView->GetStringWidth(L"XXSizeXXmmm"); //width4 = pListView->GetStringWidth("XXBlock CountXX"); - pListView->InsertColumn(0, "Volume Name", LVCFMT_LEFT, width1); - pListView->InsertColumn(1, "Format", LVCFMT_LEFT, width2); - pListView->InsertColumn(2, "Size", LVCFMT_LEFT, width3); - pListView->InsertColumn(3, "Block Count", LVCFMT_LEFT, + pListView->InsertColumn(0, L"Volume Name", LVCFMT_LEFT, width1); + pListView->InsertColumn(1, L"Format", LVCFMT_LEFT, width2); + pListView->InsertColumn(2, L"Size", LVCFMT_LEFT, width3); + pListView->InsertColumn(3, L"Block Count", LVCFMT_LEFT, rect.Width() - (width1+width2+width3) - ::GetSystemMetrics(SM_CXVSCROLL) ); @@ -274,7 +274,7 @@ VolumeCopyDialog::ScanDiskInfo(bool scanTop) if (dierr != kDIErrNone) { CString appName, msg; appName.LoadString(IDS_MB_APP_NAME); - msg.Format("Warning: error during disk scan: %s.", + msg.Format(L"Warning: error during disk scan: %hs.", DiskImgLib::DIStrError(dierr)); fpWaitDlg->MessageBox(msg, appName, MB_OK | MB_ICONEXCLAMATION); /* keep going */ @@ -362,13 +362,13 @@ VolumeCopyDialog::AddToList(CListCtrl* pListView, DiskImg* pDiskImg, volName = pDiskFS->GetVolumeName(); format = DiskImg::ToString(pDiskImg->GetFSFormat()); - blocksStr.Format("%ld", pDiskImg->GetNumBlocks()); + blocksStr.Format(L"%ld", pDiskImg->GetNumBlocks()); if (numBlocks > 1024*1024*2) - sizeStr.Format("%.2fGB", (double) numBlocks / (1024.0*1024.0*2.0)); + sizeStr.Format(L"%.2fGB", (double) numBlocks / (1024.0*1024.0*2.0)); else if (numBlocks > 1024*2) - sizeStr.Format("%.2fMB", (double) numBlocks / (1024.0*2.0)); + sizeStr.Format(L"%.2fMB", (double) numBlocks / (1024.0*2.0)); else - sizeStr.Format("%.2fKB", (double) numBlocks / 2.0); + sizeStr.Format(L"%.2fKB", (double) numBlocks / 2.0); /* add entry; first entry is the whole volume */ pListView->InsertItem(*pIndex, volName, @@ -455,26 +455,25 @@ VolumeCopyDialog::OnCopyToFile(void) dierr = pSrcImg->OverrideFormat(pSrcImg->GetPhysicalFormat(), DiskImg::kFormatGenericProDOSOrd, pSrcImg->GetSectorOrder()); if (dierr != kDIErrNone) { - errMsg.Format("Internal error: couldn't switch to generic ProDOS: %s.", + errMsg.Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } - WMSG2("Logical volume '%s' has %d 512-byte blocks\n", - srcName, pSrcImg->GetNumBlocks()); + WMSG2("Logical volume '%ls' has %d 512-byte blocks\n", + (LPCWSTR) srcName, pSrcImg->GetNumBlocks()); /* * Select file to write blocks to. */ { - CFileDialog saveDlg(FALSE, "po", NULL, + CFileDialog saveDlg(FALSE, L"po", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - "All Files (*.*)|*.*||", this); + L"All Files (*.*)|*.*||", this); CString saveFolder; - static char* title = "New disk image (.po)"; - saveDlg.m_ofn.lpstrTitle = title; + saveDlg.m_ofn.lpstrTitle = L"New disk image (.po)"; saveDlg.m_ofn.lpstrInitialDir = pPreferences->GetPrefString(kPrOpenArchiveFolder); @@ -489,7 +488,7 @@ VolumeCopyDialog::OnCopyToFile(void) saveName = saveDlg.GetPathName(); } - WMSG1("File will be saved to '%s'\n", saveName); + WMSG1("File will be saved to '%ls'\n", (LPCWSTR) saveName); /* DiskImgLib does not like it if file already exists */ errMsg = pMain->RemoveFile(saveName); @@ -511,7 +510,8 @@ VolumeCopyDialog::OnCopyToFile(void) pMain->PeekAndPump(); // redraw CWaitCursor waitc; - dierr = dstImg.CreateImage(saveName, nil, + CStringA saveNameA(saveName); + dierr = dstImg.CreateImage(saveNameA, nil, DiskImg::kOuterFormatNone, DiskImg::kFileFormatUnadorned, DiskImg::kPhysicalFormatSectors, @@ -525,7 +525,7 @@ VolumeCopyDialog::OnCopyToFile(void) //pMain->PeekAndPump(); // redraw } if (dierr != kDIErrNone) { - errMsg.Format("Couldn't create disk image: %s.", + errMsg.Format(L"Couldn't create disk image: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -554,9 +554,9 @@ VolumeCopyDialog::OnCopyToFile(void) ShowFailureMsg(pProgressDialog, errMsg, IDS_CANCELLED); // remove the partially-written file dstImg.CloseImage(); - unlink(saveName); + (void) _wunlink(saveName); } else { - errMsg.Format("Copy failed: %s.", DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"Copy failed: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); } goto bail; @@ -564,7 +564,7 @@ VolumeCopyDialog::OnCopyToFile(void) dierr = dstImg.CloseImage(); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: dstImg close failed (err=%d)\n", dierr); + errMsg.Format(L"ERROR: dstImg close failed (err=%d)\n", dierr); ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); goto bail; } @@ -576,12 +576,12 @@ VolumeCopyDialog::OnCopyToFile(void) elapsed = 1.0; else elapsed = (float) (endWhen - startWhen); - msg.Format("Copied %ld blocks in %ld seconds (%.2fKB/sec)", + msg.Format(L"Copied %ld blocks in %ld seconds (%.2fKB/sec)", pSrcImg->GetNumBlocks(), endWhen - startWhen, (pSrcImg->GetNumBlocks() / 2.0) / elapsed); - WMSG1("%s\n", (const char*) msg); + WMSG1("%ls\n", (LPCWSTR) msg); #ifdef _DEBUG - pProgressDialog->MessageBox(msg, "DEBUG: elapsed time", MB_OK); + pProgressDialog->MessageBox(msg, L"DEBUG: elapsed time", MB_OK); #endif pMain->SuccessBeep(); @@ -651,11 +651,11 @@ VolumeCopyDialog::OnCopyFromFile(void) openFilters = MainWindow::kOpenDiskImage; openFilters += MainWindow::kOpenAll; openFilters += MainWindow::kOpenEnd; - CFileDialog dlg(TRUE, "dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); + CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); /* source file gets opened read-only */ dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - dlg.m_ofn.lpstrTitle = "Select image to copy from"; + dlg.m_ofn.lpstrTitle = L"Select image to copy from"; dlg.m_ofn.lpstrInitialDir = pPreferences->GetPrefString(kPrOpenArchiveFolder); if (dlg.DoModal() != IDOK) @@ -669,15 +669,15 @@ VolumeCopyDialog::OnCopyFromFile(void) /* open the image file and analyze it */ dierr = srcImg.OpenImage(loadName, PathProposal::kLocalFssep, true); if (dierr != kDIErrNone) { - errMsg.Format("Unable to open disk image: %s.", + errMsg.Format(L"Unable to open disk image: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (srcImg.AnalyzeImage() != kDIErrNone) { - errMsg.Format("The file '%s' doesn't seem to hold a valid disk image.", - loadName); + errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", + (LPCWSTR) loadName); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -699,8 +699,8 @@ VolumeCopyDialog::OnCopyFromFile(void) * EOF on the output. */ if (!srcImg.GetHasBlocks()) { - errMsg = "The disk image must be block-oriented. Nibble images" - " cannot be copied."; + errMsg = L"The disk image must be block-oriented. Nibble images" + L" cannot be copied."; ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -709,38 +709,39 @@ VolumeCopyDialog::OnCopyFromFile(void) dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); if (dierr != kDIErrNone) { - errMsg.Format("Internal error: couldn't switch source to generic ProDOS: %s.", + errMsg.Format(L"Internal error: couldn't switch source to generic ProDOS: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } - WMSG2("Source image '%s' has %d 512-byte blocks\n", - loadName, srcImg.GetNumBlocks()); + WMSG2("Source image '%ls' has %d 512-byte blocks\n", + (LPCWSTR) loadName, srcImg.GetNumBlocks()); WMSG1("Target volume has %d 512-byte blocks\n", pDstImg->GetNumBlocks()); if (srcImg.GetNumBlocks() > pDstImg->GetNumBlocks()) { - errMsg.Format("Error: the disk image file has %ld blocks, but the" - " target volume holds %ld blocks. The target must" - " have more space than the input file.", + errMsg.Format(L"Error: the disk image file has %ld blocks, but the" + L" target volume holds %ld blocks. The target must" + L" have more space than the input file.", srcImg.GetNumBlocks(), pDstImg->GetNumBlocks()); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (pDstImg->GetNumBlocks() >= DiskImgLib::kVolumeMaxBlocks) { - errMsg.Format("Error: for safety reasons, copying disk images to" - " larger volumes is not supported when the target" - " is 8GB or larger."); + // TODO: re-evaluate this limitation + errMsg.Format(L"Error: for safety reasons, copying disk images to" + L" larger volumes is not supported when the target" + L" is 8GB or larger."); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (srcImg.GetNumBlocks() != pDstImg->GetNumBlocks()) { errMsg.LoadString(IDS_WARNING); - errMsg.Format("The disk image file has %ld blocks, but the target" - " volume holds %ld blocks. The leftover space may be" - " wasted, and non-ProDOS volumes may not be identified" - " correctly. Do you wish to continue?", + errMsg.Format(L"The disk image file has %ld blocks, but the target" + L" volume holds %ld blocks. The leftover space may be" + L" wasted, and non-ProDOS volumes may not be identified" + L" correctly. Do you wish to continue?", srcImg.GetNumBlocks(), pDstImg->GetNumBlocks()); result = MessageBox(errMsg, warning, MB_OKCANCEL | MB_ICONQUESTION); if (result != IDOK) { @@ -750,11 +751,11 @@ VolumeCopyDialog::OnCopyFromFile(void) isPartial = true; } - errMsg.LoadString(IDS_WARNING); - errMsg.Format("You are about to overwrite volume %s with the" - " contents of '%s'. This will destroy all data on" - " %s. Are you sure you wish to continue?", - targetName, loadName, targetName); + errMsg.LoadString(IDS_WARNING); // TODO: what does this accomplish? + errMsg.Format(L"You are about to overwrite volume %ls with the" + L" contents of '%ls'. This will destroy all data on" + L" %ls. Are you sure you wish to continue?", + (LPCWSTR) targetName, (LPCWSTR) loadName, (LPCWSTR) targetName); result = MessageBox(errMsg, warning, MB_OKCANCEL | MB_ICONEXCLAMATION); if (result != IDOK) { WMSG0("User chickened out of disk copy\n"); @@ -765,7 +766,7 @@ VolumeCopyDialog::OnCopyFromFile(void) dierr = pDstImg->OverrideFormat(pDstImg->GetPhysicalFormat(), DiskImg::kFormatGenericProDOSOrd, pDstImg->GetSectorOrder()); if (dierr != kDIErrNone) { - errMsg.Format("Internal error: couldn't switch target to generic ProDOS: %s.", + errMsg.Format(L"Internal error: couldn't switch target to generic ProDOS: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; @@ -811,7 +812,7 @@ VolumeCopyDialog::OnCopyFromFile(void) errMsg.LoadString(IDS_OPERATION_CANCELLED); ShowFailureMsg(pProgressDialog, errMsg, IDS_CANCELLED); } else { - errMsg.Format("Copy failed: %s.", DiskImgLib::DIStrError(dierr)); + errMsg.Format(L"Copy failed: %hs.", DiskImgLib::DIStrError(dierr)); ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); } goto bail; @@ -819,7 +820,7 @@ VolumeCopyDialog::OnCopyFromFile(void) dierr = srcImg.CloseImage(); if (dierr != kDIErrNone) { - errMsg.Format("ERROR: srcImg close failed (err=%d)\n", dierr); + errMsg.Format(L"ERROR: srcImg close failed (err=%d)\n", dierr); ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); goto bail; } @@ -830,12 +831,12 @@ VolumeCopyDialog::OnCopyFromFile(void) elapsed = 1.0; else elapsed = (float) (endWhen - startWhen); - errMsg.Format("Copied %ld blocks in %ld seconds (%.2fKB/sec)", + errMsg.Format(L"Copied %ld blocks in %ld seconds (%.2fKB/sec)", srcImg.GetNumBlocks(), endWhen - startWhen, (srcImg.GetNumBlocks() / 2.0) / elapsed); - WMSG1("%s\n", (const char*) errMsg); + WMSG1("%ls\n", (LPCWSTR) errMsg); #ifdef _DEBUG - pProgressDialog->MessageBox(errMsg, "DEBUG: elapsed time", MB_OK); + pProgressDialog->MessageBox(errMsg, L"DEBUG: elapsed time", MB_OK); #endif pMain->SuccessBeep(); diff --git a/app/VolumeCopyDialog.h b/app/VolumeCopyDialog.h index dc935bd..c136a8c 100644 --- a/app/VolumeCopyDialog.h +++ b/app/VolumeCopyDialog.h @@ -7,8 +7,8 @@ * Dialog that allows copying volumes or sub-volumes to and from files on * disk. Handy for backing up and restoring floppy disks and CFFA partitions. */ -#ifndef __VOLUMECOPYDIALOG__ -#define __VOLUMECOPYDIALOG__ +#ifndef APP_VOLUMECOPYDIALOG_H +#define APP_VOLUMECOPYDIALOG_H #include #include "../diskimg/DiskImg.h" @@ -77,4 +77,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__VOLUMECOPYDIALOG*/ +#endif /*APP_VOLUMECOPYDIALOG_H*/ diff --git a/app/app.vcxproj b/app/app.vcxproj index 79c550d..1be1e1a 100644 --- a/app/app.vcxproj +++ b/app/app.vcxproj @@ -43,14 +43,14 @@ <_ProjectFileVersion>12.0.30501.0 - .\Release\ - .\Release\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ false - .\Debug\ - .\Debug\ - false + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true @@ -62,23 +62,25 @@ true stdafx.h - .\Release/app.pch - .\Release/ - .\Release/ - .\Release/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb true Level3 true + true + false - ..\prebuilt\nufxlib2.lib;..\prebuilt\zdll.lib;%(AdditionalDependencies) - Release/CiderPress.exe + $(OutDir)$(TargetName)$(TargetExt) true - .\Release/CiderPress.pdb - true - .\Release/CiderPress.map + $(OutDir)$(TargetName).pdb + + Windows MachineX86 + NDEBUG;%(PreprocessorDefinitions) @@ -89,12 +91,10 @@ - Copying DLLs and help - copy Help\CiderPress.hlp Release\ -copy Help\CiderPress.cnt Release\ -copy ..\prebuilt\nufxlib2.dll . -copy ..\prebuilt\zlib1.dll . - + + + copy Help\CiderPress.hlp Release\ +copy Help\CiderPress.cnt Release\ NDEBUG;%(PreprocessorDefinitions) @@ -109,23 +109,25 @@ copy ..\prebuilt\zlib1.dll . MultiThreadedDebugDLL stdafx.h - .\Debug/app.pch - .\Debug/ - .\Debug/ - .\Debug/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb true Level3 true EditAndContinue + true + false - ..\prebuilt\nufxlib2D.lib;..\prebuilt\zdll.lib;%(AdditionalDependencies) - Debug/CiderPress.exe + $(OutDir)$(TargetName)$(TargetExt) true true - .\Debug/CiderPress.pdb + $(OutDir)$(TargetName).pdb Windows MachineX86 + _DEBUG;%(PreprocessorDefinitions) @@ -136,11 +138,10 @@ copy ..\prebuilt\zlib1.dll . - Copying debug DLLs and help - copy Help\CiderPress.hlp Debug\ -copy Help\CiderPress.cnt Debug\ -copy ..\prebuilt\nufxlib2D.dll . -copy ..\prebuilt\zlib1.dll . + Copying help + copy Help\CiderPress.hlp Debug\ +copy Help\CiderPress.cnt Debug\ + @@ -148,292 +149,10 @@ copy ..\prebuilt\zlib1.dll . 0x0409 - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - Disabled - EnableFastChecks - true - MaxSpeed - true - - - - @@ -450,7 +169,6 @@ copy ..\prebuilt\zlib1.dll . - @@ -482,7 +200,6 @@ copy ..\prebuilt\zlib1.dll . - @@ -490,10 +207,10 @@ copy ..\prebuilt\zlib1.dll . + - @@ -509,18 +226,20 @@ copy ..\prebuilt\zlib1.dll . + + - - - {0cfe6fad-0126-4e99-8625-c807d1d2aaf4} false + + {c48ae53b-3dcb-43b1-9207-b7c5b6bb78af} + {18bcf397-397e-460c-a1dc-3e26798966e4} false @@ -529,6 +248,71 @@ copy ..\prebuilt\zlib1.dll . {04bfae2a-7ab3-4b63-b4ab-42ff1d6ad3c5} false + + {b66109f4-217b-43c0-86aa-eb55657e5ac0} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + diff --git a/app/app.vcxproj.filters b/app/app.vcxproj.filters index eee2f4a..70b890c 100644 --- a/app/app.vcxproj.filters +++ b/app/app.vcxproj.filters @@ -14,6 +14,237 @@ ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + Source Files @@ -187,241 +418,4 @@ Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - - - - - - \ No newline at end of file diff --git a/app/targetver.h b/app/targetver.h new file mode 100644 index 0000000..f3d0a90 --- /dev/null +++ b/app/targetver.h @@ -0,0 +1,18 @@ +/* + * CiderPress + * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. + * See the file LICENSE for distribution terms. + */ + +// Visual C++ 2013 yells if you don't define these values. By default +// they're set to the highest version, but we want to support WinXP, so +// we use that definition. +// +// The symbolic names for the constants are defined in SDKDDKVer.h, but +// that also sets the default values, so the auto-generated "targetver.h" +// recommends doing it this way. Web docs confirm that it should be done +// numerically. +#include +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 +#include diff --git a/diskimg/ASPI.cpp b/diskimg/ASPI.cpp index 6fa26e5..35b1a10 100644 --- a/diskimg/ASPI.cpp +++ b/diskimg/ASPI.cpp @@ -14,7 +14,7 @@ * - The Win98 generic ASPI only finds CD-ROM drives on the IDE bus. */ #include "StdAfx.h" -#ifdef _WIN32 +#if defined(_WIN32) && defined (WANT_ASPI) #include "DiskImgPriv.h" #include "SCSIDefs.h" diff --git a/diskimg/ASPI.h b/diskimg/ASPI.h index 42375e0..0ad6b54 100644 --- a/diskimg/ASPI.h +++ b/diskimg/ASPI.h @@ -8,11 +8,14 @@ * * This may be included directly by an application. It must not be necessary * to include the lower-level headers, e.g. wnaspi32.h. + * + * TODO: this was only necessary for older versions of Windows, e.g. Win98, + * as a way to access SCSI drives. It's no longer needed. */ #ifndef __ASPI__ #define __ASPI__ -#ifndef _WIN32 +#if !defined(_WIN32) || !defined(WANT_ASPI) /* * Placeholder definition to keep Linux build happy. */ diff --git a/diskimg/DOS33.cpp b/diskimg/DOS33.cpp index 38ec8a0..1469983 100644 --- a/diskimg/DOS33.cpp +++ b/diskimg/DOS33.cpp @@ -1634,7 +1634,7 @@ void DiskFSDOS33::DoNormalizePath(const char* name, char fssep, char* outBuf) { char* outp = outBuf; - char* cp; + const char* cp; /* throw out leading pathname, if any */ if (fssep != '\0') { diff --git a/diskimg/DiskImg.h b/diskimg/DiskImg.h index ff1450d..aafb422 100644 --- a/diskimg/DiskImg.h +++ b/diskimg/DiskImg.h @@ -203,7 +203,7 @@ public: static bool GetHasSPTI(void); static bool GetHasASPI(void); - // return a pointer to our global ASPI instance + // return a pointer to our global ASPI instance, or NULL static ASPI* GetASPI(void) { return fpASPI; } // shortcut for fpASPI->GetVersion() static unsigned long GetASPIVersion(void); @@ -1177,7 +1177,7 @@ public: kStorageDirectory = 13, }; typedef struct CreateParms { - const char* pathName; // full pathname + const char* pathName; // full pathname for file on disk image char fssep; int storageType; // determines normal, subdir, or forked long fileType; diff --git a/diskimg/DiskImgDetail.h b/diskimg/DiskImgDetail.h index 99d5a1c..f8d89e2 100644 --- a/diskimg/DiskImgDetail.h +++ b/diskimg/DiskImgDetail.h @@ -12,12 +12,11 @@ * examine the actual directory structure of a file, it can do so through * these declarations. */ -#ifndef __DISKIMGDETAIL__ -#define __DISKIMGDETAIL__ +#ifndef DISKIMG_DISKIMGDETAIL_H +#define DISKIMG_DISKIMGDETAIL_H -#include "../prebuilt/NufxLib.h" -#define ZLIB_DLL -#include "../prebuilt/zlib.h" +#include "../nufxlib/NufxLib.h" +#include "../zlib/zlib.h" #include "DiskImg.h" @@ -3215,6 +3214,6 @@ private: A2FileDescr* fpOpenFile; }; -}; // namespace DiskImgLib +} // namespace DiskImgLib -#endif /*__DISKIMGDETAIL__*/ +#endif /*DISKIMG_DISKIMGDETAIL_H*/ diff --git a/diskimg/GenericFD.cpp b/diskimg/GenericFD.cpp index 2ba4982..9590f29 100644 --- a/diskimg/GenericFD.cpp +++ b/diskimg/GenericFD.cpp @@ -627,7 +627,15 @@ GFDWinVolume::Open(const char* deviceName, bool readOnly) fPathName = new char[strlen(deviceName) +1]; strcpy(fPathName, deviceName); - dierr = fVolAccess.Open(deviceName, readOnly); + // Create a UNICODE representation of the device name. We may want + // to make the argument UNICODE instead, but most of diskimg is 8-bit + // character oriented. + size_t srcLen = strlen(deviceName) + 1; + WCHAR* wdeviceName = new WCHAR[srcLen]; + size_t convertedChars; + mbstowcs_s(&convertedChars, wdeviceName, srcLen, deviceName, _TRUNCATE); + dierr = fVolAccess.Open(wdeviceName, readOnly); + delete[] wdeviceName; if (dierr != kDIErrNone) goto bail; diff --git a/diskimg/Global.cpp b/diskimg/Global.cpp index 664e2f6..42cb581 100644 --- a/diskimg/Global.cpp +++ b/diskimg/Global.cpp @@ -17,6 +17,7 @@ /* global constant */ const char* DiskImgLib::kASPIDev = "ASPI:"; + /* * Perform one-time DLL initialization. */ @@ -36,13 +37,14 @@ Global::AppInit(void) #ifdef _WIN32 HMODULE hModule; - char fileNameBuf[256]; - hModule = ::GetModuleHandle("DiskImg4.dll"); + WCHAR fileNameBuf[256]; + hModule = ::GetModuleHandle(L"DiskImg4.dll"); if (hModule != nil && - ::GetModuleFileName(hModule, fileNameBuf, sizeof(fileNameBuf)) != 0) + ::GetModuleFileName(hModule, fileNameBuf, + sizeof(fileNameBuf) / sizeof(WCHAR)) != 0) { // GetModuleHandle does not increase ref count, so no need to release - WMSG1("DiskImg DLL loaded from '%s'\n", fileNameBuf); + WMSG1("DiskImg DLL loaded from '%ls'\n", fileNameBuf); } else { WMSG0("Unable to get DiskImg DLL filename\n"); } @@ -68,7 +70,7 @@ Global::AppInit(void) */ DiskImg::CalcNibbleInvTables(); -#ifdef HAVE_WINDOWS_CDROM +#if defined(HAVE_WINDOWS_CDROM) && defined(WANT_ASPI) if (kAlwaysTryASPI || IsWin9x()) { fpASPI = new ASPI; if (fpASPI->Init() != kDIErrNone) { @@ -107,7 +109,11 @@ Global::AppCleanup(void) /*static*/ bool Global::GetHasASPI(void) { return fpASPI != nil; } /*static*/ unsigned long Global::GetASPIVersion(void) { assert(fpASPI != nil); +#ifdef WANT_ASPI return fpASPI->GetVersion(); +#else + return 123456789; +#endif } #else /*static*/ bool Global::GetHasSPTI(void) { return false; } diff --git a/diskimg/HFS.cpp b/diskimg/HFS.cpp index 8204b61..da91f7d 100644 --- a/diskimg/HFS.cpp +++ b/diskimg/HFS.cpp @@ -234,7 +234,7 @@ DiskFSHFS::LoadVolHeader(void) tmWhen = *ptm; // make a copy -- static buffers in time functions tmWhen.tm_isdst = isDst; - fLocalTimeOffset = when - mktime(&tmWhen); + fLocalTimeOffset = (long) (when - mktime(&tmWhen)); } else fLocalTimeOffset = 0; diff --git a/diskimg/Pascal.cpp b/diskimg/Pascal.cpp index 5cfc677..8f1233f 100644 --- a/diskimg/Pascal.cpp +++ b/diskimg/Pascal.cpp @@ -870,7 +870,7 @@ void DiskFSPascal::DoNormalizePath(const char* name, char fssep, char* outBuf) { char* outp = outBuf; - char* cp; + const char* cp; /* throw out leading pathname, if any */ if (fssep != '\0') { diff --git a/diskimg/StdAfx.h b/diskimg/StdAfx.h index 17e15e9..f133226 100644 --- a/diskimg/StdAfx.h +++ b/diskimg/StdAfx.h @@ -39,10 +39,16 @@ #define HAVE_WINDOWS_CDROM // enable CD-ROM access under Windows #define HAVE_CHSIZE + // Insert your headers here # define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_WARNINGS // disable VC++ libc complaints here + +#include "../app/targetver.h" + #include +#include #include #include #include diff --git a/diskimg/Win32BlockIO.cpp b/diskimg/Win32BlockIO.cpp index 17865ce..f45dc98 100644 --- a/diskimg/Win32BlockIO.cpp +++ b/diskimg/Win32BlockIO.cpp @@ -44,7 +44,7 @@ * Open a logical volume. */ DIError -Win32VolumeAccess::Open(const char* deviceName, bool readOnly) +Win32VolumeAccess::Open(const WCHAR* deviceName, bool readOnly) { DIError dierr = kDIErrNone; @@ -55,6 +55,7 @@ Win32VolumeAccess::Open(const char* deviceName, bool readOnly) return kDIErrAlreadyOpen; } +#ifdef WANT_ASPI if (strncmp(deviceName, kASPIDev, strlen(kASPIDev)) == 0) { fpBlockAccess = new ASPIBlockAccess; if (fpBlockAccess == nil) { @@ -64,7 +65,9 @@ Win32VolumeAccess::Open(const char* deviceName, bool readOnly) dierr = fpBlockAccess->Open(deviceName, readOnly); if (dierr != kDIErrNone) goto bail; - } else if (deviceName[0] >= 'A' && deviceName[0] <= 'Z') { + } else +#endif + if (deviceName[0] >= 'A' && deviceName[0] <= 'Z') { fpBlockAccess = new LogicalBlockAccess; if (fpBlockAccess == nil) { dierr = kDIErrMalloc; @@ -1163,7 +1166,7 @@ Win32VolumeAccess::BlockAccess::WriteBlocksWin2K(HANDLE handle, * Open a logical device. The device name should be of the form "A:\". */ DIError -Win32VolumeAccess::LogicalBlockAccess::Open(const char* deviceName, bool readOnly) +Win32VolumeAccess::LogicalBlockAccess::Open(const WCHAR* deviceName, bool readOnly) { DIError dierr = kDIErrNone; const bool kPreferASPI = true; @@ -1209,7 +1212,7 @@ Win32VolumeAccess::LogicalBlockAccess::Open(const char* deviceName, bool readOnl if (fIsCDROM) return kDIErrCDROMNotSupported; - fHandle = CreateFile("\\\\.\\vwin32", 0, 0, NULL, + fHandle = CreateFile(_T("\\\\.\\vwin32"), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (fHandle == INVALID_HANDLE_VALUE) { DWORD lastError = GetLastError(); @@ -1233,7 +1236,7 @@ Win32VolumeAccess::LogicalBlockAccess::Open(const char* deviceName, bool readOnl } #endif } else { - char device[7] = "\\\\.\\_:"; + WCHAR device[7] = _T("\\\\.\\_:"); device[4] = deviceName[0]; WMSG1("Opening '%s'\n", device); @@ -1423,7 +1426,7 @@ Win32VolumeAccess::LogicalBlockAccess::ReadBlocksCDROM(HANDLE handle, * Open a physical device. The device name should be of the form "80:\". */ DIError -Win32VolumeAccess::PhysicalBlockAccess::Open(const char* deviceName, bool readOnly) +Win32VolumeAccess::PhysicalBlockAccess::Open(const WCHAR* deviceName, bool readOnly) { DIError dierr = kDIErrNone; @@ -1480,7 +1483,7 @@ Win32VolumeAccess::PhysicalBlockAccess::Open(const char* deviceName, bool readOn access = GENERIC_READ | GENERIC_WRITE; if (fIsWin9x) { - fHandle = CreateFile("\\\\.\\vwin32", 0, 0, NULL, + fHandle = CreateFile(_T("\\\\.\\vwin32"), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (fHandle == INVALID_HANDLE_VALUE) { DWORD lastError = GetLastError(); @@ -1495,7 +1498,7 @@ Win32VolumeAccess::PhysicalBlockAccess::Open(const char* deviceName, bool readOn if (dierr != kDIErrNone) goto bail; } else { - char device[19] = "\\\\.\\PhysicalDrive_"; + WCHAR device[19] = _T("\\\\.\\PhysicalDrive_"); assert(fInt13Unit >= 0x80 && fInt13Unit <= 0x89); device[17] = fInt13Unit - 0x80 + '0'; WMSG2("Opening '%s' (access=0x%02x)\n", device, access); @@ -1554,7 +1557,8 @@ Win32VolumeAccess::PhysicalBlockAccess::DetectFloppyGeometry(void) int status; /* verify that we can directly index the table with the enum */ - for (int chk = 0; chk < NELEM(floppyGeometry); chk++) { + int chk; + for (chk = 0; chk < NELEM(floppyGeometry); chk++) { assert(floppyGeometry[chk].kind == chk); } assert(chk == kFloppyMax); @@ -1661,6 +1665,7 @@ Win32VolumeAccess::PhysicalBlockAccess::Close(void) * ASPIBlockAccess * =========================================================================== */ +#ifdef WANT_ASPI /* * Unpack device name and verify that the device is a CD-ROM drive or @@ -1971,6 +1976,7 @@ Win32VolumeAccess::ASPIBlockAccess::Close(void) fpASPI = nil; return kDIErrNone; } +#endif /* diff --git a/diskimg/Win32BlockIO.h b/diskimg/Win32BlockIO.h index 078f238..25cc82e 100644 --- a/diskimg/Win32BlockIO.h +++ b/diskimg/Win32BlockIO.h @@ -47,8 +47,8 @@ * Win2K/XP logical drive: CreateFile("\\.\X") * Win2K/XP SCSI drive or CD-ROM drive: SPTI */ -#ifndef __WIN32BLOCKIO__ -#define __WIN32BLOCKIO__ +#ifndef DISKIMG_WIN32BLOCKIO_H +#define DISKIMG_WIN32BLOCKIO_H namespace DiskImgLib { @@ -130,7 +130,7 @@ public: // "deviceName" has the form "X:\" (logical), "81:\" (physical), or // "ASPI:x:y:z\" (ASPI) - DIError Open(const char* deviceName, bool readOnly); + DIError Open(const WCHAR* deviceName, bool readOnly); // close the device void Close(void); // is the device open and working? @@ -165,7 +165,7 @@ private: } DiskGeometry; // generic interfaces - virtual DIError Open(const char* deviceName, bool readOnly) = 0; + virtual DIError Open(const WCHAR* deviceName, bool readOnly) = 0; virtual DIError DetectCapacity(long* pNumBlocks) = 0; virtual DIError ReadBlocks(long startBlock, short blockCount, void* buf) = 0; @@ -248,7 +248,7 @@ private: delete[] fLastSectorCache; } - virtual DIError Open(const char* deviceName, bool readOnly); + virtual DIError Open(const WCHAR* deviceName, bool readOnly); virtual DIError DetectCapacity(long* pNumBlocks) { /* use SCSI length value if at all possible */ DIError dierr; @@ -308,7 +308,7 @@ private: PhysicalBlockAccess(void) : fHandle(NULL), fInt13Unit(-1) {} virtual ~PhysicalBlockAccess(void) {} - virtual DIError Open(const char* deviceName, bool readOnly); + virtual DIError Open(const WCHAR* deviceName, bool readOnly); virtual DIError DetectCapacity(long* pNumBlocks) { /* try SPTI in case it happens to work */ DIError dierr; @@ -351,6 +351,7 @@ private: DiskGeometry fGeometry; }; +#ifdef WANT_ASPI /* * Access to a SCSI volume via the ASPI interface. */ @@ -385,7 +386,7 @@ private: long fLastChunkNum; long fChunkSize; // set by DetectCapacity }; - +#endif /*WANT_ASPI*/ // write a series of blocks to the volume DIError DoWriteBlocks(long startBlock, short blockCount, const void* buf) @@ -398,8 +399,9 @@ private: CBCache fBlockCache; }; + }; // namespace DiskImgLib -#endif /*WIN32BLOCKIO*/ +#endif /*DISKIMG_WIN32BLOCKIO_H*/ #endif /*_WIN32*/ diff --git a/diskimg/Win32Extra.h b/diskimg/Win32Extra.h index 6175388..fa77ea3 100644 --- a/diskimg/Win32Extra.h +++ b/diskimg/Win32Extra.h @@ -9,8 +9,8 @@ * * (Do we want IOCTL_DISK_GET_DRIVE_LAYOUT_EX too?) */ -#ifndef __WIN32EXTRA__ -#define __WIN32EXTRA__ +#ifndef DISKIMG_WIN32EXTRA_H +#define DISKIMG_WIN32EXTRA_H #include // base definitions @@ -57,4 +57,4 @@ PDISK_PARTITION_INFO DiskGeometryGetPartition(PDISK_GEOMETRY_EX Geometry); #endif /*IOCTL_DISK_GET_DRIVE_GEOMETRY_EX*/ -#endif /*__WIN32EXTRA__*/ +#endif /*DISKIMG_WIN32EXTRA_H*/ diff --git a/diskimg/diskimg.vcxproj b/diskimg/diskimg.vcxproj index e5c37ad..914fe16 100644 --- a/diskimg/diskimg.vcxproj +++ b/diskimg/diskimg.vcxproj @@ -26,6 +26,7 @@ DynamicLibrary v120 false + Unicode @@ -41,14 +42,16 @@ <_ProjectFileVersion>12.0.30501.0 - .\Release\ - .\Release\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ false + diskimg4 - .\Debug\ - .\Debug\ - false + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + diskimg4 @@ -60,20 +63,22 @@ true Use stdafx.h - .\Release/diskimg.pch - .\Release/ - .\Release/ - .\Release/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true + true ..\prebuilt\nufxlib2.lib;..\prebuilt\zdll.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - Release/diskimg4.dll - true - .\Release/diskimg4.pdb - .\Release/diskimg4.lib + $(OutDir)$(TargetName)$(TargetExt) + false + $(OutDir)$(TargetName).pdb + $(OutDir)$(TargetName).lib MachineX86 + NDEBUG;%(PreprocessorDefinitions) @@ -84,10 +89,10 @@ - Copying DLL to app directory - copy Release\diskimg4.dll ..\app -copy Release\diskimg4.dll ..\mdc - + + + + NDEBUG;%(PreprocessorDefinitions) @@ -102,22 +107,23 @@ copy Release\diskimg4.dll ..\mdc MultiThreadedDebugDLL Use stdafx.h - .\Debug/diskimg.pch - .\Debug/ - .\Debug/ - .\Debug/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true EditAndContinue + true - ..\prebuilt\nufxlib2D.lib;..\prebuilt\zdll.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) - Debug/diskimg4.dll - true + $(OutDir)$(TargetName)$(TargetExt) + false true - .\Debug/diskimg4.pdb - .\Debug/diskimg4.lib + $(OutDir)$(TargetName).pdb + $(OutDir)$(TargetName).lib MachineX86 + _DEBUG;%(PreprocessorDefinitions) @@ -128,10 +134,10 @@ copy Release\diskimg4.dll ..\mdc - Copying debug DLL to app directory - copy Debug\diskimg4.dll ..\app -copy Debug\diskimg4.dll ..\mdc - + + + + _DEBUG;%(PreprocessorDefinitions) @@ -139,145 +145,6 @@ copy Debug\diskimg4.dll ..\mdc - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - Create - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - Create - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - Disabled - WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - EnableFastChecks - MaxSpeed - WIN32;NDEBUGX;_WINDOWS;_MBCS;_USRDLL;DISKIMG_EXPORTS - - - - - @@ -292,11 +159,55 @@ copy Debug\diskimg4.dll ..\mdc + + {c48ae53b-3dcb-43b1-9207-b7c5b6bb78af} + + + {b66109f4-217b-43c0-86aa-eb55657e5ac0} + {0fa742e9-8c07-43dd-aff8-ce31faf70821} false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + diff --git a/diskimg/diskimg.vcxproj.filters b/diskimg/diskimg.vcxproj.filters index 6df8f94..18d2b7f 100644 --- a/diskimg/diskimg.vcxproj.filters +++ b/diskimg/diskimg.vcxproj.filters @@ -14,6 +14,44 @@ ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Source Files @@ -60,6 +98,9 @@ Source Files + + Source Files + Source Files @@ -96,9 +137,6 @@ Source Files - - Source Files - Source Files @@ -111,46 +149,8 @@ Source Files - - - + Source Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - + \ No newline at end of file diff --git a/diskimg/diskimg.vcxproj.user b/diskimg/diskimg.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/diskimg/diskimg.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/diskimg/libhfs/libhfs.vcxproj b/diskimg/libhfs/libhfs.vcxproj index 3f050ea..2a53e78 100644 --- a/diskimg/libhfs/libhfs.vcxproj +++ b/diskimg/libhfs/libhfs.vcxproj @@ -20,7 +20,7 @@ StaticLibrary v120 false - MultiByte + Unicode StaticLibrary @@ -42,12 +42,12 @@ <_ProjectFileVersion>12.0.30501.0 - .\Debug\ - .\Debug\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ - .\Release\ - .\Release\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ @@ -56,17 +56,17 @@ EnableFastChecks MultiThreadedDebugDLL - .\Debug/libhfs.pch - .\Debug/ - .\Debug/ - .\Debug/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb true Level2 true EditAndContinue - .\Debug\libhfs.lib + $(OutDir)$(TargetName)$(TargetExt) true @@ -83,15 +83,15 @@ MultiThreadedDLL true - .\Release/libhfs.pch - .\Release/ - .\Release/ - .\Release/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level2 true - .\Release\libhfs.lib + $(OutDir)$(TargetName)$(TargetExt) true @@ -99,80 +99,6 @@ 0x0409 - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - @@ -190,6 +116,20 @@ + + + + + + + + + + + + + + diff --git a/diskimg/libhfs/libhfs.vcxproj.filters b/diskimg/libhfs/libhfs.vcxproj.filters index 8c47666..3a24437 100644 --- a/diskimg/libhfs/libhfs.vcxproj.filters +++ b/diskimg/libhfs/libhfs.vcxproj.filters @@ -10,44 +10,6 @@ h;hpp;hxx;hm;inl - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - Header Files @@ -95,4 +57,42 @@ Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/mdc/AboutDlg.cpp b/mdc/AboutDlg.cpp index 2c35522..ce6cfb4 100644 --- a/mdc/AboutDlg.cpp +++ b/mdc/AboutDlg.cpp @@ -49,7 +49,7 @@ BOOL AboutDlg::OnInitDialog() pWnd->GetWindowText(fmt); newText.Format(fmt, kAppMajorVersion, kAppMinorVersion, kAppBugVersion); pWnd->SetWindowText(newText); - WMSG1("STR is '%s'\n", newText); + WMSG1("STR is '%ls'\n", (LPCWSTR) newText); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE diff --git a/mdc/ChooseFilesDlg.h b/mdc/ChooseFilesDlg.h index 94358ab..d673804 100644 --- a/mdc/ChooseFilesDlg.h +++ b/mdc/ChooseFilesDlg.h @@ -6,8 +6,8 @@ /* * Choose files and directories. */ -#ifndef __CHOOSEFILESDIALOG__ -#define __CHOOSEFILESDIALOG__ +#ifndef MDC_CHOOSEFILESDLG_H +#define MDC_CHOOSEFILESDLG_H #include "../util/UtilLib.h" #include "resource.h" @@ -15,9 +15,9 @@ class ChooseFilesDlg : public SelectFilesDialog { public: ChooseFilesDlg(CWnd* pParentWnd = NULL) : - SelectFilesDialog("IDD_CHOOSE_FILES", pParentWnd) + SelectFilesDialog(L"IDD_CHOOSE_FILES", pParentWnd) { - SetWindowTitle(_T("Choose Files...")); + SetWindowTitle(L"Choose Files..."); fAcceptButtonID = IDC_SELECT_ACCEPT; } @@ -36,4 +36,4 @@ private: //DECLARE_MESSAGE_MAP() }; -#endif /*__CHOOSEFILESDIALOG__*/ \ No newline at end of file +#endif /*MDC_CHOOSEFILESDLG_H*/ diff --git a/mdc/Main.cpp b/mdc/Main.cpp index 2ee1453..90a3c1b 100644 --- a/mdc/Main.cpp +++ b/mdc/Main.cpp @@ -14,9 +14,9 @@ #include "ProgressDlg.h" #include "resource.h" #include "../diskimg/DiskImg.h" -#include "../prebuilt/zlib.h" +#include "../zlib/zlib.h" -const char* kWebSiteURL = "http://www.faddensoft.com/"; +const WCHAR* kWebSiteURL = L"http://www.faddensoft.com/"; BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) @@ -33,7 +33,7 @@ END_MESSAGE_MAP() */ MainWindow::MainWindow() { - static const char* kAppName = "MDC"; + static const WCHAR* kAppName = L"MDC"; CString wndClass = AfxRegisterWndClass( CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/, @@ -92,11 +92,11 @@ MainWindow::OnHelpWebSite(void) { int err; - err = (int) ::ShellExecute(m_hWnd, _T("open"), kWebSiteURL, NULL, NULL, + err = (int) ::ShellExecute(m_hWnd, L"open", kWebSiteURL, NULL, NULL, SW_SHOWNORMAL); if (err <= 32) { CString msg; - msg.Format("Unable to launch web browser (err=%d).", err); + msg.Format(L"Unable to launch web browser (err=%d).", err); ShowFailureMsg(this, msg, IDS_FAILED); } } @@ -178,9 +178,9 @@ MainWindow::DebugMsgHandler(const char* file, int line, const char* msg) #if defined(_DEBUG_LOG) //fprintf(gLog, "%s(%d) : %s", file, line, msg); - fprintf(gLog, "%05u %s", gPid, msg); + fprintf(gLog, "%05u %hs", gPid, msg); #elif defined(_DEBUG) - _CrtDbgReport(_CRT_WARN, file, line, NULL, "%s", msg); + _CrtDbgReport(_CRT_WARN, file, line, NULL, "%hs", msg); #else /* do nothing */ #endif @@ -195,17 +195,17 @@ MainWindow::NufxErrorMsgHandler(NuArchive* /*pArchive*/, void* vErrorMessage) #if defined(_DEBUG_LOG) if (pErrorMessage->isDebug) { - fprintf(gLog, "%05u [D] %s\n", gPid, pErrorMessage->message); + fprintf(gLog, "%05u [D] %hs\n", gPid, pErrorMessage->message); } else { - fprintf(gLog, "%05u %s\n", gPid, pErrorMessage->message); + fprintf(gLog, "%05u %hs\n", gPid, pErrorMessage->message); } #elif defined(_DEBUG) if (pErrorMessage->isDebug) { _CrtDbgReport(_CRT_WARN, pErrorMessage->file, pErrorMessage->line, - NULL, " [D] %s\n", pErrorMessage->message); + NULL, " [D] %hs\n", pErrorMessage->message); } else { _CrtDbgReport(_CRT_WARN, pErrorMessage->file, pErrorMessage->line, - NULL, " %s\n", pErrorMessage->message); + NULL, " %hs\n", pErrorMessage->message); } #else /* do nothing */ @@ -229,7 +229,7 @@ MainWindow::ScanFiles(void) { ChooseFilesDlg chooseFiles; ScanOpts scanOpts; - char curDir[MAX_PATH] = ""; + WCHAR curDir[MAX_PATH] = L""; CString errMsg; CString outPath; bool doResetDir = false; @@ -238,29 +238,30 @@ MainWindow::ScanFiles(void) /* choose input files */ chooseFiles.DoModal(); - if (chooseFiles.GetExitStatus() != IDOK) + if (chooseFiles.GetExitStatus() != IDOK) { return; + } - const char* buf = chooseFiles.GetFileNames(); - WMSG2("Selected path = '%s' (offset=%d)\n", buf, + const WCHAR* buf = chooseFiles.GetFileNames(); + WMSG2("Selected path = '%ls' (offset=%d)\n", buf, chooseFiles.GetFileNameOffset()); /* choose output file */ - CFileDialog dlg(FALSE, _T("txt"), NULL, + CFileDialog dlg(FALSE, L"txt", NULL, OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN, - "Text Files (*.txt)|*.txt|All Files (*.*)|*.*||", this); + L"Text Files (*.txt)|*.txt|All Files (*.*)|*.*||", this); - dlg.m_ofn.lpstrTitle = "Save Output As..."; - strcpy(dlg.m_ofn.lpstrFile, "mdc-out.txt"); + dlg.m_ofn.lpstrTitle = L"Save Output As..."; + wcscpy(dlg.m_ofn.lpstrFile, L"mdc-out.txt"); if (dlg.DoModal() != IDOK) { goto bail; } outPath = dlg.GetPathName(); - WMSG1("NEW FILE '%s'\n", (LPCTSTR) outPath); + WMSG1("NEW FILE '%ls'\n", (LPCWSTR) outPath); - scanOpts.outfp = fopen(outPath, "w"); + scanOpts.outfp = _wfopen(outPath, L"w"); if (scanOpts.outfp == nil) { ShowFailureMsg(this, "Unable to open output file", IDS_FAILED); goto bail; @@ -277,18 +278,18 @@ MainWindow::ScanFiles(void) "MDC is part of CiderPress, available from http://www.faddensoft.com/.\n"); NuGetVersion(&major, &minor, &bug, NULL, NULL); fprintf(scanOpts.outfp, - "Linked against NufxLib v%ld.%ld.%ld and zlib v%s\n", + "Linked against NufxLib v%ld.%ld.%ld and zlib v%hs\n", major, minor, bug, zlibVersion()); fprintf(scanOpts.outfp, "\n"); /* change to base directory */ - if (GetCurrentDirectory(sizeof(curDir), curDir) == 0) { - errMsg = "Unable to get current directory."; + if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { + errMsg = L"Unable to get current directory."; ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } if (SetCurrentDirectory(buf) == false) { - errMsg.Format("Unable to set current directory to '%s'.", buf); + errMsg.Format(L"Unable to set current directory to '%ls'.", buf); ShowFailureMsg(this, errMsg, IDS_FAILED); goto bail; } @@ -297,7 +298,7 @@ MainWindow::ScanFiles(void) time_t now; now = time(nil); fprintf(scanOpts.outfp, - "Run started at %.24s in '%s'\n\n", ctime(&now), buf); + "Run started at %.24hs in '%ls'\n\n", ctime(&now), buf); /* obstruct input to the main window */ EnableWindow(FALSE); @@ -323,34 +324,34 @@ MainWindow::ScanFiles(void) buf += chooseFiles.GetFileNameOffset(); while (*buf != '\0') { if (Process(buf, &scanOpts, &errMsg) != 0) { - WMSG2("Skipping '%s': %s.\n", buf, (LPCTSTR) errMsg); + WMSG2("Skipping '%ls': %ls.\n", buf, (LPCWSTR) errMsg); } if (fCancelFlag) { WMSG0("CANCELLED by user\n"); - MessageBox("Cancelled!", "MDC", MB_OK); + MessageBox(L"Cancelled!", L"MDC", MB_OK); goto bail; } - buf += strlen(buf)+1; + buf += wcslen(buf)+1; } end = time(nil); fprintf(scanOpts.outfp, "\nScan completed in %ld seconds.\n", end - start); { - SetWindowText(_T("MDC Done!")); + SetWindowText(L"MDC Done!"); CString doneMsg; CString appName; appName.LoadString(IDS_APP_TITLE); #ifdef _DEBUG_LOG - doneMsg.Format("Processing completed.\r\n\r\n" - "Output is in '%s', log messages in '%s'.", - outPath, kDebugLog); + doneMsg.Format(L"Processing completed.\r\n\r\n" + "Output is in '%ls', log messages in '%ls'.", + (LPCWSTR) outPath, kDebugLog); #else - doneMsg.Format("Processing completed."); + doneMsg.Format(L"Processing completed."); #endif scanOpts.pProgress->MessageBox(doneMsg, appName, MB_OK|MB_ICONINFORMATION); } @@ -360,7 +361,7 @@ bail: fclose(scanOpts.outfp); if (doResetDir && SetCurrentDirectory(curDir) == false) { - errMsg.Format("Unable to reset current directory to '%s'.\n", curDir); + errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); ShowFailureMsg(this, errMsg, IDS_FAILED); // bummer } @@ -371,20 +372,20 @@ bail: if (scanOpts.pProgress != nil) scanOpts.pProgress->DestroyWindow(); - SetWindowText(_T("MDC")); + SetWindowText(L"MDC"); } /* - * Directory structure and functions, based on zDIR in Info-Zip sources. + * Directory structure and functions, modified from zDIR in Info-Zip sources. */ typedef struct Win32dirent { - char d_attr; - char d_name[MAX_PATH]; + DWORD d_attr; + WCHAR d_name[MAX_PATH]; int d_first; HANDLE d_hFindFile; } Win32dirent; -static const char* kWildMatchAll = "*.*"; +static const WCHAR kWildMatchAll[] = L"*.*"; /* * Prepare a directory for reading. @@ -392,37 +393,38 @@ static const char* kWildMatchAll = "*.*"; * Allocates a Win32dirent struct that must be freed by the caller. */ Win32dirent* -MainWindow::OpenDir(const char* name) +MainWindow::OpenDir(const WCHAR* name) { Win32dirent* dir = nil; - char* tmpStr = nil; - char* cp; + WCHAR* tmpStr = nil; + WCHAR* cp; WIN32_FIND_DATA fnd; dir = (Win32dirent*) malloc(sizeof(*dir)); - tmpStr = (char*) malloc(strlen(name) + (2 + sizeof(kWildMatchAll))); + tmpStr = (WCHAR*) malloc((wcslen(name) + wcslen(kWildMatchAll) + 2) + * sizeof(WCHAR)); if (dir == nil || tmpStr == nil) goto failed; - strcpy(tmpStr, name); - cp = tmpStr + strlen(tmpStr); + wcscpy(tmpStr, name); + cp = tmpStr + wcslen(tmpStr); /* don't end in a colon (e.g. "C:") */ - if ((cp - tmpStr) > 0 && strrchr(tmpStr, ':') == (cp - 1)) + if ((cp - tmpStr) > 0 && wcsrchr(tmpStr, ':') == (cp - 1)) *cp++ = '.'; /* must end in a slash */ if ((cp - tmpStr) > 0 && - strrchr(tmpStr, kLocalFssep) != (cp - 1)) + wcsrchr(tmpStr, kLocalFssep) != (cp - 1)) *cp++ = kLocalFssep; - strcpy(cp, kWildMatchAll); + wcscpy(cp, kWildMatchAll); dir->d_hFindFile = FindFirstFile(tmpStr, &fnd); if (dir->d_hFindFile == INVALID_HANDLE_VALUE) goto failed; - strcpy(dir->d_name, fnd.cFileName); - dir->d_attr = (unsigned char) fnd.dwFileAttributes; + wcscpy(dir->d_name, fnd.cFileName); + dir->d_attr = fnd.dwFileAttributes; dir->d_first = 1; bail: @@ -450,7 +452,7 @@ MainWindow::ReadDir(Win32dirent* dir) if (!FindNextFile(dir->d_hFindFile, &fnd)) return nil; - strcpy(dir->d_name, fnd.cFileName); + wcscpy(dir->d_name, fnd.cFileName); dir->d_attr = (unsigned char) fnd.dwFileAttributes; } @@ -470,10 +472,6 @@ MainWindow::CloseDir(Win32dirent* dir) free(dir); } -/* might as well blend in with the UNIX version */ -#define DIR_NAME_LEN(dirent) ((int)strlen((dirent)->d_name)) - - /* * Process a file or directory. These are expected to be names of files in * the current directory. @@ -481,11 +479,11 @@ MainWindow::CloseDir(Win32dirent* dir) * Returns 0 on success, nonzero on error with a message in "*pErrMsg". */ int -MainWindow::Process(const char* pathname, ScanOpts* pScanOpts, +MainWindow::Process(const WCHAR* pathname, ScanOpts* pScanOpts, CString* pErrMsg) { bool exists, isDir, isReadable; - struct stat sb; + struct _stat sb; int result = -1; if (fCancelFlag) @@ -497,17 +495,17 @@ MainWindow::Process(const char* pathname, ScanOpts* pScanOpts, PathName checkPath(pathname); int ierr = checkPath.CheckFileStatus(&sb, &exists, &isReadable, &isDir); if (ierr != 0) { - pErrMsg->Format("Unexpected error while examining '%s': %s", pathname, + pErrMsg->Format(L"Unexpected error while examining '%ls': %hs", pathname, strerror(ierr)); goto bail; } if (!exists) { - pErrMsg->Format("Couldn't find '%s'", pathname); + pErrMsg->Format(L"Couldn't find '%ls'", pathname); goto bail; } if (!isReadable) { - pErrMsg->Format("File '%s' isn't readable", pathname); + pErrMsg->Format(L"File '%ls' isn't readable", pathname); goto bail; } if (isDir) { @@ -521,7 +519,7 @@ MainWindow::Process(const char* pathname, ScanOpts* pScanOpts, bail: if (result != 0 && pErrMsg->IsEmpty()) { - pErrMsg->Format("Unable to add file '%s'", pathname); + pErrMsg->Format(L"Unable to add file '%ls'", pathname); } return result; } @@ -532,24 +530,24 @@ bail: * add the file. */ int -MainWindow::ProcessDirectory(const char* dirName, ScanOpts* pScanOpts, +MainWindow::ProcessDirectory(const WCHAR* dirName, ScanOpts* pScanOpts, CString* pErrMsg) { Win32dirent* dirp = nil; Win32dirent* entry; - char nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ - char fssep; + WCHAR nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ + WCHAR fssep; int len; int result = -1; ASSERT(dirName != nil); ASSERT(pErrMsg != nil); - WMSG1("+++ DESCEND: '%s'\n", dirName); + WMSG1("+++ DESCEND: '%ls'\n", (LPCWSTR) dirName); dirp = OpenDir(dirName); if (dirp == nil) { - pErrMsg->Format("Failed on '%s': %s", dirName, strerror(errno)); + pErrMsg->Format(L"Failed on '%ls': %hs", dirName, strerror(errno)); goto bail; } @@ -558,21 +556,22 @@ MainWindow::ProcessDirectory(const char* dirName, ScanOpts* pScanOpts, /* could use readdir_r, but we don't care about reentrancy here */ while ((entry = ReadDir(dirp)) != nil) { /* skip the dotsies */ - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + if (wcscmp(entry->d_name, L".") == 0 || wcscmp(entry->d_name, L"..") == 0) continue; - len = strlen(dirName); - if (len + DIR_NAME_LEN(entry) +2 > MAX_PATH) { - WMSG4("ERROR: Filename exceeds %d bytes: %s%c%s", + len = wcslen(dirName); + if (len + wcslen(entry->d_name) + 2 > MAX_PATH) { + WMSG4("ERROR: Filename exceeds %d bytes: %ls%c%ls", MAX_PATH, dirName, fssep, entry->d_name); goto bail; } /* form the new name, inserting an fssep if needed */ - strcpy(nbuf, dirName); - if (dirName[len-1] != fssep) + wcscpy(nbuf, dirName); + if (dirName[len - 1] != fssep) { nbuf[len++] = fssep; - strcpy(nbuf+len, entry->d_name); + } + wcscpy(nbuf+len, entry->d_name); result = Process(nbuf, pScanOpts, pErrMsg); if (result != 0) @@ -594,7 +593,7 @@ bail: * Returns 0 on success, nonzero on failure. */ int -MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) +MainWindow::ScanDiskImage(const WCHAR* pathName, ScanOpts* pScanOpts) { ASSERT(pathName != nil); ASSERT(pScanOpts != nil); @@ -614,11 +613,11 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) return -1; CString title; - title = _T("MDC "); - title += FilenameOnly(pathName, '\\'); + title = L"MDC "; + title += PathName::FilenameOnly(pathName, '\\'); SetWindowText(title); - fprintf(pScanOpts->outfp, "File: %s\n", pathName); + fprintf(pScanOpts->outfp, "File: %ls\n", pathName); fflush(pScanOpts->outfp); // in case we crash if (!ext.IsEmpty()) { @@ -628,14 +627,14 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) dierr = diskImg.OpenImage(pathName, '\\', true); if (dierr != kDIErrNone) { - errMsg.Format("Unable to open '%s': %s", pathName, + errMsg.Format(L"Unable to open '%ls': %hs", pathName, DiskImgLib::DIStrError(dierr)); goto bail; } dierr = diskImg.AnalyzeImage(); if (dierr != kDIErrNone) { - errMsg.Format("Analysis of '%s' failed: %s", pathName, + errMsg.Format(L"Analysis of '%ls' failed: %hs", pathName, DiskImgLib::DIStrError(dierr)); goto bail; } @@ -643,7 +642,7 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) if (diskImg.GetFSFormat() == DiskImg::kFormatUnknown || diskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - errMsg.Format("Unable to identify filesystem on '%s'", pathName); + errMsg.Format(L"Unable to identify filesystem on '%ls'", pathName); goto bail; } @@ -652,7 +651,7 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) if (pDiskFS == nil) { /* unknown FS should've been caught above! */ ASSERT(false); - errMsg.Format("Format of '%s' not recognized.", pathName); + errMsg.Format(L"Format of '%ls' not recognized.", pathName); goto bail; } @@ -661,7 +660,7 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) /* object created; prep it */ dierr = pDiskFS->Initialize(&diskImg, DiskFS::kInitFull); if (dierr != kDIErrNone) { - errMsg.Format("Error reading list of files from disk: %s", + errMsg.Format(L"Error reading list of files from disk: %hs", DiskImgLib::DIStrError(dierr)); goto bail; } @@ -674,7 +673,7 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) pDiskFS->GetDiskImg()->GetNumSectPerTrack()) / 4; else kbytes = 0; - fprintf(pScanOpts->outfp, "Disk: %s%s (%dKB)\n", pDiskFS->GetVolumeID(), + fprintf(pScanOpts->outfp, "Disk: %hs%hs (%dKB)\n", pDiskFS->GetVolumeID(), pDiskFS->GetFSDamaged() ? " [*]" : "", kbytes); fprintf(pScanOpts->outfp, " Name Type Auxtyp Modified" @@ -683,7 +682,7 @@ MainWindow::ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) "------------------------------------------------------" "------------------------\n"); if (LoadDiskFSContents(pDiskFS, "", pScanOpts) != 0) { - errMsg.Format("Failed while loading contents of '%s'.", pathName); + errMsg.Format(L"Failed while loading contents of '%ls'.", pathName); goto bail; } fprintf(pScanOpts->outfp, @@ -696,7 +695,7 @@ bail: //PeekAndPump(); if (!errMsg.IsEmpty()) { - fprintf(pScanOpts->outfp, "Failed: %s\n\n", (LPCTSTR) errMsg); + fprintf(pScanOpts->outfp, "Failed: %ls\n\n", (LPCWSTR) errMsg); return -1; } else { return 0; @@ -830,7 +829,7 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, ASSERT(pDiskFS != nil); pFile = pDiskFS->GetNextFile(nil); for ( ; pFile != nil; pFile = pDiskFS->GetNextFile(pFile)) { - CString subVolName, dispName; + CStringA subVolName, dispName; RecordKind recordKind; LONGLONG totalLen, totalCompLen; char tmpbuf[16]; @@ -839,13 +838,13 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, if (recordKind == kRecordKindVolumeDir) { /* this is a volume directory */ - WMSG1("Not displaying volume dir '%s'\n", pFile->GetPathName()); + WMSG1("Not displaying volume dir '%ls'\n", pFile->GetPathName()); continue; } /* prepend volName for sub-volumes; must be valid Win32 dirname */ if (volName[0] != '\0') - subVolName.Format("_%s", volName); + subVolName.Format("_%hs", volName); const char* ccp = pFile->GetPathName(); ASSERT(ccp != nil); @@ -867,6 +866,7 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, } /* strip out ctrl chars and high ASCII in HFS names */ + // TODO: consider having a Unicode output mode MacSanitize(dispName.GetBuffer(0)); dispName.ReleaseBuffer(); @@ -884,13 +884,13 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, } switch (recordKind) { case kRecordKindUnknown: - fprintf(pScanOpts->outfp, "%s- $%04lX ", + fprintf(pScanOpts->outfp, "%hs- $%04lX ", GetFileTypeString(pFile->GetFileType()), pFile->GetAuxType()); break; case kRecordKindDisk: sprintf(tmpbuf, "%ldk", totalLen / 1024); - fprintf(pScanOpts->outfp, "Disk %-6s ", tmpbuf); + fprintf(pScanOpts->outfp, "Disk %-6hs ", tmpbuf); break; case kRecordKindFile: case kRecordKindForkedFile: @@ -902,7 +902,7 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, pFile->GetAuxType() >= 0 && pFile->GetAuxType() <= 0xffff) { /* ProDOS type embedded in HFS */ - fprintf(pScanOpts->outfp, "%s%c $%04lX ", + fprintf(pScanOpts->outfp, "%hs%c $%04lX ", GetFileTypeString(pFile->GetFileType()), recordKind == kRecordKindForkedFile ? '+' : ' ', pFile->GetAuxType()); @@ -938,7 +938,7 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, } } } else { - fprintf(pScanOpts->outfp, "%s%c $%04lX ", + fprintf(pScanOpts->outfp, "%hs%c $%04lX ", GetFileTypeString(pFile->GetFileType()), recordKind == kRecordKindForkedFile ? '+' : ' ', pFile->GetAuxType()); @@ -999,7 +999,7 @@ MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, else if (pFile->GetQuality() == A2File::kQualitySuspicious) fmtStr = "BAD? "; - fprintf(pScanOpts->outfp, "%s ", fmtStr); + fprintf(pScanOpts->outfp, "%hs ", fmtStr); #if 0 diff --git a/mdc/Main.h b/mdc/Main.h index f608ff3..6c6fd89 100644 --- a/mdc/Main.h +++ b/mdc/Main.h @@ -6,11 +6,11 @@ /* * Main frame window declarations. */ -#ifndef __MAIN__ -#define __MAIN__ +#ifndef MDC_MAIN_H +#define MDC_MAIN_H #include "../diskimg/DiskImg.h" -#include "../prebuilt/NufxLib.h" +#include "../nufxlib/NufxLib.h" using namespace DiskImgLib; struct Win32dirent; @@ -48,14 +48,14 @@ private: void* vErrorMessage); void ScanFiles(void); - Win32dirent* OpenDir(const char* name); + Win32dirent* OpenDir(const WCHAR* name); Win32dirent* ReadDir(Win32dirent* dir); void CloseDir(Win32dirent* dir); - int Process(const char* pathname, ScanOpts* pScanOpts, + int Process(const WCHAR* pathname, ScanOpts* pScanOpts, CString* pErrMsg); - int ProcessDirectory(const char* dirName, ScanOpts* pScanOpts, + int ProcessDirectory(const WCHAR* dirName, ScanOpts* pScanOpts, CString* pErrMsg); - int ScanDiskImage(const char* pathName, ScanOpts* pScanOpts); + int ScanDiskImage(const WCHAR* pathName, ScanOpts* pScanOpts); int LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, ScanOpts* pScanOpts); void AnalyzeFile(const A2File* pFile, RecordKind* pRecordKind, @@ -69,4 +69,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__MAIN__*/ \ No newline at end of file +#endif /*MDC_MAIN_H*/ diff --git a/mdc/ProgressDlg.cpp b/mdc/ProgressDlg.cpp index ceb8694..11bf1cd 100644 --- a/mdc/ProgressDlg.cpp +++ b/mdc/ProgressDlg.cpp @@ -60,7 +60,7 @@ void ProgressDlg::PostNcDestroy() * working on. */ void -ProgressDlg::SetCurrentFile(const char* fileName) +ProgressDlg::SetCurrentFile(const WCHAR* fileName) { CWnd* pWnd = GetDlgItem(IDC_PROGRESS_FILENAME); ASSERT(pWnd != nil); diff --git a/mdc/ProgressDlg.h b/mdc/ProgressDlg.h index f405ed3..f12a697 100644 --- a/mdc/ProgressDlg.h +++ b/mdc/ProgressDlg.h @@ -42,7 +42,7 @@ public: BOOL Create(CWnd* pParentWnd = NULL) { return CDialog::Create(IDD_PROGRESS, pParentWnd); } - void SetCurrentFile(const char* fileName); + void SetCurrentFile(const WCHAR* fileName); // Implementation protected: diff --git a/mdc/StdAfx.h b/mdc/StdAfx.h index b13157b..a389165 100644 --- a/mdc/StdAfx.h +++ b/mdc/StdAfx.h @@ -17,6 +17,7 @@ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include "../app/targetver.h" // Windows Header Files: //#include diff --git a/mdc/mdc.cpp b/mdc/mdc.cpp index 635536a..17e24e9 100644 --- a/mdc/mdc.cpp +++ b/mdc/mdc.cpp @@ -38,7 +38,7 @@ MyApp::MyApp(LPCTSTR lpszAppName) : CWinApp(lpszAppName) fprintf(gLog, "\n"); #endif - WMSG1("MDC started at %.24s\n", ctime(&now)); + WMSG1("MDC started at %.24hs\n", ctime(&now)); int tmpDbgFlag; // enable memory leak detection diff --git a/mdc/mdc.vcxproj b/mdc/mdc.vcxproj index 9e05e6d..3591193 100644 --- a/mdc/mdc.vcxproj +++ b/mdc/mdc.vcxproj @@ -27,7 +27,7 @@ Application v120 Dynamic - MultiByte + Unicode @@ -43,14 +43,14 @@ <_ProjectFileVersion>12.0.30501.0 - .\Release\ - .\Release\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ false - .\Debug\ - .\Debug\ - false + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true @@ -62,22 +62,23 @@ true Use stdafx.h - .\Release/mdc.pch - .\Release/ - .\Release/ - .\Release/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true + true - ..\prebuilt\nufxlib2.lib;..\prebuilt\zdll.lib;%(AdditionalDependencies) - .\Release/mdc.exe - true - .\Release/mdc.pdb + $(OutDir)$(TargetName)$(TargetExt) + false + Release/mdc.pdb true - .\Release/mdc.map + Release/mdc.map Windows MachineX86 + NDEBUG;%(PreprocessorDefinitions) @@ -88,10 +89,10 @@ - Copy DLLs in - copy ..\prebuilt\nufxlib2.dll . -copy ..\prebuilt\zlib1.dll . - + + + + NDEBUG;%(PreprocessorDefinitions) @@ -106,23 +107,24 @@ copy ..\prebuilt\zlib1.dll . MultiThreadedDebugDLL Use stdafx.h - .\Debug/mdc.pch - .\Debug/ - .\Debug/ - .\Debug/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb true Level3 true EditAndContinue + true - ..\prebuilt\nufxlib2D.lib;..\prebuilt\zdll.lib;%(AdditionalDependencies) - .\Debug/mdc.exe - true + $(OutDir)$(TargetName)$(TargetExt) + false true - .\Debug/mdc.pdb + Debug/mdc.pdb Windows MachineX86 + _DEBUG;%(PreprocessorDefinitions) @@ -133,56 +135,16 @@ copy ..\prebuilt\zlib1.dll . - Copy debug DLLs in - copy ..\prebuilt\nufxlib2D.dll . -copy ..\prebuilt\zlib1.dll . - + + + + _DEBUG;%(PreprocessorDefinitions) 0x0409 - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - true - MaxSpeed - - - Disabled - EnableFastChecks - Create - true - MaxSpeed - Create - - @@ -203,10 +165,32 @@ copy ..\prebuilt\zlib1.dll . {0cfe6fad-0126-4e99-8625-c807d1d2aaf4} false + + {c48ae53b-3dcb-43b1-9207-b7c5b6bb78af} + {04bfae2a-7ab3-4b63-b4ab-42ff1d6ad3c5} false + + {b66109f4-217b-43c0-86aa-eb55657e5ac0} + false + false + false + true + false + + + + + + + + + + Create + Create + diff --git a/mdc/mdc.vcxproj.filters b/mdc/mdc.vcxproj.filters index 5c2c148..1424398 100644 --- a/mdc/mdc.vcxproj.filters +++ b/mdc/mdc.vcxproj.filters @@ -14,26 +14,6 @@ ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - Source Files @@ -67,4 +47,24 @@ Resource Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/mdc/mdc.vcxproj.user b/mdc/mdc.vcxproj.user new file mode 100644 index 0000000..ef5ff2a --- /dev/null +++ b/mdc/mdc.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/nufxlib/Archive.c b/nufxlib/Archive.c new file mode 100644 index 0000000..4dd544c --- /dev/null +++ b/nufxlib/Archive.c @@ -0,0 +1,1218 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Archive structure creation and manipulation. + */ +#include "NufxLibPriv.h" + +#ifdef HAVE_FCNTL_H +# include +#endif +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* master header identification */ +static const uchar kNuMasterID[kNufileIDLen] = + { 0x4e, 0xf5, 0x46, 0xe9, 0x6c, 0xe5 }; + +/* other identification; can be no longer than kNufileIDLen */ +static const uchar kNuBinary2ID[] = + { 0x0a, 0x47, 0x4c }; +static const uchar kNuSHKSEAID[] = + { 0xa2, 0x2e, 0x00 }; + +/* + * Offsets to some interesting places in the wrappers. + */ +#define kNuBNYFileSizeLo 8 /* file size in 512-byte blocks (2B) */ +#define kNuBNYFileSizeHi 114 /* ... (2B) */ +#define kNuBNYEOFLo 20 /* file size in bytes (3B) */ +#define kNuBNYEOFHi 116 /* ... (1B) */ +#define kNuBNYDiskSpace 117 /* total space req'd; equiv FileSize (4B) */ +#define kNuBNYFilesToFollow 127 /* (1B) #of files in rest of BNY file */ +#define kNuSEAFunkySize 11938 /* length of archive + 68 (4B?) */ +#define kNuSEAFunkyAdjust 68 /* ... adjustment to "FunkySize" */ +#define kNuSEALength1 11946 /* length of archive (4B?) */ +#define kNuSEALength2 12001 /* length of archive (4B?) */ + +#define kDefaultJunkSkipMax 1024 /* default junk scan size */ + +static void Nu_CloseAndFree(NuArchive* pArchive); + + +/* + * =========================================================================== + * Archive and MasterHeader utility functions + * =========================================================================== + */ + +/* + * Allocate and initialize a new NuArchive structure. + */ +static NuError +Nu_NuArchiveNew(NuArchive** ppArchive) +{ + Assert(ppArchive != nil); + + /* validate some assumptions we make throughout the code */ + Assert(sizeof(int) >= 2); + Assert(sizeof(ushort) >= 2); + Assert(sizeof(ulong) >= 4); + Assert(sizeof(void*) >= sizeof(NuArchive*)); + + *ppArchive = Nu_Calloc(nil, sizeof(**ppArchive)); + if (*ppArchive == nil) + return kNuErrMalloc; + + (*ppArchive)->structMagic = kNuArchiveStructMagic; + + (*ppArchive)->recordIdxSeed = 1000; /* could be a random number */ + (*ppArchive)->nextRecordIdx = (*ppArchive)->recordIdxSeed; + + /* + * Initialize assorted values to defaults. We don't try to do any + * system-specific values here; it's up to the application to decide + * what is most appropriate for the current system. + */ + (*ppArchive)->valIgnoreCRC = false; + #ifdef ENABLE_LZW + (*ppArchive)->valDataCompression = kNuCompressLZW2; + #else + (*ppArchive)->valDataCompression = kNuCompressNone; + #endif + (*ppArchive)->valDiscardWrapper = false; + (*ppArchive)->valEOL = kNuEOLLF; /* non-UNIX apps must override */ + (*ppArchive)->valConvertExtractedEOL = kNuConvertOff; + (*ppArchive)->valOnlyUpdateOlder = false; + (*ppArchive)->valAllowDuplicates = false; + (*ppArchive)->valHandleExisting = kNuMaybeOverwrite; + (*ppArchive)->valModifyOrig = false; + (*ppArchive)->valMimicSHK = false; + (*ppArchive)->valMaskDataless = false; + (*ppArchive)->valStripHighASCII = false; + /* bug: this can't be set by application! */ + (*ppArchive)->valJunkSkipMax = kDefaultJunkSkipMax; + (*ppArchive)->valIgnoreLZW2Len = false; + (*ppArchive)->valHandleBadMac = false; + + (*ppArchive)->messageHandlerFunc = gNuGlobalErrorMessageHandler; + + return kNuErrNone; +} + +/* + * Free up a NuArchive structure and its contents. + */ +static NuError +Nu_NuArchiveFree(NuArchive* pArchive) +{ + Assert(pArchive != nil); + Assert(pArchive->structMagic == kNuArchiveStructMagic); + + (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->origRecordSet); + pArchive->haveToc = false; + (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); + (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->newRecordSet); + + Nu_Free(nil, pArchive->archivePathname); + Nu_Free(nil, pArchive->tmpPathname); + Nu_Free(nil, pArchive->compBuf); + Nu_Free(nil, pArchive->lzwCompressState); + Nu_Free(nil, pArchive->lzwExpandState); + + /* mark it as deceased to prevent further use, then free it */ + pArchive->structMagic = kNuArchiveStructMagic ^ 0xffffffff; + Nu_Free(nil, pArchive); + + return kNuErrNone; +} + + +/* + * Copy a NuMasterHeader struct. + */ +void +Nu_MasterHeaderCopy(NuArchive* pArchive, NuMasterHeader* pDstHeader, + const NuMasterHeader* pSrcHeader) +{ + Assert(pArchive != nil); + Assert(pDstHeader != nil); + Assert(pSrcHeader != nil); + + *pDstHeader = *pSrcHeader; +} + +/* + * Get a pointer to the archive master header (this is an API call). + */ +NuError +Nu_GetMasterHeader(NuArchive* pArchive, const NuMasterHeader** ppMasterHeader) +{ + if (ppMasterHeader == nil) + return kNuErrInvalidArg; + + *ppMasterHeader = &pArchive->masterHeader; + + return kNuErrNone; +} + + +/* + * Allocate the general-purpose compression buffer, if needed. + */ +NuError +Nu_AllocCompressionBufferIFN(NuArchive* pArchive) +{ + Assert(pArchive != nil); + + if (pArchive->compBuf != nil) + return kNuErrNone; + + pArchive->compBuf = Nu_Malloc(pArchive, kNuGenCompBufSize); + if (pArchive->compBuf == nil) + return kNuErrMalloc; + + return kNuErrNone; +} + + +/* + * Return a unique value. + */ +NuRecordIdx +Nu_GetNextRecordIdx(NuArchive* pArchive) +{ + return pArchive->nextRecordIdx++; +} + +/* + * Return a unique value. + */ +NuThreadIdx +Nu_GetNextThreadIdx(NuArchive* pArchive) +{ + return pArchive->nextRecordIdx++; /* just use the record counter */ +} + + +/* + * =========================================================================== + * Wrapper (SEA, BXY, BSE) functions + * =========================================================================== + */ + +/* + * Copy the wrapper from the archive file to the temp file. + */ +NuError +Nu_CopyWrapperToTemp(NuArchive* pArchive) +{ + NuError err; + + Assert(pArchive->headerOffset); /* no wrapper to copy?? */ + + err = Nu_FSeek(pArchive->archiveFp, 0, SEEK_SET); + BailError(err); + err = Nu_FSeek(pArchive->tmpFp, 0, SEEK_SET); + BailError(err); + err = Nu_CopyFileSection(pArchive, pArchive->tmpFp, + pArchive->archiveFp, pArchive->headerOffset); + BailError(err); + +bail: + return err; +} + + +/* + * Fix up the wrapper. The SEA and BXY headers have some fields + * set according to file length and archive attributes. + * + * Pass in the file pointer that will be written to. Wrappers are + * assumed to start at offset 0. + * + * Wrappers must appear in this order: + * Leading junk + * Binary II + * ShrinkIt SEA (Self-Extracting Archive) + * + * If they didn't, we wouldn't be this far. + * + * I have a Binary II specification, but don't have one for SEA, so I'm + * making educated guesses based on the differences between archives. I'd + * guess some of the SEA weirdness stems from some far-sighted support + * for multiple archives within a single SEA wrapper. + */ +NuError +Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp) +{ + NuError err = kNuErrNone; + Boolean hasBinary2, hasSea; + uchar identBuf[kNufileIDLen]; + ulong archiveLen, archiveLen512; + + Assert(pArchive->newMasterHeader.isValid); /* need new crc and len */ + + hasBinary2 = hasSea = false; + + switch (pArchive->archiveType) { + case kNuArchiveNuFX: + goto bail; + case kNuArchiveNuFXInBNY: + hasBinary2 = true; + break; + case kNuArchiveNuFXSelfEx: + hasSea = true; + break; + case kNuArchiveNuFXSelfExInBNY: + hasBinary2 = hasSea = true; + break; + default: + if (pArchive->headerOffset != 0 && + pArchive->headerOffset != pArchive->junkOffset) + { + Nu_ReportError(NU_BLOB, kNuErrNone, "Can't fix the wrapper??"); + err = kNuErrInternal; + goto bail; + } else + goto bail; + } + + err = Nu_FSeek(fp, pArchive->junkOffset, SEEK_SET); + BailError(err); + + if (hasBinary2) { + /* sanity check - make sure it's Binary II */ + Nu_ReadBytes(pArchive, fp, identBuf, kNufileIDLen); + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed reading BNY wrapper"); + goto bail; + } + if (memcmp(identBuf, kNuBinary2ID, sizeof(kNuBinary2ID)) != 0) { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, kNuErrNone,"Didn't find Binary II wrapper"); + goto bail; + } + + /* archiveLen includes the SEA wrapper, if any, but excludes junk */ + archiveLen = pArchive->newMasterHeader.mhMasterEOF + + (pArchive->headerOffset - pArchive->junkOffset) - + kNuBinary2BlockSize; + archiveLen512 = (archiveLen + 511) / 512; + + err = Nu_FSeek(fp, kNuBNYFileSizeLo - kNufileIDLen, SEEK_CUR); + BailError(err); + Nu_WriteTwo(pArchive, fp, (ushort)(archiveLen512 & 0xffff)); + + err = Nu_FSeek(fp, kNuBNYFileSizeHi - (kNuBNYFileSizeLo+2), SEEK_CUR); + BailError(err); + Nu_WriteTwo(pArchive, fp, (ushort)(archiveLen512 >> 16)); + + err = Nu_FSeek(fp, kNuBNYEOFLo - (kNuBNYFileSizeHi+2), SEEK_CUR); + BailError(err); + Nu_WriteTwo(pArchive, fp, (ushort)(archiveLen & 0xffff)); + Nu_WriteOne(pArchive, fp, (uchar)((archiveLen >> 16) & 0xff)); + + err = Nu_FSeek(fp, kNuBNYEOFHi - (kNuBNYEOFLo+3), SEEK_CUR); + BailError(err); + Nu_WriteOne(pArchive, fp, (uchar)(archiveLen >> 24)); + + err = Nu_FSeek(fp, kNuBNYDiskSpace - (kNuBNYEOFHi+1), SEEK_CUR); + BailError(err); + Nu_WriteFour(pArchive, fp, archiveLen512); + + /* probably ought to update "modified when" date/time field */ + + /* seek just past end of BNY wrapper */ + err = Nu_FSeek(fp, kNuBinary2BlockSize - (kNuBNYDiskSpace+4), SEEK_CUR); + BailError(err); + + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed updating Binary II wrapper"); + goto bail; + } + } + + if (hasSea) { + /* sanity check - make sure it's SEA */ + Nu_ReadBytes(pArchive, fp, identBuf, kNufileIDLen); + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed reading SEA wrapper"); + goto bail; + } + if (memcmp(identBuf, kNuSHKSEAID, sizeof(kNuSHKSEAID)) != 0) { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, kNuErrNone, "Didn't find SEA wrapper"); + goto bail; + } + + archiveLen = pArchive->newMasterHeader.mhMasterEOF; + + err = Nu_FSeek(fp, kNuSEAFunkySize - kNufileIDLen, SEEK_CUR); + BailError(err); + Nu_WriteFour(pArchive, fp, archiveLen + kNuSEAFunkyAdjust); + + err = Nu_FSeek(fp, kNuSEALength1 - (kNuSEAFunkySize+4), SEEK_CUR); + BailError(err); + Nu_WriteTwo(pArchive, fp, (ushort)archiveLen); + + err = Nu_FSeek(fp, kNuSEALength2 - (kNuSEALength1+2), SEEK_CUR); + BailError(err); + Nu_WriteTwo(pArchive, fp, (ushort)archiveLen); + + /* seek past end of SEA wrapper */ + err = Nu_FSeek(fp, kNuSEAOffset - (kNuSEALength2+2), SEEK_CUR); + BailError(err); + + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed updating SEA wrapper"); + goto bail; + } + } + +bail: + return kNuErrNone; +} + + +/* + * Adjust wrapper-induced padding on the archive. + * + * GS/ShrinkIt v1.1 does some peculiar things with SEA (Self-Extracting + * Archive) files. For no apparent reason, it always adds one extra 00 + * byte to the end. When you combine SEA and BXY to make BSE, it will + * leave that extra byte inside the BXY 128-byte padding area, UNLESS + * the archive itself happens to be exactly 128 bytes, in which case + * it throws the pad byte onto the end -- resulting in an archive that + * isn't an exact multiple of 128. + * + * I've chosen to emulate the 1-byte padding "feature" of GSHK, but I'm + * not going to try to emulate the quirky behavior described above. + * + * The SEA pad byte is added first, and then the 128-byte BXY padding + * is considered. In the odd case described above, the file would be + * 127 bytes larger with nufxlib than it is with GSHK. This shouldn't + * require additional disk space to be used, assuming a filesystem block + * size of at least 128 bytes. + */ +NuError +Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp) +{ + NuError err = kNuErrNone; + Boolean hasBinary2, hasSea; + + hasBinary2 = hasSea = false; + + switch (pArchive->archiveType) { + case kNuArchiveNuFX: + goto bail; + case kNuArchiveNuFXInBNY: + hasBinary2 = true; + break; + case kNuArchiveNuFXSelfEx: + hasSea = true; + break; + case kNuArchiveNuFXSelfExInBNY: + hasBinary2 = hasSea = true; + break; + default: + if (pArchive->headerOffset != 0 && + pArchive->headerOffset != pArchive->junkOffset) + { + Nu_ReportError(NU_BLOB, kNuErrNone, "Can't check the padding??"); + err = kNuErrInternal; + goto bail; + } else + goto bail; + } + + err = Nu_FSeek(fp, 0, SEEK_END); + BailError(err); + + if (hasSea && pArchive->valMimicSHK) { + /* throw on a single pad byte, for no apparent reason whatsoever */ + Nu_WriteOne(pArchive, fp, 0); + } + + if (hasBinary2) { + /* pad out to the next 128-byte boundary */ + long curOffset; + + err = Nu_FTell(fp, &curOffset); + BailError(err); + curOffset -= pArchive->junkOffset; /* don't factor junk into account */ + + DBUG(("+++ BNY needs %ld bytes of padding\n", curOffset & 0x7f)); + if (curOffset & 0x7f) { + int i; + + for (i = kNuBinary2BlockSize - (curOffset & 0x7f); i > 0; i--) + Nu_WriteOne(pArchive, fp, 0); + } + } + + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed updating wrapper padding"); + goto bail; + } + +bail: + return err; +} + + +/* + * =========================================================================== + * Open an archive + * =========================================================================== + */ + +/* + * Read the master header from the archive file. + * + * This also handles skipping the first 128 bytes of a .BXY file and the + * front part of a self-extracting GSHK archive. + * + * We try to provide helpful messages about things that aren't archives, + * but try to stay silent about files that are other types of archives. + * That way, if the application is trying a series of libraries to find + * one that will accept the file, we don't generate spurious complaints. + * + * Since there's a fair possibility that whoever is opening this file is + * also interested in related formats, we try to return a meaningful error + * code for stuff we recognize (especially Binary II). + * + * If at first we don't succeed, we keep trying further along until we + * find something we recognize. We don't want to just scan for the + * NuFile ID, because that might prevent this from working properly with + * SEA archives which push the NuFX start out about 12K. We also wouldn't + * be able to update the BNY/SEA wrappers correctly. So, we inch our way + * along until we find something we recognize or get bored. + * + * On exit, the stream will be positioned just past the master header. + */ +static NuError +Nu_ReadMasterHeader(NuArchive* pArchive) +{ + NuError err; + ushort crc; + FILE* fp; + NuMasterHeader* pHeader; + Boolean isBinary2 = false; + Boolean isSea = false; + + Assert(pArchive != nil); + + fp = pArchive->archiveFp; /* saves typing */ + pHeader = &pArchive->masterHeader; + + pArchive->junkOffset = 0; + +retry: + pArchive->headerOffset = pArchive->junkOffset; + Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); + /* may have read fewer than kNufileIDLen; that's okay */ + + if (memcmp(pHeader->mhNufileID, kNuBinary2ID, sizeof(kNuBinary2ID)) == 0) + { + int count; + + /* looks like a Binary II archive, might be BXY or BSE; seek forward */ + err = Nu_SeekArchive(pArchive, fp, kNuBNYFilesToFollow - kNufileIDLen, + SEEK_CUR); + if (err != kNuErrNone) { + err = kNuErrNotNuFX; + /* probably too short to be BNY, so go ahead and whine */ + Nu_ReportError(NU_BLOB, kNuErrNone, + "Looks like a truncated Binary II archive?"); + goto bail; + } + + /* + * Check "files to follow", so we can be sure this isn't a BNY that + * just happened to have a .SHK as the first file. If it is, then + * any updates to the archive will trash the rest of the BNY files. + */ + count = Nu_ReadOne(pArchive, fp); + if (count != 0) { + err = kNuErrIsBinary2; + /*Nu_ReportError(NU_BLOB, kNuErrNone, + "This is a Binary II archive with %d files in it", count+1);*/ + DBUG(("This is a Binary II archive with %d files in it\n",count+1)); + goto bail; + } + + /* that was last item in BNY header, no need to seek */ + Assert(kNuBNYFilesToFollow == kNuBinary2BlockSize -1); + + isBinary2 = true; + pArchive->headerOffset += kNuBinary2BlockSize; + Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); + } + if (memcmp(pHeader->mhNufileID, kNuSHKSEAID, sizeof(kNuSHKSEAID)) == 0) + { + /* might be GSHK self-extracting; seek forward */ + err = Nu_SeekArchive(pArchive, fp, kNuSEAOffset - kNufileIDLen, + SEEK_CUR); + if (err != kNuErrNone) { + err = kNuErrNotNuFX; + Nu_ReportError(NU_BLOB, kNuErrNone, + "Looks like GS executable, not NuFX"); + goto bail; + } + + isSea = true; + pArchive->headerOffset += kNuSEAOffset; + Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); + } + + if (memcmp(kNuMasterID, pHeader->mhNufileID, kNufileIDLen) != 0) { + /* + * Doesn't look like a NuFX archive. Scan forward and see if we + * can find the start past some leading junk. MacBinary headers + * and chunks of HTTP seem popular on FTP sites. + */ + if ((pArchive->openMode == kNuOpenRO || + pArchive->openMode == kNuOpenRW) && + pArchive->junkOffset < (long)pArchive->valJunkSkipMax) + { + pArchive->junkOffset++; + DBUG(("+++ scanning from offset %ld\n", pArchive->junkOffset)); + err = Nu_SeekArchive(pArchive, fp, pArchive->junkOffset, SEEK_SET); + BailError(err); + goto retry; + } + + err = kNuErrNotNuFX; + + if (isBinary2) { + err = kNuErrIsBinary2; + /*Nu_ReportError(NU_BLOB, kNuErrNone, + "Looks like Binary II, not NuFX");*/ + DBUG(("Looks like Binary II, not NuFX\n")); + } else if (isSea) + Nu_ReportError(NU_BLOB, kNuErrNone, + "Looks like GS executable, not NuFX"); + else if (Nu_HeaderIOFailed(pArchive, fp) != kNuErrNone) + Nu_ReportError(NU_BLOB, kNuErrNone, + "Couldn't read enough data, not NuFX?"); + else + Nu_ReportError(NU_BLOB, kNuErrNone, + "Not a NuFX archive? Got 0x%02x%02x%02x%02x%02x%02x...", + pHeader->mhNufileID[0], pHeader->mhNufileID[1], + pHeader->mhNufileID[2], pHeader->mhNufileID[3], + pHeader->mhNufileID[4], pHeader->mhNufileID[5]); + goto bail; + } + + if (pArchive->junkOffset != 0) { + DBUG(("+++ found apparent start of archive at offset %ld\n", + pArchive->junkOffset)); + } + + crc = 0; + pHeader->mhMasterCRC = Nu_ReadTwo(pArchive, fp); + pHeader->mhTotalRecords = Nu_ReadFourC(pArchive, fp, &crc); + pHeader->mhArchiveCreateWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); + pHeader->mhArchiveModWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); + pHeader->mhMasterVersion = Nu_ReadTwoC(pArchive, fp, &crc); + Nu_ReadBytesC(pArchive, fp, pHeader->mhReserved1, + kNufileMasterReserved1Len, &crc); + pHeader->mhMasterEOF = Nu_ReadFourC(pArchive, fp, &crc); + Nu_ReadBytesC(pArchive, fp, pHeader->mhReserved2, + kNufileMasterReserved2Len, &crc); + + /* check for errors in any of the above reads */ + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed reading master header"); + goto bail; + } + if (pHeader->mhMasterVersion > kNuMaxMHVersion) { + err = kNuErrBadMHVersion; + Nu_ReportError(NU_BLOB, err, "Bad Master Header version %u", + pHeader->mhMasterVersion); + goto bail; + } + + /* compare the CRC */ + if (!pArchive->valIgnoreCRC && crc != pHeader->mhMasterCRC) { + if (!Nu_ShouldIgnoreBadCRC(pArchive, nil, kNuErrBadMHCRC)) { + err = kNuErrBadMHCRC; + Nu_ReportError(NU_BLOB, err, "Stored MH CRC=0x%04x, calc=0x%04x", + pHeader->mhMasterCRC, crc); + goto bail; + } + } + + /* + * Check for an unusual condition. GS/ShrinkIt appears to update + * the archive structure in the disk file periodically as it writes, + * so it's possible to get an apparently complete archive (with + * correct CRCs in the master and record headers!) that is actually + * only partially written. I did this by accident when archiving a + * 3.5" disk across a slow AppleTalk network. The only obvious + * indication of brain-damage, until you try to unpack the archive, + * seems to be a bogus MasterEOF==48. + * + * Matthew Fischer found some archives that exhibit MasterEOF==0 + * but are otherwise functional, suggesting that there might be a + * version of ShrinkIt that created these without reporting an error. + * One such archive was a disk image with no filename entry, suggesting + * that it was created by an early version of P8 ShrinkIt. + * + * So, we only fail if the EOF equals 48. + */ + if (pHeader->mhMasterEOF == kNuMasterHeaderSize) { + err = kNuErrNoRecords; + Nu_ReportError(NU_BLOB, err, + "Master EOF is %ld, archive is probably truncated", + pHeader->mhMasterEOF); + goto bail; + } + + /* + * Set up a few things in the archive structure on our way out. + */ + if (isBinary2) { + if (isSea) + pArchive->archiveType = kNuArchiveNuFXSelfExInBNY; + else + pArchive->archiveType = kNuArchiveNuFXInBNY; + } else { + if (isSea) + pArchive->archiveType = kNuArchiveNuFXSelfEx; + else + pArchive->archiveType = kNuArchiveNuFX; + } + + if (isSea || isBinary2) { + DBUG(("--- Archive isSea=%d isBinary2=%d type=%d\n", + isSea, isBinary2, pArchive->archiveType)); + } + + /*pArchive->origNumRecords = pHeader->mhTotalRecords;*/ + pArchive->currentOffset = pArchive->headerOffset + kNuMasterHeaderSize; + + /*DBUG(("--- GOT: records=%ld, vers=%d, EOF=%ld, type=%d, hdrOffset=%ld\n", + pHeader->mhTotalRecords, pHeader->mhMasterVersion, + pHeader->mhMasterEOF, pArchive->archiveType, pArchive->headerOffset));*/ + + pHeader->isValid = true; + +bail: + return err; +} + + +/* + * Prepare the NuArchive and NuMasterHeader structures for use with a + * newly-created archive. + */ +static void +Nu_InitNewArchive(NuArchive* pArchive) +{ + NuMasterHeader* pHeader; + + Assert(pArchive != nil); + + pHeader = &pArchive->masterHeader; + + memcpy(pHeader->mhNufileID, kNuMasterID, kNufileIDLen); + /*pHeader->mhMasterCRC*/ + pHeader->mhTotalRecords = 0; + Nu_SetCurrentDateTime(&pHeader->mhArchiveCreateWhen); + /*pHeader->mhArchiveModWhen*/ + pHeader->mhMasterVersion = kNuOurMHVersion; + /*pHeader->mhReserved1*/ + pHeader->mhMasterEOF = kNuMasterHeaderSize; + /*pHeader->mhReserved2*/ + + pHeader->isValid = true; + + /* no need to use a temp file for a newly-created archive */ + pArchive->valModifyOrig = true; +} + + +/* + * Open an archive in streaming read-only mode. + */ +NuError +Nu_StreamOpenRO(FILE* infp, NuArchive** ppArchive) +{ + NuError err; + NuArchive* pArchive = nil; + + Assert(infp != nil); + Assert(ppArchive != nil); + + err = Nu_NuArchiveNew(ppArchive); + if (err != kNuErrNone) + goto bail; + pArchive = *ppArchive; + + pArchive->openMode = kNuOpenStreamingRO; + pArchive->archiveFp = infp; + pArchive->archivePathname = strdup("(stream)"); + + err = Nu_ReadMasterHeader(pArchive); + BailError(err); + +bail: + if (err != kNuErrNone) { + if (pArchive != nil) + (void) Nu_NuArchiveFree(pArchive); + *ppArchive = nil; + } + return err; +} + + +/* + * Open an archive in non-streaming read-only mode. + */ +NuError +Nu_OpenRO(const char* archivePathname, NuArchive** ppArchive) +{ + NuError err; + NuArchive* pArchive = nil; + FILE* fp = nil; + + if (archivePathname == nil || !strlen(archivePathname) || ppArchive == nil) + return kNuErrInvalidArg; + + *ppArchive = nil; + + fp = fopen(archivePathname, kNuFileOpenReadOnly); + if (fp == nil) { + Nu_ReportError(NU_BLOB, errno, "Unable to open '%s'", archivePathname); + err = kNuErrFileOpen; + goto bail; + } + + err = Nu_NuArchiveNew(ppArchive); + if (err != kNuErrNone) + goto bail; + pArchive = *ppArchive; + + pArchive->openMode = kNuOpenRO; + pArchive->archiveFp = fp; + fp = nil; + pArchive->archivePathname = strdup(archivePathname); + + err = Nu_ReadMasterHeader(pArchive); + BailError(err); + +bail: + if (err != kNuErrNone) { + if (pArchive != nil) { + (void) Nu_CloseAndFree(pArchive); + *ppArchive = nil; + } + if (fp != nil) + fclose(fp); + } + return err; +} + + +/* + * Open a temp file. If "fileName" contains six Xs ("XXXXXX"), it will + * be treated as a mktemp-style template, and modified before use. + * + * Thought for the day: consider using Win32 SetFileAttributes() to make + * temp files hidden. We will need to un-hide it before rolling it over. + */ +static NuError +Nu_OpenTempFile(char* fileName, FILE** pFp) +{ + NuArchive* pArchive = nil; /* dummy for NU_BLOB */ + NuError err = kNuErrNone; + int len; + + /* + * If this is a mktemp-style template, use mktemp or mkstemp to fill in + * the blanks. + * + * BUG: not all implementations of mktemp actually generate a unique + * name. We probably need to do probing here. Some BSD variants like + * to complain about mktemp, since it's generally a bad way to do + * things. + */ + len = strlen(fileName); + if (len > 6 && strcmp(fileName + len - 6, "XXXXXX") == 0) { +#if defined(HAVE_MKSTEMP) && defined(HAVE_FDOPEN) + int fd; + + DBUG(("+++ Using mkstemp\n")); + + /* this modifies the template *and* opens the file */ + fd = mkstemp(fileName); + if (fd < 0) { + err = errno ? errno : kNuErrFileOpen; + Nu_ReportError(NU_BLOB, kNuErrNone, "mkstemp failed on '%s'", + fileName); + goto bail; + } + + DBUG(("--- Fd-opening temp file '%s'\n", fileName)); + *pFp = fdopen(fd, kNuFileOpenReadWriteCreat); + if (*pFp == nil) { + close(fd); + err = errno ? errno : kNuErrFileOpen; + goto bail; + } + + /* file is open, we're done */ + goto bail; + +#else + char* result; + + DBUG(("+++ Using mktemp\n")); + result = mktemp(fileName); + if (result == nil) { + Nu_ReportError(NU_BLOB, kNuErrNone, "mktemp failed on '%s'", + fileName); + err = kNuErrInternal; + goto bail; + } + + /* now open the filename as usual */ +#endif + } + + DBUG(("--- Opening temp file '%s'\n", fileName)); + +#if defined(HAVE_FDOPEN) + { + int fd; + + fd = open(fileName, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0600); + if (fd < 0) { + err = errno ? errno : kNuErrFileOpen; + goto bail; + } + + *pFp = fdopen(fd, kNuFileOpenReadWriteCreat); + if (*pFp == nil) { + close(fd); + err = errno ? errno : kNuErrFileOpen; + goto bail; + } + } +#else + /* (not sure how portable "access" is... I think it's POSIX) */ + if (access(fileName, F_OK) == 0) { + err = kNuErrFileExists; + goto bail; + } + + *pFp = fopen(fileName, kNuFileOpenReadWriteCreat); + if (*pFp == nil) { + err = errno ? errno : kNuErrFileOpen; + goto bail; + } +#endif + + +bail: + return err; +} + +/* + * Open an archive in read-write mode, optionally creating it if it doesn't + * exist. + */ +NuError +Nu_OpenRW(const char* archivePathname, const char* tmpPathname, ulong flags, + NuArchive** ppArchive) +{ + NuError err; + FILE* fp = nil; + FILE* tmpFp = nil; + NuArchive* pArchive = nil; + char* tmpPathDup = nil; + Boolean archiveExists; + Boolean newlyCreated; + + if (archivePathname == nil || !strlen(archivePathname) || + tmpPathname == nil || !strlen(tmpPathname) || ppArchive == nil || + (flags & ~(kNuOpenCreat|kNuOpenExcl)) != 0) + { + return kNuErrInvalidArg; + } + + archiveExists = (access(archivePathname, F_OK) == 0); + + /* + * Open or create archive file. + */ + if (archiveExists) { + if ((flags & kNuOpenCreat) && (flags & kNuOpenExcl)) { + err = kNuErrFileExists; + Nu_ReportError(NU_BLOB, err, "File '%s' exists", archivePathname); + goto bail; + } + fp = fopen(archivePathname, kNuFileOpenReadWrite); + newlyCreated = false; + } else { + if (!(flags & kNuOpenCreat)) { + err = kNuErrFileNotFound; + Nu_ReportError(NU_BLOB, err, "File '%s' not found",archivePathname); + goto bail; + } + fp = fopen(archivePathname, kNuFileOpenReadWriteCreat); + newlyCreated = true; + } + + if (fp == nil) { + if (errno == EACCES) + err = kNuErrFileAccessDenied; + else + err = kNuErrFileOpen; + Nu_ReportError(NU_BLOB, errno, "Unable to open '%s'", archivePathname); + goto bail; + } + + /* + * Treat zero-length files as newly-created archives. + */ + if (archiveExists && !newlyCreated) { + long length; + + err = Nu_GetFileLength(nil, fp, &length); + BailError(err); + + if (!length) { + DBUG(("--- treating zero-length file as newly created archive\n")); + newlyCreated = true; + } + } + + /* + * Create a temp file. We don't need one for a newly-created archive, + * at least not right away. It's possible the caller could add some + * files, flush the changes, and then want to delete them without + * closing and reopening the archive. + * + * So, create a temp file whether we think we need one or not. Won't + * do any harm, and might save us some troubles later. + */ + tmpPathDup = strdup(tmpPathname); + BailNil(tmpPathDup); + err = Nu_OpenTempFile(tmpPathDup, &tmpFp); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed opening temp file '%s'", + tmpPathname); + goto bail; + } + + err = Nu_NuArchiveNew(ppArchive); + if (err != kNuErrNone) + goto bail; + pArchive = *ppArchive; + + pArchive->openMode = kNuOpenRW; + pArchive->newlyCreated = newlyCreated; + pArchive->archivePathname = strdup(archivePathname); + pArchive->archiveFp = fp; + fp = nil; + pArchive->tmpFp = tmpFp; + tmpFp = nil; + pArchive->tmpPathname = tmpPathDup; + tmpPathDup = nil; + + if (archiveExists && !newlyCreated) { + err = Nu_ReadMasterHeader(pArchive); + BailError(err); + } else { + Nu_InitNewArchive(pArchive); + } + +bail: + if (err != kNuErrNone) { + if (pArchive != nil) { + (void) Nu_CloseAndFree(pArchive); + *ppArchive = nil; + } + if (fp != nil) + fclose(fp); + if (tmpFp != nil) + fclose(tmpFp); + if (tmpPathDup != nil) + Nu_Free(pArchive, tmpPathDup); + } + return err; +} + + +/* + * =========================================================================== + * Update an archive + * =========================================================================== + */ + +/* + * Write the NuFX master header at the current offset. + */ +NuError +Nu_WriteMasterHeader(NuArchive* pArchive, FILE* fp, + NuMasterHeader* pHeader) +{ + NuError err; + long crcOffset; + ushort crc; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pHeader != nil); + Assert(pHeader->isValid); + Assert(pHeader->mhMasterVersion == kNuOurMHVersion); + + crc = 0; + + Nu_WriteBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); + err = Nu_FTell(fp, &crcOffset); + BailError(err); + Nu_WriteTwo(pArchive, fp, 0); + Nu_WriteFourC(pArchive, fp, pHeader->mhTotalRecords, &crc); + Nu_WriteDateTimeC(pArchive, fp, pHeader->mhArchiveCreateWhen, &crc); + Nu_WriteDateTimeC(pArchive, fp, pHeader->mhArchiveModWhen, &crc); + Nu_WriteTwoC(pArchive, fp, pHeader->mhMasterVersion, &crc); + Nu_WriteBytesC(pArchive, fp, pHeader->mhReserved1, + kNufileMasterReserved1Len, &crc); + Nu_WriteFourC(pArchive, fp, pHeader->mhMasterEOF, &crc); + Nu_WriteBytesC(pArchive, fp, pHeader->mhReserved2, + kNufileMasterReserved2Len, &crc); + + /* go back and write the CRC (sadly, the seek will flush the stdio buf) */ + pHeader->mhMasterCRC = crc; + err = Nu_FSeek(fp, crcOffset, SEEK_SET); + BailError(err); + Nu_WriteTwo(pArchive, fp, pHeader->mhMasterCRC); + + /* check for errors in any of the above writes */ + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed writing master header"); + goto bail; + } + + DBUG(("--- Master header written successfully at %ld (crc=0x%04x)\n", + crcOffset - kNufileIDLen, crc)); + +bail: + return err; +} + + +/* + * =========================================================================== + * Close an archive + * =========================================================================== + */ + +/* + * Close all open files, and free the memory associated with the structure. + * + * If it's a brand-new archive, and we didn't add anything to it, then we + * want to remove the stub archive file. + */ +static void +Nu_CloseAndFree(NuArchive* pArchive) +{ + if (pArchive->archiveFp != nil) { + DBUG(("--- Closing archive\n")); + fclose(pArchive->archiveFp); + pArchive->archiveFp = nil; + } + + if (pArchive->tmpFp != nil) { + DBUG(("--- Closing and removing temp file\n")); + fclose(pArchive->tmpFp); + pArchive->tmpFp = nil; + Assert(pArchive->tmpPathname != nil); + if (remove(pArchive->tmpPathname) != 0) { + Nu_ReportError(NU_BLOB, errno, "Unable to remove temp file '%s'", + pArchive->tmpPathname); + /* keep going */ + } + } + + if (pArchive->newlyCreated && Nu_RecordSet_IsEmpty(&pArchive->origRecordSet)) + { + DBUG(("--- Newly-created archive unmodified; removing it\n")); + if (remove(pArchive->archivePathname) != 0) { + Nu_ReportError(NU_BLOB, errno, "Unable to remove archive file '%s'", + pArchive->archivePathname); + } + } + + Nu_NuArchiveFree(pArchive); +} + +/* + * Flush pending changes to the archive, then close it. + */ +NuError +Nu_Close(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + long flushStatus; + + Assert(pArchive != nil); + + if (!Nu_IsReadOnly(pArchive)) + err = Nu_Flush(pArchive, &flushStatus); + if (err == kNuErrNone) + Nu_CloseAndFree(pArchive); + else { + DBUG(("--- Close NuFlush status was 0x%4lx\n", flushStatus)); + } + + if (err != kNuErrNone) { + DBUG(("--- Nu_Close returning error %d\n", err)); + } + return err; +} + + +/* + * =========================================================================== + * Delete and replace an archive + * =========================================================================== + */ + +/* + * Delete the archive file, which should already have been closed. + */ +NuError +Nu_DeleteArchiveFile(NuArchive* pArchive) +{ + Assert(pArchive != nil); + Assert(pArchive->archiveFp == nil); + Assert(pArchive->archivePathname != nil); + + return Nu_DeleteFile(pArchive->archivePathname); +} + +/* + * Rename the temp file on top of the original archive. The temp file + * should be closed, and the archive file should be deleted. + */ +NuError +Nu_RenameTempToArchive(NuArchive* pArchive) +{ + Assert(pArchive != nil); + Assert(pArchive->archiveFp == nil); + Assert(pArchive->tmpFp == nil); + Assert(pArchive->archivePathname != nil); + Assert(pArchive->tmpPathname != nil); + + return Nu_RenameFile(pArchive->tmpPathname, pArchive->archivePathname); +} + diff --git a/nufxlib/ArchiveIO.c b/nufxlib/ArchiveIO.c new file mode 100644 index 0000000..5f71486 --- /dev/null +++ b/nufxlib/ArchiveIO.c @@ -0,0 +1,425 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Functions for reading from and writing to the archive. These are + * specialized functions that deal with byte ordering and CRC computation. + * The functions associated with reading from an archive work equally well + * with streaming archives. + */ +#include "NufxLibPriv.h" + + +/* this makes valgrind and purify happy, at some tiny cost in speed */ +#define CLEAN_INIT =0 +/*#define CLEAN_INIT */ + + +/* + * =========================================================================== + * Read and write + * =========================================================================== + */ + +/* + * Read one byte, optionally computing a CRC. + */ +uchar +Nu_ReadOneC(NuArchive* pArchive, FILE* fp, ushort* pCrc) +{ + int ic; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + + return (uchar) ic; +} + +uchar +Nu_ReadOne(NuArchive* pArchive, FILE* fp) +{ + ushort dummyCrc CLEAN_INIT; + return Nu_ReadOneC(pArchive, fp, &dummyCrc); +} + +/* + * Write one byte, optionally computing a CRC. + */ +void +Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uchar val, ushort* pCrc) +{ + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + putc(val, fp); +} + +void +Nu_WriteOne(NuArchive* pArchive, FILE* fp, uchar val) +{ + ushort dummyCrc CLEAN_INIT; + Nu_WriteOneC(pArchive, fp, val, &dummyCrc); +} + + +/* + * Read two little-endian bytes, optionally computing a CRC. + */ +ushort +Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, ushort* pCrc) +{ + int ic1, ic2; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic1 = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc); + ic2 = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc); + + return ic1 | ic2 << 8; +} + +ushort +Nu_ReadTwo(NuArchive* pArchive, FILE* fp) +{ + ushort dummyCrc CLEAN_INIT; + return Nu_ReadTwoC(pArchive, fp, &dummyCrc); +} + + +/* + * Write two little-endian bytes, optionally computing a CRC. + */ +void +Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, ushort val, ushort* pCrc) +{ + int ic1, ic2; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic1 = val & 0xff; + *pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc); + ic2 = val >> 8; + *pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc); + + putc(ic1, fp); + putc(ic2, fp); +} + +void +Nu_WriteTwo(NuArchive* pArchive, FILE* fp, ushort val) +{ + ushort dummyCrc CLEAN_INIT; + Nu_WriteTwoC(pArchive, fp, val, &dummyCrc); +} + + +/* + * Read four little-endian bytes, optionally computing a CRC. + */ +ulong +Nu_ReadFourC(NuArchive* pArchive, FILE* fp, ushort* pCrc) +{ + int ic1, ic2, ic3, ic4; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic1 = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc); + ic2 = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc); + ic3 = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic3, *pCrc); + ic4 = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic4, *pCrc); + + return ic1 | ic2 << 8 | (ulong)ic3 << 16 | (ulong)ic4 << 24; +} + +ulong +Nu_ReadFour(NuArchive* pArchive, FILE* fp) +{ + ushort dummyCrc CLEAN_INIT; + return Nu_ReadFourC(pArchive, fp, &dummyCrc); +} + + +/* + * Write four little-endian bytes, optionally computing a CRC. + */ +void +Nu_WriteFourC(NuArchive* pArchive, FILE* fp, ulong val, ushort* pCrc) +{ + int ic1, ic2, ic3, ic4; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic1 = val & 0xff; + *pCrc = Nu_UpdateCRC16((uchar)ic1, *pCrc); + ic2 = (val >> 8) & 0xff; + *pCrc = Nu_UpdateCRC16((uchar)ic2, *pCrc); + ic3 = (val >> 16) & 0xff; + *pCrc = Nu_UpdateCRC16((uchar)ic3, *pCrc); + ic4 = val >> 24; + *pCrc = Nu_UpdateCRC16((uchar)ic4, *pCrc); + + putc(ic1, fp); + putc(ic2, fp); + putc(ic3, fp); + putc(ic4, fp); +} + +void +Nu_WriteFour(NuArchive* pArchive, FILE* fp, ulong val) +{ + ushort dummyCrc CLEAN_INIT; + Nu_WriteFourC(pArchive, fp, val, &dummyCrc); +} + + +/* + * Read an 8-byte NuFX Date/Time structure. + * + * I've chosen *not* to filter away the Y2K differences between P8 ShrinkIt + * and GS/ShrinkIt. It's easy enough to deal with, and I figure the less + * messing-with, the better. + */ +NuDateTime +Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, ushort* pCrc) +{ + NuDateTime temp; + int ic; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.second = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.minute = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.hour = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.year = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.day = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.month = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.extra = ic; + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + temp.weekDay = ic; + + return temp; +} + +NuDateTime +Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, ushort* pCrc) +{ + ushort dummyCrc CLEAN_INIT; + return Nu_ReadDateTimeC(pArchive, fp, &dummyCrc); +} + + +/* + * Write an 8-byte NuFX Date/Time structure. + */ +void +Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime, + ushort* pCrc) +{ + int ic; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + ic = dateTime.second; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.minute; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.hour; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.year; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.day; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.month; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.extra; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + ic = dateTime.weekDay; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); +} + +void +Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime) +{ + ushort dummyCrc CLEAN_INIT; + Nu_WriteDateTimeC(pArchive, fp, dateTime, &dummyCrc); +} + + +/* + * Read N bytes from the stream, optionally computing a CRC. + */ +void +Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count, + ushort* pCrc) +{ + uchar* buffer = vbuffer; + int ic; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + Assert(buffer != nil); + Assert(count > 0); + + while (count--) { + ic = getc(fp); + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + *buffer++ = ic; + } +} + +void +Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count) +{ + ushort dummyCrc CLEAN_INIT; + Nu_ReadBytesC(pArchive, fp, vbuffer, count, &dummyCrc); +} + + +/* + * Write N bytes to the stream, optionally computing a CRC. + */ +void +Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer, long count, + ushort* pCrc) +{ + const uchar* buffer = vbuffer; + int ic; + + Assert(pArchive != nil); + Assert(fp != nil); + Assert(pCrc != nil); + Assert(buffer != nil); + Assert(count > 0); + + while (count--) { + ic = *buffer++; + *pCrc = Nu_UpdateCRC16((uchar)ic, *pCrc); + putc(ic, fp); + } +} + +void +Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer, long count) +{ + ushort dummyCrc CLEAN_INIT; + Nu_WriteBytesC(pArchive, fp, vbuffer, count, &dummyCrc); +} + + +/* + * =========================================================================== + * General + * =========================================================================== + */ + +/* + * Determine whether the stream completed the last set of operations + * successfully. + */ +NuError +Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp) +{ + if (feof(fp) || ferror(fp)) + return kNuErrFile; + else + return kNuErrNone; +} + + +/* + * Seek around in an archive file. If this is a streaming-mode archive, + * we only allow forward relative seeks, which are emulated with read calls. + * + * The values for "ptrname" are the same as for fseek(). + */ +NuError +Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset, int ptrname) +{ + if (Nu_IsStreaming(pArchive)) { + Assert(ptrname == SEEK_CUR); + Assert(offset >= 0); + + /* OPT: might be faster to fread a chunk at a time */ + while (offset--) + (void) getc(fp); + + if (ferror(fp) || feof(fp)) + return kNuErrFileSeek; + } else { + if (fseek(fp, offset, ptrname) < 0) + return kNuErrFileSeek; + } + + return kNuErrNone; +} + + +/* + * Rewind an archive to the start of NuFX record data. + * + * Note that rewind(3S) resets the error indication, but this doesn't. + */ +NuError +Nu_RewindArchive(NuArchive* pArchive) +{ + Assert(pArchive != nil); + Assert(!Nu_IsStreaming(pArchive)); + + if (Nu_SeekArchive(pArchive, pArchive->archiveFp, + pArchive->headerOffset + kNuMasterHeaderSize, SEEK_SET) != 0) + return kNuErrFileSeek; + + pArchive->currentOffset = pArchive->headerOffset + kNuMasterHeaderSize; + + return kNuErrNone; +} + diff --git a/nufxlib/Bzip2.c b/nufxlib/Bzip2.c new file mode 100644 index 0000000..8fde0b6 --- /dev/null +++ b/nufxlib/Bzip2.c @@ -0,0 +1,299 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Support for the "bzip2" (BTW+Huffman) algorithm, via "libbz2". + * + * This compression format is totally unsupported on the Apple II. This + * is provided primarily for the benefit of Apple II emulators that want + * a better storage format for disk images than SHK+LZW or a ZIP file. + * + * This code was developed and tested with libz2 version 1.0.2. Visit + * http://sources.redhat.com/bzip2/ for more information. + */ +#include "NufxLibPriv.h" + +#ifdef ENABLE_BZIP2 +#include "bzlib.h" + +#define kBZBlockSize 8 /* use 800K blocks */ +#define kBZVerbosity 1 /* library verbosity level (0-4) */ + + +/* + * Alloc and free functions provided to libbz2. + */ +static void* +Nu_bzalloc(void* opaque, int items, int size) +{ + return Nu_Malloc(opaque, items * size); +} +static void +Nu_bzfree(void* opaque, void* address) +{ + Nu_Free(opaque, address); +} + + +/* + * =========================================================================== + * Compression + * =========================================================================== + */ + +/* + * Compress "srcLen" bytes from "pStraw" to "fp". + */ +NuError +Nu_CompressBzip2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + NuError err = kNuErrNone; + bz_stream bzstream; + int bzerr; + uchar* outbuf = nil; + + Assert(pArchive != nil); + Assert(pStraw != nil); + Assert(fp != nil); + Assert(srcLen > 0); + Assert(pDstLen != nil); + Assert(pCrc != nil); + + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + /* allocate a similarly-sized buffer for the output */ + outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); + BailAlloc(outbuf); + + /* + * Initialize the bz2lib stream. + */ + bzstream.bzalloc = Nu_bzalloc; + bzstream.bzfree = Nu_bzfree; + bzstream.opaque = pArchive; + bzstream.next_in = nil; + bzstream.avail_in = 0; + bzstream.next_out = outbuf; + bzstream.avail_out = kNuGenCompBufSize; + + /* fourth arg is "workFactor"; set to zero for default (30) */ + bzerr = BZ2_bzCompressInit(&bzstream, kBZBlockSize, kBZVerbosity, 0); + if (bzerr != BZ_OK) { + err = kNuErrInternal; + if (bzerr == BZ_CONFIG_ERROR) { + Nu_ReportError(NU_BLOB, err, "error configuring bz2lib"); + } else { + Nu_ReportError(NU_BLOB, err, + "call to BZ2_bzCompressInit failed (bzerr=%d)", bzerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + ulong getSize; + int action; + + /* should be able to read a full buffer every time */ + if (bzstream.avail_in == 0 && srcLen) { + getSize = (srcLen > kNuGenCompBufSize) ? kNuGenCompBufSize : srcLen; + DBUG(("+++ reading %ld bytes\n", getSize)); + + err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "bzip2 read failed"); + goto bz_bail; + } + + srcLen -= getSize; + + *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getSize); + + bzstream.next_in = pArchive->compBuf; + bzstream.avail_in = getSize; + } + + if (srcLen == 0) + action = BZ_FINISH; /* tell libbz2 that we're done */ + else + action = BZ_RUN; /* more to come! */ + + bzerr = BZ2_bzCompress(&bzstream, action); + if (bzerr != BZ_RUN_OK && bzerr != BZ_FINISH_OK && bzerr != BZ_STREAM_END) + { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, err, + "libbz2 compress call failed (bzerr=%d)", bzerr); + goto bz_bail; + } + + /* write when we're full or when we're done */ + if (bzstream.avail_out == 0 || + (bzerr == BZ_STREAM_END && bzstream.avail_out != kNuGenCompBufSize)) + { + DBUG(("+++ writing %d bytes\n", + (uchar*)bzstream.next_out - outbuf)); + err = Nu_FWrite(fp, outbuf, (uchar*)bzstream.next_out - outbuf); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "fwrite failed in bzip2"); + goto bz_bail; + } + + bzstream.next_out = outbuf; + bzstream.avail_out = kNuGenCompBufSize; + } + } while (bzerr != BZ_STREAM_END); + + *pDstLen = bzstream.total_out_lo32; + Assert(bzstream.total_out_hi32 == 0); /* no huge files for us */ + +bz_bail: + BZ2_bzCompressEnd(&bzstream); /* free up any allocated structures */ + +bail: + if (outbuf != nil) + free(outbuf); + return err; +} + + +/* + * =========================================================================== + * Expansion + * =========================================================================== + */ + +/* + * Expand from "infp" to "pFunnel". + */ +NuError +Nu_ExpandBzip2(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc) +{ + NuError err = kNuErrNone; + bz_stream bzstream; + int bzerr; + ulong compRemaining; + uchar* outbuf; + + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(infp != nil); + Assert(pFunnel != nil); + + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + /* allocate a similarly-sized buffer for the output */ + outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); + BailAlloc(outbuf); + + compRemaining = pThread->thCompThreadEOF; + + /* + * Initialize the libbz2 stream. + */ + bzstream.bzalloc = Nu_bzalloc; + bzstream.bzfree = Nu_bzfree; + bzstream.opaque = pArchive; + bzstream.next_in = nil; + bzstream.avail_in = 0; + bzstream.next_out = outbuf; + bzstream.avail_out = kNuGenCompBufSize; + + /* third arg is "small" (set nonzero to reduce mem) */ + bzerr = BZ2_bzDecompressInit(&bzstream, kBZVerbosity, 0); + if (bzerr != BZ_OK) { + err = kNuErrInternal; + if (bzerr == BZ_CONFIG_ERROR) { + Nu_ReportError(NU_BLOB, err, "error configuring libbz2"); + } else { + Nu_ReportError(NU_BLOB, err, + "call to BZ2_bzDecompressInit failed (bzerr=%d)", bzerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + ulong getSize; + + /* read as much as we can */ + if (bzstream.avail_in == 0) { + getSize = (compRemaining > kNuGenCompBufSize) ? + kNuGenCompBufSize : compRemaining; + DBUG(("+++ reading %ld bytes (%ld left)\n", getSize, + compRemaining)); + + err = Nu_FRead(infp, pArchive->compBuf, getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "bzip2 read failed"); + goto bz_bail; + } + + compRemaining -= getSize; + + bzstream.next_in = pArchive->compBuf; + bzstream.avail_in = getSize; + } + + /* uncompress the data */ + bzerr = BZ2_bzDecompress(&bzstream); + if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, err, + "libbz2 decompress call failed (bzerr=%d)", bzerr); + goto bz_bail; + } + + /* write every time there's anything (buffer will usually be full) */ + if (bzstream.avail_out != kNuGenCompBufSize) { + DBUG(("+++ writing %d bytes\n",(uchar*)bzstream.next_out - outbuf)); + err = Nu_FunnelWrite(pArchive, pFunnel, outbuf, + (uchar*)bzstream.next_out - outbuf); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "write failed in bzip2"); + goto bz_bail; + } + + if (pCrc != nil) + *pCrc = Nu_CalcCRC16(*pCrc, outbuf, + (uchar*) bzstream.next_out - outbuf); + + bzstream.next_out = outbuf; + bzstream.avail_out = kNuGenCompBufSize; + } + } while (bzerr == BZ_OK); + + Assert(bzerr == BZ_STREAM_END); /* other errors should've been caught */ + + Assert(bzstream.total_out_hi32 == 0); /* no huge files for us */ + + if (bzstream.total_out_lo32 != pThread->actualThreadEOF) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, + "size mismatch on expanded bzip2 file (%d vs %ld)", + bzstream.total_out_lo32, pThread->actualThreadEOF); + goto bz_bail; + } + +bz_bail: + BZ2_bzDecompressEnd(&bzstream); /* free up any allocated structures */ + +bail: + if (outbuf != nil) + free(outbuf); + return err; +} + +#endif /*ENABLE_BZIP2*/ diff --git a/nufxlib/COPYING-LIB b/nufxlib/COPYING-LIB new file mode 100644 index 0000000..d27a925 --- /dev/null +++ b/nufxlib/COPYING-LIB @@ -0,0 +1,29 @@ +Copyright (C) 2007, Andy McFadden. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the copyright holder nor the names of project + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/nufxlib/ChangeLog.txt b/nufxlib/ChangeLog.txt new file mode 100644 index 0000000..b932e41 --- /dev/null +++ b/nufxlib/ChangeLog.txt @@ -0,0 +1,288 @@ +2014/10/30 ***** v2.2.2 shipped ***** + +2014/10/28 fadden + - Switched from CVS on sourceforge to github. + - Updated configure scripts and makefiles. + +2007/02/19 ***** v2.2.0 shipped ***** + +2007/02/19 fadden + - Auto-detect and handle "bad Mac" archives. + - Switched from LGPL to BSD license. + +2006/12/02 fadden + - Check for overrun when unpacking RLE. + +2006/02/18 ***** v2.1.1 shipped ***** + +2006/02/18 fadden + - Correct a wayward assert. (Changing the filetype of a file from an + HFS disk, which has zero-length data fork, falsely triggered the + assert.) + +2005/09/17 ***** v2.1.0 shipped ***** + +2005/09/17 fadden + - Added "kNuValIgnoreLZW2Len" flag, which enables NuLib2 to handle + archives created by an unknown but badly broken program. + - Fixed build for gcc v4.0. + +2004/10/11 ***** v2.0.3 shipped ***** + +2004/09/25 fadden + - Fixed: attempting to add files after deleting *all* entries in an + archive would fail. + - Removed use of a "ushort" from NufxLib.h. + +2004/09/20 fadden + - Corrected behavior after flush when original archive can't be + deleted. + +2004/09/09 fadden + - Added header offset and junk offset to NuGetAttr. + +2004/08/22 fadden + - Fixed obscure bug when recompressing a GSHK-added zero-length file + when "fake threads" is enabled. + +2004/03/10 ***** v2.0.2 shipped ***** + +2004/03/09 fadden + - Set access permissions based on umask when extracting a "locked" + file. My thanks to Matthew Fischer for sending a patch. + - Reject archives with a MasterEOF == 48, not <= 48. There are + some otherwise valid archives created by an old version of ShrinkIt + that have MasterEOF==0. + +2003/10/16 ***** v2.0.1 shipped ***** + +2003/10/16 fadden + - Added workaround for bad HFS option lists created by GSHK. + - Added junk-skipping feature. Up to 1024 bytes of crud (e.g. + MacBinary headers or HTTP remnants) will be searched for evidence + of an archive. + +2003/06/19 sheppy + - Added support for resource forks and file and aux types when built + for Mac OS X. + +2003/03/18 ***** v2.0.0 shipped ***** + +2003/03/10 fadden + - Added support for automatic high-ASCII text stripping. + +2003/02/23 fadden + - Added test-twirl to samples. + +2003/02/22 fadden + - Turn off EOL conversion when extracting disk images. + - Added NuTestRecord(). + +2003/02/18 fadden + - Added "original pathname" fields to NuFileDetails and NuErrorStatus. + - Changed callback setters to return NuCallback instead of NuError. + - Switched to case-sensitive filename comparisons. + +2003/02/08 fadden + - Upped version to v2.0.0. + - Changed DataSource API. Removed "doClose" and added an optional + callback function that handles releasing of resources. Necessary + to make Win32 DLLs work right with unsuspecting apps. + - Changed DataSource "copy" function to use refcounting. Still + not quite right, but it'll do for now. Memory leaks in DataSource + handling appear to be fixed. (I love valgrind.) + +2003/01/10 fadden + - Added version numbers to header. + - Added kNuValueMaskThreadless to control handling of "threadless" + records. Now records without threads can be silently "fixed" so + the application does need to handle them specially. + +2002/12/06 fadden + - Made changes to allow NufxLib to be built as a Win32 DLL. + +2002/10/20 ***** v1.1.0 shipped ***** + +2002/10/10 fadden + - changed behavior so that deleting all records is allowed + +2002/10/09 fadden + - added support for "bzip2" compression via libbz2 + - added ability to selectively disable compression methods + - added "-m" flag to samples/launder so you can specify compression + +2002/09/30 fadden + - added support for "deflate" compression via zlib + +2002/09/27 fadden + - added support for 12-bit and 16-bit LZC (UNIX compress) + +2002/09/26 fadden + - added support for SQueezed files (both compress and expand) + +2002/09/23 fadden + - ran the code through valgrind; found and fixed some minor bugs + +2002/09/20 fadden + - pulled the sources out and started fiddling with them again + - changed hard tabs to spaces + +2000/05/22 ***** v1.0.1 shipped ***** + +2000/05/22 fadden + - added workaround for buggy 140K DOS3.3 GSHK images + +2000/05/18 ***** v1.0.0 shipped ***** + +2000/05/18 fadden + - updated version information to indicate final release + +2000/03/25 ***** v0.6.1 shipped ***** + +2000/03/25 fadden + - Sheppy says Mac OS X PPC v1.02 and v1.2 work with minor SysDefs tweak + +2000/03/05 ***** v0.6.0 (beta) shipped ***** + +2000/03/05 fadden + - modified NuOpenRW to call mktemp or mkstemp if tmpPath looks like + a template + - removed DEBUG_MSGS from default CFLAGS + - updated version information to indicate beta release + +2000/02/24 ***** v0.5.1 shipped ***** + +2000/02/20 changes from Scott Blackman + - portability fixes for DJGPP under Win95 + +2000/02/17 changes from Devin Reade + - portability fixes for BSD, AIX, and others + +2000/02/09 ***** v0.5.0 (alpha) shipped ***** + +2000/02/08 fadden + - tweaked the BeOS/PPC config around a little + - deleted some commas to make "gcc -pendantic" happy + +2000/02/06 fadden + - include @CFLAGS@ in case somebody wants to override them + +2000/02/06 ***** v0.4.0b shipped ***** + +2000/02/06 fadden + - added "install-shared" make target + - portability fixes for HP/UX + - configure.in test for presence of snprintf/vsnprintf declarations + +2000/02/06 ***** v0.4.0a shipped ***** + +2000/02/06 fadden + - massaged configure.in for BeOS, and added some type casts for mwerks + +2000/02/06 ***** v0.4.0 shipped ***** + +2000/02/06 fadden + - added value range checking to Nu_SetValue + +2000/02/05 fadden + - finished "test-basic" + - added an "install" target to copy libnufx and NufxLib.h + - added "mkinstalldirs" + - fixed a memory leak in NuTest + - made several implicit typecasts explicit for Visual C++'s benefit + - renamed MiscStuff's replacement function to "Nu_function" + - use "rb" or "wb" as fopen arg in sample code for Win32 + +2000/02/04 fadden + - wrote a fair piece of "test-basic" + - added "stickyErr" to "toBuffer" data sink so we can catch overruns + +2000/02/02 fadden + - minor changes to get it working under Win32 (Visual C++ 6.0) + - added --enable-dmalloc to configuration + - instead of constantly allocating 16K buffers, use pArchive->compBuf + - ignore DataSink convertEOL value when doExpand is false + +2000/02/01 fadden + - added system-specific PATH_SEP define for samples (imgconv, exerciser) + - set the pathname in ErrorStatus for CRC failures + +2000/01/31 fadden + - fixed a typo causing zero-byte GSHK-damaged files to report CRC errors + - added support for DOS-ordered 2MG images to "imgconv" + +2000/01/29 ***** v0.3.0 shipped ***** + +2000/01/29 fadden + - renamed "tests" to "samples" + - changed library version to x.y.z format (major, minor, bug-fix) + - added DEBUG_VERBOSE define, took some stuff out of DEBUG_MSGS + +2000/01/28 fadden + - make the Skip result work when an input file can't be opened + - don't allow leading fssep chars in AddRecord + - don't treat a multi-file BNY that happens to have a ShrinkIt archive + in the first slot as a BXY + - added "-t" flag (write to temp) to "launder" + - in OpenReadWrite, treat zero-length archive files as newly-created + - added workaround for GSHK's zero-byte data fork bug + +2000/01/26 fadden + - added status result flags to NuFlush + - dropped kNuAbortAll and added kNuIgnore + - implemented kNuValueIgnoreCRC + - update the storageType whenever we change the record + +2000/01/25 fadden + - don't remove the temp file if the rename fails + - Nu_ReportError now optionally uses a callback instead of stderr + - pass NuArchive* and all the trimmings into Nu_ReportError so we can + do the callback thing; required adding arguments to lots of places + - clearly labeled BailError output as debug-only, then replaced most + of the BailErrorQuiet calls with BailError + - added global error message for when pArchive doesn't exist (e.g. Open) + +2000/01/24 fadden + - added args to "launder", and made it work right with 0-length threads + - reject disk image threads that aren't a valid size + - in NuFlush, recognize when a "copy" set hasn't had any changes made + - AddThread no longer makes a copy of the DataSource + +2000/01/24 ***** v0.2 shipped ***** + +2000/01/23 fadden + - added "sec" (Set ErrorHandler Callback) to exerciser + - wrote "launder" test program + - made "doExpand" option on data sinks work + +2000/01/22 fadden + - added OnlyUpdateOlder attribute and implemented for add and extract + - made HandleExisting work for AddFile/AddRecord + - AddThread's validation now blocks data and control threads in same + record + - AddFile and AddRecord now use same validation function as AddThread + +2000/01/20 fadden + - added Eric Shepherd's BeOS shared lib stuff to configure.in + - restructed the progress updater, and made it work when adding files + +2000/01/19 fadden + - normalized SysDefs.h, changing UNIX to UNIX_LIKE and defining for BeOS + - added "shared" target to makefile + - added BeOS stuff to autoconf setup + +2000/01/17 fadden + - fixed Makefile issue preventing "tests" from working with old GNU make + - fixed Lzw.c problem fouling up SunOS gcc v2.5.8 + - discovered "<" vs "<=" flapping in GSHK, which I can't Mimic + - fixed option list dump in debug print + - properly return from all Malloc errors; abort is now debug-only again + - lots of BeOS/Metrowerks "it's not gcc" changes from Eric Shepherd + +2000/01/17 ***** v0.1 shipped ***** + +(much time passes) + +mid-1998 fadden + - work begins + diff --git a/nufxlib/Compress.c b/nufxlib/Compress.c new file mode 100644 index 0000000..c8f4cc3 --- /dev/null +++ b/nufxlib/Compress.c @@ -0,0 +1,404 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Compress data into an archive. + */ +#include "NufxLibPriv.h" + +/* for ShrinkIt-mimic mode, don't compress files under 512 bytes */ +#define kNuSHKLZWThreshold 512 + + +/* + * "Compress" an uncompressed thread. + */ +static NuError +Nu_CompressUncompressed(NuArchive* pArchive, NuStraw* pStraw, + FILE* fp, ulong srcLen, ulong* pDstLen, ushort *pCrc) +{ + NuError err = kNuErrNone; + /*uchar* buffer = nil;*/ + ulong count, getsize; + + Assert(pArchive != nil); + Assert(pStraw != nil); + Assert(fp != nil); + Assert(srcLen > 0); + + *pDstLen = srcLen; /* get this over with */ + + err = Nu_AllocCompressionBufferIFN(pArchive); + BailError(err); + + if (pCrc != nil) + *pCrc = kNuInitialThreadCRC; + count = srcLen; + + while (count) { + getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; + + err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getsize); + BailError(err); + if (pCrc != nil) + *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getsize); + err = Nu_FWrite(fp, pArchive->compBuf, getsize); + BailError(err); + + count -= getsize; + } + +bail: + /*Nu_Free(pArchive, buffer);*/ + return err; +} + + +/* + * Compress from a data source to an archive. + * + * All archive-specified fields in "pThread" will be filled in, as will + * "actualThreadEOF". The "nuThreadIdx" and "fileOffset" fields will + * not be modified, and must be specified before calling here. + * + * If "sourceFormat" is uncompressed: + * "targetFormat" will be used to compress the data + * the data source length will be placed into pThread->thThreadEOF + * the compressed size will be placed into pThread->thCompThreadEOF + * the CRC is computed + * + * If "sourceFormat" is compressed: + * the data will be copied without compression (targetFormat is ignored) + * the data source "otherLen" value will be placed into pThread->thThreadEOF + * the data source length will be placed into pThread->thCompThreadEOF + * the CRC is retrieved from Nu_DataSourceGetRawCrc + * + * The actual format used will be placed in pThread->thThreadFormat, and + * the CRC of the uncompressed data will be placed in pThread->thThreadCRC. + * The remaining fields of "pThread", thThreadClass and thThreadKind, will + * be set based on the fields in "pDataSource". + * + * Data will be written to "dstFp", which must be positioned at the + * correct point in the output. The position is expected to match + * pThread->fileOffset. + * + * On exit, the output file will be positioned after the last byte of the + * output. (For a pre-sized buffer, this may not be the desired result.) + */ +NuError +Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource, + NuThreadID threadID, NuThreadFormat sourceFormat, + NuThreadFormat targetFormat, NuProgressData* pProgressData, FILE* dstFp, + NuThread* pThread) +{ + NuError err; + long origOffset; + NuStraw* pStraw = nil; + NuDataSink* pDataSink = nil; + ulong srcLen = 0, dstLen = 0; + ushort threadCrc; + + Assert(pArchive != nil); + Assert(pDataSource != nil); + /* okay if pProgressData is nil */ + Assert(dstFp != nil); + Assert(pThread != nil); + + /* remember file offset, so we can back up if compression fails */ + err = Nu_FTell(dstFp, &origOffset); + BailError(err); + Assert(origOffset == pThread->fileOffset); /* can get rid of ftell? */ + + /* fill in some thread fields */ + threadCrc = kNuInitialThreadCRC; + + pThread->thThreadClass = NuThreadIDGetClass(threadID); + pThread->thThreadKind = NuThreadIDGetKind(threadID); + pThread->actualThreadEOF = (ulong)-1; + /* nuThreadIdx and fileOffset should already be set */ + + /* + * Get the input length. For "buffer" and "fp" sources, this is just + * a value passed in. For "file" sources, this is the length of the + * file on disk. The file should already have been opened successfully + * by the caller. + * + * If the input file is zero bytes long, "store" it uncompressed and + * bail immediately. + * + * (Our desire to store uncompressible data without compression clashes + * with a passing interest in doing CRLF conversions on input data. We + * want to know the length ahead of time, which potentially makes the + * compression code simpler, but prevents us from doing the conversion + * unless we pre-flight the conversion with a separate pass through the + * input file. Of course, it's still possible for the application to + * convert the file into a temp file and add from there, so all is + * not lost.) + */ + srcLen = Nu_DataSourceGetDataLen(pDataSource); + /*DBUG(("+++ input file length is %lu\n", srcLen));*/ + + /* + * Create a "Straw" to slurp the input through and track progress. + */ + err = Nu_StrawNew(pArchive, pDataSource, pProgressData, &pStraw); + BailError(err); + + if (!srcLen) { + /* empty file! */ + if (sourceFormat != kNuThreadFormatUncompressed) { + DBUG(("ODD: empty source is compressed?\n")); + } + pThread->thThreadFormat = kNuThreadFormatUncompressed; + pThread->thThreadCRC = threadCrc; + pThread->thThreadEOF = 0; + pThread->thCompThreadEOF = 0; + pThread->actualThreadEOF = 0; + goto done; /* send final progress message */ + } + + if (sourceFormat == kNuThreadFormatUncompressed) { + /* + * Compress the input to the requested target format. + */ + + /* for some reason, GSHK doesn't compress anything under 512 bytes */ + if (pArchive->valMimicSHK && srcLen < kNuSHKLZWThreshold) + targetFormat = kNuThreadFormatUncompressed; + + if (pProgressData != nil) { + if (targetFormat != kNuThreadFormatUncompressed) + Nu_StrawSetProgressState(pStraw, kNuProgressCompressing); + else + Nu_StrawSetProgressState(pStraw, kNuProgressStoring); + } + err = Nu_ProgressDataCompressPrep(pArchive, pStraw, targetFormat, + srcLen); + BailError(err); + + switch (targetFormat) { + case kNuThreadFormatUncompressed: + err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen, + &dstLen, &threadCrc); + break; + #ifdef ENABLE_SQ + case kNuThreadFormatHuffmanSQ: + err = Nu_CompressHuffmanSQ(pArchive, pStraw, dstFp, srcLen, + &dstLen, &threadCrc); + break; + #endif + #ifdef ENABLE_LZW + case kNuThreadFormatLZW1: + err = Nu_CompressLZW1(pArchive, pStraw, dstFp, srcLen, &dstLen, + &threadCrc); + break; + case kNuThreadFormatLZW2: + err = Nu_CompressLZW2(pArchive, pStraw, dstFp, srcLen, &dstLen, + &threadCrc); + break; + #endif + #ifdef ENABLE_LZC + case kNuThreadFormatLZC12: + err = Nu_CompressLZC12(pArchive, pStraw, dstFp, srcLen, &dstLen, + &threadCrc); + break; + case kNuThreadFormatLZC16: + err = Nu_CompressLZC16(pArchive, pStraw, dstFp, srcLen, &dstLen, + &threadCrc); + break; + #endif + #ifdef ENABLE_DEFLATE + case kNuThreadFormatDeflate: + err = Nu_CompressDeflate(pArchive, pStraw, dstFp, srcLen, &dstLen, + &threadCrc); + break; + #endif + #ifdef ENABLE_BZIP2 + case kNuThreadFormatBzip2: + err = Nu_CompressBzip2(pArchive, pStraw, dstFp, srcLen, &dstLen, + &threadCrc); + break; + #endif + default: + /* should've been blocked in Value.c */ + Assert(0); + err = kNuErrInternal; + goto bail; + } + + BailError(err); + + pThread->thThreadCRC = threadCrc; /* CRC of uncompressed data */ + + if (dstLen < srcLen || + (dstLen == srcLen && targetFormat == kNuThreadFormatUncompressed)) + { + /* got smaller, or we didn't try to compress it; keep it */ + pThread->thThreadEOF = srcLen; + pThread->thCompThreadEOF = dstLen; + pThread->thThreadFormat = targetFormat; + } else { + /* got bigger, store it uncompressed */ + err = Nu_FSeek(dstFp, origOffset, SEEK_SET); + BailError(err); + err = Nu_StrawRewind(pArchive, pStraw); + BailError(err); + if (pProgressData != nil) + Nu_StrawSetProgressState(pStraw, kNuProgressStoring); + err = Nu_ProgressDataCompressPrep(pArchive, pStraw, + kNuThreadFormatUncompressed, srcLen); + BailError(err); + + DBUG(("--- compression (%d) failed (%ld vs %ld), storing\n", + targetFormat, dstLen, srcLen)); + err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen, + &dstLen, &threadCrc); + BailError(err); + + /* + * This holds so long as the previous attempt at compressing + * computed a CRC on the entire file (i.e. didn't stop early + * when it noticed the output was larger than the input). If + * this is always the case, then we can change "&threadCrc" + * a few lines back to "nil" and avoid re-computing the CRC. + * If this is not always the case, remove this assert. + */ + Assert(threadCrc == pThread->thThreadCRC); + + pThread->thThreadEOF = srcLen; + pThread->thCompThreadEOF = dstLen; + pThread->thThreadFormat = kNuThreadFormatUncompressed; + } + + } else { + /* + * Copy the already-compressed input. + */ + if (pProgressData != nil) + Nu_StrawSetProgressState(pStraw, kNuProgressCopying); + err = Nu_ProgressDataCompressPrep(pArchive, pStraw, + kNuThreadFormatUncompressed, srcLen); + BailError(err); + + err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen, + &dstLen, nil); + BailError(err); + + pThread->thThreadEOF = Nu_DataSourceGetOtherLen(pDataSource); + pThread->thCompThreadEOF = srcLen; + pThread->thThreadFormat = sourceFormat; + pThread->thThreadCRC = Nu_DataSourceGetRawCrc(pDataSource); + } + pThread->actualThreadEOF = pThread->thThreadEOF; + +done: + DBUG(("+++ srcLen=%ld, dstLen=%ld, actual=%ld\n", + srcLen, dstLen, pThread->actualThreadEOF)); + + /* make sure we send a final "success" progress message at 100% */ + if (pProgressData != nil) { + (void) Nu_StrawSetProgressState(pStraw, kNuProgressDone); + err = Nu_StrawSendProgressUpdate(pArchive, pStraw); + BailError(err); + } + +bail: + (void) Nu_StrawFree(pArchive, pStraw); + (void) Nu_DataSinkFree(pDataSink); + return err; +} + + +/* + * Copy pre-sized data into the archive at the current offset. + * + * All archive-specified fields in "pThread" will be filled in, as will + * "actualThreadEOF". The "nuThreadIdx" and "fileOffset" fields will + * not be modified. + * + * Pre-sized data is always uncompressed, and doesn't have a CRC. This + * will copy the data, and then continue writing zeros to fill out the rest + * of the pre-sized buffer. + */ +NuError +Nu_CopyPresizedToArchive(NuArchive* pArchive, NuDataSource* pDataSource, + NuThreadID threadID, FILE* dstFp, NuThread* pThread, char** ppSavedCopy) +{ + NuError err = kNuErrNone; + NuStraw* pStraw = nil; + ulong srcLen, bufferLen; + ulong count, getsize; + + srcLen = Nu_DataSourceGetDataLen(pDataSource); + bufferLen = Nu_DataSourceGetOtherLen(pDataSource); + if (bufferLen < srcLen) { + /* hey, this won't fit! */ + DBUG(("--- can't fit %lu into buffer of %lu!\n", srcLen, bufferLen)); + err = kNuErrPreSizeOverflow; + goto bail; + } + DBUG(("+++ copying %lu into buffer of %lu\n", srcLen, bufferLen)); + + pThread->thThreadClass = NuThreadIDGetClass(threadID); + pThread->thThreadFormat = kNuThreadFormatUncompressed; + pThread->thThreadKind = NuThreadIDGetKind(threadID); + pThread->thThreadCRC = 0; /* no CRC on pre-sized stuff */ + pThread->thThreadEOF = srcLen; + pThread->thCompThreadEOF = bufferLen; + pThread->actualThreadEOF = srcLen; + /* nuThreadIdx and fileOffset should already be set */ + + /* + * Prepare to copy the data through a buffer. The "straw" thing + * is a convenient way to deal with the dataSource, even though we + * don't have a progress updater. + */ + err = Nu_StrawNew(pArchive, pDataSource, nil, &pStraw); + BailError(err); + + count = srcLen; + err = Nu_AllocCompressionBufferIFN(pArchive); + BailError(err); + + while (count) { + getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; + + err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getsize); + BailError(err); + err = Nu_FWrite(dstFp, pArchive->compBuf, getsize); + BailError(err); + + if (ppSavedCopy != nil && *ppSavedCopy == nil) { + /* + * Grab a copy of the filename for our own use. This assumes + * that the filename fits in kNuGenCompBufSize, which is a + * pretty safe thing to assume. + */ + Assert(threadID == kNuThreadIDFilename); + Assert(count == getsize); + *ppSavedCopy = Nu_Malloc(pArchive, getsize+1); + BailAlloc(*ppSavedCopy); + memcpy(*ppSavedCopy, pArchive->compBuf, getsize); + (*ppSavedCopy)[getsize] = '\0'; /* make sure it's terminated */ + } + + count -= getsize; + } + + /* + * Pad out the rest of the buffer. Could probably do this more + * efficiently through the buffer we've allocated, but these regions + * tend to be either 32 or 200 bytes. + */ + count = bufferLen - srcLen; + while (count--) + Nu_WriteOne(pArchive, dstFp, 0); + +bail: + (void) Nu_StrawFree(pArchive, pStraw); + /*Nu_Free(pArchive, buffer);*/ + return err; +} + diff --git a/nufxlib/Crc16.c b/nufxlib/Crc16.c new file mode 100644 index 0000000..15a0769 --- /dev/null +++ b/nufxlib/Crc16.c @@ -0,0 +1,110 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Compute 16-bit CRCs. Depending on the hardware, the table version + * might be slower than the loop computation. + */ +#define __Crc16_c__ 1 +#include "NufxLibPriv.h" + +#define CRC_TAB +#ifdef CRC_TAB +/* + * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. + * NOTE: First srgument must be in range 0 to 255. + * Second argument is referenced twice. + * + * Programmers may incorporate any or all code into their programs, + * giving proper credit within the source. Publication of the + * source routines is permitted so long as proper credit is given + * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, + * Omen Technology. + */ + + +/*#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)*/ +#define updcrc(cp, crc) ( (crctab[((crc >> 8) & 0xFF) ^ cp] ^ (crc << 8)) & 0xFFFF) + + +/* crctab calculated by Mark G. Mendel, Network Systems Corporation */ +const ushort gNuCrc16Table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; +#endif + + +/* + * Calculate CRC on a region + * + * A CRC is the result of a mathematical operation based on the + * coefficients of a polynomial when multiplied by X^16 then divided by + * the generator polynomial (X^16 + X^12 + X^5 + 1) using modulo two + * arithmetic. + * + * This routine is a slightly modified verison of one found in: + * _Advanced Programming Techniques for the Apple //gs Toolbox_ + * By Morgan Davis and Dan Gookin (Compute! Publications, Inc.) + * It can either calculate the CRC bit-by-bit or use a table. + * + * Depending on CPU architecture, one may be dramatically faster than + * the other. + */ +ushort +Nu_CalcCRC16(ushort seed, const uchar* ptr, int count) +{ + ushort CRC = seed; +#ifndef CRC_TAB + int x; + Assert(sizeof(ushort) == 2); /* I think this is assumed */ +#endif + + do { +#ifndef CRC_TAB + CRC ^= *ptr++ << 8; /* XOR hi-byte of CRC w/dat */ + for (x = 8; x; --x) /* Then, for 8 bit shifts... */ + if (CRC & 0x8000) /* Test hi order bit of CRC */ + CRC = CRC << 1 ^ 0x1021; /* if set, shift & XOR w/$1021 */ + else + CRC <<= 1; /* Else, just shift left once. */ +#else + CRC = Nu_UpdateCRC16(*ptr++, CRC); /* look up new value in table */ +#endif + } while (--count); + + return (CRC); +} + diff --git a/nufxlib/Debug.c b/nufxlib/Debug.c new file mode 100644 index 0000000..eeb1daa --- /dev/null +++ b/nufxlib/Debug.c @@ -0,0 +1,395 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Debugging functions. These are omitted from the non-debug build. + */ +#include "NufxLibPriv.h" + +#if defined(DEBUG_MSGS) + + +/* pull a string out of one of the static arrays */ +#define GetStaticString(index, staticArray) ( \ + (index) >= NELEM(staticArray) ? "" : staticArray[index] \ + ) + +/* thread's thread_class */ +static const char* gThreadClassNames[] = { + "message_thread", + "control_thread", + "data_thread", + "filename_thread", +}; + +/* thread's thread_format */ +static const char* gThreadFormatNames[] = { + "uncompressed", + "Huffman Squeeze", + "dynamic LZW/1", + "dynamic LZW/2", + "12-bit LZC", + "16-bit LZC", + "deflate", + "bzip2" +}; + +/* days of the week */ +static const char* gDayNames[] = { + "[ null ]", + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +/* months of the year */ +static const char* gMonths[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +#define kNuDateOutputLen 64 + +/* file_sys_id values */ +static const char* gFileSysIDs[] = { + "Reserved/unknown ($00)", "ProDOS/SOS", "DOS 3.3", "DOS 3.2", + "Apple II Pascal", "Macintosh (HFS)", "Macintosh (MFS)", + "LISA file system", "Apple CP/M", "Reserved 0x09", "MS-DOS", + "High-Sierra", "ISO 9660", "AppleShare" +}; + + +/* + * Convert a DateTime structure into something printable. + * + * The buffer passed in must hold at least kNuDateOutputLen bytes. + * + * Returns "buffer" for the benefit of printf() calls. + */ +static char* +Nu_DebugDumpDate(const NuDateTime* pDateTime, char* buffer) +{ + char* cp; + + /* is it valid? */ + if (pDateTime->day > 30 || pDateTime->month > 11 || pDateTime->hour > 24 || + pDateTime->minute > 59) + { + strcpy(buffer, " "); + goto bail; + } + + /* is it empty? */ + if ((pDateTime->second | pDateTime->minute | pDateTime->hour | + pDateTime->year | pDateTime->day | pDateTime->month | + pDateTime->extra | pDateTime->weekDay) == 0) + { + strcpy(buffer, " [No Date] "); + goto bail; + } + + cp = buffer; + + /* only print weekDay if one was stored */ + if (pDateTime->weekDay) { + if (pDateTime->weekDay < NELEM(gDayNames)) + sprintf(cp, "%s, ", gDayNames[pDateTime->weekDay]); + else + sprintf(cp, "??%d, ", pDateTime->weekDay); + cp += strlen(cp); + } + + sprintf(cp, "%02d-%s-%04d %02d:%02d:%02d", + pDateTime->day+1, gMonths[pDateTime->month], + pDateTime->year < 40 ? pDateTime->year + 2000 : pDateTime->year + 1900, + pDateTime->hour, pDateTime->minute, pDateTime->second); + +bail: + sprintf(buffer + strlen(buffer), " [s%d m%d h%d Y%d D%d M%d x%d w%d]", + pDateTime->second, pDateTime->minute, pDateTime->hour, + pDateTime->year, pDateTime->day, pDateTime->month, pDateTime->extra, + pDateTime->weekDay); + + return buffer; +} + + +/* + * Convert a buffer into a hexadecimal character string. + * + * The result will be 2x the size of the original, +1 for a null byte. + */ +static void +ConvertToHexStr(const uchar* inBuf, int inLen, char* outBuf) +{ + while (inLen--) { + *outBuf++ = HexConv((*inBuf >> 4) & 0x0f); + *outBuf++ = HexConv(*inBuf & 0x0f); + inBuf++; + } + *outBuf = '\0'; +} + + +/* + * Dump everything we know about pThread. + */ +void +Nu_DebugDumpThread(const NuThread* pThread) +{ + static const char* kInd = " "; + NuThreadID threadID; + const char* descr; + + Assert(pThread != nil); + + printf("%sThreadClass: 0x%04x (%s)\n", kInd, + pThread->thThreadClass, + GetStaticString(pThread->thThreadClass, gThreadClassNames)); + printf("%sThreadFormat: 0x%04x (%s)\n", kInd, + pThread->thThreadFormat, + GetStaticString(pThread->thThreadFormat, gThreadFormatNames)); + + threadID = NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind); + switch (threadID) { + case kNuThreadIDOldComment: descr = "old comment"; break; + case kNuThreadIDComment: descr = "comment"; break; + case kNuThreadIDIcon: descr = "icon"; break; + case kNuThreadIDMkdir: descr = "mkdir"; break; + case kNuThreadIDDataFork: descr = "data fork"; break; + case kNuThreadIDDiskImage: descr = "disk image"; break; + case kNuThreadIDRsrcFork: descr = "rsrc fork"; break; + case kNuThreadIDFilename: descr = "filename"; break; + default: descr = ""; break; + } + printf("%sThreadKind: 0x%04x (%s)\n", kInd, + pThread->thThreadKind, descr); + + printf("%sThreadCRC: 0x%04x ThreadEOF: %lu CompThreadEOF: %lu\n", kInd, + pThread->thThreadCRC, pThread->thThreadEOF, pThread->thCompThreadEOF); + printf("%s*File data offset: %ld actualThreadEOF: %ld\n", kInd, + pThread->fileOffset, pThread->actualThreadEOF); +} + +/* + * Dump everything we know about pRecord, including its threads and ThreadMods. + * + * Changes to existing records are made to the "copy" set, not the "orig" + * set. Pass in the "orig" copy in "pRecord", and optionally pass in the + * "copy" set in "pXrefRecord" to glean data from both. + */ +static void +Nu_DebugDumpRecord(NuArchive* pArchive, const NuRecord* pRecord, + const NuRecord* pXrefRecord, Boolean isDeleted) +{ + NuError err; /* dummy */ + static const char* kInd = " "; + char dateBuf[kNuDateOutputLen]; + const NuThreadMod* pThreadMod; + const NuThread* pThread; + ulong idx; + + Assert(pRecord != nil); + + /*printf("PTR: pRecord=0x%08lx pXrefRecord=0x%08lx\n", (long) pRecord, + (long) pXrefRecord);*/ + + printf("%s%s%sFilename: '%s' (idx=%lu)\n", kInd, + isDeleted ? "[DEL] " : "", + pXrefRecord != nil && pXrefRecord->pThreadMods != nil ? "[MOD] " : "", + pRecord->filename == nil ? "" : pRecord->filename, + pRecord->recordIdx); + printf("%sHeaderID: '%.4s' VersionNumber: 0x%04x HeaderCRC: 0x%04x\n", + kInd, + pRecord->recNufxID, pRecord->recVersionNumber, pRecord->recHeaderCRC); + printf("%sAttribCount: %u TotalThreads: %lu\n", kInd, + pRecord->recAttribCount, pRecord->recTotalThreads); + printf("%sFileSysID: %u (%s) FileSysInfo: 0x%04x ('%c')\n", kInd, + pRecord->recFileSysID, + GetStaticString(pRecord->recFileSysID, gFileSysIDs), + pRecord->recFileSysInfo, + NuGetSepFromSysInfo(pRecord->recFileSysInfo)); + /* do something fancy for ProDOS? */ + printf("%sFileType: 0x%08lx ExtraType: 0x%08lx Access: 0x%08lx\n", kInd, + pRecord->recFileType, pRecord->recExtraType, pRecord->recAccess); + printf("%sCreateWhen: %s\n", kInd, + Nu_DebugDumpDate(&pRecord->recCreateWhen, dateBuf)); + printf("%sModWhen: %s\n", kInd, + Nu_DebugDumpDate(&pRecord->recModWhen, dateBuf)); + printf("%sArchiveWhen: %s\n", kInd, + Nu_DebugDumpDate(&pRecord->recArchiveWhen, dateBuf)); + printf("%sStorageType: %u OptionSize: %u FilenameLength: %u\n", kInd, + pRecord->recStorageType, pRecord->recOptionSize, + pRecord->recFilenameLength); + if (pRecord->recOptionSize) { + char* outBuf = Nu_Malloc(pArchive, pRecord->recOptionSize * 2 +1); + BailAlloc(outBuf); + Assert(pRecord->recOptionList != nil); + ConvertToHexStr(pRecord->recOptionList, pRecord->recOptionSize, outBuf); + printf("%sOptionList: [%s]\n", kInd, outBuf); + Nu_Free(pArchive, outBuf); + } + + printf("%s*ExtraCount: %ld RecFileOffset: %ld RecHeaderLength: %ld\n", + kInd, + pRecord->extraCount, pRecord->fileOffset, pRecord->recHeaderLength); + + for (idx = 0; idx < pRecord->recTotalThreads; idx++) { + Boolean isFake; + + isFake = (idx >= pRecord->recTotalThreads - pRecord->fakeThreads); + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + printf("%s--Thread #%lu (idx=%lu)%s\n", kInd, idx, pThread->threadIdx, + isFake ? " [FAKE]" : ""); + Nu_DebugDumpThread(pThread); + } + + if (pXrefRecord != nil) + pThreadMod = pXrefRecord->pThreadMods; + else + pThreadMod = pRecord->pThreadMods; /* probably empty */ + + if (pThreadMod != nil) + printf("%s*ThreadMods -----\n", kInd); + while (pThreadMod != nil) { + switch (pThreadMod->entry.kind) { + case kNuThreadModAdd: + printf("%s *-ThreadMod ADD 0x%08lx 0x%04x (sourceType=%d)\n", kInd, + pThreadMod->entry.add.threadID, + pThreadMod->entry.add.threadFormat, + Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource)); + break; + case kNuThreadModUpdate: + printf("%s *-ThreadMod UPDATE %6ld\n", kInd, + pThreadMod->entry.update.threadIdx); + break; + case kNuThreadModDelete: + printf("%s *-ThreadMod DELETE %6ld\n", kInd, + pThreadMod->entry.delete.threadIdx); + break; + case kNuThreadModUnknown: + default: + Assert(0); + printf("%s++ThreadMod UNKNOWN\n", kInd); + break; + } + + pThreadMod = pThreadMod->pNext; + } + + /*printf("%s*TotalLength: %ld TotalCompLength: %ld\n", + kInd, pRecord->totalLength, pRecord->totalCompLength);*/ + printf("%s*TotalCompLength: %ld\n", kInd, pRecord->totalCompLength); + printf("\n"); + +bail: + return; +} + +/* + * Dump the records in a RecordSet. + */ +static void +Nu_DebugDumpRecordSet(NuArchive* pArchive, const NuRecordSet* pRecordSet, + const NuRecordSet* pXrefSet) +{ + const NuRecord* pRecord; + const NuRecord* pXrefRecord; + Boolean doXref; + long count; + + doXref = false; + pXrefRecord = nil; + if (pXrefSet != nil && Nu_RecordSet_GetLoaded(pXrefSet)) { + pXrefRecord = Nu_RecordSet_GetListHead(pXrefSet); + doXref = true; + } + + /* dump every record, if we've loaded them */ + count = Nu_RecordSet_GetNumRecords(pRecordSet); + pRecord = Nu_RecordSet_GetListHead(pRecordSet); + if (pRecord != nil) { + Assert(count != 0); + while (count--) { + Assert(pRecord != nil); + + if (pXrefRecord != nil && + pRecord->recordIdx == pXrefRecord->recordIdx) + { + Nu_DebugDumpRecord(pArchive, pRecord, pXrefRecord, false); + pXrefRecord = pXrefRecord->pNext; + } else { + Nu_DebugDumpRecord(pArchive, pRecord, nil, doXref); + } + pRecord = pRecord->pNext; + } + } else { + Assert(count == 0); + } +} + +/* + * Dump the master header block. + */ +static void +Nu_DebugDumpMH(const NuMasterHeader* pMasterHeader) +{ + static const char* kInd = " "; + char dateBuf1[kNuDateOutputLen]; + + Assert(pMasterHeader != nil); + + printf("%sNufileID: '%.6s' MasterCRC: 0x%04x TotalRecords: %lu\n", kInd, + pMasterHeader->mhNufileID, pMasterHeader->mhMasterCRC, + pMasterHeader->mhTotalRecords); + printf("%sArchiveCreateWhen: %s\n", kInd, + Nu_DebugDumpDate(&pMasterHeader->mhArchiveCreateWhen, dateBuf1)); + printf("%sArchiveModWhen: %s\n", kInd, + Nu_DebugDumpDate(&pMasterHeader->mhArchiveModWhen, dateBuf1)); + printf("%sMasterVersion: %u MasterEOF: %lu\n", kInd, + pMasterHeader->mhMasterVersion, pMasterHeader->mhMasterEOF); +} + +/* + * Dump everything we know about pArchive. + * + * This will only print the records that we have seen so far. If the + * application hasn't caused us to scan through all of the records in + * the archive, then this won't be very interesting. This will never + * show any records for streaming-mode archives. + */ +void +Nu_DebugDumpAll(NuArchive* pArchive) +{ + Assert(pArchive != nil); + + printf("*Archive pathname: '%s'\n", pArchive->archivePathname); + printf("*Archive type: %d\n", pArchive->archiveType); + printf("*Header offset: %ld (junk offset=%ld)\n", + pArchive->headerOffset, pArchive->junkOffset); + printf("*Num records: %ld orig, %ld copy, %ld new\n", + Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet), + Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet), + Nu_RecordSet_GetNumRecords(&pArchive->newRecordSet)); + printf("*NuRecordIdx seed: %lu NuRecordIdx next: %lu\n", + pArchive->recordIdxSeed, pArchive->nextRecordIdx); + + /* master header */ + Nu_DebugDumpMH(&pArchive->masterHeader); + + printf(" *ORIG record set (x-ref with COPY):\n"); + Nu_DebugDumpRecordSet(pArchive, &pArchive->origRecordSet, + &pArchive->copyRecordSet); + printf(" *NEW record set:\n"); + Nu_DebugDumpRecordSet(pArchive, &pArchive->newRecordSet, nil); + + if (!Nu_RecordSet_GetLoaded(&pArchive->origRecordSet) && + !Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) + { + printf("*** DEBUG: original records not loaded yet? ***\n"); + } + +} + +#endif /*DEBUG_MSGS*/ diff --git a/nufxlib/Deferred.c b/nufxlib/Deferred.c new file mode 100644 index 0000000..fface90 --- /dev/null +++ b/nufxlib/Deferred.c @@ -0,0 +1,2578 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Deferred write handling. + */ +#include "NufxLibPriv.h" + + +/* + * =========================================================================== + * NuThreadMod functions + * =========================================================================== + */ + +/* + * Alloc and initialize a new "add" ThreadMod. + * + * Caller is allowed to dispose of the data source, as this makes a copy. + * + * NOTE: threadFormat is how you want the data to be compressed. The + * threadFormat passed to DataSource describes the source data. + */ +NuError +Nu_ThreadModAdd_New(NuArchive* pArchive, NuThreadID threadID, + NuThreadFormat threadFormat, NuDataSource* pDataSource, + NuThreadMod** ppThreadMod) +{ + Assert(ppThreadMod != nil); + Assert(pDataSource != nil); + + *ppThreadMod = Nu_Calloc(pArchive, sizeof(**ppThreadMod)); + if (*ppThreadMod == nil) + return kNuErrMalloc; + + (*ppThreadMod)->entry.kind = kNuThreadModAdd; + (*ppThreadMod)->entry.add.used = false; + (*ppThreadMod)->entry.add.threadIdx = Nu_GetNextThreadIdx(pArchive); + (*ppThreadMod)->entry.add.threadID = threadID; + (*ppThreadMod)->entry.add.threadFormat = threadFormat; + (*ppThreadMod)->entry.add.pDataSource = Nu_DataSourceCopy(pDataSource); + + /* decide if this is a pre-sized thread [do we want to do this here??] */ + (*ppThreadMod)->entry.add.isPresized = Nu_IsPresizedThreadID(threadID); + + return kNuErrNone; +} + +/* + * Alloc and initialize a new "update" ThreadMod. + * + * Caller is allowed to dispose of the data source. + */ +NuError +Nu_ThreadModUpdate_New(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSource* pDataSource, NuThreadMod** ppThreadMod) +{ + Assert(ppThreadMod != nil); + Assert(pDataSource != nil); + + *ppThreadMod = Nu_Calloc(pArchive, sizeof(**ppThreadMod)); + if (*ppThreadMod == nil) + return kNuErrMalloc; + + (*ppThreadMod)->entry.kind = kNuThreadModUpdate; + (*ppThreadMod)->entry.update.used = false; + (*ppThreadMod)->entry.update.threadIdx = threadIdx; + (*ppThreadMod)->entry.update.pDataSource = Nu_DataSourceCopy(pDataSource); + + return kNuErrNone; +} + +/* + * Alloc and initialize a new "delete" ThreadMod. + * + * The "threadID" argument is really only needed for filename threads. We + * use it when trying to track how many filename threads we really have. + */ +NuError +Nu_ThreadModDelete_New(NuArchive* pArchive, NuThreadIdx threadIdx, + NuThreadID threadID, NuThreadMod** ppThreadMod) +{ + Assert(ppThreadMod != nil); + + *ppThreadMod = Nu_Calloc(pArchive, sizeof(**ppThreadMod)); + if (*ppThreadMod == nil) + return kNuErrMalloc; + + (*ppThreadMod)->entry.kind = kNuThreadModDelete; + (*ppThreadMod)->entry.delete.used = false; + (*ppThreadMod)->entry.delete.threadIdx = threadIdx; + (*ppThreadMod)->entry.delete.threadID = threadID; + + return kNuErrNone; +} + +/* + * Free a single NuThreadMod. + */ +void +Nu_ThreadModFree(NuArchive* pArchive, NuThreadMod* pThreadMod) +{ + if (pThreadMod == nil) + return; + + switch (pThreadMod->entry.kind) { + case kNuThreadModAdd: + Nu_DataSourceFree(pThreadMod->entry.add.pDataSource); + break; + case kNuThreadModUpdate: + Nu_DataSourceFree(pThreadMod->entry.update.pDataSource); + break; + default: + break; + } + + Nu_Free(pArchive, pThreadMod); +} + + +/* + * Return a threadMod with a matching "threadIdx", if any. Because "add" + * threads can't have a threadIdx that matches an existing thread, this + * will only return updates and deletes. + * + * We don't allow more than one threadMod on the same thread, so we don't + * have to deal with having more than one match. (To be safe, we go + * ahead and do debug-only checks for multiple matches. There shouldn't + * be more than three or four threads per record, so the extra search + * isn't costly.) + * + * Returns "nil" if nothing found. + */ +NuThreadMod* +Nu_ThreadMod_FindByThreadIdx(const NuRecord* pRecord, NuThreadIdx threadIdx) +{ + NuThreadMod* pThreadMod; + NuThreadMod* pMatch = nil; + + pThreadMod = pRecord->pThreadMods; + while (pThreadMod) { + switch (pThreadMod->entry.kind) { + case kNuThreadModAdd: + /* can't happen */ + Assert(pThreadMod->entry.add.threadIdx != threadIdx); + break; + case kNuThreadModUpdate: + if (pThreadMod->entry.update.threadIdx == threadIdx) { + Assert(pMatch == nil); + pMatch = pThreadMod; + } + break; + case kNuThreadModDelete: + if (pThreadMod->entry.delete.threadIdx == threadIdx) { + Assert(pMatch == nil); + pMatch = pThreadMod; + } + break; + default: + Assert(0); + /* keep going, I guess */ + } + pThreadMod = pThreadMod->pNext; + } + + return pMatch; +} + + +/* + * =========================================================================== + * ThreadMod list operations + * =========================================================================== + */ + +/* + * Search for an "add" ThreadMod, by threadID. + */ +NuError +Nu_ThreadModAdd_FindByThreadID(const NuRecord* pRecord, NuThreadID threadID, + NuThreadMod** ppThreadMod) +{ + NuThreadMod* pThreadMod; + + Assert(pRecord != nil); + Assert(ppThreadMod != nil); + + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + if (pThreadMod->entry.kind != kNuThreadModAdd) + continue; + + if (pThreadMod->entry.add.threadID == threadID) { + *ppThreadMod = pThreadMod; + return kNuErrNone; + } + + pThreadMod = pThreadMod->pNext; + } + + return kNuErrNotFound; +} + + +/* + * Free up the list of NuThreadMods in this record. + */ +void +Nu_FreeThreadMods(NuArchive* pArchive, NuRecord* pRecord) +{ + NuThreadMod* pThreadMod; + NuThreadMod* pNext; + + Assert(pRecord != nil); + pThreadMod = pRecord->pThreadMods; + + if (pThreadMod == nil) + return; + + while (pThreadMod != nil) { + pNext = pThreadMod->pNext; + + Nu_ThreadModFree(pArchive, pThreadMod); + pThreadMod = pNext; + } + + pRecord->pThreadMods = nil; +} + + +/* + * =========================================================================== + * Temporary structure for holding updated thread info + * =========================================================================== + */ + +/* used when constructing a new set of threads */ +typedef struct { + int numThreads; /* max #of threads */ + int nextSlot; /* where the next one goes */ + NuThread* pThreads; /* static-sized array */ +} NuNewThreads; + +/* + * Allocate and initialize a NuNewThreads struct. + */ +static NuError +Nu_NewThreads_New(NuArchive* pArchive, NuNewThreads** ppNewThreads, + long numThreads) +{ + NuError err = kNuErrNone; + + *ppNewThreads = Nu_Malloc(pArchive, sizeof(**ppNewThreads)); + BailAlloc(*ppNewThreads); + (*ppNewThreads)->numThreads = numThreads; + (*ppNewThreads)->nextSlot = 0; + (*ppNewThreads)->pThreads = Nu_Malloc(pArchive, numThreads * sizeof(NuThread)); + BailAlloc((*ppNewThreads)->pThreads); + +bail: + return err; +} + +/* + * Free a NuNewThreads struct. + */ +static void +Nu_NewThreads_Free(NuArchive* pArchive, NuNewThreads* pNewThreads) +{ + if (pNewThreads != nil) { + Nu_Free(pArchive, pNewThreads->pThreads); + Nu_Free(pArchive, pNewThreads); + } +} + +/* + * Returns true if "pNewThreads" has room for another entry, false otherwise. + */ +static Boolean +Nu_NewThreads_HasRoom(const NuNewThreads* pNewThreads) +{ + if (pNewThreads->nextSlot < pNewThreads->numThreads) + return true; + else + return false; +} + +/* + * Get the next available slot. The contents of the slot are first + * initialized. + * + * The "next slot" marker is automatically advanced. + */ +static NuThread* +Nu_NewThreads_GetNext(NuNewThreads* pNewThreads, NuArchive* pArchive) +{ + NuThread* pThread; + + pThread = &pNewThreads->pThreads[pNewThreads->nextSlot]; + memset(pThread, 0, sizeof(*pThread)); + + pThread->fileOffset = -1; /* mark as invalid */ + + /* advance slot */ + pNewThreads->nextSlot++; + Assert(pNewThreads->nextSlot <= pNewThreads->numThreads); + + return pThread; +} + +/* + * Return the #of threads we're meant to hold. + */ +static int +Nu_NewThreads_GetNumThreads(const NuNewThreads* pNewThreads) +{ + Assert(pNewThreads != nil); + + return pNewThreads->numThreads; +} + +/* + * Total up the compressed EOFs of all threads. + */ +static ulong +Nu_NewThreads_TotalCompThreadEOF(NuNewThreads* pNewThreads) +{ + ulong compThreadEOF; + int i; + + /* we should be all full up at this point; if not, we have a bug */ + Assert(pNewThreads != nil); + Assert(pNewThreads->numThreads == pNewThreads->nextSlot); + + compThreadEOF = 0; + for (i = 0; i < pNewThreads->numThreads; i++) + compThreadEOF += pNewThreads->pThreads[i].thCompThreadEOF; + + return compThreadEOF; +} + + +/* + * "Donate" the thread collection to the caller. This returns a pointer + * to the thread array, and then nukes our copy of the pointer. This + * allows us to transfer ownership of the storage to the caller. + */ +static NuThread* +Nu_NewThreads_DonateThreads(NuNewThreads* pNewThreads) +{ + NuThread* pThreads = pNewThreads->pThreads; + + pNewThreads->pThreads = nil; + return pThreads; +} + + +/* + * =========================================================================== + * Archive construction - Record-level functions + * =========================================================================== + */ + +/* + * Copy an entire record (threads and all) from the source archive to the + * current offset in the temp file. + * + * Pass in the record from the *copy* set, not the original. + */ +static NuError +Nu_CopyArchiveRecord(NuArchive* pArchive, NuRecord* pRecord) +{ + NuError err = kNuErrNone; + long offsetAdjust; + long outputOffset; + int i; + + err = Nu_FTell(pArchive->tmpFp, &outputOffset); + BailError(err); + offsetAdjust = outputOffset - pRecord->fileOffset; + + DBUG(("--- Copying record '%s' (curOff=%ld adj=%ld)\n", pRecord->filename, + outputOffset, offsetAdjust)); + + /* seek to the start point in the source file, and copy the whole thing */ + err = Nu_FSeek(pArchive->archiveFp, pRecord->fileOffset, SEEK_SET); + BailError(err); + err = Nu_CopyFileSection(pArchive, pArchive->tmpFp, pArchive->archiveFp, + pRecord->recHeaderLength + pRecord->totalCompLength); + BailError(err); + + /* adjust the file offsets in the record header and in the threads */ + pRecord->fileOffset += offsetAdjust; + + for (i = 0; i < (int)pRecord->recTotalThreads; i++) { + NuThread* pThread = Nu_GetThread(pRecord, i); + + pThread->fileOffset += offsetAdjust; + } + + Assert(outputOffset + pRecord->recHeaderLength + pRecord->totalCompLength == + (ulong)ftell(pArchive->tmpFp)); + Assert(pRecord->fileOffset == outputOffset); + +bail: + return err; +} + + +/* + * Count the number of threads that will eventually inhabit this record. + * + * Returns -1 on error. + */ +static NuError +Nu_CountEventualThreads(const NuRecord* pRecord, long* pTotalThreads, + long* pFilenameThreads) +{ + const NuThreadMod* pThreadMod; + const NuThread* pThread; + long idx, numThreads, numFilenameThreads; + + /* + * Number of threads is equal to: + * the number of existing threads + * MINUS the number of "delete" threadMods (you can't delete the same + * thread more than once) + * PLUS the number of "add" threadMods + */ + numThreads = pRecord->recTotalThreads; + numFilenameThreads = 0; + + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + switch (pThreadMod->entry.kind) { + case kNuThreadModAdd: + numThreads++; + if (pThreadMod->entry.add.threadID == kNuThreadIDFilename) + numFilenameThreads++; + break; + case kNuThreadModDelete: + numThreads--; + if (pThreadMod->entry.delete.threadID == kNuThreadIDFilename) + numFilenameThreads--; + break; + case kNuThreadModUpdate: + break; + default: + Assert(0); + break; + } + + pThreadMod = pThreadMod->pNext; + } + + /* + * If the record has more than one filename thread, we only keep + * the first one, so remove it from our accounting here. It should + * not have been possible to add a new filename thread when an + * existing one was present, so we don't check the threadMods. + */ + for (idx = 0; idx < (long)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + if (NuGetThreadID(pThread) == kNuThreadIDFilename) + numFilenameThreads++; + } + Assert(numFilenameThreads >= 0); + if (numFilenameThreads > 1) { + DBUG(("--- ODD: found multiple filename threads (%ld)\n", + numFilenameThreads)); + numThreads -= (numFilenameThreads -1); + } + + /* + * Records with no threads should've been screened out already. + */ + if (numThreads <= 0) + return kNuErrInternal; + + *pTotalThreads = numThreads; + *pFilenameThreads = numFilenameThreads; /* [should cap this at 1?] */ + return kNuErrNone; +} + + +/* + * Verify that all of the threads and threadMods in a record have + * been touched. This is done after the record has been written to + * the destination archive, in order to ensure that we don't leave + * anything behind. + * + * All items, including things like duplicate filename threads that + * we ignore, are marked "used" during processing, so we don't need + * to be terribly bright here. + */ +static Boolean +Nu_VerifyAllTouched(NuArchive* pArchive, const NuRecord* pRecord) +{ + const NuThreadMod* pThreadMod; + const NuThread* pThread; + long idx; + + Assert(pArchive != nil); + Assert(pRecord != nil); + + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + Assert(pThreadMod->entry.generic.used == false || + pThreadMod->entry.generic.used == true); + if (!pThreadMod->entry.generic.used) + return false; + pThreadMod = pThreadMod->pNext; + } + + for (idx = 0; idx < (long)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + Assert(pThread->used == false || pThread->used == true); + if (!pThread->used) + return false; + } + + return true; +} + + +/* + * Set the threadFilename field of a record to a new value. This does + * not affect the record header filename. + * + * This call should only be made after an "add" or "update" threadMod has + * successfully completed. + * + * "newName" must be allocated storage. + */ +static void +Nu_SetNewThreadFilename(NuArchive* pArchive, NuRecord* pRecord, char* newName) +{ + Assert(pRecord != nil); + Assert(newName != nil); + + Nu_Free(pArchive, pRecord->threadFilename); + pRecord->threadFilename = newName; + pRecord->filename = pRecord->threadFilename; +} + +/* + * If this is a disk image, we require that the uncompressed length + * be equal to recExtraType * recStorageType (where recStorageType + * is the block size, usually 512). If they haven't set those to + * appropriate values, we'll set them on their behalf, so long as + * the uncompressed size is a multiple of 512. + */ +static NuError +Nu_UpdateDiskImageFields(NuArchive* pArchive, NuRecord* pRecord, long sourceLen) +{ + NuError err = kNuErrNone; + long actualLen; + + if (pRecord->recStorageType <= 13) + pRecord->recStorageType = 512; + actualLen = pRecord->recExtraType * pRecord->recStorageType; + + if (actualLen != sourceLen) { + /* didn't match, see if we can fix it */ + DBUG(("--- fixing up disk image size\n")); + if ((sourceLen & 0x1ff) == 0) { + pRecord->recStorageType = 512; + pRecord->recExtraType = sourceLen / 512; + } else { + /* oh dear */ + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, kNuErrNone,"disk image size of %ld invalid", + sourceLen); + /* fall through and out */ + } + } + + return err; +} + +/* + * As part of thread construction or in-place updating, handle a single + * "update" threadMod. We have an existing thread, and are replacing + * the contents of it with new data. + * + * "pThread" is a thread from the copy list or a "new" thread (a copy of + * the thread from the "copy" list), and "pThreadMod" is a threadMod that + * effects pThread. + * + * "fp" is a pointer into the archive at the offset where the data is + * to be written. On exit, "fp" will point past the end of the pre-sized + * buffer. + * + * Possible side-effects on "pRecord": threadFilename may be updated. + */ +static NuError +Nu_ConstructArchiveUpdate(NuArchive* pArchive, FILE* fp, NuRecord* pRecord, + NuThread* pThread, const NuThreadMod* pThreadMod) +{ + NuError err; + NuDataSource* pDataSource = nil; + ulong sourceLen; + ulong threadBufSize; + + /* + * We're going to copy the data out of the data source. Because + * "update" actions only operate on pre-sized chunks, and the data + * is never compressed, this should be straightforward. However, + * we do need to make sure that the data will fit. + * + * I expect these to be small, and it's just a raw data copy, so no + * progress updater is used. + */ + Assert(Nu_IsPresizedThreadID(NuGetThreadID(pThread))); + Assert(pThread->thCompThreadEOF >= pThread->thThreadEOF); + threadBufSize = pThread->thCompThreadEOF; + pDataSource = pThreadMod->entry.update.pDataSource; + Assert(pDataSource != nil); + + err = Nu_DataSourcePrepareInput(pArchive, pDataSource); + if (err == kNuErrSkipped) { + /* something failed (during file open?), just skip this one */ + DBUG(("--- skipping pre-sized thread update to %ld\n", + pThread->threadIdx)); + err = kNuErrNone; + goto skip_update; + } else if (err != kNuErrNone) + goto bail; + + /* + * Check to see if the data will fit. In some cases we can verify + * the size during the UpdatePresizedThread call, but if it's being + * added from a file we can't tell until now. + * + * We could be nice and give the user a chance to do something about + * this, but frankly the application should have checked the file + * size before handing it to us. + */ + sourceLen = Nu_DataSourceGetDataLen(pDataSource); + if (sourceLen > pThread->thCompThreadEOF) { + err = kNuErrPreSizeOverflow; + Nu_ReportError(NU_BLOB, err, "can't fit %ld bytes into %ld-byte buffer", + sourceLen, pThread->thCompThreadEOF); + goto bail; + } + + /* + * During an update operation, the user's specification of "otherLen" + * doesn't really matter, because we're not going to change the size + * of the region in the archive. However, this size *is* used by + * the code to figure out how big the buffer should be, and will + * determine where the file pointer ends up when the call returns. + * So, we jam in the "real" value. + */ + Nu_DataSourceSetOtherLen(pDataSource, pThread->thCompThreadEOF); + + if (NuGetThreadID(pThread) == kNuThreadIDFilename) { + /* special handling for filename updates */ + char* savedCopy = nil; + err = Nu_CopyPresizedToArchive(pArchive, pDataSource, + NuGetThreadID(pThread), fp, pThread, &savedCopy); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "thread update failed"); + goto bail; + } + Nu_SetNewThreadFilename(pArchive, pRecord, savedCopy); + + } else { + err = Nu_CopyPresizedToArchive(pArchive, pDataSource, + NuGetThreadID(pThread), fp, pThread, nil); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "thread update failed"); + goto bail; + } + } + Assert((ulong)ftell(fp) == pThread->fileOffset + threadBufSize); + +skip_update: + Nu_DataSourceUnPrepareInput(pArchive, pDataSource); + +bail: + return err; +} + + +/* + * Handle all "add" threadMods in the current record. This is invoked both + * when creating a new record from the "new" list or constructing a + * modified record from the "copy" list. + * + * Writes to either the archiveFp or tmpFp; pass in the correct one, + * properly positioned. + * + * If something goes wrong with one of the "adds", this will return + * immediately with kNuErrSkipped. The caller is expected to abort the + * entire record, so there's no point in continuing to process other + * threads. + * + * Possible side-effects on "pRecord": disk image fields may be revised + * (storage type, extra type), and threadFilename may be updated. + */ +static NuError +Nu_HandleAddThreadMods(NuArchive* pArchive, NuRecord* pRecord, + NuThreadID threadID, Boolean doKeepFirstOnly, NuNewThreads* pNewThreads, + FILE* dstFp) +{ + NuError err = kNuErrNone; + + NuProgressData progressData; + NuProgressData* pProgressData; + NuThreadMod* pThreadMod; + NuThread* pNewThread; + Boolean foundOne = false; + + /* + * Now find all "add" threadMods with matching threadIDs. Allow + * matching by wildcards, but don't re-use "used" entries. + */ + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + if (pThreadMod->entry.kind == kNuThreadModAdd && + !pThreadMod->entry.generic.used && + (pThreadMod->entry.add.threadID == threadID || + threadID == kNuThreadIDWildcard)) + { + DBUG(("+++ found ADD for 0x%08lx\n", pThreadMod->entry.add.threadID)); + pThreadMod->entry.generic.used = true; + + /* if we're adding filename threads, stop after first one */ + /* [shouldn't be able to happen... we only allow one filename!] */ + if (doKeepFirstOnly && foundOne) { + Assert(0); /* can this happen?? */ + continue; + } + foundOne = true; + + if (!Nu_NewThreads_HasRoom(pNewThreads)) { + Assert(0); + err = kNuErrInternal; + goto bail; + } + + /* if this is a data thread, prepare the progress message */ + pProgressData = nil; + if (NuThreadIDGetClass(pThreadMod->entry.add.threadID) == + kNuThreadClassData) + { + /* + * We're going to show the name as it appears in the + * archive, rather than the name of the file we're + * reading the data out of. We could do this differently + * for a "file" data source, but we might as well be + * consistent. + * + * [Actually, the above remark is bogus. During a bulk add + * there's no other way to recover the original filename. + * Do something different here for data sinks with + * filenames attached. ++ATM 2003/02/17] + */ + if (Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource) + == kNuDataSourceFromFile) + { + /* use on-disk filename */ + err = Nu_ProgressDataInit_Compress(pArchive, &progressData, + pRecord, Nu_DataSourceFile_GetPathname( + pThreadMod->entry.add.pDataSource)); + } else { + /* use archive filename for both */ + err = Nu_ProgressDataInit_Compress(pArchive, &progressData, + pRecord, pRecord->filename); + } + BailError(err); + + /* send initial progress so they see name if "open" fails */ + progressData.state = kNuProgressOpening; + err = Nu_SendInitialProgress(pArchive, &progressData); + BailError(err); + + pProgressData = &progressData; + } + + /* get new thread storage, and init the thread's data offset */ + /* (the threadIdx is set by GetNext) */ + pNewThread = Nu_NewThreads_GetNext(pNewThreads, pArchive); + pNewThread->threadIdx = pThreadMod->entry.add.threadIdx; + err = Nu_FTell(dstFp, &pNewThread->fileOffset); + BailError(err); + + /* this returns kNuErrSkipped if user elects to skip */ + err = Nu_DataSourcePrepareInput(pArchive, + pThreadMod->entry.add.pDataSource); + BailError(err); + + /* + * If they're adding a disk image thread, make sure the disk- + * related fields in the record header are correct. + */ + if (pThreadMod->entry.add.threadID == kNuThreadIDDiskImage) { + const NuDataSource* pDataSource = + pThreadMod->entry.add.pDataSource; + ulong uncompLen; + + if (Nu_DataSourceGetThreadFormat(pDataSource) == + kNuThreadFormatUncompressed) + { + uncompLen = Nu_DataSourceGetDataLen(pDataSource); + } else { + uncompLen = Nu_DataSourceGetOtherLen(pDataSource); + } + + err = Nu_UpdateDiskImageFields(pArchive, pRecord, uncompLen); + BailError(err); + } + + if (Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource) == + kNuDataSourceFromFile) + { + DBUG(("+++ ADDING from '%s' for '%s' (idx=%ld id=0x%08lx)\n", + Nu_DataSourceFile_GetPathname(pThreadMod->entry.add.pDataSource), + pRecord->filename, + pThreadMod->entry.add.threadIdx, + pThreadMod->entry.add.threadID)); + } else { + DBUG(("+++ ADDING from (type=%d) for '%s' (idx=%ld id=0x%08lx)\n", + Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource), + pRecord->filename, + pThreadMod->entry.add.threadIdx, + pThreadMod->entry.add.threadID)); + } + + if (pThreadMod->entry.add.threadID == kNuThreadIDFilename) { + /* filenames are special */ + char* savedCopy = nil; + + Assert(pThreadMod->entry.add.threadFormat == + kNuThreadFormatUncompressed); + err = Nu_CopyPresizedToArchive(pArchive, + pThreadMod->entry.add.pDataSource, + pThreadMod->entry.add.threadID, + dstFp, pNewThread, &savedCopy); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "fn thread add failed"); + goto bail; + } + /* NOTE: on failure, "dropRecFilename" is still set. This + doesn't matter though, since we'll either copy the original + record, or abort the entire thing. At any rate, we can't + just clear it, because we've already made space for the + record header, and didn't include the filename in it. */ + + Nu_SetNewThreadFilename(pArchive, pRecord, savedCopy); + + } else if (pThreadMod->entry.add.isPresized) { + /* don't compress, just copy */ + Assert(pThreadMod->entry.add.threadFormat == + kNuThreadFormatUncompressed); + err = Nu_CopyPresizedToArchive(pArchive, + pThreadMod->entry.add.pDataSource, + pThreadMod->entry.add.threadID, + dstFp, pNewThread, nil); + /* fall through with err */ + + } else { + /* compress (possibly by just copying) the source to dstFp */ + err = Nu_CompressToArchive(pArchive, + pThreadMod->entry.add.pDataSource, + pThreadMod->entry.add.threadID, + Nu_DataSourceGetThreadFormat( + pThreadMod->entry.add.pDataSource), + pThreadMod->entry.add.threadFormat, + pProgressData, dstFp, pNewThread); + /* fall through with err */ + } + + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "thread add failed"); + goto bail; + } + Nu_DataSourceUnPrepareInput(pArchive, + pThreadMod->entry.add.pDataSource); + } + + pThreadMod = pThreadMod->pNext; + } + +bail: + return err; +} + +/* + * Run through the list of threads and threadMods, looking for threads + * with an ID that matches "threadID". When one is found, we take all + * the appropriate steps to get the data into the archive. + * + * This takes into account the ThreadMods, including "delete" (ignore + * existing thread), "update" (use data from threadMod instead of + * existing thread), and "add" (use data from threadMod). + * + * Threads that are used or discarded will have a flag set so that + * future examinations, notably those where "threadID" is a wildcard, + * will ignore them. + * + * Always writes to the temp file. The temp file must be positioned in + * the proper location. + * + * "pRecord" must be from the "copy" data set. + */ +static NuError +Nu_ConstructArchiveThreads(NuArchive* pArchive, NuRecord* pRecord, + NuThreadID threadID, Boolean doKeepFirstOnly, NuNewThreads* pNewThreads) +{ + NuError err = kNuErrNone; + NuThread* pThread; + NuThreadMod* pThreadMod; + Boolean foundOne = false; + NuThread* pNewThread; + int idx; + + /* + * First, find any existing threads that match. If they have a + * "delete" threadMod, ignore them; if they have an "update" threadMod, + * use that instead. + */ + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + DBUG(("+++ THREAD #%d (used=%d)\n", idx, pThread->used)); + if (threadID == kNuThreadIDWildcard || + threadID == NuGetThreadID(pThread)) + { + /* match! */ + DBUG(("+++ MATCH THREAD #%d\n", idx)); + if (pThread->used) + continue; + pThread->used = true; /* no matter what, we're done with this */ + + pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, + pThread->threadIdx); + + if (pThreadMod != nil) { + /* + * The thread has a related ThreadMod. Deal with it. + */ + + pThreadMod->entry.generic.used = true; /* for Assert, later */ + + if (pThreadMod->entry.kind == kNuThreadModDelete) { + /* this is a delete, ignore this thread */ + DBUG(("+++ deleted %ld!\n", pThread->threadIdx)); + continue; + } else if (pThreadMod->entry.kind == kNuThreadModUpdate) { + /* update pre-sized data in place */ + + DBUG(("+++ updating threadIdx=%ld\n", + pThread->threadIdx)); + + /* only one filename per customer */ + /* [does this make sense here??] */ + if (doKeepFirstOnly && foundOne) + continue; + foundOne = true; + + /* add an entry in the new list of threads */ + pNewThread = Nu_NewThreads_GetNext(pNewThreads, pArchive); + Nu_CopyThreadContents(pNewThread, pThread); + + /* set the thread's file offset */ + err = Nu_FTell(pArchive->tmpFp, &pNewThread->fileOffset); + BailError(err); + + err = Nu_ConstructArchiveUpdate(pArchive, pArchive->tmpFp, + pRecord, pNewThread, pThreadMod); + BailError(err); + } else { + /* unknown ThreadMod type - this shouldn't happen! */ + Assert(0); + err = kNuErrInternal; + goto bail; + } + } else { + /* + * Thread is unmodified. + */ + + /* only one filename per customer */ + if (doKeepFirstOnly && foundOne) + continue; + foundOne = true; + + /* + * Copy the original data to the new location. Right now, + * pThread->fileOffset has the correct offset for the + * original file, and tmpFp is positioned at the correct + * output offset. We want to seek the source file, replace + * pThread->fileOffset with the *new* offset, and then + * copy the data. + * + * This feels skankier than it really is because we're + * using the thread in the "copy" set for two purposes. + * It'd be cleaner to pass in the thread from the "orig" + * set, but there's really not much value in doing so. + * + * [should this have a progress meter associated?] + */ + DBUG(("+++ just copying threadIdx=%ld\n", + pThread->threadIdx)); + err = Nu_FSeek(pArchive->archiveFp, pThread->fileOffset, + SEEK_SET); + BailError(err); + err = Nu_FTell(pArchive->tmpFp, &pThread->fileOffset); + BailError(err); + err = Nu_CopyFileSection(pArchive, pArchive->tmpFp, + pArchive->archiveFp, pThread->thCompThreadEOF); + BailError(err); + + /* copy an entry over into the replacement thread list */ + pNewThread = Nu_NewThreads_GetNext(pNewThreads, pArchive); + Nu_CopyThreadContents(pNewThread, pThread); + } + } + } + + /* no need to check for "add" mods; there can't be one for us */ + if (doKeepFirstOnly && foundOne) + goto bail; + + /* + * Now handle any "add" threadMods. + */ + err = Nu_HandleAddThreadMods(pArchive, pRecord, threadID, doKeepFirstOnly, + pNewThreads, pArchive->tmpFp); + BailError(err); + +bail: + return err; +} + +/* + * Construct a record in the temp file, based on the contents of the + * original. Takes into account "dirty" headers and threadMod changes. + * + * Pass in the record from the *copy* set, not the original. The temp + * file should be positioned at the correct spot. + * + * If something goes wrong, and the user wants to abort the record but + * not the entire operation, we rewind the temp file to the initial + * position. It's not possible to abandon part of a record; either you + * get everything you asked for or nothing at all. We then return + * kNuErrSkipped, which should cause the caller to simply copy the + * previous record. + */ +static NuError +Nu_ConstructArchiveRecord(NuArchive* pArchive, NuRecord* pRecord) +{ + NuError err; + NuNewThreads* pNewThreads = nil; + long threadDisp; + long initialOffset, finalOffset; + long numThreads, numFilenameThreads; + int newHeaderSize; + + Assert(pArchive != nil); + Assert(pRecord != nil); + + DBUG(("--- Reconstructing '%s'\n", pRecord->filename)); + + err = Nu_FTell(pArchive->tmpFp, &initialOffset); + BailError(err); + Assert(initialOffset != 0); + + /* + * Figure out how large the record header is. This requires + * measuring the static elements, the not-so-static elements like + * the GS/OS option list and perhaps the filename, and getting an + * accurate count of the number of threads. + * + * Since we're going to keep any option lists and extra junk stored in + * the header originally, the size of the new base record header is + * equal to the original recAttribCount. The attribute count conveniently + * does *not* include the filename, so if we've moved it out of the + * record header and into a thread, it won't affect us here. + */ + err = Nu_CountEventualThreads(pRecord, &numThreads, &numFilenameThreads); + BailError(err); + Assert(numThreads > 0); /* threadless records should already be gone */ + if (numThreads <= 0) { + err = kNuErrInternal; + goto bail; + } + + /* + * Handle filename deletion. + */ + if (!numFilenameThreads && pRecord->threadFilename) { + /* looks like a previously existing filename thread got removed */ + DBUG(("--- Dropping thread filename '%s'\n", pRecord->threadFilename)); + if (pRecord->filename == pRecord->threadFilename) + pRecord->filename = nil; /* don't point at freed memory! */ + Nu_Free(pArchive, pRecord->threadFilename); + pRecord->threadFilename = nil; + + /* I don't think this is possible, but check it anyway */ + if (pRecord->filename == nil && pRecord->recFilename != nil && + !pRecord->dropRecFilename) + { + DBUG(("--- HEY, how did this happen?\n")); + pRecord->filename = pRecord->recFilename; + } + } + if (pRecord->filename == nil) + pRecord->filename = kNuDefaultRecordName; + + /* + * Make a hole, including the header filename if we're not dropping it. + * + * This ignores fake vs. non-fake threads, because once we're done + * writing they're all "real". + */ + newHeaderSize = pRecord->recAttribCount + numThreads * kNuThreadHeaderSize; + if (!pRecord->dropRecFilename) + newHeaderSize += pRecord->recFilenameLength; + + DBUG(("+++ new header size = %d\n", newHeaderSize)); + err = Nu_FSeek(pArchive->tmpFp, newHeaderSize, SEEK_CUR); + BailError(err); + + /* + * It is important to arrange the threads in a specific order. For + * example, we can have trouble doing a streaming archive read if the + * filename isn't the first thread the collection. It's prudent to + * mimic GSHK's behavior, so we act to ensure that things appear in + * the following order: + * + * (1) filename thread + * (2) comment thread(s) + * (3) data thread with data fork + * (4) data thread with disk image + * (5) data thread with rsrc fork + * (6) everything else + * + * If we ended up with two filename threads (perhaps some other aberrant + * application created the archive; we certainly wouldn't do that), we + * keep the first one. We're more lenient on propagating strange + * multiple comment and data thread situations, even though the + * thread updating mechanism in this library won't necessarily allow + * such situations. + */ + + err = Nu_NewThreads_New(pArchive, &pNewThreads, numThreads); + BailError(err); + + err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDFilename, + true, pNewThreads); + BailError(err); + err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDComment, + false, pNewThreads); + BailError(err); + err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDDataFork, + false, pNewThreads); + BailError(err); + err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDDiskImage, + false, pNewThreads); + BailError(err); + err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDRsrcFork, + false, pNewThreads); + BailError(err); + err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDWildcard, + false, pNewThreads); + BailError(err); + + /* + * Perform some sanity checks. + */ + Assert(!Nu_NewThreads_HasRoom(pNewThreads)); + + /* verify that all threads and threadMods have been touched */ + if (!Nu_VerifyAllTouched(pArchive, pRecord)) { + err = kNuErrInternal; + Assert(0); + goto bail; + } + + /* verify that file displacement is where it should be */ + threadDisp = (long)Nu_NewThreads_TotalCompThreadEOF(pNewThreads); + err = Nu_FTell(pArchive->tmpFp, &finalOffset); + BailError(err); + Assert(finalOffset > initialOffset); + if (finalOffset - (initialOffset + newHeaderSize) != threadDisp) { + Nu_ReportError(NU_BLOB, kNuErrNone, + "ERROR: didn't end up where expected (%ld %ld %ld)", + initialOffset, finalOffset, threadDisp); + err = kNuErrInternal; + Assert(0); + goto bail; + } + + /* + * Free existing Threads and ThreadMods, and move the list from + * pNewThreads over. + */ + Nu_Free(pArchive, pRecord->pThreads); + Nu_FreeThreadMods(pArchive, pRecord); + pRecord->pThreads = Nu_NewThreads_DonateThreads(pNewThreads); + pRecord->recTotalThreads = Nu_NewThreads_GetNumThreads(pNewThreads); + + /* + * Now, seek back and write the record header. + */ + err = Nu_FSeek(pArchive->tmpFp, initialOffset, SEEK_SET); + BailError(err); + err = Nu_WriteRecordHeader(pArchive, pRecord, pArchive->tmpFp); + BailError(err); + + Assert(newHeaderSize == (int) pRecord->recHeaderLength); + + /* + * Seek forward once again, so we are positioned at the correct + * place to write the next record. + */ + err = Nu_FSeek(pArchive->tmpFp, finalOffset, SEEK_SET); + BailError(err); + + /* update the record's fileOffset to reflect its new position */ + DBUG(("+++ record shifted by %ld bytes\n", + initialOffset - pRecord->fileOffset)); + pRecord->fileOffset = initialOffset; + +bail: + if (err == kNuErrSkipped) { + /* + * Something went wrong and they want to skip this record but + * keep going otherwise. We need to back up in the file so the + * original copy of the record can go here. + */ + err = Nu_FSeek(pArchive->tmpFp, initialOffset, SEEK_SET); + if (err == kNuErrNone) + err = kNuErrSkipped; /* tell the caller we skipped it */ + } + + Nu_NewThreads_Free(pArchive, pNewThreads); + return err; +} + + +/* + * Construct a new record and add it to the original or temp file. The + * new record has no threads but some number of threadMods. (This + * function is a cousin to Nu_ConstructArchiveRecord.) "pRecord" must + * come from the "new" record set. + * + * The original/temp file should be positioned at the correct spot. + * + * If something goes wrong, and the user wants to abort the record but + * not the entire operation, we rewind the temp file to the initial + * position and return kNuErrSkipped. + */ +static NuError +Nu_ConstructNewRecord(NuArchive* pArchive, NuRecord* pRecord, FILE* fp) +{ + NuError err; + NuNewThreads* pNewThreads = nil; + NuThreadMod* pThreadMod; + long threadDisp; + long initialOffset, finalOffset; + long numThreadMods, numFilenameThreads; + int newHeaderSize; + + Assert(pArchive != nil); + Assert(pRecord != nil); + + DBUG(("--- Constructing '%s'\n", pRecord->filename)); + + err = Nu_FTell(fp, &initialOffset); + BailError(err); + Assert(initialOffset != 0); + + /* + * Quick sanity check: verify that the record has no threads of its + * own, and all threadMods are "add" threadMods. While we're at it, + * make ourselves useful by counting up the number of eventual + * threads, and verify that there is exactly one filename thread. + */ + Assert(pRecord->pThreads == nil); + + numThreadMods = 0; + numFilenameThreads = 0; + pThreadMod = pRecord->pThreadMods; + while (pThreadMod) { + if (pThreadMod->entry.kind != kNuThreadModAdd) { + Nu_ReportError(NU_BLOB, kNuErrNone, "unexpected non-add threadMod"); + err = kNuErrInternal; + Assert(0); + goto bail; + } + numThreadMods++; + if (pThreadMod->entry.add.threadID == kNuThreadIDFilename) + numFilenameThreads++; + + pThreadMod = pThreadMod->pNext; + } + Assert(numFilenameThreads <= 1); + + /* + * If there's no filename thread, make one. We do this for brand-new + * records when the application doesn't explicitly add a thread. + */ + if (!numFilenameThreads) { + NuDataSource* pTmpDataSource = nil; + NuThreadMod* pNewThreadMod = nil; + int len, maxLen; + + /* + * Generally speaking, the "add file" call should set the + * filename. If somehow it didn't, assign a default. + */ + if (pRecord->filename == nil) { + pRecord->newFilename = strdup(kNuDefaultRecordName); + pRecord->filename = pRecord->newFilename; + } + + DBUG(("--- No filename thread found, adding one ('%s')\n", + pRecord->filename)); + + /* + * Create a trivial data source for the filename. The size of + * the filename buffer is the larger of the filename length and + * the default filename buffer size. This mimics GSHK's behavior. + * (If we're really serious about renaming it, maybe we should + * leave some extra space on the end...?) + */ + len = strlen(pRecord->filename); + maxLen = len > kNuDefaultFilenameThreadSize ? + len : kNuDefaultFilenameThreadSize; + err = Nu_DataSourceBuffer_New(kNuThreadFormatUncompressed, + maxLen, (const uchar*)pRecord->filename, 0, + strlen(pRecord->filename), nil, &pTmpDataSource); + BailError(err); + + /* put in a new "add" threadMod (which copies the data source) */ + err = Nu_ThreadModAdd_New(pArchive, kNuThreadIDFilename, + kNuThreadFormatUncompressed, pTmpDataSource, &pNewThreadMod); + Nu_DataSourceFree(pTmpDataSource); + BailError(err); + + /* add it to the list */ + Nu_RecordAddThreadMod(pRecord, pNewThreadMod); + pNewThreadMod = nil; + + numFilenameThreads++; + numThreadMods++; + } + + /* + * Figure out how large the record header is. We don't generate + * GS/OS option lists or "extra" data here, and we always put the + * filename in a thread, so the size is constant. (If somebody + * does a GS/OS or Mac port and wants to add option lists, it should + * not be hard to adjust the size accordingly.) + * + * This initializes the record's attribCount. We use the "base size" + * and add two for the (unused) filename length. + */ + pRecord->recAttribCount = kNuRecordHeaderBaseSize +2; + newHeaderSize = pRecord->recAttribCount + numThreadMods * kNuThreadHeaderSize; + + DBUG(("+++ new header size = %d\n", newHeaderSize)); + + /* leave a hole */ + err = Nu_FSeek(fp, newHeaderSize, SEEK_CUR); + BailError(err); + + /* + * It is important to arrange the threads in a specific order. See + * the comments in Nu_ConstructArchiveRecord for the rationale. + */ + err = Nu_NewThreads_New(pArchive, &pNewThreads, numThreadMods); + BailError(err); + + err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDFilename, + true, pNewThreads, fp); + BailError(err); + err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDComment, + false, pNewThreads, fp); + BailError(err); + err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDDataFork, + false, pNewThreads, fp); + BailError(err); + err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDDiskImage, + false, pNewThreads, fp); + BailError(err); + err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDRsrcFork, + false, pNewThreads, fp); + BailError(err); + err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDWildcard, + false, pNewThreads, fp); + BailError(err); + + /* + * Perform some sanity checks. + */ + Assert(!Nu_NewThreads_HasRoom(pNewThreads)); + + /* verify that all threads and threadMods have been touched */ + if (!Nu_VerifyAllTouched(pArchive, pRecord)) { + err = kNuErrInternal; + Assert(0); + goto bail; + } + + /* verify that file displacement is where it should be */ + threadDisp = Nu_NewThreads_TotalCompThreadEOF(pNewThreads); + err = Nu_FTell(fp, &finalOffset); + BailError(err); + Assert(finalOffset > initialOffset); + if (finalOffset - (initialOffset + newHeaderSize) != threadDisp) { + Nu_ReportError(NU_BLOB, kNuErrNone, + "ERROR: didn't end up where expected (%ld %ld %ld)", + initialOffset, finalOffset, threadDisp); + err = kNuErrInternal; + Assert(0); + goto bail; + } + + /* + * Install pNewThreads as the thread list. + */ + Assert(pRecord->pThreads == nil && pRecord->recTotalThreads == 0); + pRecord->pThreads = Nu_NewThreads_DonateThreads(pNewThreads); + pRecord->recTotalThreads = Nu_NewThreads_GetNumThreads(pNewThreads); + + /* + * Fill in misc record header fields. + * + * We could set recArchiveWhen here, if we wanted to override what + * the application set, but I don't think there's any value in that. + */ + pRecord->fileOffset = initialOffset; + + /* + * Now, seek back and write the record header. + */ + err = Nu_FSeek(fp, initialOffset, SEEK_SET); + BailError(err); + err = Nu_WriteRecordHeader(pArchive, pRecord, fp); + BailError(err); + + /* + * Seek forward once again, so we are positioned at the correct + * place to write the next record. + */ + err = Nu_FSeek(fp, finalOffset, SEEK_SET); + BailError(err); + + /* + * Trash the threadMods. + */ + Nu_FreeThreadMods(pArchive, pRecord); + +bail: + if (err == kNuErrSkipped) { + /* + * Something went wrong and they want to skip this record but + * keep going otherwise. We need to back up in the file so the + * next record can go here. + */ + err = Nu_FSeek(fp, initialOffset, SEEK_SET); + if (err == kNuErrNone) + err = kNuErrSkipped; /* tell the caller we skipped it */ + } + + Nu_NewThreads_Free(pArchive, pNewThreads); + return err; +} + + +/* + * Update a given record in the original archive file. + * + * "pRecord" is the record from the "copy" set. It can have the + * "dirtyHeader" flag set, and may have "update" threadMods, but + * that's all. + * + * The position of pArchive->archiveFp on entry and on exit is not + * defined. + */ +static NuError +Nu_UpdateRecordInOriginal(NuArchive* pArchive, NuRecord* pRecord) +{ + NuError err = kNuErrNone; + NuThread* pThread; + const NuThreadMod* pThreadMod; + + /* + * Loop through all threadMods. + */ + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + Assert(pThreadMod->entry.kind == kNuThreadModUpdate); + + /* find the thread associated with this threadMod */ + err = Nu_FindThreadByIdx(pRecord, pThreadMod->entry.update.threadIdx, + &pThread); + BailError(err); /* should never happen */ + + /* seek to the appropriate spot */ + err = Nu_FSeek(pArchive->archiveFp, pThread->fileOffset, SEEK_SET); + BailError(err); + + /* do the update; this updates "pThread" with the new info */ + err = Nu_ConstructArchiveUpdate(pArchive, pArchive->archiveFp, + pRecord, pThread, pThreadMod); + BailError(err); + + pThreadMod = pThreadMod->pNext; + } + + + /* + * We have to write a new record header without disturbing + * anything around it. Nothing we've done should've changed + * the size of the record header, so just go ahead and write it. + * + * We have to do this regardless of "dirtyHeader", because we just + * tweaked some of our threads around, and we need to rewrite the + * thread headers (which updates the record header CRC, and so on). + */ + err = Nu_FSeek(pArchive->archiveFp, pRecord->fileOffset, SEEK_SET); + BailError(err); + err = Nu_WriteRecordHeader(pArchive, pRecord, pArchive->archiveFp); + BailError(err); + + /* + * Let's be paranoid and verify that the write didn't overflow + * into the thread header. We compare our current offset against + * the offset of the first thread. (If we're in a weird record + * with no threads, we could compare against the offset of the + * next record, but I don't want to deal with a case that should + * never happen anyway.) + */ + DBUG(("--- record header wrote %ld bytes\n", + pArchive->currentOffset - pRecord->fileOffset)); + pThread = pRecord->pThreads; + if (pThread != nil && pArchive->currentOffset != pThread->fileOffset) { + /* guess what, we just trashed the archive */ + err = kNuErrDamaged; + Nu_ReportError(NU_BLOB, err, + "Bad record header write (off by %ld), archive damaged", + pArchive->currentOffset - pThread->fileOffset); + goto bail; + } + DBUG(("--- record header written safely\n")); + + + /* + * It's customary to throw out the thread mods when you're done. (I'm + * not really sure why I'm doing this now, but here we are.) + */ + Nu_FreeThreadMods(pArchive, pRecord); + +bail: + return err; +} + + +/* + * =========================================================================== + * Archive construction - main functions + * =========================================================================== + */ + +/* + * Fill in the temp file with the contents of the original archive. The + * file offsets and any other generated data in the "copy" set will be + * updated as appropriate, so that the "copy" set can eventually replace + * the "orig" set. + * + * On exit, pArchive->tmpFp will point at the archive EOF. + */ +static NuError +Nu_CreateTempFromOriginal(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + + Assert(pArchive->tmpFp != 0); + Assert(ftell(pArchive->tmpFp) == 0); /* should be empty as well */ + + /* + * Leave space for the master header and (if we're preserving it) any + * header gunk. + */ + Assert(!pArchive->valDiscardWrapper || pArchive->headerOffset == 0); + err = Nu_FSeek(pArchive->tmpFp, + pArchive->headerOffset + kNuMasterHeaderSize, SEEK_SET); + BailError(err); + + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + /* + * Run through the "copy" records. If the original record header is + * umodified, just copy it; otherwise write a new one with a new CRC. + */ + if (Nu_RecordSet_IsEmpty(&pArchive->copyRecordSet)) { + /* new archive or all records deleted */ + DBUG(("--- No records in 'copy' set\n")); + goto bail; + } + pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); + } else { + /* + * There's no "copy" set defined. If we have an "orig" set, we + * must be doing nothing but add files to an existing archive + * without the "modify orig" flag set. + */ + if (Nu_RecordSet_IsEmpty(&pArchive->origRecordSet)) { + DBUG(("--- No records in 'copy' or 'orig' set\n")); + goto bail; + } + pRecord = Nu_RecordSet_GetListHead(&pArchive->origRecordSet); + } + + /* + * Reconstruct or copy the records. It's probably not necessary + * to reconstruct the entire record if we're just updating the + * record header, but since all we do is copy the data anyway, + * it's not much slower. + */ + while (pRecord != nil) { + if (!pRecord->dirtyHeader && pRecord->pThreadMods == nil) { + err = Nu_CopyArchiveRecord(pArchive, pRecord); + BailError(err); + } else { + err = Nu_ConstructArchiveRecord(pArchive, pRecord); + if (err == kNuErrSkipped) { + /* + * We're going to retain the original. This requires us + * to copy the original record from the "orig" record set + * and replace what we had in the "copy" set, so that at + * the end of the day the "copy" set accurately reflects + * what's in the archive. + */ + DBUG(("--- Skipping, copying %ld instead\n", + pRecord->recordIdx)); + err = Nu_RecordSet_ReplaceRecord(pArchive, + &pArchive->copyRecordSet, pRecord, + &pArchive->origRecordSet, &pRecord); + BailError(err); + err = Nu_CopyArchiveRecord(pArchive, pRecord); + BailError(err); + } + BailError(err); + } + + pRecord = pRecord->pNext; + } + +bail: + return err; +} + + +/* + * Perform updates to certain items in the original archive. None of + * the operations changes the position of items within. + * + * On exit, pArchive->archiveFp will point at the archive EOF. + */ +static NuError +Nu_UpdateInOriginal(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + + if (!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + /* + * There's nothing for us to do; we probably just have a + * bunch of new stuff being added. + */ + DBUG(("--- UpdateInOriginal: nothing to do\n")); + goto done; + } + + /* + * Run through and process all the updates. + */ + pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); + while (pRecord != nil) { + if (pRecord->dirtyHeader || pRecord->pThreadMods != nil) { + err = Nu_UpdateRecordInOriginal(pArchive, pRecord); + BailError(err); + } + + pRecord = pRecord->pNext; + } + +done: + /* seek to the end of the archive */ + err = Nu_FSeek(pArchive->archiveFp, + pArchive->headerOffset + pArchive->masterHeader.mhMasterEOF, + SEEK_SET); + BailError(err); + +bail: + return err; +} + + +/* + * Create new records for all items in the "new" list, writing them to + * "fp" at the current offset. + * + * On completion, "fp" will point at the end of the archive. + */ +static NuError +Nu_CreateNewRecords(NuArchive* pArchive, FILE* fp) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + + pRecord = Nu_RecordSet_GetListHead(&pArchive->newRecordSet); + while (pRecord != nil) { + err = Nu_ConstructNewRecord(pArchive, pRecord, fp); + if (err == kNuErrSkipped) { + /* + * We decided to skip this record, so delete it from "new". + * + * (I think this is the only time we delete something from the + * "new" set...) + */ + NuRecord* pNextRecord = pRecord->pNext; + + DBUG(("--- Skipping, deleting new %ld\n", pRecord->recordIdx)); + err = Nu_RecordSet_DeleteRecord(pArchive, &pArchive->newRecordSet, + pRecord); + Assert(err == kNuErrNone); + BailError(err); + pRecord = pNextRecord; + } else { + BailError(err); + pRecord = pRecord->pNext; + } + } + +bail: + return err; +} + + +/* + * =========================================================================== + * Archive update helpers + * =========================================================================== + */ + +/* + * Determine if any "heavy updates" have been made. A "heavy" update is + * one that requires us to create and rename a temp file. + * + * If the "copy" record set hasn't been loaded, we're done. If it has + * been loaded, we scan through the list for thread mods other than updates + * to pre-sized fields. We also have to check to see if any records were + * deleted. + * + * At present, a "dirtyHeader" flag is not of itself cause to rebuild + * the archive, so we don't test for it here. + */ +static Boolean +Nu_NoHeavyUpdates(NuArchive* pArchive) +{ + const NuRecord* pRecord; + long count; + + /* if not loaded, then *no* changes were made to original records */ + if (!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) + return true; + + /* + * You can't add to "copy" set, so any deletions are visible by the + * reduced record count. The function that deletes records from + * which all threads have been removed should be called before we + * get here. + */ + if (Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet) != + Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet)) + { + return false; + } + + /* + * Run through the set of records, looking for a threadMod with a + * change type we can't handle in place. + */ + count = Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet); + pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); + while (count--) { + const NuThreadMod* pThreadMod; + + Assert(pRecord != nil); + + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + /* the only acceptable kind is "update" */ + if (pThreadMod->entry.kind != kNuThreadModUpdate) + return false; + + pThreadMod = pThreadMod->pNext; + } + + pRecord = pRecord->pNext; + } + + return true; +} + + +/* + * Purge any records that don't have any threads. This has to take into + * account pending modifications, so that we dispose of any records that + * have had all of their threads deleted. + * + * Simplest approach is to count up the #of "delete" mods and subtract + * it from the number of threads, skipping on if the record has any + * "add" thread mods. + */ +static NuError +Nu_PurgeEmptyRecords(NuArchive* pArchive, NuRecordSet* pRecordSet) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + NuRecord** ppRecord; + + Assert(pArchive != nil); + Assert(pRecordSet != nil); + + if (Nu_RecordSet_IsEmpty(pRecordSet)) + return kNuErrNone; + + ppRecord = Nu_RecordSet_GetListHeadPtr(pRecordSet); + Assert(ppRecord != nil); + Assert(*ppRecord != nil); + + /* maintain a pointer to the pointer, so we can delete easily */ + while (*ppRecord != nil) { + pRecord = *ppRecord; + + if (Nu_RecordIsEmpty(pArchive, pRecord)) { + DBUG(("--- Purging empty record %06ld '%s' (0x%08lx-->0x%08lx)\n", + pRecord->recordIdx, pRecord->filename, + (ulong)ppRecord, (ulong)pRecord)); + err = Nu_RecordSet_DeleteRecordPtr(pArchive, pRecordSet, ppRecord); + BailError(err); + /* pRecord is now invalid, and *ppRecord has been updated */ + } else { + ppRecord = &pRecord->pNext; + } + } + +bail: + return err; +} + + +/* + * Update the "new" master header block with the contents of the modified + * archive, and write it to the file. + * + * Pass in a correctly positioned "fp" and the total length of the archive + * file. + */ +static NuError +Nu_UpdateMasterHeader(NuArchive* pArchive, FILE* fp, long archiveEOF) +{ + NuError err; + long numRecords; + + Nu_MasterHeaderCopy(pArchive, &pArchive->newMasterHeader, + &pArchive->masterHeader); + + numRecords = 0; + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) + numRecords += Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet); + else + numRecords += Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet); + if (Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) + numRecords += Nu_RecordSet_GetNumRecords(&pArchive->newRecordSet); + #if 0 /* we allow delete-all now */ + if (numRecords == 0) { + /* don't allow empty archives */ + DBUG(("--- UpdateMasterHeader didn't find any records\n")); + err = kNuErrNoRecords; + goto bail; + } + #endif + + pArchive->newMasterHeader.mhTotalRecords = numRecords; + pArchive->newMasterHeader.mhMasterEOF = archiveEOF; + pArchive->newMasterHeader.mhMasterVersion = kNuOurMHVersion; + Nu_SetCurrentDateTime(&pArchive->newMasterHeader.mhArchiveModWhen); + + err = Nu_WriteMasterHeader(pArchive, fp, &pArchive->newMasterHeader); + BailError(err); + +bail: + return err; +} + + +/* + * Reset the temp file to a known (empty) state. + */ +static NuError +Nu_ResetTempFile(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + + /* read-only archives don't have a temp file */ + if (Nu_IsReadOnly(pArchive)) + return kNuErrNone; /* or kNuErrArchiveRO? */ + + Assert(pArchive != nil); + Assert(pArchive->tmpPathname != nil); + +#if 0 /* keep the temp file around for examination */ +if (pArchive->tmpFp != nil) { + DBUG(("--- NOT Resetting temp file\n")); + fflush(pArchive->tmpFp); + goto bail; +} +#endif + + DBUG(("--- Resetting temp file\n")); + + /* if we renamed the temp over the original, we need to open a new temp */ + if (pArchive->tmpFp == nil) { + pArchive->tmpFp = fopen(pArchive->tmpPathname, kNuFileOpenReadWriteCreat); + if (pArchive->tmpFp == nil) { + err = errno ? errno : kNuErrFileOpen; + Nu_ReportError(NU_BLOB, errno, "Unable to open temp file '%s'", + pArchive->tmpPathname); + goto bail; + } + } else { + /* + * Truncate the temp file. + */ + err = Nu_FSeek(pArchive->tmpFp, 0, SEEK_SET); + BailError(err); + err = Nu_TruncateOpenFile(pArchive->tmpFp, 0); + if (err == kNuErrInternal) { + /* do it the hard way if we don't have ftruncate or equivalent */ + err = kNuErrNone; + fclose(pArchive->tmpFp); + pArchive->tmpFp = fopen(pArchive->tmpPathname, kNuFileOpenWriteTrunc); + if (pArchive->tmpFp == nil) { + err = errno ? errno : kNuErrFileOpen; + Nu_ReportError(NU_BLOB, err, "failed truncating tmp file"); + goto bail; + } + fclose(pArchive->tmpFp); + pArchive->tmpFp = + fopen(pArchive->tmpPathname, kNuFileOpenReadWriteCreat); + if (pArchive->tmpFp == nil) { + err = errno ? errno : kNuErrFileOpen; + Nu_ReportError(NU_BLOB, err, "Unable to open temp file '%s'", + pArchive->tmpPathname); + goto bail; + } + } + } + +bail: + return err; +} + +/* + * Ensure that all of the threads and threadMods in a record are in + * a pristine state, i.e. "threads" aren't marked used and "threadMods" + * don't even exist. This is done as we are cleaning up the record sets + * after a successful (or aborted) update. + */ +static NuError +Nu_RecordResetUsedFlags(NuArchive* pArchive, NuRecord* pRecord) +{ + NuThread* pThread; + long idx; + + Assert(pArchive != nil); + Assert(pRecord != nil); + + /* these should already be clear */ + if (pRecord->pThreadMods) { + Assert(0); + return kNuErrInternal; + } + + /* these might still be set */ + for (idx = 0; idx < (long)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + pThread->used = false; + } + + /* and this */ + pRecord->dirtyHeader = false; + + return kNuErrNone; +} + +/* + * Invoke Nu_RecordResetUsedFlags on all records in a record set. + */ +static NuError +Nu_ResetUsedFlags(NuArchive* pArchive, NuRecordSet* pRecordSet) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + + pRecord = Nu_RecordSet_GetListHead(pRecordSet); + while (pRecord != nil) { + err = Nu_RecordResetUsedFlags(pArchive, pRecord); + if (err != kNuErrNone) { + Assert(0); + break; + } + + pRecord = pRecord->pNext; + } + + return err; +} + + +/* + * If nothing in the "copy" set has actually been disturbed, throw it out. + */ +static void +Nu_ResetCopySetIfUntouched(NuArchive* pArchive) +{ + const NuRecord* pRecord; + + /* have any records been deleted? */ + if (Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet) != + pArchive->masterHeader.mhTotalRecords) + { + return; + } + + /* do we have any thread mods or dirty record headers? */ + pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); + while (pRecord != nil) { + if (pRecord->pThreadMods != nil || pRecord->dirtyHeader) + return; + + pRecord = pRecord->pNext; + } + + /* looks like nothing has been touched */ + DBUG(("--- copy set untouched, trashing it\n")); + (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); +} + + +/* + * GSHK always adds a comment to the first new record added to an archive. + * Imitate this behavior. + */ +static NuError +Nu_AddCommentToFirstNewRecord(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + NuThreadMod* pThreadMod = nil; + NuThreadMod* pExistingThreadMod = nil; + NuDataSource* pDataSource = nil; + + /* if there aren't any records there, skip this */ + if (Nu_RecordSet_IsEmpty(&pArchive->newRecordSet)) + goto bail; + + pRecord = Nu_RecordSet_GetListHead(&pArchive->newRecordSet); + + /* + * See if this record already has a comment. If so, don't add + * another one. + */ + err = Nu_ThreadModAdd_FindByThreadID(pRecord, kNuThreadIDComment, + &pExistingThreadMod); + if (err == kNuErrNone) { + DBUG(("+++ record already has a comment, not adding another\n")); + goto bail; /* already exists */ + } + err = kNuErrNone; + + /* create a new data source with nothing in it */ + err = Nu_DataSourceBuffer_New(kNuThreadFormatUncompressed, + kNuDefaultCommentSize, nil, 0, 0, nil, &pDataSource); + BailError(err); + Assert(pDataSource != nil); + + /* create a new ThreadMod */ + err = Nu_ThreadModAdd_New(pArchive, kNuThreadIDComment, + kNuThreadFormatUncompressed, pDataSource, &pThreadMod); + BailError(err); + Assert(pThreadMod != nil); + /*pDataSource = nil;*/ /* ThreadModAdd_New makes a copy */ + + /* add the thread mod to the record */ + Nu_RecordAddThreadMod(pRecord, pThreadMod); + pThreadMod = nil; /* don't free on exit */ + +bail: + Nu_ThreadModFree(pArchive, pThreadMod); + Nu_DataSourceFree(pDataSource); + return err; +} + + +/* + * =========================================================================== + * Main entry points + * =========================================================================== + */ + +/* + * Force all deferred changes to occur. + * + * If the flush fails, the archive state may be aborted or even placed + * into read-only mode to prevent problems from compounding. + * + * If the things this function is doing aren't making any sense at all, + * read "NOTES.txt" for an introduction. + */ +NuError +Nu_Flush(NuArchive* pArchive, long* pStatusFlags) +{ + NuError err = kNuErrNone; + Boolean canAbort = true; + Boolean writeToTemp = true; + Boolean deleteAll = false; + long initialEOF, finalOffset; + + DBUG(("--- FLUSH\n")); + + if (pStatusFlags == nil) + return kNuErrInvalidArg; + /* these do get set on error, so clear them no matter what */ + *pStatusFlags = 0; + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + + err = Nu_GetFileLength(pArchive, pArchive->archiveFp, &initialEOF); + BailError(err); + + /* + * Step 1: figure out if we have anything to do. If the "copy" and "new" + * lists are empty, then there's nothing for us to do. + * + * As a special case, we test for an archive that had all of its + * records deleted. This looks a lot like an archive that has had + * nothing done, because we would have made a "copy" list and then + * deleted all the records, leaving us with an empty list. (The + * difference is that an untouched archive wouldn't have a "copy" + * list allocated.) + * + * In some cases, such as doing a bulk delete that doesn't end up + * matching anything or an attempted UpdatePresizedThread on a thread + * that isn't actually pre-sized, we create the "copy" list but don't + * actually change anything. We deal with that by frying the "copy" + * list if it doesn't have anything interesting in it (i.e. it's an + * exact match of the "orig" list). + */ + Nu_ResetCopySetIfUntouched(pArchive); + if (Nu_RecordSet_IsEmpty(&pArchive->copyRecordSet) && + Nu_RecordSet_IsEmpty(&pArchive->newRecordSet)) + { + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + DBUG(("--- All records deleted!\n")); + #if 0 + /* + * Options: + * (1) allow it, leaving an archive with nothing but a header + * that will probably be rejected by other NuFX applications + * (2) reject it, returning an error + * (3) allow it, and just delete the original archive + * + * I dislike #1, and #3 can be implemented by the application + * when it gets a #2. + */ + err = kNuErrAllDeleted; + goto bail; + #else + /* + * (4) go ahead and delete everything, then mark the archive + * as brand new, so that closing the archive with new + * records in it will trigger deletion of the archive file. + */ + deleteAll = true; + #endif + } else { + DBUG(("--- Nothing pending\n")); + goto flushed; + } + } + + /* if we have any changes, we certainly should have the TOC by now */ + Assert(pArchive->haveToc); + Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); + + /* + * Step 2: purge any records from the "copy" and "new" lists that don't + * have any threads. You can't delete threads from the "new" list, but + * it's possible somebody called NuAddRecord and never put anything in it. + */ + err = Nu_PurgeEmptyRecords(pArchive, &pArchive->copyRecordSet); + BailError(err); + err = Nu_PurgeEmptyRecords(pArchive, &pArchive->newRecordSet); + BailError(err); + + /* we checked delete-all actions above, so just check for empty */ + if (Nu_RecordSet_IsEmpty(&pArchive->copyRecordSet) && + Nu_RecordSet_IsEmpty(&pArchive->newRecordSet) && + !deleteAll) + { + DBUG(("--- Nothing pending after purge\n")); + goto flushed; + } + + /* + * Step 3: if we're in ShrinkIt-compatibility mode, add a comment + * thread to the first record in the new list. GSHK does this every + * time it adds files, regardless of the prior contents of the archive. + */ + if (pArchive->valMimicSHK) { + err = Nu_AddCommentToFirstNewRecord(pArchive); + BailError(err); + } + + /* + * Step 4: decide if we want to make changes in place, or write to + * a temp file. Any deletions or additions to existing records will + * require writing to a temp file. Additions of new records and + * updates to pre-sized threads can be done in place. + */ + writeToTemp = true; + if (pArchive->valModifyOrig && Nu_NoHeavyUpdates(pArchive)) + writeToTemp = false; + /* discard the wrapper, if desired */ + if (writeToTemp && pArchive->valDiscardWrapper) + pArchive->headerOffset = 0; + + /* + * Step 5: handle updates to existing records. + */ + if (!writeToTemp) { + /* + * Step 5a: modifying in place, process all UPDATE ThreadMods now. + */ + DBUG(("--- No heavy updates found, updating in place\n")); + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) + canAbort = false; /* modifying original, can't cleanly abort */ + + err = Nu_UpdateInOriginal(pArchive); + if (err == kNuErrDamaged) + *pStatusFlags |= kNuFlushCorrupted; + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "update to original failed"); + goto bail; + } + } else { + /* + * Step 5b: not modifying in place, reconstruct the appropriate + * parts of the original archive in the temp file, possibly copying + * the front bits over first. Updates and thread-adds will be + * done here. + */ + DBUG(("--- Updating to temp file (valModifyOrig=%ld)\n", + pArchive->valModifyOrig)); + err = Nu_CreateTempFromOriginal(pArchive); + if (err != kNuErrNone) { + DBUG(("--- Create temp from original failed\n")); + goto bail; + } + } + /* on completion, tmpFp (or archiveFp) points to current archive EOF */ + + /* + * Step 6: add the new records from the "new" list, if any. Add a + * filename thread to records where one wasn't provided. These records + * are either added to the original archive or the temp file as + * appropriate. + */ + if (writeToTemp) + err = Nu_CreateNewRecords(pArchive, pArchive->tmpFp); + else + err = Nu_CreateNewRecords(pArchive, pArchive->archiveFp); + BailError(err); + + /* on completion, tmpFp (or archiveFp) points to current archive EOF */ + + /* + * Step 7: truncate the archive. This isn't strictly necessary. It + * comes in handy if we were compressing the very last file and it + * actually expanded. We went back and wrote the uncompressed data, + * but there's a bunch of junk after it from the first try. + * + * On systems like Win32 that don't support ftruncate, this will fail, + * so we just ignore the result. + */ + if (writeToTemp) { + err = Nu_FTell(pArchive->tmpFp, &finalOffset); + BailError(err); + (void) Nu_TruncateOpenFile(pArchive->tmpFp, finalOffset); + } else { + err = Nu_FTell(pArchive->archiveFp, &finalOffset); + BailError(err); + (void) Nu_TruncateOpenFile(pArchive->archiveFp, finalOffset); + } + + /* + * Step 8: create an updated master header, and write it to the + * appropriate file. The "newMasterHeader" field in pArchive will + * hold the new header. + */ + Assert(!pArchive->newMasterHeader.isValid); + if (writeToTemp) { + err = Nu_FSeek(pArchive->tmpFp, pArchive->headerOffset, SEEK_SET); + BailError(err); + err = Nu_UpdateMasterHeader(pArchive, pArchive->tmpFp, + finalOffset - pArchive->headerOffset); + /* fall through with err */ + } else { + err = Nu_FSeek(pArchive->archiveFp, pArchive->headerOffset, SEEK_SET); + BailError(err); + err = Nu_UpdateMasterHeader(pArchive, pArchive->archiveFp, + finalOffset - pArchive->headerOffset); + /* fall through with err */ + } + if (err == kNuErrNoRecords && !deleteAll) { + /* + * Somehow we ended up without any records at all. If we managed + * to get this far, it could only be because the user told us to + * skip adding everything. + */ + Nu_ReportError(NU_BLOB, kNuErrNone, "no records in this archive"); + goto bail; + } else if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "failed writing master header"); + goto bail; + } + Assert(pArchive->newMasterHeader.isValid); + + + /* + * Step 9: carry forward the BXY, SEA, or BSE header, if necessary. This + * implicitly assumes that the header doesn't change size. If this + * assumption is invalid, we'd need to adjust "headerOffset" earlier, + * or do lots of data copying. Looks like Binary II and SEA headers + * are both fixed size, so we should be okay. + * + * We also carry forward any unrecognized junk. + */ + if (pArchive->headerOffset) { + if (writeToTemp) { + if (!pArchive->valDiscardWrapper) { + DBUG(("--- Preserving wrapper\n")); + /* copy header to temp */ + err = Nu_CopyWrapperToTemp(pArchive); + BailError(err); + /* update fields that require it */ + err = Nu_UpdateWrapper(pArchive, pArchive->tmpFp); + BailError(err); + /* check the padding */ + err = Nu_AdjustWrapperPadding(pArchive, pArchive->tmpFp); + BailError(err); + } + } else { + /* may need to tweak what's in place? */ + DBUG(("--- Updating wrapper\n")); + err = Nu_UpdateWrapper(pArchive, pArchive->archiveFp); + BailError(err); + /* should only be necessary if we've added new records */ + err = Nu_AdjustWrapperPadding(pArchive, pArchive->archiveFp); + BailError(err); + } + } + + /* + * Step 10: if necessary, remove the original file and rename the + * temp file over it. + * + * I'm not messing with access permissions on the archive file here, + * because if they opened it read-write then the archive itself + * must also be read-write (unless somebody snuck in and chmodded it + * while we were busy). The temp file is certainly writable, so we + * should be able to just leave it all alone. + * + * I'm closing both temp and archive before renaming, because on some + * operating systems you can't do certain things with open files. + */ + if (writeToTemp) { + canAbort = false; /* no going back */ + *pStatusFlags |= kNuFlushSucceeded; /* temp file is fully valid */ + + fclose(pArchive->archiveFp); + pArchive->archiveFp = nil; + + err = Nu_DeleteArchiveFile(pArchive); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "unable to remove original archive"); + Nu_ReportError(NU_BLOB, kNuErrNone, "New data is in '%s'", + pArchive->tmpPathname); + *pStatusFlags |= kNuFlushInaccessible; + goto bail_reopen; /* must re-open archiveFp */ + } + + fclose(pArchive->tmpFp); + pArchive->tmpFp = nil; + + err = Nu_RenameTempToArchive(pArchive); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "unable to rename temp file"); + Nu_ReportError(NU_BLOB, kNuErrNone, + "NOTE: only copy of archive is in '%s'", pArchive->tmpPathname); + /* maintain Entry.c semantics (and keep them from removing temp) */ + Nu_Free(pArchive, pArchive->archivePathname); + pArchive->archivePathname = nil; + Nu_Free(pArchive, pArchive->tmpPathname); + pArchive->tmpPathname = nil; + /* bail will put us into read-only mode, which is what we want */ + goto bail; + } + +bail_reopen: + pArchive->archiveFp = fopen(pArchive->archivePathname, + kNuFileOpenReadWrite); + if (pArchive->archiveFp == nil) { + err = errno ? errno : -1; + Nu_ReportError(NU_BLOB, err, + "unable to reopen archive file '%s' after rename", + pArchive->archivePathname); + *pStatusFlags |= kNuFlushInaccessible; + goto bail; /* the Entry.c funcs will obstruct further use */ + } + + if (err != kNuErrNone) // earlier failure? + goto bail; + } else { + fflush(pArchive->archiveFp); + if (ferror(pArchive->archiveFp)) { + err = kNuErrFileWrite; + Nu_ReportError(NU_BLOB, kNuErrNone, "final archive flush failed"); + *pStatusFlags |= kNuFlushCorrupted; + goto bail; + } + canAbort = false; + *pStatusFlags |= kNuFlushSucceeded; + } + + Assert(canAbort == false); + + /* + * Step 11: clean up data structures. If we have a "copy" list, then + * throw out the "orig" list and move the "copy" list over it. Append + * anything in the "new" list to it. Move the "new" master header + * over the original. + */ + Assert(pArchive->newMasterHeader.isValid); + Nu_MasterHeaderCopy(pArchive, &pArchive->masterHeader, + &pArchive->newMasterHeader); + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + err = Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->origRecordSet); + BailError(err); + err = Nu_RecordSet_MoveAllRecords(pArchive, &pArchive->origRecordSet, + &pArchive->copyRecordSet); + BailError(err); + } + err = Nu_RecordSet_MoveAllRecords(pArchive, &pArchive->origRecordSet, + &pArchive->newRecordSet); + BailError(err); + err = Nu_ResetUsedFlags(pArchive, &pArchive->origRecordSet); + BailError(err); + +flushed: + /* + * Step 12: reset the "copy" and "new" lists, and reset the temp file. + * Clear out the "new" master header copy. + */ + err = Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); + BailError(err); + err = Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->newRecordSet); + BailError(err); + pArchive->newMasterHeader.isValid = false; + + err = Nu_ResetTempFile(pArchive); + if (err != kNuErrNone) { + /* can't NuAbort() our way out of a bad temp file */ + canAbort = false; + goto bail; + } + + if (deleteAll) { + /* there's nothing in it, so treat it like a newly-created archive */ + /* (that way it gets deleted if the app closes without adding stuff) */ + DBUG(("--- marking archive as newly created\n")); + pArchive->newlyCreated = true; + /*pArchive->valModifyOrig = true;*/ + } + +bail: + if (err != kNuErrNone) { + if (canAbort) { + (void) Nu_Abort(pArchive); + Assert(!(*pStatusFlags & kNuFlushSucceeded)); + *pStatusFlags |= kNuFlushAborted; + + /* + * If we were adding to original archive, truncate it back if + * we are able to do so. This retains any BXY/BSE wrapper padding. + */ + if (!writeToTemp) { + NuError err2; + err2 = Nu_TruncateOpenFile(pArchive->archiveFp, initialEOF); + if (err2 == kNuErrNone) { + DBUG(("+++ truncated orig archive back to %ld\n", + initialEOF)); + } else { + DBUG(("+++ truncate orig failed (err=%d)\n", err2)); + } + } + } else { + Nu_ReportError(NU_BLOB, kNuErrNone, + "disabling write access after failed update"); + pArchive->openMode = kNuOpenRO; + *pStatusFlags |= kNuFlushReadOnly; + } + } + + /* last-minute sanity check */ + Assert(pArchive->origRecordSet.numRecords == 0 || + (pArchive->origRecordSet.nuRecordHead != nil && + pArchive->origRecordSet.nuRecordTail != nil)); + + return err; +} + + +/* + * Abort any pending changes. + */ +NuError +Nu_Abort(NuArchive* pArchive) +{ + Assert(pArchive != nil); + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + + DBUG(("--- Aborting changes\n")); + + /* + * Throw out the "copy" and "new" record sets, and reset the + * temp file. + */ + (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); + (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->newRecordSet); + pArchive->newMasterHeader.isValid = false; + + return Nu_ResetTempFile(pArchive); +} + diff --git a/nufxlib/Deflate.c b/nufxlib/Deflate.c new file mode 100644 index 0000000..c5050ec --- /dev/null +++ b/nufxlib/Deflate.c @@ -0,0 +1,299 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Support for the "deflate" algorithm, via the "zlib" library. + * + * This compression format is totally unsupported on the Apple II. This + * is provided primarily for the benefit of Apple II emulators that want + * a better storage format for disk images than SHK+LZW or a ZIP file. + * + * This code was developed and tested with ZLIB_VERSION "1.1.3". It is + * expected to work with any version >= 1.1.3 and < 2.x. Please visit + * http://www.zlib.org/ for more information. + */ +#include "NufxLibPriv.h" + +#ifdef ENABLE_DEFLATE +#include "zlib.h" + +#define kNuDeflateLevel 9 /* use maximum compression */ + + +/* + * Alloc and free functions provided to zlib. + */ +static voidpf +Nu_zalloc(voidpf opaque, uInt items, uInt size) +{ + return Nu_Malloc(opaque, items * size); +} +static void +Nu_zfree(voidpf opaque, voidpf address) +{ + Nu_Free(opaque, address); +} + + +/* + * =========================================================================== + * Compression + * =========================================================================== + */ + +/* + * Compress "srcLen" bytes from "pStraw" to "fp". + */ +NuError +Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + NuError err = kNuErrNone; + z_stream zstream; + int zerr; + Bytef* outbuf = nil; + + Assert(pArchive != nil); + Assert(pStraw != nil); + Assert(fp != nil); + Assert(srcLen > 0); + Assert(pDstLen != nil); + Assert(pCrc != nil); + + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + /* allocate a similarly-sized buffer for the output */ + outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); + BailAlloc(outbuf); + + /* + * Initialize the zlib stream. + */ + zstream.zalloc = Nu_zalloc; + zstream.zfree = Nu_zfree; + zstream.opaque = pArchive; + zstream.next_in = nil; + zstream.avail_in = 0; + zstream.next_out = outbuf; + zstream.avail_out = kNuGenCompBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = deflateInit(&zstream, kNuDeflateLevel); + if (zerr != Z_OK) { + err = kNuErrInternal; + if (zerr == Z_VERSION_ERROR) { + Nu_ReportError(NU_BLOB, err, + "installed zlib is not compatible with linked version (%s)", + ZLIB_VERSION); + } else { + Nu_ReportError(NU_BLOB, err, + "call to deflateInit failed (zerr=%d)", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + ulong getSize; + int flush; + + /* should be able to read a full buffer every time */ + if (zstream.avail_in == 0 && srcLen) { + getSize = (srcLen > kNuGenCompBufSize) ? kNuGenCompBufSize : srcLen; + DBUG(("+++ reading %ld bytes\n", getSize)); + + err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "deflate read failed"); + goto z_bail; + } + + srcLen -= getSize; + + *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getSize); + + zstream.next_in = pArchive->compBuf; + zstream.avail_in = getSize; + } + + if (srcLen == 0) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ + + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, err, "zlib deflate call failed (zerr=%d)", + zerr); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != kNuGenCompBufSize)) + { + DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf)); + err = Nu_FWrite(fp, outbuf, zstream.next_out - outbuf); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "fwrite failed in deflate"); + goto z_bail; + } + + zstream.next_out = outbuf; + zstream.avail_out = kNuGenCompBufSize; + } + } while (zerr == Z_OK); + + Assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + *pDstLen = zstream.total_out; + +z_bail: + deflateEnd(&zstream); /* free up any allocated structures */ + +bail: + if (outbuf != nil) + free(outbuf); + return err; +} + + +/* + * =========================================================================== + * Expansion + * =========================================================================== + */ + +/* + * Expand from "infp" to "pFunnel". + */ +NuError +Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc) +{ + NuError err = kNuErrNone; + z_stream zstream; + int zerr; + ulong compRemaining; + Bytef* outbuf; + + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(infp != nil); + Assert(pFunnel != nil); + + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + /* allocate a similarly-sized buffer for the output */ + outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); + BailAlloc(outbuf); + + compRemaining = pThread->thCompThreadEOF; + + /* + * Initialize the zlib stream. + */ + zstream.zalloc = Nu_zalloc; + zstream.zfree = Nu_zfree; + zstream.opaque = pArchive; + zstream.next_in = nil; + zstream.avail_in = 0; + zstream.next_out = outbuf; + zstream.avail_out = kNuGenCompBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = inflateInit(&zstream); + if (zerr != Z_OK) { + err = kNuErrInternal; + if (zerr == Z_VERSION_ERROR) { + Nu_ReportError(NU_BLOB, err, + "installed zlib is not compatible with linked version (%s)", + ZLIB_VERSION); + } else { + Nu_ReportError(NU_BLOB, err, + "call to inflateInit failed (zerr=%d)", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + ulong getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kNuGenCompBufSize) ? + kNuGenCompBufSize : compRemaining; + DBUG(("+++ reading %ld bytes (%ld left)\n", getSize, + compRemaining)); + + err = Nu_FRead(infp, pArchive->compBuf, getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "inflate read failed"); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = pArchive->compBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, err, "zlib inflate call failed (zerr=%d)", + zerr); + goto z_bail; + } + + /* write every time there's anything (buffer will usually be full) */ + if (zstream.avail_out != kNuGenCompBufSize) { + DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf)); + err = Nu_FunnelWrite(pArchive, pFunnel, outbuf, + zstream.next_out - outbuf); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "write failed in inflate"); + goto z_bail; + } + + if (pCrc != nil) + *pCrc = Nu_CalcCRC16(*pCrc, outbuf, zstream.next_out - outbuf); + + zstream.next_out = outbuf; + zstream.avail_out = kNuGenCompBufSize; + } + } while (zerr == Z_OK); + + Assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if (zstream.total_out != pThread->actualThreadEOF) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, + "size mismatch on inflated file (%ld vs %ld)", + zstream.total_out, pThread->actualThreadEOF); + goto z_bail; + } + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + if (outbuf != nil) + free(outbuf); + return err; +} + +#endif /*ENABLE_DEFLATE*/ diff --git a/nufxlib/Entry.c b/nufxlib/Entry.c new file mode 100644 index 0000000..f068e30 --- /dev/null +++ b/nufxlib/Entry.c @@ -0,0 +1,864 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * All external entry points. + */ +#include "NufxLibPriv.h" + + +/* + * =========================================================================== + * Misc utils + * =========================================================================== + */ + +/* + * Set the busy flag. + * + * The busy flag is intended to prevent the caller from executing illegal + * operations while inside a callback function. It is NOT intended to + * allow concurrent access to the same archive from multiple threads, so + * it does not follow all sorts of crazy semaphore semantics. If you + * have the need, go ahead and fix it. + */ +static inline void +Nu_SetBusy(NuArchive* pArchive) +{ + pArchive->busy = true; +} + +/* + * Clear the busy flag. + */ +static inline void +Nu_ClearBusy(NuArchive* pArchive) +{ + pArchive->busy = false; +} + + +/* + * Do a partial validation on NuArchive. Some calls, such as GetExtraData, + * can be made during callback functions when the archive isn't fully + * consistent. + */ +static NuError +Nu_PartiallyValidateNuArchive(const NuArchive* pArchive) +{ + if (pArchive == nil) + return kNuErrInvalidArg; + + pArchive = pArchive; + if (pArchive->structMagic != kNuArchiveStructMagic) + return kNuErrBadStruct; + + return kNuErrNone; +} + +/* + * Validate the NuArchive* argument passed in to us. + */ +static NuError +Nu_ValidateNuArchive(const NuArchive* pArchive) +{ + NuError err; + + err = Nu_PartiallyValidateNuArchive(pArchive); + if (err != kNuErrNone) + return err; + + /* explicitly block reentrant calls */ + if (pArchive->busy) + return kNuErrBusy; + + /* make sure the TOC state is consistent */ + if (pArchive->haveToc) { + if (pArchive->masterHeader.mhTotalRecords != 0) + Assert(Nu_RecordSet_GetListHead(&pArchive->origRecordSet) != nil); + Assert(Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet) == + pArchive->masterHeader.mhTotalRecords); + } else { + Assert(Nu_RecordSet_GetListHead(&pArchive->origRecordSet) == nil); + } + + /* make sure we have open files to work with */ + Assert(pArchive->archivePathname == nil || pArchive->archiveFp != nil); + if (pArchive->archivePathname != nil && pArchive->archiveFp == nil) + return kNuErrInternal; + Assert(pArchive->tmpPathname == nil || pArchive->tmpFp != nil); + if (pArchive->tmpPathname != nil && pArchive->tmpFp == nil) + return kNuErrInternal; + + /* further validations */ + + return kNuErrNone; +} + + +/* + * =========================================================================== + * Streaming and non-streaming read-only + * =========================================================================== + */ + +NUFXLIB_API NuError +NuStreamOpenRO(FILE* infp, NuArchive** ppArchive) +{ + NuError err; + + if (infp == nil || ppArchive == nil) + return kNuErrInvalidArg; + + err = Nu_StreamOpenRO(infp, (NuArchive**) ppArchive); + + return err; +} + +NUFXLIB_API NuError +NuContents(NuArchive* pArchive, NuCallback contentFunc) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + if (Nu_IsStreaming(pArchive)) + err = Nu_StreamContents(pArchive, contentFunc); + else + err = Nu_Contents(pArchive, contentFunc); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuExtract(NuArchive* pArchive) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + if (Nu_IsStreaming(pArchive)) + err = Nu_StreamExtract(pArchive); + else + err = Nu_Extract(pArchive); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuTest(NuArchive* pArchive) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + if (Nu_IsStreaming(pArchive)) + err = Nu_StreamTest(pArchive); + else + err = Nu_Test(pArchive); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuTestRecord(NuArchive* pArchive, NuRecordIdx recordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_TestRecord(pArchive, recordIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + + +/* + * =========================================================================== + * Strictly non-streaming read-only + * =========================================================================== + */ + +NUFXLIB_API NuError +NuOpenRO(const char* filename, NuArchive** ppArchive) +{ + NuError err; + + err = Nu_OpenRO(filename, (NuArchive**) ppArchive); + + return err; +} + +NUFXLIB_API NuError +NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_ExtractRecord(pArchive, recordIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSink* pDataSink) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_ExtractThread(pArchive, threadIdx, pDataSink); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecord** ppRecord) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_GetRecord(pArchive, recordIdx, ppRecord); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuGetRecordIdxByName(NuArchive* pArchive, const char* name, + NuRecordIdx* pRecordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_GetRecordIdxByName(pArchive, name, pRecordIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuGetRecordIdxByPosition(NuArchive* pArchive, unsigned long position, + NuRecordIdx* pRecordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_GetRecordIdxByPosition(pArchive, position, pRecordIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + + +/* + * =========================================================================== + * Read/Write + * =========================================================================== + */ + +NUFXLIB_API NuError +NuOpenRW(const char* archivePathname, const char* tmpPathname, + unsigned long flags, NuArchive** ppArchive) +{ + NuError err; + + err = Nu_OpenRW(archivePathname, tmpPathname, flags, + (NuArchive**) ppArchive); + + return err; +} + +NUFXLIB_API NuError +NuFlush(NuArchive* pArchive, long* pStatusFlags) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_Flush(pArchive, pStatusFlags); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuAbort(NuArchive* pArchive) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_Abort(pArchive); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuAddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails, + NuRecordIdx* pRecordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_AddRecord(pArchive, pFileDetails, pRecordIdx, nil); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx, NuThreadID threadID, + NuDataSource* pDataSource, NuThreadIdx* pThreadIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_AddThread(pArchive, recordIdx, threadID, + pDataSource, pThreadIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuAddFile(NuArchive* pArchive, const char* pathname, + const NuFileDetails* pFileDetails, short isFromRsrcFork, + NuRecordIdx* pRecordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_AddFile(pArchive, pathname, pFileDetails, + (Boolean)(isFromRsrcFork != 0), pRecordIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuRename(NuArchive* pArchive, NuRecordIdx recordIdx, const char* pathname, + char fssep) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_Rename(pArchive, recordIdx, pathname, fssep); + Nu_ClearBusy(pArchive); + } + + return err; +} + + +NUFXLIB_API NuError +NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecordAttr* pRecordAttr) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_SetRecordAttr(pArchive, recordIdx, pRecordAttr); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuUpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSource* pDataSource, long* pMaxLen) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_UpdatePresizedThread(pArchive, threadIdx, + pDataSource, pMaxLen); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuDelete(NuArchive* pArchive) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_Delete(pArchive); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_DeleteRecord(pArchive, recordIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_DeleteThread(pArchive, threadIdx); + Nu_ClearBusy(pArchive); + } + + return err; +} + + +/* + * =========================================================================== + * General interfaces + * =========================================================================== + */ + +NUFXLIB_API NuError +NuClose(NuArchive* pArchive) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + Nu_SetBusy(pArchive); + err = Nu_Close(pArchive); + /* on success, pArchive has been freed */ + if (err != kNuErrNone) + Nu_ClearBusy(pArchive); + } + + return err; +} + +NUFXLIB_API NuError +NuGetMasterHeader(NuArchive* pArchive, const NuMasterHeader** ppMasterHeader) +{ + NuError err; + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) + err = Nu_GetMasterHeader(pArchive, ppMasterHeader); + + return err; +} + +NUFXLIB_API NuError +NuGetExtraData(NuArchive* pArchive, void** ppData) +{ + NuError err; + + if (ppData == nil) + return kNuErrInvalidArg; + if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) + *ppData = pArchive->extraData; + + return err; +} + +NUFXLIB_API NuError +NuSetExtraData(NuArchive* pArchive, void* pData) +{ + NuError err; + + if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) + pArchive->extraData = pData; + + return err; +} + +NUFXLIB_API NuError +NuGetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue) +{ + NuError err; + + if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) + return Nu_GetValue(pArchive, ident, pValue); + + return err; +} + +NUFXLIB_API NuError +NuSetValue(NuArchive* pArchive, NuValueID ident, NuValue value) +{ + NuError err; + + if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) + return Nu_SetValue(pArchive, ident, value); + + return err; +} + +NUFXLIB_API NuError +NuGetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr) +{ + NuError err; + + if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) + return Nu_GetAttr(pArchive, ident, pAttr); + + return err; +} + +NUFXLIB_API NuError +NuDebugDumpArchive(NuArchive* pArchive) +{ +#if defined(DEBUG_MSGS) + /* skip validation checks for this one */ + Nu_DebugDumpAll(pArchive); + return kNuErrNone; +#else + /* function doesn't exist */ + return kNuErrGeneric; +#endif +} + + +/* + * =========================================================================== + * Sources and Sinks + * =========================================================================== + */ + +NUFXLIB_API NuError +NuCreateDataSourceForFile(NuThreadFormat threadFormat, + unsigned long otherLen, const char* pathname, short isFromRsrcFork, + NuDataSource** ppDataSource) +{ + return Nu_DataSourceFile_New(threadFormat, otherLen, + pathname, (Boolean)(isFromRsrcFork != 0), ppDataSource); +} + +NUFXLIB_API NuError +NuCreateDataSourceForFP(NuThreadFormat threadFormat, + unsigned long otherLen, FILE* fp, long offset, long length, + NuCallback fcloseFunc, NuDataSource** ppDataSource) +{ + return Nu_DataSourceFP_New(threadFormat, otherLen, + fp, offset, length, fcloseFunc, ppDataSource); +} + +NUFXLIB_API NuError +NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, + unsigned long otherLen, const unsigned char* buffer, long offset, + long length, NuCallback freeFunc, NuDataSource** ppDataSource) +{ + return Nu_DataSourceBuffer_New(threadFormat, otherLen, + buffer, offset, length, freeFunc, ppDataSource); +} + +NUFXLIB_API NuError +NuFreeDataSource(NuDataSource* pDataSource) +{ + return Nu_DataSourceFree(pDataSource); +} + +NUFXLIB_API NuError +NuDataSourceSetRawCrc(NuDataSource* pDataSource, unsigned short crc) +{ + if (pDataSource == nil) + return kNuErrInvalidArg; + Nu_DataSourceSetRawCrc(pDataSource, crc); + return kNuErrNone; +} + +NUFXLIB_API NuError +NuCreateDataSinkForFile(short doExpand, NuValue convertEOL, + const char* pathname, char fssep, NuDataSink** ppDataSink) +{ + return Nu_DataSinkFile_New((Boolean)(doExpand != 0), convertEOL, pathname, + fssep, ppDataSink); +} + +NUFXLIB_API NuError +NuCreateDataSinkForFP(short doExpand, NuValue convertEOL, FILE* fp, + NuDataSink** ppDataSink) +{ + return Nu_DataSinkFP_New((Boolean)(doExpand != 0), convertEOL, fp, + ppDataSink); +} + +NUFXLIB_API NuError +NuCreateDataSinkForBuffer(short doExpand, NuValue convertEOL, + unsigned char* buffer, unsigned long bufLen, NuDataSink** ppDataSink) +{ + return Nu_DataSinkBuffer_New((Boolean)(doExpand != 0), convertEOL, buffer, + bufLen, ppDataSink); +} + +NUFXLIB_API NuError +NuFreeDataSink(NuDataSink* pDataSink) +{ + return Nu_DataSinkFree(pDataSink); +} + +NUFXLIB_API NuError +NuDataSinkGetOutCount(NuDataSink* pDataSink, ulong* pOutCount) +{ + if (pDataSink == nil || pOutCount == nil) + return kNuErrInvalidArg; + + *pOutCount = Nu_DataSinkGetOutCount(pDataSink); + return kNuErrNone; +} + + +/* + * =========================================================================== + * Non-archive operations + * =========================================================================== + */ + +NUFXLIB_API const char* +NuStrError(NuError err) +{ + return Nu_StrError(err); +} + +NUFXLIB_API NuError +NuGetVersion(long* pMajorVersion, long* pMinorVersion, long* pBugVersion, + const char** ppBuildDate, const char** ppBuildFlags) +{ + return Nu_GetVersion(pMajorVersion, pMinorVersion, pBugVersion, + ppBuildDate, ppBuildFlags); +} + +NUFXLIB_API NuError +NuTestFeature(NuFeature feature) +{ + NuError err = kNuErrUnsupFeature; + + switch (feature) { + case kNuFeatureCompressSQ: + #ifdef ENABLE_SQ + err = kNuErrNone; + #endif + break; + case kNuFeatureCompressLZW: + #ifdef ENABLE_LZW + err = kNuErrNone; + #endif + break; + case kNuFeatureCompressLZC: + #ifdef ENABLE_LZC + err = kNuErrNone; + #endif + break; + case kNuFeatureCompressDeflate: + #ifdef ENABLE_DEFLATE + err = kNuErrNone; + #endif + break; + case kNuFeatureCompressBzip2: + #ifdef ENABLE_BZIP2 + err = kNuErrNone; + #endif + break; + default: + err = kNuErrUnknownFeature; + break; + } + + return err; +} + +NUFXLIB_API void +NuRecordCopyAttr(NuRecordAttr* pRecordAttr, const NuRecord* pRecord) +{ + pRecordAttr->fileSysID = pRecord->recFileSysID; + /*pRecordAttr->fileSysInfo = pRecord->recFileSysInfo;*/ + pRecordAttr->access = pRecord->recAccess; + pRecordAttr->fileType = pRecord->recFileType; + pRecordAttr->extraType = pRecord->recExtraType; + pRecordAttr->createWhen = pRecord->recCreateWhen; + pRecordAttr->modWhen = pRecord->recModWhen; + pRecordAttr->archiveWhen = pRecord->recArchiveWhen; +} + +NUFXLIB_API NuError +NuRecordCopyThreads(const NuRecord* pNuRecord, NuThread** ppThreads) +{ + if (pNuRecord == nil || ppThreads == nil) + return kNuErrInvalidArg; + + Assert(pNuRecord->pThreads != nil); + + *ppThreads = Nu_Malloc(nil, pNuRecord->recTotalThreads * sizeof(NuThread)); + if (*ppThreads == nil) + return kNuErrMalloc; + + memcpy(*ppThreads, pNuRecord->pThreads, + pNuRecord->recTotalThreads * sizeof(NuThread)); + + return kNuErrNone; +} + +NUFXLIB_API unsigned long +NuRecordGetNumThreads(const NuRecord* pNuRecord) +{ + if (pNuRecord == nil) + return -1; + + return pNuRecord->recTotalThreads; +} + +NUFXLIB_API const NuThread* +NuThreadGetByIdx(const NuThread* pNuThread, long idx) +{ + if (pNuThread == nil) + return nil; + return &pNuThread[idx]; /* can't range-check here */ +} + +NUFXLIB_API short +NuIsPresizedThreadID(NuThreadID threadID) +{ + return Nu_IsPresizedThreadID(threadID); +} + + +/* + * =========================================================================== + * Callback setters + * =========================================================================== + */ + +NUFXLIB_API NuCallback +NuSetSelectionFilter(NuArchive* pArchive, NuCallback filterFunc) +{ + NuError err; + NuCallback oldFunc = kNuInvalidCallback; + + /*Assert(!((ulong)filterFunc % 4));*/ + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + oldFunc = pArchive->selectionFilterFunc; + pArchive->selectionFilterFunc = filterFunc; + } + + return oldFunc; +} + +NUFXLIB_API NuCallback +NuSetOutputPathnameFilter(NuArchive* pArchive, NuCallback filterFunc) +{ + NuError err; + NuCallback oldFunc = kNuInvalidCallback; + + /*Assert(!((ulong)filterFunc % 4));*/ + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + oldFunc = pArchive->outputPathnameFunc; + pArchive->outputPathnameFunc = filterFunc; + } + + return oldFunc; +} + +NUFXLIB_API NuCallback +NuSetProgressUpdater(NuArchive* pArchive, NuCallback updateFunc) +{ + NuError err; + NuCallback oldFunc = kNuInvalidCallback; + + /*Assert(!((ulong)updateFunc % 4));*/ + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + oldFunc = pArchive->progressUpdaterFunc; + pArchive->progressUpdaterFunc = updateFunc; + } + + return oldFunc; +} + +NUFXLIB_API NuCallback +NuSetErrorHandler(NuArchive* pArchive, NuCallback errorFunc) +{ + NuError err; + NuCallback oldFunc = kNuInvalidCallback; + + /*Assert(!((ulong)errorFunc % 4));*/ + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + oldFunc = pArchive->errorHandlerFunc; + pArchive->errorHandlerFunc = errorFunc; + } + + return oldFunc; +} + +NUFXLIB_API NuCallback +NuSetErrorMessageHandler(NuArchive* pArchive, NuCallback messageHandlerFunc) +{ + NuError err; + NuCallback oldFunc = kNuInvalidCallback; + + /*Assert(!((ulong)messageHandlerFunc % 4));*/ + + if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { + oldFunc = pArchive->messageHandlerFunc; + pArchive->messageHandlerFunc = messageHandlerFunc; + } + + return oldFunc; +} + +NUFXLIB_API NuCallback +NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc) +{ + NuCallback oldFunc = kNuInvalidCallback; + /*Assert(!((ulong)messageHandlerFunc % 4));*/ + + oldFunc = gNuGlobalErrorMessageHandler; + gNuGlobalErrorMessageHandler = messageHandlerFunc; + return oldFunc; +} + diff --git a/nufxlib/Expand.c b/nufxlib/Expand.c new file mode 100644 index 0000000..ecf901b --- /dev/null +++ b/nufxlib/Expand.c @@ -0,0 +1,230 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Expand a thread from an archive. + */ +#include "NufxLibPriv.h" + + +/* + * "Expand" an uncompressed thread. + */ +static NuError +Nu_ExpandUncompressed(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc) +{ + NuError err; + /*uchar* buffer = nil;*/ + ulong count, getsize; + + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(infp != nil); + Assert(pFunnel != nil); + + /* doesn't have to be same size as funnel, but it's not a bad idea */ + /*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/ + /*BailAlloc(buffer);*/ + err = Nu_AllocCompressionBufferIFN(pArchive); + BailError(err); + + /* quick assert for bad archive that should have been caught earlier */ + /* (filename threads are uncompressed, but compThreadEOF is buf len) */ + if (pThread->thThreadClass == kNuThreadClassData) + Assert(pThread->actualThreadEOF == pThread->thCompThreadEOF); + + count = pThread->actualThreadEOF; + + while (count) { + getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; + + err = Nu_FRead(infp, pArchive->compBuf, getsize); + BailError(err); + if (pCrc != nil) + *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getsize); + err = Nu_FunnelWrite(pArchive, pFunnel, pArchive->compBuf, getsize); + BailError(err); + + count -= getsize; + } + + err = Nu_FunnelFlush(pArchive, pFunnel); + BailError(err); + +bail: + /*Nu_Free(pArchive, buffer);*/ + return err; +} + +/* + * Copy the "raw" data out of the thread. Unlike the preceeding function, + * this reads up to "thCompThreadEOF", and doesn't even try to compute a CRC. + */ +static NuError +Nu_ExpandRaw(NuArchive* pArchive, const NuThread* pThread, FILE* infp, + NuFunnel* pFunnel) +{ + NuError err; + /*uchar* buffer = nil;*/ + ulong count, getsize; + + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(infp != nil); + Assert(pFunnel != nil); + + /* doesn't have to be same size as funnel, but it's not a bad idea */ + /*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/ + /*BailAlloc(buffer);*/ + err = Nu_AllocCompressionBufferIFN(pArchive); + BailError(err); + + count = pThread->thCompThreadEOF; + + while (count) { + getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; + + err = Nu_FRead(infp, pArchive->compBuf, getsize); + BailError(err); + err = Nu_FunnelWrite(pArchive, pFunnel, pArchive->compBuf, getsize); + BailError(err); + + count -= getsize; + } + + err = Nu_FunnelFlush(pArchive, pFunnel); + BailError(err); + +bail: + /*Nu_Free(pArchive, buffer);*/ + return err; +} + + +/* + * Expand a thread from "infp" to "pFunnel", using the compression + * and stream length specified by "pThread". + */ +NuError +Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel) +{ + NuError err = kNuErrNone; + ushort calcCrc; + ushort* pCalcCrc; + + if (!pThread->thThreadEOF && !pThread->thCompThreadEOF) { + /* somebody stored an empty file! */ + goto done; + } + + /* + * A brief history of the "threadCRC" field in the thread header: + * record versions 0 and 1 didn't use the threadCRC field + * record version 2 put the CRC of the compressed data in threadCRC + * record version 3 put the CRC of the uncompressed data in threadCRC + * + * P8 ShrinkIt uses v1, GSHK uses v3. If something ever shipped with + * v2, it didn't last long enough to leave an impression, so I'm not + * going to support it. BTW, P8 ShrinkIt always uses LZW/1, which + * puts a CRC in the compressed stream. Your uncompressed data is, + * unfortunately, unprotected before v3. + */ + calcCrc = kNuInitialThreadCRC; + pCalcCrc = nil; + if (Nu_ThreadHasCRC(pRecord->recVersionNumber, NuGetThreadID(pThread)) && + !pArchive->valIgnoreCRC) + { + pCalcCrc = &calcCrc; + } + + err = Nu_ProgressDataExpandPrep(pArchive, pFunnel, pThread); + BailError(err); + + /* + * If we're not expanding the data, use a simple copier. + */ + if (!Nu_FunnelGetDoExpand(pFunnel)) { + Nu_FunnelSetProgressState(pFunnel, kNuProgressCopying); + err = Nu_ExpandRaw(pArchive, pThread, infp, pFunnel); + BailError(err); + goto done; + } + + Nu_FunnelSetProgressState(pFunnel, kNuProgressExpanding); + switch (pThread->thThreadFormat) { + case kNuThreadFormatUncompressed: + Nu_FunnelSetProgressState(pFunnel, kNuProgressCopying); + err = Nu_ExpandUncompressed(pArchive, pRecord, pThread, infp, pFunnel, + pCalcCrc); + break; + #ifdef ENABLE_SQ + case kNuThreadFormatHuffmanSQ: + err = Nu_ExpandHuffmanSQ(pArchive, pRecord, pThread, infp, pFunnel, + pCalcCrc); + break; + #endif + #ifdef ENABLE_LZW + case kNuThreadFormatLZW1: + case kNuThreadFormatLZW2: + err = Nu_ExpandLZW(pArchive, pRecord, pThread, infp, pFunnel, pCalcCrc); + break; + #endif + #ifdef ENABLE_LZC + case kNuThreadFormatLZC12: + case kNuThreadFormatLZC16: + err = Nu_ExpandLZC(pArchive, pRecord, pThread, infp, pFunnel, pCalcCrc); + break; + #endif + #ifdef ENABLE_DEFLATE + case kNuThreadFormatDeflate: + err = Nu_ExpandDeflate(pArchive, pRecord, pThread, infp, pFunnel, + pCalcCrc); + break; + #endif + #ifdef ENABLE_BZIP2 + case kNuThreadFormatBzip2: + err = Nu_ExpandBzip2(pArchive, pRecord, pThread, infp, pFunnel, + pCalcCrc); + break; + #endif + default: + err = kNuErrBadFormat; + Nu_ReportError(NU_BLOB, err, + "compression format %u not supported", pThread->thThreadFormat); + break; + } + BailError(err); + + err = Nu_FunnelFlush(pArchive, pFunnel); + BailError(err); + + /* + * If we have a CRC to check, check it. + */ + if (pCalcCrc != nil) { + if (calcCrc != pThread->thThreadCRC) { + if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadThreadCRC)) { + err = kNuErrBadDataCRC; + Nu_ReportError(NU_BLOB, err, "expected 0x%04x, got 0x%04x", + pThread->thThreadCRC, calcCrc); + goto bail; + } + } else { + DBUG(("--- thread CRCs match\n")); + } + } + +done: + /* make sure we send a final "success" progress message at 100% */ + (void) Nu_FunnelSetProgressState(pFunnel, kNuProgressDone); + err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel); + BailError(err); + +bail: + return err; +} + diff --git a/nufxlib/FileIO.c b/nufxlib/FileIO.c new file mode 100644 index 0000000..aa5cc46 --- /dev/null +++ b/nufxlib/FileIO.c @@ -0,0 +1,1501 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Operations on output (i.e. non-archive) files, largely system-specific. + * Portions taken from NuLib, including some code that Devin Reade worked on. + * + * It could be argued that "create file" should be a callback function, + * since it is so heavily system-specific, and most of the other + * system dependencies are handled by the application rather than the + * NuFX library. It would also provide a cleaner solution for renaming + * extracted files. However, the goal of the library is to do the work + * for the application, not the other way around; and while it might be + * nice to offload all direct file handling on the application, it + * complicates rather than simplifies the interface. + */ +#include "NufxLibPriv.h" + +#ifdef MAC_LIKE +# include +#endif + +/* + * For systems (e.g. Visual C++ 6.0) that don't have these standard values. + */ +#ifndef S_IRUSR +# define S_IRUSR 0400 +# define S_IWUSR 0200 +# define S_IXUSR 0100 +# define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) +# define S_IRGRP (S_IRUSR >> 3) +# define S_IWGRP (S_IWUSR >> 3) +# define S_IXGRP (S_IXUSR >> 3) +# define S_IRWXG (S_IRWXU >> 3) +# define S_IROTH (S_IRGRP >> 3) +# define S_IWOTH (S_IWGRP >> 3) +# define S_IXOTH (S_IXGRP >> 3) +# define S_IRWXO (S_IRWXG >> 3) +#endif +#ifndef S_ISREG +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/* + * =========================================================================== + * DateTime conversions + * =========================================================================== + */ + +/* + * Dates and times in a NuFX archive are always considered to be in the + * local time zone. The use of GMT time stamps would have been more + * appropriate for an archive, but local time works well enough. + * + * Regarding Y2K on the Apple II: + * + * Dave says P8 drivers should return year values in the range 0..99, where + * 40..99 = 1940..1999, and 0..39 = 2000..2039. Year values 100..127 should + * never be used. For ProDOS 8, the year 2000 is "00". + * + * The IIgs ReadTimeHex call uses "year minus 1900". For GS/OS, the year + * 2000 is "100". + * + * The NuFX file type note says the archive format should work like + * The IIgs ReadTimeHex call, which uses "year minus 1900" as its + * format. GS/ShrinkIt v1.1 uses the IIgs date calls, and so stores the + * year 2000 as "100". P8 ShrinkIt v3.4 uses the P8 mapping, and stores + * it as "0". Neither really quite understands what the other is doing. + * + * For our purposes, we will follow the NuFX standard and emit "100" + * for the year 2000, but will accept and understand "0" as well. + */ + + +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) +/* + * Convert from local time in a NuDateTime struct to GMT seconds since 1970. + * + * If the conversion is invalid, "*pWhen" is set to zero. + */ +static void +Nu_DateTimeToGMTSeconds(const NuDateTime* pDateTime, time_t* pWhen) +{ + struct tm tmbuf; + time_t when; + + Assert(pDateTime != nil); + Assert(pWhen != nil); + + tmbuf.tm_sec = pDateTime->second; + tmbuf.tm_min = pDateTime->minute; + tmbuf.tm_hour = pDateTime->hour; + tmbuf.tm_mday = pDateTime->day +1; + tmbuf.tm_mon = pDateTime->month; + tmbuf.tm_year = pDateTime->year; + if (pDateTime->year < 40) + tmbuf.tm_year += 100; /* P8 uses 0-39 for 2000-2039 */ + tmbuf.tm_wday = 0; + tmbuf.tm_yday = 0; + tmbuf.tm_isdst = -1; /* let it figure DST and time zone */ + + #if defined(HAVE_MKTIME) + when = mktime(&tmbuf); + #elif defined(HAVE_TIMELOCAL) + when = timelocal(&tmbuf); + #else + # error "need time converter" + #endif + + if (when == (time_t) -1) + *pWhen = 0; + else + *pWhen = when; +} + +/* + * Convert from GMT seconds since 1970 to local time in a NuDateTime struct. + */ +static void +Nu_GMTSecondsToDateTime(const time_t* pWhen, NuDateTime *pDateTime) +{ + struct tm* ptm; + + Assert(pWhen != nil); + Assert(pDateTime != nil); + + #if defined(HAVE_LOCALTIME_R) && defined(USE_REENTRANT_CALLS) + struct tm res; + ptm = localtime_r(pWhen, &res); + #else + /* NOTE: not thread-safe */ + ptm = localtime(pWhen); + #endif + pDateTime->second = ptm->tm_sec; + pDateTime->minute = ptm->tm_min; + pDateTime->hour = ptm->tm_hour; + pDateTime->day = ptm->tm_mday -1; + pDateTime->month = ptm->tm_mon; + pDateTime->year = ptm->tm_year; + pDateTime->extra = 0; + pDateTime->weekDay = ptm->tm_wday +1; +} +#endif + + +/* + * Fill in the current time. + */ +void +Nu_SetCurrentDateTime(NuDateTime* pDateTime) +{ + Assert(pDateTime != nil); + +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + { + time_t now = time(nil); + Nu_GMTSecondsToDateTime(&now, pDateTime); + } +#else + #error "Port this" +#endif +} + + +/* + * Returns "true" if "pWhen1" is older than "pWhen2". Returns false if + * "pWhen1" is the same age or newer than "pWhen2". + * + * On systems with mktime, it would be straightforward to convert the dates + * to time in seconds, and compare them that way. However, I don't want + * to rely on that function too heavily, so we just compare fields. + */ +Boolean +Nu_IsOlder(const NuDateTime* pWhen1, const NuDateTime* pWhen2) +{ + long result, year1, year2; + + /* adjust for P8 ShrinkIt Y2K problem */ + year1 = pWhen1->year; + if (year1 < 40) + year1 += 100; + year2 = pWhen2->year; + if (year2 < 40) + year2 += 100; + + result = year1 - year2; + if (!result) + result = pWhen1->month - pWhen2->month; + if (!result) + result = pWhen1->day - pWhen2->day; + if (!result) + result = pWhen1->hour - pWhen2->hour; + if (!result) + result = pWhen1->minute - pWhen2->minute; + if (!result) + result = pWhen1->second - pWhen2->second; + + if (result < 0) + return true; + return false; +} + + +/* + * =========================================================================== + * Get/set file info + * =========================================================================== + */ + +/* + * System-independent (mostly) file info struct. + */ +typedef struct NuFileInfo { + Boolean isValid; /* init to "false", set "true" after we get data */ + + Boolean isRegularFile; /* is this a regular file? */ + Boolean isDirectory; /* is this a directory? */ + + ulong dataEof; + ulong rsrcEof; + + ulong fileType; + ulong auxType; + NuDateTime modWhen; + mode_t unixMode; /* UNIX-style permissions */ +} NuFileInfo; + +#define kDefaultFileType 0 /* "NON" */ +#define kDefaultAuxType 0 /* $0000 */ + + +/* + * Determine whether the record has both data and resource forks. + */ +static Boolean +Nu_IsForkedFile(NuArchive* pArchive, const NuRecord* pRecord) +{ + const NuThread* pThread; + NuThreadID threadID; + Boolean gotData, gotRsrc; + int i; + + gotData = gotRsrc = false; + + for (i = 0; i < (int)pRecord->recTotalThreads; i++) { + pThread = Nu_GetThread(pRecord, i); + Assert(pThread != nil); + + threadID = NuMakeThreadID(pThread->thThreadClass,pThread->thThreadKind); + if (threadID == kNuThreadIDDataFork) + gotData = true; + else if (threadID == kNuThreadIDRsrcFork) + gotRsrc = true; + } + + if (gotData && gotRsrc) + return true; + else + return false; +} + + +/* + * Get the file info into a NuFileInfo struct. Fields which are + * inappropriate for the current system are set to default values. + */ +static NuError +Nu_GetFileInfo(NuArchive* pArchive, const char* pathname, + NuFileInfo* pFileInfo) +{ + NuError err = kNuErrNone; + Assert(pArchive != nil); + Assert(pathname != nil); + Assert(pFileInfo != nil); + + pFileInfo->isValid = false; + +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + { + struct stat sbuf; + int cc; + + cc = stat(pathname, &sbuf); + if (cc) { + if (errno == ENOENT) + err = kNuErrFileNotFound; + else + err = kNuErrFileStat; + goto bail; + } + + pFileInfo->isRegularFile = false; + if (S_ISREG(sbuf.st_mode)) + pFileInfo->isRegularFile = true; + pFileInfo->isDirectory = false; + if (S_ISDIR(sbuf.st_mode)) + pFileInfo->isDirectory = true; + + /* BUG: should check for 32-bit overflow from 64-bit off_t */ + pFileInfo->dataEof = sbuf.st_size; + pFileInfo->rsrcEof = 0; + pFileInfo->fileType = kDefaultFileType; + pFileInfo->auxType = kDefaultAuxType; +# if defined(MAC_LIKE) + if (!pFileInfo->isDirectory) { + char path[4096]; + struct stat res_sbuf; + OSErr result; + OSType fileType, creator; + FSCatalogInfo catalogInfo; + FSRef ref; + unsigned long proType, proAux; + + strcpy(path, pathname); + strcat(path, "/rsrc"); + cc = stat(path, &res_sbuf); + if (cc) { + if (!errno) { + pFileInfo->rsrcEof = res_sbuf.st_size; + } + } + + result = FSPathMakeRef(pathname, &ref, NULL); + if (!result) { + result = FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo, + NULL, NULL, NULL); + if (!result) { + fileType = ((FileInfo *) &catalogInfo.finderInfo)->fileType; + creator = ((FileInfo *) &catalogInfo.finderInfo)->fileCreator; + + /* This actually is probably more efficient than a weird table, for + so few values */ + + switch(creator) { + case 'pdos': + if (fileType == 'PSYS') { + proType = 0xFF; + proAux = 0x0000; + } else if (fileType == 'PS16') { + proType = 0xB3; + proAux = 0x0000; + } else { + if (((fileType >> 24) & 0xFF) == 'p') { + proType = (fileType >> 16) & 0xFF; + proAux = fileType & 0xFFFF; + } else { + proType = 0x00; + proAux = 0x0000; + } + } + break; + case 'dCpy': + if (fileType == 'dImg') { + proType = 0xE0; + proAux = 0x0005; + } else { + proType = 0x00; + proAux = 0x0000; + } + break; + default: + switch(fileType) { + case 'BINA': + proType = 0x06; + proAux = 0x0000; + break; + case 'TEXT': + proType = 0x04; + proAux = 0x0000; + break; + case 'MIDI': + proType = 0xD7; + proAux = 0x0000; + break; + case 'AIFF': + proType = 0xD8; + proAux = 0x0000; + break; + case 'AIFC': + proType = 0xD8; + proAux = 0x0001; + break; + default: + proType = 0x00; + proAux = 0x0000; + break; + } + break; + } + pFileInfo->fileType = proType; + pFileInfo->auxType = proAux; + } + } + } +# endif + Nu_GMTSecondsToDateTime(&sbuf.st_mtime, &pFileInfo->modWhen); + pFileInfo->unixMode = sbuf.st_mode; + pFileInfo->isValid = true; + } +#else + #error "Port this" +#endif + +bail: + return err; +} + + +/* + * Determine whether a specific fork in the file exists. + * + * On systems that don't support forked files, the "checkRsrcFork" argument + * is ignored. If forked files are supported, and we are extracting a + * file with data and resource forks, we only claim it exists if it has + * nonzero length. + */ +static NuError +Nu_FileForkExists(NuArchive* pArchive, const char* pathname, + Boolean isForkedFile, Boolean checkRsrcFork, Boolean* pExists, + NuFileInfo* pFileInfo) +{ + NuError err = kNuErrNone; + + Assert(pArchive != nil); + Assert(pathname != nil); + Assert(checkRsrcFork == true || checkRsrcFork == false); + Assert(pExists != nil); + Assert(pFileInfo != nil); + +#if defined(MAC_LIKE) + /* + * On Mac OS X, we do much like on Unix, but we do need to look for + * a resource fork. + */ + *pExists = true; + if (!checkRsrcFork) { + /* + * Check the data fork. + */ + Assert(pArchive->lastFileCreated == nil); + err = Nu_GetFileInfo(pArchive, pathname, pFileInfo); + if (err == kNuErrFileNotFound) { + err = kNuErrNone; + *pExists = false; + } +/* DBUG(("Data fork %s: %d (err %d)\n", pathname, *pExists, err));*/ + } else { + /* + * Check the resource fork. + */ + char path[4096]; + strncpy(path, pathname, 4089); + strcat(path, "/rsrc"); + err = Nu_GetFileInfo(pArchive, path, pFileInfo); + if (err == kNuErrFileNotFound) { + err = kNuErrNone; + *pExists = false; + } else if (!err && !pFileInfo->rsrcEof) { + err = kNuErrNone; + *pExists = false; + } +/* DBUG(("Rsrc fork %s: %d (err %d)\n", path, *pExists, err));*/ + } + +#elif defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + /* + * On Unix and Windows we ignore "isForkedFile" and "checkRsrcFork". + * The file must not exist at all. + */ + Assert(pArchive->lastFileCreated == nil); + + *pExists = true; + err = Nu_GetFileInfo(pArchive, pathname, pFileInfo); + if (err == kNuErrFileNotFound) { + err = kNuErrNone; + *pExists = false; + } + +#elif defined(__ORCAC__) + /* + * If the file doesn't exist, great. If it does, and "lastFileCreated" + * matches up with this one, then we know that it exists because we + * created it. + * + * This is great unless the record has two data forks or something + * equally dopey, so we check to be sure that the fork we want to + * put the data into is currently empty. + * + * It is possible, though asinine, for a Mac OS or GS/OS extraction + * program to put the data and resource forks of a record into + * separate files, so we can't just assume that because we wrote + * the data fork to file A it is okay for file B to exist. That's + * why we compare the pathname instead of just remembering that + * we already created a file for this record. + */ + #error "Finish me" + +#else + #error "Port this" +#endif + + return err; +} + + +/* + * Set the dates on a file according to what's in the record. + */ +static NuError +Nu_SetFileDates(NuArchive* pArchive, const NuRecord* pRecord, + const char* pathname) +{ + NuError err = kNuErrNone; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(pathname != nil); + +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + { + struct utimbuf utbuf; + + /* ignore create time, and set access time equal to mod time */ + Nu_DateTimeToGMTSeconds(&pRecord->recModWhen, &utbuf.modtime); + utbuf.actime = utbuf.modtime; + + /* only do it if the NuDateTime was valid */ + if (utbuf.modtime) { + if (utime(pathname, &utbuf) < 0) { + Nu_ReportError(NU_BLOB, errno, + "Unable to set time stamp on '%s'", pathname); + err = kNuErrFileSetDate; + goto bail; + } + } + } + +#else + #error "Port this" +#endif + +bail: + return err; +} + + +/* + * Returns "true" if the record is locked (in the ProDOS sense). + * + * Bits 31-8 reserved, must be zero + * Bit 7 (D) 1 = destroy enabled + * Bit 6 (R) 1 = rename enabled + * Bit 5 (B) 1 = file needs to be backed up + * Bits 4-3 reserved, must be zero + * Bit 2 (I) 1 = file is invisible + * Bit 1 (W) 1 = write enabled + * Bit 0 (R) 1 = read enabled + * + * A "locked" file would be 00?00001, "unlocked" 11?00011, with many + * possible variations. For our purposes, we treat all files as unlocked + * unless they match the classic "locked" bit pattern. + */ +static Boolean +Nu_IsRecordLocked(const NuRecord* pRecord) +{ + if (pRecord->recAccess == 0x21L || pRecord->recAccess == 0x01L) + return true; + else + return false; +} + +/* + * Set the file access permissions based on what's in the record. + * + * This assumes that the file is currently writable, so we only need + * to do something if the original file was "locked". + */ +static NuError +Nu_SetFileAccess(NuArchive* pArchive, const NuRecord* pRecord, + const char* pathname) +{ + NuError err = kNuErrNone; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(pathname != nil); + +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + /* only need to do something if the file was "locked" */ + if (Nu_IsRecordLocked(pRecord)) { + mode_t mask; + + /* set it to 444, modified by umask */ + mask = umask(0); + umask(mask); + //DBUG(("+++ chmod '%s' %03o (mask=%03o)\n", pathname, + // (S_IRUSR | S_IRGRP | S_IROTH) & ~mask, mask)); + if (chmod(pathname, (S_IRUSR | S_IRGRP | S_IROTH) & ~mask) < 0) { + Nu_ReportError(NU_BLOB, errno, + "unable to set access for '%s' to %03o", pathname, (int) mask); + err = kNuErrFileSetAccess; + goto bail; + } + } + +#else + #error "Port this" +#endif + +bail: + return err; +} + + +/* + * =========================================================================== + * Create/open an output file + * =========================================================================== + */ + +/* + * Prepare an existing file for writing. + * + * Generally this just involves ensuring that the file is writable. If + * this is a convenient place to truncate it, we should do that too. + */ +static NuError +Nu_PrepareForWriting(NuArchive* pArchive, const char* pathname, + Boolean prepRsrc, NuFileInfo* pFileInfo) +{ + NuError err = kNuErrNone; +#if defined(MAC_LIKE) + char path[4096]; +#endif + + Assert(pArchive != nil); + Assert(pathname != nil); + Assert(prepRsrc == true || prepRsrc == false); + Assert(pFileInfo != nil); + + Assert(pFileInfo->isValid == true); + + /* don't go playing with directories, pipes, etc */ + if (pFileInfo->isRegularFile != true) + return kNuErrNotRegularFile; + +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) +# if defined(MAC_LIKE) + if (prepRsrc) { + strcpy(path, pathname); + strcat(path, "/rsrc"); + pathname = path; + } +# endif + if (!(pFileInfo->unixMode & S_IWUSR)) { + /* make it writable by owner, plus whatever it was before */ + if (chmod(pathname, S_IWUSR | pFileInfo->unixMode) < 0) { + Nu_ReportError(NU_BLOB, errno, + "unable to set access for '%s'", pathname); + err = kNuErrFileSetAccess; + goto bail; + } + } + + return kNuErrNone; + +#else + #error "Port this" +#endif + +bail: + return err; +} + + +/* + * Invoke the system-dependent directory creation function. + */ +static NuError +Nu_Mkdir(NuArchive* pArchive, const char* dir) +{ + NuError err = kNuErrNone; + + Assert(pArchive != nil); + Assert(dir != nil); + +#if defined(UNIX_LIKE) + if (mkdir(dir, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH) < 0) { + err = errno ? errno : kNuErrDirCreate; + Nu_ReportError(NU_BLOB, err, "Unable to create dir '%s'", dir); + goto bail; + } + +#elif defined(WINDOWS_LIKE) + if (mkdir(dir) < 0) { + err = errno ? errno : kNuErrDirCreate; + Nu_ReportError(NU_BLOB, err, "Unable to create dir '%s'", dir); + goto bail; + } + +#else + #error "Port this" +#endif + +bail: + return err; +} + + +/* + * Create a single subdirectory if it doesn't exist. If the next-highest + * subdirectory level doesn't exist either, cut down the pathname and + * recurse. + */ +static NuError +Nu_CreateSubdirIFN(NuArchive* pArchive, const char* pathStart, + const char* pathEnd, char fssep) +{ + NuError err = kNuErrNone; + NuFileInfo fileInfo; + char* tmpBuf = nil; + + Assert(pArchive != nil); + Assert(pathStart != nil); + Assert(pathEnd != nil); + Assert(fssep != '\0'); + + /* pathStart might have whole path, but we only want up to "pathEnd" */ + tmpBuf = strdup(pathStart); + tmpBuf[pathEnd - pathStart +1] = '\0'; + + err = Nu_GetFileInfo(pArchive, tmpBuf, &fileInfo); + if (err == kNuErrFileNotFound) { + /* dir doesn't exist; move up a level and check parent */ + pathEnd = strrchr(tmpBuf, fssep); + if (pathEnd != nil) { + pathEnd--; + Assert(pathEnd >= tmpBuf); + err = Nu_CreateSubdirIFN(pArchive, tmpBuf, pathEnd, fssep); + BailError(err); + } + + /* parent is taken care of; create this one */ + err = Nu_Mkdir(pArchive, tmpBuf); + BailError(err); + } else if (err != kNuErrNone) { + goto bail; + } else { + /* file does exist, make sure it's a directory */ + Assert(fileInfo.isValid == true); + if (!fileInfo.isDirectory) { + err = kNuErrNotDir; + Nu_ReportError(NU_BLOB, err, "Unable to create path '%s'", tmpBuf); + goto bail; + } + } + +bail: + Nu_Free(pArchive, tmpBuf); + return err; +} + +/* + * Create subdirectories, if needed. The paths leading up to the filename + * in "pathname" will be created. + * + * If "pathname" is just a filename, or the set of directories matches + * the last directory we created, we don't do anything. + */ +static NuError +Nu_CreatePathIFN(NuArchive* pArchive, const char* pathname, char fssep) +{ + NuError err = kNuErrNone; + const char* pathStart; + const char* pathEnd; + + Assert(pArchive != nil); + Assert(pathname != nil); + Assert(fssep != '\0'); + + pathStart = pathname; + +#if !defined(MAC_LIKE) /* On the Mac, if it's a full path, treat it like one */ + if (pathname[0] == fssep) + pathStart++; +#endif + + /* NOTE: not expecting names like "foo/bar/ack/", with terminating fssep */ + pathEnd = strrchr(pathStart, fssep); + if (pathEnd == nil) { + /* no subdirectory components found */ + goto bail; + } + pathEnd--; + + Assert(pathEnd >= pathStart); + if (pathEnd - pathStart < 0) + goto bail; + + /* + * On some filesystems, strncasecmp would be appropriate here. However, + * this is meant solely as an optimization to avoid extra stat() calls, + * so we want to use the most restrictive case. + */ + if (pArchive->lastDirCreated && + strncmp(pathStart, pArchive->lastDirCreated, pathEnd - pathStart +1) == 0) + { + /* we created this one recently, don't do it again */ + goto bail; + } + + /* + * Test to determine which directories exist. The most likely case + * is that some or all of the components have already been created, + * so we start with the last one and work backward. + */ + err = Nu_CreateSubdirIFN(pArchive, pathStart, pathEnd, fssep); + BailError(err); + +bail: + return err; +} + + +/* + * Open the file for writing, possibly truncating it. + */ +static NuError +Nu_OpenFileForWrite(NuArchive* pArchive, const char* pathname, + Boolean openRsrc, FILE** pFp) +{ +#if defined(MAC_LIKE) + char path[4096]; + if (openRsrc) { + strcpy(path, pathname); + strcat(path, "/rsrc"); + pathname = path; + } +#endif + *pFp = fopen(pathname, kNuFileOpenWriteTrunc); + if (*pFp == nil) + return errno ? errno : -1; + return kNuErrNone; +} + + +/* + * Open an output file and prepare it for writing. + * + * There are a number of things to take into consideration, including + * deal with "file exists" conditions, handling Mac/IIgs file types, + * coping with resource forks on extended files, and handling the + * "freshen" option that requires us to only update files that are + * older than what we have. + */ +NuError +Nu_OpenOutputFile(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, const char* newPathname, char newFssep, + FILE** pFp) +{ + NuError err = kNuErrNone; + Boolean exists, isForkedFile, extractingRsrc = false; + NuFileInfo fileInfo; + NuErrorStatus errorStatus; + NuResult result; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(pThread != nil); + Assert(newPathname != nil); + Assert(pFp != nil); + + /* set up some defaults, in case something goes wrong */ + errorStatus.operation = kNuOpExtract; + errorStatus.err = kNuErrInternal; + errorStatus.sysErr = 0; + errorStatus.message = nil; + errorStatus.pRecord = pRecord; + errorStatus.pathname = newPathname; + errorStatus.origPathname = nil; + errorStatus.filenameSeparator = newFssep; + /*errorStatus.origArchiveTouched = false;*/ + errorStatus.canAbort = true; + errorStatus.canRetry = true; + errorStatus.canIgnore = false; + errorStatus.canSkip = true; + errorStatus.canRename = true; + errorStatus.canOverwrite = true; + + /* decide if this is a forked file (i.e. has *both* forks) */ + isForkedFile = Nu_IsForkedFile(pArchive, pRecord); + + /* decide if we're extracting a resource fork */ + if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == + kNuThreadIDRsrcFork) + { + extractingRsrc = true; + } + + /* + * Determine whether the file and fork already exists. If the file + * is one we just created, and the fork we want to write to is + * empty, this will *not* set "exists". + */ + fileInfo.isValid = false; + err = Nu_FileForkExists(pArchive, newPathname, isForkedFile, + extractingRsrc, &exists, &fileInfo); + BailError(err); + + if (exists) { + Assert(fileInfo.isValid == true); + + /* + * The file exists when it shouldn't. Decide what to do, based + * on the options configured by the application. + */ + + /* + * Start by checking to see if we're willing to overwrite older files. + * If not, see if the application wants to rename the file, or force + * the overwrite. Most likely they'll just want to skip it. + */ + if ((pArchive->valOnlyUpdateOlder) && + !Nu_IsOlder(&fileInfo.modWhen, &pRecord->recModWhen)) + { + if (pArchive->errorHandlerFunc != nil) { + errorStatus.err = kNuErrNotNewer; + result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); + + switch (result) { + case kNuAbort: + err = kNuErrAborted; + goto bail; + case kNuRetry: + case kNuRename: + err = kNuErrRename; + goto bail; + case kNuSkip: + err = kNuErrSkipped; + goto bail; + case kNuOverwrite: + break; /* fall back into main code */ + case kNuIgnore: + default: + err = kNuErrSyntax; + Nu_ReportError(NU_BLOB, err, + "Wasn't expecting result %d here", result); + goto bail; + } + } else { + err = kNuErrNotNewer; + goto bail; + } + } + + /* If they "might" allow overwrites, and they have an error-handling + * callback defined, call that to find out what they want to do + * here. Options include skipping the file, overwriting the file, + * and extracting to a different file. + */ + if (pArchive->valHandleExisting == kNuMaybeOverwrite) { + if (pArchive->errorHandlerFunc != nil) { + errorStatus.err = kNuErrFileExists; + result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); + + switch (result) { + case kNuAbort: + err = kNuErrAborted; + goto bail; + case kNuRetry: + case kNuRename: + err = kNuErrRename; + goto bail; + case kNuSkip: + err = kNuErrSkipped; + goto bail; + case kNuOverwrite: + break; /* fall back into main code */ + case kNuIgnore: + default: + err = kNuErrSyntax; + Nu_ReportError(NU_BLOB, err, + "Wasn't expecting result %d here", result); + goto bail; + } + } else { + /* no error handler, return an error to the caller */ + err = kNuErrFileExists; + goto bail; + } + } else if (pArchive->valHandleExisting == kNuNeverOverwrite) { + err = kNuErrSkipped; + goto bail; + } + } else { + /* + * The file doesn't exist. If we're doing a "freshen" from the + * archive, we don't want to create a new file, so we return an + * error to the user instead. However, we give the application + * a chance to straighten things out. Most likely they'll just + * return kNuSkip. + */ + if (pArchive->valHandleExisting == kNuMustOverwrite) { + DBUG(("+++ can't freshen nonexistent file '%s'\n", newPathname)); + if (pArchive->errorHandlerFunc != nil) { + errorStatus.err = kNuErrDuplicateNotFound; + + /* give them a chance to rename */ + result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); + switch (result) { + case kNuAbort: + err = kNuErrAborted; + goto bail; + case kNuRetry: + case kNuRename: + err = kNuErrRename; + goto bail; + case kNuSkip: + err = kNuErrSkipped; + goto bail; + case kNuOverwrite: + break; /* fall back into main code */ + case kNuIgnore: + default: + err = kNuErrSyntax; + Nu_ReportError(NU_BLOB, err, + "Wasn't expecting result %d here", result); + goto bail; + } + } else { + /* no error handler, return an error to the caller */ + err = kNuErrDuplicateNotFound; + goto bail; + } + } + } + + Assert(err == kNuErrNone); + + /* + * After the above, if the file exists then we need to prepare it for + * writing. On some systems -- notably those with forked files -- it + * may be easiest to delete the entire file and start over. On + * simpler systems, an (optional) chmod followed by an open that + * truncates the file should be sufficient. + * + * If the file didn't exist, we need to be sure that the path leading + * up to its eventual location exists. This might require creating + * several directories. We distinguish the case of "file isn't there" + * from "file is there but fork isn't" by seeing if we were able to + * get valid file info. + */ + if (exists) { + Assert(fileInfo.isValid == true); + err = Nu_PrepareForWriting(pArchive, newPathname, extractingRsrc, + &fileInfo); + BailError(err); + } else if (!fileInfo.isValid) { + err = Nu_CreatePathIFN(pArchive, newPathname, newFssep); + BailError(err); + } + + /* + * Open sesame. + */ + err = Nu_OpenFileForWrite(pArchive, newPathname, extractingRsrc, pFp); + BailError(err); + + +#if defined(HAS_RESOURCE_FORKS) + pArchive->lastFileCreated = newPathname; +#endif + +bail: + if (err != kNuErrNone) { + if (err != kNuErrSkipped && err != kNuErrRename && + err != kNuErrFileExists) + { + Nu_ReportError(NU_BLOB, err, "Unable to open '%s'%s", + newPathname, extractingRsrc ? " (rsrc fork)" : ""); + } + } + return err; +} + + +/* + * Close the output file, adjusting the modification date and access + * permissions as needed. + * + * On GS/OS and Mac OS, we may need to set the file type here, depending on + * how much we managed to do when the file was first created. IIRC, + * the GS/OS Open call should allow setting the file type. + * + * BUG: on GS/OS, if we set the file access after writing the data fork, + * we may not be able to open the same file for writing the rsrc fork. + * We can't suppress setting the access permissions, because we don't know + * if the application will want to write both forks to the same file, or + * for that matter will want to write the resource fork at all. Looks + * like we will have to be smart enough to reset the access permissions + * when writing a rsrc fork to a file with just a data fork. This isn't + * quite right, but it's close enough. + */ +NuError +Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord, FILE* fp, + const char* pathname) +{ + NuError err; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(fp != nil); + + fclose(fp); + + err = Nu_SetFileDates(pArchive, pRecord, pathname); + BailError(err); + + err = Nu_SetFileAccess(pArchive, pRecord, pathname); + BailError(err); + +#ifdef MAC_LIKE + OSErr result; + OSType fileType; + FSCatalogInfo catalogInfo; + FSRef ref; + + result = FSPathMakeRef(pathname, &ref, NULL); + BailError(result); + + result = FSGetCatalogInfo(&ref, kFSCatInfoNodeFlags|kFSCatInfoFinderInfo, &catalogInfo, + NULL, NULL, NULL); + if (result) { + BailError(kNuErrFileStat); + } + + /* Build the type and creator */ + + fileType = 0x70000000; + fileType |= (pRecord->recFileType & 0xFF) << 16; + fileType |= (pRecord->recExtraType & 0xFFFF); + + /* Set the type and creator */ + + ((FileInfo *) &catalogInfo.finderInfo)->fileType = fileType; + ((FileInfo *) &catalogInfo.finderInfo)->fileCreator = 'pdos'; + result = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo); + BailError(result); +#endif + +bail: + return kNuErrNone; +} + +/* + * =========================================================================== + * Open an input file + * =========================================================================== + */ + +/* + * Open the file for reading, in "binary" mode when necessary. + */ +static NuError +Nu_OpenFileForRead(NuArchive* pArchive, const char* pathname, + Boolean openRsrc, FILE** pFp) +{ + *pFp = fopen(pathname, kNuFileOpenReadOnly); + if (*pFp == nil) + return errno ? errno : -1; + return kNuErrNone; +} + + +/* + * Open an input file and prepare it for reading. + * + * If the file can't be found, we give the application an opportunity to + * skip the absent file, retry, or abort the whole thing. + */ +NuError +Nu_OpenInputFile(NuArchive* pArchive, const char* pathname, + Boolean openRsrc, FILE** pFp) +{ + NuError err = kNuErrNone; + NuError openErr = kNuErrNone; + NuErrorStatus errorStatus; + NuResult result; + + Assert(pArchive != nil); + Assert(pathname != nil); + Assert(pFp != nil); + +#if defined(MAC_LIKE) + char path[4096]; + if (openRsrc) { + strcpy(path, pathname); + strcat(path, "/rsrc"); + pathname = path; + } +#endif + +retry: + /* + * Open sesame. + */ + err = Nu_OpenFileForRead(pArchive, pathname, openRsrc, pFp); + if (err == kNuErrNone) /* success! */ + goto bail; + + if (err == ENOENT) + openErr = kNuErrFileNotFound; + + if (pArchive->errorHandlerFunc != nil) { + errorStatus.operation = kNuOpAdd; + errorStatus.err = openErr; + errorStatus.sysErr = 0; + errorStatus.message = nil; + errorStatus.pRecord = nil; + errorStatus.pathname = pathname; + errorStatus.origPathname = nil; + errorStatus.filenameSeparator = '\0'; + /*errorStatus.origArchiveTouched = false;*/ + errorStatus.canAbort = true; + errorStatus.canRetry = true; + errorStatus.canIgnore = false; + errorStatus.canSkip = true; + errorStatus.canRename = false; + errorStatus.canOverwrite = false; + + DBUG(("--- invoking error handler function\n")); + result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); + + switch (result) { + case kNuAbort: + err = kNuErrAborted; + goto bail; + case kNuRetry: + goto retry; + case kNuSkip: + err = kNuErrSkipped; + goto bail; + case kNuRename: + case kNuOverwrite: + case kNuIgnore: + default: + err = kNuErrSyntax; + Nu_ReportError(NU_BLOB, err, + "Wasn't expecting result %d here", result); + goto bail; + } + } else { + DBUG(("+++ no error callback in OpenInputFile\n")); + } + +bail: + if (err == kNuErrNone) { + Assert(*pFp != nil); + } else { + if (err != kNuErrSkipped && err != kNuErrRename && + err != kNuErrFileExists) + { + Nu_ReportError(NU_BLOB, err, "Unable to open '%s'%s", + pathname, openRsrc ? " (rsrc fork)" : ""); + } + } + return err; +} + + +/* + * =========================================================================== + * Delete and rename files + * =========================================================================== + */ + +/* + * Delete a file. + */ +NuError +Nu_DeleteFile(const char* pathname) +{ +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + int cc; + + DBUG(("--- Deleting '%s'\n", pathname)); + + cc = unlink(pathname); + if (cc < 0) + return errno ? errno : -1; + else + return kNuErrNone; +#else + #error "Port this" +#endif +} + +/* + * Rename a file from "fromPath" to "toPath". + */ +NuError +Nu_RenameFile(const char* fromPath, const char* toPath) +{ +#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) + int cc; + + DBUG(("--- Renaming '%s' to '%s'\n", fromPath, toPath)); + + cc = rename(fromPath, toPath); + if (cc < 0) + return errno ? errno : -1; + else + return kNuErrNone; +#else + #error "Port this" +#endif +} + + +/* + * =========================================================================== + * NuError wrappers for std functions + * =========================================================================== + */ + +/* + * Wrapper for ftell(). + */ +NuError +Nu_FTell(FILE* fp, long* pOffset) +{ + Assert(fp != nil); + Assert(pOffset != nil); + + errno = 0; + *pOffset = ftell(fp); + if (*pOffset < 0) { + Nu_ReportError(NU_NILBLOB, errno, "file ftell failed"); + return errno ? errno : kNuErrFileSeek; + } + return kNuErrNone; +} + +/* + * Wrapper for fseek(). + */ +NuError +Nu_FSeek(FILE* fp, long offset, int ptrname) +{ + Assert(fp != nil); + Assert(ptrname == SEEK_SET || ptrname == SEEK_CUR || ptrname == SEEK_END); + + errno = 0; + if (fseek(fp, offset, ptrname) < 0) { + Nu_ReportError(NU_NILBLOB, errno, + "file fseek(%ld, %d) failed", offset, ptrname); + return errno ? errno : kNuErrFileSeek; + } + return kNuErrNone; +} + +/* + * Wrapper for fread(). Note the arguments resemble read(2) rather than the + * slightly silly ones used by fread(3S). + */ +NuError +Nu_FRead(FILE* fp, void* buf, size_t nbyte) +{ + size_t result; + + errno = 0; + result = fread(buf, nbyte, 1, fp); + if (result != 1) + return errno ? errno : kNuErrFileRead; + return kNuErrNone; +} + +/* + * Wrapper for fwrite(). Note the arguments resemble write(2) rather than the + * slightly silly ones used by fwrite(3S). + */ +NuError +Nu_FWrite(FILE* fp, const void* buf, size_t nbyte) +{ + size_t result; + + errno = 0; + result = fwrite(buf, nbyte, 1, fp); + if (result != 1) + return errno ? errno : kNuErrFileWrite; + return kNuErrNone; +} + +/* + * =========================================================================== + * Misc functions + * =========================================================================== + */ + +/* + * Copy a section from one file to another. + */ +NuError +Nu_CopyFileSection(NuArchive* pArchive, FILE* dstFp, FILE* srcFp, long length) +{ + NuError err; + long readLen; + + Assert(pArchive != nil); + Assert(dstFp != nil); + Assert(srcFp != nil); + Assert(length >= 0); /* can be == 0, e.g. empty data fork from HFS */ + + /* nice big buffer, for speed... could use getc/putc for simplicity */ + err = Nu_AllocCompressionBufferIFN(pArchive); + BailError(err); + + DBUG(("+++ Copying %ld bytes\n", length)); + + while (length) { + readLen = length > kNuGenCompBufSize ? kNuGenCompBufSize : length; + + err = Nu_FRead(srcFp, pArchive->compBuf, readLen); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, + "Nu_FRead failed while copying file section " + "(fp=0x%08lx, readLen=%ld, length=%ld, err=%d)\n", + (long) srcFp, readLen, length, err); + goto bail; + } + err = Nu_FWrite(dstFp, pArchive->compBuf, readLen); + BailError(err); + + length -= readLen; + } + +bail: + return err; +} + + +/* + * Find the length of an open file. + * + * On UNIX it would be easier to just call fstat(), but fseek is portable. + * + * Only useful for files < 2GB in size. + * + * (pArchive is only used for BailError message reporting, so it's okay + * to call here with a nil pointer if the archive isn't open yet.) + */ +NuError +Nu_GetFileLength(NuArchive* pArchive, FILE* fp, long* pLength) +{ + NuError err; + long oldpos; + + Assert(fp != nil); + Assert(pLength != nil); + + err = Nu_FTell(fp, &oldpos); + BailError(err); + + err = Nu_FSeek(fp, 0, SEEK_END); + BailError(err); + + err = Nu_FTell(fp, pLength); + BailError(err); + + err = Nu_FSeek(fp, oldpos, SEEK_SET); + BailError(err); + +bail: + return err; +} + + +/* + * Truncate an open file. This differs from ftruncate() in that it takes + * a FILE* instead of an fd, and the length is a long instead of off_t. + */ +NuError +Nu_TruncateOpenFile(FILE* fp, long length) +{ + #if defined(HAVE_FTRUNCATE) + if (ftruncate(fileno(fp), length) < 0) + return errno ? errno : -1; + return kNuErrNone; + #elif defined(HAVE_CHSIZE) + if (chsize(fileno(fp), length) < 0) + return errno ? errno : -1; + return kNuErrNone; + #else + /* not fatal; return this to indicate that it's an unsupported operation */ + return kNuErrInternal; + #endif +} + diff --git a/nufxlib/Funnel.c b/nufxlib/Funnel.c new file mode 100644 index 0000000..4762d96 --- /dev/null +++ b/nufxlib/Funnel.c @@ -0,0 +1,925 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Implementation of NuFunnel, NuStraw and ProgressUpdater. + */ +#include "NufxLibPriv.h" + + +/* + * =========================================================================== + * Progress updater + * =========================================================================== + */ + +/* + * Initialize the fields in a ProgressData structure, prior to compressing + * data into a record. + * + * The same structure will be used when expanding all threads in a given + * record. + */ +NuError +Nu_ProgressDataInit_Compress(NuArchive* pArchive, NuProgressData* pProgressData, + const NuRecord* pRecord, const char* origPathname) +{ + const char* cp; + + Assert(pProgressData != nil); + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(origPathname != nil); + + pProgressData->pRecord = pRecord; + + pProgressData->origPathname = origPathname; + pProgressData->pathname = pRecord->filename; + cp = strrchr(pRecord->filename, + NuGetSepFromSysInfo(pRecord->recFileSysInfo)); + if (cp == nil || *(cp+1) == '\0') + pProgressData->filename = pProgressData->pathname; + else + pProgressData->filename = cp+1; + + pProgressData->operation = kNuOpAdd; + pProgressData->state = kNuProgressPreparing; + /*pProgressData->compressedLength = 0;*/ + /*pProgressData->compressedProgress = 0;*/ + pProgressData->uncompressedLength = 0; + pProgressData->uncompressedProgress = 0; + + pProgressData->compress.threadFormat = (NuThreadFormat)-1; + + /* ya know... if this is nil, none of the above matters much */ + pProgressData->progressFunc = pArchive->progressUpdaterFunc; + + return kNuErrNone; +} + + +/* + * Initialize the fields in a ProgressData structure, prior to expanding + * data from a record. + * + * The same structure will be used when expanding all threads in a given + * record. + */ +NuError +Nu_ProgressDataInit_Expand(NuArchive* pArchive, NuProgressData* pProgressData, + const NuRecord* pRecord, const char* newPathname, char newFssep, + NuValue convertEOL) +{ + const NuThread* pThreadIter; + const char* cp; + int i; + + Assert(pProgressData != nil); + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(newPathname != nil); + Assert(newFssep != 0); + + pProgressData->pRecord = pRecord; + pProgressData->expand.pThread = nil; + + pProgressData->origPathname = pRecord->filename; + pProgressData->pathname = newPathname; + cp = strrchr(newPathname, newFssep); + if (cp == nil || *(cp+1) == '\0') + pProgressData->filename = newPathname; + else + pProgressData->filename = cp+1; + + pProgressData->expand.convertEOL = convertEOL; + + /* total up the data threads */ + pProgressData->expand.totalCompressedLength = 0; + pProgressData->expand.totalUncompressedLength = 0; + + for (i = 0; i < (int)pRecord->recTotalThreads; i++) { + pThreadIter = Nu_GetThread(pRecord, i); + if (pThreadIter->thThreadClass != kNuThreadClassData) + continue; + pProgressData->expand.totalCompressedLength += pThreadIter->thCompThreadEOF; + pProgressData->expand.totalUncompressedLength += pThreadIter->actualThreadEOF; + } + + pProgressData->operation = kNuOpExtract; + if (pArchive->testMode) + pProgressData->operation = kNuOpTest; + pProgressData->state = kNuProgressPreparing; + /*pProgressData->expand.compressedLength = 0;*/ + /*pProgressData->expand.compressedProgress = 0;*/ + pProgressData->uncompressedLength = 0; + pProgressData->uncompressedProgress = 0; + + /* ya know... if this is nil, none of the above matters much */ + pProgressData->progressFunc = pArchive->progressUpdaterFunc; + + return kNuErrNone; +} + + +/* + * Do the setup on a ProgressData prior to compressing a thread. + */ +NuError +Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw, + NuThreadFormat threadFormat, ulong sourceLen) +{ + NuProgressData* pProgressData; + + Assert(pArchive != nil); + Assert(pStraw != nil); + Assert(sourceLen < 32767*65536); + + pProgressData = pStraw->pProgress; + if (pProgressData == nil) + return kNuErrNone; + + pProgressData->uncompressedLength = sourceLen; + pProgressData->compress.threadFormat = threadFormat; + + return kNuErrNone; +} + +/* + * Do the setup on a ProgressData prior to expanding a thread. + * + * "pThread" is the thread being expanded. + */ +NuError +Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel, + const NuThread* pThread) +{ + NuProgressData* pProgressData; + + Assert(pArchive != nil); + Assert(pFunnel != nil); + Assert(pThread != nil); + + pProgressData = pFunnel->pProgress; + if (pProgressData == nil) + return kNuErrNone; + + /*pProgressData->compressedLength = pThread->thCompThreadEOF;*/ + pProgressData->uncompressedLength = pThread->actualThreadEOF; + pProgressData->expand.pThread = pThread; + + return kNuErrNone; +} + +/* + * Compute a completion percentage. + */ +static int +Nu_ComputePercent(ulong total, ulong progress) +{ + ulong perc; + + if (!total) + return 0; + + if (total < 21474836) { + perc = (progress * 100 + 50) / total; + if (perc > 100) + perc = 100; + } else { + perc = progress / (total / 100); + if (perc > 100) + perc = 100; + } + + return (int) perc; +} + +/* + * Send the initial progress message, before the output file is opened + * (when extracting) or the input file is opened (when adding). + */ +NuError +Nu_SendInitialProgress(NuArchive* pArchive, NuProgressData* pProgress) +{ + NuResult result; + + Assert(pArchive != nil); + Assert(pProgress != nil); + + if (pProgress->progressFunc == nil) + return kNuErrNone; + + pProgress->percentComplete = Nu_ComputePercent( + pProgress->uncompressedLength, pProgress->uncompressedProgress); + + result = (*pProgress->progressFunc)(pArchive, (NuProgressData*) pProgress); + + if (result == kNuSkip) + return kNuErrSkipped; /* [dunno how well this works] */ + if (result == kNuAbort) + return kNuErrAborted; + + return kNuErrNone; +} + + +/* + * =========================================================================== + * NuFunnel object + * =========================================================================== + */ + +/* + * Allocate and initialize a Funnel. + */ +NuError +Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink, NuValue convertEOL, + NuValue convertEOLTo, NuProgressData* pProgress, NuFunnel** ppFunnel) +{ + NuError err = kNuErrNone; + NuFunnel* pFunnel = nil; + + Assert(ppFunnel != nil); + Assert(pDataSink != nil); + Assert(convertEOL == kNuConvertOff || + convertEOL == kNuConvertOn || + convertEOL == kNuConvertAuto); + + pFunnel = Nu_Calloc(pArchive, sizeof(*pFunnel)); + BailAlloc(pFunnel); + pFunnel->buffer = Nu_Malloc(pArchive, kNuFunnelBufSize); + BailAlloc(pFunnel->buffer); + + pFunnel->pDataSink = pDataSink; + pFunnel->convertEOL = convertEOL; + pFunnel->convertEOLTo = convertEOLTo; + pFunnel->convertEOLFrom = kNuEOLUnknown; + pFunnel->pProgress = pProgress; + + pFunnel->checkStripHighASCII = (pArchive->valStripHighASCII != 0); + pFunnel->doStripHighASCII = false; /* determined on first write */ + + pFunnel->isFirstWrite = true; + +bail: + if (err != kNuErrNone) + Nu_FunnelFree(pArchive, pFunnel); + else + *ppFunnel = pFunnel; + return err; +} + + +/* + * Free a Funnel. + * + * The data should already have been written; it's not the duty of a + * "free" function to flush data out. + */ +NuError +Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel) +{ + if (pFunnel == nil) + return kNuErrNone; + +#ifdef DEBUG_MSGS + if (pFunnel->bufCount) + Nu_ReportError(NU_BLOB_DEBUG, kNuErrNone, + "freeing non-empty funnel"); +#endif + + Nu_Free(pArchive, pFunnel->buffer); + Nu_Free(pArchive, pFunnel); + + return kNuErrNone; +} + + +#if 0 +/* + * Set the maximum amount of output we're willing to push through the + * funnel. Attempts to write more than this many bytes will fail. This + * allows us to bail out as soon as it's apparent that compression is + * failing and is actually resulting in a larger file. + */ +void +Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, ulong maxBytes) +{ + Assert(pFunnel != nil); + Assert(maxBytes > 0); + + pFunnel->outMax = maxBytes; + if (pFunnel->outCount >= pFunnel->outMax) + pFunnel->outMaxExceeded = true; + else + pFunnel->outMaxExceeded = false; +} +#endif + + +/* + * Check to see if this is a high-ASCII file. To qualify, EVERY + * character must have its high bit set, except for spaces (0x20). + * (The exception is courtesy Glen Bredon's "Merlin".) + */ +static Boolean +Nu_CheckHighASCII(const NuFunnel* pFunnel, const unsigned char* buffer, + unsigned long count) +{ + Boolean isHighASCII; + + Assert(buffer != nil); + Assert(count != 0); + Assert(pFunnel->checkStripHighASCII); + + isHighASCII = true; + while (count--) { + if ((*buffer & 0x80) == 0 && *buffer != 0x20) { + isHighASCII = false; + break; + } + + buffer++; + } + + return isHighASCII; +} + +/* + * Table determining what's a binary character and what isn't. It would + * possibly be more compact to generate this from a simple description, + * but I'm hoping static/const data will end up in the code segment and + * save space on the heap. + * + * This corresponds to less-316's ISO-latin1 "8bcccbcc18b95.33b.". This + * may be too loose by itself; we may want to require that the lower-ASCII + * values appear in higher proportions than the upper-ASCII values. + * Otherwise we run the risk of converting a binary file with specific + * properties. (Note that "upper-ASCII" refers to umlauts and other + * accented characters, not DOS 3.3 "high ASCII".) + * + * The auto-detect mechanism will never be perfect though, so there's not + * much point in tweaking it to death. + */ +static const char gNuIsBinary[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, /* ^@-^O */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P-^_ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* - / */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - ? */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* P - _ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - DEL */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 */ +}; + +#define kNuMaxUpperASCII 1 /* max #of binary chars per 100 bytes */ +#define kNuMinConvThreshold 40 /* min of 40 chars for auto-detect */ +/* + * Decide, based on the contents of the buffer, whether we should do an + * EOL conversion on the data. + * + * We need to decide if we are looking at text data, and if so, what kind + * of line terminator is in use. + * + * If we don't have enough data to make a determination, don't mess with it. + * (Thought for the day: add a "bias" flag, based on the NuRecord fileType, + * that causes us to handle borderline or sub-min-threshold cases more + * reasonably. If it's of type TXT, it's probably text.) + * + * We try to figure out whether it's CR, LF, or CRLF, so that we can + * skip the CPU-intensive conversion process if it isn't necessary. + * + * We will also enable a "high-ASCII" stripper if requested. This is + * only enabled when EOL conversions are enabled. + * + * Returns kConvEOLOff or kConvEOLOn, and sets pFunnel->doStripHighASCII + * if pFunnel->CheckStripHighASCII is set. + */ +static NuValue +Nu_DetermineConversion(NuFunnel* pFunnel, const uchar* buffer, ulong count) +{ + ulong bufCount, numBinary, numLF, numCR; + Boolean isHighASCII; + uchar val; + + if (count < kNuMinConvThreshold) + return kNuConvertOff; + + /* + * Check to see if the buffer is all high-ASCII characters. If it is, + * we want to strip characters before we test them below. + */ + if (pFunnel->checkStripHighASCII) { + isHighASCII = Nu_CheckHighASCII(pFunnel, buffer, count); + DBUG(("+++ determined isHighASCII=%d\n", isHighASCII)); + } else { + isHighASCII = false; + DBUG(("+++ not even checking isHighASCII\n")); + } + + bufCount = count; + numBinary = numLF = numCR = 0; + while (bufCount--) { + val = *buffer++; + if (isHighASCII) + val &= 0x7f; + if (gNuIsBinary[val]) + numBinary++; + if (val == kNuCharLF) + numLF++; + if (val == kNuCharCR) + numCR++; + } + + /* if #found is > #allowed, it's a binary file */ + if (count < 100) { + /* use simplified check on files between kNuMinConvThreshold and 100 */ + if (numBinary > kNuMaxUpperASCII) + return kNuConvertOff; + } else if (numBinary > (count / 100) * kNuMaxUpperASCII) + return kNuConvertOff; + + /* + * If our "convert to" setting is the same as what we're converting + * from, we can turn off the converter and speed things up. + * + * These are simplistic, but this is intended as an optimization. We + * will blow it if the input has lots of CRs and LFs scattered about, + * and they just happen to be in equal amounts, but it's not clear + * to me that an automatic EOL conversion makes sense on that sort + * of file anyway. + * + * None of this applies if we also need to do a high-ASCII conversion. + */ + if (isHighASCII) { + pFunnel->doStripHighASCII = true; + } else { + if (numLF && !numCR) + pFunnel->convertEOLFrom = kNuEOLLF; + else if (!numLF && numCR) + pFunnel->convertEOLFrom = kNuEOLCR; + else if (numLF && numLF == numCR) + pFunnel->convertEOLFrom = kNuEOLCRLF; + else + pFunnel->convertEOLFrom = kNuEOLUnknown; + } + + return kNuConvertOn; +} + +/* + * Write a block of data to the appropriate output device. Test for + * excessive data, and raise "outMaxExceeded" if we overrun. + * + * This is either a Funnel function or a DataSink function, depending on + * your perspective. + */ +static inline void +Nu_FunnelPutBlock(NuFunnel* pFunnel, const uchar* buf, ulong len) +{ + Assert(pFunnel != nil); + Assert(pFunnel->pDataSink != nil); + Assert(buf != nil); + Assert(len > 0); + +#if 0 + if (pFunnel->outMax) { + if (pFunnel->outMaxExceeded) + return; + if (pFunnel->outCount + len > pFunnel->outMax) { + pFunnel->outMaxExceeded = true; + return; + } + } + pFunnel->outCount += len; +#endif + + Nu_DataSinkPutBlock(pFunnel->pDataSink, buf, len); +} + + +/* + * Output the EOL marker requested for this system. + */ +static inline void +Nu_PutEOL(NuFunnel* pFunnel) +{ + uchar ch; + + if (pFunnel->convertEOLTo == kNuEOLCR) { + ch = kNuCharCR; + Nu_FunnelPutBlock(pFunnel, &ch, 1); + } else if (pFunnel->convertEOLTo == kNuEOLLF) { + ch = kNuCharLF; + Nu_FunnelPutBlock(pFunnel, &ch, 1); + } else if (pFunnel->convertEOLTo == kNuEOLCRLF) { + ch = kNuCharCR; + Nu_FunnelPutBlock(pFunnel, &ch, 1); + ch = kNuCharLF; + Nu_FunnelPutBlock(pFunnel, &ch, 1); + } else { + Assert(0); + } +} + +/* + * Write a buffer of data, using the EOL conversion associated with the + * funnel (if any). + * + * When converting to the system's EOL convention, we take anything + * that looks like an EOL mark and convert it. Doesn't matter if it's + * CR, LF, or CRLF; all three get converted to whatever the system uses. + */ +static NuError +Nu_FunnelWriteConvert(NuFunnel* pFunnel, const uchar* buffer, ulong count) +{ + NuError err = kNuErrNone; + ulong progressCount = count; + + /*if (pFunnel->outMaxExceeded) + return kNuErrOutMax;*/ + + if (pFunnel->isFirstWrite) { + /* + * This is the first write/flush we've done on this Funnel. + * Check the data we have buffered to decide whether or not + * we want to do text conversions. + */ + if (pFunnel->convertEOL == kNuConvertAuto) { + pFunnel->convertEOL = Nu_DetermineConversion(pFunnel, buffer,count); + DBUG(("+++ DetermineConversion --> %ld / %ld (%d)\n", + pFunnel->convertEOL, pFunnel->convertEOLFrom, + pFunnel->doStripHighASCII)); + + if (pFunnel->convertEOLFrom == pFunnel->convertEOLTo) { + DBUG(("+++ Switching redundant converter off\n")); + pFunnel->convertEOL = kNuConvertOff; + } + /* put it where the progress meter can see it */ + if (pFunnel->pProgress != nil) + pFunnel->pProgress->expand.convertEOL = pFunnel->convertEOL; + } else if (pFunnel->convertEOL == kNuConvertOn) { + if (pFunnel->checkStripHighASCII) { + /* assume this part of the buffer is representative */ + pFunnel->doStripHighASCII = Nu_CheckHighASCII(pFunnel, + buffer, count); + } else { + Assert(!pFunnel->doStripHighASCII); + } + DBUG(("+++ Converter is on, convHighASCII=%d\n", + pFunnel->doStripHighASCII)); + } + } + Assert(pFunnel->convertEOL != kNuConvertAuto); /* on or off now */ + pFunnel->isFirstWrite = false; + + if (pFunnel->convertEOL == kNuConvertOff) { + /* write it straight */ + Nu_FunnelPutBlock(pFunnel, buffer, count); + } else { + /* do the EOL conversion and optional high-bit stripping */ + Boolean lastCR = pFunnel->lastCR; /* make local copy */ + uchar uch; + int mask; + + if (pFunnel->doStripHighASCII) + mask = 0x7f; + else + mask = 0xff; + + /* + * We could get a significant speed improvement here by writing + * non-EOL chars as a larger block instead of single bytes. + */ + while (count--) { + uch = (*buffer) & mask; + + if (uch == kNuCharCR) { + Nu_PutEOL(pFunnel); + lastCR = true; + } else if (uch == kNuCharLF) { + if (!lastCR) + Nu_PutEOL(pFunnel); + lastCR = false; + } else { + Nu_FunnelPutBlock(pFunnel, &uch, 1); + lastCR = false; + } + buffer++; + } + pFunnel->lastCR = lastCR; /* save copy */ + + } + + /*if (pFunnel->outMaxExceeded) + err = kNuErrOutMax;*/ + + err = Nu_DataSinkGetError(pFunnel->pDataSink); + + /* update progress counter with pre-LFCR count */ + if (err == kNuErrNone && pFunnel->pProgress != nil) + pFunnel->pProgress->uncompressedProgress += progressCount; + + return err; +} + + +/* + * Flush any data currently in the funnel. + */ +NuError +Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel) +{ + NuError err = kNuErrNone; + + if (!pFunnel->bufCount) + goto bail; + + err = Nu_FunnelWriteConvert(pFunnel, pFunnel->buffer, pFunnel->bufCount); + BailError(err); + + pFunnel->bufCount = 0; + err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel); + /* fall through with error */ + +bail: + return err; +} + + +/* + * Write a bunch of bytes into a funnel. They will be held in the buffer + * if they fit, or flushed out the bottom if not. + */ +NuError +Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel, const uchar* buffer, + ulong count) +{ + NuError err = kNuErrNone; + + /*pFunnel->inCount += count;*/ + + /* + * If it will fit into the buffer, just copy it in. + */ + if (pFunnel->bufCount + count < kNuFunnelBufSize) { + if (count == 1) /* minor optimization */ + *(pFunnel->buffer + pFunnel->bufCount) = *buffer; + else + memcpy(pFunnel->buffer + pFunnel->bufCount, buffer, count); + pFunnel->bufCount += count; + goto bail; + } else { + /* + * Won't fit. We have to flush what we have, and we can either + * blow out what we were just given or put it at the start of + * the buffer. + */ + if (pFunnel->bufCount) { + err = Nu_FunnelFlush(pArchive, pFunnel); + BailError(err); + } else { + err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel); + BailError(err); + } + + Assert(pFunnel->bufCount == 0); + + if (count >= kNuFunnelBufSize / 4) { + /* it's more than 25% of the buffer, just write it now */ + err = Nu_FunnelWriteConvert(pFunnel, buffer, count); + BailError(err); + } else { + memcpy(pFunnel->buffer, buffer, count); + pFunnel->bufCount = count; + } + goto bail; + } + +bail: + return err; +} + + +/* + * Set the Funnel's progress state. + */ +NuError +Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state) +{ + Assert(pFunnel != nil); + + if (pFunnel->pProgress == nil) + return kNuErrNone; + + pFunnel->pProgress->state = state; + + return kNuErrNone; +} + + +/* + * Send a progress update to the application, if they're interested. + */ +NuError +Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel) +{ + NuProgressData* pProgress; + + Assert(pArchive != nil); + Assert(pFunnel != nil); + + pProgress = pFunnel->pProgress; + if (pProgress == nil) + return kNuErrNone; /* no progress meter attached */ + + /* don't continue if they're not accepting progress messages */ + if (pProgress->progressFunc == nil) + return kNuErrNone; + + /* other than the choice of arguments, it's pretty much the same story */ + return Nu_SendInitialProgress(pArchive, pProgress); +} + + +/* + * Pull the "doExpand" parameter out of the data source. + */ +Boolean +Nu_FunnelGetDoExpand(NuFunnel* pFunnel) +{ + Assert(pFunnel != nil); + Assert(pFunnel->pDataSink != nil); + + return Nu_DataSinkGetDoExpand(pFunnel->pDataSink); +} + + +/* + * =========================================================================== + * NuStraw object + * =========================================================================== + */ + +/* + * Allocate and initialize a Straw. + */ +NuError +Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource, + NuProgressData* pProgress, NuStraw** ppStraw) +{ + NuError err = kNuErrNone; + NuStraw* pStraw = nil; + + Assert(ppStraw != nil); + Assert(pDataSource != nil); + + pStraw = Nu_Calloc(pArchive, sizeof(*pStraw)); + BailAlloc(pStraw); + pStraw->pDataSource = pDataSource; + pStraw->pProgress = pProgress; + pStraw->lastProgress = 0; + pStraw->lastDisplayed = 0; + +bail: + if (err != kNuErrNone) + Nu_StrawFree(pArchive, pStraw); + else + *ppStraw = pStraw; + return err; +} + +/* + * Free a Straw. + */ +NuError +Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw) +{ + if (pStraw == nil) + return kNuErrNone; + + /* we don't own the data source or progress meter */ + Nu_Free(pArchive, pStraw); + + return kNuErrNone; +} + + +/* + * Set the Straw's progress state. + */ +NuError +Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state) +{ + Assert(pStraw != nil); + Assert(pStraw->pProgress != nil); + + pStraw->pProgress->state = state; + + return kNuErrNone; +} + +/* + * Send a progress update to the application, if they're interested. + */ +NuError +Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw) +{ + NuProgressData* pProgress; + + Assert(pArchive != nil); + Assert(pStraw != nil); + + pProgress = pStraw->pProgress; + if (pProgress == nil) + return kNuErrNone; /* no progress meter attached */ + + /* don't continue if they're not accepting progress messages */ + if (pProgress->progressFunc == nil) + return kNuErrNone; + + /* other than the choice of arguments, it's pretty much the same story */ + return Nu_SendInitialProgress(pArchive, pProgress); +} + + +/* + * Read data from a straw. + */ +NuError +Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uchar* buffer, long len) +{ + NuError err; + + Assert(pArchive != nil); + Assert(pStraw != nil); + Assert(buffer != nil); + Assert(len > 0); + + /* + * No buffering going on, so this is straightforward. + */ + + err = Nu_DataSourceGetBlock(pStraw->pDataSource, buffer, len); + BailError(err); + + /* + * Progress updating for adding is a little more complicated than + * for extracting. When extracting, the funnel controls the size + * of the output buffer, and only pushes an update when the output + * buffer fills. Here, we don't know how much will be asked for at + * a time, so we have to pace the updates or we risk flooding the + * application. + * + * We also have another problem: we want to indicate how much data + * has been processed, not how much data is *about* to be processed. + * So we have to set the percentage based on how much was requested + * on the previous call. (This assumes that whatever they asked for + * last time has already been fully processed.) + */ + if (pStraw->pProgress != nil) { + pStraw->pProgress->uncompressedProgress = pStraw->lastProgress; + pStraw->lastProgress += len; + + if (!pStraw->pProgress->uncompressedProgress || + (pStraw->pProgress->uncompressedProgress - pStraw->lastDisplayed + > (kNuFunnelBufSize * 3 / 4))) + { + err = Nu_StrawSendProgressUpdate(pArchive, pStraw); + pStraw->lastDisplayed = pStraw->pProgress->uncompressedProgress; + BailError(err); + } + + } + +bail: + return err; +} + + +/* + * Rewind a straw. This rewinds the underlying data source, and resets + * some progress counters. + */ +NuError +Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw) +{ + Assert(pStraw != nil); + Assert(pStraw->pDataSource != nil); + + pStraw->lastProgress = 0; + pStraw->lastDisplayed = 0; + + return Nu_DataSourceRewind(pStraw->pDataSource); +} + diff --git a/nufxlib/INSTALL b/nufxlib/INSTALL new file mode 100644 index 0000000..50dbe43 --- /dev/null +++ b/nufxlib/INSTALL @@ -0,0 +1,183 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/nufxlib/Lzc.c b/nufxlib/Lzc.c new file mode 100644 index 0000000..ded5168 --- /dev/null +++ b/nufxlib/Lzc.c @@ -0,0 +1,1106 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * This is the LZW implementation found in the UNIX "compress" command, + * sometimes referred to as "LZC". GS/ShrinkIt v1.1 can unpack threads + * in LZC format, P8 ShrinkIt cannot. The only other application that + * is known to create LZC threads is the original NuLib. + * + * There's a lot of junk in here for the sake of smaller systems (e.g. MSDOS) + * and pre-ANSI compilers. For the most part it has been left unchanged. + * I have done some minor reformatting, and have undone the authors' + * penchant for assigning variables inside function call statements, but + * for the most part it is as it was. (A much cleaner implementation + * could probably be derived by adapting the NufxLib Lzw.c code...) + */ +#include "NufxLibPriv.h" + +#ifdef ENABLE_LZC + +/*#define DEBUG_LZC*/ + +/* + * Selected definitions from compress.h. + */ +typedef unsigned short CODE; +typedef unsigned char UCHAR; +typedef unsigned int INTCODE; +typedef unsigned int HASH; +typedef int FLAG; + +#ifndef FALSE /* let's get some sense to this */ +#define FALSE 0 +#define TRUE !FALSE +#endif + +#define CONST const +#ifndef FAR +# define FAR +#endif +#define NULLPTR(type) ((type FAR *) NULL) +#define ALLOCTYPE void + +#define INITBITS 9 +#define MINBITS 12 +#define MAXMAXBITS 16 +#define MAXBITS MAXMAXBITS +#define DFLTBITS MAXBITS + +#define UNUSED ((CODE)0) /* Indicates hash table value unused */ +#define CLEAR ((CODE)256) /* Code requesting table to be cleared */ +#define FIRSTFREE ((CODE)257) /* First free code for token encoding */ +#define MAXTOKLEN 512 /* Max chars in token; size of buffer */ +#define OK kNuErrNone /* Result codes from functions: */ + +#define BIT_MASK 0x1f +#define BLOCK_MASK 0x80 + +#define CHECK_GAP 10000L /* ratio check interval, for COMP40 */ + +static UCHAR gNu_magic_header[] = { 0x1F,0x9D }; + +/* don't need these */ +/*#define SPLIT_HT 1*/ +/*#define SPLIT_PFX 1*/ +/*#define COMP40 1*/ + +#define NOMEM kNuErrMalloc /* Ran out of memory */ +#define TOKTOOBIG kNuErrBadData /* Token longer than MAXTOKLEN chars */ +#define READERR kNuErrFileRead /* I/O error on input */ +#define WRITEERR kNuErrFileWrite /* I/O error on output */ +#define CODEBAD kNuErrBadData /* Infile contained a bad token code */ +#define TABLEBAD kNuErrInternal /* The tables got corrupted (!) */ +#define NOSAVING kNuErrNone /* no saving in file size */ + + +/* + * Normally in COMPUSI.UNI. + */ +static inline ALLOCTYPE FAR * +Nu_LZC_emalloc(NuArchive* pArchive, unsigned int x, int y) +{ + return Nu_Malloc(pArchive, x*y); +} +static inline void +Nu_LZC_efree(NuArchive* pArchive, ALLOCTYPE FAR * ptr) +{ + Nu_Free(pArchive, ptr); +} + +/*@H************************ < COMPRESS API > **************************** +* $@(#) compapi.c,v 4.3d 90/01/18 03:00:00 don Release ^ * +* * +* compress : compapi.c * +* * +* port by : Donald J. Gloistein * +* * +* Source, Documentation, Object Code: * +* released to Public Domain. This code is based on code as documented * +* below in release notes. * +* * +*--------------------------- Module Description --------------------------* +* Contains source code for modified Lempel-Ziv method (LZW) compression * +* and decompression. * +* * +* This code module can be maintained to keep current on releases on the * +* Unix system. The command shell and dos modules can remain the same. * +* * +*--------------------------- Implementation Notes --------------------------* +* * +* compiled with : compress.h compress.fns compress.c * +* linked with : compress.obj compusi.obj * +* * +* problems: * +* * +* * +* CAUTION: Uses a number of defines for access and speed. If you change * +* anything, make sure about side effects. * +* * +* Compression: * +* Algorithm: use open addressing double hashing (no chaining) on the * +* prefix code / next character combination. We do a variant of Knuth's * +* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime * +* secondary probe. Here, the modular division first probe is gives way * +* to a faster exclusive-or manipulation. * +* Also block compression with an adaptive reset was used in original code, * +* whereby the code table is cleared when the compression ration decreases * +* but after the table fills. This was removed from this edition. The table * +* is re-sized at this point when it is filled , and a special CLEAR code is * +* generated for the decompressor. This results in some size difference from * +* straight version 4.0 joe Release. But it is fully compatible in both v4.0 * +* and v4.01 * +* * +* Decompression: * +* This routine adapts to the codes in the file building the "string" table * +* on-the-fly; requiring no table to be stored in the compressed file. The * +* tables used herein are shared with those of the compress() routine. * +* * +* Initials ---- Name --------------------------------- * +* DjG Donald J. Gloistein, current port to MsDos 16 bit * +* Plus many others, see rev.hst file for full list * +* LvR Lyle V. Rains, many thanks for improved implementation * +* of the compression and decompression routines. * +*************************************************************************@H*/ + +#include + +/* + * LZC state, largely variables with non-local scope. + */ +typedef struct LZCState { + NuArchive* pArchive; + int doCalcCRC; + ushort crc; + + /* compression */ + NuStraw* pStraw; + FILE* outfp; + long uncompRemaining; + + /* expansion */ + FILE* infp; + NuFunnel* pFunnel; + ushort* pCrc; + long compRemaining; + + + /* + * Globals from Compress sources. + */ + int offset; + long int in_count ; /* length of input */ + long int bytes_out; /* length of compressed output */ + + INTCODE prefxcode, nextfree; + INTCODE highcode; + INTCODE maxcode; + HASH hashsize; + int bits; + + char FAR *sfx; + + #if (SPLIT_PFX) + CODE FAR *pfx[2]; + #else + CODE FAR *pfx; + #endif + + #if (SPLIT_HT) + CODE FAR *ht[2]; + #else + CODE FAR *ht; + #endif + + #ifdef COMP40 + long int ratio; + long checkpoint; /* initialized to CHECK_GAP */ + #endif + + #ifdef DEBUG_LZC + int debug; /* initialized to FALSE */ + #endif + + NuError exit_stat; + + int maxbits; /* initialized to DFLTBITS */ + int block_compress; /* initialized to BLOCK_MASK */ + + /* + * Static local variables. Some of these were explicitly initialized + * to zero. + */ + INTCODE oldmaxcode; /* alloc_tables */ + HASH oldhashsize; /* alloc_tables */ + int oldbits; /* putcode */ + UCHAR outbuf[MAXBITS]; /* putcode */ + int prevbits; /* nextcode */ + int size; /* nextcode */ + UCHAR inbuf[MAXBITS]; /* nextcode */ +} LZCState; + + +/* + * The following two parameter tables are the hash table sizes and + * maximum code values for various code bit-lengths. The requirements + * are that Hashsize[n] must be a prime number and Maxcode[n] must be less + * than Maxhash[n]. Table occupancy factor is (Maxcode - 256)/Maxhash. + * Note: I am using a lower Maxcode for 16-bit codes in order to + * keep the hash table size less than 64k entries. + */ +static CONST HASH gNu_hs[] = { + 0x13FF, /* 12-bit codes, 75% occupancy */ + 0x26C3, /* 13-bit codes, 80% occupancy */ + 0x4A1D, /* 14-bit codes, 85% occupancy */ + 0x8D0D, /* 15-bit codes, 90% occupancy */ + 0xFFD9 /* 16-bit codes, 94% occupancy, 6% of code values unused */ +}; +#define Hashsize(maxb) (gNu_hs[(maxb) -MINBITS]) + +static CONST INTCODE gNu_mc[] = { + 0x0FFF, /* 12-bit codes */ + 0x1FFF, /* 13-bit codes */ + 0x3FFF, /* 14-bit codes */ + 0x7FFF, /* 15-bit codes */ + 0xEFFF /* 16-bit codes, 6% of code values unused */ +}; +#define Maxcode(maxb) (gNu_mc[(maxb) -MINBITS]) + +#ifdef __STDC__ +#ifdef DEBUG_LZC +#define allocx(type, ptr, size) \ + (((ptr) = (type FAR *) Nu_LZC_emalloc(pArchive, (unsigned int)(size),sizeof(type))) == NULLPTR(type) \ + ? (DBUG(("%s: "#ptr" -- ", "LZC")), NOMEM) : OK \ + ) +#else +#define allocx(type,ptr,size) \ + (((ptr) = (type FAR *) Nu_LZC_emalloc(pArchive, (unsigned int)(size),sizeof(type))) == NULLPTR(type) \ + ? NOMEM : OK \ + ) +#endif +#else +#define allocx(type,ptr,size) \ + (((ptr) = (type FAR *) Nu_LZC_emalloc(pArchive, (unsigned int)(size),sizeof(type))) == NULLPTR(type) \ + ? NOMEM : OK \ + ) +#endif + +#define free_array(type,ptr,offset) \ + if (ptr != NULLPTR(type)) { \ + Nu_LZC_efree(pArchive, (ALLOCTYPE FAR *)((ptr) + (offset))); \ + (ptr) = NULLPTR(type); \ + } + + /* + * Macro to allocate new memory to a pointer with an offset value. + */ +#define alloc_array(type, ptr, size, offset) \ + ( allocx(type, ptr, (size) - (offset)) != OK \ + ? NOMEM \ + : (((ptr) -= (offset)), OK) \ + ) + +/*static char FAR *sfx = NULLPTR(char) ;*/ +#define suffix(code) pLzcState->sfx[code] + + +#if (SPLIT_PFX) + /*static CODE FAR *pfx[2] = {NULLPTR(CODE), NULLPTR(CODE)};*/ +#else + /*static CODE FAR *pfx = NULLPTR(CODE);*/ +#endif + + +#if (SPLIT_HT) + /*static CODE FAR *ht[2] = {NULLPTR(CODE),NULLPTR(CODE)};*/ +#else + /*static CODE FAR *ht = NULLPTR(CODE);*/ +#endif + + +static int +Nu_LZC_alloc_tables(LZCState* pLzcState, INTCODE newmaxcode, HASH newhashsize) +{ + NuArchive* pArchive = pLzcState->pArchive; + /*static INTCODE oldmaxcode = 0;*/ + /*static HASH oldhashsize = 0;*/ + + if (newhashsize > pLzcState->oldhashsize) { +#if (SPLIT_HT) + free_array(CODE,pLzcState->ht[1], 0); + free_array(CODE,pLzcState->ht[0], 0); +#else + free_array(CODE,pLzcState->ht, 0); +#endif + pLzcState->oldhashsize = 0; + } + + if (newmaxcode > pLzcState->oldmaxcode) { +#if (SPLIT_PFX) + free_array(CODE,pLzcState->pfx[1], 128); + free_array(CODE,pLzcState->pfx[0], 128); +#else + free_array(CODE,pLzcState->pfx, 256); +#endif + free_array(char,pLzcState->sfx, 256); + + if ( alloc_array(char, pLzcState->sfx, newmaxcode + 1, 256) +#if (SPLIT_PFX) + || alloc_array(CODE, pLzcState->pfx[0], (newmaxcode + 1) / 2, 128) + || alloc_array(CODE, pLzcState->pfx[1], (newmaxcode + 1) / 2, 128) +#else + || alloc_array(CODE, pLzcState->pfx, (newmaxcode + 1), 256) +#endif + ) { + pLzcState->oldmaxcode = 0; + pLzcState->exit_stat = NOMEM; + return(NOMEM); + } + pLzcState->oldmaxcode = newmaxcode; + } + if (newhashsize > pLzcState->oldhashsize) { + if ( +#if (SPLIT_HT) + alloc_array(CODE, pLzcState->ht[0], (newhashsize / 2) + 1, 0) + || alloc_array(CODE, pLzcState->ht[1], newhashsize / 2, 0) +#else + alloc_array(CODE, pLzcState->ht, newhashsize, 0) +#endif + ) { + pLzcState->oldhashsize = 0; + pLzcState->exit_stat = NOMEM; + return(NOMEM); + } + pLzcState->oldhashsize = newhashsize; + } + return (OK); +} + +# if (SPLIT_PFX) + /* + * We have to split pfx[] table in half, + * because it's potentially larger than 64k bytes. + */ +# define prefix(code) (pLzcState->pfx[(code) & 1][(code) >> 1]) +# else + /* + * Then pfx[] can't be larger than 64k bytes, + * or we don't care if it is, so we don't split. + */ +# define prefix(code) (pLzcState->pfx[code]) +# endif + + +/* The initializing of the tables can be done quicker with memset() */ +/* but this way is portable through out the memory models. */ +/* If you use Microsoft halloc() to allocate the arrays, then */ +/* include the pragma #pragma function(memset) and make sure that */ +/* the length of the memory block is not greater than 64K. */ +/* This also means that you MUST compile in a model that makes the */ +/* default pointers to be far pointers (compact or large models). */ +/* See the file COMPUSI.DOS to modify function emalloc(). */ + +# if (SPLIT_HT) + /* + * We have to split ht[] hash table in half, + * because it's potentially larger than 64k bytes. + */ +# define probe(hash) (pLzcState->ht[(hash) & 1][(hash) >> 1]) +# define init_tables() \ + { \ + hash = pLzcState->hashsize >> 1; \ + pLzcState->ht[0][hash] = 0; \ + while (hash--) pLzcState->ht[0][hash] = pLzcState->ht[1][hash] = 0; \ + pLzcState->highcode = ~(~(INTCODE)0 << (pLzcState->bits = INITBITS)); \ + pLzcState->nextfree = (pLzcState->block_compress ? FIRSTFREE : 256); \ + } + +# else + + /* + * Then ht[] can't be larger than 64k bytes, + * or we don't care if it is, so we don't split. + */ +# define probe(hash) (pLzcState->ht[hash]) +# define init_tables() \ + { \ + hash = pLzcState->hashsize; \ + while (hash--) pLzcState->ht[hash] = 0; \ + pLzcState->highcode = ~(~(INTCODE)0 << (pLzcState->bits = INITBITS)); \ + pLzcState->nextfree = (pLzcState->block_compress ? FIRSTFREE : 256); \ + } + +# endif + + +/* + * =========================================================================== + * Compression + * =========================================================================== + */ + +static void +Nu_prratio(long int num, long int den) +{ + register int q; /* Doesn't need to be long */ + + if(num > 214748L) { /* 2147483647/10000 */ + q = (int) (num / (den / 10000L)); + } + else { + q = (int) (10000L * num / den); /* Long calculations, though */ + } + if (q < 0) { + DBUG(("-")); + q = -q; + } + DBUG(("%d.%02d%%", q / 100, q % 100)); +} + +#ifdef COMP40 +/* table clear for block compress */ +/* this is for adaptive reset present in version 4.0 joe release */ +/* DjG, sets it up and returns TRUE to compress and FALSE to not compress */ +static int +Nu_LZC_cl_block(LZCState* pLzcState) +{ + register long int rat; + + pLzcState->checkpoint = pLzcState->in_count + CHECK_GAP; +#ifdef DEBUG_LZC + if ( pLzcState->debug ) { + DBUG(( "count: %ld, ratio: ", pLzcState->in_count )); + Nu_prratio ( pLzcState->in_count, pLzcState->bytes_out ); + DBUG(( "\n")); + } +#endif + + if(pLzcState->in_count > 0x007fffff) { /* shift will overflow */ + rat = pLzcState->bytes_out >> 8; + if(rat == 0) /* Don't divide by zero */ + rat = 0x7fffffff; + else + rat = pLzcState->in_count / rat; + } + else + rat = (pLzcState->in_count << 8) / pLzcState->bytes_out; /* 8 fractional bits */ + + if ( rat > pLzcState->ratio ){ + pLzcState->ratio = rat; + return FALSE; + } + else { + pLzcState->ratio = 0; +#ifdef DEBUG_LZC + if(pLzcState->debug) { + DBUG(( "clear\n" )); + } +#endif + return TRUE; /* clear the table */ + } + return FALSE; /* don't clear the table */ +} +#endif + +static CONST UCHAR gNu_rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static void +Nu_LZC_putcode(LZCState* pLzcState, INTCODE code, register int bits) +{ + /*static int oldbits = 0;*/ + /*static UCHAR outbuf[MAXBITS];*/ + register UCHAR *buf; + register int shift; + + if (bits != pLzcState->oldbits) { + if (bits == 0) { + /* bits == 0 means EOF, write the rest of the buffer. */ + if (pLzcState->offset > 0) { + fwrite(pLzcState->outbuf,1,(pLzcState->offset +7) >> 3, pLzcState->outfp); + pLzcState->bytes_out += ((pLzcState->offset +7) >> 3); + } + pLzcState->offset = 0; + pLzcState->oldbits = 0; + fflush(pLzcState->outfp); + return; + } + else { + /* Change the code size. We must write the whole buffer, + * because the expand side won't discover the size change + * until after it has read a buffer full. + */ + if (pLzcState->offset > 0) { + fwrite(pLzcState->outbuf, 1, pLzcState->oldbits, pLzcState->outfp); + pLzcState->bytes_out += pLzcState->oldbits; + pLzcState->offset = 0; + } + pLzcState->oldbits = bits; + #ifdef DEBUG_LZC + if ( pLzcState->debug ) { + DBUG(( "\nChange to %d bits\n", bits )); + } + #endif /* DEBUG_LZC */ + } + } + /* Get to the first byte. */ + buf = pLzcState->outbuf + ((shift = pLzcState->offset) >> 3); + if ((shift &= 7) != 0) { + *(buf) |= (*buf & gNu_rmask[shift]) | (UCHAR)(code << shift); + *(++buf) = (UCHAR)(code >> (8 - shift)); + if (bits + shift > 16) + *(++buf) = (UCHAR)(code >> (16 - shift)); + } + else { + /* Special case for fast execution */ + *(buf) = (UCHAR)code; + *(++buf) = (UCHAR)(code >> 8); + } + if ((pLzcState->offset += bits) == (bits << 3)) { + pLzcState->bytes_out += bits; + fwrite(pLzcState->outbuf,1,bits,pLzcState->outfp); + pLzcState->offset = 0; + } + return; +} + + +#define kNuLZCEOF (-1) + +/* + * Get the next byte from the input straw. Also updates the CRC + * if "doCalcCRC" is set to true. + * + * Returns kNuLZCEOF as the value when we're out of data. + */ +static NuError +Nu_LZCGetcCRC(LZCState* pLzcState, int* pSym) +{ + NuError err; + uchar c; + + if (!pLzcState->uncompRemaining) { + *pSym = kNuLZCEOF; + return kNuErrNone; + } + + err = Nu_StrawRead(pLzcState->pArchive, pLzcState->pStraw, &c, 1); + if (err == kNuErrNone) { + if (pLzcState->doCalcCRC) + pLzcState->crc = Nu_CalcCRC16(pLzcState->crc, &c, 1); + *pSym = c; + pLzcState->uncompRemaining--; + } + + return err; +} + +/* + * compress stdin to stdout + */ +static void +Nu_LZC_compress(LZCState* pLzcState, ulong* pDstLen) +{ + int c,adjbits; + register HASH hash; + register INTCODE code; + HASH hashf[256]; + + Assert(pLzcState->outfp != nil); + + pLzcState->maxcode = Maxcode(pLzcState->maxbits); + pLzcState->hashsize = Hashsize(pLzcState->maxbits); + +#ifdef COMP40 +/* Only needed for adaptive reset */ + pLzcState->checkpoint = CHECK_GAP; + pLzcState->ratio = 0; +#endif + + adjbits = pLzcState->maxbits -10; + for (c = 256; --c >= 0; ){ + hashf[c] = ((( c &0x7) << 7) ^ c) << adjbits; + } + pLzcState->exit_stat = OK; + if (Nu_LZC_alloc_tables(pLzcState, pLzcState->maxcode, pLzcState->hashsize)) /* exit_stat already set */ + return; + init_tables(); + + #if 0 + /* if not zcat or filter */ + if(is_list && !zcat_flg) { /* Open output file */ + if (freopen(ofname, WRITE_FILE_TYPE, pLzcState->outfp) == NULL) { + pLzcState->exit_stat = NOTOPENED; + return; + } + if (!quiet) + fprintf(stderr, "%s: ",ifname); /*#if 0*/ + setvbuf(Xstdout,zbuf,_IOFBF,ZBUFSIZE); + } + #endif + + /* + * Check the input stream for previously seen strings. We keep + * adding characters to the previously seen prefix string until we + * get a character which forms a new (unseen) string. We then send + * the code for the previously seen prefix string, and add the new + * string to our tables. The check for previous strings is done by + * hashing. If the code for the hash value is unused, then we have + * a new string. If the code is used, we check to see if the prefix + * and suffix values match the current input; if so, we have found + * a previously seen string. Otherwise, we have a hash collision, + * and we try secondary hash probes until we either find the current + * string, or we find an unused entry (which indicates a new string). + */ + if (1 /*!nomagic*/) { + putc(gNu_magic_header[0], pLzcState->outfp); + putc(gNu_magic_header[1], pLzcState->outfp); + putc((char)(pLzcState->maxbits | pLzcState->block_compress), pLzcState->outfp); + if(ferror(pLzcState->outfp)){ /* check it on entry */ + pLzcState->exit_stat = WRITEERR; + return; + } + pLzcState->bytes_out = 3L; /* includes 3-byte header mojo */ + } + else + pLzcState->bytes_out = 0L; /* no 3-byte header mojo */ + pLzcState->in_count = 1L; + pLzcState->offset = 0; + + pLzcState->exit_stat = Nu_LZCGetcCRC(pLzcState, &c); + if (pLzcState->exit_stat != kNuErrNone) + return; + pLzcState->prefxcode = (INTCODE)c; + + while (1) { + pLzcState->exit_stat = Nu_LZCGetcCRC(pLzcState, &c); + if (pLzcState->exit_stat != kNuErrNone) + return; + if (c == kNuLZCEOF) + break; + + pLzcState->in_count++; + hash = pLzcState->prefxcode ^ hashf[c]; + /* I need to check that my hash value is within range + * because my 16-bit hash table is smaller than 64k. + */ + if (hash >= pLzcState->hashsize) + hash -= pLzcState->hashsize; + if ((code = (INTCODE)probe(hash)) != UNUSED) { + if (suffix(code) != (char)c || (INTCODE)prefix(code) != pLzcState->prefxcode) { + /* hashdelta is subtracted from hash on each iteration of + * the following hash table search loop. I compute it once + * here to remove it from the loop. + */ + HASH hashdelta = (0x120 - c) << (adjbits); + do { + /* rehash and keep looking */ + Assert(code >= FIRSTFREE && code <= pLzcState->maxcode); + if (hash >= hashdelta) hash -= hashdelta; + else hash += (pLzcState->hashsize - hashdelta); + Assert(hash < pLzcState->hashsize); + if ((code = (INTCODE)probe(hash)) == UNUSED) + goto newcode; + } while (suffix(code) != (char)c || (INTCODE)prefix(code) != pLzcState->prefxcode); + } + pLzcState->prefxcode = code; + } + else { + newcode: { + Nu_LZC_putcode(pLzcState, pLzcState->prefxcode, pLzcState->bits); + code = pLzcState->nextfree; + Assert(hash < pLzcState->hashsize); + Assert(code >= FIRSTFREE); + Assert(code <= pLzcState->maxcode + 1); + if (code <= pLzcState->maxcode) { + probe(hash) = (CODE)code; + prefix(code) = (CODE)pLzcState->prefxcode; + suffix(code) = (char)c; + if (code > pLzcState->highcode) { + pLzcState->highcode += code; + ++pLzcState->bits; + } + pLzcState->nextfree = code + 1; + } +#ifdef COMP40 + else if (pLzcState->in_count >= pLzcState->checkpoint && pLzcState->block_compress ) { + if (Nu_LZC_cl_block(pLzcState)){ +#else + else if (pLzcState->block_compress){ +#endif + Nu_LZC_putcode(pLzcState, (INTCODE)c, pLzcState->bits); + Nu_LZC_putcode(pLzcState, CLEAR, pLzcState->bits); + init_tables(); + pLzcState->exit_stat = Nu_LZCGetcCRC(pLzcState, &c); + if (pLzcState->exit_stat != kNuErrNone) + return; + if (c == kNuLZCEOF) + break; + pLzcState->in_count++; +#ifdef COMP40 + } +#endif + } + pLzcState->prefxcode = (INTCODE)c; + } + } + } + Nu_LZC_putcode(pLzcState, pLzcState->prefxcode, pLzcState->bits); + Nu_LZC_putcode(pLzcState, CLEAR, 0); + /* + * Print out stats on stderr + */ + if(1 /*zcat_flg == 0 && !quiet*/) { +#ifdef DEBUG_LZC + DBUG(( + "%ld chars in, (%ld bytes) out, compression factor: ", + pLzcState->in_count, pLzcState->bytes_out )); + Nu_prratio( pLzcState->in_count, pLzcState->bytes_out ); + DBUG(( "\n")); + DBUG(( "\tCompression as in compact: " )); + Nu_prratio( pLzcState->in_count-pLzcState->bytes_out, pLzcState->in_count ); + DBUG(( "\n")); + DBUG(( "\tLargest code (of last block) was %d (%d bits)\n", + pLzcState->prefxcode - 1, pLzcState->bits )); +#else + DBUG(( "Compression: " )); + Nu_prratio( pLzcState->in_count-pLzcState->bytes_out, pLzcState->in_count ); +#endif /* DEBUG_LZC */ + } + if(pLzcState->bytes_out > pLzcState->in_count) /* if no savings */ + pLzcState->exit_stat = NOSAVING; + *pDstLen = pLzcState->bytes_out; + return ; +} + + +/* + * NufxLib interface to LZC compression. + */ +static NuError +Nu_CompressLZC(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc, int maxbits) +{ + NuError err = kNuErrNone; + LZCState lzcState; + + memset(&lzcState, 0, sizeof(lzcState)); + lzcState.pArchive = pArchive; + lzcState.pStraw = pStraw; + lzcState.outfp = fp; + lzcState.uncompRemaining = srcLen; + + if (pCrc == nil) { + lzcState.doCalcCRC = false; + } else { + lzcState.doCalcCRC = true; + lzcState.crc = *pCrc; + } + + lzcState.maxbits = maxbits; + lzcState.block_compress = BLOCK_MASK; /* enabled */ + + Nu_LZC_compress(&lzcState, pDstLen); + err = lzcState.exit_stat; + DBUG(("+++ LZC_compress returned with %d\n", err)); + +#if (SPLIT_HT) + free_array(CODE,lzcState.ht[1], 0); + free_array(CODE,lzcState.ht[0], 0); +#else + free_array(CODE,lzcState.ht, 0); +#endif + +#if (SPLIT_PFX) + free_array(CODE,lzcState.pfx[1], 128); + free_array(CODE,lzcState.pfx[0], 128); +#else + free_array(CODE,lzcState.pfx, 256); +#endif + free_array(char,lzcState.sfx, 256); + + if (pCrc != nil) + *pCrc = lzcState.crc; + + return err; +} + +NuError +Nu_CompressLZC12(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + return Nu_CompressLZC(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, 12); +} + +NuError +Nu_CompressLZC16(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + return Nu_CompressLZC(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, 16); +} + + +/* + * =========================================================================== + * Expansion + * =========================================================================== + */ + +/* + * Write the next byte to the output funnel. Also updates the CRC + * if "doCalcCRC" is set to true. + * + * Returns kNuLZCEOF as the value when we're out of data. + */ +static NuError +Nu_LZCPutcCRC(LZCState* pLzcState, char c) +{ + NuError err; + + err = Nu_FunnelWrite(pLzcState->pArchive, pLzcState->pFunnel, + (uchar*) &c, 1); + if (pLzcState->doCalcCRC) + pLzcState->crc = Nu_CalcCRC16(pLzcState->crc, (uchar*) &c, 1); + + return err; +} + + +static int +Nu_LZC_nextcode(LZCState* pLzcState, INTCODE* codeptr) +/* Get the next code from input and put it in *codeptr. + * Return (TRUE) on success, or return (FALSE) on end-of-file. + * Adapted from COMPRESS V4.0. + */ +{ + /*static int prevbits = 0;*/ + register INTCODE code; + /*static int size;*/ + /*static UCHAR inbuf[MAXBITS];*/ + register int shift; + UCHAR *bp; + + /* If the next entry is a different bit-size than the preceeding one + * then we must adjust the size and scrap the old buffer. + */ + if (pLzcState->prevbits != pLzcState->bits) { + pLzcState->prevbits = pLzcState->bits; + pLzcState->size = 0; + } + /* If we can't read another code from the buffer, then refill it. + */ + shift = pLzcState->offset; + if (pLzcState->size - shift < pLzcState->bits) { + /* Read more input and convert size from # of bytes to # of bits */ + long getSize; + + getSize = pLzcState->bits; + if (getSize > pLzcState->compRemaining) + getSize = pLzcState->compRemaining; + if (!getSize) /* act like EOF */ + return FALSE; + pLzcState->size = fread(pLzcState->inbuf, 1, getSize, pLzcState->infp) << 3; + if (pLzcState->size <= 0 || ferror(pLzcState->infp)) + return(FALSE); + pLzcState->compRemaining -= getSize; + pLzcState->offset = shift = 0; + } + /* Get to the first byte. */ + bp = pLzcState->inbuf + (shift >> 3); + /* Get first part (low order bits) */ + code = (*bp++ >> (shift &= 7)); + /* high order bits. */ + code |= *bp++ << (shift = 8 - shift); + if ((shift += 8) < pLzcState->bits) code |= *bp << shift; + *codeptr = code & pLzcState->highcode; + pLzcState->offset += pLzcState->bits; + return (TRUE); +} + +static void +Nu_LZC_decompress(LZCState* pLzcState, ulong compressedLen) +{ + NuArchive* pArchive = pLzcState->pArchive; + register int i; + register INTCODE code; + char sufxchar = 0; + INTCODE savecode; + FLAG fulltable = FALSE, cleartable; + /*static*/ char *token= NULL; /* String buffer to build token */ + /*static*/ int maxtoklen = MAXTOKLEN; + int flags; + + Assert(pLzcState->infp != nil); + + pLzcState->exit_stat = OK; + + if (compressedLen < 3) { + /* not long enough to be valid! */ + pLzcState->exit_stat = kNuErrBadData; + Nu_ReportError(NU_BLOB, pLzcState->exit_stat, "thread too short to be valid LZC"); + return; + } + pLzcState->compRemaining = compressedLen; + + /* + * This comes out of "compress.c" rather than "compapi.c". + */ + if ((getc(pLzcState->infp)!=(gNu_magic_header[0] & 0xFF)) + || (getc(pLzcState->infp)!=(gNu_magic_header[1] & 0xFF))) + { + DBUG(("not in compressed format\n")); + pLzcState->exit_stat = kNuErrBadData; + return; + } + flags = getc(pLzcState->infp); /* set -b from file */ + pLzcState->block_compress = flags & BLOCK_MASK; + pLzcState->maxbits = flags & BIT_MASK; + if(pLzcState->maxbits > MAXBITS) { + DBUG(("compressed with %d bits, can only handle %d bits\n", + pLzcState->maxbits, MAXBITS)); + pLzcState->exit_stat = kNuErrBadData; + return; + } + + pLzcState->compRemaining -= 3; + + /* Initialze the token buffer. */ + token = (char*)Nu_Malloc(pArchive, maxtoklen); + if (token == NULL) { + pLzcState->exit_stat = NOMEM; + return; + } + + if (Nu_LZC_alloc_tables(pLzcState, pLzcState->maxcode = ~(~(INTCODE)0 << pLzcState->maxbits),0)) /* exit_stat already set */ + return; + + #if 0 + /* if not zcat or filter */ + if(is_list && !zcat_flg) { /* Open output file */ + if (freopen(ofname, WRITE_FILE_TYPE, stdout) == NULL) { + pLzcState->exit_stat = NOTOPENED; + return; + } + if (!quiet) + fprintf(stderr, "%s: ",ifname); /*#if 0*/ + setvbuf(stdout,xbuf,_IOFBF,XBUFSIZE); + } + #endif + + cleartable = TRUE; + savecode = CLEAR; + pLzcState->offset = 0; + do { + if ((code = savecode) == CLEAR && cleartable) { + pLzcState->highcode = ~(~(INTCODE)0 << (pLzcState->bits = INITBITS)); + fulltable = FALSE; + pLzcState->nextfree = (cleartable = pLzcState->block_compress) == FALSE ? 256 : FIRSTFREE; + if (!Nu_LZC_nextcode(pLzcState, &pLzcState->prefxcode)) + break; + /*putc((*/sufxchar = (char)pLzcState->prefxcode/*), stdout)*/; + pLzcState->exit_stat = Nu_LZCPutcCRC(pLzcState, sufxchar); + if (pLzcState->exit_stat != kNuErrNone) + return; + continue; + } + i = 0; + if (code >= pLzcState->nextfree && !fulltable) { + if (code != pLzcState->nextfree){ + DBUG(("ERROR: code (0x%x) != nextfree (0x%x)\n", + code, pLzcState->nextfree)); + pLzcState->exit_stat = CODEBAD; + return ; /* Non-existant code */ + } + /* Special case for sequence KwKwK (see text of article) */ + code = pLzcState->prefxcode; + token[i++] = sufxchar; + } + /* Build the token string in reverse order by chasing down through + * successive prefix tokens of the current token. Then output it. + */ + while (code >= 256) { + #ifdef DEBUG_LZC + /* These are checks to ease paranoia. Prefix codes must decrease + * monotonically, otherwise we must have corrupt tables. We can + * also check that we haven't overrun the token buffer. + */ + if (code <= (INTCODE)prefix(code)){ + pLzcState->exit_stat= TABLEBAD; + return; + } + #endif + if (i >= maxtoklen) { + maxtoklen *= 2; /* double the size of the token buffer */ + if ((token = Nu_Realloc(pArchive, token, maxtoklen)) == NULL) { + pLzcState->exit_stat = TOKTOOBIG; + return; + } + } + token[i++] = suffix(code); + code = (INTCODE)prefix(code); + } + /*putc(*/sufxchar = (char)code/*, stdout)*/; + pLzcState->exit_stat = Nu_LZCPutcCRC(pLzcState, sufxchar); + while (--i >= 0) { + /*putc(token[i], stdout);*/ + pLzcState->exit_stat = Nu_LZCPutcCRC(pLzcState, token[i]); + } + if (pLzcState->exit_stat != kNuErrNone) + return; + /* If table isn't full, add new token code to the table with + * codeprefix and codesuffix, and remember current code. + */ + if (!fulltable) { + code = pLzcState->nextfree; + Assert(256 <= code && code <= pLzcState->maxcode); + prefix(code) = (CODE)pLzcState->prefxcode; + suffix(code) = sufxchar; + pLzcState->prefxcode = savecode; + if (code++ == pLzcState->highcode) { + if (pLzcState->highcode >= pLzcState->maxcode) { + fulltable = TRUE; + --code; + } + else { + ++pLzcState->bits; + pLzcState->highcode += code; /* nextfree == highcode + 1 */ + } + } + pLzcState->nextfree = code; + } + } while (Nu_LZC_nextcode(pLzcState, &savecode)); + pLzcState->exit_stat = (ferror(pLzcState->infp))? READERR : OK; + + Nu_Free(pArchive, token); + return ; +} + + +/* + * NufxLib interface to LZC expansion. + */ +NuError +Nu_ExpandLZC(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc) +{ + NuError err = kNuErrNone; + LZCState lzcState; + + memset(&lzcState, 0, sizeof(lzcState)); + lzcState.pArchive = pArchive; + lzcState.infp = infp; + lzcState.pFunnel = pFunnel; + + if (pCrc == nil) { + lzcState.doCalcCRC = false; + } else { + lzcState.doCalcCRC = true; + lzcState.crc = *pCrc; + } + + Nu_LZC_decompress(&lzcState, pThread->thCompThreadEOF); + err = lzcState.exit_stat; + DBUG(("+++ LZC_decompress returned with %d\n", err)); + +#if (SPLIT_HT) + free_array(CODE,lzcState.ht[1], 0); + free_array(CODE,lzcState.ht[0], 0); +#else + free_array(CODE,lzcState.ht, 0); +#endif + +#if (SPLIT_PFX) + free_array(CODE,lzcState.pfx[1], 128); + free_array(CODE,lzcState.pfx[0], 128); +#else + free_array(CODE,lzcState.pfx, 256); +#endif + free_array(char,lzcState.sfx, 256); + + if (pCrc != nil) + *pCrc = lzcState.crc; + return err; +} + +#endif /*ENABLE_LZC*/ diff --git a/nufxlib/Lzw.c b/nufxlib/Lzw.c new file mode 100644 index 0000000..14934a9 --- /dev/null +++ b/nufxlib/Lzw.c @@ -0,0 +1,1635 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * ShrinkIt LZW functions. The original code was developed by Kent Dickey + * and Andy Nicholas. + * + * Unisys holds US patent #4,558,302 (filed June 20, 1983 and issued December + * 10, 1985). A policy set in 1995 specifies the lifetime of a patent as + * the longer of 20 years from the date of application or 17 years from the + * date of grant, so the Unisys LZW patent expired on June 20, 2003 in the + * USA. Patents in some other countries expire after July 7, 2004. + * + * An older note: + * + * The Unisys patent is one of many that covers LZW compression, but Unisys + * is the only company actively attacking anyone who uses it. The statement + * Unisys made regarding LZW (and, specifically, GIF and TIFF-LZW) says: + * + * Q: I use LZW in my programs, but not for GIF or TIFF graphics. What should + * I do? + * A: If you are not a business, and the programs are for your own personal + * non-commercial or not-for-profit use, Unisys does not require you to + * obtain a license. If they are used as part of a business and/or you sell + * the programs for commercial or for-profit purposes, then you must contact + * the Welch Patent Licensing Department at Unisys and explain your + * circumstances. They will have a license agreement for your application of + * their LZW algorithm. + * + * According to this, the use of LZW in NufxLib has never required a license. + */ +#include "NufxLibPriv.h" + +#ifdef ENABLE_LZW + +/* the LZW algorithms operate on 4K chunks */ +#define kNuLZWBlockSize 4096 + +/* a little padding to avoid mysterious crashes on bad data */ +#define kNuSafetyPadding 64 + +#define kNuLZWClearCode 0x0100 +#define kNuLZWFirstCode 0x0101 + + +/* sometimes we want to get *really* verbose rather late in a large archive */ +#ifdef DEBUG_LZW + static Boolean gNuDebugVerbose = true; + #define DBUG_LZW(x) { if (gNuDebugVerbose) { DBUG(x); } } +#else + #define DBUG_LZW ((void)0) +#endif + + +/* + * =========================================================================== + * Compression + * =========================================================================== + */ + +/* + * We use a hash function borrowed from UNIX compress, which is described + * in the v4.3 sources as: + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. + * + * The function used to generate it is: + * + * int c, hashf[256]; + * for (c = 256; --c >= 0; ) { + * hashf[c] = (((c & 0x7) << 7) ^ c) << (maxbits-10); + * } + * + * It is used with: + * + * hash = prefixcode ^ hashf[c]; \* c is char from getchar() *\ + * + * The value for kNuLZWHashSize determines the size of the hash table and + * the % occupancy. We want a fair number of vacancies because we probe + * when we collide. Using 5119 (0x13ff) with 12-bit codes yields 75% + * occupancy. + */ + +#define kNuLZWHashSize 5119 /* must be prime */ +#define kNuLZWEntryUnused 0 /* indicates an unused hash entry */ +#define kNuLZWHashFuncTblSize 256 /* one entry per char value */ +#define kNuLZWDefaultVol 0xfe /* use this as volume number */ +#define kNuLZWHashDelta 0x120 /* used in secondary hashing */ +#define kNuLZWMinCode kNuLZWClearCode /* smallest 12-bit LZW code */ +#define kNuLZWMaxCode 0x0fff /* largest 12-bit LZW code */ +#define kNuLZW2StopCode 0x0ffd /* LZW/2 stops here */ + +/* + * Mask of bits, from 0 to 8. + */ +static const int gNuBitMask[] = { + 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff +}; + +#define kNuRLEDefaultEscape 0xdb /* ShrinkIt standard */ + +/* + * This holds all of the "big" dynamic state, plus a few things that I + * don't want to pass around. It's allocated once for each instance of + * an open archive, and re-used. + * + * The hash table consists of three parts. We have a choice for some of + * them, "ushort" or "uint". With "ushort" it uses less memory and is + * more likely to fit in a CPU cache, but on some processors you have to + * add instructions to manipulate 16-bit values in a 32-bit word. I'm + * guessing "ushort" is better overall. + */ +typedef struct LZWCompressState { + NuArchive* pArchive; + + ushort entry[kNuLZWHashSize]; /* uint or ushort */ + ushort prefix[kNuLZWMaxCode+1]; /* uint or ushort */ + uchar suffix[kNuLZWMaxCode+1]; + + ushort hashFunc[kNuLZWHashFuncTblSize]; /* uint or ushort */ + + uchar inputBuf[kNuLZWBlockSize]; /* 4K of raw input */ + uchar rleBuf[kNuLZWBlockSize*2 + kNuSafetyPadding]; + uchar lzwBuf[(kNuLZWBlockSize * 3) / 2 + kNuSafetyPadding]; + + ushort chunkCrc; /* CRC for LZW/1 */ + + /* LZW/2 state variables */ + int nextFree; + int codeBits; + int highCode; + Boolean initialClear; +} LZWCompressState; + + +/* + * Allocate some "reusable" state for LZW compression. + * + * The only thing that really needs to be retained across calls is + * the hash function. This way we don't have to re-create it for + * every file, or store it statically in the binary. + */ +static NuError +Nu_AllocLZWCompressState(NuArchive* pArchive) +{ + NuError err; + LZWCompressState* lzwState; + int ic; + + Assert(pArchive != nil); + Assert(pArchive->lzwCompressState == nil); + + /* allocate the general-purpose compression buffer, if needed */ + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + pArchive->lzwCompressState = Nu_Malloc(pArchive, sizeof(LZWCompressState)); + if (pArchive->lzwCompressState == nil) + return kNuErrMalloc; + + /* + * The "hashFunc" table only needs to be set up once. + */ + lzwState = pArchive->lzwCompressState; + for (ic = 256; --ic >= 0; ) + lzwState->hashFunc[ic] = (((ic & 0x7) << 7) ^ ic) << 2; + + return kNuErrNone; +} + + +/* + * Compress a block of input from lzwState->inputBuf to lzwState->rleBuf. + * The size of the output is returned in "*pRLESize" (will be zero if the + * block expanded instead of compressing). + * + * The maximum possible size of the output is 2x the original, which can + * only occur if the input is an alternating sequence of RLE delimiters + * and non-delimiters. It requires 3 bytes to encode a solitary 0xdb, + * so you get (4096 / 2) non-delimiters plus (4096 / 2) * 3 RLE-encoded + * delimiters. We deal with this by using an 8K output buffer, so we + * don't have to watch for overflow in the inner loop. + * + * The RLE format is " ", where count is zero-based + * (i.e. for three bytes we encode "2", allowing us to express 1-256). + */ +static NuError +Nu_CompressBlockRLE(LZWCompressState* lzwState, int* pRLESize) +{ + const uchar* inPtr = lzwState->inputBuf; + const uchar* endPtr = inPtr + kNuLZWBlockSize; + uchar* outPtr = lzwState->rleBuf; + uchar matchChar; + int matchCount; + + while (inPtr < endPtr) { + matchChar = *inPtr; + matchCount = 1; + + /* count up the matching chars */ + while (*++inPtr == matchChar && inPtr < endPtr) + matchCount++; + + if (matchCount > 3) { + if (matchCount > 256) { + /* rare case - really long match */ + while (matchCount > 256) { + *outPtr++ = kNuRLEDefaultEscape; + *outPtr++ = matchChar; + *outPtr++ = 255; + matchCount -= 256; + } + + /* take care of the odd bits -- which might not form a run! */ + if (matchCount > 3) { + *outPtr++ = kNuRLEDefaultEscape; + *outPtr++ = matchChar; + *outPtr++ = matchCount -1; + } else { + while (matchCount--) + *outPtr++ = matchChar; + } + + } else { + /* common case */ + *outPtr++ = kNuRLEDefaultEscape; + *outPtr++ = matchChar; + *outPtr++ = matchCount -1; + } + + } else { + if (matchChar == kNuRLEDefaultEscape) { + /* encode 1-3 0xDBs */ + *outPtr++ = kNuRLEDefaultEscape; + *outPtr++ = kNuRLEDefaultEscape; + *outPtr++ = matchCount -1; + } else { + while (matchCount--) + *outPtr++ = matchChar; + } + } + } + + *pRLESize = outPtr - lzwState->rleBuf; + Assert(*pRLESize > 0 && *pRLESize < sizeof(lzwState->rleBuf)); + + return kNuErrNone; +} + + +/* + * Clear the LZW table. Also resets the LZW/2 state. + */ +static void +Nu_ClearLZWTable(LZWCompressState* lzwState) +{ + Assert(lzwState != nil); + + /*DBUG_LZW(("### clear table\n"));*/ + + /* reset table entries */ + Assert(kNuLZWEntryUnused == 0); /* make sure this is okay */ + memset(lzwState->entry, 0, sizeof(lzwState->entry)); + + /* reset state variables */ + lzwState->nextFree = kNuLZWFirstCode; + lzwState->codeBits = 9; + lzwState->highCode = ~(~0 << lzwState->codeBits); /* a/k/a 0x01ff */ + lzwState->initialClear = false; +} + + +/* + * Write a variable-width LZW code to the output. "prefixCode" has the + * value to write, and "codeBits" is the width. + * + * Data is written in little-endian order (lowest byte first). The + * putcode function in LZC is probably faster, but the format isn't + * compatible with SHK. + * + * The worst conceivable expansion for LZW is 12 bits of output for every + * byte of input. Because we're using variable-width codes and LZW is + * reasonably effective at finding matches, the actual expansion will + * certainly be less. Throwing the extra 2K onto the end of the buffer + * saves us from having to check for a buffer overflow here. + * + * On exit, "*pOutBuf" will point PAST the last byte we wrote (even if + * it's a partial byte), and "*pAtBit" will contain the bit offset. + * + * (Turning this into a macro might speed things up.) + */ +static inline void +Nu_LZWPutCode(uchar** pOutBuf, ulong prefixCode, int codeBits, int* pAtBit) +{ + int atBit = *pAtBit; + uchar* outBuf = *pOutBuf; + + /*DBUG_LZW(("### PUT: prefixCode=0x%04lx, codeBits=%d, atBit=%d\n", + prefixCode, codeBits, atBit));*/ + + Assert(atBit >= 0 && atBit < sizeof(gNuBitMask)); + + if (atBit) { + /* align the prefix code with the existing byte */ + prefixCode <<= atBit; + + /* merge it with the buffer contents (if necessary) and write lo bits */ + outBuf--; + *outBuf = (uchar)((*outBuf & gNuBitMask[atBit]) | prefixCode); + outBuf++; + } else { + /* nothing to merge with; write lo byte at next posn and advance */ + *outBuf++ = (uchar)prefixCode; + } + + /* codes are at least 9 bits, so we know we have to write one more */ + *outBuf++ = (uchar)(prefixCode >> 8); + + /* in some cases, we may have to write yet another */ + atBit += codeBits; + if (atBit > 16) + *outBuf++ = (uchar)(prefixCode >> 16); + + *pAtBit = atBit & 0x07; + *pOutBuf = outBuf; +} + + +/* + * Compress a block of data with LZW, from "inputBuf" to lzwState->lzwBuf. + * + * LZW/1 is just like LZW/2, except that for the former the table is + * always cleared before this function is called. Because of this, the + * table never fills completely, so none of the table-overflow code + * ever happens. + * + * This function is patterned after the LZC compress function, rather + * than the NuLib LZW code, because the NuLib code was abysmal (a rather + * straight translation from 6502 assembly). This function differs from LZC + * in a few areas in order to make the output match GS/ShrinkIt. + * + * There is a (deliberate) minor bug here: if a table clear is emitted + * when there is only one character left in the input, nothing will be + * added to the hash table (as there is nothing to add) but "nextFree" + * will be advanced. This mimics GSHK's behavior, and accounts for the + * "resetFix" logic in the expansion functions. Code 0x0101 is essentially + * lost in this situation. + */ +static NuError +Nu_CompressLZWBlock(LZWCompressState* lzwState, const uchar* inputBuf, + int inputCount, int* pOutputCount) +{ + int nextFree, ic, atBit, codeBits; + int hash, hashDelta; + int prefixCode, code, highCode; + const uchar* inputEnd = inputBuf + inputCount; + /* local copies of lzwState members, for speed */ + const ushort* pHashFunc = lzwState->hashFunc; + ushort* pEntry = lzwState->entry; + ushort* pPrefix = lzwState->prefix; + uchar* pSuffix = lzwState->suffix; + uchar* outBuf = lzwState->lzwBuf; + + Assert(lzwState != nil); + Assert(inputBuf != nil); + Assert(inputCount > 0 && inputCount <= kNuLZWBlockSize); + /* make sure nobody has been messing with the types */ + Assert(sizeof(pHashFunc[0]) == sizeof(lzwState->hashFunc[0])); + Assert(sizeof(pEntry[0]) == sizeof(lzwState->entry[0])); + Assert(sizeof(pPrefix[0]) == sizeof(lzwState->prefix[0])); + Assert(sizeof(pSuffix[0]) == sizeof(lzwState->suffix[0])); + + /*DBUG_LZW(("### START LZW (nextFree=0x%04x)\n", lzwState->nextFree));*/ + + atBit = 0; + + if (lzwState->initialClear) { + /*DBUG_LZW(("### initialClear set\n"));*/ + codeBits = lzwState->codeBits; + Nu_LZWPutCode(&outBuf, kNuLZWClearCode, codeBits, &atBit); + Nu_ClearLZWTable(lzwState); + } + + table_cleared: + /* recover our state (or get newly-cleared state) */ + nextFree = lzwState->nextFree; + codeBits = lzwState->codeBits; + highCode = lzwState->highCode; + + prefixCode = *inputBuf++; + + /*DBUG_LZW(("### fchar=0x%02x\n", prefixCode));*/ + + while (inputBuf < inputEnd) { + ic = *inputBuf++; + /*DBUG_LZW(("### char=0x%02x\n", ic));*/ + + hash = prefixCode ^ pHashFunc[ic]; + code = pEntry[hash]; + + if (code != kNuLZWEntryUnused) { + /* something is here, either our prefix or a hash collision */ + if (pSuffix[code] != ic || pPrefix[code] != prefixCode) { + /* we've collided; do the secondary probe */ + hashDelta = (kNuLZWHashDelta - ic) << 2; + do { + /* rehash and keep looking */ + Assert(code >= kNuLZWMinCode && code <= kNuLZWMaxCode); + if (hash >= hashDelta) + hash -= hashDelta; + else + hash += kNuLZWHashSize - hashDelta; + Assert(hash >= 0 && hash < kNuLZWHashSize); + + if ((code = pEntry[hash]) == kNuLZWEntryUnused) + goto new_code; + } while (pSuffix[code] != ic || pPrefix[code] != prefixCode); + } + + /* else we found a matching string, and can keep searching */ + prefixCode = code; + + } else { + /* found an empty entry, add the prefix+suffix to the table */ + new_code: + Nu_LZWPutCode(&outBuf, prefixCode, codeBits, &atBit); + Assert(outBuf < lzwState->lzwBuf + sizeof(lzwState->lzwBuf)); + /*DBUG_LZW(("### outBuf now at +%d\n",outBuf - lzwState->lzwBuf));*/ + + code = nextFree; + Assert(hash < kNuLZWHashSize); + Assert(code >= kNuLZWMinCode); + Assert(code <= kNuLZWMaxCode); + + /* + * GSHK accepts 0x0ffd, and then sends the table clear + * immediately. We could improve on GSHK's compression slightly + * by using the entire table, but I want to generate the exact + * same output as GSHK. (The decoder believes the table clear + * is entry 0xffe, so we've got one more coming, and possibly + * two if we tweak getcode slightly.) + * + * Experiments show that switching to 0xffe increases the size + * of files that don't compress well, and decreases the size + * of files that do. In both cases, the difference in size + * is very small. + */ + Assert(code <= kNuLZW2StopCode); + /*if (code <= kNuLZW2StopCode) {*/ + /*DBUG_LZW(("### added new code 0x%04x prefix=0x%04x ch=0x%02x\n", + code, prefixCode, ic));*/ + + pEntry[hash] = code; + pPrefix[code] = prefixCode; + pSuffix[code] = ic; + + /* + * Check and see if it's time to increase the code size (note + * we flip earlier than LZC by one here). + */ + if (code >= highCode) { + highCode += code +1; + codeBits++; + } + + nextFree++; + + /*}*/ + + prefixCode = ic; + + /* if the table is full, clear it (only for LZW/2) */ + if (code == kNuLZW2StopCode) { + /* output last code */ + Nu_LZWPutCode(&outBuf, prefixCode, codeBits, &atBit); + + if (inputBuf < inputEnd) { + /* still have data, keep going */ + Nu_LZWPutCode(&outBuf, kNuLZWClearCode, codeBits, &atBit); + Nu_ClearLZWTable(lzwState); + goto table_cleared; + } else { + /* no more input, hold table clear for next block */ + DBUG(("--- RARE: block-end clear\n")); + lzwState->initialClear = true; + goto table_clear_finish; + } + } + + Assert(nextFree <= kNuLZW2StopCode); + } + } + + /* + * Output the last code. Since there's no following character, we don't + * need to add an entry to the table... whatever we've found is already + * in there. + */ + Nu_LZWPutCode(&outBuf, prefixCode, codeBits, &atBit); + + /* + * Update the counters so LZW/2 has continuity. + */ + Assert(nextFree <= kNuLZW2StopCode); + if (nextFree >= highCode) { + highCode += nextFree +1; + codeBits++; + } + nextFree++; /* make room for the code we just wrote */ + + if (nextFree > kNuLZW2StopCode) { + /* + * The code we just wrote, which was part of a longer string already + * in the tree, took the last entry in the table. We need to clear + * the table, but we can't do it in this block. We will have to + * emit a table clear as the very first thing in the next block. + */ + DBUG(("--- RARE: block-end inter clear\n")); + lzwState->initialClear = true; + } + table_clear_finish: + + /* save state for next pass through */ + lzwState->nextFree = nextFree; + lzwState->codeBits = codeBits; + lzwState->highCode = highCode; + + Assert(inputBuf == inputEnd); + + *pOutputCount = outBuf - lzwState->lzwBuf; + + /* + if (*pOutputCount < inputCount) { + DBUG_LZW(("### compressed from %d to %d\n", inputCount, *pOutputCount)); + } else { + DBUG_LZW(("### NO compression (%d to %d)\n", inputCount,*pOutputCount)); + } + */ + + return kNuErrNone; +} + +/* + * Compress ShrinkIt-style "LZW/1" and "LZW/2". + * + * "*pThreadCrc" should already be set to its initial value. On exit it + * will contain the CRC of the uncompressed data. + * + * On exit, the output file will be positioned past the last byte written. + */ +static NuError +Nu_CompressLZW(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pThreadCrc, Boolean isType2) +{ + NuError err = kNuErrNone; + LZWCompressState* lzwState; + long initialOffset; + const uchar* lzwInputBuf; + uint blockSize, rleSize, lzwSize; + long compressedLen; + Boolean keepLzw; + + Assert(pArchive != nil); + Assert(pStraw != nil); + Assert(fp != nil); + Assert(srcLen > 0); + Assert(pDstLen != nil); + Assert(pThreadCrc != nil); + Assert(isType2 == true || isType2 == false); + + /* + * Do some initialization and set-up. + */ + if (pArchive->lzwCompressState == nil) { + err = Nu_AllocLZWCompressState(pArchive); + BailError(err); + } + Assert(pArchive->lzwCompressState != nil); + Assert(pArchive->compBuf != nil); + + lzwState = pArchive->lzwCompressState; + lzwState->pArchive = pArchive; + compressedLen = 0; + + /* + * And now for something ugly: for LZW/1 we have to compute the CRC + * twice. Old versions of ShrinkIt used LZW/1 and put the CRC in + * the compressed block while newer versions used LZW/2 and put the + * CRC in the thread header. We're using LZW/1 with the newer record + * format, so we need two CRCs. For some odd reason Andy N. decided + * to use 0xffff as the initial value for the thread one, so we can't + * just store the same thing in two places. + * + * Of course, this also means that an LZW/2 chunk stored in an old + * pre-v3 record wouldn't have a CRC at all... + * + * LZW/1 is included here for completeness. I can't think of a reason + * why you'd want to use it, really. + */ + lzwState->chunkCrc = kNuInitialChunkCRC; /* 0x0000 */ + + /* + * An LZW/1 file starts off with a CRC of the data, which means we + * have to compress the whole thing, then seek back afterward and + * write the value. This annoyance went away in LZW/2. + */ + err = Nu_FTell(fp, &initialOffset); + BailError(err); + + if (!isType2) { + putc(0, fp); /* leave space for CRC */ + putc(0, fp); + compressedLen += 2; + } + putc(kNuLZWDefaultVol, fp); + putc(kNuRLEDefaultEscape, fp); + compressedLen += 2; + + if (isType2) + Nu_ClearLZWTable(lzwState); + + while (srcLen) { + /* + * Fill up the input buffer. + */ + blockSize = (srcLen > kNuLZWBlockSize) ? kNuLZWBlockSize : srcLen; + + err = Nu_StrawRead(pArchive, pStraw, lzwState->inputBuf, blockSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "compression read failed"); + goto bail; + } + + /* + * ShrinkIt was originally just going to be a 5.25" disk compressor, + * so the compression functions were organized around 4K blocks (the + * size of one track on a 5.25" disk). The block passed into the + * RLE function is always 4K, so we zero out any extra space. + */ + if (blockSize < kNuLZWBlockSize) { + memset(lzwState->inputBuf + blockSize, 0, + kNuLZWBlockSize - blockSize); + } + + /* + * Compute the CRC. For LZW/1 this is on the entire 4K block, for + * the "version 3" thread header CRC this is on just the "real" data. + */ + *pThreadCrc = Nu_CalcCRC16(*pThreadCrc, lzwState->inputBuf, blockSize); + if (!isType2) { + lzwState->chunkCrc = Nu_CalcCRC16(lzwState->chunkCrc, + lzwState->inputBuf, kNuLZWBlockSize); + } + + /* + * Try to compress with RLE, from inputBuf to rleBuf. + */ + err = Nu_CompressBlockRLE(lzwState, (int*) &rleSize); + BailError(err); + + if (rleSize < kNuLZWBlockSize) { + lzwInputBuf = lzwState->rleBuf; + } else { + lzwInputBuf = lzwState->inputBuf; + rleSize = kNuLZWBlockSize; + } + + /* + * Compress with LZW, into lzwBuf. + */ + if (!isType2) + Nu_ClearLZWTable(lzwState); + err = Nu_CompressLZWBlock(lzwState, lzwInputBuf, rleSize, + (int*) &lzwSize); + BailError(err); + + /* decide if we want to keep it, bearing in mind the LZW/2 header */ + if (pArchive->valMimicSHK) { + /* GSHK doesn't factor in header -- and *sometimes* uses "<=" !! */ + keepLzw = (lzwSize < rleSize); + } else { + if (isType2) + keepLzw = (lzwSize +2 < rleSize); + else + keepLzw = (lzwSize < rleSize); + } + + /* + * Write the compressed (or not) chunk. + */ + if (keepLzw) { + /* + * LZW succeeded. + */ + if (isType2) + rleSize |= 0x8000; /* for LZW/2, set "LZW used" flag */ + + putc(rleSize & 0xff, fp); /* size after RLE */ + putc(rleSize >> 8, fp); + compressedLen += 2; + + if (isType2) { + /* write compressed LZW len (+4 for header bytes) */ + putc((lzwSize+4) & 0xff, fp); + putc((lzwSize+4) >> 8, fp); + compressedLen += 2; + } else { + /* set LZW/1 "LZW used" flag */ + putc(1, fp); + compressedLen++; + } + + /* write data from LZW buffer */ + err = Nu_FWrite(fp, lzwState->lzwBuf, lzwSize); + BailError(err); + compressedLen += lzwSize; + } else { + /* + * LZW failed. + */ + putc(rleSize & 0xff, fp); /* size after RLE */ + putc(rleSize >> 8, fp); + compressedLen += 2; + + if (isType2) { + /* clear LZW/2 table; we can't use it next time */ + Nu_ClearLZWTable(lzwState); + } else { + /* set LZW/1 "LZW not used" flag */ + putc(0, fp); + compressedLen++; + } + + /* write data from RLE or plain-input buffer */ + err = Nu_FWrite(fp, lzwInputBuf, rleSize); + BailError(err); + compressedLen += rleSize; + } + + + /* + * Update the counter and continue. + */ + srcLen -= blockSize; + } + + /* + * For LZW/1, go back and write the CRC. + */ + if (!isType2) { + long curOffset; + + err = Nu_FTell(fp, &curOffset); + BailError(err); + err = Nu_FSeek(fp, initialOffset, SEEK_SET); + BailError(err); + putc(lzwState->chunkCrc & 0xff, fp); + putc(lzwState->chunkCrc >> 8, fp); + err = Nu_FSeek(fp, curOffset, SEEK_SET); + BailError(err); + } + + /* P8SHK and GSHK add an extra byte to LZW-compressed threads */ + if (pArchive->valMimicSHK) { + putc(0, fp); + compressedLen++; + } + + *pDstLen = compressedLen; + +bail: + return err; +} + +/* + * Compress ShrinkIt-style "LZW/1". + */ +NuError +Nu_CompressLZW1(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + return Nu_CompressLZW(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, false); +} + +/* + * Compress ShrinkIt-style "LZW/2". + */ +NuError +Nu_CompressLZW2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + return Nu_CompressLZW(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, true); +} + + +/* + * =========================================================================== + * Expansion + * =========================================================================== + */ + +/* if we don't have at least this much data, we try to read more */ +/* (the "+3" is for the chunk header bytes) */ +#define kNuLZWDesiredChunk (kNuLZWBlockSize + 3) + +/* + * Static tables useful for bit manipulation. + */ +static const uint gNuMaskTable[17] = { + 0x0000, 0x01ff, 0x03ff, 0x03ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, + 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, + 0x0fff +}; +/* convert high byte of "entry" into a bit width */ +static const uint gNuBitWidth[17] = { + 8,9,10,10,11,11,11,11,12,12,12,12,12,12,12,12,12 +}; + + +/* entry in the trie */ +typedef struct TableEntry { + uchar ch; + uint prefix; +} TableEntry; + +/* + * This holds all of the "big" dynamic state, plus a few things that I + * don't want to pass around. It's allocated once for each instance of + * an open archive, and re-used. + */ +typedef struct LZWExpandState { + NuArchive* pArchive; + + TableEntry trie[4096-256]; /* holds from 9 bits to 12 bits */ + uchar stack[kNuLZWBlockSize]; + + uint entry; /* 16-bit index into table */ + uint oldcode; /* carryover state for LZW/2 */ + uint incode; /* carryover state for LZW/2 */ + uint finalc; /* carryover state for LZW/2 */ + Boolean resetFix; /* work around an LZW/2 bug */ + + ushort chunkCrc; /* CRC we calculate for LZW/1 */ + ushort fileCrc; /* CRC stored with file */ + + uchar diskVol; /* disk volume # */ + uchar rleEscape; /* RLE escape char, usually 0xdb */ + + ulong dataInBuffer; /* #of bytes in compBuf */ + uchar* dataPtr; /* current data offset */ + + uchar lzwOutBuf[kNuLZWBlockSize + kNuSafetyPadding]; + uchar rleOutBuf[kNuLZWBlockSize + kNuSafetyPadding]; +} LZWExpandState; + + +/* + * Allocate some "reusable" state for LZW expansion. + */ +static NuError +Nu_AllocLZWExpandState(NuArchive* pArchive) +{ + NuError err; + + Assert(pArchive != nil); + Assert(pArchive->lzwExpandState == nil); + + /* allocate the general-purpose compression buffer, if needed */ + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + pArchive->lzwExpandState = Nu_Malloc(pArchive, sizeof(LZWExpandState)); + if (pArchive->lzwExpandState == nil) + return kNuErrMalloc; + return kNuErrNone; +} + + +#ifdef NDEBUG +# define Nu_LZWPush(uch) ( *stackPtr++ = (uch) ) +# define Nu_LZWPop() ( *(--stackPtr) ) +# define Nu_LZWStackEmpty() ( stackPtr == lzwState->stack ) + +#else +# define Nu_LZWPush(uch) \ + ( Nu_LZWPushCheck(uch, lzwState, stackPtr), *stackPtr++ = (uch) ) +# define Nu_LZWPop() \ + ( Nu_LZWPopCheck(lzwState, stackPtr), *(--stackPtr) ) +# define Nu_LZWStackEmpty() ( stackPtr == lzwState->stack ) + +static inline void +Nu_LZWPushCheck(uchar uch, const LZWExpandState* lzwState,const uchar* stackPtr) +{ + if (stackPtr >= lzwState->stack + sizeof(lzwState->stack)) { + Nu_ReportError(lzwState->NU_BLOB, kNuErrBadData, "stack overflow"); + abort(); + } +} + +static inline void +Nu_LZWPopCheck(const LZWExpandState* lzwState, const uchar* stackPtr) +{ + if (stackPtr == lzwState->stack) { + Nu_ReportError(lzwState->NU_BLOB, kNuErrBadData, "stack underflow"); + abort(); + } +} + +#endif + +/* + * Get the next LZW code from the input, advancing pointers as needed. + * + * This would be faster as a macro and less ugly with pass-by-reference. + * Resorting to globals is unacceptable. Might be less ugly if we clumped + * some stuff into a struct. Should be good enough as-is. + * + * Returns an integer up to 12 bits long. + * + * (Turning this into a macro might speed things up.) + */ +static inline uint +Nu_LZWGetCode(const uchar** pInBuf, uint entry, int* pAtBit, uint* pLastByte) +{ + uint numBits, startBit, lastBit; + ulong value; + + Assert(sizeof(uint) >= 2); + + numBits = (entry +1) >> 8; /* bit-width of next code */ + startBit = *pAtBit; + lastBit = startBit + gNuBitWidth[numBits]; + + /* + * We need one or two bytes from the input. These have to be shifted + * around and merged with the bits we already have (if any). + */ + if (!startBit) + value = *(*pInBuf)++; + else + value = *pLastByte; + + if (lastBit > 16) { + /* need two more bytes */ + value |= *(*pInBuf)++ << 8; + *pLastByte = *(*pInBuf)++; + value |= (ulong) *pLastByte << 16; + } else { + /* only need one more byte */ + *pLastByte = *(*pInBuf)++; + value |= *pLastByte << 8; + } + + *pAtBit = lastBit & 0x07; + + /*printf("| EX: value=$%06lx mask=$%04x return=$%03lx\n", + value,gNuMaskTable[numBits], (value >> startBit) & gNuMaskTable[numBits]);*/ + + /*DBUG_LZW(("### getcode 0x%04lx\n", + (value >> startBit) & gNuMaskTable[numBits]));*/ + + /* I believe ANSI allows shifting by zero bits, so don't test "!startBit" */ + return (value >> startBit) & gNuMaskTable[numBits]; +} + + +/* + * Expand an LZW/1 chunk. + * + * Reads from lzwState->dataPtr, writes to lzwState->lzwOutBuf. + */ +static NuError +Nu_ExpandLZW1(LZWExpandState* lzwState, uint expectedLen) +{ + NuError err = kNuErrNone; + TableEntry* tablePtr; + int atBit; + uint entry, oldcode, incode, ptr; + uint lastByte, finalc; + const uchar* inbuf; + uchar* outbuf; + uchar* outbufend; + uchar* stackPtr; + + Assert(lzwState != nil); + Assert(expectedLen > 0 && expectedLen <= kNuLZWBlockSize); + + inbuf = lzwState->dataPtr; + outbuf = lzwState->lzwOutBuf; + outbufend = outbuf + expectedLen; + tablePtr = lzwState->trie - 256; /* don't store 256 empties */ + stackPtr = lzwState->stack; + + atBit = 0; + lastByte = 0; + + entry = kNuLZWFirstCode; /* 0x101 */ + finalc = oldcode = incode = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); + *outbuf++ = incode; + Assert(incode <= 0xff); + if (incode > 0xff) { + err = kNuErrBadData; + Nu_ReportError(lzwState->NU_BLOB, err, "invalid initial LZW symbol"); + goto bail; + } + + while (outbuf < outbufend) { + incode = ptr = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); + + /* handle KwKwK case */ + if (ptr >= entry) { + //DBUG_LZW(("### KwKwK (ptr=%d entry=%d)\n", ptr, entry)); + if (ptr != entry) { + /* bad code -- this would make us read uninitialized data */ + DBUG(("--- bad code (ptr=%d entry=%d)\n", ptr, entry)); + err = kNuErrBadData; + return err; + } + Nu_LZWPush((uchar)finalc); + ptr = oldcode; + } + + /* fill the stack by chasing up the trie */ + while (ptr > 0xff) { + Nu_LZWPush(tablePtr[ptr].ch); + ptr = tablePtr[ptr].prefix; + Assert(ptr < 4096); + } + + /* done chasing up, now dump the stack, starting with ptr */ + finalc = ptr; + *outbuf++ = ptr; + /*printf("PUT 0x%02x\n", *(outbuf-1));*/ + while (!Nu_LZWStackEmpty()) { + *outbuf++ = Nu_LZWPop(); + /*printf("POP/PUT 0x%02x\n", *(outbuf-1));*/ + } + + /* add the new prefix to the trie -- last string plus new char */ + Assert(finalc <= 0xff); + tablePtr[entry].ch = finalc; + tablePtr[entry].prefix = oldcode; + entry++; + oldcode = incode; + } + +bail: + if (outbuf != outbufend) { + err = kNuErrBadData; + Nu_ReportError(lzwState->NU_BLOB, err, "LZW expansion failed"); + return err; + } + + /* adjust input buffer */ + lzwState->dataInBuffer -= (inbuf - lzwState->dataPtr); + Assert(lzwState->dataInBuffer < 32767*65536); + lzwState->dataPtr = (uchar*)inbuf; + + return err; +} + +/* + * Expand an LZW/2 chunk. Main difference from LZW/1 is that the state + * is carried over from the previous block in most cases, and the table + * is cleared explicitly. + * + * Reads from lzwState->dataPtr, writes to lzwState->lzwOutBuf. + * + * In some cases, "expectedInputUsed" will be -1 to indicate that the + * value is not known. + */ +static NuError +Nu_ExpandLZW2(LZWExpandState* lzwState, uint expectedLen, + uint expectedInputUsed) +{ + NuError err = kNuErrNone; + TableEntry* tablePtr; + int atBit; + uint entry, oldcode, incode, ptr; + uint lastByte, finalc; + const uchar* inbuf; + const uchar* inbufend; + uchar* outbuf; + uchar* outbufend; + uchar* stackPtr; + + /*DBUG_LZW(("### LZW/2 block start (compIn=%d, rleOut=%d, entry=0x%04x)\n", + expectedInputUsed, expectedLen, lzwState->entry));*/ + Assert(lzwState != nil); + Assert(expectedLen > 0 && expectedLen <= kNuLZWBlockSize); + + inbuf = lzwState->dataPtr; + inbufend = lzwState->dataPtr + expectedInputUsed; + outbuf = lzwState->lzwOutBuf; + outbufend = outbuf + expectedLen; + entry = lzwState->entry; + tablePtr = lzwState->trie - 256; /* don't store 256 empties */ + stackPtr = lzwState->stack; + + atBit = 0; + lastByte = 0; + + /* + * If the table isn't empty, initialize from the saved state and + * jump straight into the main loop. + * + * There's a funny situation that arises when a table clear is the + * second-to-last code in the previous chunk. After we see the + * table clear, we get the next code and use it to initialize "oldcode" + * and "incode" -- but we don't advance "entry" yet. The way that + * ShrinkIt originally worked, the next time we came through we'd + * see what we thought was an empty table and we'd reinitialize. So + * we use "resetFix" to keep track of this situation. + */ + if (entry != kNuLZWFirstCode || lzwState->resetFix) { + /* table not empty */ + oldcode = lzwState->oldcode; + incode = lzwState->incode; + finalc = lzwState->finalc; + lzwState->resetFix = false; + goto main_loop; + } + +clear_table: + /* table is either empty or was just explicitly cleared; reset */ + entry = kNuLZWFirstCode; /* 0x0101 */ + if (outbuf == outbufend) { + /* block must've ended on a table clear */ + DBUG(("--- RARE: ending clear\n")); + /* reset values, mostly to quiet gcc's "used before init" warnings */ + oldcode = incode = finalc = 0; + goto main_loop; /* the while condition will fall through */ + } + finalc = oldcode = incode = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); + *outbuf++ = incode; + /*printf("PUT 0x%02x\n", *(outbuf-1));*/ + if (incode > 0xff) { + err = kNuErrBadData; + Nu_ReportError(lzwState->NU_BLOB, err, "invalid initial LZW symbol"); + goto bail; + } + + if (outbuf == outbufend) { + /* if we're out of data, raise the "reset fix" flag */ + DBUG(("--- RARE: resetFix!\n")); + lzwState->resetFix = true; + /* fall through; the while condition will let us slip past */ + } + +main_loop: + while (outbuf < outbufend) { + incode = ptr = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); + //DBUG_LZW(("### read incode=0x%04x\n", incode)); + if (incode == kNuLZWClearCode) /* table clear - 0x0100 */ + goto clear_table; + + /* handle KwKwK case */ + if (ptr >= entry) { + //DBUG_LZW(("### KwKwK (ptr=%d entry=%d)\n", ptr, entry)); + if (ptr != entry) { + /* bad code -- this would make us read uninitialized data */ + DBUG(("--- bad code (ptr=%d entry=%d)\n", ptr, entry)); + err = kNuErrBadData; + return err; + } + Nu_LZWPush((uchar)finalc); + ptr = oldcode; + } + + /* fill the stack by chasing up the trie */ + while (ptr > 0xff) { + Nu_LZWPush(tablePtr[ptr].ch); + ptr = tablePtr[ptr].prefix; + Assert(ptr < 4096); + } + + /* done chasing up, now dump the stack, starting with ptr */ + finalc = ptr; + *outbuf++ = ptr; + /*printf("PUT 0x%02x\n", *(outbuf-1));*/ + while (!Nu_LZWStackEmpty()) { + *outbuf++ = Nu_LZWPop(); + /*printf("POP/PUT 0x%02x\n", *(outbuf-1));*/ + } + + /* add the new prefix to the trie -- last string plus new char */ + /*DBUG_LZW(("### entry 0x%04x gets prefix=0x%04x and ch=0x%02x\n", + entry, oldcode, finalc));*/ + Assert(finalc <= 0xff); + tablePtr[entry].ch = finalc; + tablePtr[entry].prefix = oldcode; + entry++; + oldcode = incode; + } + +bail: + /*DBUG_LZW(("### end of block\n"));*/ + if (expectedInputUsed != (unsigned int) -1 && inbuf != inbufend) { + /* data was corrupted; if we keep going this will get worse */ + DBUG(("--- inbuf != inbufend in ExpandLZW2 (diff=%d)\n", + inbufend - inbuf)); + err = kNuErrBadData; + return err; + } + Assert(outbuf == outbufend); + + /* adjust input buffer */ + lzwState->dataInBuffer -= (inbuf - lzwState->dataPtr); + Assert(lzwState->dataInBuffer < 32767*65536); + lzwState->dataPtr = (uchar*)inbuf; + + /* save off local copies of stuff */ + lzwState->entry = entry; + lzwState->oldcode = oldcode; + lzwState->incode = incode; + lzwState->finalc = finalc; + + return err; +} + + +/* + * Expands a chunk of RLEd data into 4K of output. + */ +static NuError +Nu_ExpandRLE(LZWExpandState* lzwState, const uchar* inbuf, + uint expectedInputUsed) +{ + NuError err = kNuErrNone; + uchar *outbuf; + uchar *outbufend; + const uchar *inbufend; + uchar uch, rleEscape; + int count; + + outbuf = lzwState->rleOutBuf; + outbufend = outbuf + kNuLZWBlockSize; + inbufend = inbuf + expectedInputUsed; + rleEscape = lzwState->rleEscape; + + while (outbuf < outbufend) { + uch = *inbuf++; + if (uch == rleEscape) { + uch = *inbuf++; + count = *inbuf++; + if (outbuf + count >= outbufend) { + /* don't overrun buffer */ + Assert(outbuf != outbufend); + break; + } + while (count-- >= 0) + *outbuf++ = uch; + } else { + *outbuf++ = uch; + } + } + + if (outbuf != outbufend) { + err = kNuErrBadData; + Nu_ReportError(lzwState->NU_BLOB, err, + "RLE output glitch (off by %d)", (int)(outbufend-outbuf)); + goto bail; + } + if (inbuf != inbufend) { + err = kNuErrBadData; + Nu_ReportError(lzwState->NU_BLOB, err, + "RLE input glitch (off by %d)", (int)(inbufend-inbuf)); + goto bail; + } + +bail: + return err; +} + + +/* + * Utility function to get a byte from the input buffer. + */ +static inline uchar +Nu_GetHeaderByte(LZWExpandState* lzwState) +{ + lzwState->dataInBuffer--; + Assert(lzwState->dataInBuffer > 0); + return *lzwState->dataPtr++; +} + +/* + * Expand ShrinkIt-style "LZW/1" and "LZW/2". + * + * This manages the input data buffer, passing chunks of compressed data + * into the appropriate expansion function. + * + * Pass in nil for "pThreadCrc" if no thread CRC is desired. Otherwise, + * "*pThreadCrc" should already be set to its initial value. On exit it + * will contain the CRC of the uncompressed data. + */ +NuError +Nu_ExpandLZW(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pThreadCrc) +{ + NuError err = kNuErrNone; + Boolean isType2; + LZWExpandState* lzwState; + ulong compRemaining, uncompRemaining, minSize; + + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(infp != nil); + Assert(pFunnel != nil); + + /* + * Do some initialization and set-up. + */ + if (pArchive->lzwExpandState == nil) { + err = Nu_AllocLZWExpandState(pArchive); + BailError(err); + } + Assert(pArchive->lzwExpandState != nil); + Assert(pArchive->compBuf != nil); + + lzwState = pArchive->lzwExpandState; + lzwState->pArchive = pArchive; + + if (pThread->thThreadFormat == kNuThreadFormatLZW1) { + isType2 = false; + minSize = 7; /* crc-lo,crc-hi,vol,rle-delim,len-lo,len-hi,lzw-used */ + lzwState->chunkCrc = kNuInitialChunkCRC; /* 0x0000 */ + } else if (pThread->thThreadFormat == kNuThreadFormatLZW2) { + isType2 = true; + minSize = 4; /* vol,rle-delim,len-lo,len-hi */ + } else { + err = kNuErrBadFormat; + goto bail; + } + + uncompRemaining = pThread->actualThreadEOF; + compRemaining = pThread->thCompThreadEOF; + if (compRemaining < minSize) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, "thread too short to be valid LZW"); + goto bail; + } + if (compRemaining && !uncompRemaining) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, + "compressed data but no uncompressed data??"); + goto bail; + } + + /* + * Read the LZW header out of the data stream. + */ + if (!isType2) { + lzwState->fileCrc = getc(infp); + lzwState->fileCrc |= getc(infp) << 8; + compRemaining -= 2; + } + lzwState->diskVol = getc(infp); /* disk volume #; not really used */ + lzwState->rleEscape = getc(infp); /* RLE escape char for this thread */ + compRemaining -= 2; + + lzwState->dataInBuffer = 0; + lzwState->dataPtr = nil; + + /* reset pointers */ + lzwState->entry = kNuLZWFirstCode; /* 0x0101 */ + lzwState->resetFix = false; + + /*DBUG_LZW(("### LZW%d block, vol=0x%02x, rleEsc=0x%02x\n", + isType2 +1, lzwState->diskVol, lzwState->rleEscape));*/ + + /* + * Read large blocks of the source file into compBuf, taking care not + * to read past the end of the thread data. + * + * The motivation for doing it this way rather than just reading the + * next compressed chunk are (1) compBuf is considerably larger than + * stdio BUFSIZ on most systems, and (2) for LZW/1 we don't know the + * size of the compressed data anyway. + * + * We need to ensure that we have at least one full compressed chunk + * in the buffer. Since the compressor will refuse to store the + * compressed data if it grows, we know that we need 4K plus the + * chunk header. + * + * Once we have what looks like a full chunk, invoke the LZW decoder. + */ + while (uncompRemaining) { + Boolean rleUsed; + Boolean lzwUsed; + ulong getSize; + uint rleLen; /* length after RLE; 4096 if no RLE */ + uint lzwLen = 0; /* type 2 only */ + uint writeLen, inCount; + const uchar* writeBuf; + + /* if we're low, and there's more data available, read more */ + if (lzwState->dataInBuffer < kNuLZWDesiredChunk && compRemaining) { + /* + * First thing we do is slide the old data to the start of + * the buffer. + */ + if (lzwState->dataInBuffer) { + Assert(lzwState->dataPtr != nil); + Assert(pArchive->compBuf != lzwState->dataPtr); + memmove(pArchive->compBuf, lzwState->dataPtr, + lzwState->dataInBuffer); + } + lzwState->dataPtr = pArchive->compBuf; + + /* + * Next we read as much as we can. + */ + if (kNuGenCompBufSize - lzwState->dataInBuffer < compRemaining) + getSize = kNuGenCompBufSize - lzwState->dataInBuffer; + else + getSize = compRemaining; + + /*printf("+++ READING %ld\n", getSize);*/ + err = Nu_FRead(infp, lzwState->dataPtr + lzwState->dataInBuffer, + getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, + "failed reading compressed data (%ld bytes)", getSize); + goto bail; + } + lzwState->dataInBuffer += getSize; + compRemaining -= getSize; + + Assert(compRemaining < 32767*65536); + Assert(lzwState->dataInBuffer <= kNuGenCompBufSize); + } + Assert(lzwState->dataInBuffer); + + /* + * Read the LZW block header. + */ + if (isType2) { + rleLen = Nu_GetHeaderByte(lzwState); + rleLen |= Nu_GetHeaderByte(lzwState) << 8; + lzwUsed = rleLen & 0x8000 ? true : false; + rleLen &= 0x1fff; + rleUsed = (rleLen != kNuLZWBlockSize); + + if (lzwUsed) { + lzwLen = Nu_GetHeaderByte(lzwState); + lzwLen |= Nu_GetHeaderByte(lzwState) << 8; + lzwLen -= 4; /* don't include header bytes */ + } + } else { + rleLen = Nu_GetHeaderByte(lzwState); + rleLen |= Nu_GetHeaderByte(lzwState) << 8; + lzwUsed = Nu_GetHeaderByte(lzwState); + if (lzwUsed != 0 && lzwUsed != 1) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, "garbled LZW header"); + goto bail; + } + rleUsed = (rleLen != kNuLZWBlockSize); + } + + /*DBUG_LZW(("### CHUNK rleLen=%d(%d) lzwLen=%d(%d) uncompRem=%ld\n", + rleLen, rleUsed, lzwLen, lzwUsed, uncompRemaining));*/ + + if (uncompRemaining <= kNuLZWBlockSize) + writeLen = uncompRemaining; /* last block */ + else + writeLen = kNuLZWBlockSize; + + #ifndef NDEBUG + writeBuf = nil; + #endif + + /* + * Decode the chunk, and point "writeBuf" at the uncompressed data. + * + * LZW always expands from the read buffer into lzwState->lzwOutBuf. + * RLE expands from a specific buffer to lzwState->rleOutBuf. + */ + if (lzwUsed) { + if (!isType2) { + err = Nu_ExpandLZW1(lzwState, rleLen); + } else { + if (pRecord->isBadMac || pArchive->valIgnoreLZW2Len) { + /* might be big-endian, might be okay; just ignore it */ + lzwLen = (unsigned int) -1; + } else if (lzwState->dataInBuffer < lzwLen) { + /* rare -- GSHK will do this if you don't let it finish */ + err = kNuErrBufferUnderrun; + Nu_ReportError(NU_BLOB, err, "not enough compressed data " + "-- archive truncated during creation?"); + goto bail; + } + err = Nu_ExpandLZW2(lzwState, rleLen, lzwLen); + } + + BailError(err); + + if (rleUsed) { + err = Nu_ExpandRLE(lzwState, lzwState->lzwOutBuf, rleLen); + BailError(err); + writeBuf = lzwState->rleOutBuf; + } else { + writeBuf = lzwState->lzwOutBuf; + } + + } else { + if (rleUsed) { + err = Nu_ExpandRLE(lzwState, lzwState->dataPtr, rleLen); + BailError(err); + writeBuf = lzwState->rleOutBuf; + inCount = rleLen; + } else { + writeBuf = lzwState->dataPtr; + inCount = writeLen; + } + + /* + * Advance the input buffer data pointers to consume the input. + * The LZW expansion functions do this for us, but we're not + * using LZW. + */ + lzwState->dataPtr += inCount; + lzwState->dataInBuffer -= inCount; + Assert(lzwState->dataInBuffer < 32767*65536); + + /* no LZW used, reset pointers */ + lzwState->entry = kNuLZWFirstCode; /* 0x0101 */ + lzwState->resetFix = false; + } + + Assert(writeBuf != nil); + + /* + * Compute the CRC of the uncompressed data, and write it. For + * LZW/1, the CRC of the last block includes the zeros that pad + * it out to 4096 bytes. + * + * See commentary in the compression code for why we have to + * compute two CRCs for LZW/1. + */ + if (pThreadCrc != nil) { + *pThreadCrc = Nu_CalcCRC16(*pThreadCrc, writeBuf, writeLen); + } + if (!isType2) { + lzwState->chunkCrc = Nu_CalcCRC16(lzwState->chunkCrc, + writeBuf, kNuLZWBlockSize); + } + + /* write the data, possibly doing an EOL conversion */ + err = Nu_FunnelWrite(pArchive, pFunnel, writeBuf, writeLen); + if (err != kNuErrNone) { + if (err != kNuErrAborted) + Nu_ReportError(NU_BLOB, err, "unable to write output"); + goto bail; + } + + uncompRemaining -= writeLen; + Assert(uncompRemaining < 32767*65536); + } + + /* + * It appears that ShrinkIt appends an extra byte after the last + * LZW block. The byte is included in the compThreadEOF, but isn't + * consumed by the LZW expansion routine, so it's usually harmless. + * + * It is *possible* for extra bytes to be here legitimately, but very + * unlikely. The very last block is always padded out to 4K with + * zeros. If you found a situation where that last block failed + * to compress with RLE and LZW (perhaps the last block filled up + * all but the last 2 or 3 bytes with uncompressible data), but + * earlier data made the overall file compressible, you would have + * a few stray bytes in the archive. + * + * This is a little easier to do if the last block has lots of single + * 0xdb characters in it, since that requires RLE to escape them. + * + * Whatever the case, issue a warning if it looks like there's too + * many of them. + */ + if (lzwState->dataInBuffer > 1) { + DBUG(("--- Found %ld bytes following compressed data (compRem=%ld)\n", + lzwState->dataInBuffer, compRemaining)); + if (lzwState->dataInBuffer > 32) { + Nu_ReportError(NU_BLOB, kNuErrNone, "(Warning) lots of fluff (%ld)", + lzwState->dataInBuffer); + } + } + + /* + * We might be okay with stray bytes in the thread, but we're definitely + * not okay with anything identified as compressed data being unused. + */ + if (compRemaining) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, + "not all compressed data was used (%ld/%ld)", + compRemaining, lzwState->dataInBuffer); + goto bail; + } + + /* + * ShrinkIt used to put the CRC in the stream and not in the thread + * header. For LZW/1, we check the CRC here; for LZW/2, we hope it's + * in the thread header. (As noted in the compression code, it's + * possible to end up with two CRCs or no CRCs.) + */ + if (!isType2 && !pArchive->valIgnoreCRC) { + if (lzwState->chunkCrc != lzwState->fileCrc) { + if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadDataCRC)) { + err = kNuErrBadDataCRC; + Nu_ReportError(NU_BLOB, err, + "expected 0x%04x, got 0x%04x (LZW/1)", + lzwState->fileCrc, lzwState->chunkCrc); + (void) Nu_FunnelFlush(pArchive, pFunnel); + goto bail; + } + } else { + DBUG(("--- LZW/1 CRCs match (0x%04x)\n", lzwState->chunkCrc)); + } + } + +bail: + return err; +} + +#endif /*ENABLE_LZW*/ diff --git a/nufxlib/Makefile.in b/nufxlib/Makefile.in new file mode 100644 index 0000000..c02d0ec --- /dev/null +++ b/nufxlib/Makefile.in @@ -0,0 +1,122 @@ +# +# NuFX archive manipulation library +# Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. +# This is free software; you can redistribute it and/or modify it under the +# terms of the BSD License, see the file COPYING-LIB. +# +# Makefile for nufxlib (should work with non-GNU "make"). +# +# You can use: +# make (builds library and sample applications) +# make shared (builds shared library if you're using GNU ld or similar) +# +# The shared library support currently leaves much to be desired. +# + +# NufxLib install location. +prefix = @prefix@ +exec_prefix = @exec_prefix@ +includedir = @includedir@ +libdir = @libdir@ +srcdir = @srcdir@ + +SHELL = @SHELL@ +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +CC = @CC@ +AR = ar rcv +#OPT = @CFLAGS@ -DNDEBUG +OPT = @CFLAGS@ +#OPT = @CFLAGS@ -DDEBUG_MSGS +#OPT = @CFLAGS@ -DDEBUG_VERBOSE +GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow +CFLAGS = @BUILD_FLAGS@ -I. @DEFS@ -DOPTFLAGSTR="\"$(OPT)\"" + +SRCS = Archive.c ArchiveIO.c Bzip2.c Compress.c Crc16.c Debug.c \ + Deferred.c Deflate.c Entry.c Expand.c FileIO.c Funnel.c \ + Lzc.c Lzw.c MiscStuff.c MiscUtils.c Record.c SourceSink.c \ + Squeeze.c Thread.c Value.c Version.c +OBJS = Archive.o ArchiveIO.o Bzip2.o Compress.o Crc16.o Debug.o \ + Deferred.o Deflate.o Entry.o Expand.o FileIO.o Funnel.o \ + Lzc.o Lzw.o MiscStuff.o MiscUtils.o Record.o SourceSink.o \ + Squeeze.o Thread.o Value.o Version.o + +STATIC_PRODUCT = libnufx.a +SHARED_PRODUCT = libnufx.so +PRODUCT = $(STATIC_PRODUCT) + + +# +# Build stuff +# + +all: $(PRODUCT) samples + @true + +install: $(STATIC_PRODUCT) + $(srcdir)/mkinstalldirs $(libdir) + $(INSTALL_DATA) $(STATIC_PRODUCT) $(libdir) + $(srcdir)/mkinstalldirs $(includedir) + $(INSTALL_DATA) NufxLib.h $(includedir) + +install-shared: $(SHARED_PRODUCT) + $(srcdir)/mkinstalldirs $(libdir) + $(INSTALL_DATA) $(SHARED_PRODUCT) $(libdir) + $(srcdir)/mkinstalldirs $(includedir) + $(INSTALL_DATA) NufxLib.h $(includedir) + +samples:: + @echo "Building samples..." + @(cd samples; set +e; unset CFLAGS OBJS; set -e; \ + @SET_MAKE@ LIB_PRODUCT="../$(PRODUCT)" $(MAKE)) + +shared:: + PRODUCT="$(SHARED_PRODUCT)" $(MAKE) -e + +$(STATIC_PRODUCT): $(OBJS) + -rm -f $(STATIC_PRODUCT) $(SHARED_PRODUCT) + $(AR) $@ $(OBJS) + @RANLIB@ $@ + +# BUG: we probably want -fPIC -D_REENTRANT on the compile lines for this. +# BUG: for Linux we may want -Wl,-soname,libnufx.so.1 on the link line. +$(SHARED_PRODUCT): $(OBJS) + -rm -f $(STATIC_PRODUCT) $(SHARED_PRODUCT) + $(CC) @SHARE_FLAGS@ -o $@ $(OBJS) @LIBS@ + +clean: + (cd samples; make clean) + -rm -f *.o core + -rm -f $(SHARED_PRODUCT) $(STATIC_PRODUCT) + +# build tags; assumes fancy GNU tag generation +tags:: + @ctags -R --totals * + @#ctags *.[ch] + +distclean: clean + (cd samples; make distclean) + -rm -f Version.c + -rm -f Makefile Makefile.bak + -rm -f config.log config.cache config.status config.h + -rm -f tags + +# Make a tarfile with a backup of the essential files. We include "Makefile" +# so that we can do a "make distclean" during packaging. +baktar: + @tar cvf nufxlib.tar *.txt COPYING-LIB INSTALL configure *.in Makefile \ + Makefile.msc Makefile.dll install-sh config.guess config.sub \ + mkinstalldirs *.[ch] samples/*.txt samples/Makefile* samples/*.[ch] + @gzip -9 nufxlib.tar + @mv -i nufxlib.tar.gz /home/fadden/BAK/ + +depend: + makedepend -- $(CFLAGS) -I/usr/local/include -- $(SRCS) + @(cd samples; unset CFLAGS OBJS; @SET_MAKE@ $(MAKE) depend) + +# catch OPTFLAGSTR updates +Version.o: Makefile + +# DO NOT DELETE THIS LINE -- make depend depends on it. + diff --git a/nufxlib/Makefile.msc b/nufxlib/Makefile.msc new file mode 100644 index 0000000..aafb34c --- /dev/null +++ b/nufxlib/Makefile.msc @@ -0,0 +1,155 @@ +# Makefile for NufxLib using Microsoft Visual C++. This builds the library +# as a static lib and as a DLL, and builds all samples. The test-basic +# sample is built twice, once with the static lib, and once with the DLL. +# +# Tested with VS 2013 Pro. From the "VS2013 x86 Native Tools Command +# Prompt", run "nmake -f makefile.msc". +# +# If you're including zlib support, place copies of zlib.h, zconf.h, +# and the zlib library in this directory. +# +# Adapted from zlib's Makefile.msc. +# + +TOP = . + +STATICLIB = nufxlib2.lib +SHAREDLIB = nufxlib2.dll +IMPLIB = nufxdll.lib + +CC = cl +LD = link +AR = lib + +# C compiler flags +# -Fd: rename PDB file from "VCx0.pdb" (where 'x' is the version number); +# allows DLL debug info to be separate from app debug info +# -Ox: full optimization +# -Oy-: disable frame pointer omission (for easier debugging) +# -MD: create a multithreaded DLL using MSVCRT.lib; alternatively, +# use -MDd to create a debug executable with MSVCRTD.lib +# -nologo: suppress display of copyright banner +# -W3: set warning level to 3 (all production-level warnings) +# -Zi: generate a PDB file with full debugging info +# +# The OPTFLAGSTR define is used by Version.c to show how the library was +# built. Defining NUFXLIB_EXPORTS enables the __declspec(dllexport) +# macros that are required for creating the DLL. +OPTFLAGS = -Ox -Oy- +CFLAGS = -nologo -MD -W3 $(OPTFLAGS) -Zi -Fd"nufxlib" + +LIB_CFLAGS = -DOPTFLAGSTR="\"$(OPTFLAGS)\"" #-DNUFXLIB_EXPORTS + +# Warning suppression flags +WFLAGS = -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE + +# Linker flags +# -debug: creates debugging info for EXE or DLL in PDB file +# -incremental:no: disable incremental linking, making the resulting library +# a tad smaller +# -nologo: suppress display of copyright banner +# -opt:ref: eliminates unreferenced functions and data (default for non-debug +# builds, but we've enabled debug info) +LDFLAGS = -nologo -debug -incremental:no -opt:ref + +# Library creator flags +ARFLAGS = -nologo + + +ZLIB=1 +!ifdef ZLIB +# enable deflate support; requires zlib +CFLAGS = $(CFLAGS) -DENABLE_DEFLATE +LDFLAGS = $(LDFLAGS) zlib.lib +!endif + + +# object files +OBJS = Archive.obj ArchiveIO.obj Bzip2.obj Compress.obj Crc16.obj Debug.obj \ + Deferred.obj Deflate.obj Entry.obj Expand.obj FileIO.obj Funnel.obj \ + Lzc.obj Lzw.obj MiscStuff.obj MiscUtils.obj Record.obj SourceSink.obj \ + Squeeze.obj Thread.obj Value.obj Version.obj + + +# build targets -- static library, dynamic library, and test programs +all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) \ + exerciser.exe imgconv.exe launder.exe test-basic.exe \ + test-basic-d.exe test-extract.exe test-simple.exe test-twirl.exe + +clean: + -del *.obj *.pdb *.exp + -del $(STATICLIB) $(SHAREDLIB) $(IMPLIB) + +$(STATICLIB): $(OBJS) + $(AR) $(ARFLAGS) -out:$@ $(OBJS) + +$(IMPLIB): $(SHAREDLIB) + +$(SHAREDLIB): $(OBJS) + $(LD) $(LDFLAGS) -dll -def:nufxlib.def -implib:$(IMPLIB) -out:$@ \ + $(OBJS) + +exerciser.exe: Exerciser.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ Exerciser.obj $(STATICLIB) + +imgconv.exe: ImgConv.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ ImgConv.obj $(STATICLIB) + +launder.exe: Launder.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ Launder.obj $(STATICLIB) + +test-basic.exe: TestBasic.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ TestBasic.obj $(STATICLIB) + +test-basic-d.exe: TestBasic.obj $(IMPLIB) + $(LD) $(LDFLAGS) -out:$@ TestBasic.obj $(IMPLIB) + +test-extract.exe: TestExtract.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ TestExtract.obj $(STATICLIB) + +test-simple.exe: TestSimple.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ TestSimple.obj $(STATICLIB) + +test-twirl.exe: TestTwirl.obj $(STATICLIB) + $(LD) $(LDFLAGS) -out:$@ TestTwirl.obj $(STATICLIB) + +# generic rules +{$(TOP)}.c.obj: + $(CC) -c $(WFLAGS) $(CFLAGS) $(LIB_CFLAGS) $< + +{$(TOP)/samples}.c.obj: + $(CC) -c -I$(TOP) $(WFLAGS) $(CFLAGS) $< + +# dependency info +COMMON_HDRS = NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h +Archive.obj: Archive.c $(COMMON_HDRS) +ArchiveIO.obj: ArchiveIO.c $(COMMON_HDRS) +Bzip2.obj: Bzip2.c $(COMMON_HDRS) +Compress.obj: Compress.c $(COMMON_HDRS) +Crc16.obj: Crc16.c $(COMMON_HDRS) +Debug.obj: Debug.c $(COMMON_HDRS) +Deferred.obj: Deferred.c $(COMMON_HDRS) +Deflate.obj: Deflate.c $(COMMON_HDRS) +Entry.obj: Entry.c $(COMMON_HDRS) +Expand.obj: Expand.c $(COMMON_HDRS) +FileIO.obj: FileIO.c $(COMMON_HDRS) +Funnel.obj: Funnel.c $(COMMON_HDRS) +Lzc.obj: Lzc.c $(COMMON_HDRS) +Lzw.obj: Lzw.c $(COMMON_HDRS) +MiscStuff.obj: MiscStuff.c $(COMMON_HDRS) +MiscUtils.obj: MiscUtils.c $(COMMON_HDRS) +Record.obj: Record.c $(COMMON_HDRS) +SourceSink.obj: SourceSink.c $(COMMON_HDRS) +Squeeze.obj: Squeeze.c $(COMMON_HDRS) +Thread.obj: Thread.c $(COMMON_HDRS) +Value.obj: Value.c $(COMMON_HDRS) +Version.obj: Version.c $(COMMON_HDRS) + +Exerciser.obj: samples/Exerciser.c $(COMMON_HDRS) +ImgConv.obj: samples/ImgConv.c $(COMMON_HDRS) +Launder.obj: samples/Launder.c $(COMMON_HDRS) +TestBasic.obj: samples/TestBasic.c $(COMMON_HDRS) +TestExtract.obj: samples/TestExtract.c $(COMMON_HDRS) +TestSimple.obj: samples/TestSimple.c $(COMMON_HDRS) +TestTwirl.obj: samples/TestTwirl.c $(COMMON_HDRS) + diff --git a/nufxlib/MiscStuff.c b/nufxlib/MiscStuff.c new file mode 100644 index 0000000..4de6ab7 --- /dev/null +++ b/nufxlib/MiscStuff.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Misc stuff (shared between nufxlib and nulib2). This is a collection + * of standard functions that aren't available in libc on this system. + */ +#include "SysDefs.h" +#include "MiscStuff.h" +#include + + +#ifndef HAVE_STRERROR +/* + * Return a pointer to the appropriate string in the system table, or NULL + * if the value is out of bounds. + */ +const char* +Nu_strerror(int errnum) +{ + extern int sys_nerr; + extern char *sys_errlist[]; + + if (errnum < 0 || errnum > sys_nerr) + return NULL; + + return sys_errlist[errnum]; +} +#endif + +#ifndef HAVE_MEMMOVE +/* + * Move a block of memory. Unlike memcpy, this is expected to work + * correctly with overlapping blocks. + * + * This is a straightforward implementation. A much faster implementation, + * from BSD, is available in the PGP 2.6.2 distribution, but this should + * suffice for those few systems that don't have memmove. + */ +void* +Nu_memmove(void* dst, const void* src, size_t n) +{ + void* retval = dst; + char* srcp = (char*)src; + char* dstp = (char*)dst; + + /* you can normally get away with this if n==0 */ + Assert(dst != NULL); + Assert(src != NULL); + + if (dstp == srcp || !n) { + /* nothing to do */ + } else if (dstp > srcp) { + /* start from the end */ + (char*)dstp += n-1; + (char*)srcp += n-1; + while (n--) + *dstp-- = *srcp--; + } else { + /* start from the front */ + while (n--) + *dstp++ = *srcp++; + } + + return retval; +} +#endif + +#ifndef HAVE_STRTOUL +/* + * Perform strtol, but on an unsigned long. + * + * On systems that have strtol but don't have strtoul, the strtol + * function doesn't clamp the return value, making it similar in + * function to strtoul. The comparison is not exact, however, + * because strtoul is expected to lots of fancy things (like set + * errno to ERANGE). + * + * For our purposes here, strtol does all we need it to. Someday + * we should replace this with a "real" version. + */ +unsigned long +Nu_strtoul(const char *nptr, char **endptr, int base) +{ + return strtol(nptr, endptr, base); +} +#endif + +#ifndef HAVE_STRCASECMP +/* + * Compare two strings, case-insensitive. + */ +int +Nu_strcasecmp(const char *str1, const char *str2) +{ + while (*str1 && *str2 && toupper(*str1) == toupper(*str2)) + str1++, str2++; + return (toupper(*str1) - toupper(*str2)); +} + +#endif + +#ifndef HAVE_STRNCASECMP +/* + * Compare two strings, case-insensitive, stopping after "n" chars. + */ +int +Nu_strncasecmp(const char *str1, const char *str2, size_t n) +{ + while (n && *str1 && *str2 && toupper(*str1) == toupper(*str2)) + str1++, str2++, n--; + + if (n) + return (toupper(*str1) - toupper(*str2)); + else + return 0; /* no mismatch in first n chars */ +} +#endif + diff --git a/nufxlib/MiscStuff.h b/nufxlib/MiscStuff.h new file mode 100644 index 0000000..2e8a376 --- /dev/null +++ b/nufxlib/MiscStuff.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Misc stuff (shared between nufxlib and nulib2). This is a collection + * of miscellaneous types and macros that I find generally useful. + */ +#ifndef __MiscStuff__ +#define __MiscStuff__ + +#define VALGRIND /* assume we're using it */ + +#include "SysDefs.h" + +/* + * Use our versions of functions if they don't exist locally. + */ +#ifndef HAVE_STRERROR +#define strerror Nu_strerror +const char* Nu_strerror(int errnum); +#endif +#ifndef HAVE_MEMMOVE +#define memmove Nu_memmove +void* Nu_memmove(void *dest, const void *src, size_t n); +#endif +#ifndef HAVE_STRTOUL +#define strtoul Nu_strtoul +unsigned long Nu_strtoul(const char *nptr, char **endptr, int base); +#endif +#ifndef HAVE_STRCASECMP +#define strcasecmp Nu_strcasecmp +int Nu_strcasecmp(const char *s1, const char *s2); +#endif +#ifndef HAVE_STRNCASECMP +#define strncasecmp Nu_strncasecmp +int Nu_strncasecmp(const char *s1, const char *s2, size_t n); +#endif + + +/* + * Misc types. + */ + +#include + +#define nil NULL /* I can't seem to stop typing 'nil' now */ + +typedef uchar Boolean; +#define false (0) +#define true (!false) + + +/* + * Handy macros. + */ + +/* compute #of elements in a static array */ +#define NELEM(x) (sizeof(x) / sizeof((x)[0])) + +/* convert single hex digit char to number */ +#define HexDigit(x) ( !isxdigit((int)(x)) ? -1 : \ + (x) <= '9' ? (x) - '0' : toupper(x) +10 - 'A' ) + +/* convert number from 0-15 to hex digit */ +#define HexConv(x) ( ((uint)(x)) <= 15 ? \ + ( (x) <= 9 ? (x) + '0' : (x) -10 + 'A') : -1 ) + + +/* + * Debug stuff. + */ + +/* + * Redefine this if you want assertions to do something other than default. + * Changing the definition of assert is tough, because assert.h redefines + * it every time it's included. On a Solaris 2.7 system I was using, gcc + * pulled assert.h in with some of the system headers, and their definition + * resulted in corrupted core dumps. + */ +#define Assert assert + +#if defined(DEBUG_VERBOSE) + /* quick debug printf macro */ + #define DBUG(args) printf args +#else + #define DBUG(args) ((void)0) +#endif + + +#if defined(NDEBUG) + #define DebugFill(addr, len) ((void)0) + + #define DebugAbort() ((void)0) + +#else + /* when debugging, fill Malloc blocks with junk, unless we're using Purify */ + #if !defined(PURIFY) && !defined(VALGRIND) + #define DebugFill(addr, len) memset(addr, 0xa3, len) + #else + #define DebugFill(addr, len) ((void)0) + #endif + + #define DebugAbort() abort() +#endif + +#define kInvalidPtr ((void*)0xa3a3a3a3) + +#endif /*__MiscStuff__*/ diff --git a/nufxlib/MiscUtils.c b/nufxlib/MiscUtils.c new file mode 100644 index 0000000..9d6a7c6 --- /dev/null +++ b/nufxlib/MiscUtils.c @@ -0,0 +1,382 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Miscellaneous NufxLib utility functions. + */ +#define __MiscUtils_c__ +#include "NufxLibPriv.h" + +/* + * Big fat hairy global. Unfortunately this is unavoidable. + */ +NuCallback gNuGlobalErrorMessageHandler = nil; + + +static const char* kNufxLibName = "nufxlib"; + + +/* + * strerror() equivalent for NufxLib errors. + */ +const char* +Nu_StrError(NuError err) +{ + /* + * BUG: this should be set up as per-thread storage in an MT environment. + * I would be more inclined to worry about this if I was expecting + * it to be used. So long as valid values are passed in, and the + * switch statement is kept up to date, we should never have cause + * to return this. + * + * An easier solution, should this present a problem for someone, would + * be to have the function return nil or "unknown error" when the + * error value isn't recognized. I'd recommend leaving it as-is for + * debug builds, though, as it's helpful to know *which* error is not + * recognized. + */ + static char defaultMsg[32]; + + switch (err) { + case kNuErrNone: + return "(no error)"; + + case kNuErrGeneric: + return "NufxLib generic error"; + case kNuErrInternal: + return "NufxLib internal error"; + case kNuErrUsage: + return "NufxLib usage error"; + case kNuErrSyntax: + return "NufxLib syntax error"; + case kNuErrMalloc: + return "NufxLib malloc error"; + case kNuErrInvalidArg: + return "Invalid arguments to NufxLib"; + case kNuErrBadStruct: + return "Bad NuArchive structure passed to NufxLib"; + case kNuErrBusy: + return "Attempted invalid reentrant call"; + + case kNuErrSkipped: + return "Skipped by user"; + case kNuErrAborted: + return "Processing aborted"; + case kNuErrRename: + return "User wants to rename file"; + + case kNuErrFile: + return "NufxLib trouble with a file"; + case kNuErrFileOpen: + return "NufxLib unable to open file"; + case kNuErrFileClose: + return "NufxLib unable to close file"; + case kNuErrFileRead: + return "NufxLib unable to read file"; + case kNuErrFileWrite: + return "NufxLib unable to write file"; + case kNuErrFileSeek: + return "NufxLib unable to seek file"; + case kNuErrFileExists: + return "File already exists"; + case kNuErrFileNotFound: + return "No such file or directory"; + case kNuErrFileStat: + return "Couldn't get file info"; + case kNuErrFileNotReadable: + return "Read access denied"; + + case kNuErrDirExists: + return "Directory already exists"; + case kNuErrNotDir: + return "Not a directory"; + case kNuErrNotRegularFile: + return "Not a regular file"; + case kNuErrDirCreate: + return "Unable to create directory"; + case kNuErrOpenDir: + return "Unable to open directory"; + case kNuErrReadDir: + return "Unable to read directory"; + case kNuErrFileSetDate: + return "Unable to set file date"; + case kNuErrFileSetAccess: + return "Unable to set file access"; + case kNuErrFileAccessDenied: + return "Access denied"; + + case kNuErrNotNuFX: + return "Input is not a NuFX archive"; + case kNuErrBadMHVersion: + return "Unrecognized Master Header version"; + case kNuErrRecHdrNotFound: + return "Next record not found"; + case kNuErrNoRecords: + return "No records in archive"; + case kNuErrBadRecord: + return "Bad data in record"; + case kNuErrBadMHCRC: + return "Bad Master Header CRC"; + case kNuErrBadRHCRC: + return "Bad Record header CRC"; + case kNuErrBadThreadCRC: + return "Bad Thread header CRC"; + case kNuErrBadDataCRC: + return "Data CRC mismatch"; + + case kNuErrBadFormat: + return "Thread compression format unsupported"; + case kNuErrBadData: + return "Bad data found"; + case kNuErrBufferOverrun: + return "Buffer overrun"; + case kNuErrBufferUnderrun: + return "Buffer underrun"; + case kNuErrOutMax: + return "Output limit exceeded"; + + case kNuErrNotFound: + return "Not found"; + case kNuErrRecordNotFound: + return "Record not found"; + case kNuErrRecIdxNotFound: + return "RecordIdx not found"; + case kNuErrThreadIdxNotFound: + return "ThreadIdx not found"; + case kNuErrThreadIDNotFound: + return "ThreadID not found"; + case kNuErrRecNameNotFound: + return "Record name not found"; + case kNuErrRecordExists: + return "Record already exists"; + + case kNuErrAllDeleted: + return "Tried to delete all files"; + case kNuErrArchiveRO: + return "Archive is in read-only mode"; + case kNuErrModRecChange: + return "Attempt to alter a modified record"; + case kNuErrModThreadChange: + return "Attempt to alter a modified thread"; + case kNuErrThreadAdd: + return "Can't add conflicting threadID"; + case kNuErrNotPreSized: + return "Operation only permitted on pre-sized threads"; + case kNuErrPreSizeOverflow: + return "Data exceeds pre-sized thread size"; + case kNuErrInvalidFilename: + return "Invalid filename"; + + case kNuErrLeadingFssep: + return "Storage name started with fssep char"; + case kNuErrNotNewer: + return "New item wasn't newer than existing"; + case kNuErrDuplicateNotFound: + return "Can only update an existing item"; + case kNuErrDamaged: + return "Original archive may have been damaged"; + + case kNuErrIsBinary2: + return "This is a Binary II archive"; + + case kNuErrUnknownFeature: + return "Unknown feature"; + case kNuErrUnsupFeature: + return "Feature not supported"; + + default: + sprintf(defaultMsg, "(error=%d)", err); + return defaultMsg; + } +} + + +#define kNuHeftyBufSize 256 /* all error messages should fit in this */ +#define kNuExtraGoodies 8 /* leave room for "\0" and other trivial chars*/ + +/* + * Similar to perror(), but takes the error as an argument, and knows + * about NufxLib errors as well as system errors. + * + * Depending on the compiler, "file", "line", and "function" may be nil/zero. + * + * Calling here with "pArchive"==nil is allowed, but should only be done + * if the archive is inaccessible (perhaps because it failed to open). We + * can't invoke the error message callback if the pointer is nil. + */ +void +Nu_ReportError(NuArchive* pArchive, const char* file, int line, + const char* function, Boolean isDebug, NuError err, const char* format, ...) +{ + NuErrorMessage errorMessage; + const char* msg; + va_list args; + char buf[kNuHeftyBufSize]; + int count; + #if !defined(HAVE_SNPRINTF) && defined(SPRINTF_RETURNS_INT) + int cc; + #endif + + Assert(format != nil); + + + va_start(args, format); + + #if defined(HAVE_VSNPRINTF) && defined(VSNPRINTF_DECLARED) + count = vsnprintf(buf, sizeof(buf)-kNuExtraGoodies, format, args); + #else + #ifdef SPRINTF_RETURNS_INT + count = vsprintf(buf, format, args); + #else + vsprintf(buf, format, args); + count = strlen(buf); + #endif + #endif + + va_end(args); + + Assert(count > 0); + if (count < 0) + goto bail; + + /* print the error code data, if any */ + if (err != kNuErrNone) { + /* we know we have room for ": ", because of kNuExtraGoodies */ + strcpy(buf+count, ": "); + count += 2; + + msg = nil; + if (err >= 0) + msg = strerror(err); + if (msg == nil) + msg = Nu_StrError(err); + + #if defined(HAVE_SNPRINTF) && defined(SNPRINTF_DECLARED) + if (msg == nil) + snprintf(buf+count, sizeof(buf) - count, + "(unknown err=%d)", err); + else + snprintf(buf+count, sizeof(buf) - count, "%s", msg); + #else + #ifdef SPRINTF_RETURNS_INT + if (msg == nil) + cc = sprintf(buf+count, "(unknown err=%d)", err); + else + cc = sprintf(buf+count, "%s", msg); + Assert(cc > 0); + count += cc; + #else + if (msg == nil) + sprintf(buf+count, "(unknown err=%d)", err); + else + sprintf(buf+count, "%s", msg); + count += strlen(buf + count); + #endif + #endif + + } + + #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) || \ + !defined(SNPRINTF_DELCARED) || !defined(VSNPRINTF_DECLARED) + /* couldn't do it right, so check for overflow */ + Assert(count <= kNuHeftyBufSize); + #endif + + if ((pArchive != nil && pArchive->messageHandlerFunc == nil) || + (pArchive == nil && gNuGlobalErrorMessageHandler == nil)) + { + if (isDebug) { + fprintf(stderr, "%s: [%s:%d %s] %s\n", kNufxLibName, + file, line, function, buf); + } else { + fprintf(stderr, "%s: ERROR: %s\n", kNufxLibName, buf); + } + } else { + errorMessage.message = buf; + errorMessage.err = err; + errorMessage.isDebug = isDebug; + errorMessage.file = file; + errorMessage.line = line; + errorMessage.function = function; + + if (pArchive == nil) + (void) (*gNuGlobalErrorMessageHandler)(pArchive, &errorMessage); + else + (void) (*pArchive->messageHandlerFunc)(pArchive, &errorMessage); + } + +bail: + return; +} + + +/* + * Memory allocation wrappers. + * + * Under gcc these would be macros, but not all compilers can handle that. + * + * [ It should be possible to use mmalloc instead of malloc. Just tuck the + * mmalloc descriptor into the NuArchive struct. ] + */ + +#ifndef USE_DMALLOC +void* +Nu_Malloc(NuArchive* pArchive, size_t size) +{ + void* _result; + + Assert(size > 0); + _result = malloc(size); + if (_result == nil) { + Nu_ReportError(NU_BLOB, kNuErrMalloc, "malloc(%u) failed", (uint) size); + DebugAbort(); /* leave a core dump if we're built for it */ + } + DebugFill(_result, size); + return _result; +} + +void* +Nu_Calloc(NuArchive* pArchive, size_t size) +{ + void* _cresult = Nu_Malloc(pArchive, size); + memset(_cresult, 0, size); + return _cresult; +} + +void* +Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size) +{ + void* _result; + + Assert(ptr != nil); /* disallow this usage */ + Assert(size > 0); /* disallow this usage */ + _result = realloc(ptr, size); + if (_result == nil) { + Nu_ReportError(NU_BLOB, kNuErrMalloc, "realloc(%u) failed",(uint) size); + DebugAbort(); /* leave a core dump if we're built for it */ + } + return _result; +} + +void +Nu_Free(NuArchive* pArchive, void* ptr) +{ + if (ptr != nil) + free(ptr); +} +#endif + +/* + * If somebody internal wants to set doClose on a buffer DataSource + * (looks like "Rename" does), we need to supply a "free" callback. + */ +NuResult +Nu_InternalFreeCallback(NuArchive* pArchive, void* args) +{ + DBUG(("+++ internal free callback 0x%08lx\n", (long) args)); + Nu_Free(nil, args); + return kNuOK; +} + diff --git a/nufxlib/NOTES.txt b/nufxlib/NOTES.txt new file mode 100644 index 0000000..a68de39 --- /dev/null +++ b/nufxlib/NOTES.txt @@ -0,0 +1,206 @@ +NufxLib NOTES +Last revised: 2000/01/23 + + +The interface is documented in "nufxlibapi.html", available from the +www.nulib.com web site. This discusses some of the internal design that +may be of interest. + +Some familiarity with the NuFX file format is assumed. + + +Read-Write Data Structures +========================== + +For both read-only and read-write files (but not streaming read-only files), +the archive is represented internally as a linked list of Records, each +of which has an array of Threads attached. No attempt is made to +optimize searches by filename, so use of the "replace existing entry when +filenames match" option should be restricted to situations where it is +necessary. Otherwise, O(N^2) behavior can result. + +Modifications, such as deletions, changes to filename threads, and +additions of new records, are queued up in a separate list until a NuFlush +call is issued. The list works much the same way as the temporary file: +when the operation completes, the "new" list becomes the "original" list. +If the operation is aborted, the "new" list is scrubbed, and the "original" +list remains unmodified. + +Just as it is inefficient to write data to the temp file when it's not +necessary to do so, it is inefficient to allocate a complete copy of the +records from the original list if none are changed. As a result, there are +actually two "new" lists, one with a copy of the original record list, and +one with new additions. The "copy" list starts out uninitialized, and +remains that way until one of the entries from the original list is +modified. When that happens, the entire original list is duplicated, and +the changes are made directly to members of the "copy" list. (This is +important for really large archives, like a by-file archive with the +entire contents of a hard drive, where the record index could be several +megabytes in size.) + +It would be more *memory* efficient to simply maintain a list of what +has changed. However, we can't disturb the "original" list in any way or +we lose the ability to roll back quickly if the operation is aborted. +Consequently, we need to create a new list of records that reflects +the state of the new archive, so that when we rename the temp file over +the original, we can simply "rename" the new record list over the original. +Since we're going to need the new list eventually, we might as well create +it as soon as it is needed, and deal with memory allocation failures up +front rather than during the update process. (Some items, such as the +record's file offset in the archive, have to be updated even for records +that aren't themselves changing... which means we potentially need to +modify all existing record structures, so we need a complete copy of the +record list regardless of how little or how much has changed.) + +This also ties into the "modify original archive file directly if possible" +option, which avoids the need for creating and renaming a temp file. If +the only changes are updates to pre-sized records (e.g. renaming a file +inside the archive, or updating a comment), or adding new records onto the +end, there is little risk and possibly a huge efficiency gain in just +modifying the archive in place. If none of the operations caused the +"copy" list to be initialized, then clearly there's no need to write to a +temp file. (It's not actually this simple, because updates to pre-sized +threads are annotated in the "copy" list.) + +One of the goals was to be able to execute a sequence of operations like: + + - open original archive + - read original archive + - modify archive + - flush (success) + - modify archive + - flush (failure, rollback) + - modify archive + - flush (success) + - close archive + +The archive is opened at the start and held open across many operations. +There is never a need to re-read the entire archive. We could avoid the +need to allocate two complete Record lists by requiring that the archive be +re-scanned after changes are aborted; if we did that, we could just modify +the original record list in place, and let the changes become "permanent" +after a successful write. In many ways, though, its cleaner to have two +lists. + +Archives with several thousand entries should be sufficiently rare, and +virtual memory should be sufficiently plentiful, that this won't be a +problem for anyone. Scanning repeatedly through a 15MB archive stored on a +CD-ROM is likely to be very annoying though, so the design makes every +attempt to avoid repeated scans of the archive. And in any event, this +only applies to archive updates. The memory requirements for simple file +extraction are minimal. + +In summary: + + "orig" list has original set of records, and is not disturbed until + the changes are committed. + "copy" list is created on first add/update/delete operation, and + initially contains a complete copy of "orig". + "new" list contains all new additions to the archive, including + new additions that replace existing entries (the existing entry + is deleted from "copy" and then added to "new"). + + +Each Record in the list has a "thread modification" list attached to it. +Any changes to the record header or additions to the thread mod list are +made in the "copy" set; the "original" set remains untouched. The thread +mod list can have the following items in it: + + - delete thread (NuThreadIdx) + - add thread (type, otherSize, format, +contents) + - update pre-sized thread (NuThreadIdx, +contents) + +Contents are specified with a NuDataSource, which allows the application +to indicate that the data is already compressed. This is useful for +copying parts of records between archives without having to expand and +recompress the data. + +Some interactions and concepts that are important to understand: + + When a file is added, the file type information will be placed in the + "new" Record immediately (subject to some restrictions: adding a data + fork always causes the type info to be updated, adding a rsrc fork only + updates the type info if a data fork is not already present). + + Deleting a record results in the Record being removed from the "copy" + list immediately. Future modify operations on that NuRecordIdx will + fail. Future read operations will work just fine until the next + NuFlush is issued, because read operations use the "original" list. + + Deleting all threads from a record results in the record being + deleted, but not until the NuFlush call is issued. It is possible to + delete all the existing threads and then add new ones. + + It is *not* allowed to delete a modified thread, modify a deleted thread, + or delete a record that has been modified. This limitation was added to + keep the system simple. Note this does not mean you can't delete a data + fork and add a data fork; doing so results in operations on two threads + with different NuThreadIdx values. What you can't do is update the + filename thread and then delete it, or vice-versa. (If anyone can think + of a reason why you'd want to rename a file and then delete it with the + same NuFlush call, I'll figure out a way to support it.) + + Updating a filename thread is intercepted, and causes the Record's + filename cache to be updated as well. Adding a filename thread for + records where the filename is stored in the record itself cause the + "in-record" filename to be zeroed. Adding a filename thread to a + record that already has one isn't allowed; nufxlib restricts you to + a single filename thread per record. + + Some actions on an archive are allowed but strongly discouraged. For + example, deleting a filename thread but leaving the data threads behind + is a valid thing to do, but leaves most archivers in a state of mild + confusion. Deleting the data threads but leaving the filename thread is + similarly perplexing. + + You can't call "update thread" on a thread that doesn't yet exist, + even if an "add thread" call has been made. You can, however, call + "add thread" on a newly created Record. + +When a new record is created because of a "create record" call, a filename +thread is created automatically. It is not necessary to explicitly add the +filename. + +Failures encountered while committing changes to a record cause all +operations on that record to be rolled back. If, during a NuFlush, a +file add fails, the user is given the option of aborting the entire +operation or skipping the file in question (and perhaps retrying or other +options as well). Aborting the flush causes a complete rollback. If only +the thread mod operation is canceled, then all thread mods for that record +are ignored. The temp file (or archive file) will have its file pointer +reset to the original start of the record, and if the record already +existed in the original archive, the full original record will be copied +over. This may seem drastic, but it helps ensure that you don't end up +with a record in a partially created state. + +If a failure occurs during an "update in place", it isn't possible to +roll back all changes. If the failure was due to a bug in NufxLib, it +is possible that the archive could be unrecoverably damaged. NufxLib +tries to identify such situations, and will leave the archive open in +read-only mode after rolling back any new file additions. + + +Updating Filenames +================== + +Updating filenames is a small nightmare, because the filename can be +either in the record header or in a filename thread. It's possible, +but illogical, to have a single record with a filename in the record +header and two or more filenames in threads. + +NufxLib will not automatically "fix" broken records, but it will prevent +applications from creating situations that should not exist. + + When reading an archive, NufxLib will use the filename from the + first filename thread found. If no filename threads are found, the + filename from the record header will be used. + + If you add a filename thread to a record that has a filename in the + record header, the header name will be removed. + + If you update a filename thread in a record that has a filename in + the record header, the header name will be left untouched. + + Adding a filename thread is only allowed if no filename thread exists, + or all existing filename threads have been deleted. + diff --git a/prebuilt/NufxLib.h b/nufxlib/NufxLib.h similarity index 97% rename from prebuilt/NufxLib.h rename to nufxlib/NufxLib.h index 5408a0a..0dc4dbc 100644 --- a/prebuilt/NufxLib.h +++ b/nufxlib/NufxLib.h @@ -1,845 +1,843 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * External interface (types, defines, and function prototypes). - */ -#ifndef __NufxLib__ -#define __NufxLib__ - -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * NufxLib version number. Compare these values (which represent the - * version against which your application was compiled) to the values - * returned by NuGetVersion (representing the version against which - * your application is statically or dynamically linked). If the major - * number doesn't match exactly, an existing interface has changed and you - * should halt immediately. If the minor number from NuGetVersion is - * less, there may be new interfaces, new features, or bug fixes missing - * upon which your application depends, so you should halt immediately. - * (If the minor number is greater, there are new features, but your - * application will not be affected by them.) - * - * The "bug" version can usually be ignored, since it represents minor - * fixes. Unless, of course, your code depends upon that fix. - */ -#define kNuVersionMajor 2 -#define kNuVersionMinor 2 -#define kNuVersionBug 0 - - -/* - * =========================================================================== - * Types - * =========================================================================== - */ - -/* - * Error values returned from functions. - * - * These are negative so that they don't conflict with system-defined - * errors (like ENOENT). A NuError can hold either. - */ -typedef enum NuError { - kNuErrNone = 0, - - kNuErrGeneric = -1, - kNuErrInternal = -2, - kNuErrUsage = -3, - kNuErrSyntax = -4, - kNuErrMalloc = -5, - kNuErrInvalidArg = -6, - kNuErrBadStruct = -7, - kNuErrUnexpectedNil = -8, - kNuErrBusy = -9, - - kNuErrSkipped = -10, /* processing skipped by request */ - kNuErrAborted = -11, /* processing aborted by request */ - kNuErrRename = -12, /* user wants to rename before extracting */ - - kNuErrFile = -20, - kNuErrFileOpen = -21, - kNuErrFileClose = -22, - kNuErrFileRead = -23, - kNuErrFileWrite = -24, - kNuErrFileSeek = -25, - kNuErrFileExists = -26, /* existed when it shouldn't */ - kNuErrFileNotFound = -27, /* didn't exist when it should have */ - kNuErrFileStat = -28, /* some sort of GetFileInfo failure */ - kNuErrFileNotReadable = -29, /* bad access permissions */ - - kNuErrDirExists = -30, /* dir exists, don't need to create it */ - kNuErrNotDir = -31, /* expected a dir, got a regular file */ - kNuErrNotRegularFile = -32, /* expected regular file, got weirdness */ - kNuErrDirCreate = -33, /* unable to create a directory */ - kNuErrOpenDir = -34, /* error opening directory */ - kNuErrReadDir = -35, /* error reading directory */ - kNuErrFileSetDate = -36, /* unable to set file date */ - kNuErrFileSetAccess = -37, /* unable to set file access permissions */ - kNuErrFileAccessDenied = -38, /* equivalent to EACCES */ - - kNuErrNotNuFX = -40, /* 'NuFile' missing; not a NuFX archive? */ - kNuErrBadMHVersion = -41, /* bad master header version */ - kNuErrRecHdrNotFound = -42, /* 'NuFX' missing; corrupted archive? */ - kNuErrNoRecords = -43, /* archive doesn't have any records */ - kNuErrBadRecord = -44, /* something about the record looked bad */ - kNuErrBadMHCRC = -45, /* bad master header CRC */ - kNuErrBadRHCRC = -46, /* bad record header CRC */ - kNuErrBadThreadCRC = -47, /* bad thread header CRC */ - kNuErrBadDataCRC = -48, /* bad CRC detected in the data */ - - kNuErrBadFormat = -50, /* compression type not supported */ - kNuErrBadData = -51, /* expansion func didn't like input */ - kNuErrBufferOverrun = -52, /* overflowed a user buffer */ - kNuErrBufferUnderrun = -53, /* underflowed a user buffer */ - kNuErrOutMax = -54, /* output limit exceeded */ - - kNuErrNotFound = -60, /* (generic) search unsuccessful */ - kNuErrRecordNotFound = -61, /* search for specific record failed */ - kNuErrRecIdxNotFound = -62, /* search by NuRecordIdx failed */ - kNuErrThreadIdxNotFound = -63, /* search by NuThreadIdx failed */ - kNuErrThreadIDNotFound = -64, /* search by NuThreadID failed */ - kNuErrRecNameNotFound = -65, /* search by storageName failed */ - kNuErrRecordExists = -66, /* found existing record with same name */ - - kNuErrAllDeleted = -70, /* attempt to delete everything */ - kNuErrArchiveRO = -71, /* archive is open in read-only mode */ - kNuErrModRecChange = -72, /* tried to change modified record */ - kNuErrModThreadChange = -73, /* tried to change modified thread */ - kNuErrThreadAdd = -74, /* adding that thread creates a conflict */ - kNuErrNotPreSized = -75, /* tried to update a non-pre-sized thread */ - kNuErrPreSizeOverflow = -76, /* too much data */ - kNuErrInvalidFilename = -77, /* invalid filename */ - - kNuErrLeadingFssep = -80, /* names in archives must not start w/sep */ - kNuErrNotNewer = -81, /* item same age or older than existing */ - kNuErrDuplicateNotFound = -82, /* "must overwrite" was set, but item DNE */ - kNuErrDamaged = -83, /* original archive may have been damaged */ - - kNuErrIsBinary2 = -90, /* this looks like a Binary II archive */ - - kNuErrUnknownFeature =-100, /* attempt to test unknown feature */ - kNuErrUnsupFeature = -101, /* feature not supported */ -} NuError; - -/* - * Return values from callback functions. - */ -typedef enum NuResult { - kNuOK = 0, - kNuSkip = 1, - kNuAbort = 2, - /*kNuAbortAll = 3,*/ - kNuRetry = 4, - kNuIgnore = 5, - kNuRename = 6, - kNuOverwrite = 7 -} NuResult; - -/* - * NuRecordIdxs are assigned to records in an archive. You may assume that - * the values are unique, but that is all. - */ -typedef unsigned long NuRecordIdx; - -/* - * NuThreadIdxs are assigned to threads within a record. Again, you may - * assume that the values are unique within a record, but that is all. - */ -typedef unsigned long NuThreadIdx; - -/* - * Thread ID, a combination of thread_class and thread_kind. Standard - * values have explicit identifiers. - */ -typedef unsigned long NuThreadID; -#define NuMakeThreadID(class, kind) /* construct a NuThreadID */ \ - ((unsigned long)(class) << 16 | (unsigned long)(kind)) -#define NuGetThreadID(pThread) /* pull NuThreadID out of NuThread */ \ - (NuMakeThreadID((pThread)->thThreadClass, (pThread)->thThreadKind)) -#define NuThreadIDGetClass(threadID) /* get threadClass from NuThreadID */ \ - ((unsigned short) ((unsigned long)(threadID) >> 16)) -#define NuThreadIDGetKind(threadID) /* get threadKind from NuThreadID */ \ - ((unsigned short) ((threadID) & 0xffff)) -#define kNuThreadClassMessage 0x0000 -#define kNuThreadClassControl 0x0001 -#define kNuThreadClassData 0x0002 -#define kNuThreadClassFilename 0x0003 -#define kNuThreadIDOldComment NuMakeThreadID(kNuThreadClassMessage, 0x0000) -#define kNuThreadIDComment NuMakeThreadID(kNuThreadClassMessage, 0x0001) -#define kNuThreadIDIcon NuMakeThreadID(kNuThreadClassMessage, 0x0002) -#define kNuThreadIDMkdir NuMakeThreadID(kNuThreadClassControl, 0x0000) -#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, 0x0000) -#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, 0x0001) -#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, 0x0002) -#define kNuThreadIDFilename NuMakeThreadID(kNuThreadClassFilename, 0x0000) -#define kNuThreadIDWildcard NuMakeThreadID(0xffff, 0xffff) - -/* enumerate the possible values for thThreadFormat */ -typedef enum NuThreadFormat { - kNuThreadFormatUncompressed = 0x0000, - kNuThreadFormatHuffmanSQ = 0x0001, - kNuThreadFormatLZW1 = 0x0002, - kNuThreadFormatLZW2 = 0x0003, - kNuThreadFormatLZC12 = 0x0004, - kNuThreadFormatLZC16 = 0x0005, - kNuThreadFormatDeflate = 0x0006, /* NOTE: not in NuFX standard */ - kNuThreadFormatBzip2 = 0x0007, /* NOTE: not in NuFX standard */ -} NuThreadFormat; - - -/* extract the filesystem separator char from the "file_sys_info" field */ -#define NuGetSepFromSysInfo(sysInfo) \ - ((char) ((sysInfo) & 0xff)) -/* return a file_sys_info with a replaced filesystem separator */ -#define NuSetSepInSysInfo(sysInfo, newSep) \ - ((unsigned short) (((sysInfo) & 0xff00) | ((newSep) & 0xff)) ) - -/* GS/OS-defined file system identifiers; sadly, UNIX is not among them */ -typedef enum NuFileSysID { - kNuFileSysUnknown = 0, /* NuFX spec says use this */ - kNuFileSysProDOS = 1, - kNuFileSysDOS33 = 2, - kNuFileSysDOS32 = 3, - kNuFileSysPascal = 4, - kNuFileSysMacHFS = 5, - kNuFileSysMacMFS = 6, - kNuFileSysLisa = 7, - kNuFileSysCPM = 8, - kNuFileSysCharFST = 9, - kNuFileSysMSDOS = 10, - kNuFileSysHighSierra = 11, - kNuFileSysISO9660 = 12, - kNuFileSysAppleShare = 13 -} NuFileSysID; - -/* simplified definition of storage types */ -typedef enum NuStorageType { - kNuStorageUnknown = 0, /* (used by ProDOS for deleted files) */ - kNuStorageSeedling = 1, /* <= 512 bytes */ - kNuStorageSapling = 2, /* < 128KB */ - kNuStorageTree = 3, /* < 16MB */ - kNuStoragePascalVol = 4, /* (embedded pascal volume; rare) */ - kNuStorageExtended = 5, /* forked (any size) */ - kNuStorageDirectory = 13, /* directory */ - kNuStorageSubdirHeader = 14, /* (only used in subdir headers) */ - kNuStorageVolumeHeader = 15, /* (only used in volume dir header) */ -} NuStorageType; - -/* bit flags for NuOpenRW */ -enum { - kNuOpenCreat = 0x0001, - kNuOpenExcl = 0x0002 -}; - - -/* - * The actual NuArchive structure is opaque, and should only be visible - * to the library. We define it here as an ambiguous struct. - */ -typedef struct NuArchive NuArchive; - -/* - * Generic callback prototype. - */ -typedef NuResult (*NuCallback)(NuArchive* pArchive, void* args); - -/* - * Parameters that affect archive operations. - */ -typedef enum NuValueID { - kNuValueInvalid = 0, - kNuValueIgnoreCRC = 1, - kNuValueDataCompression = 2, - kNuValueDiscardWrapper = 3, - kNuValueEOL = 4, - kNuValueConvertExtractedEOL = 5, - kNuValueOnlyUpdateOlder = 6, - kNuValueAllowDuplicates = 7, - kNuValueHandleExisting = 8, - kNuValueModifyOrig = 9, - kNuValueMimicSHK = 10, - kNuValueMaskDataless = 11, - kNuValueStripHighASCII = 12, - kNuValueJunkSkipMax = 13, - kNuValueIgnoreLZW2Len = 14, - kNuValueHandleBadMac = 15 -} NuValueID; -typedef unsigned long NuValue; - -/* - * Enumerated values for things you pass in a NuValue. - */ -enum NuValueValue { - /* for the truly retentive */ - kNuValueFalse = 0, - kNuValueTrue = 1, - - /* for kNuValueDataCompression */ - kNuCompressNone = 10, - kNuCompressSQ = 11, - kNuCompressLZW1 = 12, - kNuCompressLZW2 = 13, - kNuCompressLZC12 = 14, - kNuCompressLZC16 = 15, - kNuCompressDeflate = 16, - kNuCompressBzip2 = 17, - - /* for kNuValueEOL */ - kNuEOLUnknown = 50, - kNuEOLCR = 51, - kNuEOLLF = 52, - kNuEOLCRLF = 53, - - /* for kNuValueConvertExtractedEOL */ - kNuConvertOff = 60, - kNuConvertOn = 61, - kNuConvertAuto = 62, - - /* for kNuValueHandleExisting */ - kNuMaybeOverwrite = 90, - kNuNeverOverwrite = 91, - kNuAlwaysOverwrite = 93, - kNuMustOverwrite = 94 -}; - - -/* - * Pull out archive attributes. - */ -typedef enum NuAttrID { - kNuAttrInvalid = 0, - kNuAttrArchiveType = 1, - kNuAttrNumRecords = 2, - kNuAttrHeaderOffset = 3, - kNuAttrJunkOffset = 4, -} NuAttrID; -typedef unsigned long NuAttr; - -/* - * Archive types. - */ -typedef enum NuArchiveType { - kNuArchiveUnknown, /* .??? */ - kNuArchiveNuFX, /* .SHK (sometimes .SDK) */ - kNuArchiveNuFXInBNY, /* .BXY */ - kNuArchiveNuFXSelfEx, /* .SEA */ - kNuArchiveNuFXSelfExInBNY, /* .BSE */ - - kNuArchiveBNY /* .BNY, .BQY - not supported */ -} NuArchiveType; - - -/* - * Some common values for "locked" and "unlocked". Under ProDOS each bit - * can be set independently, so don't use these defines to *interpret* - * what you see. They're reasonable things to *set* the access field to. - * - * The defined bits are: - * 0x80 'D' destroy enabled - * 0x40 'N' rename enabled - * 0x20 'B' file needs to be backed up - * 0x10 (reserved, must be zero) - * 0x08 (reserved, must be zero) - * 0x04 'I' file is invisible - * 0x02 'W' write enabled - * 0x01 'R' read enabled - */ -#define kNuAccessLocked 0x21 -#define kNuAccessUnlocked 0xe3 - - -/* - * NuFlush result flags. - */ -#define kNuFlushSucceeded (1L) -#define kNuFlushAborted (1L << 1) -#define kNuFlushCorrupted (1L << 2) -#define kNuFlushReadOnly (1L << 3) -#define kNuFlushInaccessible (1L << 4) - - -/* - * =========================================================================== - * NuFX archive defintions - * =========================================================================== - */ - -typedef struct NuThreadMod NuThreadMod; /* dummy def for internal struct */ -typedef union NuDataSource NuDataSource; /* dummy def for internal struct */ -typedef union NuDataSink NuDataSink; /* dummy def for internal struct */ - -/* - * NuFX Date/Time structure; same as TimeRec from IIgs "misctool.h". - */ -typedef struct NuDateTime { - unsigned char second; /* 0-59 */ - unsigned char minute; /* 0-59 */ - unsigned char hour; /* 0-23 */ - unsigned char year; /* year - 1900 */ - unsigned char day; /* 0-30 */ - unsigned char month; /* 0-11 */ - unsigned char extra; /* (must be zero) */ - unsigned char weekDay; /* 1-7 (1=sunday) */ -} NuDateTime; - -/* - * NuFX "thread" definition. - * - * Guaranteed not to have pointers in it. Can be copied with memcpy or - * assignment. - */ -typedef struct NuThread { - /* from the archive */ - unsigned short thThreadClass; - NuThreadFormat thThreadFormat; - unsigned short thThreadKind; - unsigned short thThreadCRC; /* comp or uncomp data; see rec vers */ - unsigned long thThreadEOF; - unsigned long thCompThreadEOF; - - /* extra goodies */ - NuThreadIdx threadIdx; - unsigned long actualThreadEOF; /* disk images might be off */ - long fileOffset; /* fseek offset to data in shk */ - - /* internal use only */ - unsigned short used; /* mark as uninteresting */ -} NuThread; - -/* - * NuFX "record" definition. - * - * (Note to developers: update Nu_AddRecord if this changes.) - */ -#define kNufxIDLen 4 /* len of 'NuFX' with funky MSBs */ -#define kNuReasonableAttribCount 256 -#define kNuReasonableFilenameLen 1024 -#define kNuReasonableTotalThreads 16 -#define kNuMaxRecordVersion 3 /* max we can handle */ -#define kNuOurRecordVersion 3 /* what we write */ -typedef struct NuRecord { - /* version 0+ */ - unsigned char recNufxID[kNufxIDLen]; - unsigned short recHeaderCRC; - unsigned short recAttribCount; - unsigned short recVersionNumber; - unsigned long recTotalThreads; - NuFileSysID recFileSysID; - unsigned short recFileSysInfo; - unsigned long recAccess; - unsigned long recFileType; - unsigned long recExtraType; - unsigned short recStorageType; /* NuStorage*,file_sys_block_size */ - NuDateTime recCreateWhen; - NuDateTime recModWhen; - NuDateTime recArchiveWhen; - - /* option lists only in version 1+ */ - unsigned short recOptionSize; - unsigned char* recOptionList; /* NULL if v0 or recOptionSize==0 */ - - /* data specified by recAttribCount, not accounted for by option list */ - long extraCount; - unsigned char* extraBytes; - - unsigned short recFilenameLength; /* usually zero */ - char* recFilename; /* doubles as disk volume_name */ - - /* extra goodies; "dirtyHeader" does not apply to anything below */ - NuRecordIdx recordIdx; /* session-unique record index */ - char* threadFilename; /* extracted from filename thread */ - char* newFilename; /* memorized during "add file" call */ - const char* filename; /* points at recFilen or threadFilen */ - unsigned long recHeaderLength; /* size of rec hdr, incl thread hdrs */ - unsigned long totalCompLength; /* total len of data in archive file */ - long fakeThreads; /* used by "MaskDataless" */ - int isBadMac; /* malformed "bad mac" header */ - - long fileOffset; /* file offset of record header */ - - /* use provided interface to access this */ - struct NuThread* pThreads; /* ptr to thread array */ - - /* private -- things the application shouldn't look at */ - struct NuRecord* pNext; /* used internally */ - NuThreadMod* pThreadMods; /* used internally */ - short dirtyHeader; /* set in "copy" when hdr fields uptd */ - short dropRecFilename; /* if set, we're dropping this name */ -} NuRecord; - -/* - * NuFX "master header" definition. - * - * The "mhReserved2" entry doesn't appear in my copy of the $e0/8002 File - * Type Note, but as best as I can recall the MH block must be 48 bytes. - */ -#define kNufileIDLen 6 /* length of 'NuFile' with funky MSBs */ -#define kNufileMasterReserved1Len 8 -#define kNufileMasterReserved2Len 6 -#define kNuMaxMHVersion 2 /* max we can handle */ -#define kNuOurMHVersion 2 /* what we write */ -typedef struct NuMasterHeader { - unsigned char mhNufileID[kNufileIDLen]; - unsigned short mhMasterCRC; - unsigned long mhTotalRecords; - NuDateTime mhArchiveCreateWhen; - NuDateTime mhArchiveModWhen; - unsigned short mhMasterVersion; - unsigned char mhReserved1[kNufileMasterReserved1Len]; - unsigned long mhMasterEOF; - unsigned char mhReserved2[kNufileMasterReserved2Len]; - - /* private -- internal use only */ - short isValid; -} NuMasterHeader; - - -/* - * =========================================================================== - * Misc declarations - * =========================================================================== - */ - -/* - * Record attributes that can be changed with NuSetRecordAttr. This is - * a small subset of the full record. - */ -typedef struct NuRecordAttr { - NuFileSysID fileSysID; - /*unsigned short fileSysInfo;*/ - unsigned long access; - unsigned long fileType; - unsigned long extraType; - NuDateTime createWhen; - NuDateTime modWhen; - NuDateTime archiveWhen; -} NuRecordAttr; - -/* - * Some additional details about a file. - */ -typedef struct NuFileDetails { - /* used during AddFile call */ - NuThreadID threadID; /* data, rsrc, disk img? */ - const char* origName; - - /* these go straight into the NuRecord */ - const char* storageName; - NuFileSysID fileSysID; - unsigned short fileSysInfo; - unsigned long access; - unsigned long fileType; - unsigned long extraType; - unsigned short storageType; /* use Unknown, or disk block size */ - NuDateTime createWhen; - NuDateTime modWhen; - NuDateTime archiveWhen; -} NuFileDetails; - - -/* - * Passed into the SelectionFilter callback. - */ -typedef struct NuSelectionProposal { - const NuRecord* pRecord; - const NuThread* pThread; -} NuSelectionProposal; - -/* - * Passed into the OutputPathnameFilter callback. - */ -typedef struct NuPathnameProposal { - const char* pathname; - char filenameSeparator; - const NuRecord* pRecord; - const NuThread* pThread; - - const char* newPathname; - unsigned char newFilenameSeparator; - /*NuThreadID newStorage;*/ - NuDataSink* newDataSink; -} NuPathnameProposal; - - -/* used by error handler and progress updater to indicate what we're doing */ -typedef enum NuOperation { - kNuOpUnknown = 0, - kNuOpAdd, - kNuOpExtract, - kNuOpTest, - kNuOpDelete, /* not used for progress updates */ - kNuOpContents /* not used for progress updates */ -} NuOperation; - -/* state of progress when adding or extracting */ -typedef enum NuProgressState { - kNuProgressPreparing, /* not started yet */ - kNuProgressOpening, /* opening files */ - - kNuProgressAnalyzing, /* analyzing data */ - kNuProgressCompressing, /* compressing data */ - kNuProgressStoring, /* storing (no compression) data */ - kNuProgressExpanding, /* expanding data */ - kNuProgressCopying, /* copying data (in or out) */ - - kNuProgressDone, /* all done, success */ - kNuProgressSkipped, /* all done, we skipped this one */ - kNuProgressAborted, /* all done, user cancelled the operation */ - kNuProgressFailed /* all done, failure */ -} NuProgressState; - -/* - * Passed into the ProgressUpdater callback. - * - * [ Thought for the day: add an optional flag that causes us to only - * call the progressFunc when the "percentComplete" changes by more - * than a specified amount. ] - */ -typedef struct NuProgressData { - /* what are we doing */ - NuOperation operation; - /* what specifically are we doing */ - NuProgressState state; - /* how far along are we */ - short percentComplete; /* 0-100 */ - - /* original pathname (in archive for expand, on disk for compress) */ - const char* origPathname; - /* processed pathname (PathnameFilter for expand, in-record for compress) */ - const char* pathname; - /* basename of "pathname" */ - const char* filename; - /* pointer to the record we're expanding from */ - const NuRecord* pRecord; - - unsigned long uncompressedLength; /* size of uncompressed data */ - unsigned long uncompressedProgress; /* #of bytes in/out */ - - struct { - NuThreadFormat threadFormat; /* compression being applied */ - } compress; - - struct { - unsigned long totalCompressedLength; /* all "data" threads */ - unsigned long totalUncompressedLength; - - /*unsigned long compressedLength; * size of compressed data */ - /*unsigned long compressedProgress; * #of compressed bytes in/out*/ - const NuThread* pThread; /* thread we're working on */ - NuValue convertEOL; /* set if LF/CR conv is on */ - } expand; - - /* pay no attention */ - NuCallback progressFunc; -} NuProgressData; - -/* - * Passed into the ErrorHandler callback. - */ -typedef struct NuErrorStatus { - NuOperation operation; /* were we adding, extracting, ?? */ - NuError err; /* library error code */ - int sysErr; /* system error code, if applicable */ - const char* message; /* (optional) message to user */ - const NuRecord* pRecord; /* relevant record, if any */ - const char* pathname; /* problematic pathname, if any */ - const char* origPathname; /* original pathname, if any */ - char filenameSeparator; /* fssep for pathname, if any */ - /*char origArchiveTouched;*/ - - char canAbort; /* give option to abort */ - /*char canAbortAll;*/ /* abort + discard all recent changes */ - char canRetry; /* give option to retry same op */ - char canIgnore; /* give option to ignore error */ - char canSkip; /* give option to skip this file/rec */ - char canRename; /* give option to rename file */ - char canOverwrite; /* give option to overwrite file */ -} NuErrorStatus; - -/* - * Error message callback gets one of these. - */ -typedef struct NuErrorMessage { - const char* message; /* the message itself */ - NuError err; /* relevant error code (may be none) */ - short isDebug; /* set for debug-only messages */ - - /* these identify where the message originated if lib built w/debug set */ - const char* file; /* source file */ - int line; /* line number */ - const char* function; /* function name (might be nil) */ -} NuErrorMessage; - - -/* - * Options for the NuTestFeature function. - */ -typedef enum NuFeature { - kNuFeatureUnknown = 0, - - kNuFeatureCompressSQ = 1, /* kNuThreadFormatHuffmanSQ */ - kNuFeatureCompressLZW = 2, /* kNuThreadFormatLZW1 and LZW2 */ - kNuFeatureCompressLZC = 3, /* kNuThreadFormatLZC12 and LZC16 */ - kNuFeatureCompressDeflate = 4, /* kNuThreadFormatDeflate */ - kNuFeatureCompressBzip2 = 5, /* kNuThreadFormatBzip2 */ -} NuFeature; - - -/* - * =========================================================================== - * Function prototypes - * =========================================================================== - */ - -/* - * Win32 dll magic. - */ -#if defined(_WIN32) -# include -# if defined(NUFXLIB_EXPORTS) - /* building the NufxLib DLL */ -# define NUFXLIB_API __declspec(dllexport) -# elif defined (NUFXLIB_DLL) - /* building to link against the NufxLib DLL */ -# define NUFXLIB_API __declspec(dllimport) -# else - /* using static libs */ -# define NUFXLIB_API -# endif -#else - /* not using Win32... hooray! */ -# define NUFXLIB_API -#endif - -/* streaming and non-streaming read-only interfaces */ -NUFXLIB_API NuError NuStreamOpenRO(FILE* infp, NuArchive** ppArchive); -NUFXLIB_API NuError NuContents(NuArchive* pArchive, NuCallback contentFunc); -NUFXLIB_API NuError NuExtract(NuArchive* pArchive); -NUFXLIB_API NuError NuTest(NuArchive* pArchive); - -/* strictly non-streaming read-only interfaces */ -NUFXLIB_API NuError NuOpenRO(const char* archivePathname,NuArchive** ppArchive); -NUFXLIB_API NuError NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx); -NUFXLIB_API NuError NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSink* pDataSink); -NUFXLIB_API NuError NuTestRecord(NuArchive* pArchive, NuRecordIdx recordIdx); -NUFXLIB_API NuError NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecord** ppRecord); -NUFXLIB_API NuError NuGetRecordIdxByName(NuArchive* pArchive, const char* name, - NuRecordIdx* pRecordIdx); -NUFXLIB_API NuError NuGetRecordIdxByPosition(NuArchive* pArchive, - unsigned long position, NuRecordIdx* pRecordIdx); - -/* read/write interfaces */ -NUFXLIB_API NuError NuOpenRW(const char* archivePathname, - const char* tempPathname, unsigned long flags, - NuArchive** ppArchive); -NUFXLIB_API NuError NuFlush(NuArchive* pArchive, long* pStatusFlags); -NUFXLIB_API NuError NuAddRecord(NuArchive* pArchive, - const NuFileDetails* pFileDetails, NuRecordIdx* pRecordIdx); -NUFXLIB_API NuError NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx, - NuThreadID threadID, NuDataSource* pDataSource, - NuThreadIdx* pThreadIdx); -NUFXLIB_API NuError NuAddFile(NuArchive* pArchive, const char* pathname, - const NuFileDetails* pFileDetails, short fromRsrcFork, - NuRecordIdx* pRecordIdx); -NUFXLIB_API NuError NuRename(NuArchive* pArchive, NuRecordIdx recordIdx, - const char* pathname, char fssep); -NUFXLIB_API NuError NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecordAttr* pRecordAttr); -NUFXLIB_API NuError NuUpdatePresizedThread(NuArchive* pArchive, - NuThreadIdx threadIdx, NuDataSource* pDataSource, long* pMaxLen); -NUFXLIB_API NuError NuDelete(NuArchive* pArchive); -NUFXLIB_API NuError NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx); -NUFXLIB_API NuError NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx); - -/* general interfaces */ -NUFXLIB_API NuError NuClose(NuArchive* pArchive); -NUFXLIB_API NuError NuAbort(NuArchive* pArchive); -NUFXLIB_API NuError NuGetMasterHeader(NuArchive* pArchive, - const NuMasterHeader** ppMasterHeader); -NUFXLIB_API NuError NuGetExtraData(NuArchive* pArchive, void** ppData); -NUFXLIB_API NuError NuSetExtraData(NuArchive* pArchive, void* pData); -NUFXLIB_API NuError NuGetValue(NuArchive* pArchive, NuValueID ident, - NuValue* pValue); -NUFXLIB_API NuError NuSetValue(NuArchive* pArchive, NuValueID ident, - NuValue value); -NUFXLIB_API NuError NuGetAttr(NuArchive* pArchive, NuAttrID ident, - NuAttr* pAttr); -NUFXLIB_API NuError NuDebugDumpArchive(NuArchive* pArchive); - -/* sources and sinks */ -NUFXLIB_API NuError NuCreateDataSourceForFile(NuThreadFormat threadFormat, - unsigned long otherLen, const char* pathname, - short isFromRsrcFork, NuDataSource** ppDataSource); -NUFXLIB_API NuError NuCreateDataSourceForFP(NuThreadFormat threadFormat, - unsigned long otherLen, FILE* fp, long offset, long length, - NuCallback closeFunc, NuDataSource** ppDataSource); -NUFXLIB_API NuError NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, - unsigned long otherLen, const unsigned char* buffer, long offset, - long length, NuCallback freeFunc, NuDataSource** ppDataSource); -NUFXLIB_API NuError NuFreeDataSource(NuDataSource* pDataSource); -NUFXLIB_API NuError NuDataSourceSetRawCrc(NuDataSource* pDataSource, - unsigned short crc); -NUFXLIB_API NuError NuCreateDataSinkForFile(short doExpand, NuValue convertEOL, - const char* pathname, char fssep, NuDataSink** ppDataSink); -NUFXLIB_API NuError NuCreateDataSinkForFP(short doExpand, NuValue convertEOL, - FILE* fp, NuDataSink** ppDataSink); -NUFXLIB_API NuError NuCreateDataSinkForBuffer(short doExpand, - NuValue convertEOL, unsigned char* buffer, unsigned long bufLen, - NuDataSink** ppDataSink); -NUFXLIB_API NuError NuFreeDataSink(NuDataSink* pDataSink); -NUFXLIB_API NuError NuDataSinkGetOutCount(NuDataSink* pDataSink, - unsigned long* pOutCount); - -/* miscellaneous non-archive operations */ -NUFXLIB_API NuError NuGetVersion(long* pMajorVersion, long* pMinorVersion, - long* pBugVersion, const char** ppBuildDate, - const char** ppBuildFlags); -NUFXLIB_API const char* NuStrError(NuError err); -NUFXLIB_API NuError NuTestFeature(NuFeature feature); -NUFXLIB_API void NuRecordCopyAttr(NuRecordAttr* pRecordAttr, - const NuRecord* pRecord); -NUFXLIB_API NuError NuRecordCopyThreads(const NuRecord* pRecord, - NuThread** ppThreads); -NUFXLIB_API unsigned long NuRecordGetNumThreads(const NuRecord* pRecord); -NUFXLIB_API const NuThread* NuThreadGetByIdx(const NuThread* pThread, long idx); -NUFXLIB_API short NuIsPresizedThreadID(NuThreadID threadID); - -#define NuGetThread(pRecord, idx) ( (const NuThread*) \ - ((unsigned long) (idx) < (unsigned long) (pRecord)->recTotalThreads ? \ - &(pRecord)->pThreads[(idx)] : NULL) \ - ) - - -/* callback setters */ -#define kNuInvalidCallback ((NuCallback) 1) -NUFXLIB_API NuCallback NuSetSelectionFilter(NuArchive* pArchive, - NuCallback filterFunc); -NUFXLIB_API NuCallback NuSetOutputPathnameFilter(NuArchive* pArchive, - NuCallback filterFunc); -NUFXLIB_API NuCallback NuSetProgressUpdater(NuArchive* pArchive, - NuCallback updateFunc); -NUFXLIB_API NuCallback NuSetFreeHandler(NuArchive* pArchive, - NuCallback freeFunc); -NUFXLIB_API NuCallback NuSetErrorHandler(NuArchive* pArchive, - NuCallback errorFunc); -NUFXLIB_API NuCallback NuSetErrorMessageHandler(NuArchive* pArchive, - NuCallback messageHandlerFunc); -NUFXLIB_API NuCallback NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc); - - -#ifdef __cplusplus -} -#endif - -#endif /*__NufxLib__*/ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * External interface (types, defines, and function prototypes). + */ +#ifndef __NufxLib__ +#define __NufxLib__ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * NufxLib version number. Compare these values (which represent the + * version against which your application was compiled) to the values + * returned by NuGetVersion (representing the version against which + * your application is statically or dynamically linked). If the major + * number doesn't match exactly, an existing interface has changed and you + * should halt immediately. If the minor number from NuGetVersion is + * less, there may be new interfaces, new features, or bug fixes missing + * upon which your application depends, so you should halt immediately. + * (If the minor number is greater, there are new features, but your + * application will not be affected by them.) + * + * The "bug" version can usually be ignored, since it represents minor + * fixes. Unless, of course, your code depends upon that fix. + */ +#define kNuVersionMajor 2 +#define kNuVersionMinor 2 +#define kNuVersionBug 2 + + +/* + * =========================================================================== + * Types + * =========================================================================== + */ + +/* + * Error values returned from functions. + * + * These are negative so that they don't conflict with system-defined + * errors (like ENOENT). A NuError can hold either. + */ +typedef enum NuError { + kNuErrNone = 0, + + kNuErrGeneric = -1, + kNuErrInternal = -2, + kNuErrUsage = -3, + kNuErrSyntax = -4, + kNuErrMalloc = -5, + kNuErrInvalidArg = -6, + kNuErrBadStruct = -7, + kNuErrUnexpectedNil = -8, + kNuErrBusy = -9, + + kNuErrSkipped = -10, /* processing skipped by request */ + kNuErrAborted = -11, /* processing aborted by request */ + kNuErrRename = -12, /* user wants to rename before extracting */ + + kNuErrFile = -20, + kNuErrFileOpen = -21, + kNuErrFileClose = -22, + kNuErrFileRead = -23, + kNuErrFileWrite = -24, + kNuErrFileSeek = -25, + kNuErrFileExists = -26, /* existed when it shouldn't */ + kNuErrFileNotFound = -27, /* didn't exist when it should have */ + kNuErrFileStat = -28, /* some sort of GetFileInfo failure */ + kNuErrFileNotReadable = -29, /* bad access permissions */ + + kNuErrDirExists = -30, /* dir exists, don't need to create it */ + kNuErrNotDir = -31, /* expected a dir, got a regular file */ + kNuErrNotRegularFile = -32, /* expected regular file, got weirdness */ + kNuErrDirCreate = -33, /* unable to create a directory */ + kNuErrOpenDir = -34, /* error opening directory */ + kNuErrReadDir = -35, /* error reading directory */ + kNuErrFileSetDate = -36, /* unable to set file date */ + kNuErrFileSetAccess = -37, /* unable to set file access permissions */ + kNuErrFileAccessDenied = -38, /* equivalent to EACCES */ + + kNuErrNotNuFX = -40, /* 'NuFile' missing; not a NuFX archive? */ + kNuErrBadMHVersion = -41, /* bad master header version */ + kNuErrRecHdrNotFound = -42, /* 'NuFX' missing; corrupted archive? */ + kNuErrNoRecords = -43, /* archive doesn't have any records */ + kNuErrBadRecord = -44, /* something about the record looked bad */ + kNuErrBadMHCRC = -45, /* bad master header CRC */ + kNuErrBadRHCRC = -46, /* bad record header CRC */ + kNuErrBadThreadCRC = -47, /* bad thread header CRC */ + kNuErrBadDataCRC = -48, /* bad CRC detected in the data */ + + kNuErrBadFormat = -50, /* compression type not supported */ + kNuErrBadData = -51, /* expansion func didn't like input */ + kNuErrBufferOverrun = -52, /* overflowed a user buffer */ + kNuErrBufferUnderrun = -53, /* underflowed a user buffer */ + kNuErrOutMax = -54, /* output limit exceeded */ + + kNuErrNotFound = -60, /* (generic) search unsuccessful */ + kNuErrRecordNotFound = -61, /* search for specific record failed */ + kNuErrRecIdxNotFound = -62, /* search by NuRecordIdx failed */ + kNuErrThreadIdxNotFound = -63, /* search by NuThreadIdx failed */ + kNuErrThreadIDNotFound = -64, /* search by NuThreadID failed */ + kNuErrRecNameNotFound = -65, /* search by storageName failed */ + kNuErrRecordExists = -66, /* found existing record with same name */ + + kNuErrAllDeleted = -70, /* attempt to delete everything */ + kNuErrArchiveRO = -71, /* archive is open in read-only mode */ + kNuErrModRecChange = -72, /* tried to change modified record */ + kNuErrModThreadChange = -73, /* tried to change modified thread */ + kNuErrThreadAdd = -74, /* adding that thread creates a conflict */ + kNuErrNotPreSized = -75, /* tried to update a non-pre-sized thread */ + kNuErrPreSizeOverflow = -76, /* too much data */ + kNuErrInvalidFilename = -77, /* invalid filename */ + + kNuErrLeadingFssep = -80, /* names in archives must not start w/sep */ + kNuErrNotNewer = -81, /* item same age or older than existing */ + kNuErrDuplicateNotFound = -82, /* "must overwrite" was set, but item DNE */ + kNuErrDamaged = -83, /* original archive may have been damaged */ + + kNuErrIsBinary2 = -90, /* this looks like a Binary II archive */ + + kNuErrUnknownFeature =-100, /* attempt to test unknown feature */ + kNuErrUnsupFeature = -101, /* feature not supported */ +} NuError; + +/* + * Return values from callback functions. + */ +typedef enum NuResult { + kNuOK = 0, + kNuSkip = 1, + kNuAbort = 2, + /*kNuAbortAll = 3,*/ + kNuRetry = 4, + kNuIgnore = 5, + kNuRename = 6, + kNuOverwrite = 7 +} NuResult; + +/* + * NuRecordIdxs are assigned to records in an archive. You may assume that + * the values are unique, but that is all. + */ +typedef unsigned long NuRecordIdx; + +/* + * NuThreadIdxs are assigned to threads within a record. Again, you may + * assume that the values are unique within a record, but that is all. + */ +typedef unsigned long NuThreadIdx; + +/* + * Thread ID, a combination of thread_class and thread_kind. Standard + * values have explicit identifiers. + */ +typedef unsigned long NuThreadID; +#define NuMakeThreadID(class, kind) /* construct a NuThreadID */ \ + ((unsigned long)(class) << 16 | (unsigned long)(kind)) +#define NuGetThreadID(pThread) /* pull NuThreadID out of NuThread */ \ + (NuMakeThreadID((pThread)->thThreadClass, (pThread)->thThreadKind)) +#define NuThreadIDGetClass(threadID) /* get threadClass from NuThreadID */ \ + ((unsigned short) ((unsigned long)(threadID) >> 16)) +#define NuThreadIDGetKind(threadID) /* get threadKind from NuThreadID */ \ + ((unsigned short) ((threadID) & 0xffff)) +#define kNuThreadClassMessage 0x0000 +#define kNuThreadClassControl 0x0001 +#define kNuThreadClassData 0x0002 +#define kNuThreadClassFilename 0x0003 +#define kNuThreadIDOldComment NuMakeThreadID(kNuThreadClassMessage, 0x0000) +#define kNuThreadIDComment NuMakeThreadID(kNuThreadClassMessage, 0x0001) +#define kNuThreadIDIcon NuMakeThreadID(kNuThreadClassMessage, 0x0002) +#define kNuThreadIDMkdir NuMakeThreadID(kNuThreadClassControl, 0x0000) +#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, 0x0000) +#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, 0x0001) +#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, 0x0002) +#define kNuThreadIDFilename NuMakeThreadID(kNuThreadClassFilename, 0x0000) +#define kNuThreadIDWildcard NuMakeThreadID(0xffff, 0xffff) + +/* enumerate the possible values for thThreadFormat */ +typedef enum NuThreadFormat { + kNuThreadFormatUncompressed = 0x0000, + kNuThreadFormatHuffmanSQ = 0x0001, + kNuThreadFormatLZW1 = 0x0002, + kNuThreadFormatLZW2 = 0x0003, + kNuThreadFormatLZC12 = 0x0004, + kNuThreadFormatLZC16 = 0x0005, + kNuThreadFormatDeflate = 0x0006, /* NOTE: not in NuFX standard */ + kNuThreadFormatBzip2 = 0x0007, /* NOTE: not in NuFX standard */ +} NuThreadFormat; + + +/* extract the filesystem separator char from the "file_sys_info" field */ +#define NuGetSepFromSysInfo(sysInfo) \ + ((char) ((sysInfo) & 0xff)) +/* return a file_sys_info with a replaced filesystem separator */ +#define NuSetSepInSysInfo(sysInfo, newSep) \ + ((unsigned short) (((sysInfo) & 0xff00) | ((newSep) & 0xff)) ) + +/* GS/OS-defined file system identifiers; sadly, UNIX is not among them */ +typedef enum NuFileSysID { + kNuFileSysUnknown = 0, /* NuFX spec says use this */ + kNuFileSysProDOS = 1, + kNuFileSysDOS33 = 2, + kNuFileSysDOS32 = 3, + kNuFileSysPascal = 4, + kNuFileSysMacHFS = 5, + kNuFileSysMacMFS = 6, + kNuFileSysLisa = 7, + kNuFileSysCPM = 8, + kNuFileSysCharFST = 9, + kNuFileSysMSDOS = 10, + kNuFileSysHighSierra = 11, + kNuFileSysISO9660 = 12, + kNuFileSysAppleShare = 13 +} NuFileSysID; + +/* simplified definition of storage types */ +typedef enum NuStorageType { + kNuStorageUnknown = 0, /* (used by ProDOS for deleted files) */ + kNuStorageSeedling = 1, /* <= 512 bytes */ + kNuStorageSapling = 2, /* < 128KB */ + kNuStorageTree = 3, /* < 16MB */ + kNuStoragePascalVol = 4, /* (embedded pascal volume; rare) */ + kNuStorageExtended = 5, /* forked (any size) */ + kNuStorageDirectory = 13, /* directory */ + kNuStorageSubdirHeader = 14, /* (only used in subdir headers) */ + kNuStorageVolumeHeader = 15, /* (only used in volume dir header) */ +} NuStorageType; + +/* bit flags for NuOpenRW */ +enum { + kNuOpenCreat = 0x0001, + kNuOpenExcl = 0x0002 +}; + + +/* + * The actual NuArchive structure is opaque, and should only be visible + * to the library. We define it here as an ambiguous struct. + */ +typedef struct NuArchive NuArchive; + +/* + * Generic callback prototype. + */ +typedef NuResult (*NuCallback)(NuArchive* pArchive, void* args); + +/* + * Parameters that affect archive operations. + */ +typedef enum NuValueID { + kNuValueInvalid = 0, + kNuValueIgnoreCRC = 1, + kNuValueDataCompression = 2, + kNuValueDiscardWrapper = 3, + kNuValueEOL = 4, + kNuValueConvertExtractedEOL = 5, + kNuValueOnlyUpdateOlder = 6, + kNuValueAllowDuplicates = 7, + kNuValueHandleExisting = 8, + kNuValueModifyOrig = 9, + kNuValueMimicSHK = 10, + kNuValueMaskDataless = 11, + kNuValueStripHighASCII = 12, + kNuValueJunkSkipMax = 13, + kNuValueIgnoreLZW2Len = 14, + kNuValueHandleBadMac = 15 +} NuValueID; +typedef unsigned long NuValue; + +/* + * Enumerated values for things you pass in a NuValue. + */ +enum NuValueValue { + /* for the truly retentive */ + kNuValueFalse = 0, + kNuValueTrue = 1, + + /* for kNuValueDataCompression */ + kNuCompressNone = 10, + kNuCompressSQ = 11, + kNuCompressLZW1 = 12, + kNuCompressLZW2 = 13, + kNuCompressLZC12 = 14, + kNuCompressLZC16 = 15, + kNuCompressDeflate = 16, + kNuCompressBzip2 = 17, + + /* for kNuValueEOL */ + kNuEOLUnknown = 50, + kNuEOLCR = 51, + kNuEOLLF = 52, + kNuEOLCRLF = 53, + + /* for kNuValueConvertExtractedEOL */ + kNuConvertOff = 60, + kNuConvertOn = 61, + kNuConvertAuto = 62, + + /* for kNuValueHandleExisting */ + kNuMaybeOverwrite = 90, + kNuNeverOverwrite = 91, + kNuAlwaysOverwrite = 93, + kNuMustOverwrite = 94 +}; + + +/* + * Pull out archive attributes. + */ +typedef enum NuAttrID { + kNuAttrInvalid = 0, + kNuAttrArchiveType = 1, + kNuAttrNumRecords = 2, + kNuAttrHeaderOffset = 3, + kNuAttrJunkOffset = 4, +} NuAttrID; +typedef unsigned long NuAttr; + +/* + * Archive types. + */ +typedef enum NuArchiveType { + kNuArchiveUnknown, /* .??? */ + kNuArchiveNuFX, /* .SHK (sometimes .SDK) */ + kNuArchiveNuFXInBNY, /* .BXY */ + kNuArchiveNuFXSelfEx, /* .SEA */ + kNuArchiveNuFXSelfExInBNY, /* .BSE */ + + kNuArchiveBNY /* .BNY, .BQY - not supported */ +} NuArchiveType; + + +/* + * Some common values for "locked" and "unlocked". Under ProDOS each bit + * can be set independently, so don't use these defines to *interpret* + * what you see. They're reasonable things to *set* the access field to. + * + * The defined bits are: + * 0x80 'D' destroy enabled + * 0x40 'N' rename enabled + * 0x20 'B' file needs to be backed up + * 0x10 (reserved, must be zero) + * 0x08 (reserved, must be zero) + * 0x04 'I' file is invisible + * 0x02 'W' write enabled + * 0x01 'R' read enabled + */ +#define kNuAccessLocked 0x21 +#define kNuAccessUnlocked 0xe3 + + +/* + * NuFlush result flags. + */ +#define kNuFlushSucceeded (1L) +#define kNuFlushAborted (1L << 1) +#define kNuFlushCorrupted (1L << 2) +#define kNuFlushReadOnly (1L << 3) +#define kNuFlushInaccessible (1L << 4) + + +/* + * =========================================================================== + * NuFX archive defintions + * =========================================================================== + */ + +typedef struct NuThreadMod NuThreadMod; /* dummy def for internal struct */ +typedef union NuDataSource NuDataSource; /* dummy def for internal struct */ +typedef union NuDataSink NuDataSink; /* dummy def for internal struct */ + +/* + * NuFX Date/Time structure; same as TimeRec from IIgs "misctool.h". + */ +typedef struct NuDateTime { + unsigned char second; /* 0-59 */ + unsigned char minute; /* 0-59 */ + unsigned char hour; /* 0-23 */ + unsigned char year; /* year - 1900 */ + unsigned char day; /* 0-30 */ + unsigned char month; /* 0-11 */ + unsigned char extra; /* (must be zero) */ + unsigned char weekDay; /* 1-7 (1=sunday) */ +} NuDateTime; + +/* + * NuFX "thread" definition. + * + * Guaranteed not to have pointers in it. Can be copied with memcpy or + * assignment. + */ +typedef struct NuThread { + /* from the archive */ + unsigned short thThreadClass; + NuThreadFormat thThreadFormat; + unsigned short thThreadKind; + unsigned short thThreadCRC; /* comp or uncomp data; see rec vers */ + unsigned long thThreadEOF; + unsigned long thCompThreadEOF; + + /* extra goodies */ + NuThreadIdx threadIdx; + unsigned long actualThreadEOF; /* disk images might be off */ + long fileOffset; /* fseek offset to data in shk */ + + /* internal use only */ + unsigned short used; /* mark as uninteresting */ +} NuThread; + +/* + * NuFX "record" definition. + * + * (Note to developers: update Nu_AddRecord if this changes.) + */ +#define kNufxIDLen 4 /* len of 'NuFX' with funky MSBs */ +#define kNuReasonableAttribCount 256 +#define kNuReasonableFilenameLen 1024 +#define kNuReasonableTotalThreads 16 +#define kNuMaxRecordVersion 3 /* max we can handle */ +#define kNuOurRecordVersion 3 /* what we write */ +typedef struct NuRecord { + /* version 0+ */ + unsigned char recNufxID[kNufxIDLen]; + unsigned short recHeaderCRC; + unsigned short recAttribCount; + unsigned short recVersionNumber; + unsigned long recTotalThreads; + NuFileSysID recFileSysID; + unsigned short recFileSysInfo; + unsigned long recAccess; + unsigned long recFileType; + unsigned long recExtraType; + unsigned short recStorageType; /* NuStorage*,file_sys_block_size */ + NuDateTime recCreateWhen; + NuDateTime recModWhen; + NuDateTime recArchiveWhen; + + /* option lists only in version 1+ */ + unsigned short recOptionSize; + unsigned char* recOptionList; /* NULL if v0 or recOptionSize==0 */ + + /* data specified by recAttribCount, not accounted for by option list */ + long extraCount; + unsigned char* extraBytes; + + unsigned short recFilenameLength; /* usually zero */ + char* recFilename; /* doubles as disk volume_name */ + + /* extra goodies; "dirtyHeader" does not apply to anything below */ + NuRecordIdx recordIdx; /* session-unique record index */ + char* threadFilename; /* extracted from filename thread */ + char* newFilename; /* memorized during "add file" call */ + const char* filename; /* points at recFilen or threadFilen */ + unsigned long recHeaderLength; /* size of rec hdr, incl thread hdrs */ + unsigned long totalCompLength; /* total len of data in archive file */ + long fakeThreads; /* used by "MaskDataless" */ + int isBadMac; /* malformed "bad mac" header */ + + long fileOffset; /* file offset of record header */ + + /* use provided interface to access this */ + struct NuThread* pThreads; /* ptr to thread array */ + + /* private -- things the application shouldn't look at */ + struct NuRecord* pNext; /* used internally */ + NuThreadMod* pThreadMods; /* used internally */ + short dirtyHeader; /* set in "copy" when hdr fields uptd */ + short dropRecFilename; /* if set, we're dropping this name */ +} NuRecord; + +/* + * NuFX "master header" definition. + * + * The "mhReserved2" entry doesn't appear in my copy of the $e0/8002 File + * Type Note, but as best as I can recall the MH block must be 48 bytes. + */ +#define kNufileIDLen 6 /* length of 'NuFile' with funky MSBs */ +#define kNufileMasterReserved1Len 8 +#define kNufileMasterReserved2Len 6 +#define kNuMaxMHVersion 2 /* max we can handle */ +#define kNuOurMHVersion 2 /* what we write */ +typedef struct NuMasterHeader { + unsigned char mhNufileID[kNufileIDLen]; + unsigned short mhMasterCRC; + unsigned long mhTotalRecords; + NuDateTime mhArchiveCreateWhen; + NuDateTime mhArchiveModWhen; + unsigned short mhMasterVersion; + unsigned char mhReserved1[kNufileMasterReserved1Len]; + unsigned long mhMasterEOF; + unsigned char mhReserved2[kNufileMasterReserved2Len]; + + /* private -- internal use only */ + short isValid; +} NuMasterHeader; + + +/* + * =========================================================================== + * Misc declarations + * =========================================================================== + */ + +/* + * Record attributes that can be changed with NuSetRecordAttr. This is + * a small subset of the full record. + */ +typedef struct NuRecordAttr { + NuFileSysID fileSysID; + /*unsigned short fileSysInfo;*/ + unsigned long access; + unsigned long fileType; + unsigned long extraType; + NuDateTime createWhen; + NuDateTime modWhen; + NuDateTime archiveWhen; +} NuRecordAttr; + +/* + * Some additional details about a file. + */ +typedef struct NuFileDetails { + /* used during AddFile call */ + NuThreadID threadID; /* data, rsrc, disk img? */ + const char* origName; + + /* these go straight into the NuRecord */ + const char* storageName; + NuFileSysID fileSysID; + unsigned short fileSysInfo; + unsigned long access; + unsigned long fileType; + unsigned long extraType; + unsigned short storageType; /* use Unknown, or disk block size */ + NuDateTime createWhen; + NuDateTime modWhen; + NuDateTime archiveWhen; +} NuFileDetails; + + +/* + * Passed into the SelectionFilter callback. + */ +typedef struct NuSelectionProposal { + const NuRecord* pRecord; + const NuThread* pThread; +} NuSelectionProposal; + +/* + * Passed into the OutputPathnameFilter callback. + */ +typedef struct NuPathnameProposal { + const char* pathname; + char filenameSeparator; + const NuRecord* pRecord; + const NuThread* pThread; + + const char* newPathname; + unsigned char newFilenameSeparator; + /*NuThreadID newStorage;*/ + NuDataSink* newDataSink; +} NuPathnameProposal; + + +/* used by error handler and progress updater to indicate what we're doing */ +typedef enum NuOperation { + kNuOpUnknown = 0, + kNuOpAdd, + kNuOpExtract, + kNuOpTest, + kNuOpDelete, /* not used for progress updates */ + kNuOpContents /* not used for progress updates */ +} NuOperation; + +/* state of progress when adding or extracting */ +typedef enum NuProgressState { + kNuProgressPreparing, /* not started yet */ + kNuProgressOpening, /* opening files */ + + kNuProgressAnalyzing, /* analyzing data */ + kNuProgressCompressing, /* compressing data */ + kNuProgressStoring, /* storing (no compression) data */ + kNuProgressExpanding, /* expanding data */ + kNuProgressCopying, /* copying data (in or out) */ + + kNuProgressDone, /* all done, success */ + kNuProgressSkipped, /* all done, we skipped this one */ + kNuProgressAborted, /* all done, user cancelled the operation */ + kNuProgressFailed /* all done, failure */ +} NuProgressState; + +/* + * Passed into the ProgressUpdater callback. + * + * [ Thought for the day: add an optional flag that causes us to only + * call the progressFunc when the "percentComplete" changes by more + * than a specified amount. ] + */ +typedef struct NuProgressData { + /* what are we doing */ + NuOperation operation; + /* what specifically are we doing */ + NuProgressState state; + /* how far along are we */ + short percentComplete; /* 0-100 */ + + /* original pathname (in archive for expand, on disk for compress) */ + const char* origPathname; + /* processed pathname (PathnameFilter for expand, in-record for compress) */ + const char* pathname; + /* basename of "pathname" */ + const char* filename; + /* pointer to the record we're expanding from */ + const NuRecord* pRecord; + + unsigned long uncompressedLength; /* size of uncompressed data */ + unsigned long uncompressedProgress; /* #of bytes in/out */ + + struct { + NuThreadFormat threadFormat; /* compression being applied */ + } compress; + + struct { + unsigned long totalCompressedLength; /* all "data" threads */ + unsigned long totalUncompressedLength; + + /*unsigned long compressedLength; * size of compressed data */ + /*unsigned long compressedProgress; * #of compressed bytes in/out*/ + const NuThread* pThread; /* thread we're working on */ + NuValue convertEOL; /* set if LF/CR conv is on */ + } expand; + + /* pay no attention */ + NuCallback progressFunc; +} NuProgressData; + +/* + * Passed into the ErrorHandler callback. + */ +typedef struct NuErrorStatus { + NuOperation operation; /* were we adding, extracting, ?? */ + NuError err; /* library error code */ + int sysErr; /* system error code, if applicable */ + const char* message; /* (optional) message to user */ + const NuRecord* pRecord; /* relevant record, if any */ + const char* pathname; /* problematic pathname, if any */ + const char* origPathname; /* original pathname, if any */ + char filenameSeparator; /* fssep for pathname, if any */ + /*char origArchiveTouched;*/ + + char canAbort; /* give option to abort */ + /*char canAbortAll;*/ /* abort + discard all recent changes */ + char canRetry; /* give option to retry same op */ + char canIgnore; /* give option to ignore error */ + char canSkip; /* give option to skip this file/rec */ + char canRename; /* give option to rename file */ + char canOverwrite; /* give option to overwrite file */ +} NuErrorStatus; + +/* + * Error message callback gets one of these. + */ +typedef struct NuErrorMessage { + const char* message; /* the message itself */ + NuError err; /* relevant error code (may be none) */ + short isDebug; /* set for debug-only messages */ + + /* these identify where the message originated if lib built w/debug set */ + const char* file; /* source file */ + int line; /* line number */ + const char* function; /* function name (might be nil) */ +} NuErrorMessage; + + +/* + * Options for the NuTestFeature function. + */ +typedef enum NuFeature { + kNuFeatureUnknown = 0, + + kNuFeatureCompressSQ = 1, /* kNuThreadFormatHuffmanSQ */ + kNuFeatureCompressLZW = 2, /* kNuThreadFormatLZW1 and LZW2 */ + kNuFeatureCompressLZC = 3, /* kNuThreadFormatLZC12 and LZC16 */ + kNuFeatureCompressDeflate = 4, /* kNuThreadFormatDeflate */ + kNuFeatureCompressBzip2 = 5, /* kNuThreadFormatBzip2 */ +} NuFeature; + + +/* + * =========================================================================== + * Function prototypes + * =========================================================================== + */ + +/* + * Win32 dll magic. + */ +#if defined(_WIN32) +# include +# if defined(NUFXLIB_EXPORTS) + /* building the NufxLib DLL */ +# define NUFXLIB_API __declspec(dllexport) +# elif defined (NUFXLIB_DLL) + /* building to link against the NufxLib DLL */ +# define NUFXLIB_API __declspec(dllimport) +# else + /* using static libs */ +# define NUFXLIB_API +# endif +#else + /* not using Win32... hooray! */ +# define NUFXLIB_API +#endif + +/* streaming and non-streaming read-only interfaces */ +NUFXLIB_API NuError NuStreamOpenRO(FILE* infp, NuArchive** ppArchive); +NUFXLIB_API NuError NuContents(NuArchive* pArchive, NuCallback contentFunc); +NUFXLIB_API NuError NuExtract(NuArchive* pArchive); +NUFXLIB_API NuError NuTest(NuArchive* pArchive); + +/* strictly non-streaming read-only interfaces */ +NUFXLIB_API NuError NuOpenRO(const char* archivePathname,NuArchive** ppArchive); +NUFXLIB_API NuError NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx); +NUFXLIB_API NuError NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSink* pDataSink); +NUFXLIB_API NuError NuTestRecord(NuArchive* pArchive, NuRecordIdx recordIdx); +NUFXLIB_API NuError NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecord** ppRecord); +NUFXLIB_API NuError NuGetRecordIdxByName(NuArchive* pArchive, const char* name, + NuRecordIdx* pRecordIdx); +NUFXLIB_API NuError NuGetRecordIdxByPosition(NuArchive* pArchive, + unsigned long position, NuRecordIdx* pRecordIdx); + +/* read/write interfaces */ +NUFXLIB_API NuError NuOpenRW(const char* archivePathname, + const char* tempPathname, unsigned long flags, + NuArchive** ppArchive); +NUFXLIB_API NuError NuFlush(NuArchive* pArchive, long* pStatusFlags); +NUFXLIB_API NuError NuAddRecord(NuArchive* pArchive, + const NuFileDetails* pFileDetails, NuRecordIdx* pRecordIdx); +NUFXLIB_API NuError NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx, + NuThreadID threadID, NuDataSource* pDataSource, + NuThreadIdx* pThreadIdx); +NUFXLIB_API NuError NuAddFile(NuArchive* pArchive, const char* pathname, + const NuFileDetails* pFileDetails, short fromRsrcFork, + NuRecordIdx* pRecordIdx); +NUFXLIB_API NuError NuRename(NuArchive* pArchive, NuRecordIdx recordIdx, + const char* pathname, char fssep); +NUFXLIB_API NuError NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecordAttr* pRecordAttr); +NUFXLIB_API NuError NuUpdatePresizedThread(NuArchive* pArchive, + NuThreadIdx threadIdx, NuDataSource* pDataSource, long* pMaxLen); +NUFXLIB_API NuError NuDelete(NuArchive* pArchive); +NUFXLIB_API NuError NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx); +NUFXLIB_API NuError NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx); + +/* general interfaces */ +NUFXLIB_API NuError NuClose(NuArchive* pArchive); +NUFXLIB_API NuError NuAbort(NuArchive* pArchive); +NUFXLIB_API NuError NuGetMasterHeader(NuArchive* pArchive, + const NuMasterHeader** ppMasterHeader); +NUFXLIB_API NuError NuGetExtraData(NuArchive* pArchive, void** ppData); +NUFXLIB_API NuError NuSetExtraData(NuArchive* pArchive, void* pData); +NUFXLIB_API NuError NuGetValue(NuArchive* pArchive, NuValueID ident, + NuValue* pValue); +NUFXLIB_API NuError NuSetValue(NuArchive* pArchive, NuValueID ident, + NuValue value); +NUFXLIB_API NuError NuGetAttr(NuArchive* pArchive, NuAttrID ident, + NuAttr* pAttr); +NUFXLIB_API NuError NuDebugDumpArchive(NuArchive* pArchive); + +/* sources and sinks */ +NUFXLIB_API NuError NuCreateDataSourceForFile(NuThreadFormat threadFormat, + unsigned long otherLen, const char* pathname, + short isFromRsrcFork, NuDataSource** ppDataSource); +NUFXLIB_API NuError NuCreateDataSourceForFP(NuThreadFormat threadFormat, + unsigned long otherLen, FILE* fp, long offset, long length, + NuCallback closeFunc, NuDataSource** ppDataSource); +NUFXLIB_API NuError NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, + unsigned long otherLen, const unsigned char* buffer, long offset, + long length, NuCallback freeFunc, NuDataSource** ppDataSource); +NUFXLIB_API NuError NuFreeDataSource(NuDataSource* pDataSource); +NUFXLIB_API NuError NuDataSourceSetRawCrc(NuDataSource* pDataSource, + unsigned short crc); +NUFXLIB_API NuError NuCreateDataSinkForFile(short doExpand, NuValue convertEOL, + const char* pathname, char fssep, NuDataSink** ppDataSink); +NUFXLIB_API NuError NuCreateDataSinkForFP(short doExpand, NuValue convertEOL, + FILE* fp, NuDataSink** ppDataSink); +NUFXLIB_API NuError NuCreateDataSinkForBuffer(short doExpand, + NuValue convertEOL, unsigned char* buffer, unsigned long bufLen, + NuDataSink** ppDataSink); +NUFXLIB_API NuError NuFreeDataSink(NuDataSink* pDataSink); +NUFXLIB_API NuError NuDataSinkGetOutCount(NuDataSink* pDataSink, + unsigned long* pOutCount); + +/* miscellaneous non-archive operations */ +NUFXLIB_API NuError NuGetVersion(long* pMajorVersion, long* pMinorVersion, + long* pBugVersion, const char** ppBuildDate, + const char** ppBuildFlags); +NUFXLIB_API const char* NuStrError(NuError err); +NUFXLIB_API NuError NuTestFeature(NuFeature feature); +NUFXLIB_API void NuRecordCopyAttr(NuRecordAttr* pRecordAttr, + const NuRecord* pRecord); +NUFXLIB_API NuError NuRecordCopyThreads(const NuRecord* pRecord, + NuThread** ppThreads); +NUFXLIB_API unsigned long NuRecordGetNumThreads(const NuRecord* pRecord); +NUFXLIB_API const NuThread* NuThreadGetByIdx(const NuThread* pThread, long idx); +NUFXLIB_API short NuIsPresizedThreadID(NuThreadID threadID); + +#define NuGetThread(pRecord, idx) ( (const NuThread*) \ + ((unsigned long) (idx) < (unsigned long) (pRecord)->recTotalThreads ? \ + &(pRecord)->pThreads[(idx)] : NULL) \ + ) + + +/* callback setters */ +#define kNuInvalidCallback ((NuCallback) 1) +NUFXLIB_API NuCallback NuSetSelectionFilter(NuArchive* pArchive, + NuCallback filterFunc); +NUFXLIB_API NuCallback NuSetOutputPathnameFilter(NuArchive* pArchive, + NuCallback filterFunc); +NUFXLIB_API NuCallback NuSetProgressUpdater(NuArchive* pArchive, + NuCallback updateFunc); +NUFXLIB_API NuCallback NuSetErrorHandler(NuArchive* pArchive, + NuCallback errorFunc); +NUFXLIB_API NuCallback NuSetErrorMessageHandler(NuArchive* pArchive, + NuCallback messageHandlerFunc); +NUFXLIB_API NuCallback NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc); + + +#ifdef __cplusplus +} +#endif + +#endif /*__NufxLib__*/ diff --git a/nufxlib/NufxLibPriv.h b/nufxlib/NufxLibPriv.h new file mode 100644 index 0000000..3e59030 --- /dev/null +++ b/nufxlib/NufxLibPriv.h @@ -0,0 +1,870 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Global internal declarations and definitions. + */ +#ifndef __NufxLibPriv__ +#define __NufxLibPriv__ + +/* include files that everybody needs */ +#include "SysDefs.h" +#include "NufxLib.h" +#include "MiscStuff.h" + +#ifdef USE_DMALLOC +/* enable with something like "dmalloc -l logfile -i 100 medium" */ +# include "dmalloc.h" +#endif + + +/* + * =========================================================================== + * NuArchive definition + * =========================================================================== + */ + +/* + * Archives can be opened in streaming read-only, non-streaming read-only, + * and non-streaming read-write mode. + */ +typedef enum NuOpenMode { + kNuOpenUnknown, + kNuOpenStreamingRO, + kNuOpenRO, + kNuOpenRW +} NuOpenMode; +#define Nu_IsStreaming(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO) +#define Nu_IsReadOnly(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO ||\ + (pArchive)->openMode == kNuOpenRO) + +#ifdef FOPEN_WANTS_B +# define kNuFileOpenReadOnly "rb" +# define kNuFileOpenReadWrite "r+b" +# define kNuFileOpenWriteTrunc "wb" +# define kNuFileOpenReadWriteCreat "w+b" +#else +# define kNuFileOpenReadOnly "r" +# define kNuFileOpenReadWrite "r+" +# define kNuFileOpenWriteTrunc "w" +# define kNuFileOpenReadWriteCreat "w+" +#endif + + +/* + * Some NuFX and Binary II definitions. + */ +#define kNuMasterHeaderSize 48 /* size of fixed-length master header */ +#define kNuRecordHeaderBaseSize 58 /* size of rec hdr up to variable stuff */ +#define kNuThreadHeaderSize 16 /* size of fixed-length thread header */ +#define kNuDefaultFilenameThreadSize 32 /* default size of filename thred */ +#define kNuDefaultCommentSize 200 /* size of GSHK-mimic comments */ +#define kNuBinary2BlockSize 128 /* size of bxy header and padding */ +#define kNuSEAOffset 0x2ee5 /* fixed(??) offset to data in SEA */ + +#define kNuInitialChunkCRC 0x0000 /* start for CRC in LZW/1 chunk */ +#define kNuInitialThreadCRC 0xffff /* start for CRC in v3 thread header */ + +/* size of general-purpose compression buffer */ +#define kNuGenCompBufSize 32768 + +#define kNuCharLF 0x0a +#define kNuCharCR 0x0d + + +/* + * A list of records. Generally we use one of these for read-only + * archives, and two for read-write. + * + * The "loaded" flag is set when we've made some use of the record set. + * Relying on "numRecords" won't always work; for example, if the "copy" + * record set was initialized from "orig", and then had all of its records + * deleted, you couldn't look at "numRecords" and decide whether it was + * appropriate to use "orig" or not. + */ +typedef struct NuRecordSet { + Boolean loaded; + ulong numRecords; + NuRecord* nuRecordHead; + NuRecord* nuRecordTail; +} NuRecordSet; + +/* + * Archive state. + */ +struct NuArchive { + ulong structMagic; + Boolean busy; + + NuOpenMode openMode; + Boolean newlyCreated; + char* archivePathname; /* pathname or "(stream)" */ + FILE* archiveFp; + NuArchiveType archiveType; + + /* stuff before NuFX; both offsets are from 0, i.e. hdrOff includes junk */ + long junkOffset; /* skip past leading junk */ + long headerOffset; /* adjustment for BXY/SEA/BSE */ + + char* tmpPathname; /* temp file, for writes */ + FILE* tmpFp; + + /* used during initial processing; helps avoid ftell() calls */ + long currentOffset; + + /* setting this changes Extract into Test */ + Boolean testMode; + + /* clumsy way of remembering name used for other fork in forked file */ + const char* lastFileCreated; + /* clumsy way to avoid trying to create the same subdir several times */ + const char* lastDirCreated; + + /* master header from the archive */ + NuMasterHeader masterHeader; /* original */ + NuMasterHeader newMasterHeader; /* working copy during update */ + + /* list of records from archive, plus some extra state */ + NuRecordIdx recordIdxSeed; /* where the NuRecordIdxs start */ + NuRecordIdx nextRecordIdx; /* next record gets this value */ + Boolean haveToc; /* set if we have full TOC */ + NuRecordSet origRecordSet; /* records from archive */ + NuRecordSet copyRecordSet; /* copy of orig, for write ops */ + NuRecordSet newRecordSet; /* newly-added records */ + + /* state for compression functions */ + uchar* compBuf; /* large general-purpose buffer */ + void* lzwCompressState; /* state for LZW/1 and LZW/2 */ + void* lzwExpandState; /* state for LZW/1 and LZW/2 */ + + /* options and attributes that the user can set */ + /* (these can be changed by a callback, so don't cache them internally) */ + void* extraData; /* application-defined pointer */ + + NuValue valAllowDuplicates; /* allow dups when adding? */ + NuValue valConvertExtractedEOL; /* convert EOL during extract? */ + NuValue valDataCompression; /* how to compress when adding? */ + NuValue valDiscardWrapper; /* remove BNY or SEA header? */ + NuValue valEOL; /* EOL value to convert to */ + NuValue valHandleExisting; /* how to deal with existing files*/ + NuValue valIgnoreCRC; /* don't compute or test CRCs */ + NuValue valMaskDataless; /* alter Records w/o data threads */ + NuValue valMimicSHK; /* mimic some ShrinkIt quirks */ + NuValue valModifyOrig; /* modify original arc in place? */ + NuValue valOnlyUpdateOlder; /* modify original arc in place? */ + NuValue valStripHighASCII; /* during EOL conv, strip hi bit? */ + NuValue valJunkSkipMax; /* scan this far for header */ + NuValue valIgnoreLZW2Len; /* don't verify LZW/II len field */ + NuValue valHandleBadMac; /* handle "bad Mac" archives */ + + /* callback functions */ + NuCallback selectionFilterFunc; + NuCallback outputPathnameFunc; + NuCallback progressUpdaterFunc; + NuCallback errorHandlerFunc; + NuCallback messageHandlerFunc; +}; + +#define kNuArchiveStructMagic 0xc0edbabe + +#define kNuDefaultRecordName "UNKNOWN" + + +/* + * =========================================================================== + * ThreadMod definition + * =========================================================================== + */ + +/* operations we can perform on threads in a record */ +typedef enum ThreadModKind { + kNuThreadModUnknown = 0, + + kNuThreadModAdd, + kNuThreadModUpdate, + kNuThreadModDelete +} ThreadModKind; + +/* + * We attach a list of these to records we plan to modify. Care is taken + * to ensure that they don't conflict, e.g. you can't update a thread + * right after you delete it, nor delete one you have modified. + */ +struct NuThreadMod { + union { + ThreadModKind kind; + + struct { + ThreadModKind kind; + Boolean used; + } generic; + + struct { + ThreadModKind kind; + Boolean used; + Boolean isPresized; + NuThreadIdx threadIdx; + NuThreadID threadID; + NuThreadFormat threadFormat; + NuDataSource* pDataSource; + } add; + + struct { + ThreadModKind kind; + Boolean used; + NuThreadIdx threadIdx; + NuDataSource* pDataSource; + } update; + + struct { + ThreadModKind kind; + Boolean used; + NuThreadIdx threadIdx; + NuThreadID threadID; /* used for watching filename threads */ + } delete; + } entry; + + struct NuThreadMod* pNext; +}; + + +/* + * =========================================================================== + * NuFunnel/NuStraw definition + * =========================================================================== + */ + +#define kNuFunnelBufSize 16384 + +/* + * File funnel definition. This is used for writing output to files + * (so we can do things like pipe compressed output through an LF->CR + * converter) and archive files (so we can halt compression when the + * output size exceeds the uncompressed original). [ for various reasons, + * I'm not using this on the archive anymore. ] + * + * Funnels are unidirectional. You write data into them with a + * function call; the top-level action (which is usually compressing or + * expanding data) reads from the input and crams things into the pipe. + * We could fully abstract the concept, and write the compression + * functions so that they operate as a Funnel filter, but it's much + * easier to write block-oriented compression than stream-oriented (and + * more to the point, the ShrinkIt LZW functions are very much + * block-oriented). + */ +typedef struct NuFunnel { + /* data storage */ + uchar* buffer; /* kNuFunnelBufSize worth of storage */ + long bufCount; /* #of bytes in buffer */ + + /* text conversion; if "auto", on first flush we convert to "on" or "off" */ + NuValue convertEOL; /* on/off/auto */ + NuValue convertEOLTo; /* EOL to switch to */ + NuValue convertEOLFrom; /* EOL terminator we think we found */ + Boolean checkStripHighASCII; /* do we want to check for it? */ + Boolean doStripHighASCII; /* strip high ASCII during EOL conv */ + Boolean lastCR; /* was last char a CR? */ + + Boolean isFirstWrite; /* cleared on first write */ + +#if 0 + ulong inCount; /* total #of bytes in the top */ + ulong outCount; /* total #of bytes out the bottom */ + + ulong outMax; /* flag an err when outCount exceeds this */ + Boolean outMaxExceeded; /* in fact, it's this flag */ +#endif + + /* update this when stuff happens */ + NuProgressData* pProgress; + + /* data goeth out here */ + NuDataSink* pDataSink; +} NuFunnel; + + +/* + * File straw definition. This is used for slurping up input data. + * + * Mostly this is an encapsulation of an input source and a progress + * updater, useful for reading uncompressed data and feeding it to a + * compressor. It doesn't make sense to show a thermometer based on + * compressed output, since we don't know how big the eventual result + * will be, so we want to do it for the input. + */ +typedef struct NuStraw { + /* update this when stuff happens */ + NuProgressData* pProgress; + + /* data cometh in here */ + NuDataSource* pDataSource; + + /* progress update fields */ + ulong lastProgress; + ulong lastDisplayed; +} NuStraw; + +/*NuError Nu_CopyStreamToStream(FILE* outfp, FILE* infp, ulong count);*/ + + +/* + * =========================================================================== + * Data source and sink abstractions + * =========================================================================== + */ + +/* + * DataSource is used when adding data to an archive. + */ + +typedef enum NuDataSourceType { + kNuDataSourceUnknown = 0, + kNuDataSourceFromFile, + kNuDataSourceFromFP, + kNuDataSourceFromBuffer +} NuDataSourceType; + +typedef struct NuDataSourceCommon { + NuDataSourceType sourceType; + NuThreadFormat threadFormat; /* is it already compressed? */ + ushort rawCrc; /* crc for already-compressed data*/ + /*Boolean doClose; \* close on completion? */ + ulong dataLen; /* length of data (var for buf) */ + ulong otherLen; /* uncomp len or preset buf size */ + int refCount; /* so we can copy structs */ +} NuDataSourceCommon; + +union NuDataSource { + NuDataSourceType sourceType; + + NuDataSourceCommon common; + + struct { + NuDataSourceCommon common; + char* pathname; + Boolean fromRsrcFork; + + /* temp storage; only valid when processing in library */ + FILE* fp; + } fromFile; + + struct { + NuDataSourceCommon common; + FILE* fp; + long offset; /* starting offset */ + + NuCallback fcloseFunc; /* how to fclose the file */ + } fromFP; + + struct { + NuDataSourceCommon common; + const uchar* buffer; /* non-const if doClose=true */ + long offset; /* starting offset */ + + long curOffset; /* current offset */ + long curDataLen; /* remaining data */ + + NuCallback freeFunc; /* how to free data */ + } fromBuffer; +}; + + +/* + * DataSink is used when extracting data from an archive. + */ + +typedef enum NuDataSinkType { + kNuDataSinkUnknown = 0, + kNuDataSinkToFile, + kNuDataSinkToFP, + kNuDataSinkToBuffer, + kNuDataSinkToVoid +} NuDataSinkType; + +typedef struct NuDataSinkCommon { + NuDataSinkType sinkType; + Boolean doExpand; /* expand file? */ + NuValue convertEOL; /* convert EOL? (req "expand") */ + ulong outCount; +} NuDataSinkCommon; + +union NuDataSink { + NuDataSinkType sinkType; + + NuDataSinkCommon common; + + struct { + NuDataSinkCommon common; + char* pathname; /* file to open */ + char fssep; + + /* temp storage; must be nil except when processing in library */ + FILE* fp; + } toFile; + + struct { + NuDataSinkCommon common; + FILE* fp; + } toFP; + + struct { + NuDataSinkCommon common; + uchar* buffer; + ulong bufLen; /* max amount of data "buffer" holds */ + NuError stickyErr; + } toBuffer; +}; + + +/* + * =========================================================================== + * Function prototypes + * =========================================================================== + */ + +/* + * This is a little unpleasant. This blob of stuff gets stuffed in as + * the first arguments to Nu_ReportError, so we don't have to type them + * in every time we use the function. It would've been much easier to + * use a gcc-style varargs macro, but not everybody uses gcc. + */ +#ifdef DEBUG_MSGS + #ifdef HAS__FUNCTION__ + #define _FUNCTION_ __FUNCTION__ + #else + #define _FUNCTION_ "" + #endif + + #define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false + #define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true + #define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false + #define DebugShowError(err) \ + Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \ + true, err, "(DEBUG)"); +#else + #define NU_BLOB pArchive, "", 0, "", false + #define NU_BLOB_DEBUG pArchive, "", 0, "", true + #define NU_NILBLOB NULL, "", 0, "", false + #define DebugShowError(err) ((void)0) +#endif + +/* + * The BailError macro serves two purposes. First, it's a convenient + * way to avoid typing, "if (err != kNuErrNone) goto bail;". Second, + * when the library is built with debugging enabled, it vitually gives + * us a stack trace of exiting functions. This makes it easier to debug + * problems sent in as screen dumps via e-mail. + */ +#define BailError(err) { \ + if ((err) != kNuErrNone) { \ + /* [should this be debug-only, or all the time?] */ \ + DebugShowError(err); \ + goto bail; \ + } \ + } +#define BailErrorQuiet(err) { \ + if ((err) != kNuErrNone) \ + goto bail; \ + } +#define BailNil(val) { \ + if ((val) == nil) { \ + err = kNuErrUnexpectedNil; \ + BailError(err); \ + } \ + } +#define BailAlloc(val) { \ + if ((val) == nil) { \ + err = kNuErrMalloc; \ + BailError(err); \ + } \ + } + + +/* + * Internal function prototypes and inline functions. + */ + +/* Archive.c */ +void Nu_MasterHeaderCopy(NuArchive* pArchive, NuMasterHeader* pDstHeader, + const NuMasterHeader* pSrcHeader); +NuError Nu_GetMasterHeader(NuArchive* pArchive, + const NuMasterHeader** ppMasterHeader); +NuRecordIdx Nu_GetNextRecordIdx(NuArchive* pArchive); +NuThreadIdx Nu_GetNextThreadIdx(NuArchive* pArchive); +NuError Nu_CopyWrapperToTemp(NuArchive* pArchive); +NuError Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp); +NuError Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp); +NuError Nu_AllocCompressionBufferIFN(NuArchive* pArchive); +NuError Nu_StreamOpenRO(FILE* infp, NuArchive** ppArchive); +NuError Nu_OpenRO(const char* filename, NuArchive** ppArchive); +NuError Nu_OpenRW(const char* archivePathname, const char* tempPathname, + ulong flags, NuArchive** ppArchive); +NuError Nu_WriteMasterHeader(NuArchive* pArchive, FILE* fp, + NuMasterHeader* pMasterHeader); +NuError Nu_Close(NuArchive* pArchive); +NuError Nu_Abort(NuArchive* pArchive); +NuError Nu_RenameTempToArchive(NuArchive* pArchive); +NuError Nu_DeleteArchiveFile(NuArchive* pArchive); + +/* ArchiveIO.c */ +uchar Nu_ReadOneC(NuArchive* pArchive, FILE* fp, ushort* pCrc); +uchar Nu_ReadOne(NuArchive* pArchive, FILE* fp); +void Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uchar val, ushort* pCrc); +void Nu_WriteOne(NuArchive* pArchive, FILE* fp, uchar val); +ushort Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, ushort* pCrc); +ushort Nu_ReadTwo(NuArchive* pArchive, FILE* fp); +void Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, ushort val, ushort* pCrc); +void Nu_WriteTwo(NuArchive* pArchive, FILE* fp, ushort val); +ulong Nu_ReadFourC(NuArchive* pArchive, FILE* fp, ushort* pCrc); +ulong Nu_ReadFour(NuArchive* pArchive, FILE* fp); +void Nu_WriteFourC(NuArchive* pArchive, FILE* fp, ulong val, ushort* pCrc); +void Nu_WriteFour(NuArchive* pArchive, FILE* fp, ulong val); +NuDateTime Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, ushort* pCrc); +NuDateTime Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, ushort* pCrc); +void Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime, + ushort* pCrc); +void Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime); +void Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count, + ushort* pCrc); +void Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count); +void Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer, + long count, ushort* pCrc); +void Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer, + long count); +NuError Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp); +NuError Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset, + int ptrname); +NuError Nu_RewindArchive(NuArchive* pArchive); + +/* Bzip2.c */ +NuError Nu_CompressBzip2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_ExpandBzip2(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc); + +/* Compress.c */ +NuError Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource, + NuThreadID threadID, NuThreadFormat sourceFormat, + NuThreadFormat targetFormat, NuProgressData* progressData, FILE* dstFp, + NuThread* pThread); +NuError Nu_CopyPresizedToArchive(NuArchive* pArchive, + NuDataSource* pDataSource, NuThreadID threadID, FILE* dstFp, + NuThread* pThread, char** ppSavedCopy); + +/* Crc16.c */ +extern const ushort gNuCrc16Table[256]; +ushort Nu_CalcCRC16(ushort seed, const uchar* ptr, int count); +#ifdef __Crc16_c__ /* just doing "static inline" warns def-but-not-used */ + #define CRC_INLINE /**/ +#else + #define CRC_INLINE extern inline +#endif +#if defined(inline) && !defined(__Crc16_c__) /* somebody ovrd inline def? */ +ushort Nu_UpdateCRC16(uchar val, ushort crc); +#else +CRC_INLINE ushort +Nu_UpdateCRC16(uchar val, ushort crc) +{ + return (gNuCrc16Table[((crc >> 8) & 0xFF) ^ val] ^ (crc << 8)) & 0xFFFF; +} +#endif + +/* Debug.c */ +#if defined(DEBUG_MSGS) || !defined(NDEBUG) +void Nu_DebugDumpAll(NuArchive* pArchive); +void Nu_DebugDumpThread(const NuThread* pThread); +#endif + +/* Deferred.c */ +NuError Nu_ThreadModAdd_New(NuArchive* pArchive, NuThreadID threadID, + NuThreadFormat threadFormat, NuDataSource* pDataSource, + NuThreadMod** ppThreadMod); +NuError Nu_ThreadModUpdate_New(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSource* pDataSource, NuThreadMod** ppThreadMod); +NuError Nu_ThreadModDelete_New(NuArchive* pArchive, NuThreadIdx threadIdx, + NuThreadID threadID, NuThreadMod** ppThreadMod); +void Nu_ThreadModFree(NuArchive* pArchive, NuThreadMod* pThreadMod); +NuError Nu_ThreadModAdd_FindByThreadID(const NuRecord* pRecord, + NuThreadID threadID, NuThreadMod** ppThreadMod); +void Nu_FreeThreadMods(NuArchive* pArchive, NuRecord* pRecord); +NuThreadMod* Nu_ThreadMod_FindByThreadIdx(const NuRecord* pRecord, + NuThreadIdx threadIdx); +NuError Nu_Flush(NuArchive* pArchive, long* pStatusFlags); + +/* Deflate.c */ +NuError Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc); + +/* Expand.c */ +NuError Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel); + +/* FileIO.c */ +void Nu_SetCurrentDateTime(NuDateTime* pDateTime); +Boolean Nu_IsOlder(const NuDateTime* pWhen1, const NuDateTime* pWhen2); +NuError Nu_OpenOutputFile(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, const char* newPathname, char newFssep, + FILE** pFp); +NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord, + FILE* fp, const char* pathname); +NuError Nu_OpenInputFile(NuArchive* pArchive, const char* pathname, + Boolean openRsrc, FILE** pFp); +NuError Nu_DeleteFile(const char* pathname); +NuError Nu_RenameFile(const char* fromPath, const char* toPath); +NuError Nu_FTell(FILE* fp, long* pOffset); +NuError Nu_FSeek(FILE* fp, long offset, int ptrname); +NuError Nu_FRead(FILE* fp, void* buf, size_t nbyte); +NuError Nu_FWrite(FILE* fp, const void* buf, size_t nbyte); +NuError Nu_CopyFileSection(NuArchive* pArchive, FILE* dstFp, FILE* srcFp, + long length); +NuError Nu_GetFileLength(NuArchive* pArchive, FILE* fp, long* pLength); +NuError Nu_TruncateOpenFile(FILE* fp, long length); + +/* Funnel.c */ +NuError Nu_ProgressDataInit_Compress(NuArchive* pArchive, + NuProgressData* pProgressData, const NuRecord* pRecord, + const char* origPathname); +NuError Nu_ProgressDataInit_Expand(NuArchive* pArchive, + NuProgressData* pProgressData, const NuRecord* pRecord, + const char* newPathname, char newFssep, NuValue convertEOL); +NuError Nu_SendInitialProgress(NuArchive* pArchive, NuProgressData* pProgress); + +NuError Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink, + NuValue convertEOL, NuValue convertEOLTo, NuProgressData* pProgress, + NuFunnel** ppFunnel); +NuError Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel); +/*void Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, ulong maxBytes);*/ +NuError Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel, + const uchar* buffer, ulong count); +NuError Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel); +NuError Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw, + NuThreadFormat threadFormat, ulong sourceLen); +NuError Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel, + const NuThread* pThread); +NuError Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state); +NuError Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel); +Boolean Nu_FunnelGetDoExpand(NuFunnel* pFunnel); + +NuError Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource, + NuProgressData* pProgress, NuStraw** ppStraw); +NuError Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw); +NuError Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state); +NuError Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw); +NuError Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uchar* buffer, + long len); +NuError Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw); + +/* Lzc.c */ +NuError Nu_CompressLZC12(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_CompressLZC16(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_ExpandLZC(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc); + +/* Lzw.c */ +NuError Nu_CompressLZW1(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_CompressLZW2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_ExpandLZW(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pThreadCrc); + +/* MiscUtils.c */ +/*extern const char* kNufxLibName;*/ +extern NuCallback gNuGlobalErrorMessageHandler; +const char* Nu_StrError(NuError err); +void Nu_ReportError(NuArchive* pArchive, const char* file, int line, + const char* function, Boolean isDebug, NuError err, const char* format, ...) + #if defined(__GNUC__) + __attribute__ ((format(printf, 7, 8))) + #endif + ; +#ifdef USE_DMALLOC /* want file and line numbers for calls */ +# define Nu_Malloc(archive, size) malloc(size) +# define Nu_Calloc(archive, size) calloc(1, size) +# define Nu_Realloc(archive, ptr, size) realloc(ptr, size) +# define Nu_Free(archive, ptr) (ptr != nil ? free(ptr) : (void)0) +#else +void* Nu_Malloc(NuArchive* pArchive, size_t size); +void* Nu_Calloc(NuArchive* pArchive, size_t size); +void* Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size); +void Nu_Free(NuArchive* pArchive, void* ptr); +#endif +NuResult Nu_InternalFreeCallback(NuArchive* pArchive, void* args); + +/* Record.c */ +void Nu_RecordAddThreadMod(NuRecord* pRecord, NuThreadMod* pThreadMod); +Boolean Nu_RecordIsEmpty(NuArchive* pArchive, const NuRecord* pRecord); +Boolean Nu_RecordSet_GetLoaded(const NuRecordSet* pRecordSet); +ulong Nu_RecordSet_GetNumRecords(const NuRecordSet* pRecordSet); +void Nu_RecordSet_SetNumRecords(NuRecordSet* pRecordSet, ulong val); +void Nu_RecordSet_IncNumRecords(NuRecordSet* pRecordSet); +NuRecord* Nu_RecordSet_GetListHead(const NuRecordSet* pRecordSet); +NuRecord** Nu_RecordSet_GetListHeadPtr(NuRecordSet* pRecordSet); +NuRecord* Nu_RecordSet_GetListTail(const NuRecordSet* pRecordSet); +Boolean Nu_RecordSet_IsEmpty(const NuRecordSet* pRecordSet); +NuError Nu_RecordSet_FreeAllRecords(NuArchive* pArchive, + NuRecordSet* pRecordSet); +NuError Nu_RecordSet_DeleteRecordPtr(NuArchive* pArchive, + NuRecordSet* pRecordSet, NuRecord** ppRecord); +NuError Nu_RecordSet_DeleteRecord(NuArchive* pArchive, NuRecordSet* pRecordSet, + NuRecord* pRecord); +NuError Nu_RecordSet_Clone(NuArchive* pArchive, NuRecordSet* pDstSet, + const NuRecordSet* pSrcSet); +NuError Nu_RecordSet_MoveAllRecords(NuArchive* pArchive, NuRecordSet* pDstSet, + NuRecordSet* pSrcSet); +NuError Nu_RecordSet_FindByIdx(const NuRecordSet* pRecordSet, NuRecordIdx rec, + NuRecord** ppRecord); +NuError Nu_RecordSet_FindByThreadIdx(NuRecordSet* pRecordSet, + NuThreadIdx threadIdx, NuRecord** ppRecord, NuThread** ppThread); +NuError Nu_RecordSet_ReplaceRecord(NuArchive* pArchive, NuRecordSet* pBadSet, + NuRecord* pBadRecord, NuRecordSet* pGoodSet, NuRecord** ppGoodRecord); +Boolean Nu_ShouldIgnoreBadCRC(NuArchive* pArchive, const NuRecord* pRecord, + NuError err); +NuError Nu_WriteRecordHeader(NuArchive* pArchive, NuRecord* pRecord, FILE* fp); +NuError Nu_GetTOCIfNeeded(NuArchive* pArchive); +NuError Nu_StreamContents(NuArchive* pArchive, NuCallback contentFunc); +NuError Nu_StreamExtract(NuArchive* pArchive); +NuError Nu_StreamTest(NuArchive* pArchive); +NuError Nu_Contents(NuArchive* pArchive, NuCallback contentFunc); +NuError Nu_Extract(NuArchive* pArchive); +NuError Nu_ExtractRecord(NuArchive* pArchive, NuRecordIdx recIdx); +NuError Nu_Test(NuArchive* pArchive); +NuError Nu_TestRecord(NuArchive* pArchive, NuRecordIdx recIdx); +NuError Nu_GetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecord** ppRecord); +NuError Nu_GetRecordIdxByName(NuArchive* pArchive, const char* name, + NuRecordIdx* pRecordIdx); +NuError Nu_GetRecordIdxByPosition(NuArchive* pArchive, ulong position, + NuRecordIdx* pRecordIdx); +NuError Nu_FindRecordForWriteByIdx(NuArchive* pArchive, NuRecordIdx recIdx, + NuRecord** ppFoundRecord); +NuError Nu_AddFile(NuArchive* pArchive, const char* pathname, + const NuFileDetails* pFileDetails, Boolean fromRsrcFork, + NuRecordIdx* pRecordIdx); +NuError Nu_AddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails, + NuRecordIdx* pRecordIdx, NuRecord** ppRecord); +NuError Nu_Rename(NuArchive* pArchive, NuRecordIdx recIdx, + const char* pathname, char fssep); +NuError Nu_SetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecordAttr* pRecordAttr); +NuError Nu_Delete(NuArchive* pArchive); +NuError Nu_DeleteRecord(NuArchive* pArchive, NuRecordIdx rec); + +/* SourceSink.c */ +NuError Nu_DataSourceFile_New(NuThreadFormat threadFormat, + ulong otherLen, const char* pathname, Boolean isFromRsrcFork, + NuDataSource** ppDataSource); +NuError Nu_DataSourceFP_New(NuThreadFormat threadFormat, + ulong otherLen, FILE* fp, long offset, long length, + NuCallback fcloseFunc, NuDataSource** ppDataSource); +NuError Nu_DataSourceBuffer_New(NuThreadFormat threadFormat, + ulong otherLen, const uchar* buffer, long offset, long length, + NuCallback freeFunc, NuDataSource** ppDataSource); +NuDataSource* Nu_DataSourceCopy(NuDataSource* pDataSource); +NuError Nu_DataSourceFree(NuDataSource* pDataSource); +NuDataSourceType Nu_DataSourceGetType(const NuDataSource* pDataSource); +NuThreadFormat Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource); +ulong Nu_DataSourceGetDataLen(const NuDataSource* pDataSource); +ulong Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource); +void Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen); +ushort Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource); +void Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, ushort crc); +NuError Nu_DataSourcePrepareInput(NuArchive* pArchive, + NuDataSource* pDataSource); +void Nu_DataSourceUnPrepareInput(NuArchive* pArchive, + NuDataSource* pDataSource); +const char* Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource); +NuError Nu_DataSourceGetBlock(NuDataSource* pDataSource, uchar* buf, ulong len); +NuError Nu_DataSourceRewind(NuDataSource* pDataSource); +NuError Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL, + const char* pathname, char fssep, NuDataSink** ppDataSink); +NuError Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp, + NuDataSink** ppDataSink); +NuError Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL, + uchar* buffer, ulong bufLen, NuDataSink** ppDataSink); +NuError Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL, + NuDataSink** ppDataSink); +NuError Nu_DataSinkFree(NuDataSink* pDataSink); +NuDataSinkType Nu_DataSinkGetType(const NuDataSink* pDataSink); +Boolean Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink); +NuValue Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink); +ulong Nu_DataSinkGetOutCount(const NuDataSink* pDataSink); +const char* Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink); +char Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink); +FILE* Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink); +void Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp); +void Nu_DataSinkFile_Close(NuDataSink* pDataSink); +NuError Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uchar* buf, ulong len); +NuError Nu_DataSinkGetError(NuDataSink* pDataSink); + +/* Squeeze.c */ +NuError Nu_CompressHuffmanSQ(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc); +NuError Nu_ExpandHuffmanSQ(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc); + +/* Thread.c */ +#ifdef __Thread_c__ + #define THREAD_INLINE /**/ +#else + #define THREAD_INLINE extern inline +#endif +#if defined(inline) && !defined(__Thread_c__) /* somebody ovrd inline def? */ +NuThread* Nu_GetThread(const NuRecord* pRecord, int idx); +#else +THREAD_INLINE NuThread* +Nu_GetThread(const NuRecord* pRecord, int idx) +{ + if (idx >= (int)pRecord->recTotalThreads) + return nil; + else + return &pRecord->pThreads[idx]; +} +#endif +void Nu_StripHiIfAllSet(char* str); +Boolean Nu_IsPresizedThreadID(NuThreadID threadID); +Boolean Nu_IsCompressibleThreadID(NuThreadID threadID); +Boolean Nu_ThreadHasCRC(long recordVersion, NuThreadID threadID); +NuError Nu_FindThreadByIdx(const NuRecord* pRecord, NuThreadIdx thread, + NuThread** ppThread); +NuError Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID, + NuThread** ppThread); +void Nu_CopyThreadContents(NuThread* pDstThread, const NuThread* pSrcThread); +NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, + ushort* pCrc); +NuError Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp, + ushort* pCrc); +NuError Nu_ComputeThreadData(NuArchive* pArchive, NuRecord* pRecord); +NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord,long numThreads); +NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread); +NuError Nu_SkipThread(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread); +NuError Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSink* pDataSink); +NuError Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord, + NuThreadID threadID); +NuError Nu_AddThread(NuArchive* pArchive, NuRecordIdx rec, NuThreadID threadID, + NuDataSource* pDataSource, NuThreadIdx* pThreadIdx); +NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSource* pDataSource, long* pMaxLen); +NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx); + +/* Value.c */ +NuError Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue); +NuError Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value); +NuError Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr); +NuThreadFormat Nu_ConvertCompressValToFormat(NuArchive* pArchive, + NuValue compValue); + +/* Version.c */ +NuError Nu_GetVersion(long* pMajorVersion, long* pMinorVersion, + long* pBugVersion, const char** ppBuildDate, const char** ppBuildFlags); + +#endif /*__NufxLibPriv__*/ diff --git a/nufxlib/README.txt b/nufxlib/README.txt new file mode 100644 index 0000000..37c7c49 --- /dev/null +++ b/nufxlib/README.txt @@ -0,0 +1,124 @@ +NufxLib README, updated 2004/03/18 +http://www.nulib.com/ + +See "COPYING-LIB" for distribution restrictions. + + +UNIX +==== + +Run the "configure" script. Read through "INSTALL" if you haven't used +one of these before, especially if you want to use a specific compiler +or a particular set of compiler flags. + +You can disable specific compression methods with "--disable-METHOD" +(run "sh ./configure --help" to see the possible options). By default, +all methods are enabled except bzip2. + +Run "make depend" if you have makedepend, and then type "make". This will +build the library and all of the programs in the "samples" directory. +There are some useful programs in "samples", described in a README.txt +file there. In particular, you should run samples/test-basic to verify +that things are more or less working. + +If you want to install the library and header file into standard system +locations (usually /usr/local), run "make install". To learn how to +specify different locations, read the INSTALL document. + +There are some flags in "OPT" you may want to use. The "autoconf" default +for @CFLAGS@ is "-g -O2". + +-DNDEBUG + Disable assert() calls and extra tests. This will speed things up, + but errors won't get caught until later on, making the root cause + harder to locate. + +-DDEBUG_MSGS + Enable debug messages. This increases the size of the executable, + but shouldn't affect performance. When errors occur, more output is + produced. The "debug dump" feature is enabled by this flag. + +-DDEBUG_VERBOSE + (Implicitly sets DEBUG_MSGS.) Spray lots of debugging output. + +If you want to do benchmarks, use "-O2 -DNDEBUG". The recommended +configuration during testing is "-g -O2 -DDEBUG_MSGS", so that verbose +debug output is available when errors occur. + +The flags are stuffed into Version.c, so the application program can +examine and display the flags that were used to build the library. + + +BeOS +==== + +This works just like the UNIX version, but certain defaults have been +changed. Running configure without arguments under BeOS is equivalent to: + + ./configure --prefix=/boot --includedir='${prefix}/develop/headers' + --libdir='${exec_prefix}/home/config/lib' --mandir='/tmp' + --bindir='${exec_prefix}/home/config/bin' + +If you're using BeOS/PPC, it will also do: + + CC=cc CFLAGS='-proc 603 -opt full' + + +Mac OS X +======== + +This works just like the UNIX version, with the exception that when you link +against nufxlib, your project must also link against the Carbon framework. +This can be done in ProjectBuilder by using the Add Framework option in the +Project menu, or by adding "-framework Carbon" to the gcc command line. + +You'll see some warnings due to some namespace collisions between nufxlib and +Carbon, but everything will work fine. Carbon is used to provide support for +file types and resource forks. + + +Win32 +===== + +If you're using an environment that supports "configure" scripts, such as +DJGPP, follow the UNIX instructions. + +NufxLib has been tested with Microsoft Visual C++ 6.0. To build NufxLib, +start up a DOS shell and run vcvars32.bat to set your environment. Run: + nmake -f makefile.msc +to build with debugging info, or + nmake -f makefile.msc nodebug=1 +to build optimized. + +See the makefile for comments about including zlib or libbz2. These +need to be enabled at compile time and linked into the sample apps. + +Once the library has been built, "cd samples" and run the same command there. +When it finishes, run "test-basic.exe". + +If you want to build NufxLib as a DLL, use "makefile.dll" instead. +If you're using zlib or libbz2, these will need to be linked into the DLL. +The makefile currently assumes that you will want to use zlib.dll. + + +Other Notes +=========== + +All of the source code is now formatted with spaces instead of tabs. + +If you want to use the library in a multithreaded application, you should +define "USE_REENTRANT_CALLS" to tell it to use reentrant versions of +certain library calls. This defines _REENTRANT, which causes Solaris to +add the appropriate goodies. (Seems to me you'd always want this on, but +for some reason Solaris makes you take an extra step, so I'm not going to +define it by default.) + + +Legalese +======== + +NufxLib, a NuFX archive manipulation library. +Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + +See COPYING for license. + diff --git a/nufxlib/Record.c b/nufxlib/Record.c new file mode 100644 index 0000000..300cf2d --- /dev/null +++ b/nufxlib/Record.c @@ -0,0 +1,2867 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Record-level operations. + */ +#include "NufxLibPriv.h" + + +/* + * Local constants. + */ +static const uchar kNufxID[kNufxIDLen] = { 0x4e, 0xf5, 0x46, 0xd8 }; + + +/* + * =========================================================================== + * Simple NuRecord stuff + * =========================================================================== + */ + +/* + * Initialize the contents of a NuRecord. The goal here is to init the + * things that a Nu_FreeRecordContents call will check, so that we don't + * end up trying to free garbage. No need to memset() the whole thing. + */ +static NuError +Nu_InitRecordContents(NuArchive* pArchive, NuRecord* pRecord) +{ + Assert(pRecord != nil); + + DebugFill(pRecord, sizeof(*pRecord)); + + pRecord->recOptionList = nil; + pRecord->extraBytes = nil; + pRecord->recFilename = nil; + pRecord->threadFilename = nil; + pRecord->newFilename = nil; + pRecord->pThreads = nil; + pRecord->pNext = nil; + pRecord->pThreadMods = nil; + pRecord->dirtyHeader = false; + pRecord->dropRecFilename = false; + pRecord->isBadMac = false; + + return kNuErrNone; +} + +/* + * Allocate and initialize a new NuRecord struct. + */ +static NuError +Nu_RecordNew(NuArchive* pArchive, NuRecord** ppRecord) +{ + Assert(ppRecord != nil); + + *ppRecord = Nu_Malloc(pArchive, sizeof(**ppRecord)); + if (*ppRecord == nil) + return kNuErrMalloc; + + return Nu_InitRecordContents(pArchive, *ppRecord); +} + +/* + * Free anything allocated within a record. Doesn't try to free the record + * itself. + */ +static NuError +Nu_FreeRecordContents(NuArchive* pArchive, NuRecord* pRecord) +{ + Assert(pRecord != nil); + + Nu_Free(pArchive, pRecord->recOptionList); + Nu_Free(pArchive, pRecord->extraBytes); + Nu_Free(pArchive, pRecord->recFilename); + Nu_Free(pArchive, pRecord->threadFilename); + Nu_Free(pArchive, pRecord->newFilename); + Nu_Free(pArchive, pRecord->pThreads); + /* don't Free(pRecord->pNext)! */ + Nu_FreeThreadMods(pArchive, pRecord); + + (void) Nu_InitRecordContents(pArchive, pRecord); /* mark as freed */ + + return kNuErrNone; +} + +/* + * Free up a NuRecord struct. + */ +static NuError +Nu_RecordFree(NuArchive* pArchive, NuRecord* pRecord) +{ + if (pRecord == nil) + return kNuErrNone; + + (void) Nu_FreeRecordContents(pArchive, pRecord); + Nu_Free(pArchive, pRecord); + + return kNuErrNone; +} + +/* + * Copy a field comprised of a buffer and a length from one structure to + * another. It is assumed that the length value has already been copied. + */ +static NuError +CopySizedField(NuArchive* pArchive, void* vppDst, const void* vpSrc, uint len) +{ + NuError err = kNuErrNone; + uchar** ppDst = vppDst; + const uchar* pSrc = vpSrc; + + Assert(ppDst != nil); + + if (len) { + Assert(pSrc != nil); + *ppDst = Nu_Malloc(pArchive, len); + BailAlloc(*ppDst); + memcpy(*ppDst, pSrc, len); + } else { + Assert(pSrc == nil); + *ppDst = nil; + } + +bail: + return err; +} + +/* + * Make a copy of a record. + */ +static NuError +Nu_RecordCopy(NuArchive* pArchive, NuRecord** ppDst, const NuRecord* pSrc) +{ + NuError err; + NuRecord* pDst; + + err = Nu_RecordNew(pArchive, ppDst); + BailError(err); + + /* copy all the static fields, then copy or blank the "hairy" parts */ + pDst = *ppDst; + memcpy(pDst, pSrc, sizeof(*pSrc)); + CopySizedField(pArchive, &pDst->recOptionList, pSrc->recOptionList, + pSrc->recOptionSize); + CopySizedField(pArchive, &pDst->extraBytes, pSrc->extraBytes, + pSrc->extraCount); + CopySizedField(pArchive, &pDst->recFilename, pSrc->recFilename, + pSrc->recFilenameLength == 0 ? 0 : pSrc->recFilenameLength+1); + CopySizedField(pArchive, &pDst->threadFilename, pSrc->threadFilename, + pSrc->threadFilename == nil ? 0 : strlen(pSrc->threadFilename) +1); + CopySizedField(pArchive, &pDst->newFilename, pSrc->newFilename, + pSrc->newFilename == nil ? 0 : strlen(pSrc->newFilename) +1); + CopySizedField(pArchive, &pDst->pThreads, pSrc->pThreads, + pSrc->recTotalThreads * sizeof(*pDst->pThreads)); + + /* now figure out what the filename is supposed to point at */ + if (pSrc->filename == pSrc->threadFilename) + pDst->filename = pDst->threadFilename; + else if (pSrc->filename == pSrc->recFilename) + pDst->filename = pDst->recFilename; + else if (pSrc->filename == pSrc->newFilename) + pDst->filename = pDst->newFilename; + else + pDst->filename = pSrc->filename; /* probably static kDefault value */ + + pDst->pNext = nil; + + /* these only hold for copy from orig... may need to remove */ + Assert(pSrc->pThreadMods == nil); + Assert(!pSrc->dirtyHeader); + +bail: + return err; +} + + +/* + * Add a ThreadMod to the list in the NuRecord. + * + * In general, the order is not significant. However, if we're adding + * a bunch of "add" threadMods for control threads to a record, their + * order might be important. So, we want to add the threadMod to the + * end of the list. + * + * I'm expecting these lists to be short, so walking down them is + * acceptable. We could do simple optimizations, like only preserving + * ordering for "add" threadMods, but even that seems silly. + */ +void +Nu_RecordAddThreadMod(NuRecord* pRecord, NuThreadMod* pThreadMod) +{ + NuThreadMod* pScanThreadMod; + + Assert(pRecord != nil); + Assert(pThreadMod != nil); + + if (pRecord->pThreadMods == nil) { + pRecord->pThreadMods = pThreadMod; + } else { + pScanThreadMod = pRecord->pThreadMods; + while (pScanThreadMod->pNext != nil) + pScanThreadMod = pScanThreadMod->pNext; + + pScanThreadMod->pNext = pThreadMod; + } + + pThreadMod->pNext = nil; +} + + +/* + * Decide if a record is empty. An empty record is one that will have no + * threads after all adds and deletes are processed. + * + * You can't delete something you just added or has been updated, and you + * can't update something that has been deleted, so any "add" or "update" + * items indicate that the thread isn't empty. + * + * You can't delete a thread more than once, or delete a thread that + * doesn't exist, so all we need to do is count up the number of current + * threads, subtract the number of deletes, and return "true" if the net + * result is zero. + */ +Boolean +Nu_RecordIsEmpty(NuArchive* pArchive, const NuRecord* pRecord) +{ + const NuThreadMod* pThreadMod; + int numThreads; + + Assert(pRecord != nil); + + numThreads = pRecord->recTotalThreads; + + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + switch (pThreadMod->entry.kind) { + case kNuThreadModAdd: + case kNuThreadModUpdate: + return false; + case kNuThreadModDelete: + numThreads--; + break; + case kNuThreadModUnknown: + default: + Assert(0); + return false; + } + + pThreadMod = pThreadMod->pNext; + } + + if (numThreads > 0) + return false; + else if (numThreads == 0) + return true; + else { + Assert(0); + Nu_ReportError(NU_BLOB, kNuErrInternal, + "Thread counting failed (%d)", numThreads); + return false; + } +} + + +/* + * =========================================================================== + * NuRecordSet functions + * =========================================================================== + */ + +/* + * Trivial getters and setters + */ + +Boolean +Nu_RecordSet_GetLoaded(const NuRecordSet* pRecordSet) +{ + Assert(pRecordSet != nil); + return pRecordSet->loaded; +} + +void +Nu_RecordSet_SetLoaded(NuRecordSet* pRecordSet, Boolean val) +{ + pRecordSet->loaded = val; +} + +ulong +Nu_RecordSet_GetNumRecords(const NuRecordSet* pRecordSet) +{ + return pRecordSet->numRecords; +} + +void +Nu_RecordSet_SetNumRecords(NuRecordSet* pRecordSet, ulong val) +{ + pRecordSet->numRecords = val; +} + +void +Nu_RecordSet_IncNumRecords(NuRecordSet* pRecordSet) +{ + pRecordSet->numRecords++; +} + +NuRecord* +Nu_RecordSet_GetListHead(const NuRecordSet* pRecordSet) +{ + return pRecordSet->nuRecordHead; +} + +NuRecord** +Nu_RecordSet_GetListHeadPtr(NuRecordSet* pRecordSet) +{ + return &pRecordSet->nuRecordHead; +} + +NuRecord* +Nu_RecordSet_GetListTail(const NuRecordSet* pRecordSet) +{ + return pRecordSet->nuRecordTail; +} + + +/* + * Returns "true" if the record set has no records or hasn't ever been + * used. + */ +Boolean +Nu_RecordSet_IsEmpty(const NuRecordSet* pRecordSet) +{ + if (!pRecordSet->loaded || pRecordSet->numRecords == 0) + return true; + + return false; +} + +/* + * Free the list of records, and reset the record sets to initial state. + */ +NuError +Nu_RecordSet_FreeAllRecords(NuArchive* pArchive, NuRecordSet* pRecordSet) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + NuRecord* pNextRecord; + + if (!pRecordSet->loaded) { + Assert(pRecordSet->nuRecordHead == nil); + Assert(pRecordSet->nuRecordTail == nil); + Assert(pRecordSet->numRecords == 0); + return kNuErrNone; + } + + DBUG(("+++ FreeAllRecords\n")); + pRecord = pRecordSet->nuRecordHead; + while (pRecord != nil) { + pNextRecord = pRecord->pNext; + + err = Nu_RecordFree(pArchive, pRecord); + BailError(err); /* don't really expect this to fail */ + + pRecord = pNextRecord; + } + + pRecordSet->nuRecordHead = pRecordSet->nuRecordTail = nil; + pRecordSet->numRecords = 0; + pRecordSet->loaded = false; + +bail: + return err; +} + + +/* + * Add a new record to the end of the list. + */ +static NuError +Nu_RecordSet_AddRecord(NuRecordSet* pRecordSet, NuRecord* pRecord) +{ + Assert(pRecordSet != nil); + Assert(pRecord != nil); + + /* if one is nil, both must be nil */ + Assert(pRecordSet->nuRecordHead == nil || pRecordSet->nuRecordTail != nil); + Assert(pRecordSet->nuRecordTail == nil || pRecordSet->nuRecordHead != nil); + + if (pRecordSet->nuRecordHead == nil) { + /* empty list */ + pRecordSet->nuRecordHead = pRecordSet->nuRecordTail = pRecord; + pRecordSet->loaded = true; + Assert(!pRecordSet->numRecords); + } else { + pRecord->pNext = nil; + pRecordSet->nuRecordTail->pNext = pRecord; + pRecordSet->nuRecordTail = pRecord; + } + + pRecordSet->numRecords++; + + return kNuErrNone; +} + + +/* + * Delete a record from the record set. Pass in a pointer to the pointer + * to the record (usually either the head pointer or another record's + * "pNext" pointer). + * + * (Should have a "heavy assert" mode where we verify that "ppRecord" + * actually has something to do with pRecordSet.) + */ +NuError +Nu_RecordSet_DeleteRecordPtr(NuArchive* pArchive, NuRecordSet* pRecordSet, + NuRecord** ppRecord) +{ + NuError err; + NuRecord* pRecord; + + Assert(pRecordSet != nil); + Assert(ppRecord != nil); + Assert(*ppRecord != nil); + + /* save a copy of the record we're freeing */ + pRecord = *ppRecord; + + /* update the pHead or pNext pointer */ + *ppRecord = (*ppRecord)->pNext; + pRecordSet->numRecords--; + + /* if we're deleting the tail, we have to find the "new" last entry */ + if (pRecord == pRecordSet->nuRecordTail) { + if (pRecordSet->nuRecordHead == nil) { + /* this was the last entry; we're done */ + pRecordSet->nuRecordTail = nil; + } else { + /* walk through the list... delete bottom-up will be slow! */ + pRecordSet->nuRecordTail = pRecordSet->nuRecordHead; + while (pRecordSet->nuRecordTail->pNext != nil) + pRecordSet->nuRecordTail = pRecordSet->nuRecordTail->pNext; + } + } + + if (pRecordSet->numRecords) + Assert(pRecordSet->nuRecordHead!=nil && pRecordSet->nuRecordTail!=nil); + else + Assert(pRecordSet->nuRecordHead==nil && pRecordSet->nuRecordTail==nil); + + err = Nu_RecordFree(pArchive, pRecord); + return err; +} + +/* + * Delete a record from the record set. + */ +NuError +Nu_RecordSet_DeleteRecord(NuArchive* pArchive, NuRecordSet* pRecordSet, + NuRecord* pRecord) +{ + NuError err; + NuRecord** ppRecord; + + ppRecord = Nu_RecordSet_GetListHeadPtr(pRecordSet); + Assert(ppRecord != nil); + Assert(*ppRecord != nil); + + /* look for the record, so we can update his neighbors */ + /* (this also ensures that the record really is in the set we think it is)*/ + while (*ppRecord) { + if (*ppRecord == pRecord) { + err = Nu_RecordSet_DeleteRecordPtr(pArchive, pRecordSet, ppRecord); + BailError(err); + goto bail; + } + + ppRecord = &((*ppRecord)->pNext); + } + + DBUG(("--- Nu_RecordSet_DeleteRecord failed\n")); + err = kNuErrNotFound; + +bail: + return err; +} + +/* + * Make a clone of a record set. This is used to create the "copy" record + * set out of the "orig" set. + */ +NuError +Nu_RecordSet_Clone(NuArchive* pArchive, NuRecordSet* pDstSet, + const NuRecordSet* pSrcSet) +{ + NuError err = kNuErrNone; + const NuRecord* pSrcRecord; + NuRecord* pDstRecord; + + Assert(pDstSet != nil); + Assert(pSrcSet != nil); + Assert(Nu_RecordSet_GetLoaded(pDstSet) == false); + Assert(Nu_RecordSet_GetLoaded(pSrcSet) == true); + + DBUG(("--- Cloning record set\n")); + + Nu_RecordSet_SetLoaded(pDstSet, true); + + /* copy each record over */ + pSrcRecord = pSrcSet->nuRecordHead; + while (pSrcRecord != nil) { + err = Nu_RecordCopy(pArchive, &pDstRecord, pSrcRecord); + BailError(err); + err = Nu_RecordSet_AddRecord(pDstSet, pDstRecord); + BailError(err); + + pSrcRecord = pSrcRecord->pNext; + } + + Assert(pDstSet->numRecords == pSrcSet->numRecords); + +bail: + if (err != kNuErrNone) { + Nu_RecordSet_FreeAllRecords(pArchive, pDstSet); + } + return err; +} + +/* + * Move all of the records from one record set to another. The records + * from "pSrcSet" are appended to "pDstSet". + * + * On completion, "pSrcSet" will be empty and "unloaded". + */ +NuError +Nu_RecordSet_MoveAllRecords(NuArchive* pArchive, NuRecordSet* pDstSet, + NuRecordSet* pSrcSet) +{ + NuError err = kNuErrNone; + + Assert(pDstSet != nil); + Assert(pSrcSet != nil); + + /* move records over */ + if (Nu_RecordSet_GetNumRecords(pSrcSet)) { + Assert(pSrcSet->loaded); + Assert(pSrcSet->nuRecordHead != nil); + Assert(pSrcSet->nuRecordTail != nil); + if (pDstSet->nuRecordHead == nil) { + /* empty dst list */ + Assert(pDstSet->nuRecordTail == nil); + pDstSet->nuRecordHead = pSrcSet->nuRecordHead; + pDstSet->nuRecordTail = pSrcSet->nuRecordTail; + pDstSet->numRecords = pSrcSet->numRecords; + pDstSet->loaded = true; + } else { + /* append to dst list */ + Assert(pDstSet->loaded); + Assert(pDstSet->nuRecordTail != nil); + pDstSet->nuRecordTail->pNext = pSrcSet->nuRecordHead; + pDstSet->nuRecordTail = pSrcSet->nuRecordTail; + pDstSet->numRecords += pSrcSet->numRecords; + } + } else { + /* no records in src set */ + Assert(pSrcSet->nuRecordHead == nil); + Assert(pSrcSet->nuRecordTail == nil); + + if (pSrcSet->loaded) + pDstSet->loaded = true; + } + + /* nuke all pointers in original list */ + pSrcSet->nuRecordHead = pSrcSet->nuRecordTail = nil; + pSrcSet->numRecords = 0; + pSrcSet->loaded = false; + + return err; +} + + +/* + * Find a record in the list by index. + */ +NuError +Nu_RecordSet_FindByIdx(const NuRecordSet* pRecordSet, NuRecordIdx recIdx, + NuRecord** ppRecord) +{ + NuRecord* pRecord; + + pRecord = pRecordSet->nuRecordHead; + while (pRecord != nil) { + if (pRecord->recordIdx == recIdx) { + *ppRecord = pRecord; + return kNuErrNone; + } + + pRecord = pRecord->pNext; + } + + return kNuErrRecIdxNotFound; +} + + +/* + * Search for a specific thread in all records in the specified record set. + */ +NuError +Nu_RecordSet_FindByThreadIdx(NuRecordSet* pRecordSet, NuThreadIdx threadIdx, + NuRecord** ppRecord, NuThread** ppThread) +{ + NuError err = kNuErrThreadIdxNotFound; + NuRecord* pRecord; + + pRecord = Nu_RecordSet_GetListHead(pRecordSet); + while (pRecord != nil) { + err = Nu_FindThreadByIdx(pRecord, threadIdx, ppThread); + if (err == kNuErrNone) { + *ppRecord = pRecord; + break; + } + pRecord = pRecord->pNext; + } + + Assert(err != kNuErrNone || (*ppRecord != nil && *ppThread != nil)); + return err; +} + + +/* + * Compare two record filenames. This comes into play when looking for + * conflicts while adding records to an archive. + * + * Interesting issues: + * - some filesystems are case-sensitive, some aren't + * - the fssep may be different ('/', ':') for otherwise equivalent names + * - system-dependent conversions could resolve two different names to + * the same thing + * + * Some of these are out of our control. For now, I'm just doing a + * case-insensitive comparison, since the most interesting case for us is + * when the person is adding a data fork and a resource fork from the + * same file during the same operation. + * + * [ Could run both names through the pathname conversion callback first? + * Might be expensive. ] + * + * Returns an integer greater than, equal to, or less than 0, if the + * string pointed to by name1 is greater than, equal to, or less than + * the string pointed to by s2, respectively (i.e. same as strcmp). + */ +static int +Nu_CompareRecordNames(const char* name1, const char* name2) +{ +#ifdef NU_CASE_SENSITIVE + return strcmp(name1, name2); +#else + return strcasecmp(name1, name2); +#endif +} + + +/* + * Find a record in the list by storageName. + */ +static NuError +Nu_RecordSet_FindByName(const NuRecordSet* pRecordSet, const char* name, + NuRecord** ppRecord) +{ + NuRecord* pRecord; + + Assert(pRecordSet != nil); + Assert(pRecordSet->loaded); + Assert(name != nil); + Assert(ppRecord != nil); + + pRecord = pRecordSet->nuRecordHead; + while (pRecord != nil) { + if (Nu_CompareRecordNames(pRecord->filename, name) == 0) { + *ppRecord = pRecord; + return kNuErrNone; + } + + pRecord = pRecord->pNext; + } + + return kNuErrRecNameNotFound; +} + +/* + * Find a record in the list by storageName, starting from the end and + * searching backwards. + * + * Since we don't actually have a "prev" pointer in the record, we end + * up scanning the entire list and keeping the last match. If this + * causes a notable reduction in efficiency we'll have to fix this. + */ +static NuError +Nu_RecordSet_ReverseFindByName(const NuRecordSet* pRecordSet, const char* name, + NuRecord** ppRecord) +{ + NuRecord* pRecord; + NuRecord* pFoundRecord = nil; + + Assert(pRecordSet != nil); + Assert(pRecordSet->loaded); + Assert(name != nil); + Assert(ppRecord != nil); + + pRecord = pRecordSet->nuRecordHead; + while (pRecord != nil) { + if (Nu_CompareRecordNames(pRecord->filename, name) == 0) + pFoundRecord = pRecord; + + pRecord = pRecord->pNext; + } + + if (pFoundRecord != nil) { + *ppRecord = pFoundRecord; + return kNuErrNone; + } + return kNuErrRecNameNotFound; +} + + +/* + * We have a copy of the record in the "copy" set, but we've decided + * (perhaps because the user elected to Skip a failed add) that we'd + * rather have the original. + * + * Delete the record from the "copy" set, clone the "orig" record, and + * insert the "orig" record into the same spot in the "copy" set. + * + * "ppNewRecord" will get a pointer to the newly-created clone. + */ +NuError +Nu_RecordSet_ReplaceRecord(NuArchive* pArchive, NuRecordSet* pBadSet, + NuRecord* pBadRecord, NuRecordSet* pGoodSet, NuRecord** ppNewRecord) +{ + NuError err; + NuRecord* pGoodRecord; + NuRecord* pSiblingRecord; + NuRecord* pNewRecord = nil; + + Assert(pArchive != nil); + Assert(pBadSet != nil); + Assert(pBadRecord != nil); + Assert(pGoodSet != nil); + Assert(ppNewRecord != nil); + + /* + * Find a record in "pGoodSet" that has the same record index as + * the "bad" record. + */ + err = Nu_RecordSet_FindByIdx(pGoodSet, pBadRecord->recordIdx, + &pGoodRecord); + BailError(err); + + /* + * Clone the original. + */ + err = Nu_RecordCopy(pArchive, &pNewRecord, pGoodRecord); + BailError(err); + + /* + * Insert the new one into the "bad" record set, in the exact same + * position. + */ + pNewRecord->pNext = pBadRecord->pNext; + if (pBadSet->nuRecordTail == pBadRecord) + pBadSet->nuRecordTail = pNewRecord; + if (pBadSet->nuRecordHead == pBadRecord) + pBadSet->nuRecordHead = pNewRecord; + else { + /* find the record that points to pBadRecord */ + pSiblingRecord = pBadSet->nuRecordHead; + while (pSiblingRecord->pNext != pBadRecord && pSiblingRecord != nil) + pSiblingRecord = pSiblingRecord->pNext; + + if (pSiblingRecord == nil) { + /* looks like "pBadRecord" wasn't part of "pBadSet" after all */ + Assert(0); + err = kNuErrInternal; + goto bail; + } + + pSiblingRecord->pNext = pNewRecord; + } + + err = Nu_RecordFree(pArchive, pBadRecord); + BailError(err); + + *ppNewRecord = pNewRecord; + pNewRecord = nil; /* don't free */ + +bail: + if (pNewRecord != nil) + Nu_RecordFree(pArchive, pNewRecord); + return err; +} + + +/* + * =========================================================================== + * Assorted utility functions + * =========================================================================== + */ + +/* + * Ask the user if it's okay to ignore a bad CRC. If we can't ask the + * user, return "false". + */ +Boolean +Nu_ShouldIgnoreBadCRC(NuArchive* pArchive, const NuRecord* pRecord, NuError err) +{ + NuErrorStatus errorStatus; + NuResult result; + Boolean retval = false; + + Assert(pArchive->valIgnoreCRC == false); + + if (pArchive->errorHandlerFunc != nil) { + errorStatus.operation = kNuOpTest; /* mostly accurate */ + errorStatus.err = err; + errorStatus.sysErr = 0; + errorStatus.message = nil; + errorStatus.pRecord = pRecord; + errorStatus.pathname = nil; + errorStatus.origPathname = nil; + errorStatus.filenameSeparator = 0; + if (pRecord != nil) { + errorStatus.pathname = pRecord->filename; + errorStatus.filenameSeparator = + NuGetSepFromSysInfo(pRecord->recFileSysInfo); + } + /*errorStatus.origArchiveTouched = false;*/ + errorStatus.canAbort = true; + errorStatus.canRetry = false; + errorStatus.canIgnore = true; + errorStatus.canSkip = false; + errorStatus.canRename = false; + errorStatus.canOverwrite = false; + + result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); + + switch (result) { + case kNuAbort: + goto bail; + case kNuIgnore: + retval = true; + goto bail; + case kNuSkip: + case kNuOverwrite: + case kNuRetry: + case kNuRename: + default: + Nu_ReportError(NU_BLOB, kNuErrSyntax, + "Wasn't expecting result %d here", result); + break; + } + } + +bail: + return retval; +} + + +/* + * Read the next NuFX record from the current offset in the archive stream. + * This includes the record header and the thread header blocks. + * + * Pass in a NuRecord structure that will hold the data we read. + */ +static NuError +Nu_ReadRecordHeader(NuArchive* pArchive, NuRecord* pRecord) +{ + NuError err = kNuErrNone; + ushort crc; + FILE* fp; + int bytesRead; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(pRecord->pThreads == nil); + Assert(pRecord->pNext == nil); + + fp = pArchive->archiveFp; + + pRecord->recordIdx = Nu_GetNextRecordIdx(pArchive); + + /* points to whichever filename storage we like best */ + pRecord->filename = nil; + pRecord->fileOffset = pArchive->currentOffset; + + (void) Nu_ReadBytes(pArchive, fp, pRecord->recNufxID, kNufxIDLen); + if (memcmp(kNufxID, pRecord->recNufxID, kNufxIDLen) != 0) { + err = kNuErrRecHdrNotFound; + Nu_ReportError(NU_BLOB, kNuErrNone, + "Couldn't find start of next record"); + goto bail; + } + + /* + * Read the static fields. + */ + crc = 0; + pRecord->recHeaderCRC = Nu_ReadTwo(pArchive, fp); + pRecord->recAttribCount = Nu_ReadTwoC(pArchive, fp, &crc); + pRecord->recVersionNumber = Nu_ReadTwoC(pArchive, fp, &crc); + pRecord->recTotalThreads = Nu_ReadFourC(pArchive, fp, &crc); + pRecord->recFileSysID = Nu_ReadTwoC(pArchive, fp, &crc); + pRecord->recFileSysInfo = Nu_ReadTwoC(pArchive, fp, &crc); + pRecord->recAccess = Nu_ReadFourC(pArchive, fp, &crc); + pRecord->recFileType = Nu_ReadFourC(pArchive, fp, &crc); + pRecord->recExtraType = Nu_ReadFourC(pArchive, fp, &crc); + pRecord->recStorageType = Nu_ReadTwoC(pArchive, fp, &crc); + pRecord->recCreateWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); + pRecord->recModWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); + pRecord->recArchiveWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); + bytesRead = 56; /* 4-byte 'NuFX' plus the above */ + + /* + * Do some sanity checks before we continue. + */ + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed reading record header"); + goto bail; + } + if (pRecord->recAttribCount > kNuReasonableAttribCount) { + err = kNuErrBadRecord; + Nu_ReportError(NU_BLOB, err, "Attrib count is huge (%u)", + pRecord->recAttribCount); + goto bail; + } + if (pRecord->recVersionNumber > kNuMaxRecordVersion) { + err = kNuErrBadRecord; + Nu_ReportError(NU_BLOB, err, "Unrecognized record version number (%u)", + pRecord->recVersionNumber); + goto bail; + } + if (pRecord->recTotalThreads > kNuReasonableTotalThreads) { + err = kNuErrBadRecord; + Nu_ReportError(NU_BLOB, err, "Unreasonable number of threads (%lu)", + pRecord->recTotalThreads); + goto bail; + } + + /* + * Read the option list, if present. + */ + if (pRecord->recVersionNumber > 0) { + pRecord->recOptionSize = Nu_ReadTwoC(pArchive, fp, &crc); + bytesRead += 2; + + /* + * It appears GS/ShrinkIt is creating bad option lists, claiming + * 36 bytes of data when there's only room for 18. Since we don't + * really pay attention to the option list + */ + if (pRecord->recOptionSize + bytesRead > pRecord->recAttribCount -2) { + DBUG(("--- truncating option list from %d to %d\n", + pRecord->recOptionSize, + pRecord->recAttribCount -2 - bytesRead)); + if (pRecord->recAttribCount -2 > bytesRead) + pRecord->recOptionSize = pRecord->recAttribCount -2 - bytesRead; + else + pRecord->recOptionSize = 0; + } + + /* this is the older test, which rejected funky archives */ + if (pRecord->recOptionSize + bytesRead > pRecord->recAttribCount -2) { + /* option size exceeds the total attribute area */ + err = kNuErrBadRecord; + Nu_ReportError(NU_BLOB, kNuErrBadRecord, + "Option size (%u) exceeds attribs (%u,%u-2)", + pRecord->recOptionSize, bytesRead, + pRecord->recAttribCount); + goto bail; + } + + if (pRecord->recOptionSize) { + pRecord->recOptionList = Nu_Malloc(pArchive,pRecord->recOptionSize); + BailAlloc(pRecord->recOptionList); + (void) Nu_ReadBytesC(pArchive, fp, pRecord->recOptionList, + pRecord->recOptionSize, &crc); + bytesRead += pRecord->recOptionSize; + } + } else { + pRecord->recOptionSize = 0; + pRecord->recOptionList = nil; + } + + /* last two bytes are the filename len; all else is "extra" */ + pRecord->extraCount = (pRecord->recAttribCount -2) - bytesRead; + Assert(pRecord->extraCount >= 0); + + /* + * Some programs (for example, NuLib) may leave extra junk in here. This + * is allowed by the archive spec. We may want to preserve it, so we + * allocate space for it and read it if it exists. + */ + if (pRecord->extraCount) { + pRecord->extraBytes = Nu_Malloc(pArchive, pRecord->extraCount); + BailAlloc(pRecord->extraBytes); + (void) Nu_ReadBytesC(pArchive, fp, pRecord->extraBytes, + pRecord->extraCount, &crc); + bytesRead += pRecord->extraCount; + } + + /* + * Read the in-record filename if one exists (likely in v0 records only). + */ + pRecord->recFilenameLength = Nu_ReadTwoC(pArchive, fp, &crc); + bytesRead += 2; + if (pRecord->recFilenameLength > kNuReasonableFilenameLen) { + err = kNuErrBadRecord; + Nu_ReportError(NU_BLOB, kNuErrBadRecord, "Filename length is huge (%u)", + pRecord->recFilenameLength); + goto bail; + } + if (pRecord->recFilenameLength) { + pRecord->recFilename = Nu_Malloc(pArchive, pRecord->recFilenameLength +1); + BailAlloc(pRecord->recFilename); + (void) Nu_ReadBytesC(pArchive, fp, pRecord->recFilename, + pRecord->recFilenameLength, &crc); + pRecord->recFilename[pRecord->recFilenameLength] = '\0'; + + bytesRead += pRecord->recFilenameLength; + + Nu_StripHiIfAllSet(pRecord->recFilename); + + /* use the in-header one */ + pRecord->filename = pRecord->recFilename; + } + + /* + * Read the threads records. The data is included in the record header + * CRC, so we have to pass that in too. + */ + pRecord->fakeThreads = 0; + err = Nu_ReadThreadHeaders(pArchive, pRecord, &crc); + BailError(err); + + /* + * After all is said and done, did we read the file without errors, + * and does the CRC match? + */ + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed reading late record header"); + goto bail; + } + if (!pArchive->valIgnoreCRC && crc != pRecord->recHeaderCRC) { + if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadRHCRC)) { + err = kNuErrBadRHCRC; + Nu_ReportError(NU_BLOB, err, "Stored RH CRC=0x%04x, calc=0x%04x", + pRecord->recHeaderCRC, crc); + Nu_ReportError(NU_BLOB_DEBUG, kNuErrNone, + "--- Problematic record is id=%ld", pRecord->recordIdx); + goto bail; + } + } + + /* + * Init or compute misc record fields. + */ + /* adjust "currentOffset" for the entire record header */ + pArchive->currentOffset += bytesRead; + pArchive->currentOffset += + (pRecord->recTotalThreads - pRecord->fakeThreads) * kNuThreadHeaderSize; + + pRecord->recHeaderLength = + bytesRead + pRecord->recTotalThreads * kNuThreadHeaderSize; + pRecord->recHeaderLength -= pRecord->fakeThreads * kNuThreadHeaderSize; + + err = Nu_ComputeThreadData(pArchive, pRecord); + BailError(err); + + /* check for "bad Mac" archives */ + if (pArchive->valHandleBadMac) { + if (pRecord->recFileSysInfo == '?' && + pRecord->recFileSysID == kNuFileSysMacMFS) + { + DBUG(("--- using 'bad mac' handling\n")); + pRecord->isBadMac = true; + pRecord->recFileSysInfo = ':'; + } + } + +bail: + if (err != kNuErrNone) + (void)Nu_FreeRecordContents(pArchive, pRecord); + return err; +} + + +/* + * Update the record's storageType if it looks like it needs it, based on + * the current set of threads. + * + * The rules we follow (stopping at the first match) are: + * - If there's a disk thread, leave it alone. Disk block size issues + * should already have been resolved. If we end up copying the same + * bogus block size we were given initially, that's fine. + * - If there's a resource fork, set the storageType to 5. + * - If there's a data fork, set the storageType to 1-3. + * - If there are no data-class threads at all, set the storageType to zero. + * + * This assumes that all updates have already been processed, i.e. there's + * no lingering add or delete threadMods. This only examines the thread + * array. + * + * NOTE: for data files (types 1, 2, and 3), the actual value may not match + * up what ProDOS would use, because this doesn't test for sparseness. + */ +static void +Nu_UpdateStorageType(NuArchive* pArchive, NuRecord* pRecord) +{ + NuError err; + NuThread* pThread; + + err = Nu_FindThreadByID(pRecord, kNuThreadIDDiskImage, &pThread); + if (err == kNuErrNone) + goto bail; + + err = Nu_FindThreadByID(pRecord, kNuThreadIDRsrcFork, &pThread); + if (err == kNuErrNone) { + DBUG(("--- setting storageType to %d (was %d)\n", kNuStorageExtended, + pRecord->recStorageType)); + pRecord->recStorageType = kNuStorageExtended; + goto bail; + } + + err = Nu_FindThreadByID(pRecord, kNuThreadIDDataFork, &pThread); + if (err == kNuErrNone) { + int newType; + if (pThread->actualThreadEOF <= 512) + newType = kNuStorageSeedling; + else if (pThread->actualThreadEOF < 131072) + newType = kNuStorageSapling; + else + newType = kNuStorageTree; + DBUG(("--- setting storageType to %d (was %d)\n", newType, + pRecord->recStorageType)); + pRecord->recStorageType = newType; + goto bail; + } + + DBUG(("--- no stuff here, setting storageType to %d (was %d)\n", + kNuStorageUnknown, pRecord->recStorageType)); + pRecord->recStorageType = kNuStorageUnknown; + +bail: + return; +} + +/* + * Write the record header to the current offset of the specified file. + * This includes writing all of the thread headers. + * + * We don't "promote" records to newer versions, because that might + * require expanding and CRCing data threads. Instead, we write the + * record in a manner appropriate for the version. + * + * As a side effect, this may update the storageType to something appropriate. + * + * The position of the file pointer on exit is undefined. The position + * past the end of the record will be stored in pArchive->currentOffset. + */ +NuError +Nu_WriteRecordHeader(NuArchive* pArchive, NuRecord* pRecord, FILE* fp) +{ + NuError err = kNuErrNone; + ushort crc; + long crcOffset; + int bytesWritten; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(fp != nil); + + /* + * Before we get started, let's make sure the storageType makes sense + * for this record. + */ + Nu_UpdateStorageType(pArchive, pRecord); + + DBUG(("--- Writing record header (v=%d)\n", pRecord->recVersionNumber)); + + (void) Nu_WriteBytes(pArchive, fp, pRecord->recNufxID, kNufxIDLen); + err = Nu_FTell(fp, &crcOffset); + BailError(err); + + /* + * Write the static fields. + */ + crc = 0; + Nu_WriteTwo(pArchive, fp, 0); /* crc -- come back later */ + Nu_WriteTwoC(pArchive, fp, pRecord->recAttribCount, &crc); + Nu_WriteTwoC(pArchive, fp, pRecord->recVersionNumber, &crc); + Nu_WriteFourC(pArchive, fp, pRecord->recTotalThreads, &crc); + Nu_WriteTwoC(pArchive, fp, (ushort)pRecord->recFileSysID, &crc); + Nu_WriteTwoC(pArchive, fp, pRecord->recFileSysInfo, &crc); + Nu_WriteFourC(pArchive, fp, pRecord->recAccess, &crc); + Nu_WriteFourC(pArchive, fp, pRecord->recFileType, &crc); + Nu_WriteFourC(pArchive, fp, pRecord->recExtraType, &crc); + Nu_WriteTwoC(pArchive, fp, pRecord->recStorageType, &crc); + Nu_WriteDateTimeC(pArchive, fp, pRecord->recCreateWhen, &crc); + Nu_WriteDateTimeC(pArchive, fp, pRecord->recModWhen, &crc); + Nu_WriteDateTimeC(pArchive, fp, pRecord->recArchiveWhen, &crc); + bytesWritten = 56; /* 4-byte 'NuFX' plus the above */ + + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed writing record header"); + goto bail; + } + + /* + * Write the option list, if present. + */ + if (pRecord->recVersionNumber > 0) { + Nu_WriteTwoC(pArchive, fp, pRecord->recOptionSize, &crc); + bytesWritten += 2; + + if (pRecord->recOptionSize) { + Nu_WriteBytesC(pArchive, fp, pRecord->recOptionList, + pRecord->recOptionSize, &crc); + bytesWritten += pRecord->recOptionSize; + } + } + + /* + * Preserve whatever miscellaneous junk was left in here by the last guy. + * We don't know what this is or why it's here, but who knows, maybe + * it's important. + * + * Besides, if we don't, we'll have to go back and fix the attrib count. + */ + if (pRecord->extraCount) { + Nu_WriteBytesC(pArchive, fp, pRecord->extraBytes, pRecord->extraCount, + &crc); + bytesWritten += pRecord->extraCount; + } + + /* + * If the record has a filename in the header, write it, unless + * recent changes have inspired us to drop the name from the header. + * + * Records that begin with no filename will have a default one + * stuffed in, so it's possible for pRecord->filename to be set + * already even if there wasn't one in the record. (In such cases, + * we don't write a name.) + */ + if (pRecord->recFilenameLength && !pRecord->dropRecFilename) { + Nu_WriteTwoC(pArchive, fp, pRecord->recFilenameLength, &crc); + bytesWritten += 2; + Nu_WriteBytesC(pArchive, fp, pRecord->recFilename, + pRecord->recFilenameLength, &crc); + } else { + Nu_WriteTwoC(pArchive, fp, 0, &crc); + bytesWritten += 2; + } + + /* make sure we are where we thought we would be */ + if (bytesWritten != pRecord->recAttribCount) { + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, kNuErrNone, + "Didn't write what was expected (%d vs %d)", + bytesWritten, pRecord->recAttribCount); + goto bail; + } + + /* write the thread headers, and zero out "fake" thread count */ + err = Nu_WriteThreadHeaders(pArchive, pRecord, fp, &crc); + BailError(err); + + /* get the current file offset, for some computations later */ + err = Nu_FTell(fp, &pArchive->currentOffset); + BailError(err); + + /* go back and fill in the CRC */ + pRecord->recHeaderCRC = crc; + err = Nu_FSeek(fp, crcOffset, SEEK_SET); + BailError(err); + Nu_WriteTwo(pArchive, fp, pRecord->recHeaderCRC); + + /* + * All okay? + */ + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed writing late record header"); + goto bail; + } + + /* + * Update values for misc record fields. + */ + Assert(pRecord->fakeThreads == 0); + pRecord->recHeaderLength = + bytesWritten + pRecord->recTotalThreads * kNuThreadHeaderSize; + pRecord->recHeaderLength -= pRecord->fakeThreads * kNuThreadHeaderSize; + + err = Nu_ComputeThreadData(pArchive, pRecord); + BailError(err); + +bail: + return err; +} + + +/* + * Prepare for a "walk" through the records. This is useful for the + * "read the TOC as you go" method of archive use. + */ +static NuError +Nu_RecordWalkPrepare(NuArchive* pArchive, NuRecord** ppRecord) +{ + NuError err = kNuErrNone; + + Assert(pArchive != nil); + Assert(ppRecord != nil); + + DBUG(("--- walk prep\n")); + + *ppRecord = nil; + + if (!pArchive->haveToc) { + /* might have tried and aborted earlier, rewind to start of records */ + err = Nu_RewindArchive(pArchive); + BailError(err); + } + +bail: + return err; +} + +/* + * Get the next record from the "orig" set in the archive. + * + * On entry, pArchive->archiveFp must point at the start of the next + * record. On exit, it will point past the end of the record (headers and + * all data) that we just read. + * + * If we have the TOC, we just pull it out of the structure. If we don't, + * we read it from the archive file, and add it to the TOC being + * constructed. + */ +static NuError +Nu_RecordWalkGetNext(NuArchive* pArchive, NuRecord** ppRecord) +{ + NuError err = kNuErrNone; + + Assert(pArchive != nil); + Assert(ppRecord != nil); + + /*DBUG(("--- walk toc=%d\n", pArchive->haveToc));*/ + + if (pArchive->haveToc) { + if (*ppRecord == nil) + *ppRecord = Nu_RecordSet_GetListHead(&pArchive->origRecordSet); + else + *ppRecord = (*ppRecord)->pNext; + } else { + *ppRecord = nil; /* so we don't try to free it on exit */ + + /* allocate and fill in a new record */ + err = Nu_RecordNew(pArchive, ppRecord); + BailError(err); + + /* read data from archive file */ + err = Nu_ReadRecordHeader(pArchive, *ppRecord); + BailError(err); + err = Nu_ScanThreads(pArchive, *ppRecord, (*ppRecord)->recTotalThreads); + BailError(err); + + DBUG(("--- Found record '%s'\n", (*ppRecord)->filename)); + + /* add to list */ + err = Nu_RecordSet_AddRecord(&pArchive->origRecordSet, *ppRecord); + BailError(err); + } + +bail: + if (err != kNuErrNone && !pArchive->haveToc) { + /* on failure, free whatever we allocated */ + Nu_RecordFree(pArchive, *ppRecord); + *ppRecord = nil; + } + return err; +} + +/* + * Finish off a successful record walk by noting that we now have a + * full table of contents. On an unsuccessful walk, blow away the TOC + * if we don't have all of it. + */ +static NuError +Nu_RecordWalkFinish(NuArchive* pArchive, NuError walkErr) +{ + if (pArchive->haveToc) + return kNuErrNone; + + if (walkErr == kNuErrNone) { + pArchive->haveToc = true; + /* mark as loaded, even if there weren't any entries (e.g. new arc) */ + Nu_RecordSet_SetLoaded(&pArchive->origRecordSet, true); + return kNuErrNone; + } else { + pArchive->haveToc = false; /* redundant */ + return Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->origRecordSet); + } +} + + +/* + * If we don't have the complete record listing from the archive in + * the "orig" record set, go get it. + * + * Uses the "record walk" functions, because they're there. + */ +NuError +Nu_GetTOCIfNeeded(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + ulong count; + + Assert(pArchive != nil); + + if (pArchive->haveToc) + goto bail; + + DBUG(("--- GetTOCIfNeeded\n")); + + err = Nu_RecordWalkPrepare(pArchive, &pRecord); + BailError(err); + + count = pArchive->masterHeader.mhTotalRecords; + while (count--) { + err = Nu_RecordWalkGetNext(pArchive, &pRecord); + BailError(err); + } + +bail: + (void) Nu_RecordWalkFinish(pArchive, err); + return err; +} + + + +/* + * =========================================================================== + * Streaming read-only operations + * =========================================================================== + */ + +/* + * Run through the entire archive, pulling out the header bits, skipping + * over the data bits, and calling "contentFunc" for each record. + */ +NuError +Nu_StreamContents(NuArchive* pArchive, NuCallback contentFunc) +{ + NuError err = kNuErrNone; + NuRecord tmpRecord; + NuResult result; + ulong count; + + if (contentFunc == nil) { + err = kNuErrInvalidArg; + goto bail; + } + + Nu_InitRecordContents(pArchive, &tmpRecord); + count = pArchive->masterHeader.mhTotalRecords; + + while (count--) { + err = Nu_ReadRecordHeader(pArchive, &tmpRecord); + BailError(err); + err = Nu_ScanThreads(pArchive, &tmpRecord, tmpRecord.recTotalThreads); + BailError(err); + + /*Nu_DebugDumpRecord(&tmpRecord); + printf("\n");*/ + + /* let them display the contents */ + result = (*contentFunc)(pArchive, &tmpRecord); + if (result == kNuAbort) { + err = kNuErrAborted; + goto bail; + } + + /* dispose of the entry */ + (void) Nu_FreeRecordContents(pArchive, &tmpRecord); + (void) Nu_InitRecordContents(pArchive, &tmpRecord); + } + +bail: + (void) Nu_FreeRecordContents(pArchive, &tmpRecord); + return err; +} + + +/* + * If we're trying to be compatible with ShrinkIt, and we tried to extract + * a record that had nothing in it but comments and filenames, then we need + * to create a zero-byte data file. + * + * GS/ShrinkIt v1.1 has a bug that causes it to store zero-byte data files + * (and, for that matter, zero-byte resource forks) without a thread header. + * It isn't able to extract them. This isn't so much a compatibility + * thing as it is a bug-workaround thing. + * + * The record's storage type should tell us if it was an extended file or + * a plain file. Not really important when extracting, but if we want + * to recreate the original we need to re-add the resource fork so + * NufxLib knows to make it an extended file. + */ +static NuError +Nu_FakeZeroExtract(NuArchive* pArchive, NuRecord* pRecord, int threadKind) +{ + NuError err; + NuThread fakeThread; + + Assert(pRecord != nil); + + DBUG(("--- found empty record, creating zero-byte file (kind=0x%04x)\n", + threadKind)); + fakeThread.thThreadClass = kNuThreadClassData; + fakeThread.thThreadFormat = kNuThreadFormatUncompressed; + fakeThread.thThreadKind = threadKind; + fakeThread.thThreadCRC = kNuInitialThreadCRC; + fakeThread.thThreadEOF = 0; + fakeThread.thCompThreadEOF = 0; + + fakeThread.threadIdx = (NuThreadIdx)-1; /* shouldn't matter */ + fakeThread.actualThreadEOF = 0; + fakeThread.fileOffset = 0; /* shouldn't matter */ + fakeThread.used = false; + + err = Nu_ExtractThreadBulk(pArchive, pRecord, &fakeThread); + if (err == kNuErrSkipped) + err = Nu_SkipThread(pArchive, pRecord, &fakeThread); + + return err; +} + + +/* + * Run through the entire archive, extracting the contents. + */ +NuError +Nu_StreamExtract(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + NuRecord tmpRecord; + Boolean hasInterestingThread; + ulong count; + long idx; + + /* reset this just to be safe */ + pArchive->lastDirCreated = nil; + + Nu_InitRecordContents(pArchive, &tmpRecord); + count = pArchive->masterHeader.mhTotalRecords; + + while (count--) { + /* + * Read the record header (which includes the thread header blocks). + */ + err = Nu_ReadRecordHeader(pArchive, &tmpRecord); + BailError(err); + + /* + * We may need to pull the filename out of a thread, but we don't + * want to blow past any data while we do it. There's no really + * good way to deal with this, so we just assume that all NuFX + * applications are nice and put the filename thread first. + */ + for (idx = 0; idx < (long)tmpRecord.recTotalThreads; idx++) { + const NuThread* pThread = Nu_GetThread(&tmpRecord, idx); + + if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) + == kNuThreadIDFilename) + { + break; + } + } + /* if we have fn, read it; either way, leave idx pointing at next */ + if (idx < (long)tmpRecord.recTotalThreads) { + idx++; /* want count, not index */ + err = Nu_ScanThreads(pArchive, &tmpRecord, idx); + BailError(err); + } else + idx = 0; + if (tmpRecord.filename == nil) { + Nu_ReportError(NU_BLOB, kNuErrNone, + "Couldn't find filename in record"); + err = kNuErrBadRecord; + goto bail; + } + + /*Nu_DebugDumpRecord(&tmpRecord); + printf("\n");*/ + + hasInterestingThread = false; + + /* extract all relevant (remaining) threads */ + pArchive->lastFileCreated = nil; + for ( ; idx < (long)tmpRecord.recTotalThreads; idx++) { + const NuThread* pThread = Nu_GetThread(&tmpRecord, idx); + + if (pThread->thThreadClass == kNuThreadClassData) { + hasInterestingThread = true; + err = Nu_ExtractThreadBulk(pArchive, &tmpRecord, pThread); + if (err == kNuErrSkipped) { + err = Nu_SkipThread(pArchive, &tmpRecord, pThread); + BailError(err); + } else if (err != kNuErrNone) + goto bail; + } else { + DBUG(("IGNORING 0x%08lx from '%s'\n", + NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), + tmpRecord.filename)); + if (NuGetThreadID(pThread) != kNuThreadIDComment && + NuGetThreadID(pThread) != kNuThreadIDFilename) + { + hasInterestingThread = true; + } + err = Nu_SkipThread(pArchive, &tmpRecord, pThread); + BailError(err); + } + } + + /* + * If we're trying to be compatible with ShrinkIt, and the record + * had nothing in it but comments and filenames, then we need to + * create a zero-byte data file (and possibly a resource fork). + * + * See notes in previous instance, above. + */ + if (/*pArchive->valMaskDataless &&*/ !hasInterestingThread) { + err = Nu_FakeZeroExtract(pArchive, &tmpRecord, 0x0000); + BailError(err); + if (tmpRecord.recStorageType == kNuStorageExtended) { + err = Nu_FakeZeroExtract(pArchive, &tmpRecord, 0x0002); + BailError(err); + } + } + + /* dispose of the entry */ + (void) Nu_FreeRecordContents(pArchive, &tmpRecord); + (void) Nu_InitRecordContents(pArchive, &tmpRecord); + } + +bail: + (void) Nu_FreeRecordContents(pArchive, &tmpRecord); + return err; +} + +/* + * Test the contents of an archive. Works just like extraction, but we + * don't store anything. + */ +NuError +Nu_StreamTest(NuArchive* pArchive) +{ + NuError err; + + pArchive->testMode = true; + err = Nu_StreamExtract(pArchive); + pArchive->testMode = false; + return err; +} + + +/* + * =========================================================================== + * Non-streaming read-only operations + * =========================================================================== + */ + +/* + * Shove the archive table of contents through the callback function. + * + * This only walks through the "orig" list, so it does not reflect the + * results of un-flushed changes. + */ +NuError +Nu_Contents(NuArchive* pArchive, NuCallback contentFunc) +{ + NuError err = kNuErrNone; + NuRecord* pRecord; + NuResult result; + ulong count; + + if (contentFunc == nil) { + err = kNuErrInvalidArg; + goto bail; + } + + err = Nu_RecordWalkPrepare(pArchive, &pRecord); + BailError(err); + + count = pArchive->masterHeader.mhTotalRecords; + while (count--) { + err = Nu_RecordWalkGetNext(pArchive, &pRecord); + BailError(err); + + Assert(pRecord->filename != nil); + result = (*contentFunc)(pArchive, pRecord); + if (result == kNuAbort) { + err = kNuErrAborted; + goto bail; + } + } + +bail: + (void) Nu_RecordWalkFinish(pArchive, err); + return err; +} + + +/* + * Extract all interesting threads from a record, given a NuRecord pointer + * into the archive data structure. + * + * This assumes random access, so it can't be used in streaming mode. + */ +static NuError +Nu_ExtractRecordByPtr(NuArchive* pArchive, NuRecord* pRecord) +{ + NuError err = kNuErrNone; + Boolean hasInterestingThread; + ulong idx; + + Assert(!Nu_IsStreaming(pArchive)); /* we don't skip things we don't read */ + Assert(pRecord != nil); + + /* extract all relevant threads */ + hasInterestingThread = false; + pArchive->lastFileCreated = nil; + for (idx = 0; idx < pRecord->recTotalThreads; idx++) { + const NuThread* pThread = Nu_GetThread(pRecord, idx); + + if (pThread->thThreadClass == kNuThreadClassData) { + hasInterestingThread = true; + err = Nu_ExtractThreadBulk(pArchive, pRecord, pThread); + if (err == kNuErrSkipped) { + err = Nu_SkipThread(pArchive, pRecord, pThread); + BailError(err); + } else if (err != kNuErrNone) + goto bail; + } else { + if (NuGetThreadID(pThread) != kNuThreadIDComment && + NuGetThreadID(pThread) != kNuThreadIDFilename) + { + hasInterestingThread = true; + } + DBUG(("IGNORING 0x%08lx from '%s'\n", + NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), + pRecord->filename)); + } + } + + /* + * If we're trying to be compatible with ShrinkIt, and the record + * had nothing in it but comments and filenames, then we need to + * create a zero-byte file. + * + * (GSHK handles empty data and resource forks by not storing a + * thread at all. It doesn't correctly deal with them when extracting + * though, so it appears this behavior wasn't entirely expected.) + * + * If it's a forked file, we also need to create an empty rsrc file. + * + * If valMaskDataless is enabled, this won't fire, because we "forge" + * appropriate threads. + * + * Note there's another one of these below, in Nu_StreamExtract. + */ + if (/*pArchive->valMaskDataless &&*/ !hasInterestingThread) { + err = Nu_FakeZeroExtract(pArchive, pRecord, 0x0000 /*data*/); + BailError(err); + if (pRecord->recStorageType == kNuStorageExtended) { + err = Nu_FakeZeroExtract(pArchive, pRecord, 0x0002 /*rsrc*/); + BailError(err); + } + } + +bail: + return err; +} + + +/* + * Extract a big buncha files. + */ +NuError +Nu_Extract(NuArchive* pArchive) +{ + NuError err; + NuRecord* pRecord = nil; + ulong count; + long offset; + + /* reset this just to be safe */ + pArchive->lastDirCreated = nil; + + err = Nu_RecordWalkPrepare(pArchive, &pRecord); + BailError(err); + + count = pArchive->masterHeader.mhTotalRecords; + while (count--) { + /* read the record and threads if we don't have them yet */ + err = Nu_RecordWalkGetNext(pArchive, &pRecord); + BailError(err); + + if (!pArchive->haveToc) { + /* remember where the end of the record is */ + err = Nu_FTell(pArchive->archiveFp, &offset); + BailError(err); + } + + /* extract one or more threads */ + err = Nu_ExtractRecordByPtr(pArchive, pRecord); + BailError(err); + + if (!pArchive->haveToc) { + /* line us back up so RecordWalkGetNext can read the record hdr */ + err = Nu_FSeek(pArchive->archiveFp, offset, SEEK_SET); + BailError(err); + } + } + +bail: + (void) Nu_RecordWalkFinish(pArchive, err); + return err; +} + + +/* + * Extract a single record. + */ +NuError +Nu_ExtractRecord(NuArchive* pArchive, NuRecordIdx recIdx) +{ + NuError err; + NuRecord* pRecord; + + if (Nu_IsStreaming(pArchive)) + return kNuErrUsage; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* find the correct record by index */ + err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recIdx, &pRecord); + BailError(err); + Assert(pRecord != nil); + + /* extract whatever looks promising */ + err = Nu_ExtractRecordByPtr(pArchive, pRecord); + BailError(err); + +bail: + return err; +} + + +/* + * Test the contents of an archive. Works just like extraction, but we + * don't store anything. + */ +NuError +Nu_Test(NuArchive* pArchive) +{ + NuError err; + + pArchive->testMode = true; + err = Nu_Extract(pArchive); + pArchive->testMode = false; + return err; +} + +/* + * Test a single record. + */ +NuError +Nu_TestRecord(NuArchive* pArchive, NuRecordIdx recIdx) +{ + NuError err; + NuRecord* pRecord; + + if (Nu_IsStreaming(pArchive)) + return kNuErrUsage; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* find the correct record by index */ + err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recIdx, &pRecord); + BailError(err); + Assert(pRecord != nil); + + /* extract whatever looks promising */ + pArchive->testMode = true; + err = Nu_ExtractRecordByPtr(pArchive, pRecord); + pArchive->testMode = false; + BailError(err); + +bail: + return err; +} + + +/* + * Return a pointer to a NuRecord. + * + * This pulls the record out of the "orig" set, so it will work even + * for records that have been deleted. It will not reflect changes + * made by previous "write" calls, not even SetRecordAttr. + */ +NuError +Nu_GetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecord** ppRecord) +{ + NuError err; + + if (recordIdx == 0 || ppRecord == nil) + return kNuErrInvalidArg; + + if (Nu_IsStreaming(pArchive)) + return kNuErrUsage; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recordIdx, + (NuRecord**)ppRecord); + if (err == kNuErrNone) { + Assert(*ppRecord != nil); + } + /* fall through with error */ + +bail: + return err; +} + +/* + * Find the recordIdx of a record by storage name. + */ +NuError +Nu_GetRecordIdxByName(NuArchive* pArchive, const char* name, + NuRecordIdx* pRecordIdx) +{ + NuError err; + NuRecord* pRecord = nil; + + if (pRecordIdx == nil) + return kNuErrInvalidArg; + + if (Nu_IsStreaming(pArchive)) + return kNuErrUsage; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + err = Nu_RecordSet_FindByName(&pArchive->origRecordSet, name, &pRecord); + if (err == kNuErrNone) { + Assert(pRecord != nil); + *pRecordIdx = pRecord->recordIdx; + } + /* fall through with error */ + +bail: + return err; +} + +/* + * Find the recordIdx of a record by zero-based position. + */ +NuError +Nu_GetRecordIdxByPosition(NuArchive* pArchive, ulong position, + NuRecordIdx* pRecordIdx) +{ + NuError err; + const NuRecord* pRecord; + + if (pRecordIdx == nil) + return kNuErrInvalidArg; + + if (Nu_IsStreaming(pArchive)) + return kNuErrUsage; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + if (position >= Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet)) { + err = kNuErrRecordNotFound; + goto bail; + } + + pRecord = Nu_RecordSet_GetListHead(&pArchive->origRecordSet); + while (position--) { + Assert(pRecord->pNext != nil); + pRecord = pRecord->pNext; + } + + *pRecordIdx = pRecord->recordIdx; + +bail: + return err; +} + + +/* + * =========================================================================== + * Read/write record operations (add, delete) + * =========================================================================== + */ + +/* + * Find an existing record somewhere in the archive. If the "copy" set + * exists it will be searched. If not, the "orig" set is searched, and + * if an entry is found a "copy" set will be created. + * + * The goal is to always return something from the "copy" set, which we + * could do easily by just creating the "copy" set and then searching in + * it. However, we don't want to create the "copy" set if we don't have + * to, so we search "orig" if "copy" doesn't exist yet. + * + * The record returned will always be from the "copy" set. An error result + * is returned if the record isn't found. + */ +NuError +Nu_FindRecordForWriteByIdx(NuArchive* pArchive, NuRecordIdx recIdx, + NuRecord** ppFoundRecord) +{ + NuError err; + + Assert(pArchive != nil); + Assert(ppFoundRecord != nil); + + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + err = Nu_RecordSet_FindByIdx(&pArchive->copyRecordSet, recIdx, + ppFoundRecord); + } else { + Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); + err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recIdx, + ppFoundRecord); + *ppFoundRecord = nil; /* can't delete from here */ + } + BailErrorQuiet(err); + + /* + * The record exists. If we were looking in the "orig" set, we have + * to create a "copy" set and return it from there. + */ + if (*ppFoundRecord == nil) { + err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, + &pArchive->origRecordSet); + BailError(err); + err = Nu_RecordSet_FindByIdx(&pArchive->copyRecordSet, recIdx, + ppFoundRecord); + Assert(err == kNuErrNone && *ppFoundRecord != nil); /* must succeed */ + BailError(err); + } + +bail: + return err; +} + + +/* + * Deal with the situation where we're trying to add a record with the + * same name as an existing record. The existing record can't be in the + * "new" list (that's handled differently) and can't already have been + * deleted. + * + * This will either delete the existing record or return with an error. + * + * If we decide to delete the record, and the "orig" record set was + * passed in, then the record will be deleted from the "copy" set (which + * will be created only if necessary). + */ +static NuError +Nu_HandleAddDuplicateRecord(NuArchive* pArchive, NuRecordSet* pRecordSet, + NuRecord* pRecord, const NuFileDetails* pFileDetails) +{ + NuError err = kNuErrNone; + NuErrorStatus errorStatus; + NuResult result; + + Assert(pRecordSet == &pArchive->origRecordSet || + pRecordSet == &pArchive->copyRecordSet); + Assert(pRecord != nil); + Assert(pFileDetails != nil); + Assert(pArchive->valAllowDuplicates == false); + + /* + * If "only update older" is set, check the dates. Reject the + * request if the archived file isn't older than the new file. This + * tells the application that the request was rejected, but it's + * okay for them to move on to the next file. + */ + if (pArchive->valOnlyUpdateOlder) { + if (!Nu_IsOlder(&pRecord->recModWhen, &pFileDetails->modWhen)) + return kNuErrNotNewer; + } + + /* + * The file exists when it shouldn't. Decide what to do, based + * on the options configured by the application. + * + * If they "might" allow overwrites, and they have an error-handling + * callback defined, call that to find out what they want to do + * here. Options include skipping or overwriting the record. + * + * We don't currently allow renaming of records, though I suppose we + * could. + */ + switch (pArchive->valHandleExisting) { + case kNuMaybeOverwrite: + if (pArchive->errorHandlerFunc != nil) { + errorStatus.operation = kNuOpAdd; + errorStatus.err = kNuErrRecordExists; + errorStatus.sysErr = 0; + errorStatus.message = nil; + errorStatus.pRecord = pRecord; + errorStatus.pathname = pFileDetails->storageName; + errorStatus.origPathname = pFileDetails->origName; + errorStatus.filenameSeparator = + NuGetSepFromSysInfo(pFileDetails->fileSysInfo); + /*errorStatus.origArchiveTouched = false;*/ + errorStatus.canAbort = true; + errorStatus.canRetry = false; + errorStatus.canIgnore = false; + errorStatus.canSkip = true; + errorStatus.canRename = false; + errorStatus.canOverwrite = true; + + result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); + + switch (result) { + case kNuAbort: + err = kNuErrAborted; + goto bail; + case kNuSkip: + err = kNuErrSkipped; + goto bail; + case kNuOverwrite: + break; /* fall back into main code */ + case kNuRetry: + case kNuRename: + case kNuIgnore: + default: + err = kNuErrSyntax; + Nu_ReportError(NU_BLOB, err, + "Wasn't expecting result %d here", result); + goto bail; + } + } else { + /* no error handler, treat like NeverOverwrite */ + err = kNuErrSkipped; + goto bail; + } + break; + case kNuNeverOverwrite: + err = kNuErrSkipped; + goto bail; + case kNuMustOverwrite: + case kNuAlwaysOverwrite: + /* fall through to record deletion */ + break; + default: + Assert(0); + err = kNuErrInternal; + goto bail; + } + + err = kNuErrNone; + + /* + * We're going to overwrite the existing record. To do this, we have + * to start by deleting it from the "copy" list. + * + * If the copy set doesn't yet exist, we have to create it and find + * the record in the new set. + */ + if (pRecordSet == &pArchive->origRecordSet) { + Assert(!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)); + err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, + &pArchive->origRecordSet); + BailError(err); + + err = Nu_RecordSet_FindByIdx(&pArchive->copyRecordSet, + pRecord->recordIdx, &pRecord); + Assert(err == kNuErrNone && pRecord != nil); /* must succeed */ + BailError(err); + } + + DBUG(("+++ deleting record %ld\n", pRecord->recordIdx)); + err = Nu_RecordSet_DeleteRecord(pArchive,&pArchive->copyRecordSet, pRecord); + BailError(err); + +bail: + return err; +} + +/* + * Create a new record, filling in most of the blanks from "pFileDetails". + * + * The filename in pFileDetails->storageName will be remembered. If no + * filename thread is added to this record before the next Flush call, a + * filename thread will be generated from this name. + * + * This always creates a "version 3" record, regardless of what else is + * in the archive. The filename is always in a thread. + * + * On success, the NuRecordIdx of the newly-created record will be placed + * in "*pRecordIdx", and the NuThreadIdx of the filename thread will be + * placed in "*pThreadIdx". If "*ppNewRecord" is non-nil, it gets a pointer + * to the newly-created record (this isn't part of the external interface). + */ +NuError +Nu_AddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails, + NuRecordIdx* pRecordIdx, NuRecord** ppNewRecord) +{ + NuError err; + NuRecord* pNewRecord = nil; + + if (pFileDetails == nil || pFileDetails->storageName == nil || + pFileDetails->storageName[0] == '\0' || + NuGetSepFromSysInfo(pFileDetails->fileSysInfo) == 0) + /* pRecordIdx may be nil */ + /* ppNewRecord may be nil */ + { + err = kNuErrInvalidArg; + goto bail; + } + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* NuFX spec forbids leading fssep chars */ + if (pFileDetails->storageName[0] == + NuGetSepFromSysInfo(pFileDetails->fileSysInfo)) + { + err = kNuErrLeadingFssep; + goto bail; + } + + /* + * If requested, look for an existing record. Look in the "copy" + * list if we have it (so we don't complain if they've already deleted + * the record), or in the "orig" list if we don't. Look in the "new" + * list to see if it clashes with something we've just added. + * + * If this is a brand-new archive, there won't be an "orig" list + * either. + */ + if (!pArchive->valAllowDuplicates) { + NuRecordSet* pRecordSet; + NuRecord* pFoundRecord; + + pRecordSet = &pArchive->copyRecordSet; + if (!Nu_RecordSet_GetLoaded(pRecordSet)) + pRecordSet = &pArchive->origRecordSet; + Assert(Nu_RecordSet_GetLoaded(pRecordSet)); + err = Nu_RecordSet_FindByName(pRecordSet, pFileDetails->storageName, + &pFoundRecord); + if (err == kNuErrNone) { + /* handle the existing record */ + DBUG(("--- Duplicate record found (%06ld) '%s'\n", + pFoundRecord->recordIdx, pFoundRecord->filename)); + err = Nu_HandleAddDuplicateRecord(pArchive, pRecordSet, + pFoundRecord, pFileDetails); + if (err != kNuErrNone) { + /* for whatever reason, we're not replacing it */ + DBUG(("--- Returning err=%d\n", err)); + goto bail; + } + } else { + /* if we *must* replace an existing file, we fail now */ + if (pArchive->valHandleExisting == kNuMustOverwrite) { + DBUG(("+++ can't freshen nonexistent '%s'\n", + pFileDetails->storageName)); + err = kNuErrDuplicateNotFound; + goto bail; + } + } + + if (Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) { + err = Nu_RecordSet_FindByName(&pArchive->newRecordSet, + pFileDetails->storageName, &pFoundRecord); + if (err == kNuErrNone) { + /* we can't delete from the "new" list, so return an error */ + err = kNuErrRecordExists; + goto bail; + } + } + + /* clear "err" so we can continue */ + err = kNuErrNone; + } + + /* + * Prepare the new record structure. + */ + err = Nu_RecordNew(pArchive, &pNewRecord); + BailError(err); + (void) Nu_InitRecordContents(pArchive, pNewRecord); + memcpy(pNewRecord->recNufxID, kNufxID, kNufxIDLen); + /*pNewRecord->recHeaderCRC*/ + /*pNewRecord->recAttribCount*/ + pNewRecord->recVersionNumber = kNuOurRecordVersion; + pNewRecord->recTotalThreads = 0; + pNewRecord->recFileSysID = pFileDetails->fileSysID; + pNewRecord->recFileSysInfo = pFileDetails->fileSysInfo; + pNewRecord->recAccess = pFileDetails->access; + pNewRecord->recFileType = pFileDetails->fileType; + pNewRecord->recExtraType = pFileDetails->extraType; + pNewRecord->recStorageType = pFileDetails->storageType; + pNewRecord->recCreateWhen = pFileDetails->createWhen; + pNewRecord->recModWhen = pFileDetails->modWhen; + pNewRecord->recArchiveWhen = pFileDetails->archiveWhen; + pNewRecord->recOptionSize = 0; + pNewRecord->extraCount = 0; + pNewRecord->recFilenameLength = 0; + + pNewRecord->recordIdx = Nu_GetNextRecordIdx(pArchive); + pNewRecord->threadFilename = nil; + pNewRecord->newFilename = strdup(pFileDetails->storageName); + pNewRecord->filename = pNewRecord->newFilename; + pNewRecord->recHeaderLength = -1; + pNewRecord->totalCompLength = 0; + pNewRecord->fakeThreads = 0; + pNewRecord->fileOffset = -1; + + /* + * Add it to the "new" record set. + */ + err = Nu_RecordSet_AddRecord(&pArchive->newRecordSet, pNewRecord); + BailError(err); + + /* return values */ + if (pRecordIdx != nil) + *pRecordIdx = pNewRecord->recordIdx; + if (ppNewRecord != nil) + *ppNewRecord = pNewRecord; + +bail: + return err; +} + + +/* + * Add a new "add file" thread mod to the specified record. + * + * The caller should have already verified that there isn't another + * "add file" thread mod with the same ThreadID. + */ +static NuError +Nu_AddFileThreadMod(NuArchive* pArchive, NuRecord* pRecord, + const char* pathname, const NuFileDetails* pFileDetails, + Boolean fromRsrcFork) +{ + NuError err; + NuThreadFormat threadFormat; + NuDataSource* pDataSource = nil; + NuThreadMod* pThreadMod = nil; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(pathname != nil); + Assert(pFileDetails != nil); + Assert(fromRsrcFork == true || fromRsrcFork == false); + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + + /* decide if this should be compressed; we know source isn't */ + if (Nu_IsCompressibleThreadID(pFileDetails->threadID)) + threadFormat = Nu_ConvertCompressValToFormat(pArchive, + pArchive->valDataCompression); + else + threadFormat = kNuThreadFormatUncompressed; + + /* create a data source for this file, which is assumed uncompressed */ + err = Nu_DataSourceFile_New(kNuThreadFormatUncompressed, 0, + pathname, fromRsrcFork, &pDataSource); + BailError(err); + + /* create a new ThreadMod */ + err = Nu_ThreadModAdd_New(pArchive, pFileDetails->threadID, threadFormat, + pDataSource, &pThreadMod); + BailError(err); + Assert(pThreadMod != nil); + /*pDataSource = nil;*/ /* ThreadModAdd_New makes a copy */ + + /* add the thread mod to the record */ + Nu_RecordAddThreadMod(pRecord, pThreadMod); + pThreadMod = nil; /* don't free on exit */ + +bail: + if (pDataSource != nil) + Nu_DataSourceFree(pDataSource); + if (pThreadMod != nil) + Nu_ThreadModFree(pArchive, pThreadMod); + return err; +} + +/* + * Make note of a file to add. This goes beyond AddRecord and AddThread + * calls by searching the list of newly-added files for matching pairs + * of data and rsrc forks. This is independent of the "overwrite existing + * files" feature. The comparison is made based on storageName. + * + * "fromRsrcFork" tells us how to open the source file, not what type + * of thread the file should be stored as. + * + * If "pRecordIdx" is non-nil, it will receive the newly assigned recordID. + */ +NuError +Nu_AddFile(NuArchive* pArchive, const char* pathname, + const NuFileDetails* pFileDetails, Boolean fromRsrcFork, + NuRecordIdx* pRecordIdx) +{ + NuError err = kNuErrNone; + NuRecordIdx recordIdx = 0; + NuRecord* pRecord; + + if (pathname == nil || pFileDetails == nil || + !(fromRsrcFork == true || fromRsrcFork == false)) + { + return kNuErrInvalidArg; + } + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + if (pFileDetails->storageName == nil) { + err = kNuErrInvalidArg; + Nu_ReportError(NU_BLOB, err, "Must specify storageName"); + goto bail; + } + if (pFileDetails->storageName[0] == + NuGetSepFromSysInfo(pFileDetails->fileSysInfo)) + { + err = kNuErrLeadingFssep; + goto bail; + } + + DBUG(("+++ ADDING '%s' (%s) 0x%02lx 0x%04lx threadID=0x%08lx\n", pathname, + pFileDetails->storageName, pFileDetails->fileType, + pFileDetails->extraType, pFileDetails->threadID)); + + /* + * See if there's another record among the "new additions" with the + * same storageName and compatible threads. + * + * If found, add a new thread in that record. If an incompatibility + * exists (same fork already present, disk image is there, etc), either + * create a new record or return with an error. + * + * We want to search from the *end* of the "new" list, so that if + * duplicates are allowed we find the entry most likely to be paired + * up with the fork currently being added. + */ + if (Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) { + NuRecord* pNewRecord; + + err = Nu_RecordSet_ReverseFindByName(&pArchive->newRecordSet, + pFileDetails->storageName, &pNewRecord); + if (err == kNuErrNone) { + /* is it okay to add it here? */ + err = Nu_OkayToAddThread(pArchive, pNewRecord, + pFileDetails->threadID); + + if (err == kNuErrNone) { + /* okay to add it to this record */ + DBUG((" attaching to existing record %06ld\n", + pNewRecord->recordIdx)); + err = Nu_AddFileThreadMod(pArchive, pNewRecord, pathname, + pFileDetails, fromRsrcFork); + BailError(err); + recordIdx = pNewRecord->recordIdx; + goto bail; /* we're done! */ + } + + err = kNuErrNone; /* go a little farther */ + + /* + * We found a brand-new record with the same name, but we + * can't add this fork to that record. We can't delete the + * item from the "new" list, so we can ignore HandleExisting. + * If we don't allow duplicates, return an error; if we do, + * then just continue with the normal processing path. + */ + if (!pArchive->valAllowDuplicates) { + DBUG(("+++ found matching record in new list, no dups\n")); + err = kNuErrRecordExists; + goto bail; + } + + } else if (err == kNuErrRecNameNotFound) { + /* no match in "new" list, fall through to normal processing */ + err = kNuErrNone; + } else { + /* general failure */ + goto bail; + } + } + + /* + * Wasn't found, invoke Nu_AddRecord. This will search through the + * existing records, using the "allow duplicates" flag to cope with + * any matches it finds. On success, we should have a brand-new record + * to play with. + */ + err = Nu_AddRecord(pArchive, pFileDetails, &recordIdx, &pRecord); + BailError(err); + DBUG(("--- Added new record %06ld\n", recordIdx)); + + /* + * Got the record, now add a data file thread. + */ + err = Nu_AddFileThreadMod(pArchive, pRecord, pathname, pFileDetails, + fromRsrcFork); + BailError(err); + +bail: + if (err == kNuErrNone && pRecordIdx != nil) + *pRecordIdx = recordIdx; + + return err; +} + + +/* + * Rename a record. There are three situations: + * + * (1) Record has the filename in a thread, and the field has enough + * room to hold the new name. For this case we add an "update" threadMod + * with the new data. + * (2) Record has the filename in a thread, and there is not enough room + * to hold the new name. Here, we add a "delete" threadMod for the + * existing filename, and add an "add" threadMod for the new. + * (3) Record stores the filename in the header. We zero out the filename + * and add a filename thread. + * + * We don't actually check to see if the filename is changing. If you + * want to rename something to the same thing, go right ahead. (This + * provides a way for applications to "filter" records that have filenames + * in the headers instead of a thread.) + * + * BUG: we shouldn't allow a disk image to be renamed to have a complex + * path name (e.g. "dir1:dir2:foo"). However, we may not be able to catch + * that here depending on pending operations. + * + * We might also want to screen out trailing fssep chars, though the NuFX + * spec doesn't say they're illegal. + */ +NuError +Nu_Rename(NuArchive* pArchive, NuRecordIdx recIdx, const char* pathname, + char fssep) +{ + NuError err; + NuRecord* pRecord; + NuThread* pFilenameThread; + const NuThreadMod* pThreadMod; + NuThreadMod* pNewThreadMod = nil; + NuDataSource* pDataSource = nil; + long requiredCapacity, existingCapacity, newCapacity; + Boolean doDelete, doAdd, doUpdate; + + if (recIdx == 0 || pathname == nil || pathname[0] == '\0' || fssep == '\0') + return kNuErrInvalidArg; + + if (pathname[0] == fssep) { + err = kNuErrLeadingFssep; + Nu_ReportError(NU_BLOB, err, "rename path"); + goto bail; + } + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* find the record in the "copy" set */ + err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); + BailError(err); + Assert(pRecord != nil); + + /* look for a filename thread */ + err = Nu_FindThreadByID(pRecord, kNuThreadIDFilename, &pFilenameThread); + + if (err != kNuErrNone) + pFilenameThread = nil; + else if (err == kNuErrNone && pRecord->pThreadMods) { + /* found a thread, check to see if it has been deleted (or modifed) */ + Assert(pFilenameThread != nil); + pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, + pFilenameThread->threadIdx); + if (pThreadMod != nil) { + DBUG(("--- tried to modify threadIdx %ld, which has already been\n", + pFilenameThread->threadIdx)); + err = kNuErrModThreadChange; + goto bail; + } + } + + /* + * Looks like we're okay so far. Figure out what to do. + */ + doDelete = doAdd = doUpdate = false; + newCapacity = existingCapacity = 0; + requiredCapacity = strlen(pathname); + + if (pFilenameThread != nil) { + existingCapacity = pFilenameThread->thCompThreadEOF; + if (existingCapacity >= requiredCapacity) { + doUpdate = true; + newCapacity = existingCapacity; + } else { + doDelete = doAdd = true; + /* make sure they have a few bytes of leeway */ + /*newCapacity = (requiredCapacity + kNuDefaultFilenameThreadSize) & + (~(kNuDefaultFilenameThreadSize-1));*/ + newCapacity = requiredCapacity + 8; + } + } else { + doAdd = true; + /*newCapacity = (requiredCapacity + kNuDefaultFilenameThreadSize) & + (~(kNuDefaultFilenameThreadSize-1));*/ + newCapacity = requiredCapacity + 8; + } + + Assert(doAdd || doDelete || doUpdate); + Assert(doDelete == false || doAdd == true); + + /* create a data source for the filename, if needed */ + if (doAdd || doUpdate) { + Assert(newCapacity); + err = Nu_DataSourceBuffer_New(kNuThreadFormatUncompressed, + newCapacity, (const uchar*)strdup(pathname), 0, + requiredCapacity /*(strlen)*/, Nu_InternalFreeCallback, + &pDataSource); + BailError(err); + } + + if (doDelete) { + err = Nu_ThreadModDelete_New(pArchive, pFilenameThread->threadIdx, + kNuThreadIDFilename, &pNewThreadMod); + BailError(err); + Nu_RecordAddThreadMod(pRecord, pNewThreadMod); + pNewThreadMod = nil; /* successful, don't free */ + } + + if (doAdd) { + err = Nu_ThreadModAdd_New(pArchive, kNuThreadIDFilename, + kNuThreadFormatUncompressed, pDataSource, &pNewThreadMod); + BailError(err); + /*pDataSource = nil;*/ /* ThreadModAdd_New makes a copy */ + Nu_RecordAddThreadMod(pRecord, pNewThreadMod); + pNewThreadMod = nil; /* successful, don't free */ + } + + if (doUpdate) { + err = Nu_ThreadModUpdate_New(pArchive, pFilenameThread->threadIdx, + pDataSource, &pNewThreadMod); + BailError(err); + /*pDataSource = nil;*/ /* ThreadModAdd_New makes a copy */ + Nu_RecordAddThreadMod(pRecord, pNewThreadMod); + pNewThreadMod = nil; /* successful, don't free */ + } + + DBUG(("--- renaming '%s' to '%s' with delete=%d add=%d update=%d\n", + pRecord->filename, pathname, doDelete, doAdd, doUpdate)); + + /* + * Update the fssep, if necessary. (This is slightly silly -- we + * have to rewrite the record header anyway since we're changing + * threads around.) + */ + if (NuGetSepFromSysInfo(pRecord->recFileSysInfo) != fssep) { + DBUG(("--- and updating the fssep\n")); + pRecord->recFileSysInfo = NuSetSepInSysInfo(pRecord->recFileSysInfo, + fssep); + pRecord->dirtyHeader = true; + } + + /* if we had a header filename, mark it for oblivion */ + if (pFilenameThread == nil) { + DBUG(("+++ rename gonna drop the filename\n")); + pRecord->dropRecFilename = true; + } + +bail: + Nu_ThreadModFree(pArchive, pNewThreadMod); + Nu_DataSourceFree(pDataSource); + return err; +} + + +/* + * Update a record's attributes with the contents of pRecordAttr. + */ +NuError +Nu_SetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, + const NuRecordAttr* pRecordAttr) +{ + NuError err; + NuRecord* pRecord; + + if (pRecordAttr == nil) + return kNuErrInvalidArg; + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* pull the record out of the "copy" set */ + err = Nu_FindRecordForWriteByIdx(pArchive, recordIdx, &pRecord); + BailError(err); + + Assert(pRecord != nil); + pRecord->recFileSysID = pRecordAttr->fileSysID; + /*pRecord->recFileSysInfo = pRecordAttr->fileSysInfo;*/ + pRecord->recAccess = pRecordAttr->access; + pRecord->recFileType = pRecordAttr->fileType; + pRecord->recExtraType = pRecordAttr->extraType; + pRecord->recCreateWhen = pRecordAttr->createWhen; + pRecord->recModWhen = pRecordAttr->modWhen; + pRecord->recArchiveWhen = pRecordAttr->archiveWhen; + pRecord->dirtyHeader = true; + +bail: + return err; +} + + +/* + * Bulk-delete several records, using the selection filter callback. + */ +NuError +Nu_Delete(NuArchive* pArchive) +{ + NuError err; + NuSelectionProposal selProposal; + NuRecord* pNextRecord; + NuRecord* pRecord; + NuResult result; + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* + * If we don't yet have a copy set, make one. + */ + if (!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, + &pArchive->origRecordSet); + BailError(err); + } + + /* + * Run through the copy set. This is different from most other + * operations, which run through the "orig" set. However, since + * we're not interested in allowing the user to delete things that + * have already been deleted, we might as well use this set. + */ + pNextRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); + while (pNextRecord != nil) { + pRecord = pNextRecord; + pNextRecord = pRecord->pNext; + + /* + * Deletion of modified records (thread adds, deletes, or updates) + * isn't allowed. There's no point in showing the record to the + * user. + */ + if (pRecord->pThreadMods != nil) { + DBUG(("+++ Skipping delete on a modified record\n")); + continue; + } + + /* + * If a selection filter is defined, allow the user the opportunity + * to select which files will be deleted, or abort the entire + * operation. + */ + if (pArchive->selectionFilterFunc != nil) { + selProposal.pRecord = pRecord; + selProposal.pThread = pRecord->pThreads; /* doesn't matter */ + result = (*pArchive->selectionFilterFunc)(pArchive, &selProposal); + + if (result == kNuSkip) + continue; + if (result == kNuAbort) { + err = kNuErrAborted; + goto bail; + } + } + + /* + * Do we want to allow this? (Same test as for DeleteRecord.) + */ + if (pRecord->pThreadMods != nil || pRecord->dirtyHeader) { + DBUG(("--- Tried to delete a modified record\n")); + err = kNuErrModRecChange; + goto bail; + } + + err = Nu_RecordSet_DeleteRecord(pArchive, &pArchive->copyRecordSet, + pRecord); + BailError(err); + } + +bail: + return err; +} + +/* + * Delete an entire record. + */ +NuError +Nu_DeleteRecord(NuArchive* pArchive, NuRecordIdx recIdx) +{ + NuError err; + NuRecord* pRecord; + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); + BailError(err); + + /* + * Deletion of modified records (thread adds, deletes, or updates) isn't + * allowed. It probably wouldn't be hard to handle, but it's pointless. + * Preventing the action maintains our general semantics of disallowing + * conflicting actions on the same object. + * + * We also block it if the header is dirty (e.g. they changed the + * record's filetype). This isn't necessary for correct operation, + * but again it maintains the semantics. + */ + if (pRecord->pThreadMods != nil || pRecord->dirtyHeader) { + DBUG(("--- Tried to delete a modified record\n")); + err = kNuErrModRecChange; + goto bail; + } + + err = Nu_RecordSet_DeleteRecord(pArchive,&pArchive->copyRecordSet, pRecord); + BailError(err); + +bail: + return err; +} + diff --git a/nufxlib/SourceSink.c b/nufxlib/SourceSink.c new file mode 100644 index 0000000..1c13778 --- /dev/null +++ b/nufxlib/SourceSink.c @@ -0,0 +1,889 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Implementation of DataSource and DataSink objects. + */ +#include "NufxLibPriv.h" + + + +/* + * =========================================================================== + * NuDataSource + * =========================================================================== + */ + +/* + * Allocate a new DataSource structure. + */ +static NuError +Nu_DataSourceNew(NuDataSource** ppDataSource) +{ + Assert(ppDataSource != nil); + + *ppDataSource = Nu_Malloc(nil, sizeof(**ppDataSource)); + if (*ppDataSource == nil) + return kNuErrMalloc; + + (*ppDataSource)->sourceType = kNuDataSourceUnknown; + + return kNuErrNone; +} + + +/* + * Make a copy of a DataSource. Actually just increments a reference count. + * + * What we *really* want to be doing is copying the structure (since we + * can't guarantee copy-on-write semantics for the fields without adding + * more stuff) and refcounting the underlying resource, so that "auto-free" + * semantics work out right. + * + * We're okay for now, since for the most part we only do work on one + * copy of each. (I wish I could remember why this copying thing was + * needed in the first place.) Buffer sources are a little scary since + * they include a "curOffset" value. + * + * Returns nil on error. + */ +NuDataSource* +Nu_DataSourceCopy(NuDataSource* pDataSource) +{ + Assert(pDataSource->common.refCount >= 1); + pDataSource->common.refCount++; + return pDataSource; + +#if 0 /* we used to copy them -- very bad idea */ + NuDataSource* pNewDataSource; + + Assert(pDataSource != nil); + + if (Nu_DataSourceNew(&pNewDataSource) != kNuErrNone) + return nil; + Assert(pNewDataSource != nil); + + /* this gets most of it */ + memcpy(pNewDataSource, pDataSource, sizeof(*pNewDataSource)); + + /* copy anything we're sure to free up */ + if (pDataSource->sourceType == kNuDataSourceFromFile) { + Assert(pDataSource->fromFile.fp == nil); /* does this matter? */ + pNewDataSource->fromFile.pathname = + strdup(pDataSource->fromFile.pathname); + } + + /* don't let the original free up the resources */ + if (pDataSource->common.doClose) { + DBUG(("--- clearing doClose on source-copy of data source\n")); + pDataSource->common.doClose = false; + } + + return pNewDataSource; +#endif +} + + +/* + * Free a data source structure, and any type-specific elements. + */ +NuError +Nu_DataSourceFree(NuDataSource* pDataSource) +{ + if (pDataSource == nil) + return kNuErrNone; + + Assert(pDataSource->common.refCount > 0); + if (pDataSource->common.refCount > 1) { + pDataSource->common.refCount--; + return kNuErrNone; + } + + switch (pDataSource->sourceType) { + case kNuDataSourceFromFile: + Nu_Free(nil, pDataSource->fromFile.pathname); + if (pDataSource->fromFile.fp != nil) { + fclose(pDataSource->fromFile.fp); + pDataSource->fromFile.fp = nil; + } + break; + case kNuDataSourceFromFP: + if (pDataSource->fromFP.fcloseFunc != nil && + pDataSource->fromFP.fp != nil) + { + (*pDataSource->fromFP.fcloseFunc)(nil, pDataSource->fromFP.fp); + pDataSource->fromFP.fp = nil; + } + break; + case kNuDataSourceFromBuffer: + if (pDataSource->fromBuffer.freeFunc != nil) { + (*pDataSource->fromBuffer.freeFunc)(nil, + (void*)pDataSource->fromBuffer.buffer); + pDataSource->fromBuffer.buffer = nil; + } + break; + case kNuDataSourceUnknown: + break; + default: + Assert(0); + return kNuErrInternal; + } + + Nu_Free(nil, pDataSource); + return kNuErrNone; +} + + +/* + * Create a data source for an unopened file. + */ +NuError +Nu_DataSourceFile_New(NuThreadFormat threadFormat, ulong otherLen, + const char* pathname, Boolean isFromRsrcFork, NuDataSource** ppDataSource) +{ + NuError err; + + if (pathname == nil || + !(isFromRsrcFork == true || isFromRsrcFork == false) || + ppDataSource == nil) + { + return kNuErrInvalidArg; + } + + err = Nu_DataSourceNew(ppDataSource); + BailErrorQuiet(err); + + (*ppDataSource)->common.sourceType = kNuDataSourceFromFile; + (*ppDataSource)->common.threadFormat = threadFormat; + (*ppDataSource)->common.rawCrc = 0; + (*ppDataSource)->common.dataLen = 0; /* to be filled in later */ + (*ppDataSource)->common.otherLen = otherLen; + (*ppDataSource)->common.refCount = 1; + + (*ppDataSource)->fromFile.pathname = strdup(pathname); + (*ppDataSource)->fromFile.fromRsrcFork = isFromRsrcFork; + (*ppDataSource)->fromFile.fp = nil; /* to be filled in later */ + +bail: + return err; +} + + +/* + * Create a data source for an open file at a specific offset. The FILE* + * must be seekable. + */ +NuError +Nu_DataSourceFP_New(NuThreadFormat threadFormat, ulong otherLen, + FILE* fp, long offset, long length, NuCallback fcloseFunc, + NuDataSource** ppDataSource) +{ + NuError err; + + if (fp == nil || offset < 0 || length < 0 || + ppDataSource == nil) + { + return kNuErrInvalidArg; + } + + if (otherLen && otherLen < (ulong)length) { + DBUG(("--- rejecting FP len=%ld other=%ld\n", length, otherLen)); + err = kNuErrPreSizeOverflow; + goto bail; + } + + err = Nu_DataSourceNew(ppDataSource); + BailErrorQuiet(err); + + (*ppDataSource)->common.sourceType = kNuDataSourceFromFP; + (*ppDataSource)->common.threadFormat = threadFormat; + (*ppDataSource)->common.rawCrc = 0; + (*ppDataSource)->common.dataLen = length; + (*ppDataSource)->common.otherLen = otherLen; + (*ppDataSource)->common.refCount = 1; + + (*ppDataSource)->fromFP.fp = fp; + (*ppDataSource)->fromFP.offset = offset; + (*ppDataSource)->fromFP.fcloseFunc = fcloseFunc; + +bail: + return err; +} + + +/* + * Create a data source for a buffer. + * + * We allow "buffer" to be nil so long as "offset" and "length" are also + * nil. This is useful for creating empty pre-sized buffers, such as + * blank comment fields. + */ +NuError +Nu_DataSourceBuffer_New(NuThreadFormat threadFormat, ulong otherLen, + const uchar* buffer, long offset, long length, NuCallback freeFunc, + NuDataSource** ppDataSource) +{ + NuError err; + + if (offset < 0 || length < 0 || ppDataSource == nil) + return kNuErrInvalidArg; + if (buffer == nil && (offset != 0 || length != 0)) + return kNuErrInvalidArg; + + if (buffer == nil) { + DBUG(("+++ zeroing freeFunc for empty-buffer DataSource\n")); + freeFunc = nil; + } + + if (otherLen && otherLen < (ulong)length) { + DBUG(("--- rejecting buffer len=%ld other=%ld\n", length, otherLen)); + err = kNuErrPreSizeOverflow; + goto bail; + } + + err = Nu_DataSourceNew(ppDataSource); + BailErrorQuiet(err); + + (*ppDataSource)->common.sourceType = kNuDataSourceFromBuffer; + (*ppDataSource)->common.threadFormat = threadFormat; + (*ppDataSource)->common.rawCrc = 0; + (*ppDataSource)->common.dataLen = length; + (*ppDataSource)->common.otherLen = otherLen; + (*ppDataSource)->common.refCount = 1; + + (*ppDataSource)->fromBuffer.buffer = buffer; + (*ppDataSource)->fromBuffer.offset = offset; + (*ppDataSource)->fromBuffer.curOffset = offset; + (*ppDataSource)->fromBuffer.curDataLen = length; + (*ppDataSource)->fromBuffer.freeFunc = freeFunc; + +bail: + return err; +} + + +/* + * Get the type of a NuDataSource. + */ +NuDataSourceType +Nu_DataSourceGetType(const NuDataSource* pDataSource) +{ + Assert(pDataSource != nil); + return pDataSource->sourceType; +} + +/* + * Get the threadFormat for a data source. + */ +NuThreadFormat +Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource) +{ + Assert(pDataSource != nil); + return pDataSource->common.threadFormat; +} + +/* + * Get "dataLen" from a dataSource. + */ +ulong +Nu_DataSourceGetDataLen(const NuDataSource* pDataSource) +{ + Assert(pDataSource != nil); + + if (pDataSource->sourceType == kNuDataSourceFromFile) { + /* dataLen can only be valid if file has been opened */ + Assert(pDataSource->fromFile.fp != nil); + } + + return pDataSource->common.dataLen; +} + +/* + * Get "otherLen" from a dataSource. + */ +ulong +Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource) +{ + Assert(pDataSource != nil); + return pDataSource->common.otherLen; +} + +/* + * Change the "otherLen" value. + */ +void +Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen) +{ + Assert(pDataSource != nil && otherLen > 0); + pDataSource->common.otherLen = otherLen; +} + + +/* + * Get the "raw CRC" value. + */ +ushort +Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource) +{ + Assert(pDataSource != nil); + return pDataSource->common.rawCrc; +} + +/* + * Set the "raw CRC" value. You would want to do this if the input was + * already-compressed data, and you wanted to propagate the thread CRC. + */ +void +Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, ushort crc) +{ + Assert(pDataSource != nil); + pDataSource->common.rawCrc = crc; +} + + +/* + * Prepare a data source for action. + */ +NuError +Nu_DataSourcePrepareInput(NuArchive* pArchive, NuDataSource* pDataSource) +{ + NuError err = kNuErrNone; + FILE* fileFp = nil; + + /* + * Doesn't apply to buffer sources. + */ + if (Nu_DataSourceGetType(pDataSource) == kNuDataSourceFromBuffer) + goto bail; + + /* + * FP sources can be used several times, so we need to seek them + * to the correct offset before we begin. + */ + if (Nu_DataSourceGetType(pDataSource) == kNuDataSourceFromFP) { + err = Nu_FSeek(pDataSource->fromFP.fp, pDataSource->fromFP.offset, + SEEK_SET); + goto bail; /* return this err */ + } + + /* + * We're adding from a file on disk. Open it. + */ + err = Nu_OpenInputFile(pArchive, + pDataSource->fromFile.pathname, pDataSource->fromFile.fromRsrcFork, + &fileFp); + BailError(err); + + Assert(fileFp != nil); + pDataSource->fromFile.fp = fileFp; + err = Nu_GetFileLength(pArchive, fileFp, + (long*)&pDataSource->common.dataLen); + BailError(err); + + if (pDataSource->common.otherLen && + pDataSource->common.otherLen < pDataSource->common.dataLen) + { + DBUG(("--- Uh oh, looks like file len is too small for presized\n")); + } + +bail: + return err; +} + + +/* + * Un-prepare a data source. This really only affects "file" sources, and + * is only here so we don't end up with 200+ FILE* structures hanging around. + * If we don't do this, the first resource we're likely to run out of is + * file descriptors. + * + * It's not necessary to do this in all error cases -- the DataSource "Free" + * call will take care of this eventually -- but for normal operation on + * a large number of files, it's vital. + */ +void +Nu_DataSourceUnPrepareInput(NuArchive* pArchive, NuDataSource* pDataSource) +{ + if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile) + return; + + if (pDataSource->fromFile.fp != nil) { + fclose(pDataSource->fromFile.fp); + pDataSource->fromFile.fp = nil; + pDataSource->common.dataLen = 0; + } +} + + +/* + * Get the pathname from a "from-file" dataSource. + */ +const char* +Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource) +{ + Assert(pDataSource != nil); + Assert(pDataSource->sourceType == kNuDataSourceFromFile); + Assert(pDataSource->fromFile.pathname != nil); + + return pDataSource->fromFile.pathname; +} + + +/* + * Read a block of data from a dataSource. + */ +NuError +Nu_DataSourceGetBlock(NuDataSource* pDataSource, uchar* buf, ulong len) +{ + NuError err; + + Assert(pDataSource != nil); + Assert(buf != nil); + Assert(len > 0); + + switch (pDataSource->sourceType) { + case kNuDataSourceFromFile: + Assert(pDataSource->fromFile.fp != nil); + err = Nu_FRead(pDataSource->fromFile.fp, buf, len); + if (feof(pDataSource->fromFile.fp)) + Nu_ReportError(NU_NILBLOB, err, "EOF hit unexpectedly"); + return err; + + case kNuDataSourceFromFP: + err = Nu_FRead(pDataSource->fromFP.fp, buf, len); + if (feof(pDataSource->fromFP.fp)) + Nu_ReportError(NU_NILBLOB, err, "EOF hit unexpectedly"); + return err; + + case kNuDataSourceFromBuffer: + if ((long)len > pDataSource->fromBuffer.curDataLen) { + /* buffer underrun */ + return kNuErrBufferUnderrun; + } + memcpy(buf, + pDataSource->fromBuffer.buffer + pDataSource->fromBuffer.curOffset, + len); + pDataSource->fromBuffer.curOffset += len; + pDataSource->fromBuffer.curDataLen -= len; + return kNuErrNone; + + default: + Assert(false); + return kNuErrInternal; + } +} + + +/* + * Rewind a data source to the start of its input. + */ +NuError +Nu_DataSourceRewind(NuDataSource* pDataSource) +{ + NuError err; + + Assert(pDataSource != nil); + + switch (pDataSource->sourceType) { + case kNuDataSourceFromFile: + Assert(pDataSource->fromFile.fp != nil); + err = Nu_FSeek(pDataSource->fromFile.fp, 0, SEEK_SET); + break; /* fall through with error */ + case kNuDataSourceFromFP: + err = Nu_FSeek(pDataSource->fromFP.fp, pDataSource->fromFP.offset, + SEEK_SET); + break; /* fall through with error */ + case kNuDataSourceFromBuffer: + pDataSource->fromBuffer.curOffset = pDataSource->fromBuffer.offset; + pDataSource->fromBuffer.curDataLen = pDataSource->common.dataLen; + err = kNuErrNone; + break; + default: + Assert(false); + err = kNuErrInternal; + } + + return err; +} + + +/* + * =========================================================================== + * NuDataSink + * =========================================================================== + */ + +/* + * Allocate a new DataSink structure. + */ +static NuError +Nu_DataSinkNew(NuDataSink** ppDataSink) +{ + Assert(ppDataSink != nil); + + *ppDataSink = Nu_Malloc(nil, sizeof(**ppDataSink)); + if (*ppDataSink == nil) + return kNuErrMalloc; + + (*ppDataSink)->sinkType = kNuDataSinkUnknown; + + return kNuErrNone; +} + + +/* + * Free a data sink structure, and any type-specific elements. + */ +NuError +Nu_DataSinkFree(NuDataSink* pDataSink) +{ + if (pDataSink == nil) + return kNuErrNone; + + switch (pDataSink->sinkType) { + case kNuDataSinkToFile: + Nu_DataSinkFile_Close(pDataSink); + Nu_Free(nil, pDataSink->toFile.pathname); + break; + case kNuDataSinkToFP: + break; + case kNuDataSinkToBuffer: + break; + case kNuDataSinkToVoid: + break; + case kNuDataSinkUnknown: + break; + default: + Assert(0); + return kNuErrInternal; + } + + Nu_Free(nil, pDataSink); + return kNuErrNone; +} + + +/* + * Create a data sink for an unopened file. + */ +NuError +Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL, const char* pathname, + char fssep, NuDataSink** ppDataSink) +{ + NuError err; + + if ((doExpand != true && doExpand != false) || + (convertEOL != kNuConvertOff && convertEOL != kNuConvertOn && + convertEOL != kNuConvertAuto) || + pathname == nil || + fssep == 0 || + ppDataSink == nil) + { + return kNuErrInvalidArg; + } + + err = Nu_DataSinkNew(ppDataSink); + BailErrorQuiet(err); + + (*ppDataSink)->common.sinkType = kNuDataSinkToFile; + (*ppDataSink)->common.doExpand = doExpand; + if (doExpand) + (*ppDataSink)->common.convertEOL = convertEOL; + else + (*ppDataSink)->common.convertEOL = kNuConvertOff; + (*ppDataSink)->common.outCount = 0; + (*ppDataSink)->toFile.pathname = strdup(pathname); + (*ppDataSink)->toFile.fssep = fssep; + + (*ppDataSink)->toFile.fp = nil; + +bail: + return err; +} + + +/* + * Create a data sink based on a file pointer. + */ +NuError +Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp, + NuDataSink** ppDataSink) +{ + NuError err; + + if ((doExpand != true && doExpand != false) || + (convertEOL != kNuConvertOff && convertEOL != kNuConvertOn && + convertEOL != kNuConvertAuto) || + fp == nil || + ppDataSink == nil) + { + return kNuErrInvalidArg; + } + + err = Nu_DataSinkNew(ppDataSink); + BailErrorQuiet(err); + + (*ppDataSink)->common.sinkType = kNuDataSinkToFP; + (*ppDataSink)->common.doExpand = doExpand; + if (doExpand) + (*ppDataSink)->common.convertEOL = convertEOL; + else + (*ppDataSink)->common.convertEOL = kNuConvertOff; + (*ppDataSink)->common.outCount = 0; + (*ppDataSink)->toFP.fp = fp; + +bail: + return err; +} + + +/* + * Create a data sink for a buffer in memory. + */ +NuError +Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL, uchar* buffer, + ulong bufLen, NuDataSink** ppDataSink) +{ + NuError err; + + if ((doExpand != true && doExpand != false) || + (convertEOL != kNuConvertOff && convertEOL != kNuConvertOn && + convertEOL != kNuConvertAuto) || + buffer == nil || + bufLen == 0 || + ppDataSink == nil) + { + return kNuErrInvalidArg; + } + + err = Nu_DataSinkNew(ppDataSink); + BailErrorQuiet(err); + + (*ppDataSink)->common.sinkType = kNuDataSinkToBuffer; + (*ppDataSink)->common.doExpand = doExpand; + if (doExpand) + (*ppDataSink)->common.convertEOL = convertEOL; + else + (*ppDataSink)->common.convertEOL = kNuConvertOff; + (*ppDataSink)->common.convertEOL = convertEOL; + (*ppDataSink)->common.outCount = 0; + (*ppDataSink)->toBuffer.buffer = buffer; + (*ppDataSink)->toBuffer.bufLen = bufLen; + (*ppDataSink)->toBuffer.stickyErr = kNuErrNone; + +bail: + return err; +} + + +/* + * Create a data sink that goes nowhere. + */ +NuError +Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL, + NuDataSink** ppDataSink) +{ + NuError err; + + Assert(doExpand == true || doExpand == false); + Assert(ppDataSink != nil); + + err = Nu_DataSinkNew(ppDataSink); + BailErrorQuiet(err); + + (*ppDataSink)->common.sinkType = kNuDataSinkToVoid; + (*ppDataSink)->common.doExpand = doExpand; + (*ppDataSink)->common.convertEOL = convertEOL; + (*ppDataSink)->common.outCount = 0; + +bail: + return err; +} + + +/* + * Get the type of a NuDataSink. + */ +NuDataSinkType +Nu_DataSinkGetType(const NuDataSink* pDataSink) +{ + Assert(pDataSink != nil); + return pDataSink->sinkType; +} + + +/* + * Return the "doExpand" parameter from any kind of sink. + */ +Boolean +Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink) +{ + return pDataSink->common.doExpand; +} + +/* + * Return the "convertEOL" parameter from any kind of sink. + */ +NuValue +Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink) +{ + return pDataSink->common.convertEOL; +} + +/* + * Return the #of bytes written to the sink. + */ +ulong +Nu_DataSinkGetOutCount(const NuDataSink* pDataSink) +{ + return pDataSink->common.outCount; +} + + +/* + * Get "pathname" from a to-file sink. + */ +const char* +Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink) +{ + Assert(pDataSink != nil); + Assert(pDataSink->sinkType == kNuDataSinkToFile); + + return pDataSink->toFile.pathname; +} + +/* + * Get "fssep" from a to-file sink. + */ +char +Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink) +{ + Assert(pDataSink != nil); + Assert(pDataSink->sinkType == kNuDataSinkToFile); + + return pDataSink->toFile.fssep; +} + +/* + * Get the "fp" for a file sink. + */ +FILE* +Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink) +{ + Assert(pDataSink != nil); + Assert(pDataSink->sinkType == kNuDataSinkToFile); + + return pDataSink->toFile.fp; +} + +/* + * Set the "fp" for a file sink. + */ +void +Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp) +{ + Assert(pDataSink != nil); + Assert(pDataSink->sinkType == kNuDataSinkToFile); + + pDataSink->toFile.fp = fp; +} + +/* + * Close a to-file sink. + */ +void +Nu_DataSinkFile_Close(NuDataSink* pDataSink) +{ + Assert(pDataSink != nil); + + if (pDataSink->toFile.fp != nil) { + fclose(pDataSink->toFile.fp); + pDataSink->toFile.fp = nil; + } +} + + +/* + * Write a block of data to a DataSink. + */ +NuError +Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uchar* buf, ulong len) +{ + NuError err; + + Assert(pDataSink != nil); + Assert(buf != nil); + Assert(len > 0); + + switch (pDataSink->sinkType) { + case kNuDataSinkToFile: + Assert(pDataSink->toFile.fp != nil); + err = Nu_FWrite(pDataSink->toFile.fp, buf, len); + if (err != kNuErrNone) + return err; + break; + case kNuDataSinkToFP: + Assert(pDataSink->toFP.fp != nil); + err = Nu_FWrite(pDataSink->toFP.fp, buf, len); + if (err != kNuErrNone) + return err; + break; + case kNuDataSinkToBuffer: + if (len > pDataSink->toBuffer.bufLen) { + /* buffer overrun; set a "sticky" error, like FILE* does */ + err = kNuErrBufferOverrun; + pDataSink->toBuffer.stickyErr = err; + return err; + } + memcpy(pDataSink->toBuffer.buffer, buf, len); + pDataSink->toBuffer.buffer += len; + pDataSink->toBuffer.bufLen -= len; + break; + case kNuDataSinkToVoid: + /* do nothing */ + break; + default: + Assert(false); + return kNuErrInternal; + } + pDataSink->common.outCount += len; + return kNuErrNone; +} + + +/* + * Figure out if one of our earlier writes has failed. + */ +NuError +Nu_DataSinkGetError(NuDataSink* pDataSink) +{ + NuError err = kNuErrNone; + + Assert(pDataSink != nil); + + switch (pDataSink->sinkType) { + case kNuDataSinkToFile: + if (ferror(pDataSink->toFile.fp)) + err = kNuErrFileWrite; + break; + case kNuDataSinkToFP: + if (ferror(pDataSink->toFP.fp)) + err = kNuErrFileWrite; + break; + case kNuDataSinkToBuffer: + err = pDataSink->toBuffer.stickyErr; + break; + case kNuDataSinkToVoid: + /* do nothing */ + break; + default: + Assert(false); + err = kNuErrInternal; + break; + } + + return err; +} + diff --git a/nufxlib/Squeeze.c b/nufxlib/Squeeze.c new file mode 100644 index 0000000..f28b3c4 --- /dev/null +++ b/nufxlib/Squeeze.c @@ -0,0 +1,1146 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Huffman/RLE "squeeze" compression, based on SQ/USQ. This format is + * listed in the NuFX documentation, but to my knowledge has never + * actually been used (until now). Neither P8 ShrinkIt v3.4 nor II Unshrink + * handle the format correctly, so this is really only useful as an + * experiment. + * + * The algorithm appears to date back to the CP/M days. This implementation + * is based on "xsq"/"xusq" v1.7u by Richard Greenlaw (from December 1982). + * The code was also present in ARC v5.x. + * + * The "nusq.c" implementation found in NuLib was by Marcel J.E. Mol, + * who got it from Don Elton's sq3/usq2 programs for the Apple II. + * + * The SQ file format begins with this: + * +00 magic number (0xff76) + * +02 checksum on uncompressed data + * +04 filename, ending with \0 + * The NuFX format skips the above, starting immediately after it: + * +00 node count + * +02 node value array [node count], two bytes each + * +xx data immediately follows array + * + * NuFX drops the magic number, checksum, and filename from the header, + * since (with v3 records) all three are redundant. You can enable this + * if you want to experiment with SQ-compatible output. + */ +#include "NufxLibPriv.h" + +#ifdef ENABLE_SQ + +/* if this is defined, create and unpack the full SQ header (debugging only) */ +/* #define FULL_SQ_HEADER */ + + +#define kNuSQMagic 0xff76 /* magic value for file header */ +#define kNuSQRLEDelim 0x90 /* RLE delimiter */ +#define kNuSQEOFToken 256 /* distinguished stop symbol */ +#define kNuSQNumVals 257 /* 256 symbols + stop */ + + +/* + * =========================================================================== + * Compression + * =========================================================================== + */ + +#define kNuSQNoChild (-1) /* indicates end of path through tree */ +#define kNuSQNumNodes (kNuSQNumVals + kNuSQNumVals -1) +#define kNuSQMaxCount 65535 /* max value you can store in 16 bits */ + +/* states for the RLE encoding */ +typedef enum { + kNuSQRLEStateUnknown = 0, + + kNuSQRLEStateNoHist, /* nothing yet */ + kNuSQRLEStateSentChar, /* lastchar set, no lookahead yet */ + kNuSQRLEStateSendNewC, /* found run of two, send 2nd w/o DLE */ + kNuSQRLEStateSendCnt, /* newchar set, DLE sent, send count next */ +} NuSQRLEState; + +/* nodes in the Huffman encoding tree */ +typedef struct EncTreeNode { + int weight; /* #of appearances */ + int tdepth; /* length on longest path in tree */ + int lchild, rchild; /* indexes to next level */ +} EncTreeNode; + +/* + * State during compression. + */ +typedef struct SQState { + NuArchive* pArchive; + int doCalcCRC; /* boolean; if set, compute CRC on input */ + ushort crc; + + NuStraw* pStraw; + long uncompRemaining; + + #ifdef FULL_SQ_HEADER + ushort checksum; + #endif + + /* + * RLE state stuff. + */ + NuSQRLEState rleState; + int lastSym; + int likeCount; + + /* + * Huffman state stuff. + */ + EncTreeNode node[kNuSQNumNodes]; + + int treeHead; /* index to head node of final tree */ + + /* encoding table */ + int codeLen[kNuSQNumVals]; /* number of bits in code for symbol N */ + ushort code[kNuSQNumVals]; /* bits for symbol N (first bit in lsb) */ + ushort tmpCode; /* temporary code value */ +} SQState; + + +/* + * Get the next byte from the input straw. Also updates the checksum + * and SQ CRC, if "doCalcCRC" is set to true. + * + * This isn't exactly fast, but then this isn't exactly a fast algorithm, + * and there's not much point in optimizing something that isn't going + * to get used much. + * + * Returns kNuSQEOFToken as the value when we're out of data. + */ +static NuError +Nu_SQGetcCRC(SQState* pSqState, int* pSym) +{ + NuError err; + uchar c; + + if (!pSqState->uncompRemaining) { + *pSym = kNuSQEOFToken; + return kNuErrNone; + } + + err = Nu_StrawRead(pSqState->pArchive, pSqState->pStraw, &c, 1); + if (err == kNuErrNone) { + if (pSqState->doCalcCRC) { + #ifdef FULL_SQ_HEADER + pSqState->checksum += c; + #endif + pSqState->crc = Nu_CalcCRC16(pSqState->crc, &c, 1); + } + *pSym = c; + pSqState->uncompRemaining--; + } + + return err; +} + +/* + * Get the next byte from the post-RLE input stream. + * + * Returns kNuSQEOFToken in "*pSum" when we reach the end of the input. + */ +static NuError +Nu_SQGetcRLE(SQState* pSqState, int* pSym) +{ + NuError err = kNuErrNone; + int likeCount, newSym; + + switch (pSqState->rleState) { + case kNuSQRLEStateNoHist: + /* No relevant history */ + pSqState->rleState = kNuSQRLEStateSentChar; + err = Nu_SQGetcCRC(pSqState, pSym); + pSqState->lastSym = *pSym; + break; + + case kNuSQRLEStateSentChar: + /* lastChar is set, need lookahead */ + switch (pSqState->lastSym) { + case kNuSQRLEDelim: + /* send all DLEs escaped; note this is horrible for a run of DLEs */ + pSqState->rleState = kNuSQRLEStateNoHist; + *pSym = 0; /* zero len is how we define an escaped DLE */ + break; + case kNuSQEOFToken: + *pSym = kNuSQEOFToken; + break; + default: + /* + * Try for a run, using the character we previous read as + * the base. Thus, if the next character we read matches, + * we have a run of two. The count describes the total + * length of the run, including the character we've already + * emitted. + */ + likeCount = 0; + do { + likeCount++; + err = Nu_SQGetcCRC(pSqState, &newSym); + if (err != kNuErrNone) + goto bail; + } while (newSym == pSqState->lastSym && likeCount < 255); + + switch (likeCount) { + case 1: + /* not a run, return first one we got */ + pSqState->lastSym = newSym; + *pSym = newSym; + break; + case 2: + /* not long enough for run; return second one next time thru */ + pSqState->rleState = kNuSQRLEStateSendNewC; + *pSym = pSqState->lastSym; /* 1st new one */ + pSqState->lastSym = newSym; /* 2nd new one */ + break; + default: + pSqState->rleState = kNuSQRLEStateSendCnt; + pSqState->likeCount = likeCount; + pSqState->lastSym = newSym; /* 1st one after the run */ + *pSym = kNuSQRLEDelim; + break; + } + } + break; + + case kNuSQRLEStateSendNewC: + /* send first char past a run of two */ + pSqState->rleState = kNuSQRLEStateSentChar; + *pSym = pSqState->lastSym; + break; + + case kNuSQRLEStateSendCnt: + /* Sent DLE for repeat sequence, send count */ + pSqState->rleState = kNuSQRLEStateSendNewC; + *pSym = pSqState->likeCount; + break; + + default: + { + NuArchive* pArchive = pSqState->pArchive; + + err = kNuErrInternal; + Nu_ReportError(NU_BLOB, err, "invalid state %d in SQ RLE encode", + pSqState->rleState); + break; + } + } + +bail: + return err; +} + + +/* + * Comment from xsq.c: + * + * This translation uses the Huffman algorithm to develop a + * binary tree representing the decoding information for + * a variable length bit string code for each input value. + * Each string's length is in inverse proportion to its + * frequency of appearance in the incoming data stream. + * The encoding table is derived from the decoding table. + * + * The range of valid values into the Huffman algorithm are + * the values of a byte stored in an integer plus the special + * endfile value chosen to be an adjacent value. Overall, 0-SPEOF. + * + * The "node" array of structures contains the nodes of the + * binary tree. The first NUMVALS nodes are the leaves of the + * tree and represent the values of the data bytes being + * encoded and the special endfile, SPEOF. + * The remaining nodes become the internal nodes of the tree. + * + * In the original design it was believed that + * a Huffman code would fit in the same number of + * bits that will hold the sum of all the counts. + * That was disproven by a user's file and was a rare but + * infamous bug. This version attempts to choose among equally + * weighted subtrees according to their maximum depths to avoid + * unnecessarily long codes. In case that is not sufficient + * to guarantee codes <= 16 bits long, we initially scale + * the counts so the total fits in an unsigned integer, but + * if codes longer than 16 bits are generated the counts are + * rescaled to a lower ceiling and code generation is retried. + */ + +/* + * Return the greater of two integers. + */ +static int +Nu_SQMax(int a, int b) +{ + if (a > b) + return a; + else + return b; +} + +/* + * Compare two trees, if a > b return true, else return false. + * Priority is given to weight, then depth. "a" and "b" are heaps, + * so we only need to look at the root element. + */ +static int +Nu_SQCmpTrees(SQState* pSqState, int a, int b) +{ + if (pSqState->node[a].weight > pSqState->node[b].weight) + return true; + if (pSqState->node[a].weight == pSqState->node[b].weight) + if (pSqState->node[a].tdepth > pSqState->node[b].tdepth) + return true; + return false; +} + +/* + * heap() and adjust() maintain a list of binary trees as a + * heap with the top indexing the binary tree on the list + * which has the least weight or, in case of equal weights, + * least depth in its longest path. The depth part is not + * strictly necessary, but tends to avoid long codes which + * might provoke rescaling. + */ + +/* + * Recursively make a heap from a heap with a new top. + */ +static void +Nu_SQHeapAdjust(SQState* pSqState, int list[], int top, int bottom) +{ + int k, temp; + + k = 2 * top + 1; /* left child of top */ + temp = list[top]; /* remember root node of top tree */ + if (k <= bottom) { + if (k < bottom && Nu_SQCmpTrees(pSqState, list[k], list[k + 1])) + k++; + + /* k indexes "smaller" child (in heap of trees) of top */ + /* now make top index "smaller" of old top and smallest child */ + if (Nu_SQCmpTrees(pSqState, temp, list[k])) { + list[top] = list[k]; + list[k] = temp; + /* Make the changed list a heap */ + Nu_SQHeapAdjust(pSqState, list, k, bottom); /*recursive*/ + } + } +} + +/* + * Create a heap. + */ +static void +Nu_SQHeap(SQState* pSqState, int list[], int length) +{ + int i; + + for (i = (length - 2) / 2; i >= 0; i--) + Nu_SQHeapAdjust(pSqState, list, i, length - 1); +} + + +/* + * Build the encoding tree. + * + * HUFFMAN ALGORITHM: develops the single element trees + * into a single binary tree by forming subtrees rooted in + * interior nodes having weights equal to the sum of weights of all + * their descendents and having depth counts indicating the + * depth of their longest paths. + * + * When all trees have been formed into a single tree satisfying + * the heap property (on weight, with depth as a tie breaker) + * then the binary code assigned to a leaf (value to be encoded) + * is then the series of left (0) and right (1) + * paths leading from the root to the leaf. + * Note that trees are removed from the heaped list by + * moving the last element over the top element and + * reheaping the shorter list. + */ +static void +Nu_SQBuildTree(SQState* pSqState, int list[], int len) +{ + int freenode; /* next free node in tree */ + EncTreeNode* frnp; /* free node pointer */ + int lch, rch; /* temporaries for left, right children */ + + /* + * Initialize index to next available (non-leaf) node. + * Lower numbered nodes correspond to leaves (data values). + */ + freenode = kNuSQNumVals; + + while (len > 1) { + /* + * Take from list two btrees with least weight + * and build an interior node pointing to them. + * This forms a new tree. + */ + lch = list[0]; /* This one will be left child */ + + /* delete top (least) tree from the list of trees */ + list[0] = list[--len]; + Nu_SQHeapAdjust(pSqState, list, 0, len - 1); + + /* Take new top (least) tree. Reuse list slot later */ + rch = list[0]; /* This one will be right child */ + + /* + * Form new tree from the two least trees using + * a free node as root. Put the new tree in the list. + */ + frnp = &pSqState->node[freenode]; /* address of next free node */ + list[0] = freenode++; /* put at top for now */ + frnp->lchild = lch; + frnp->rchild = rch; + frnp->weight = + pSqState->node[lch].weight + pSqState->node[rch].weight; + frnp->tdepth = 1 + Nu_SQMax(pSqState->node[lch].tdepth, + pSqState->node[rch].tdepth); + + /* reheap list to get least tree at top*/ + Nu_SQHeapAdjust(pSqState, list, 0, len - 1); + } + + pSqState->treeHead = list[0]; /* head of final tree */ +} + + +/* + * Recursive routine to walk the indicated subtree and level + * and maintain the current path code in bstree. When a leaf + * is found the entire code string and length are put into + * the encoding table entry for the leaf's data value . + * + * Returns zero on success, nonzero if codes are too long. + */ +static int +Nu_SQBuildEncTable(SQState* pSqState, int level, int root) +{ + int l, r; + + l = pSqState->node[root].lchild; + r = pSqState->node[root].rchild; + + if (l == kNuSQNoChild && r == kNuSQNoChild) { + /* Leaf. Previous path determines bit string + * code of length level (bits 0 to level - 1). + * Ensures unused code bits are zero. + */ + pSqState->codeLen[root] = level; + pSqState->code[root] = + pSqState->tmpCode & (((ushort)~0) >> (16 - level)); + return (level > 16) ? -1 : 0; + } else { + if (l != kNuSQNoChild) { + /* Clear path bit and continue deeper */ + pSqState->tmpCode &= ~(1 << level); + /* NOTE RECURSION */ + if (Nu_SQBuildEncTable(pSqState, level + 1, l) != 0) + return -1; + } + if (r != kNuSQNoChild) { + /* Set path bit and continue deeper */ + pSqState->tmpCode |= 1 << level; + /* NOTE RECURSION */ + if (Nu_SQBuildEncTable(pSqState, level + 1, r) != 0) + return -1; + } + } + + return 0; /* if we got here we're ok so far */ +} + + +/* + * The count of number of occurrances of each input value + * have already been prevented from exceeding MAXCOUNT. + * Now we must scale them so that their sum doesn't exceed + * ceiling and yet no non-zero count can become zero. + * This scaling prevents errors in the weights of the + * interior nodes of the Huffman tree and also ensures that + * the codes will fit in an unsigned integer. Rescaling is + * used if necessary to limit the code length. + */ +static void +Nu_SQScale(SQState* pSqState, int ceiling) +{ + int i; + int wt, ovflw, divisor; + ushort sum; + int increased; /* flag */ + + do { + for (i = sum = ovflw = 0; i < kNuSQNumVals; i++) { + if (pSqState->node[i].weight > (ceiling - sum)) + ovflw++; + sum += pSqState->node[i].weight; + } + + divisor = ovflw + 1; /* use the high 16 bits of the sum */ + + /* Ensure no non-zero values are lost */ + increased = false; + for (i = 0; i < kNuSQNumVals; i++) { + wt = pSqState->node[i].weight; + if (wt < divisor && wt != 0) { + /* Don't fail to provide a code if it's used at all */ + pSqState->node[i].weight = divisor; + increased = true; + } + } + } while(increased); + + /* scaling factor choosen and minimums are set; now do the downscale */ + if (divisor > 1) { + for (i = 0; i < kNuSQNumVals; i++) + pSqState->node[i].weight /= divisor; + } +} + +/* + * Build a frequency table from the post-RLE input stream, then generate + * an encoding tree from the results. + */ +static NuError +Nu_SQComputeHuffTree(SQState* pSqState) +{ + NuError err = kNuErrNone; + int btreeList[kNuSQNumVals]; /* list of intermediate binary trees */ + int listLen; /* length of btreeList */ + int ceiling; /* limit for scaling */ + int i, sym, result; + + /* init tree */ + for (i = 0; i < kNuSQNumNodes; i++) { + pSqState->node[i].weight = 0; + pSqState->node[i].tdepth = 0; + pSqState->node[i].lchild = kNuSQNoChild; + pSqState->node[i].rchild = kNuSQNoChild; + } + + DBUG(("+++ SQ scanning...\n")); + + do { + int* pWeight; + + err = Nu_SQGetcRLE(pSqState, &sym); + if (err != kNuErrNone) + goto bail; + + Assert(sym >= 0 && sym <= kNuSQEOFToken); + pWeight = &pSqState->node[(unsigned)sym].weight; + if (*pWeight != kNuSQMaxCount) + (*pWeight)++; + } while (sym != kNuSQEOFToken); + + DBUG(("+++ SQ generating tree...\n")); + + ceiling = kNuSQMaxCount; + + do { + if (ceiling != kNuSQMaxCount) { + DBUG(("+++ SQ rescaling\n")); + } + + /* pick a divisor and scale everything to fit in "ceiling" */ + Nu_SQScale(pSqState, ceiling); + + ceiling /= 2; /* in case we need to rescale */ + + /* + * Build list of single node binary trees having + * leaves for the input values with non-zero counts + */ + for (i = listLen = 0; i < kNuSQNumVals; i++) { + if (pSqState->node[i].weight != 0) { + pSqState->node[i].tdepth = 0; + btreeList[listLen++] = i; + } + } + + /* + * Arrange list of trees into a heap with the entry + * indexing the node with the least weight a the top. + */ + Nu_SQHeap(pSqState, btreeList, listLen); + + /* convert the list of trees to a single decoding tree */ + Nu_SQBuildTree(pSqState, btreeList, listLen); + + /* initialize encoding table */ + for (i = 0; i < kNuSQNumVals; i++) + pSqState->codeLen[i] = 0; + + /* + * Recursively build the encoding table; returns non-zero (failure) + * if any code is > 16 bits long. + */ + result = Nu_SQBuildEncTable(pSqState, 0, pSqState->treeHead); + } while (result != 0); + +#if 0 +{ + int jj; + printf("init_huff\n"); + for (jj = 0; jj < kNuSQNumNodes; jj++) { + printf("NODE %d: w=%d d=%d l=%d r=%d\n", jj, + pSqState->node[jj].weight, + pSqState->node[jj].tdepth, + pSqState->node[jj].lchild, + pSqState->node[jj].rchild); + } +} +#endif + +bail: + return err; +} + + +/* + * Compress data from input to output, using the values in the "code" + * and "codeLen" arrays. + */ +static NuError +Nu_SQCompressInput(SQState* pSqState, FILE* fp, long* pCompressedLen) +{ + NuError err = kNuErrNone; + int sym = kNuSQEOFToken-1; + unsigned long bits, code; /* must hold at least 23 bits */ + int codeLen, gotbits; + long compressedLen; + + DBUG(("+++ SQ compressing\n")); + + Assert(sizeof(bits) >= 4); + compressedLen = *pCompressedLen; + + bits = 0; + gotbits = 0; + while (sym != kNuSQEOFToken) { + err = Nu_SQGetcRLE(pSqState, &sym); + if (err != kNuErrNone) + goto bail; + + code = pSqState->code[sym]; + codeLen = pSqState->codeLen[sym]; + + bits |= code << gotbits; + gotbits += codeLen; + + /* if we have more than a byte, output it */ + while (gotbits > 7) { + putc(bits & 0xff, fp); + compressedLen++; + bits >>= 8; + gotbits -= 8; + } + } + + if (gotbits) { + Assert(gotbits < 8); + putc(bits & 0xff, fp); + compressedLen++; + } + +bail: + *pCompressedLen = compressedLen; + return err; +} + + +/* + * Write a 16-bit value in little-endian order. + */ +static NuError +Nu_SQWriteShort(FILE* outfp, short val) +{ + NuError err; + uchar tmpc; + + tmpc = val & 0xff; + err = Nu_FWrite(outfp, &tmpc, 1); + if (err != kNuErrNone) + goto bail; + tmpc = (val >> 8) & 0xff; + err = Nu_FWrite(outfp, &tmpc, 1); + if (err != kNuErrNone) + goto bail; + +bail: + return err; +} + +/* + * Compress "srcLen" bytes into SQ format, from "pStraw" to "fp". + * + * This requires two passes through the input. + * + * Bit of trivia: "sq3" on the Apple II self-destructs if you hand + * it an empty file. "xsq" works fine, creating an empty tree that + * "xusq" unpacks. + */ +NuError +Nu_CompressHuffmanSQ(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, + ulong srcLen, ulong* pDstLen, ushort* pCrc) +{ + NuError err = kNuErrNone; + SQState sqState; + long compressedLen; + int i, j, numNodes; + + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + + sqState.pArchive = pArchive; + sqState.crc = 0; + if (pCrc == nil) { + sqState.doCalcCRC = false; + } else { + sqState.doCalcCRC = true; + sqState.crc = *pCrc; + } + + #ifdef FULL_SQ_HEADER + sqState.checksum = 0; + #endif + + /* + * Pass 1: analysis. Perform a frequency analysis on the post-RLE + * input file. This will calculate the file CRCs as a side effect. + */ + sqState.rleState = kNuSQRLEStateNoHist; + sqState.uncompRemaining = srcLen; + sqState.pStraw = pStraw; + (void) Nu_StrawSetProgressState(pStraw, kNuProgressAnalyzing); + + err = Nu_SQComputeHuffTree(&sqState); + BailError(err); + + if (pCrc != nil) + *pCrc = sqState.crc; + + /* + * Pass 2: compression. Using the encoding tree we computed, + * compress the input with RLE and Huffman. Start by writing + * the file header and rewinding the input file. + */ + sqState.doCalcCRC = false; /* don't need to re-compute */ + sqState.rleState = kNuSQRLEStateNoHist; /* reset */ + compressedLen = 0; + + /* rewind for next pass */ + (void) Nu_StrawSetProgressState(pStraw, kNuProgressCompressing); + err = Nu_StrawRewind(pArchive, pStraw); + BailError(err); + sqState.uncompRemaining = srcLen; + + #ifdef FULL_SQ_HEADER + /* write file header */ + err = Nu_SQWriteShort(fp, kNuSQMagic); + BailError(err); + compressedLen += 2; + + err = Nu_SQWriteShort(fp, sqState.checksum); + BailError(err); + compressedLen += 2; + + { + static const char fakename[] = "s.qqq"; + err = Nu_FWrite(fp, fakename, sizeof(fakename)); + BailError(err); + compressedLen += sizeof(fakename); + } + #endif + + /* + * Original description: + * Write out a simplified decoding tree. Only the interior + * nodes are written. When a child is a leaf index + * (representing a data value) it is recoded as + * -(index + 1) to distinguish it from interior indexes + * which are recoded as positive indexes in the new tree. + * Note that this tree will be empty for an empty file. + */ + if (sqState.treeHead < kNuSQNumVals) + numNodes = 0; + else + numNodes = sqState.treeHead - (kNuSQNumVals - 1); + err = Nu_SQWriteShort(fp, (short) numNodes); + BailError(err); + compressedLen += 2; + + for (i = sqState.treeHead, j = 0; j < numNodes; j++, i--) { + int l, r; + + l = sqState.node[i].lchild; + r = sqState.node[i].rchild; + l = l < kNuSQNumVals ? -(l + 1) : sqState.treeHead - l; + r = r < kNuSQNumVals ? -(r + 1) : sqState.treeHead - r; + err = Nu_SQWriteShort(fp, (short) l); + BailError(err); + err = Nu_SQWriteShort(fp, (short) r); + BailError(err); + compressedLen += 4; + + /*DBUG(("TREE %d: %d %d\n", j, l, r));*/ + } + + /* + * Convert the input to RLE/Huffman. + */ + err = Nu_SQCompressInput(&sqState, fp, &compressedLen); + BailError(err); + + /* + * Done! + */ + *pDstLen = compressedLen; + +bail: + return err; +} + + +/* + * =========================================================================== + * Expansion + * =========================================================================== + */ + +/* + * State during uncompression. + */ +typedef struct USQState { + ulong dataInBuffer; + uchar* dataPtr; + int bitPosn; + int bits; + + /* + * Decoding tree; first "nodeCount" values are populated. Positive + * values are indicies to another node in the tree, negative values + * are literals (+1 because "negative zero" doesn't work well). + */ + int nodeCount; + struct { + short child[2]; /* left/right kids, must be signed 16-bit */ + } decTree[kNuSQNumVals-1]; +} USQState; + + +/* + * Decode the next symbol from the Huffman stream. + */ +static NuError +Nu_USQDecodeHuffSymbol(USQState* pUsqState, int* pVal) +{ + short val = 0; + int bits, bitPosn; + + bits = pUsqState->bits; /* local copy */ + bitPosn = pUsqState->bitPosn; + + do { + if (++bitPosn > 7) { + /* grab the next byte and use that */ + bits = *pUsqState->dataPtr++; + bitPosn = 0; + if (!pUsqState->dataInBuffer--) + return kNuErrBufferUnderrun; + + val = pUsqState->decTree[val].child[1 & bits]; + } else { + /* still got bits; shift right and use it */ + val = pUsqState->decTree[val].child[1 & (bits >>= 1)]; + } + } while (val >= 0); + + /* val is negative literal; add one to make it zero-based then negate it */ + *pVal = -(val + 1); + + pUsqState->bits = bits; + pUsqState->bitPosn = bitPosn; + + return kNuErrNone; +} + + +/* + * Read two bytes of signed data out of the buffer. + */ +static inline NuError +Nu_USQReadShort(USQState* pUsqState, short* pShort) +{ + if (pUsqState->dataInBuffer < 2) + return kNuErrBufferUnderrun; + + *pShort = *pUsqState->dataPtr++; + *pShort |= (*pUsqState->dataPtr++) << 8; + pUsqState->dataInBuffer -= 2; + + return kNuErrNone; +} + +/* + * Expand "SQ" format. + * + * Because we have a stop symbol, knowing the uncompressed length of + * the file is not essential. + */ +NuError +Nu_ExpandHuffmanSQ(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, ushort* pCrc) +{ + NuError err = kNuErrNone; + USQState usqState; + ulong compRemaining, getSize; +#ifdef FULL_SQ_HEADER + ushort magic, fileChecksum, checksum; +#endif + short nodeCount; + int i, inrep; + uchar lastc = 0; + + err = Nu_AllocCompressionBufferIFN(pArchive); + if (err != kNuErrNone) + return err; + Assert(pArchive->compBuf != nil); + + usqState.dataInBuffer = 0; + usqState.dataPtr = pArchive->compBuf; + usqState.bits = usqState.bitPosn = 0; + + compRemaining = pThread->thCompThreadEOF; +#ifdef FULL_SQ_HEADER + if (compRemaining < 8) +#else + if (compRemaining < 3) +#endif + { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, "thread too short to be valid SQ data"); + goto bail; + } + + getSize = compRemaining; + if (getSize > kNuGenCompBufSize) + getSize = kNuGenCompBufSize; + + /* + * Grab a big chunk. "compRemaining" is the amount of compressed + * data left in the file, usqState.dataInBuffer is the amount of + * compressed data left in the buffer. + */ + err = Nu_FRead(infp, usqState.dataPtr, getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, + "failed reading compressed data (%ld bytes)", getSize); + goto bail; + } + usqState.dataInBuffer += getSize; + compRemaining -= getSize; + + /* + * Read the header. We assume that the header is less than + * kNuGenCompBufSize bytes, which is pretty fair since the buffer is + * currently 20x larger than the longest possible header (sq allowed + * 300+ for the filename, plus 257*2 for the tree, plus misc). + */ + Assert(kNuGenCompBufSize > 1200); +#ifdef FULL_SQ_HEADER + err = Nu_USQReadShort(&usqState, &magic); + BailError(err); + if (magic != kNuSQMagic) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, "bad magic number in SQ block"); + goto bail; + } + + err = Nu_USQReadShort(&usqState, &fileChecksum); + BailError(err); + + checksum = 0; + + while (*usqState.dataPtr++ != '\0') + usqState.dataInBuffer--; + usqState.dataInBuffer--; +#endif + + err = Nu_USQReadShort(&usqState, &nodeCount); + BailError(err); + if (nodeCount < 0 || nodeCount >= kNuSQNumVals) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, "invalid decode tree in SQ (%d nodes)", + nodeCount); + goto bail; + } + usqState.nodeCount = nodeCount; + + /* initialize for possibly empty tree (only happens on an empty file) */ + usqState.decTree[0].child[0] = -(kNuSQEOFToken+1); + usqState.decTree[0].child[1] = -(kNuSQEOFToken+1); + + /* read the nodes, ignoring "read errors" until we're done */ + for (i = 0; i < nodeCount; i++) { + err = Nu_USQReadShort(&usqState, &usqState.decTree[i].child[0]); + err = Nu_USQReadShort(&usqState, &usqState.decTree[i].child[1]); + } + if (err != kNuErrNone) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, "SQ data looks truncated at tree"); + goto bail; + } + + usqState.bitPosn = 99; /* force an immediate read */ + + /* + * Start pulling data out of the file. We have to Huffman-decode + * the input, and then feed that into an RLE expander. + * + * A completely lopsided (and broken) Huffman tree could require + * 256 tree descents, so we want to try to ensure we have at least 256 + * bits in the buffer. Otherwise, we could get a false buffer underrun + * indication back from DecodeHuffSymbol. + * + * The SQ sources actually guarantee that a code will fit entirely + * in 16 bits, but there's no reason not to use the larger value. + */ + inrep = false; + while (1) { + int val; + + if (usqState.dataInBuffer < 65 && compRemaining) { + /* + * Less than 256 bits, but there's more in the file. + * + * First thing we do is slide the old data to the start of + * the buffer. + */ + if (usqState.dataInBuffer) { + Assert(pArchive->compBuf != usqState.dataPtr); + memmove(pArchive->compBuf, usqState.dataPtr, + usqState.dataInBuffer); + } + usqState.dataPtr = pArchive->compBuf; + + /* + * Next we read as much as we can. + */ + if (kNuGenCompBufSize - usqState.dataInBuffer < compRemaining) + getSize = kNuGenCompBufSize - usqState.dataInBuffer; + else + getSize = compRemaining; + + err = Nu_FRead(infp, usqState.dataPtr + usqState.dataInBuffer, + getSize); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, + "failed reading compressed data (%ld bytes)", getSize); + goto bail; + } + usqState.dataInBuffer += getSize; + compRemaining -= getSize; + + Assert(compRemaining < 32767*65536); + Assert(usqState.dataInBuffer <= kNuGenCompBufSize); + } + + err = Nu_USQDecodeHuffSymbol(&usqState, &val); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "failed decoding huff symbol"); + goto bail; + } + + if (val == kNuSQEOFToken) + break; + + /* + * Feed the symbol into the RLE decoder. + */ + if (inrep) { + /* + * Last char was RLE delim, handle this specially. We use + * --val instead of val-- because we already emitted the + * first occurrence of the char (right before the RLE delim). + */ + if (val == 0) { + /* special case -- just an escaped RLE delim */ + lastc = kNuSQRLEDelim; + val = 2; + } + while (--val) { + if (pCrc != nil) + *pCrc = Nu_CalcCRC16(*pCrc, &lastc, 1); + err = Nu_FunnelWrite(pArchive, pFunnel, &lastc, 1); + #ifdef FULL_SQ_HEADER + checksum += lastc; + #endif + } + inrep = false; + } else { + /* last char was ordinary */ + if (val == kNuSQRLEDelim) { + /* set a flag and catch the count the next time around */ + inrep = true; + } else { + lastc = val; + if (pCrc != nil) + *pCrc = Nu_CalcCRC16(*pCrc, &lastc, 1); + err = Nu_FunnelWrite(pArchive, pFunnel, &lastc, 1); + #ifdef FULL_SQ_HEADER + checksum += lastc; + #endif + } + } + + } + + if (inrep) { + err = kNuErrBadData; + Nu_ReportError(NU_BLOB, err, + "got stop symbol when run length expected"); + goto bail; + } + + #ifdef FULL_SQ_HEADER + /* verify the checksum stored in the SQ file */ + if (checksum != fileChecksum && !pArchive->valIgnoreCRC) { + if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadDataCRC)) { + err = kNuErrBadDataCRC; + Nu_ReportError(NU_BLOB, err, "expected 0x%04x, got 0x%04x (SQ)", + fileChecksum, checksum); + (void) Nu_FunnelFlush(pArchive, pFunnel); + goto bail; + } + } else { + DBUG(("--- SQ checksums match (0x%04x)\n", checksum)); + } + #endif + + /* + * SQ2 adds an extra 0xff to the end, xsq doesn't. In any event, it + * appears that having an extra byte at the end is okay. + */ + if (usqState.dataInBuffer > 1) { + DBUG(("--- Found %ld bytes following compressed data (compRem=%ld)\n", + usqState.dataInBuffer, compRemaining)); + Nu_ReportError(NU_BLOB, kNuErrNone, "(Warning) unexpected fluff (%ld)", + usqState.dataInBuffer); + } + +bail: + return err; +} + +#endif /*ENABLE_SQ*/ diff --git a/nufxlib/SunOS4.h b/nufxlib/SunOS4.h new file mode 100644 index 0000000..8db6562 --- /dev/null +++ b/nufxlib/SunOS4.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * This file was adapted from Devin Reade's "sunos4.h" in NuLib 3.2.5. + * It is provided for compilation under SunOS 4.x, when an ANSI compiler + * (such as gcc) is used. The system header files aren't quite sufficient + * to eliminate hordes of warnings. + */ +#ifndef __SunOS4__ +#define __SunOS4__ + +#ifdef __GNUC__ +extern int _flsbuf(int, FILE*); +extern int _filbuf(FILE*); +#endif + +extern void bcopy(char*, char*, int); +extern int fclose(FILE*); +extern int fflush(FILE*); +extern int fprintf(FILE*, const char*, ...); +extern int fread(char*, int, int, FILE *); +extern int fseek(FILE*, long, int); +extern int ftruncate(int, off_t); +extern int fwrite(const char*, int, int, FILE*); +extern char* mktemp(char *template); +extern time_t mktime(struct tm*); +extern int perror(const char*); +extern int printf(const char*, ...); +extern int remove(const char*); +extern int rename(const char*, const char*); +extern int tolower(int); +extern int setvbuf(FILE*, char*, int, int); +extern int sscanf(char*, const char*, ...); +extern int strcasecmp(const char*, const char*); +extern int strncasecmp(const char*, const char*, size_t); +extern long strtol(const char *, char **, int); +extern int system(const char*); +extern time_t timelocal(struct tm*); +extern time_t time(time_t*); +extern int toupper(int); +extern int vfprintf(FILE*, const char *, va_list); +extern char* vsprintf(char *str, const char *format, va_list ap); + +#endif /*__SunOS4__*/ diff --git a/nufxlib/SysDefs.h b/nufxlib/SysDefs.h new file mode 100644 index 0000000..ae87530 --- /dev/null +++ b/nufxlib/SysDefs.h @@ -0,0 +1,148 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * External type definitions and function prototypes. + */ +#ifndef __SysDefs__ +#define __SysDefs__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef DEBUG_VERBOSE +# define DEBUG_MSGS +#endif + +/* these should exist everywhere */ +#include +#include +#include +#include +#include +#include +#include + +/* basic Win32 stuff -- info-zip has much more complete defs */ +#if defined(_WIN32) || defined(MSDOS) +# define WINDOWS_LIKE + +# ifndef HAVE_CONFIG_H +# define HAVE_FCNTL_H +# define HAVE_MALLOC_H +# define HAVE_STDLIB_H +# define HAVE_SYS_STAT_H +# undef HAVE_SYS_TIME_H +# define HAVE_SYS_TYPES_H +# undef HAVE_UNISTD_H +# undef HAVE_UTIME_H +# define HAVE_SYS_UTIME_H +# define HAVE_WINDOWS_H +# define HAVE_FDOPEN +# undef HAVE_FTRUNCATE +# define HAVE_MEMMOVE +# undef HAVE_MKSTEMP +# define HAVE_MKTIME +# define HAVE_SNPRINTF +# undef HAVE_STRCASECMP +# undef HAVE_STRNCASECMP +# define HAVE_STRERROR +# define HAVE_STRTOUL +# define HAVE_VSNPRINTF +# define SNPRINTF_DECLARED +# define VSNPRINTF_DECLARED +# define SPRINTF_RETURNS_INT +# define uchar unsigned char +# define ushort unsigned short +# define uint unsigned int +# define ulong unsigned long +# define inline /*Visual C++6.0 can't inline ".c" files*/ +# define mode_t int +# define ENABLE_SQ +# define ENABLE_LZW +# define ENABLE_LZC +/*# define ENABLE_DEFLATE*/ +/*# define ENABLE_BZIP2*/ +# endif + +# include +# include +# define FOPEN_WANTS_B +# define HAVE_CHSIZE +# define snprintf _snprintf +# define vsnprintf _vsnprintf +#endif + +#ifdef HAVE_MALLOC_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_UTIME_H +# include +#endif +#ifdef HAVE_SYS_UTIME_H +# include +#endif + +#if defined(WINDOWS_LIKE) +# ifndef F_OK +# define F_OK 0 /* was 02 in <= v1.1.0 */ +# endif +#endif + +#if defined(__APPLE__) && defined(__MACH__) /* OS X */ +# define MAC_LIKE +# define UNIX_LIKE +#endif + +#if defined(__unix__) || defined(__unix) || defined(__BEOS__) || \ + defined(__hpux) || defined(_AIX) +# define UNIX_LIKE /* standardize */ +#endif + +#if defined(UNIX_LIKE) +# ifdef USE_REENTRANT_CALLS +# define _REENTRANT /* Solaris 2.x convention */ +# endif +#endif + +/* resource forks on UFS filesystem under Mac OS X are a kluge */ +/*#ifdef MAC*/ +/*# define HAS_RESOURCE_FORKS*/ +/*#endif*/ + +#if defined(__ORCAC__) || defined(MAC_LIKE) +# define HAS_RESOURCE_FORKS +#endif + +/* __FUNCTION__ was missing from BeOS __MWERKS__, and might be gcc-only */ +#ifdef __GNUC__ +# define HAS__FUNCTION__ +#endif + +#if defined(__sun__) && !defined(__SVR4) +# include "SunOS4.h" +#endif + +#if defined(__linux__) +# define HAS_MALLOC_CHECK_ +#endif + +#endif /*__SysDefs__*/ diff --git a/nufxlib/Thread.c b/nufxlib/Thread.c new file mode 100644 index 0000000..99480f2 --- /dev/null +++ b/nufxlib/Thread.c @@ -0,0 +1,1370 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Thread-level operations. + */ +#define __Thread_c__ 1 +#include "NufxLibPriv.h" + + +/* + * =========================================================================== + * Utils + * =========================================================================== + */ + +/* + * ShrinkIt v3.0.0 had a bug where the filename thread would get created + * with the high bits set. We want to undo that without stomping on + * filenames that just happen to have a fancy character in them. If all + * of the high bits are set, assume it's a "defective" name and clear + * them all. If some aren't set, assume it's just a fancy filename. + * + * This high-bit-ism was also done for disk archives by most older versions + * of ShrinkIt. + */ +void +Nu_StripHiIfAllSet(char* str) +{ + uchar* cp; + + for (cp = (uchar*)str; *cp != '\0'; cp++) + if (!(*cp & 0x80)) + return; + + for (cp = (uchar*)str; *cp != '\0'; cp++) + *cp &= 0x7f; +} + + +/* + * Decide if a thread is pre-sized (i.e. has a fixed maximum size with a + * lesser amount of uncompressed data within) based on the threadID. + */ +Boolean +Nu_IsPresizedThreadID(NuThreadID threadID) +{ + if (threadID == kNuThreadIDFilename || threadID == kNuThreadIDComment) + return true; + else + return false; +} + + +/* + * Return an indication of whether the type of thread specified by ThreadID + * should ever be compressed. Right now, that's only data-class threads. + */ +Boolean +Nu_IsCompressibleThreadID(NuThreadID threadID) +{ + if (NuThreadIDGetClass(threadID) == kNuThreadClassData) + return true; + else + return false; +} + + +/* + * Decide if the thread has a CRC, based on the record version and the + * threadID. + */ +Boolean +Nu_ThreadHasCRC(long recordVersion, NuThreadID threadID) +{ + return recordVersion >= 3 && + NuThreadIDGetClass(threadID) == kNuThreadClassData; +} + + +/* + * Search through a given NuRecord for the specified thread. + */ +NuError +Nu_FindThreadByIdx(const NuRecord* pRecord, NuThreadIdx thread, + NuThread** ppThread) +{ + NuThread* pThread; + int idx; + + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + if (pThread->threadIdx == thread) { + *ppThread = pThread; + return kNuErrNone; + } + } + + return kNuErrThreadIdxNotFound; +} + + +/* + * Search through a given NuRecord for the first thread with a matching + * threadID. + */ +NuError +Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID, + NuThread** ppThread) +{ + NuThread* pThread; + int idx; + + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + if (NuGetThreadID(pThread) == threadID) { + *ppThread = pThread; + return kNuErrNone; + } + } + + return kNuErrThreadIDNotFound; +} + + +/* + * Copy the contents of a NuThread. + */ +void +Nu_CopyThreadContents(NuThread* pDstThread, const NuThread* pSrcThread) +{ + Assert(pDstThread != nil); + Assert(pSrcThread != nil); + + memcpy(pDstThread, pSrcThread, sizeof(*pDstThread)); +} + + +/* + * =========================================================================== + * Reading threads from the archive + * =========================================================================== + */ + +/* + * Read a single thread header from the archive. + */ +static NuError +Nu_ReadThreadHeader(NuArchive* pArchive, NuThread* pThread, ushort* pCrc) +{ + FILE* fp; + + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(pCrc != nil); + + fp = pArchive->archiveFp; + + pThread->thThreadClass = Nu_ReadTwoC(pArchive, fp, pCrc); + pThread->thThreadFormat = Nu_ReadTwoC(pArchive, fp, pCrc); + pThread->thThreadKind = Nu_ReadTwoC(pArchive, fp, pCrc); + pThread->thThreadCRC = Nu_ReadTwoC(pArchive, fp, pCrc); + pThread->thThreadEOF = Nu_ReadFourC(pArchive, fp, pCrc); + pThread->thCompThreadEOF = Nu_ReadFourC(pArchive, fp, pCrc); + + pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); + pThread->actualThreadEOF = 0; /* fix me later */ + pThread->fileOffset = -1; /* mark as invalid */ + pThread->used = 0xcfcf; /* init to invalid value */ + + return Nu_HeaderIOFailed(pArchive, fp); +} + +/* + * Read the threads from the current archive file position. + * + * The storage for the threads is allocated here, in one block. We could + * have used a linked list like NuLib, but that doesn't really provide any + * benefit for us, and adds complexity. + */ +NuError +Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, ushort* pCrc) +{ + NuError err = kNuErrNone; + NuThread* pThread; + long count; + Boolean hasData = false; + + Assert(pArchive != nil); + Assert(pRecord != nil); + Assert(pCrc != nil); + + if (!pRecord->recTotalThreads) { + /* not sure if this is reasonable, but we can handle it */ + DBUG(("--- WEIRD: no threads in the record?\n")); + goto bail; + } + + pRecord->pThreads = Nu_Malloc(pArchive, + pRecord->recTotalThreads * sizeof(NuThread)); + BailAlloc(pRecord->pThreads); + + count = pRecord->recTotalThreads; + pThread = pRecord->pThreads; + while (count--) { + err = Nu_ReadThreadHeader(pArchive, pThread, pCrc); + BailError(err); + + if (pThread->thThreadClass == kNuThreadClassData) + hasData = true; + + /* + * Some versions of ShrinkIt write an invalid thThreadEOF for disks, + * so we have to figure out what it's supposed to be. + */ + if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == + kNuThreadIDDiskImage) + { + if (pRecord->recStorageType <= 13) { + /* supposed to be block size, but SHK v3.0.1 stored it wrong */ + pThread->actualThreadEOF = pRecord->recExtraType * 512; + + } else if (pRecord->recStorageType == 256 && + pRecord->recExtraType == 280 && + pRecord->recFileSysID == kNuFileSysDOS33) + { + /* + * Fix for less-common ShrinkIt problem: looks like an old + * version of GS/ShrinkIt used 256 as the block size when + * compressing DOS 3.3 images from 5.25" disks. If that + * appears to be the case here, crank up the block size. + */ + DBUG(("--- no such thing as a 70K disk image!\n")); + pThread->actualThreadEOF = pRecord->recExtraType * 512; + + } else { + pThread->actualThreadEOF = + pRecord->recExtraType * pRecord->recStorageType; + } + } else { + pThread->actualThreadEOF = pThread->thThreadEOF; + } + + pThread->used = false; + pThread++; + } + + /* + * If "mask threadless" is set, create "fake" threads with empty + * data and resource forks as needed. + */ + if (!hasData && pArchive->valMaskDataless) { + Boolean needRsrc = (pRecord->recStorageType == kNuStorageExtended); + int firstNewThread = pRecord->recTotalThreads; + + pRecord->recTotalThreads++; + pRecord->fakeThreads++; + if (needRsrc) { + pRecord->recTotalThreads++; + pRecord->fakeThreads++; + } + + pRecord->pThreads = Nu_Realloc(pArchive, pRecord->pThreads, + pRecord->recTotalThreads * sizeof(NuThread)); + BailAlloc(pRecord->pThreads); + + pThread = pRecord->pThreads + firstNewThread; + + pThread->thThreadClass = kNuThreadClassData; + pThread->thThreadFormat = kNuThreadFormatUncompressed; + pThread->thThreadKind = 0x0000; /* data fork */ + pThread->thThreadCRC = kNuInitialThreadCRC; + pThread->thThreadEOF = 0; + pThread->thCompThreadEOF = 0; + pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); + pThread->actualThreadEOF = 0; + pThread->fileOffset = -99999999; + pThread->used = false; + + if (needRsrc) { + pThread++; + pThread->thThreadClass = kNuThreadClassData; + pThread->thThreadFormat = kNuThreadFormatUncompressed; + pThread->thThreadKind = 0x0002; /* rsrc fork */ + pThread->thThreadCRC = kNuInitialThreadCRC; + pThread->thThreadEOF = 0; + pThread->thCompThreadEOF = 0; + pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); + pThread->actualThreadEOF = 0; + pThread->fileOffset = -99999999; + pThread->used = false; + } + } + +bail: + return err; +} + + +/* + * Write a single thread header to the archive. + */ +static NuError +Nu_WriteThreadHeader(NuArchive* pArchive, const NuThread* pThread, FILE* fp, + ushort* pCrc) +{ + Assert(pArchive != nil); + Assert(pThread != nil); + Assert(fp != nil); + Assert(pCrc != nil); + + Nu_WriteTwoC(pArchive, fp, pThread->thThreadClass, pCrc); + Nu_WriteTwoC(pArchive, fp, (ushort)pThread->thThreadFormat, pCrc); + Nu_WriteTwoC(pArchive, fp, pThread->thThreadKind, pCrc); + Nu_WriteTwoC(pArchive, fp, pThread->thThreadCRC, pCrc); + Nu_WriteFourC(pArchive, fp, pThread->thThreadEOF, pCrc); + Nu_WriteFourC(pArchive, fp, pThread->thCompThreadEOF, pCrc); + + return Nu_HeaderIOFailed(pArchive, fp); +} + +/* + * Write the thread headers for the record at the current file position. + * + * Note this doesn't care whether a thread was "fake" or not. In + * effect, we promote all threads to "real" status. We update the + * "fake" count in pRecord accordingly. + */ +NuError +Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp, + ushort* pCrc) +{ + NuError err = kNuErrNone; + NuThread* pThread; + int idx; + + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + err = Nu_WriteThreadHeader(pArchive, pThread, fp, pCrc); + BailError(err); + } + + if (pRecord->fakeThreads != 0) { + DBUG(("+++ promoting %ld fake threads to real\n",pRecord->fakeThreads)); + pRecord->fakeThreads = 0; + } + +bail: + return err; +} + + +/* + * Compute miscellaneous thread information, like total size and file + * offsets. Some values (like file offsets) will not be useful for + * streaming archives. + * + * Requires that the pArchive->currentOffset be set to the offset + * immediately after the last of the thread headers. + */ +NuError +Nu_ComputeThreadData(NuArchive* pArchive, NuRecord* pRecord) +{ + NuThread* pThread; + long fileOffset, count; + + Assert(pArchive != nil); + Assert(pRecord != nil); + + /*pRecord->totalLength = 0;*/ + pRecord->totalCompLength = 0; + + fileOffset = pArchive->currentOffset; + + count = pRecord->recTotalThreads; + pThread = pRecord->pThreads; + while (count--) { + pThread->fileOffset = fileOffset; + + /*pRecord->totalLength += pThread->thThreadEOF;*/ + pRecord->totalCompLength += pThread->thCompThreadEOF; + fileOffset += pThread->thCompThreadEOF; + + pThread++; + } + + return kNuErrNone; +} + + +/* + * Skip past some or all of the thread data in the archive. For file + * archives, we scan all the threads, but for streaming archives we only + * want to scan up to the filename thread. (If the filename thread comes + * after one of the data threads, we have a problem!) + * + * The tricky part here is that we don't want to skip over a filename + * thread. We actually want to read it in, so that we have something to + * show to the application. (Someday I'll get AndyN for putting me + * through this...) + */ +NuError +Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord, long numThreads) +{ + NuError err = kNuErrNone; + NuThread* pThread; + FILE* fp; + + Assert(pArchive != nil); + Assert(pRecord != nil); + + fp = pArchive->archiveFp; + + Assert(numThreads <= (long)pRecord->recTotalThreads); + + pThread = pRecord->pThreads; + while (numThreads--) { + if (pRecord->threadFilename == nil && + NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == + kNuThreadIDFilename) + { + /* it's the first filename thread, read the whole thing */ + if (pThread->thCompThreadEOF > kNuReasonableFilenameLen) { + err = kNuErrBadRecord; + Nu_ReportError(NU_BLOB, err, "Bad thread filename len (%lu)", + pThread->thCompThreadEOF); + goto bail; + } + pRecord->threadFilename = Nu_Malloc(pArchive, + pThread->thCompThreadEOF +1); + BailAlloc(pRecord->threadFilename); + + /* note there is no CRC on a filename thread */ + (void) Nu_ReadBytes(pArchive, fp, pRecord->threadFilename, + pThread->thCompThreadEOF); + if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Failed reading filename thread"); + goto bail; + } + + /* null-terminate on the actual len, not the buffer len */ + pRecord->threadFilename[pThread->thThreadEOF] = '\0'; + + Nu_StripHiIfAllSet(pRecord->threadFilename); + + /* prefer this one over the record one, but only one should exist */ + if (pRecord->filename != nil) { + DBUG(("--- HEY: got record filename and thread filename\n")); + } + pRecord->filename = pRecord->threadFilename; + + } else { + /* not a filename (or not first filename), skip past it */ + err = Nu_SeekArchive(pArchive, pArchive->archiveFp, + pThread->thCompThreadEOF, SEEK_CUR); + BailError(err); + } + + pThread++; + } + + /* + * Should've had one by now. Supposedly, older versions of ShrinkIt + * wouldn't prompt for a disk image name on DOS 3.3 volumes, so you'd + * end up with a disk image that had no name attached. This will tend + * to confuse things, so we go ahead and give it a name. + */ + if (pRecord->filename == nil) { + DBUG(("+++ no filename found, using default record name\n")); + pRecord->filename = kNuDefaultRecordName; + } + + pArchive->currentOffset += pRecord->totalCompLength; + + if (!Nu_IsStreaming(pArchive)) { + Assert(pArchive->currentOffset == ftell(pArchive->archiveFp)); + } + +bail: + return err; +} + + +/* + * Skip the thread. This only has meaning for streaming archives, and + * assumes that the file pointer is set to the start of the thread's data + * already. + */ +NuError +Nu_SkipThread(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread) +{ + NuError err; + + if (!Nu_IsStreaming(pArchive)) /* for debugging */ + return kNuErrNone; /* for debugging */ + Assert(Nu_IsStreaming(pArchive)); + + err = Nu_SeekArchive(pArchive, pArchive->archiveFp, + pThread->thCompThreadEOF, SEEK_CUR); + return err; +} + + +/* + * =========================================================================== + * Extract + * =========================================================================== + */ + +/* + * Extract the thread to the specified file pointer. + * + * If the archive is a stream, the stream must be positioned at the + * start of pThread's data. If not, it will be seeked first. + */ +static NuError +Nu_ExtractThreadToDataSink(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, NuProgressData* pProgress, NuDataSink* pDataSink) +{ + NuError err; + NuFunnel* pFunnel = nil; + + /* if it's not a stream, seek to the appropriate spot in the file */ + if (!Nu_IsStreaming(pArchive)) { + err = Nu_SeekArchive(pArchive, pArchive->archiveFp, + pThread->fileOffset, SEEK_SET); + if (err != kNuErrNone) { + Nu_ReportError(NU_BLOB, err, "Unable to seek input to %ld", + pThread->fileOffset); + goto bail; + } + } + + /* + * Set up an output funnel to write to. + */ + err = Nu_FunnelNew(pArchive, pDataSink, Nu_DataSinkGetConvertEOL(pDataSink), + pArchive->valEOL, pProgress, &pFunnel); + BailError(err); + + /* + * Write it. + */ + err = Nu_ExpandStream(pArchive, pRecord, pThread, pArchive->archiveFp, + pFunnel); + if (err != kNuErrNone) { + if (err != kNuErrSkipped && err != kNuErrAborted) + Nu_ReportError(NU_BLOB, err, "ExpandStream failed"); + goto bail; + } + +bail: + (void) Nu_FunnelFree(pArchive, pFunnel); + return err; +} + + +/* + * Extract the specified thread to "pDataSink". If the sink is to a file, + * this will take care of opening (and, if appropriate, creating) the file. + * + * If we're operating on a streaming archive, the file pointer must be + * positioned at the start of the thread's data. If not, it will be + * seeked appropriately. + * + * This calls the "should we extract" and "what pathname should we use" + * filters for every thread, which means we can reject specific kinds + * of forks and/or give them different names. This is a good thing. + */ +static NuError +Nu_ExtractThreadCommon(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread, NuDataSink* pDataSink) +{ + NuError err = kNuErrNone; + NuSelectionProposal selProposal; + NuPathnameProposal pathProposal; + NuProgressData progressData; + NuProgressData* pProgressData; + NuDataSink* pOrigDataSink; + char* newPathStorage = nil; + const char* newPathname; + NuResult result; + uchar newFssep; + Boolean doFreeSink = false; + + Assert(pRecord != nil); + Assert(pThread != nil); + Assert(pDataSink != nil); + + memset(&progressData, 0, sizeof(progressData)); + pProgressData = nil; + + /* + * If we're just trying to verify the archive contents, create a + * data sink that goes nowhere at all. + */ + if (pArchive->testMode) { + err = Nu_DataSinkVoid_New( + Nu_DataSinkGetDoExpand(pDataSink), + Nu_DataSinkGetConvertEOL(pDataSink), + &pDataSink); + BailError(err); + doFreeSink = true; + } + + pOrigDataSink = pDataSink; /* save a copy for the "retry" loop */ + + /* + * Decide if we want to extract this thread. This is mostly for + * use by the "bulk" extract, not the per-thread extract, but it + * still applies if they so desire. + */ + if (pArchive->selectionFilterFunc != nil) { + selProposal.pRecord = pRecord; + selProposal.pThread = pThread; + result = (*pArchive->selectionFilterFunc)(pArchive, &selProposal); + + if (result == kNuSkip) + return Nu_SkipThread(pArchive, pRecord, pThread); + if (result == kNuAbort) { + err = kNuErrAborted; + goto bail; + } + } + + newPathname = nil; + newFssep = 0; + +retry_name: + if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { + /* + * We're extracting. Figure out the name of the file to write it to. + * If they want to use the sleazy FILE* back door, create a new + * data sink and use that instead. + * + * Start by resetting everything to defaults, in case this isn't + * our first time through the "rename" loop. + */ + newPathname = Nu_DataSinkFile_GetPathname(pDataSink); + newFssep = Nu_DataSinkFile_GetFssep(pDataSink); + pDataSink = pOrigDataSink; + + /* if they don't have a pathname func defined, we just use default */ + if (pArchive->outputPathnameFunc != nil) { + pathProposal.pathname = pRecord->filename; + pathProposal.filenameSeparator = + NuGetSepFromSysInfo(pRecord->recFileSysInfo); + pathProposal.pRecord = pRecord; + pathProposal.pThread = pThread; + pathProposal.newPathname = nil; + pathProposal.newFilenameSeparator = '\0'; + /*pathProposal.newStorage = (NuThreadID)-1;*/ + pathProposal.newDataSink = nil; + + result = (*pArchive->outputPathnameFunc)(pArchive, &pathProposal); + + if (result == kNuSkip) + return Nu_SkipThread(pArchive, pRecord, pThread); + if (result == kNuAbort) { + err = kNuErrAborted; + goto bail; + } + + /* we don't own this string, so make a copy */ + if (pathProposal.newPathname != nil) { + newPathStorage = strdup(pathProposal.newPathname); + newPathname = newPathStorage; + } else + newPathname = nil; + if (pathProposal.newFilenameSeparator != '\0') + newFssep = pathProposal.newFilenameSeparator; + + /* if they want to send this somewhere else, let them */ + if (pathProposal.newDataSink != nil) + pDataSink = pathProposal.newDataSink; + } + + /* at least one of these must be set */ + Assert(!(newPathname == nil && pathProposal.newDataSink == nil)); + } + + /* + * Prepare the progress data if this is a data thread. + */ + if (newPathname == nil) { + /* using a data sink; get the pathname out of the record */ + newPathname = pRecord->filename; + newFssep = NuGetSepFromSysInfo(pRecord->recFileSysInfo); + } + if (pThread->thThreadClass == kNuThreadClassData) { + pProgressData = &progressData; + err = Nu_ProgressDataInit_Expand(pArchive, pProgressData, pRecord, + newPathname, newFssep, Nu_DataSinkGetConvertEOL(pOrigDataSink)); + BailError(err); + + /* send initial progress so they see the right name if "open" fails */ + pProgressData->state = kNuProgressOpening; + err = Nu_SendInitialProgress(pArchive, pProgressData); + BailError(err); + } + + if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { + /* + * We're extracting to a file. Open it, creating it if necessary and + * allowed. + */ + FILE* fileFp = nil; + + err = Nu_OpenOutputFile(pArchive, pRecord, pThread, newPathname, + newFssep, &fileFp); + if (err == kNuErrRename) { + /* they want to rename; the OutputPathname callback handles this */ + Nu_Free(pArchive, newPathStorage); + newPathStorage = nil; + /* reset these just to be careful */ + newPathname = nil; + fileFp = nil; + goto retry_name; + } else if (err != kNuErrNone) { + goto bail; + } + + Assert(fileFp != nil); + (void) Nu_DataSinkFile_SetFP(pDataSink, fileFp); + + DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to '%s'\n", + NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), + pRecord->filename, pThread->fileOffset, newPathname)); + } else { + DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to sink\n", + NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), + pRecord->filename, pThread->fileOffset)); + } + + /* extract to the file */ + err = Nu_ExtractThreadToDataSink(pArchive, pRecord, pThread, + pProgressData, pDataSink); + BailError(err); + + if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { + /* + * Close the file, adjusting the modification date and access + * permissions as appropriate. + */ + err = Nu_CloseOutputFile(pArchive, pRecord, + Nu_DataSinkFile_GetFP(pDataSink), newPathname); + Nu_DataSinkFile_SetFP(pDataSink, nil); + BailError(err); + } + +bail: + if (err != kNuErrNone && pProgressData != nil) { + /* send a final progress message, indicating failure */ + if (err == kNuErrSkipped) + pProgressData->state = kNuProgressSkipped; + else if (err == kNuErrAborted) + pProgressData->state = kNuProgressAborted; + else + pProgressData->state = kNuProgressFailed; + (void) Nu_SendInitialProgress(pArchive, pProgressData); + } + + /* if this was an ordinary file, and it's still open, close it */ + if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) + Nu_DataSinkFile_Close(pDataSink); + + Nu_Free(pArchive, newPathStorage); + + if (doFreeSink) + Nu_DataSinkFree(pDataSink); + return err; +} + +/* + * Extract a thread from the archive as part of a "bulk" extract operation. + * + * Streaming archives must be properly positioned. + */ +NuError +Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread) +{ + NuError err; + NuDataSink* pDataSink = nil; + NuValue eolConv; + + /* + * Create a file data sink for the file. We use whatever EOL conversion + * is set as the default for the entire archive. (If you want to + * specify your own EOL conversion for each individual file, you will + * need to extract them individually, creating a data sink for each.) + * + * One exception: we turn EOL conversion off for disk image threads. + * It's *very* unlikely this would be desirable, and could be a problem + * if the user is extracting a collection of disks and files. + */ + eolConv = pArchive->valConvertExtractedEOL; + if (NuGetThreadID(pThread) == kNuThreadIDDiskImage) + eolConv = kNuConvertOff; + err = Nu_DataSinkFile_New(true, eolConv, pRecord->filename, + NuGetSepFromSysInfo(pRecord->recFileSysInfo), &pDataSink); + BailError(err); + + err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink); + BailError(err); + +bail: + if (pDataSink != nil) { + NuError err2 = Nu_DataSinkFree(pDataSink); + if (err == kNuErrNone) + err = err2; + } + + return err; +} + + +/* + * Extract a thread, given the IDs and a data sink. + */ +NuError +Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSink* pDataSink) +{ + NuError err; + NuRecord* pRecord; + NuThread* pThread; + + if (Nu_IsStreaming(pArchive)) + return kNuErrUsage; + if (threadIdx == 0 || pDataSink == nil) + return kNuErrInvalidArg; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* find the correct record and thread by index */ + err = Nu_RecordSet_FindByThreadIdx(&pArchive->origRecordSet, threadIdx, + &pRecord, &pThread); + BailError(err); + Assert(pRecord != nil); + + /* extract away */ + err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink); + BailError(err); + +bail: + return err; +} + + +/* + * =========================================================================== + * Add/update/delete + * =========================================================================== + */ + +/* + * Verify that a conflicting thread with the specified threadID does not + * exist in this record, now or in the future. + * + * The set of interesting threads is equal to the current threads, minus + * any that have been deleted, plus any that have been added already. + * + * If a matching threadID is found, this returns an error. + */ +static NuError +Nu_FindNoFutureThread(NuArchive* pArchive, const NuRecord* pRecord, + NuThreadID threadID) +{ + NuError err = kNuErrNone; + const NuThread* pThread; + const NuThreadMod* pThreadMod; + int idx; + + /* + * Start by scanning the existing threads (if any). + */ + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + if (NuGetThreadID(pThread) == threadID) { + /* found a match, see if it has been deleted */ + pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, + pThread->threadIdx); + if (pThreadMod != nil && + pThreadMod->entry.kind == kNuThreadModDelete) + { + /* it's deleted, ignore it */ + continue; + } + DBUG(("--- found existing thread matching 0x%08lx\n", threadID)); + err = kNuErrThreadAdd; + goto bail; + } + } + + /* + * Now look for "add" threadMods with a matching threadID. + */ + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + if (pThreadMod->entry.kind == kNuThreadModAdd && + pThreadMod->entry.add.threadID == threadID) + { + DBUG(("--- found 'add' threadMod matching 0x%08lx\n", threadID)); + err = kNuErrThreadAdd; + goto bail; + } + + pThreadMod = pThreadMod->pNext; + } + +bail: + return err; +} + +/* + * Like Nu_FindNoFutureThread, but tests against a whole class. + */ +static NuError +Nu_FindNoFutureThreadClass(NuArchive* pArchive, const NuRecord* pRecord, + long threadClass) +{ + NuError err = kNuErrNone; + const NuThread* pThread; + const NuThreadMod* pThreadMod; + int idx; + + /* + * Start by scanning the existing threads (if any). + */ + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = Nu_GetThread(pRecord, idx); + Assert(pThread != nil); + + if (pThread->thThreadClass == threadClass) { + /* found a match, see if it has been deleted */ + pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, + pThread->threadIdx); + if (pThreadMod != nil && + pThreadMod->entry.kind == kNuThreadModDelete) + { + /* it's deleted, ignore it */ + continue; + } + DBUG(("--- Found existing thread matching 0x%04lx\n", threadClass)); + err = kNuErrThreadAdd; + goto bail; + } + } + + /* + * Now look for "add" threadMods with a matching threadClass. + */ + pThreadMod = pRecord->pThreadMods; + while (pThreadMod != nil) { + if (pThreadMod->entry.kind == kNuThreadModAdd && + NuThreadIDGetClass(pThreadMod->entry.add.threadID) == threadClass) + { + DBUG(("--- Found 'add' threadMod matching 0x%04lx\n", threadClass)); + err = kNuErrThreadAdd; + goto bail; + } + + pThreadMod = pThreadMod->pNext; + } + +bail: + return err; +} + + +/* + * Find an existing thread somewhere in the archive. If the "copy" set + * exists it will be searched. If not, the "orig" set is searched, and + * if an entry is found a "copy" set will be created. + * + * The record and thread returned will always be from the "copy" set. An + * error result is returned if the record and thread aren't found. + */ +static NuError +Nu_FindThreadForWriteByIdx(NuArchive* pArchive, NuThreadIdx threadIdx, + NuRecord** ppFoundRecord, NuThread** ppFoundThread) +{ + NuError err; + + if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { + err = Nu_RecordSet_FindByThreadIdx(&pArchive->copyRecordSet, threadIdx, + ppFoundRecord, ppFoundThread); + } else { + Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); + err = Nu_RecordSet_FindByThreadIdx(&pArchive->origRecordSet, threadIdx, + ppFoundRecord, ppFoundThread); + *ppFoundThread = nil; /* can't delete from here, wipe ptr */ + } + BailError(err); + + /* + * The thread exists. If we were looking in the "orig" set, we have + * to create a "copy" set, and delete it from that. + */ + if (*ppFoundThread == nil) { + err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, + &pArchive->origRecordSet); + BailError(err); + err = Nu_RecordSet_FindByThreadIdx(&pArchive->copyRecordSet, threadIdx, + ppFoundRecord, ppFoundThread); + Assert(err == kNuErrNone && *ppFoundThread != nil); /* must succeed */ + BailError(err); + } + +bail: + return err; +} + +/* + * Determine if it's okay to add a thread of the type specified by + * "threadID" into "pRecord". + * + * Returns with an error (kNuErrThreadAdd) if it's not okay. + */ +NuError +Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord, + NuThreadID threadID) +{ + NuError err = kNuErrNone; + + /* + * Check for class conflicts (can't mix data and control threads). + */ + if (NuThreadIDGetClass(threadID) == kNuThreadClassData) { + err = Nu_FindNoFutureThreadClass(pArchive, pRecord, + kNuThreadClassControl); + BailError(err); + } else if (NuThreadIDGetClass(threadID) == kNuThreadClassControl) { + err = Nu_FindNoFutureThreadClass(pArchive, pRecord, + kNuThreadClassData); + BailError(err); + } + + /* + * Check for specific type conflicts. + */ + if (threadID == kNuThreadIDDataFork) { + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDataFork); + BailError(err); + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); + BailError(err); + } else if (threadID == kNuThreadIDRsrcFork) { + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDRsrcFork); + BailError(err); + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); + BailError(err); + } else if (threadID == kNuThreadIDDiskImage) { + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDataFork); + BailError(err); + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDRsrcFork); + BailError(err); + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); + BailError(err); + } else if (threadID == kNuThreadIDFilename) { + err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDFilename); + BailError(err); + } + +bail: + return err; +} + + + +/* + * Add a new thread to a record. + * + * In some cases, you aren't allowed to add a thread whose type matches + * one that already exists. This applies to data threads and filenames, + * but not to comments, control threads, or IIgs icons. You also can't + * add a disk image thread when there are data-class threads, or vice-versa. + * + * This is the first and last place we do this sort of checking. If + * an illegal situation gets past this function, it will either get + * caught with a fatal assert or (if NDEBUG is defined) not at all. + * + * On success, the NuThreadIdx of the newly-created record will be placed + * in "*pThreadIdx", and "pDataSource" will be owned by NufxLib. + */ +NuError +Nu_AddThread(NuArchive* pArchive, NuRecordIdx recIdx, NuThreadID threadID, + NuDataSource* pDataSource, NuThreadIdx* pThreadIdx) +{ + NuError err; + NuRecord* pRecord; + NuThreadMod* pThreadMod = nil; + NuThreadFormat threadFormat; + + /* okay for pThreadIdx to be nil */ + if (recIdx == 0 || pDataSource == nil) + return kNuErrInvalidArg; + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* + * Find the record. If it doesn't exist in the copy set, check to + * see if it's in the "new" set. + */ + err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); + if (err == kNuErrRecIdxNotFound && + Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) + { + err = Nu_RecordSet_FindByIdx(&pArchive->newRecordSet, recIdx, &pRecord); + } + BailError(err); + Assert(pRecord != nil); + + /* + * Do some tests, looking for specific types of threads that conflict + * with what we're trying to add. + */ + err = Nu_OkayToAddThread(pArchive, pRecord, threadID); + BailError(err); + + /* + * Decide if we want to compress the data from this source. If the + * data is already compressed (as indicated by the data source) or + * this type of thread isn't compressible (e.g. it's a filename), then + * we don't compress it. Otherwise, we use whatever compression mode + * is currently configured. + */ + if (Nu_DataSourceGetThreadFormat(pDataSource) == kNuThreadFormatUncompressed && + Nu_IsCompressibleThreadID(threadID)) + { + threadFormat = Nu_ConvertCompressValToFormat(pArchive, + pArchive->valDataCompression); + } else { + threadFormat = kNuThreadFormatUncompressed; + } + DBUG(("--- using threadFormat = %d\n", threadFormat)); + + /* create a new ThreadMod (which makes a copy of the data source) */ + err = Nu_ThreadModAdd_New(pArchive, threadID, threadFormat, pDataSource, + &pThreadMod); + BailError(err); + Assert(pThreadMod != nil); + + /* add the thread mod to the record */ + Nu_RecordAddThreadMod(pRecord, pThreadMod); + if (pThreadIdx != nil) + *pThreadIdx = pThreadMod->entry.add.threadIdx; + pThreadMod = nil; /* successful, don't free */ + + /* + * If we've got a header filename and we're adding a filename thread, + * we don't want to write the record header name when we reconstruct + * the record. + */ + if (threadID == kNuThreadIDFilename && pRecord->recFilenameLength) { + DBUG(("+++ gonna drop the filename\n")); + pRecord->dropRecFilename = true; + } + +bail: + if (pThreadMod != nil) + Nu_ThreadModFree(pArchive, pThreadMod); + if (err == kNuErrNone && pDataSource != nil) { + /* on success, we have ownership of the data source. ThreadMod + made its own copy, so get rid of this one */ + Nu_DataSourceFree(pDataSource); + } + return err; +} + + +/* + * Update the contents of a pre-sized thread, such as a filename or + * comment thread. + * + * The data from the source must fit within the limits of the existing + * thread. The source data is never compressed, and must not come from + * a compressed source. + * + * You aren't allowed to update threads that have been deleted. Updating + * newly-added threads isn't possible, since they aren't really threads yet. + */ +NuError +Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx, + NuDataSource* pDataSource, long* pMaxLen) +{ + NuError err; + NuThreadMod* pThreadMod = nil; + NuRecord* pFoundRecord; + NuThread* pFoundThread; + + if (pDataSource == nil) { + err = kNuErrInvalidArg; + goto bail; + } + + /* presized threads always contain uncompressed data */ + if (Nu_DataSourceGetThreadFormat(pDataSource) != + kNuThreadFormatUncompressed) + { + err = kNuErrBadFormat; + Nu_ReportError(NU_BLOB, err, + "presized threads can't hold compressed data"); + goto bail; + } + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* + * Find the thread in the "copy" set. (If there isn't a copy set, + * make one.) + */ + err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord, + &pFoundThread); + BailError(err); + + if (!Nu_IsPresizedThreadID(NuGetThreadID(pFoundThread)) || + !(pFoundThread->thCompThreadEOF >= pFoundThread->thThreadEOF)) + { + err = kNuErrNotPreSized; + Nu_ReportError(NU_BLOB, err, "invalid thread for update"); + goto bail; + } + + if (pMaxLen != nil) + *pMaxLen = pFoundThread->thCompThreadEOF; + + /* + * Check to see if somebody is trying to delete this, or has already + * updated it. + */ + if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != nil) { + DBUG(("--- Tried to modify a deleted or modified thread\n")); + err = kNuErrModThreadChange; + goto bail; + } + + /* + * Verify that "otherLen" in the data source is less than or equal + * to our len, if we can. If the data source is a file on disk, + * we're not really supposed to look at it until we flush. We + * could sneak a peek right now, which would prevent us from aborting + * the entire operation when it turns out the file won't fit, but + * that violates our semantics (and besides, the application really + * should've done that already). + * + * If the data source is from a file, we just assume it'll fit and + * let the chips fall where they may later on. + */ + if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile) { + if (pFoundThread->thCompThreadEOF < + Nu_DataSourceGetOtherLen(pDataSource)) + { + err = kNuErrPreSizeOverflow; + Nu_ReportError(NU_BLOB, err, "can't put %ld bytes into %ld", + Nu_DataSourceGetOtherLen(pDataSource), + pFoundThread->thCompThreadEOF); + goto bail; + } + + /* check for zero-length and excessively long filenames */ + if (NuGetThreadID(pFoundThread) == kNuThreadIDFilename && + (Nu_DataSourceGetOtherLen(pDataSource) == 0 || + Nu_DataSourceGetOtherLen(pDataSource) > kNuReasonableFilenameLen)) + { + err = kNuErrInvalidFilename; + Nu_ReportError(NU_BLOB, err, "invalid filename (%ld bytes)", + Nu_DataSourceGetOtherLen(pDataSource)); + goto bail; + } + } + + /* + * Looks like it'll fit, and it's the right kind of data. Create + * an "update" threadMod. Note this copies the data source. + */ + Assert(pFoundThread->thThreadFormat == kNuThreadFormatUncompressed); + err = Nu_ThreadModUpdate_New(pArchive, threadIdx, pDataSource, &pThreadMod); + BailError(err); + Assert(pThreadMod != nil); + + /* add the thread mod to the record */ + Nu_RecordAddThreadMod(pFoundRecord, pThreadMod); + + /* + * NOTE: changes to filename threads will be picked up later and + * incorporated into the record's threadFilename. We don't worry + * about the record header filename, because we might be doing an + * update-in-place and that prevents us from removing the filename + * (doing so would change the size of the archive). No need to + * do any filename-specific changes here. + */ + +bail: + return err; +} + +/* + * Delete an individual thread. + * + * You aren't allowed to delete threads that have been updated. Deleting + * newly-added threads isn't possible, since they aren't really threads yet. + * + * Don't worry about deleting filename threads here; we take care of that + * later on. Besides, it's sort of handy to hang on to the filename for + * as long as possible. + */ +NuError +Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx) +{ + NuError err; + NuThreadMod* pThreadMod = nil; + NuRecord* pFoundRecord; + NuThread* pFoundThread; + + if (Nu_IsReadOnly(pArchive)) + return kNuErrArchiveRO; + err = Nu_GetTOCIfNeeded(pArchive); + BailError(err); + + /* + * Find the thread in the "copy" set. (If there isn't a copy set, + * make one.) + */ + err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord, + &pFoundThread); + BailError(err); + + /* + * Deletion of modified threads (updates or previous deletes) isn't + * allowed. Deletion of threads from deleted records can't happen, + * because deleted records are completely removed from the "copy" set. + */ + if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != nil) { + DBUG(("--- Tried to delete a deleted or modified thread\n")); + err = kNuErrModThreadChange; + goto bail; + } + + /* + * Looks good. Add a new "delete" ThreadMod to the list. + */ + err = Nu_ThreadModDelete_New(pArchive, threadIdx, + NuGetThreadID(pFoundThread), &pThreadMod); + BailError(err); + Nu_RecordAddThreadMod(pFoundRecord, pThreadMod); + pThreadMod = nil; /* successful, don't free */ + +bail: + Nu_ThreadModFree(pArchive, pThreadMod); + return err; +} + diff --git a/nufxlib/Value.c b/nufxlib/Value.c new file mode 100644 index 0000000..d6465f7 --- /dev/null +++ b/nufxlib/Value.c @@ -0,0 +1,329 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + * + * Get/set certain values and attributes. + */ +#include "NufxLibPriv.h" + +#define kMaxJunkSkipMax 8192 + + +/* + * Get a configurable parameter. + */ +NuError +Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue) +{ + NuError err = kNuErrNone; + + if (pValue == nil) + return kNuErrInvalidArg; + + switch (ident) { + case kNuValueAllowDuplicates: + *pValue = pArchive->valAllowDuplicates; + break; + case kNuValueConvertExtractedEOL: + *pValue = pArchive->valConvertExtractedEOL; + break; + case kNuValueDataCompression: + *pValue = pArchive->valDataCompression; + break; + case kNuValueDiscardWrapper: + *pValue = pArchive->valDiscardWrapper; + break; + case kNuValueEOL: + *pValue = pArchive->valEOL; + break; + case kNuValueHandleExisting: + *pValue = pArchive->valHandleExisting; + break; + case kNuValueIgnoreCRC: + *pValue = pArchive->valIgnoreCRC; + break; + case kNuValueMaskDataless: + *pValue = pArchive->valMaskDataless; + break; + case kNuValueMimicSHK: + *pValue = pArchive->valMimicSHK; + break; + case kNuValueModifyOrig: + *pValue = pArchive->valModifyOrig; + break; + case kNuValueOnlyUpdateOlder: + *pValue = pArchive->valOnlyUpdateOlder; + break; + case kNuValueStripHighASCII: + *pValue = pArchive->valStripHighASCII; + break; + case kNuValueJunkSkipMax: + *pValue = pArchive->valJunkSkipMax; + break; + case kNuValueIgnoreLZW2Len: + *pValue = pArchive->valIgnoreLZW2Len; + break; + case kNuValueHandleBadMac: + *pValue = pArchive->valHandleBadMac; + break; + default: + err = kNuErrInvalidArg; + Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident); + goto bail; + } + +bail: + return err; +} + + +/* + * Set a configurable parameter. + */ +NuError +Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value) +{ + NuError err = kNuErrInvalidArg; + + switch (ident) { + case kNuValueAllowDuplicates: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueAllowDuplicates value %ld", value); + goto bail; + } + pArchive->valAllowDuplicates = value; + break; + case kNuValueConvertExtractedEOL: + if (value < kNuConvertOff || value > kNuConvertAuto) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueConvertExtractedEOL value %ld", value); + goto bail; + } + pArchive->valConvertExtractedEOL = value; + break; + case kNuValueDataCompression: + if (value < kNuCompressNone || value > kNuCompressBzip2) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueDataCompression value %ld", value); + goto bail; + } + pArchive->valDataCompression = value; + break; + case kNuValueDiscardWrapper: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueDiscardWrapper value %ld", value); + goto bail; + } + pArchive->valDiscardWrapper = value; + break; + case kNuValueEOL: + if (value < kNuEOLUnknown || value > kNuEOLCRLF) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueEOL value %ld", value); + goto bail; + } + pArchive->valEOL = value; + break; + case kNuValueHandleExisting: + if (value < kNuMaybeOverwrite || value > kNuMustOverwrite) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueHandleExisting value %ld", value); + goto bail; + } + pArchive->valHandleExisting = value; + break; + case kNuValueIgnoreCRC: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueIgnoreCRC value %ld", value); + goto bail; + } + pArchive->valIgnoreCRC = value; + break; + case kNuValueMaskDataless: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueMaskDataless value %ld", value); + goto bail; + } + pArchive->valMaskDataless = value; + break; + case kNuValueMimicSHK: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueMimicSHK value %ld", value); + goto bail; + } + pArchive->valMimicSHK = value; + break; + case kNuValueModifyOrig: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueModifyOrig value %ld", value); + goto bail; + } + pArchive->valModifyOrig = value; + break; + case kNuValueOnlyUpdateOlder: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueOnlyUpdateOlder value %ld", value); + goto bail; + } + pArchive->valOnlyUpdateOlder = value; + break; + case kNuValueStripHighASCII: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueStripHighASCII value %ld", value); + goto bail; + } + pArchive->valStripHighASCII = value; + break; + case kNuValueJunkSkipMax: + if (value > kMaxJunkSkipMax) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueJunkSkipMax value %ld", value); + goto bail; + } + pArchive->valJunkSkipMax = value; + break; + case kNuValueIgnoreLZW2Len: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueIgnoreLZW2Len value %ld", value); + goto bail; + } + pArchive->valIgnoreLZW2Len = value; + break; + case kNuValueHandleBadMac: + if (value != true && value != false) { + Nu_ReportError(NU_BLOB, err, + "Invalid kNuValueHandleBadMac value %ld", value); + goto bail; + } + pArchive->valHandleBadMac = value; + break; + default: + Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident); + goto bail; + } + + err = kNuErrNone; + +bail: + return err; +} + + +/* + * Get an archive attribute. These are things that you would have to + * pry into pArchive to get at (like the archive type) or get the master + * header (like the number of records). + */ +NuError +Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr) +{ + NuError err = kNuErrNone; + if (pAttr == nil) + return kNuErrInvalidArg; + + switch (ident) { + case kNuAttrArchiveType: + *pAttr = pArchive->archiveType; + break; + case kNuAttrNumRecords: + *pAttr = pArchive->masterHeader.mhTotalRecords; + break; + case kNuAttrHeaderOffset: + *pAttr = pArchive->headerOffset; + break; + case kNuAttrJunkOffset: + *pAttr = pArchive->junkOffset; + break; + default: + err = kNuErrInvalidArg; + Nu_ReportError(NU_BLOB, err, "Unknown AttrID %d requested", ident); + goto bail; + } + +bail: + return err; +} + +/* + * Convert a NuValue compression type to a "phyiscal" ThreadFormat. + * + * Unsupported compression types cause a warning to be flagged. + */ +NuThreadFormat +Nu_ConvertCompressValToFormat(NuArchive* pArchive, NuValue compValue) +{ + NuThreadFormat threadFormat; + Boolean unsup = false; + + switch (compValue) { + case kNuCompressNone: threadFormat = kNuThreadFormatUncompressed; break; + + #ifdef ENABLE_SQ + case kNuCompressSQ: threadFormat = kNuThreadFormatHuffmanSQ; break; + #else + case kNuCompressSQ: threadFormat = kNuThreadFormatHuffmanSQ; + unsup = true; break; + #endif + + #ifdef ENABLE_LZW + case kNuCompressLZW1: threadFormat = kNuThreadFormatLZW1; break; + case kNuCompressLZW2: threadFormat = kNuThreadFormatLZW2; break; + #else + case kNuCompressLZW1: threadFormat = kNuThreadFormatLZW1; + unsup = true; break; + case kNuCompressLZW2: threadFormat = kNuThreadFormatLZW2; + unsup = true; break; + #endif + + #ifdef ENABLE_LZC + case kNuCompressLZC12: threadFormat = kNuThreadFormatLZC12; break; + case kNuCompressLZC16: threadFormat = kNuThreadFormatLZC16; break; + #else + case kNuCompressLZC12: threadFormat = kNuThreadFormatLZC12; + unsup = true; break; + case kNuCompressLZC16: threadFormat = kNuThreadFormatLZC16; + unsup = true; break; + #endif + + #ifdef ENABLE_DEFLATE + case kNuCompressDeflate: threadFormat = kNuThreadFormatDeflate; break; + #else + case kNuCompressDeflate: threadFormat = kNuThreadFormatDeflate; + unsup = true; break; + #endif + + #ifdef ENABLE_BZIP2 + case kNuCompressBzip2: threadFormat = kNuThreadFormatBzip2; break; + #else + case kNuCompressBzip2: threadFormat = kNuThreadFormatBzip2; + unsup = true; break; + #endif + + default: + Nu_ReportError(NU_BLOB, kNuErrInvalidArg, + "Unknown compress value %ld", compValue); + Assert(false); + return kNuThreadFormatUncompressed; + } + + if (unsup) { + Nu_ReportError(NU_BLOB, kNuErrNone, + "Unsupported compression 0x%04x requested (%ld), storing", + threadFormat, compValue); + return kNuThreadFormatUncompressed; + } + + return threadFormat; +} + diff --git a/nufxlib/Version.c b/nufxlib/Version.c new file mode 100644 index 0000000..7eec7c2 --- /dev/null +++ b/nufxlib/Version.c @@ -0,0 +1,42 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + */ +#include "NufxLibPriv.h" + +/* executable was build on or after this date */ +#ifdef __DATE__ +static const char gNuBuildDate[] = __DATE__; +#else +static const char gNuBuildDate[] = "??? ?? ????"; +#endif + +#ifdef OPTFLAGSTR +static const char gNuBuildFlags[] = OPTFLAGSTR; +#else +static const char gNuBuildFlags[] = "-"; +#endif + + +/* + * Return the version number, date built, and build flags. + */ +NuError +Nu_GetVersion(long* pMajorVersion, long* pMinorVersion, long* pBugVersion, + const char** ppBuildDate, const char** ppBuildFlags) +{ + if (pMajorVersion != nil) + *pMajorVersion = kNuVersionMajor; + if (pMinorVersion != nil) + *pMinorVersion = kNuVersionMinor; + if (pBugVersion != nil) + *pBugVersion = kNuVersionBug; + if (ppBuildDate != nil) + *ppBuildDate = gNuBuildDate; + if (ppBuildFlags != nil) + *ppBuildFlags = gNuBuildFlags; + return kNuErrNone; +} + diff --git a/nufxlib/config.guess b/nufxlib/config.guess new file mode 100644 index 0000000..ad5281e --- /dev/null +++ b/nufxlib/config.guess @@ -0,0 +1,1466 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-08-03' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerppc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + x86:Interix*:[34]*) + echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' + exit ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && { + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit + } + test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + *86) UNAME_PROCESSOR=i686 ;; + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/nufxlib/config.h.in b/nufxlib/config.h.in new file mode 100644 index 0000000..ec6d300 --- /dev/null +++ b/nufxlib/config.h.in @@ -0,0 +1,146 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING-LIB. + */ +/* config.h.in. */ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define to empty if the keyword does not work. */ +#undef inline + +/* Define to `int' if doesn't define. */ +#undef mode_t + +/* Define to `long' if doesn't define. */ +#undef off_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your declares struct tm. */ +#undef TM_IN_SYS_TIME + +/* Define to `unsigned char' if doesn't define. */ +#undef uchar + +/* Define to `unsigned short' if doesn't define. */ +#undef ushort + +/* Define to `unsigned int' if doesn't define. */ +#undef uint + +/* Define to `unsigned long' if doesn't define. */ +#undef ulong + +/* Define to `int' if doesn't define. */ +#undef mode_t + +/* Define to `long' if doesn't define. */ +#undef off_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the fdopen function. */ +#undef HAVE_FDOPEN + +/* Define if you have the ftruncate function. */ +#undef HAVE_FTRUNCATE + +/* Define if you have the localtime_r function. */ +#undef HAVE_LOCALTIME_R + +/* Define if you have the memmove function. */ +#undef HAVE_MEMMOVE + +/* Define if you have the mkdir function. */ +#undef HAVE_MKDIR + +/* Define if you have the mkstemp function. */ +#undef HAVE_MKSTEMP + +/* Define if you have the mktime function. */ +#undef HAVE_MKTIME + +/* Define if you have the snprintf function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the strcasecmp function. */ +#undef HAVE_STRCASECMP + +/* Define if you have the strncasecmp function. */ +#undef HAVE_STRNCASECMP + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strtoul function. */ +#undef HAVE_STRTOUL + +/* Define if you have the timelocal function. */ +#undef HAVE_TIMELOCAL + +/* Define if you have the vsnprintf function. */ +#undef HAVE_VSNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_UTIME_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the header file. */ +#undef HAVE_UTIME_H + +/* Define if sprintf returns an int. */ +#undef SPRINTF_RETURNS_INT + +/* Define if SNPRINTF is declared in stdio.h. */ +#undef SNPRINTF_DECLARED + +/* Define if VSNPRINTF is declared in stdio.h. */ +#undef VSNPRINTF_DECLARED + +/* Define to include SQ (Huffman+RLE) compression. */ +#undef ENABLE_SQ + +/* Define to include LZW (ShrinkIt LZW/1 and LZW/2) compression. */ +#undef ENABLE_LZW + +/* Define to include LZC (12-bit and 16-bit UNIX "compress") compression. */ +#undef ENABLE_LZC + +/* Define to include deflate (zlib) compression (also need -l in Makefile). */ +#undef ENABLE_DEFLATE + +/* Define to include bzip2 (libbz2) compression (also need -l in Makefile). */ +#undef ENABLE_BZIP2 + +/* Define if we want to use the dmalloc library (also need -l in Makefile). */ +#undef USE_DMALLOC + diff --git a/nufxlib/config.sub b/nufxlib/config.sub new file mode 100644 index 0000000..1c366df --- /dev/null +++ b/nufxlib/config.sub @@ -0,0 +1,1579 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + +timestamp='2005-07-08' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ + kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | ms1 \ + | msp430 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m32c) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | ms1-* \ + | msp430-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + m32c-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/nufxlib/configure b/nufxlib/configure new file mode 100644 index 0000000..f029fde --- /dev/null +++ b/nufxlib/configure @@ -0,0 +1,5552 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="NufxLibPriv.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +SHARE_FLAGS +BUILD_FLAGS +EGREP +GREP +CPP +RANLIB +SET_MAKE +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_sq +enable_lzw +enable_lzc +enable_deflate +enable_bzip2 +enable_dmalloc +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-sq disable SQ compression + --disable-lzw disable LZW/1 and LZW/2 compression + --disable-lzc disable 12- and 16-bit LZC compression + --disable-deflate disable zlib deflate compression + --enable-bzip2 enable libbz2 bzip2 compression + --enable-dmalloc do dmalloc stuff + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_config_headers="$ac_config_headers config.h" + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in fcntl.h malloc.h stdlib.h sys/stat.h sys/time.h sys/types.h \ + sys/utime.h unistd.h utime.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +LIBS="" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 +$as_echo_n "checking for an ANSI C-conforming const... " >&6; } +if ${ac_cv_c_const+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +#ifndef __cplusplus + /* Ultrix mips cc rejects this sort of thing. */ + typedef int charset[2]; + const charset cs = { 0, 0 }; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_const=yes +else + ac_cv_c_const=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 +$as_echo "$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +$as_echo "#define const /**/" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" +if test "x$ac_cv_type_mode_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define mode_t int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" +if test "x$ac_cv_type_off_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define off_t long int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 +$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } +if ${ac_cv_struct_tm+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_struct_tm=time.h +else + ac_cv_struct_tm=sys/time.h +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 +$as_echo "$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h + +fi + +ac_fn_c_check_type "$LINENO" "uchar" "ac_cv_type_uchar" "$ac_includes_default" +if test "x$ac_cv_type_uchar" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define uchar unsigned char +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "ushort" "ac_cv_type_ushort" "$ac_includes_default" +if test "x$ac_cv_type_ushort" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ushort unsigned short +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "uint" "ac_cv_type_uint" "$ac_includes_default" +if test "x$ac_cv_type_uint" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define uint unsigned int +_ACEOF + +fi + +ac_fn_c_check_type "$LINENO" "ulong" "ac_cv_type_ulong" "$ac_includes_default" +if test "x$ac_cv_type_ulong" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define ulong unsigned long +_ACEOF + +fi + + +for ac_func in fdopen ftruncate memmove mkdir mkstemp mktime timelocal \ + localtime_r snprintf strcasecmp strncasecmp strtoul strerror vsnprintf +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if snprintf is declared" >&5 +$as_echo_n "checking if snprintf is declared... " >&6; } +if ${nufxlib_cv_snprintf_in_header+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "snprintf" >/dev/null 2>&1; then : + nufxlib_cv_snprintf_in_header=yes +else + nufxlib_cv_snprintf_in_header=no +fi +rm -f conftest* + + +fi + +if test $nufxlib_cv_snprintf_in_header = "yes"; then + $as_echo "#define SNPRINTF_DECLARED 1" >>confdefs.h + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $nufxlib_cv_snprintf_in_header" >&5 +$as_echo "$nufxlib_cv_snprintf_in_header" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if vsnprintf is declared" >&5 +$as_echo_n "checking if vsnprintf is declared... " >&6; } +if ${nufxlib_cv_vsnprintf_in_header+:} false; then : + $as_echo_n "(cached) " >&6 +else + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "vsnprintf" >/dev/null 2>&1; then : + nufxlib_cv_vsnprintf_in_header=yes +else + nufxlib_cv_vsnprintf_in_header=no +fi +rm -f conftest* + + +fi + +if test $nufxlib_cv_vsnprintf_in_header = "yes"; then + $as_echo "#define VSNPRINTF_DECLARED 1" >>confdefs.h + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $nufxlib_cv_vsnprintf_in_header" >&5 +$as_echo "$nufxlib_cv_vsnprintf_in_header" >&6; } + +if test -z "$GCC"; then + BUILD_FLAGS='$(OPT)' +else + BUILD_FLAGS='$(OPT) $(GCC_FLAGS)' +fi + + +SHARE_FLAGS='-shared' +if test "$host_cpu" = "powerpc" -a "$host_os" = "beos"; then + CC=cc + GCC= + CFLAGS='-proc 603 -opt full' + SHARE_FLAGS='-shared -nostdlib' + echo "forcing CC to \"$CC\" and CFLAGS to \"$CFLAGS\"" +elif test "$host_os" = "beos"; then + SHARE_FLAGS='-nostartfiles -Xlinker -soname="$@"' +fi + +if test "$host_vendor" = "apple" -a ${host_os:0:6} = "darwin"; then + echo "checking for Mac OS X... yes, adding -framework Carbon" + LIBS="$LIBS -framework Carbon" +fi + + + + +if test "$host_os" = "beos"; then + if test "$prefix" = "NONE" -a \ + "$includedir" = '${prefix}/include' -a \ + "$libdir" = '${exec_prefix}/lib' -a \ + "$bindir" = '${exec_prefix}/bin' -a \ + "$mandir" = '${prefix}/man' + then + echo replacing install locations with BeOS values + prefix=/boot + includedir='${prefix}/develop/headers' + libdir='${exec_prefix}/home/config/lib' + bindir='${exec_prefix}/home/config/bin' + mandir='/tmp' + + + + + + fi +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if sprintf returns int" >&5 +$as_echo_n "checking if sprintf returns int... " >&6; } +if ${nufxlib_cv_sprintf_returns_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + + if test "$cross_compiling" = yes; then : + nufxlib_cv_sprintf_returns_int=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + int main(void) + { + int count; + char buf[8]; + count = sprintf(buf, "123"); /* should return three */ + exit(count != 3); + } + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + nufxlib_cv_sprintf_returns_int=yes +else + nufxlib_cv_sprintf_returns_int=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + +fi + + +if test $nufxlib_cv_sprintf_returns_int = "yes"; then + $as_echo "#define SPRINTF_RETURNS_INT 1" >>confdefs.h + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $nufxlib_cv_sprintf_returns_int" >&5 +$as_echo "$nufxlib_cv_sprintf_returns_int" >&6; } + + +# Check whether --enable-sq was given. +if test "${enable_sq+set}" = set; then : + enableval=$enable_sq; +else + enable_sq=yes +fi + +if test $enable_sq = "yes"; then + $as_echo "#define ENABLE_SQ 1" >>confdefs.h + +fi + +# Check whether --enable-lzw was given. +if test "${enable_lzw+set}" = set; then : + enableval=$enable_lzw; +else + enable_lzw=yes +fi + +if test $enable_lzw = "yes"; then + $as_echo "#define ENABLE_LZW 1" >>confdefs.h + +fi + +# Check whether --enable-lzc was given. +if test "${enable_lzc+set}" = set; then : + enableval=$enable_lzc; +else + enable_lzc=yes +fi + +if test $enable_lzc = "yes"; then + $as_echo "#define ENABLE_LZC 1" >>confdefs.h + +fi + +# Check whether --enable-deflate was given. +if test "${enable_deflate+set}" = set; then : + enableval=$enable_deflate; +else + enable_deflate=yes +fi + +if test $enable_deflate = "yes"; then + got_zlibh=false + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 +$as_echo_n "checking for deflate in -lz... " >&6; } +if ${ac_cv_lib_z_deflate+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char deflate (); +int +main () +{ +return deflate (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_z_deflate=yes +else + ac_cv_lib_z_deflate=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 +$as_echo "$ac_cv_lib_z_deflate" >&6; } +if test "x$ac_cv_lib_z_deflate" = xyes; then : + got_libz=true +else + got_libz=false +fi + + if $got_libz; then + ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" +if test "x$ac_cv_header_zlib_h" = xyes; then : + got_zlibh=true LIBS="$LIBS -lz" +fi + + + fi + if $got_zlibh; then + echo " (found libz and zlib.h, enabling deflate)" + $as_echo "#define ENABLE_DEFLATE 1" >>confdefs.h + + else + echo " (couldn't find libz and zlib.h, not enabling deflate)" + fi +fi + +# Check whether --enable-bzip2 was given. +if test "${enable_bzip2+set}" = set; then : + enableval=$enable_bzip2; +else + enable_bzip2=no +fi + +if test $enable_bzip2 = "yes"; then + got_bzlibh=false + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZ2_bzCompress in -lbz2" >&5 +$as_echo_n "checking for BZ2_bzCompress in -lbz2... " >&6; } +if ${ac_cv_lib_bz2_BZ2_bzCompress+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbz2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char BZ2_bzCompress (); +int +main () +{ +return BZ2_bzCompress (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bz2_BZ2_bzCompress=yes +else + ac_cv_lib_bz2_BZ2_bzCompress=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzCompress" >&5 +$as_echo "$ac_cv_lib_bz2_BZ2_bzCompress" >&6; } +if test "x$ac_cv_lib_bz2_BZ2_bzCompress" = xyes; then : + got_libbz=true +else + got_libbz=false +fi + + if $got_libbz; then + ac_fn_c_check_header_mongrel "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" +if test "x$ac_cv_header_bzlib_h" = xyes; then : + got_bzlibh=true LIBS="$LIBS -lbz2" +fi + + + fi + if $got_bzlibh; then + echo " (found libbz2 and bzlib.h, enabling bzip2)" + $as_echo "#define ENABLE_BZIP2 1" >>confdefs.h + + else + echo " (couldn't find libbz2 and bzlib.h, not enabling bzip2)" + fi +fi + + +# Check whether --enable-dmalloc was given. +if test "${enable_dmalloc+set}" = set; then : + enableval=$enable_dmalloc; echo "--- enabling dmalloc"; + LIBS="$LIBS -L/usr/local/lib -ldmalloc"; $as_echo "#define USE_DMALLOC 1" >>confdefs.h + +fi + + +ac_config_files="$ac_config_files Makefile samples/Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "samples/Makefile") CONFIG_FILES="$CONFIG_FILES samples/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/nufxlib/configure.in b/nufxlib/configure.in new file mode 100644 index 0000000..2a0cb03 --- /dev/null +++ b/nufxlib/configure.in @@ -0,0 +1,228 @@ +dnl NuFX archive manipulation library +dnl Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. +dnl This is free software; you can redistribute it and/or modify it under the +dnl terms of the BSD License, see the file COPYING-LIB. +dnl +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(NufxLibPriv.h) +AC_CONFIG_HEADER(config.h) + +dnl Checks for programs. +AC_CANONICAL_HOST +dnl AC_PROG_AWK +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_MAKE_SET +AC_PROG_RANLIB + +dnl Checks for header files. +AC_CHECK_HEADERS(fcntl.h malloc.h stdlib.h sys/stat.h sys/time.h sys/types.h \ + sys/utime.h unistd.h utime.h) + +LIBS="" + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_MODE_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_STRUCT_TM +AC_CHECK_TYPE(uchar, unsigned char) +AC_CHECK_TYPE(ushort, unsigned short) +AC_CHECK_TYPE(uint, unsigned int) +AC_CHECK_TYPE(ulong, unsigned long) + +dnl Checks for library functions. +AC_CHECK_FUNCS(fdopen ftruncate memmove mkdir mkstemp mktime timelocal \ + localtime_r snprintf strcasecmp strncasecmp strtoul strerror vsnprintf) + +dnl Kent says: snprintf doesn't always have a declaration +AC_MSG_CHECKING(if snprintf is declared) +AC_CACHE_VAL(nufxlib_cv_snprintf_in_header, [ + AC_EGREP_HEADER(snprintf, stdio.h, + [nufxlib_cv_snprintf_in_header=yes], + [nufxlib_cv_snprintf_in_header=no]) +]) +if test $nufxlib_cv_snprintf_in_header = "yes"; then + AC_DEFINE(SNPRINTF_DECLARED) +fi +AC_MSG_RESULT($nufxlib_cv_snprintf_in_header) + +dnl Kent says: vsnprintf doesn't always have a declaration +AC_MSG_CHECKING(if vsnprintf is declared) +AC_CACHE_VAL(nufxlib_cv_vsnprintf_in_header, [ + AC_EGREP_HEADER(vsnprintf, stdio.h, + [nufxlib_cv_vsnprintf_in_header=yes], + [nufxlib_cv_vsnprintf_in_header=no]) +]) +if test $nufxlib_cv_vsnprintf_in_header = "yes"; then + AC_DEFINE(VSNPRINTF_DECLARED) +fi +AC_MSG_RESULT($nufxlib_cv_vsnprintf_in_header) + +dnl if we're using gcc, include gcc-specific warning flags +if test -z "$GCC"; then + BUILD_FLAGS='$(OPT)' +else + BUILD_FLAGS='$(OPT) $(GCC_FLAGS)' +fi + +dnl --------------------------------------------------------------------------- +dnl Some host-specific stuff. Variables you can test (set by the +dnl AC_CANONICAL_HOST call earlier) look like this: +dnl +dnl $host = i686-pc-linux-gnu +dnl $host_cpu = i686 +dnl $host_vendor = pc +dnl $host_os = linux-gnu + +dnl Figure out what the build and link flags should be; different for BeOS. +dnl (Should really use the auto-shared-lib stuff, but that adds a whole +dnl bunch of stuff.) +SHARE_FLAGS='-shared' +if test "$host_cpu" = "powerpc" -a "$host_os" = "beos"; then + dnl BeOS/PPC, with Metrowerks compiler + CC=cc + GCC= + CFLAGS='-proc 603 -opt full' + SHARE_FLAGS='-shared -nostdlib' + echo "forcing CC to \"$CC\" and CFLAGS to \"$CFLAGS\"" +elif test "$host_os" = "beos"; then + dnl BeOS/x86 + SHARE_FLAGS='-nostartfiles -Xlinker -soname="$@"' +fi + +dnl Mac OS X (powerpc-apple-darwin6.6) needs an extra flag. +if test "$host_vendor" = "apple" -a ${host_os:0:6} = "darwin"; then + echo "checking for Mac OS X... yes, adding -framework Carbon" + LIBS="$LIBS -framework Carbon" +fi + +AC_SUBST(BUILD_FLAGS) +AC_SUBST(SHARE_FLAGS) + +dnl BeOS doesn't like /usr/local/include, and gets feisty about it. If libdir +dnl and includedir are set to defaults, replace them with BeOS values. This +dnl might be going a little too far... +if test "$host_os" = "beos"; then + if test "$prefix" = "NONE" -a \ + "$includedir" = '${prefix}/include' -a \ + "$libdir" = '${exec_prefix}/lib' -a \ + "$bindir" = '${exec_prefix}/bin' -a \ + "$mandir" = '${prefix}/man' + then + echo replacing install locations with BeOS values + prefix=/boot + includedir='${prefix}/develop/headers' + libdir='${exec_prefix}/home/config/lib' + bindir='${exec_prefix}/home/config/bin' + mandir='/tmp' + AC_SUBST(prefix) + AC_SUBST(includedir) + AC_SUBST(libdir) + AC_SUBST(bindir) + AC_SUBST(mandir) + fi +fi + + + +dnl Test to see if sprintf does something reasonable. I'm going to assume +dnl that vsprintf and (if available) vsnprintf return the same thing. +AC_MSG_CHECKING(if sprintf returns int) +AC_CACHE_VAL(nufxlib_cv_sprintf_returns_int, [ + AC_TRY_RUN([ + #include + int main(void) + { + int count; + char buf[8]; + count = sprintf(buf, "123"); /* should return three */ + exit(count != 3); + } + ], + [nufxlib_cv_sprintf_returns_int=yes], [nufxlib_cv_sprintf_returns_int=no], + [nufxlib_cv_sprintf_returns_int=no]) +]) + +if test $nufxlib_cv_sprintf_returns_int = "yes"; then + AC_DEFINE(SPRINTF_RETURNS_INT) +fi +AC_MSG_RESULT($nufxlib_cv_sprintf_returns_int) + +dnl +dnl Allow selective disabling of compression algorithms. By default, +dnl all are enabled. We do a little extra work for libz and libbz2 +dnl because they're not built in. +dnl +dnl If we're creating a shared library, we need to explicitly link +dnl against libz and/or libbz2 when those features are enabled. +dnl + +AC_ARG_ENABLE(sq, + [ --disable-sq disable SQ compression], + [ ], [ enable_sq=yes ]) +if test $enable_sq = "yes"; then + AC_DEFINE(ENABLE_SQ) +fi + +AC_ARG_ENABLE(lzw, + [ --disable-lzw disable LZW/1 and LZW/2 compression], + [ ], [ enable_lzw=yes ]) +if test $enable_lzw = "yes"; then + AC_DEFINE(ENABLE_LZW) +fi + +AC_ARG_ENABLE(lzc, + [ --disable-lzc disable 12- and 16-bit LZC compression], + [ ], [ enable_lzc=yes ]) +if test $enable_lzc = "yes"; then + AC_DEFINE(ENABLE_LZC) +fi + +AC_ARG_ENABLE(deflate, + [ --disable-deflate disable zlib deflate compression], + [ ], [ enable_deflate=yes ]) +if test $enable_deflate = "yes"; then + dnl Check for zlib. Make sure it comes with zlib.h. + got_zlibh=false + AC_CHECK_LIB(z, deflate, got_libz=true, got_libz=false) + if $got_libz; then + AC_CHECK_HEADER(zlib.h, got_zlibh=true LIBS="$LIBS -lz") + fi + if $got_zlibh; then + echo " (found libz and zlib.h, enabling deflate)" + AC_DEFINE(ENABLE_DEFLATE) + else + echo " (couldn't find libz and zlib.h, not enabling deflate)" + fi +fi + +AC_ARG_ENABLE(bzip2, + [ --enable-bzip2 enable libbz2 bzip2 compression], + [ ], [ enable_bzip2=no ]) +if test $enable_bzip2 = "yes"; then + dnl Check for libbz2. Make sure it comes with bzlib.h. + dnl AC_CHECK_LIB(bz2, BZ2_bzCompress, + dnl AC_CHECK_HEADER(bzlib.h, AC_DEFINE(ENABLE_BZIP2) LIBS="$LIBS -lbz2")) + got_bzlibh=false + AC_CHECK_LIB(bz2, BZ2_bzCompress, got_libbz=true, got_libbz=false) + if $got_libbz; then + AC_CHECK_HEADER(bzlib.h, got_bzlibh=true LIBS="$LIBS -lbz2") + fi + if $got_bzlibh; then + echo " (found libbz2 and bzlib.h, enabling bzip2)" + AC_DEFINE(ENABLE_BZIP2) + else + echo " (couldn't find libbz2 and bzlib.h, not enabling bzip2)" + fi +fi + + +AC_ARG_ENABLE(dmalloc, [ --enable-dmalloc do dmalloc stuff], + [ echo "--- enabling dmalloc"; + LIBS="$LIBS -L/usr/local/lib -ldmalloc"; AC_DEFINE(USE_DMALLOC) ]) + +AC_OUTPUT(Makefile samples/Makefile) diff --git a/nufxlib/install-sh b/nufxlib/install-sh new file mode 100644 index 0000000..e9de238 --- /dev/null +++ b/nufxlib/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/nufxlib/mkinstalldirs b/nufxlib/mkinstalldirs new file mode 100644 index 0000000..936cf34 --- /dev/null +++ b/nufxlib/mkinstalldirs @@ -0,0 +1,34 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Last modified: 1995-03-05 +# Public domain + +errstatus=0 + +for file in ${1+"$@"} ; do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d in ${1+"$@"} ; do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$? + fi + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus diff --git a/nufxlib/nufxlib.def b/nufxlib/nufxlib.def new file mode 100644 index 0000000..0fc2673 --- /dev/null +++ b/nufxlib/nufxlib.def @@ -0,0 +1,67 @@ +; NufxLib library exported symbols +; +; This is redundant with the __declspec(dllexport) declarations enabled +; with the NUFXLIB_EXPORTS symbol. If we're just building a DLL, there's +; no need for this file. However, the objects we're building are used for +; both the static library and the dynamic library, and we don't want to +; have exports in the static library. If we do, the linker will create +; .lib and .exp files for every executable we link against it. This is +; mostly harmless, but a tad messy. I don't expect the interface to change, +; so there's not much of a maintenance burden here. +; +EXPORTS + NuAbort + NuAddFile + NuAddRecord + NuAddThread + NuClose + NuContents + NuCreateDataSinkForBuffer + NuCreateDataSinkForFP + NuCreateDataSinkForFile + NuCreateDataSourceForBuffer + NuCreateDataSourceForFP + NuCreateDataSourceForFile + NuDataSinkGetOutCount + NuDataSourceSetRawCrc + NuDebugDumpArchive + NuDelete + NuDeleteRecord + NuDeleteThread + NuExtract + NuExtractRecord + NuExtractThread + NuFlush + NuFreeDataSink + NuFreeDataSource + NuGetAttr + NuGetExtraData + NuGetMasterHeader + NuGetRecord + NuGetRecordIdxByName + NuGetRecordIdxByPosition + NuGetValue + NuGetVersion + NuIsPresizedThreadID + NuOpenRO + NuOpenRW + NuRecordCopyAttr + NuRecordCopyThreads + NuRecordGetNumThreads + NuRename + NuSetErrorHandler + NuSetErrorMessageHandler + NuSetExtraData + NuSetGlobalErrorMessageHandler + NuSetOutputPathnameFilter + NuSetProgressUpdater + NuSetRecordAttr + NuSetSelectionFilter + NuSetValue + NuStrError + NuStreamOpenRO + NuTest + NuTestFeature + NuTestRecord + NuThreadGetByIdx + NuUpdatePresizedThread diff --git a/nufxlib/nufxlib.vcxproj b/nufxlib/nufxlib.vcxproj new file mode 100644 index 0000000..d6ac04c --- /dev/null +++ b/nufxlib/nufxlib.vcxproj @@ -0,0 +1,116 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF} + Win32Proj + nufxlib + + + + DynamicLibrary + true + v120 + Unicode + + + DynamicLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;NUFXLIB_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;NUFXLIB_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {b66109f4-217b-43c0-86aa-eb55657e5ac0} + + + + + + \ No newline at end of file diff --git a/nufxlib/nufxlib.vcxproj.filters b/nufxlib/nufxlib.vcxproj.filters new file mode 100644 index 0000000..3408ddb --- /dev/null +++ b/nufxlib/nufxlib.vcxproj.filters @@ -0,0 +1,99 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/nufxlib/samples/Common.h b/nufxlib/samples/Common.h new file mode 100644 index 0000000..0f377b8 --- /dev/null +++ b/nufxlib/samples/Common.h @@ -0,0 +1,68 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * Common functions for NuLib tests. + */ +#ifndef __Common__ +#define __Common__ + +#include "SysDefs.h" /* might as well draft off the autoconf */ +#include "NufxLib.h" + +#ifdef USE_DMALLOC +# include "dmalloc.h" +#endif + +#define nil NULL /* this is seriously habit-forming */ + +#define NELEM(x) (sizeof(x) / sizeof((x)[0])) + +#ifndef __cplusplus + #define false 0 + #define true (!false) +#endif + + +#ifdef FOPEN_WANTS_B +# define kNuFileOpenReadOnly "rb" +# define kNuFileOpenReadWrite "r+b" +# define kNuFileOpenWriteTrunc "wb" +# define kNuFileOpenReadWriteCreat "w+b" +#else +# define kNuFileOpenReadOnly "r" +# define kNuFileOpenReadWrite "r+" +# define kNuFileOpenWriteTrunc "w" +# define kNuFileOpenReadWriteCreat "w+" +#endif + + +/* + * Figure out what path separator to use. + * + * NOTE: recent versions of Win32 will also accept '/'. + */ + +#ifdef MSDOS +# define PATH_SEP '\\' +#endif + +#ifdef WIN32 +# define PATH_SEP '\\' +#endif + +#ifdef MACOS +# define PATH_SEP ':' +#endif + +#if defined(APW) || defined(__ORCAC__) +# define PATH_SEP ':' +#endif + +#ifndef PATH_SEP +# define PATH_SEP '/' +#endif + +#endif /*__Common__*/ diff --git a/nufxlib/samples/Exerciser.c b/nufxlib/samples/Exerciser.c new file mode 100644 index 0000000..e3be72d --- /dev/null +++ b/nufxlib/samples/Exerciser.c @@ -0,0 +1,1383 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * NufxLib exerciser. Most library functions can be invoked directly from + * the exerciser command line. + * + * This was written in C++ to evaluate the interaction between NufxLib and + * the C++ language, i.e. to make sure that all type definitions and + * function calls can be used without giving the compiler fits. This + * file will compile as either "Exerciser.c" or "Exerciser.cpp". + */ +#include "NufxLib.h" +#include "Common.h" +#include + +/* not portable to other OSs, but not all that important anyway */ +static const char kFssep = PATH_SEP; + +/* ProDOS access permissions */ +#define kUnlocked 0xe3 + +#define kTempFile "exer-temp" + + +/* + * =========================================================================== + * ExerciserState object + * =========================================================================== + */ + +/* + * Exerciser state. + * + * In case it isn't immediately apparent, this was written in C++ and + * then converted back to C. + */ +typedef struct ExerciserState { + NuArchive* pArchive; + char* archivePath; + const char* archiveFile; +} ExerciserState; + + +ExerciserState* +ExerciserState_New(void) +{ + ExerciserState* pExerState; + + pExerState = (ExerciserState*) malloc(sizeof(*pExerState)); + if (pExerState == nil) + return nil; + + pExerState->pArchive = nil; + pExerState->archivePath = nil; + pExerState->archiveFile = nil; + + return pExerState; +} + +void +ExerciserState_Free(ExerciserState* pExerState) +{ + if (pExerState == nil) + return; + + if (pExerState->pArchive != nil) { + printf("Exerciser: aborting open archive\n"); + (void) NuAbort(pExerState->pArchive); + (void) NuClose(pExerState->pArchive); + } + if (pExerState->archivePath != nil) + free(pExerState->archivePath); + + free(pExerState); +} + +inline NuArchive* +ExerciserState_GetNuArchive(const ExerciserState* pExerState) +{ + return pExerState->pArchive; +} + +inline void +ExerciserState_SetNuArchive(ExerciserState* pExerState, NuArchive* newArchive) +{ + pExerState->pArchive = newArchive; +} + +inline char* +ExerciserState_GetArchivePath(const ExerciserState* pExerState) +{ + return pExerState->archivePath; +} + +inline void +ExerciserState_SetArchivePath(ExerciserState* pExerState, char* newPath) +{ + if (pExerState->archivePath != nil) + free(pExerState->archivePath); + + if (newPath == nil) { + pExerState->archivePath = nil; + pExerState->archiveFile = nil; + } else { + pExerState->archivePath = strdup(newPath); + pExerState->archiveFile = strrchr(newPath, kFssep); + if (pExerState->archiveFile != nil) + pExerState->archiveFile++; + + if (pExerState->archiveFile == nil || *pExerState->archiveFile == '\0') + pExerState->archiveFile = pExerState->archivePath; + } +} + +inline const char* +ExerciserState_GetArchiveFile(const ExerciserState* pExerState) +{ + if (pExerState->archiveFile == nil) + return "[no archive open]"; + else + return pExerState->archiveFile; +} + + +/* + * =========================================================================== + * Utility functions + * =========================================================================== + */ + +/* + * NuContents callback function. Print the contents of an individual record. + */ +NuResult +PrintEntry(NuArchive* pArchive, void* vpRecord) +{ + const NuRecord* pRecord = (const NuRecord*) vpRecord; + int idx; + + (void)pArchive; /* shut up, gcc */ + + printf("RecordIdx %ld: '%s'\n", + pRecord->recordIdx, pRecord->filename); + + for (idx = 0; idx < (int) pRecord->recTotalThreads; idx++) { + const NuThread* pThread; + NuThreadID threadID; + const char* threadLabel; + + pThread = NuGetThread(pRecord, idx); + assert(pThread != nil); + + threadID = NuGetThreadID(pThread); + switch (NuThreadIDGetClass(threadID)) { + case kNuThreadClassMessage: + threadLabel = "message class"; + break; + case kNuThreadClassControl: + threadLabel = "control class"; + break; + case kNuThreadClassData: + threadLabel = "data class"; + break; + case kNuThreadClassFilename: + threadLabel = "filename class"; + break; + default: + threadLabel = "(unknown class)"; + break; + } + + switch (threadID) { + case kNuThreadIDComment: + threadLabel = "comment"; + break; + case kNuThreadIDIcon: + threadLabel = "icon"; + break; + case kNuThreadIDMkdir: + threadLabel = "mkdir"; + break; + case kNuThreadIDDataFork: + threadLabel = "data fork"; + break; + case kNuThreadIDDiskImage: + threadLabel = "disk image"; + break; + case kNuThreadIDRsrcFork: + threadLabel = "rsrc fork"; + break; + case kNuThreadIDFilename: + threadLabel = "filename"; + break; + default: + break; + } + + printf(" ThreadIdx %ld - 0x%08lx (%s)\n", pThread->threadIdx, + threadID, threadLabel); + } + + return kNuOK; +} + + +#define kNiceLineLen 256 + +/* + * Get a line of input, stripping the '\n' off the end. + */ +static NuError +GetLine(const char* prompt, char* buffer, int bufferSize) +{ + printf("%s> ", prompt); + fflush(stdout); + + if (fgets(buffer, bufferSize, stdin) == nil) + return kNuErrGeneric; + + if (buffer[strlen(buffer)-1] == '\n') + buffer[strlen(buffer)-1] = '\0'; + + return kNuErrNone; +} + + +/* + * Selection filter for mass "extract" and "delete" operations. + */ +NuResult +SelectionFilter(NuArchive* pArchive, void* vselFilt) +{ + const NuSelectionProposal* selProposal = (NuSelectionProposal*) vselFilt; + char buffer[8]; + + printf("%s (N/y)? ", selProposal->pRecord->filename); + fflush(stdout); + + if (fgets(buffer, sizeof(buffer), stdin) == nil) + return kNuAbort; + + if (tolower(buffer[0]) == 'y') + return kNuOK; + else + return kNuSkip; +} + + +/* + * General-purpose error handler. + */ +NuResult +ErrorHandler(NuArchive* pArchive, void* vErrorStatus) +{ + const NuErrorStatus* pErrorStatus = (const NuErrorStatus*) vErrorStatus; + char buffer[8]; + NuResult result = kNuSkip; + + printf("Exerciser: error handler op=%d err=%d sysErr=%d message='%s'\n" + "\tfilename='%s' '%c'(0x%02x)\n", + pErrorStatus->operation, pErrorStatus->err, pErrorStatus->sysErr, + pErrorStatus->message == nil ? "(nil)" : pErrorStatus->message, + pErrorStatus->pathname, pErrorStatus->filenameSeparator, + pErrorStatus->filenameSeparator); + printf("\tValid options are:"); + if (pErrorStatus->canAbort) + printf(" a)bort"); + if (pErrorStatus->canRetry) + printf(" r)etry"); + if (pErrorStatus->canIgnore) + printf(" i)gnore"); + if (pErrorStatus->canSkip) + printf(" s)kip"); + if (pErrorStatus->canRename) + printf(" re)name"); + if (pErrorStatus->canOverwrite) + printf(" o)verwrite"); + putc('\n', stdout); + + printf("Return what (a/r/i/s/e/o)? "); + fflush(stdout); + + if (fgets(buffer, sizeof(buffer), stdin) == nil) { + printf("Returning kNuSkip\n"); + } else switch (buffer[0]) { + case 'a': result = kNuAbort; break; + case 'r': result = kNuRetry; break; + case 'i': result = kNuIgnore; break; + case 's': result = kNuSkip; break; + case 'e': result = kNuRename; break; + case 'o': result = kNuOverwrite; break; + default: + printf("Unknown value '%c', returning kNuSkip\n", buffer[0]); + break; + } + + return result; +} + +/* + * This gets called when a buffer DataSource is no longer needed. + */ +NuResult +FreeCallback(NuArchive* pArchive, void* args) +{ + free(args); + return kNuOK; +} + + +/* + * =========================================================================== + * Command handlers + * =========================================================================== + */ + +typedef NuError (*CommandFunc)(ExerciserState* pState, int argc, + char** argv); + +static NuError HelpFunc(ExerciserState* pState, int argc, char** argv); + +#if 0 +static NuError +GenericFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + printf("Generic! argc=%d\n", argc); + return kNuErrNone; +} +#endif + +/* + * Do nothing. Useful when the user just hits on a blank line. + */ +static NuError +NothingFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + return kNuErrNone; +} + +/* + * q - quit + * + * Do nothing. This is used as a trigger for quitting the program. In + * practice, we catch this earlier, and won't actually call here. + */ +static NuError +QuitFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(0); + return kNuErrNone; +} + + + +/* + * ab - abort current changes + */ +static NuError +AbortFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + return NuAbort(ExerciserState_GetNuArchive(pState)); +} + +/* + * af - add file to archive + */ +static NuError +AddFileFunc(ExerciserState* pState, int argc, char** argv) +{ + NuFileDetails nuFileDetails; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + memset(&nuFileDetails, 0, sizeof(nuFileDetails)); + nuFileDetails.threadID = kNuThreadIDDataFork; + nuFileDetails.storageName = argv[1]; + nuFileDetails.fileSysID = kNuFileSysUnknown; + nuFileDetails.fileSysInfo = (short) kFssep; + nuFileDetails.access = kUnlocked; + /* fileType, extraType, storageType, dates */ + + return NuAddFile(ExerciserState_GetNuArchive(pState), argv[1], + &nuFileDetails, false, nil); +} + +/* + * ar - add an empty record + */ +static NuError +AddRecordFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuRecordIdx recordIdx; + NuFileDetails nuFileDetails; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + memset(&nuFileDetails, 0, sizeof(nuFileDetails)); + nuFileDetails.threadID = 0; /* irrelevant */ + nuFileDetails.storageName = argv[1]; + nuFileDetails.fileSysID = kNuFileSysUnknown; + nuFileDetails.fileSysInfo = (short) kFssep; + nuFileDetails.access = kUnlocked; + /* fileType, extraType, storageType, dates */ + + err = NuAddRecord(ExerciserState_GetNuArchive(pState), + &nuFileDetails, &recordIdx); + if (err == kNuErrNone) + printf("Exerciser: success, new recordIdx=%ld\n", recordIdx); + return err; +} + +/* + * at - add thread to record + */ +static NuError +AddThreadFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuDataSource* pDataSource = nil; + char* lineBuf = nil; + long ourLen, maxLen; + NuThreadID threadID; + NuThreadIdx threadIdx; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 3); + + lineBuf = (char*)malloc(kNiceLineLen); + assert(lineBuf != nil); + + threadID = strtol(argv[2], nil, 0); + if (NuThreadIDGetClass(threadID) == kNuThreadClassData) { + /* load data from a file on disk */ + maxLen = 0; + err = GetLine("Enter filename", lineBuf, kNiceLineLen); + if (err != kNuErrNone) + goto bail; + if (!lineBuf[0]) { + fprintf(stderr, "Invalid filename\n"); + err = kNuErrInvalidArg; + goto bail; + } + + err = NuCreateDataSourceForFile(kNuThreadFormatUncompressed, + 0, lineBuf, false, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "Exerciser: file data source create failed (err=%d)\n", err); + goto bail; + } + } else { + if (threadID == kNuThreadIDFilename || threadID == kNuThreadIDComment) { + /* select the buffer pre-size */ + err = GetLine("Enter max buffer size", lineBuf, kNiceLineLen); + if (err != kNuErrNone) + goto bail; + maxLen = strtol(lineBuf, nil, 0); + if (maxLen <= 0) { + fprintf(stderr, "Bad length\n"); + err = kNuErrInvalidArg; + goto bail; + } + } else { + maxLen = 0; + } + + err = GetLine("Enter the thread contents", lineBuf, kNiceLineLen); + if (err != kNuErrNone) + goto bail; + ourLen = strlen(lineBuf); + + /* create a data source from the buffer */ + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + maxLen, (unsigned char*)lineBuf, 0, ourLen, FreeCallback, + &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "Exerciser: buffer data source create failed (err=%d)\n", err); + goto bail; + } + lineBuf = nil; /* now owned by the library */ + } + + + err = NuAddThread(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), threadID, pDataSource, &threadIdx); + if (err == kNuErrNone) { + pDataSource = nil; /* library owns it now */ + printf("Exerciser: success; function returned threadIdx=%ld\n", + threadIdx); + } + +bail: + NuFreeDataSource(pDataSource); + if (lineBuf != nil) + free(lineBuf); + return err; +} + +/* + * cl - close archive + */ +static NuError +CloseFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + err = NuClose(ExerciserState_GetNuArchive(pState)); + if (err == kNuErrNone) { + ExerciserState_SetNuArchive(pState, nil); + ExerciserState_SetArchivePath(pState, nil); + } + + return err; +} + +/* + * d - delete all records (selection-filtered) + */ +static NuError +DeleteFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + NuSetSelectionFilter(ExerciserState_GetNuArchive(pState), SelectionFilter); + + return NuDelete(ExerciserState_GetNuArchive(pState)); +} + +/* + * dr - delete record + */ +static NuError +DeleteRecordFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + return NuDeleteRecord(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0)); +} + +/* + * dt - delete thread + */ +static NuError +DeleteThreadFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + return NuDeleteThread(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0)); +} + +/* + * e - extract all files (selection-filtered) + */ +static NuError +ExtractFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + NuSetSelectionFilter(ExerciserState_GetNuArchive(pState), SelectionFilter); + + return NuExtract(ExerciserState_GetNuArchive(pState)); +} + +/* + * er - extract record + */ +static NuError +ExtractRecordFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + return NuExtractRecord(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0)); +} + +/* + * et - extract thread + */ +static NuError +ExtractThreadFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuDataSink* pDataSink = nil; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 3); + + err = NuCreateDataSinkForFile(true, kNuConvertOff, argv[2], kFssep, + &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "Exerciser: data sink create failed\n"); + goto bail; + } + + err = NuExtractThread(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), pDataSink); + /* fall through with err */ + +bail: + NuFreeDataSink(pDataSink); + return err; +} + +/* + * fl - flush changes to archive + */ +static NuError +FlushFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + long flushStatus; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + err = NuFlush(ExerciserState_GetNuArchive(pState), &flushStatus); + if (err != kNuErrNone) + printf("Exerciser: flush failed, status flags=0x%04lx\n", flushStatus); + return err; +} + +/* + * gev - get value + * + * Currently takes numeric arguments. We could be nice and accept the + * things like "IgnoreCRC" for kNuValueIgnoreCRC, but not yet. + */ +static NuError +GetValueFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuValue value; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + err = NuGetValue(ExerciserState_GetNuArchive(pState), + (NuValueID) strtol(argv[1], nil, 0), &value); + if (err == kNuErrNone) + printf(" --> %ld\n", value); + return err; +} + +/* + * gmh - get master header + */ +static NuError +GetMasterHeaderFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + const NuMasterHeader* pMasterHeader; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + err = NuGetMasterHeader(ExerciserState_GetNuArchive(pState), + &pMasterHeader); + if (err == kNuErrNone) { + printf("Exerciser: success (version=%u, totalRecords=%lu, EOF=%lu)\n", + pMasterHeader->mhMasterVersion, pMasterHeader->mhTotalRecords, + pMasterHeader->mhMasterEOF); + } + return err; +} + +/* + * gr - get record attributes + */ +static NuError +GetRecordFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + const NuRecord* pRecord; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + err = NuGetRecord(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), &pRecord); + if (err == kNuErrNone) { + printf("Exerciser: success, call returned:\n"); + printf("\tfileSysID : %d\n", pRecord->recFileSysID); + printf("\tfileSysInfo : 0x%04x ('%c')\n", pRecord->recFileSysInfo, + NuGetSepFromSysInfo(pRecord->recFileSysInfo)); + printf("\taccess : 0x%02lx\n", pRecord->recAccess); + printf("\tfileType : 0x%04lx\n", pRecord->recFileType); + printf("\textraType : 0x%04lx\n", pRecord->recExtraType); + printf("\tcreateWhen : ...\n"); + printf("\tmodWhen : ...\n"); /* too lazy */ + printf("\tarchiveWhen : ...\n"); + } + return err; +} + +/* + * grin - get record idx by name + */ +static NuError +GetRecordIdxByNameFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuRecordIdx recIdx; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + err = NuGetRecordIdxByName(ExerciserState_GetNuArchive(pState), + argv[1], &recIdx); + if (err == kNuErrNone) + printf("Exerciser: success, returned recordIdx=%ld\n", recIdx); + return err; +} + +/* + * grip - get record idx by position + */ +static NuError +GetRecordIdxByPositionFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuRecordIdx recIdx; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + err = NuGetRecordIdxByPosition(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), &recIdx); + if (err == kNuErrNone) + printf("Exerciser: success, returned recordIdx=%ld\n", recIdx); + return err; +} + +/* + * ocrw - open/create read-write + */ +static NuError +OpenCreateReadWriteFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuArchive* pArchive; + + assert(ExerciserState_GetNuArchive(pState) == nil); + assert(argc == 2); + + err = NuOpenRW(argv[1], kTempFile, kNuOpenCreat|kNuOpenExcl, &pArchive); + if (err == kNuErrNone) { + ExerciserState_SetNuArchive(pState, pArchive); + ExerciserState_SetArchivePath(pState, argv[1]); + } + + return err; +} + +/* + * oro - open read-only + */ +static NuError +OpenReadOnlyFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuArchive* pArchive; + + assert(ExerciserState_GetNuArchive(pState) == nil); + assert(argc == 2); + + err = NuOpenRO(argv[1], &pArchive); + if (err == kNuErrNone) { + ExerciserState_SetNuArchive(pState, pArchive); + ExerciserState_SetArchivePath(pState, argv[1]); + } + + return err; +} + +/* + * ors - open streaming read-only + */ +static NuError +OpenStreamingReadOnlyFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuArchive* pArchive; + FILE* fp = nil; + + assert(ExerciserState_GetNuArchive(pState) == nil); + assert(argc == 2); + + if ((fp = fopen(argv[1], kNuFileOpenReadOnly)) == nil) { + err = errno ? (NuError)errno : kNuErrGeneric; + fprintf(stderr, "Exerciser: unable to open '%s'\n", argv[1]); + } else { + err = NuStreamOpenRO(fp, &pArchive); + if (err == kNuErrNone) { + ExerciserState_SetNuArchive(pState, pArchive); + ExerciserState_SetArchivePath(pState, argv[1]); + fp = nil; + } + } + + if (fp != nil) + fclose(fp); + + return err; +} + +/* + * orw - open read-write + */ +static NuError +OpenReadWriteFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuArchive* pArchive; + + assert(ExerciserState_GetNuArchive(pState) == nil); + assert(argc == 2); + + err = NuOpenRW(argv[1], kTempFile, 0, &pArchive); + if (err == kNuErrNone) { + ExerciserState_SetNuArchive(pState, pArchive); + ExerciserState_SetArchivePath(pState, argv[1]); + } + + return err; +} + +/* + * p - print + */ +static NuError +PrintFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + return NuContents(ExerciserState_GetNuArchive(pState), PrintEntry); +} + +/* + * pd - print debug + */ +static NuError +PrintDebugFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + return NuDebugDumpArchive(ExerciserState_GetNuArchive(pState)); +} + +/* + * re - rename record + */ +static NuError +RenameFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 4); + + return NuRename(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), argv[2], argv[3][0]); +} + +/* + * sec - set error callback + * + * Use an error handler callback. + */ +static NuError +SetErrorCallbackFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + NuSetErrorHandler(ExerciserState_GetNuArchive(pState), ErrorHandler); + return kNuErrNone; +} + +/* + * sev - set value + * + * Currently takes numeric arguments. + */ +static NuError +SetValueFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 3); + + return NuSetValue(ExerciserState_GetNuArchive(pState), + (NuValueID) strtol(argv[1], nil, 0), strtol(argv[2], nil, 0)); +} + +/* + * sra - set record attributes + * + * Right now I'm only allowing changes to file type and aux type. This + * could be adapted to do more easily, but the command handler has a + * rigid notion of how many arguments each function should have, so + * you'd need to list all of them every time. + */ +static NuError +SetRecordAttrFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + const NuRecord* pRecord; + NuRecordAttr recordAttr; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 4); + + err = NuGetRecord(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), &pRecord); + if (err != kNuErrNone) + return err; + printf("Exerciser: NuGetRecord succeeded, calling NuSetRecordAttr\n"); + NuRecordCopyAttr(&recordAttr, pRecord); + recordAttr.fileType = strtol(argv[2], nil, 0); + recordAttr.extraType = strtol(argv[3], nil, 0); + /*recordAttr.fileSysInfo = ':';*/ + return NuSetRecordAttr(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), &recordAttr); +} + +/* + * t - test archive + */ +static NuError +TestFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 1); + + return NuTest(ExerciserState_GetNuArchive(pState)); +} + +/* + * tr - test record + */ +static NuError +TestRecordFunc(ExerciserState* pState, int argc, char** argv) +{ + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + return NuTestRecord(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0)); +} + +/* + * upt - update pre-sized thread + */ +static NuError +UpdatePresizedThreadFunc(ExerciserState* pState, int argc, char** argv) +{ + NuError err; + NuDataSource* pDataSource = nil; + char* lineBuf = nil; + long ourLen, maxLen; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + assert(ExerciserState_GetNuArchive(pState) != nil); + assert(argc == 2); + + lineBuf = (char*)malloc(kNiceLineLen); + assert(lineBuf != nil); + err = GetLine("Enter data for thread", lineBuf, kNiceLineLen); + if (err != kNuErrNone) + goto bail; + + ourLen = strlen(lineBuf); + + /* use "ourLen" for both buffer len and data len */ + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + ourLen, (unsigned char*)lineBuf, 0, ourLen, FreeCallback, + &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, "Exerciser: data source create failed (err=%d)\n", + err); + goto bail; + } + lineBuf = nil; /* now owned by the library */ + + err = NuUpdatePresizedThread(ExerciserState_GetNuArchive(pState), + strtol(argv[1], nil, 0), pDataSource, &maxLen); + if (err == kNuErrNone) + printf("Exerciser: success; function returned maxLen=%ld\n", maxLen); + +bail: + NuFreeDataSource(pDataSource); + if (lineBuf != nil) + free(lineBuf); + return err; +} + + +/* + * Command table. This drives the user interface. + */ + +/* flags for the CommandTable */ +#define kFlagArchiveReq (1L) /* must have archive open */ +#define kFlagNoArchiveReq (1L<<1) /* must NOT have archive open */ + +/* command set */ +static const struct { + const char* commandStr; + CommandFunc func; + int expectedArgCount; + const char* argumentList; + unsigned long flags; + const char* description; +} gCommandTable[] = { + { "--- exerciser commands ---", HelpFunc, 0, "", 0, + "" }, + { "?", HelpFunc, 0, "", 0, + "Show help" }, + { "h", HelpFunc, 0, "", 0, + "Show help" }, + { "q", QuitFunc, 0, "", 0, + "Quit program (will abort un-flushed changes)" }, + + { "--- archive commands ---", HelpFunc, 0, "", 0, + "" }, + + { "ab", AbortFunc, 0, "", kFlagArchiveReq, + "Abort current changes" }, + { "af", AddFileFunc, 1, "filename", kFlagArchiveReq, + "Add file" }, + { "ar", AddRecordFunc, 1, "storageName", kFlagArchiveReq, + "Add record" }, + { "at", AddThreadFunc, 2, "recordIdx threadID", kFlagArchiveReq, + "Add thread to record" }, + { "cl", CloseFunc, 0, "", kFlagArchiveReq, + "Close archive after flushing any changes" }, + { "d", DeleteFunc, 0, "", kFlagArchiveReq, + "Delete all records" }, + { "dr", DeleteRecordFunc, 1, "recordIdx", kFlagArchiveReq, + "Delete record" }, + { "dt", DeleteThreadFunc, 1, "threadIdx", kFlagArchiveReq, + "Delete thread" }, + { "e", ExtractFunc, 0, "", kFlagArchiveReq, + "Extract all files" }, + { "er", ExtractRecordFunc, 1, "recordIdx", kFlagArchiveReq, + "Extract record" }, + { "et", ExtractThreadFunc, 2, "threadIdx filename", kFlagArchiveReq, + "Extract thread" }, + { "fl", FlushFunc, 0, "", kFlagArchiveReq, + "Flush changes" }, + { "gev", GetValueFunc, 1, "ident", kFlagArchiveReq, + "Get value" }, + { "gmh", GetMasterHeaderFunc, 0, "", kFlagArchiveReq, + "Get master header" }, + { "gr", GetRecordFunc, 1, "recordIdx", kFlagArchiveReq, + "Get record" }, + { "grin", GetRecordIdxByNameFunc, 1, "name", kFlagArchiveReq, + "Get recordIdx by name" }, + { "grip", GetRecordIdxByPositionFunc, 1, "position", kFlagArchiveReq, + "Get recordIdx by position" }, + { "ocrw", OpenCreateReadWriteFunc, 1, "filename", kFlagNoArchiveReq, + "Open/create archive read-write" }, + { "oro", OpenReadOnlyFunc, 1, "filename", kFlagNoArchiveReq, + "Open archive read-only" }, + { "ors", OpenStreamingReadOnlyFunc, 1, "filename", kFlagNoArchiveReq, + "Open archive streaming read-only" }, + { "orw", OpenReadWriteFunc, 1, "filename", kFlagNoArchiveReq, + "Open archive read-write" }, + { "p", PrintFunc, 0, "", kFlagArchiveReq, + "Print archive contents" }, + { "pd", PrintDebugFunc, 0, "", kFlagArchiveReq, + "Print debugging output (if available)" }, + { "re", RenameFunc, 3, "recordIdx name sep", kFlagArchiveReq, + "Rename record" }, + { "sec", SetErrorCallbackFunc, 0, "", kFlagArchiveReq, + "Set error callback" }, + { "sev", SetValueFunc, 2, "ident value", kFlagArchiveReq, + "Set value" }, + { "sra", SetRecordAttrFunc, 3, "recordIdx type aux", kFlagArchiveReq, + "Set record attributes" }, + { "t", TestFunc, 0, "", kFlagArchiveReq, + "Test archive" }, + { "tr", TestRecordFunc, 1, "recordIdx", kFlagArchiveReq, + "Test record" }, + { "upt", UpdatePresizedThreadFunc, 1, "threadIdx", kFlagArchiveReq, + "Update pre-sized thread" }, +}; + +#define kMaxArgs 4 + +/* + * Display a summary of available commands. + */ +static NuError +HelpFunc(ExerciserState* pState, int argc, char** argv) +{ + int i; + + (void) pState, (void) argc, (void) argv; /* shut up, gcc */ + + printf("\nAvailable commands:\n"); + for (i = 0; i < (int)NELEM(gCommandTable); i++) { + printf(" %-4s %-21s %s\n", + gCommandTable[i].commandStr, + gCommandTable[i].argumentList, + gCommandTable[i].description); + } + + return kNuErrNone; +} + + +/* + * =========================================================================== + * Control + * =========================================================================== + */ + +static const char* kWhitespace = " \t\n"; + +/* + * Parse a command from the user. + * + * "lineBuf" will be mangled. On success, "pFunc", "pArgc", and "pArgv" + * will receive the results. + */ +static NuError +ParseLine(char* lineBuf, ExerciserState* pState, CommandFunc* pFunc, int* pArgc, + char*** pArgv) +{ + NuError err = kNuErrSyntax; + char* command; + char* cp; + int i; + + /* + * Parse the strings. + */ + + command = strtok(lineBuf, kWhitespace); + if (command == nil) { + /* no command; the user probably just hit "enter" on a blank line */ + *pFunc = NothingFunc; + *pArgc = 0; + *pArgv = nil; + err = kNuErrNone; + goto bail; + } + + /* no real need to be flexible; add 1 for command and one for nil */ + *pArgv = (char**) malloc(sizeof(char*) * (kMaxArgs+2)); + (*pArgv)[0] = command; + *pArgc = 1; + + cp = strtok(nil, kWhitespace); + while (cp != nil) { + if (*pArgc >= kMaxArgs+1) { + printf("ERROR: too many arguments\n"); + goto bail; + } + (*pArgv)[*pArgc] = cp; + (*pArgc)++; + + cp = strtok(nil, kWhitespace); + } + assert(*pArgc < kMaxArgs+2); + (*pArgv)[*pArgc] = nil; + + /* + * Look up the command. + */ + for (i = 0; i < (int)NELEM(gCommandTable); i++) { + if (strcmp(command, gCommandTable[i].commandStr) == 0) + break; + } + if (i == NELEM(gCommandTable)) { + printf("ERROR: unrecognized command\n"); + goto bail; + } + + *pFunc = gCommandTable[i].func; + + /* + * Check arguments and flags. + */ + if (*pArgc -1 != gCommandTable[i].expectedArgCount) { + printf("ERROR: expected %d args, found %d\n", + gCommandTable[i].expectedArgCount, *pArgc -1); + goto bail; + } + + if (gCommandTable[i].flags & kFlagArchiveReq) { + if (ExerciserState_GetNuArchive(pState) == nil) { + printf("ERROR: must have an archive open\n"); + goto bail; + } + } + if (gCommandTable[i].flags & kFlagNoArchiveReq) { + if (ExerciserState_GetNuArchive(pState) != nil) { + printf("ERROR: an archive is already open\n"); + goto bail; + } + } + + /* + * Looks good! + */ + err = kNuErrNone; + +bail: + return err; +} + + + +/* + * Interpret commands, do clever things. + */ +static NuError +CommandLoop(void) +{ + NuError err = kNuErrNone; + ExerciserState* pState = ExerciserState_New(); + CommandFunc func; + char lineBuf[128]; + int argc; + char** argv = nil; + + while (1) { + printf("\nEnter command (%s)> ", ExerciserState_GetArchiveFile(pState)); + fflush(stdout); + + if (fgets(lineBuf, sizeof(lineBuf), stdin) == nil) { + printf("\n"); + break; + } + + if (argv != nil) { + free(argv); + argv = nil; + } + + func = nil; /* sanity check */ + + err = ParseLine(lineBuf, pState, &func, &argc, &argv); + if (err != kNuErrNone) + continue; + + assert(func != nil); + if (func == QuitFunc) + break; + + err = (*func)(pState, argc, argv); + + if (err < 0) + printf("Exerciser: received error %d (%s)\n", err, NuStrError(err)); + else if (err > 0) + printf("Exerciser: received error %d\n", err); + + if (argv != nil) { + free(argv); + argv = nil; + } + } + + if (ExerciserState_GetNuArchive(pState) != nil) { + /* ought to query the archive before saying something like this... */ + printf("Exerciser: aborting any un-flushed changes in archive %s\n", + ExerciserState_GetArchivePath(pState)); + (void) NuAbort(ExerciserState_GetNuArchive(pState)); + err = NuClose(ExerciserState_GetNuArchive(pState)); + if (err != kNuErrNone) + printf("Exerciser: got error %d closing archive\n", err); + ExerciserState_SetNuArchive(pState, nil); + } + + if (pState != nil) + ExerciserState_Free(pState); + if (argv != nil) + free(argv); + return kNuErrNone; +} + + +/* + * Main entry point. + * + * We don't currently take any arguments, so this is pretty straightforward. + */ +int +main(void) +{ + NuError result; + long majorVersion, minorVersion, bugVersion; + const char* nufxLibDate; + const char* nufxLibFlags; + + (void) NuGetVersion(&majorVersion, &minorVersion, &bugVersion, + &nufxLibDate, &nufxLibFlags); + printf("NufxLib exerciser, linked with NufxLib v%ld.%ld.%ld [%s]\n\n", + majorVersion, minorVersion, bugVersion, nufxLibFlags); + printf("Use 'h' or '?' for help, 'q' to quit.\n"); + + /* stuff useful when debugging lots */ + if (unlink(kTempFile) == 0) + fprintf(stderr, "NOTE: whacked exer-temp\n"); + if (unlink("new.shk") == 0) + fprintf(stderr, "NOTE: whacked new.shk\n"); + +#if defined(HAS_MALLOC_CHECK_) && !defined(USE_DMALLOC) + /* + * This is really nice to have on Linux and any other system that + * uses the GNU libc/malloc stuff. It slows things down, but it + * tells you when you do something dumb with malloc/realloc/free. + * (Solaris 2.7 has a similar feature that is enabled by setting the + * environment variable LD_PRELOAD to include watchmalloc.so. Other + * OSs and 3rd-party malloc packages may have similar features.) + * + * This environment variable must be set when the program is launched. + * Tweaking the environment within the program has no effect. + * + * Now that the Linux world has "valgrind", this is probably + * unnecessary. + */ + { + char* debugSet = getenv("MALLOC_CHECK_"); + if (debugSet == nil) + printf("WARNING: MALLOC_CHECK_ not enabled\n\n"); + } +#endif + + result = CommandLoop(); + + exit(result != kNuErrNone); +} + diff --git a/nufxlib/samples/ImgConv.c b/nufxlib/samples/ImgConv.c new file mode 100644 index 0000000..a697b60 --- /dev/null +++ b/nufxlib/samples/ImgConv.c @@ -0,0 +1,658 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * 2IMG <-> SHK converter. This is a practical example of using the + * NufxLib Thread functions to add and extract data in the middle of a file. + * + * Conversions from 2IMG files do not work for raw nibble images. + * Conversions from SHK archives only work if the disk image is in the + * first record in the archive. (This is easy to fix, but I'm trying to + * keep it simple.) + */ +#include "NufxLib.h" +#include "Common.h" + +#ifndef HAVE_STRCASECMP +static int +strcasecmp(const char *str1, const char *str2) +{ + while (*str1 && *str2 && toupper(*str1) == toupper(*str2)) + str1++, str2++; + return (toupper(*str1) - toupper(*str2)); +} +#endif + + +#define kTempFile "imgconv.tmp" +#define kLocalFssep PATH_SEP +#define false 0 +#define true (!false) + + +/* + * =========================================================================== + * 2IMG stuff + * =========================================================================== + */ + +#define kImgMagic "2IMG" +#define kMyCreator "NFXL" + +#define kImageFormatDOS 0 +#define kImageFormatProDOS 1 +#define kImageFormatNibble 2 + +/* + * 2IMG header definition (http://www.magnet.ch/emutech/Tech/). + */ +typedef struct ImgHeader { + char magic[4]; + char creator[4]; + short headerLen; + short version; + long imageFormat; + unsigned long flags; + long numBlocks; + long dataOffset; + long dataLen; + long cmntOffset; + long cmntLen; + long creatorOffset; + long creatorLen; + long spare[4]; +} ImgHeader; + +/* + * Read a two-byte little-endian value. + */ +void +ReadShortLE(FILE* fp, short* pBuf) +{ + *pBuf = getc(fp); + *pBuf += (short) getc(fp) << 8; +} + +/* + * Write a two-byte little-endian value. + */ +void +WriteShortLE(FILE* fp, unsigned short val) +{ + putc(val, fp); + putc(val >> 8, fp); +} + +/* + * Read a four-byte little-endian value. + */ +void +ReadLongLE(FILE* fp, long* pBuf) +{ + *pBuf = getc(fp); + *pBuf += (long) getc(fp) << 8; + *pBuf += (long) getc(fp) << 16; + *pBuf += (long) getc(fp) << 24; +} + +/* + * Write a four-byte little-endian value. + */ +void +WriteLongLE(FILE* fp, unsigned long val) +{ + putc(val, fp); + putc(val >> 8, fp); + putc(val >> 16, fp); + putc(val >> 24, fp); +} + +/* + * Read the header from a 2IMG file. + */ +int +ReadImgHeader(FILE* fp, ImgHeader* pHeader) +{ + size_t ignored; + ignored = fread(pHeader->magic, 4, 1, fp); + ignored = fread(pHeader->creator, 4, 1, fp); + ReadShortLE(fp, &pHeader->headerLen); + ReadShortLE(fp, &pHeader->version); + ReadLongLE(fp, &pHeader->imageFormat); + ReadLongLE(fp, (long*)&pHeader->flags); + ReadLongLE(fp, &pHeader->numBlocks); + ReadLongLE(fp, &pHeader->dataOffset); + ReadLongLE(fp, &pHeader->dataLen); + ReadLongLE(fp, &pHeader->cmntOffset); + ReadLongLE(fp, &pHeader->cmntLen); + ReadLongLE(fp, &pHeader->creatorOffset); + ReadLongLE(fp, &pHeader->creatorLen); + ReadLongLE(fp, &pHeader->spare[0]); + ReadLongLE(fp, &pHeader->spare[1]); + ReadLongLE(fp, &pHeader->spare[2]); + ReadLongLE(fp, &pHeader->spare[3]); + + (void) ignored; + if (feof(fp) || ferror(fp)) + return -1; + + if (strncmp(pHeader->magic, kImgMagic, 4) != 0) { + fprintf(stderr, "ERROR: bad magic number on 2IMG file\n"); + return -1; + } + + if (pHeader->version > 1) { + fprintf(stderr, "WARNING: might not be able to handle version=%d\n", + pHeader->version); + } + + return 0; +} + +/* + * Write the header to a 2IMG file. + */ +int +WriteImgHeader(FILE* fp, ImgHeader* pHeader) +{ + fwrite(pHeader->magic, 4, 1, fp); + fwrite(pHeader->creator, 4, 1, fp); + WriteShortLE(fp, pHeader->headerLen); + WriteShortLE(fp, pHeader->version); + WriteLongLE(fp, pHeader->imageFormat); + WriteLongLE(fp, pHeader->flags); + WriteLongLE(fp, pHeader->numBlocks); + WriteLongLE(fp, pHeader->dataOffset); + WriteLongLE(fp, pHeader->dataLen); + WriteLongLE(fp, pHeader->cmntOffset); + WriteLongLE(fp, pHeader->cmntLen); + WriteLongLE(fp, pHeader->creatorOffset); + WriteLongLE(fp, pHeader->creatorLen); + WriteLongLE(fp, pHeader->spare[0]); + WriteLongLE(fp, pHeader->spare[1]); + WriteLongLE(fp, pHeader->spare[2]); + WriteLongLE(fp, pHeader->spare[3]); + + if (ferror(fp)) + return -1; + + return 0; +} + + +/* + * Dump the contents of an ImgHeader. + */ +void +DumpImgHeader(ImgHeader* pHeader) +{ + printf("--- header contents:\n"); + printf("\tmagic = '%.4s'\n", pHeader->magic); + printf("\tcreator = '%.4s'\n", pHeader->creator); + printf("\theaderLen = %d\n", pHeader->headerLen); + printf("\tversion = %d\n", pHeader->version); + printf("\timageFormat = %ld\n", pHeader->imageFormat); + printf("\tflags = 0x%08lx\n", pHeader->flags); + printf("\tnumBlocks = %ld\n", pHeader->numBlocks); + printf("\tdataOffset = %ld\n", pHeader->dataOffset); + printf("\tdataLen = %ld\n", pHeader->dataLen); + printf("\tcmntOffset = %ld\n", pHeader->cmntOffset); + printf("\tcmntLen = %ld\n", pHeader->cmntLen); + printf("\tcreatorOffset = %ld\n", pHeader->creatorOffset); + printf("\tcreatorLen = %ld\n", pHeader->creatorLen); + printf("\n"); +} + + +/* + * =========================================================================== + * Main functions + * =========================================================================== + */ + +typedef enum ArchiveKind { kKindUnknown, kKindShk, kKindImg } ArchiveKind; + +/* + * This gets called when a buffer DataSource is no longer needed. + */ +NuResult +FreeCallback(NuArchive* pArchive, void* args) +{ + free(args); + return kNuOK; +} + +/* + * This gets called when an "FP" DataSource is no longer needed. + */ +NuResult +FcloseCallback(NuArchive* pArchive, void* args) +{ + fclose((FILE*) args); + return kNuOK; +} + +/* + * Create a data source for a ProDOS-ordered image. Since this is already + * in the correct format, we just point at the correct offset in the 2MG file. + * + * This supplies an FcloseCallback so that we can exercise that feature + * of NufxLib. We could just as easily not set it and call fclose() + * ourselves, because the structure of this program is pretty simple. + */ +NuError +CreateProdosSource(const ImgHeader* pHeader, FILE* fp, + NuDataSource** ppDataSource) +{ + return NuCreateDataSourceForFP(kNuThreadFormatUncompressed, 0, fp, + pHeader->dataOffset, pHeader->dataLen, FcloseCallback,ppDataSource); +} + +/* + * Create a data source for a DOS-ordered image. This is a little harder, + * since we have to reorder the blocks into ProDOS ordering for ShrinkIt. + */ +NuError +CreateDosSource(const ImgHeader* pHeader, FILE* fp, + NuDataSource** ppDataSource) +{ + NuError err; + char* diskBuffer = nil; + long offset; + + if (pHeader->dataLen % 4096) { + fprintf(stderr, + "ERROR: image size must be multiple of 4096 (%ld isn't)\n", + pHeader->dataLen); + err = kNuErrGeneric; + goto bail; + } + + if (fseek(fp, pHeader->dataOffset, SEEK_SET) < 0) { + err = errno; + perror("fseek failed"); + goto bail; + } + + diskBuffer = malloc(pHeader->dataLen); + if (diskBuffer == nil) { + fprintf(stderr, "ERROR: malloc(%ld) failed\n", pHeader->dataLen); + err = kNuErrMalloc; + goto bail; + } + + /* + * Run through the image, reordering each track. This is a + * reversible transformation, i.e. if you do this twice you're back + * to ProDOS ordering. + */ + for (offset = 0; offset < pHeader->dataLen; offset += 4096) { + size_t ignored; + ignored = fread(diskBuffer + offset + 0x0000, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0e00, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0d00, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0c00, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0b00, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0a00, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0900, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0800, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0700, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0600, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0500, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0400, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0300, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0200, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0100, 256, 1, fp); + ignored = fread(diskBuffer + offset + 0x0f00, 256, 1, fp); + (void) ignored; + } + if (feof(fp) || ferror(fp)) { + err = errno ? errno : -1; + fprintf(stderr, "ERROR: failed while reading source file\n"); + goto bail; + } + + /* + * Create a data source for the buffer. We set the "doClose" flag to + * "true", so NufxLib will free the buffer for us. + */ + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, + (const unsigned char*) diskBuffer, 0, pHeader->dataLen, + FreeCallback, ppDataSource); + if (err == kNuErrNone) + diskBuffer = nil; + +bail: + if (diskBuffer != nil) + free(diskBuffer); + return err; +} + + + +/* + * Convert a 2IMG file into a new SHK archive. + * + * This requires opening up the 2IMG file, verifying that it's okay, and + * then creating a new disk image record and thread. + */ +int +ConvertFromImgToShk(const char* srcName, const char* dstName) +{ + NuError err; + NuArchive* pArchive = nil; + NuDataSource* pDataSource = nil; + NuRecordIdx recordIdx; + NuFileDetails fileDetails; + ImgHeader header; + FILE* fp = nil; + long flushStatus; + char* storageName = nil; + char* cp; + + printf("Converting 2IMG file '%s' to ShrinkIt archive '%s'\n\n", + srcName, dstName); + + err = kNuErrGeneric; + + fp = fopen(srcName, kNuFileOpenReadOnly); + if (fp == NULL) { + perror("fopen failed"); + goto bail; + } + + if (ReadImgHeader(fp, &header) < 0) { + fprintf(stderr, "ERROR: header read failed\n"); + goto bail; + } + + DumpImgHeader(&header); + + if (header.imageFormat != kImageFormatDOS && + header.imageFormat != kImageFormatProDOS) + { + fprintf(stderr, "ERROR: can only handle DOS and ProDOS images\n"); + goto bail; + } + + if (header.numBlocks > 1600) + printf("WARNING: that's a big honking image!\n"); + + /* + * Open a new archive read-write. This refuses to overwrite an + * existing file. + */ + (void) unlink(kTempFile); + err = NuOpenRW(dstName, kTempFile, kNuOpenCreat|kNuOpenExcl, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create archive (err=%d)\n", err); + goto bail; + } + + /* create the name that will be stored in the archive */ + storageName = strdup(dstName); + cp = strrchr(storageName, '.'); + if (cp != nil) + *cp = '\0'; + cp = strrchr(storageName, kLocalFssep); + if (cp != nil && *(cp+1) != '\0') + cp++; + else + cp = storageName; + + /* + * We can't say "add file", because NufxLib doesn't know what a 2MG + * archive is. However, we can point a DataSource at the data in + * the file, and construct the record manually. + */ + + /* set up the contents of the NuFX Record */ + memset(&fileDetails, 0, sizeof(fileDetails)); + fileDetails.storageName = cp; + fileDetails.fileSysID = kNuFileSysUnknown; /* DOS? ProDOS? */ + fileDetails.fileSysInfo = kLocalFssep; + fileDetails.access = kNuAccessUnlocked; + fileDetails.extraType = header.numBlocks; + fileDetails.storageType = 512; + /* FIX - ought to set the file dates */ + + /* add a new record */ + err = NuAddRecord(pArchive, &fileDetails, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create record (err=%d)\n", err); + goto bail; + } + + /* + * Create a data source for the 2IMG file. We do this differently + * for DOS and ProDOS, because we have to rearrange the sector + * ordering for DOS-ordered images (ShrinkIt always uses ProDOS order). + */ + switch (header.imageFormat) { + case kImageFormatDOS: + err = CreateDosSource(&header, fp, &pDataSource); + fp = nil; + break; + case kImageFormatProDOS: + err = CreateProdosSource(&header, fp, &pDataSource); + fp = nil; + break; + default: + fprintf(stderr, "How the heck did I get here?"); + err = kNuErrInternal; + goto bail; + } + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create data source (err=%d)\n", err); + goto bail; + } + + /* add a disk image thread */ + err = NuAddThread(pArchive, recordIdx, kNuThreadIDDiskImage, pDataSource, + nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create thread (err=%d)\n", err); + goto bail; + } + pDataSource = nil; /* library owns it now */ + + /* nothing happens until we Flush */ + err = NuFlush(pArchive, &flushStatus); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: flush failed (err=%d, status=0x%04lx)\n", + err, flushStatus); + goto bail; + } + err = NuClose(pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: close failed (err=%d)\n", err); + goto bail; + } + pArchive = nil; + +bail: + if (pArchive != nil) { + (void)NuAbort(pArchive); + (void)NuClose(pArchive); + } + NuFreeDataSource(pDataSource); + if (storageName != nil) + free(storageName); + if (fp != nil) + fclose(fp); + return (err == kNuErrNone) ? 0 : -1; +} + + +/* + * Convert an SHK archive into a 2IMG file. + * + * This takes a simple-minded approach and assumes that the first record + * in the archive has the disk image in it. If it doesn't, we give up. + */ +int +ConvertFromShkToImg(const char* srcName, const char* dstName) +{ + NuError err; + NuArchive* pArchive = nil; + NuDataSink* pDataSink = nil; + NuRecordIdx recordIdx; + const NuRecord* pRecord; + const NuThread* pThread = nil; + ImgHeader header; + FILE* fp = nil; + int idx; + + printf("Converting ShrinkIt archive '%s' to 2IMG file '%s'\n\n", + srcName, dstName); + + /* + * Open the archive. + */ + err = NuOpenRO(srcName, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to open archive (err=%d)\n", err); + goto bail; + } + + /* get the first record */ + err = NuGetRecordIdxByPosition(pArchive, 0, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get first recordIdx (err=%d)\n", err); + goto bail; + } + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get first record (err=%d)\n", err); + goto bail; + } + + /* find a disk image thread */ + for (idx = 0; idx < (int)NuRecordGetNumThreads(pRecord); idx++) { + pThread = NuGetThread(pRecord, idx); + + if (NuGetThreadID(pThread) == kNuThreadIDDiskImage) + break; + } + if (idx == (int)NuRecordGetNumThreads(pRecord)) { + fprintf(stderr, "ERROR: no disk image found in first record\n"); + err = -1; + goto bail; + } + + /* + * Looks good. Open the 2IMG file, and create the header. + */ + if (access(dstName, F_OK) == 0) { + fprintf(stderr, "ERROR: output file already exists\n"); + err = -1; + goto bail; + } + + fp = fopen(dstName, kNuFileOpenWriteTrunc); + if (fp == NULL) { + perror("fopen failed"); + goto bail; + } + + /* set up the 2MG header, based on the NuFX Record */ + memset(&header, 0, sizeof(header)); + memcpy(header.magic, kImgMagic, sizeof(header.magic)); + memcpy(header.creator, kMyCreator, sizeof(header.creator)); + header.headerLen = 64; + header.version = 1; + header.imageFormat = kImageFormatProDOS; /* always ProDOS-order */ + header.numBlocks = pRecord->recExtraType; + header.dataOffset = 64; + /* old versions of ShrinkIt blew the threadEOF, so use NufxLib's "actual" */ + header.dataLen = pThread->actualThreadEOF; + DumpImgHeader(&header); + if (WriteImgHeader(fp, &header) < 0) { + fprintf(stderr, "ERROR: header write failed\n"); + err = -1; + goto bail; + } + + /* + * We want to expand the disk image thread into "fp" at the current + * offset. + */ + err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", err); + goto bail; + } + + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to extract thread (err=%d)\n", err); + goto bail; + } + +bail: + if (pArchive != nil) + NuClose(pArchive); + NuFreeDataSink(pDataSink); + if (fp != nil) + fclose(fp); + return (err == kNuErrNone) ? 0 : -1; +} + + +/* + * Figure out what kind of archive this is by looking at the filename. + */ +ArchiveKind +DetermineKind(const char* filename) +{ + const char* dot; + + dot = strrchr(filename, '.'); + if (dot == nil) + return kKindUnknown; + + if (strcasecmp(dot, ".shk") == 0 || strcasecmp(dot, ".sdk") == 0) + return kKindShk; + else if (strcasecmp(dot, ".2mg") == 0) + return kKindImg; + + return kKindUnknown; +} + + + +/* + * Figure out what we want to do. + */ +int +main(int argc, char** argv) +{ + ArchiveKind kind; + int cc; + + if (argc != 3) { + fprintf(stderr, "Usage: %s (input.2mg|input.shk) output\n", argv[0]); + exit(2); + } + + kind = DetermineKind(argv[1]); + if (kind == kKindUnknown) { + fprintf(stderr, "ERROR: input name must end in '.shk' or '.2mg'\n"); + exit(2); + } + + if (kind == kKindShk) + cc = ConvertFromShkToImg(argv[1], argv[2]); + else + cc = ConvertFromImgToShk(argv[1], argv[2]); + + if (cc) + fprintf(stderr, "Failed\n"); + else + printf("Done!\n"); + + exit(cc != 0); +} + diff --git a/nufxlib/samples/Launder.c b/nufxlib/samples/Launder.c new file mode 100644 index 0000000..bc0f525 --- /dev/null +++ b/nufxlib/samples/Launder.c @@ -0,0 +1,710 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * Run an archive through the laundry. The net result is a duplicate + * archive that matches the original in most respects. Extracting the + * files from the duplicate will yield the same results as if they were + * extracted from the original, but the duplicate archive may differ + * in subtle ways (e.g. filename threads may be added, data may be + * recompressed). + * + * This demonstrates copying threads around, both with and without + * recompressing, between two archives that are open simultaneously. This + * also tests NufxLib's thread ordering and verifies that you can abort + * frequently with no adverse effects. + * + * NOTE: depending on the options you select, you may need to have enough + * memory to hold the entire uncompressed contents of the original archive. + * The memory requirements are reduced if you use the "copy only" flag, and + * are virtually eliminated if you use "frequent flush". + */ +#include +#include +#include +#include +#include "NufxLib.h" +#include "Common.h" + + +#define kTempFile "tmp-laundry" + +#define kFlagCopyOnly (1) +#define kFlagReverseThreads (1 << 1) +#define kFlagFrequentFlush (1 << 2) +#define kFlagFrequentAbort (1 << 3) /* implies FrequentFlush */ +#define kFlagUseTmp (1 << 4) + + +/* + * Globals. + */ +char gSentRecordWarning = false; + + +/* + * This gets called when a buffer DataSource is no longer needed. + */ +NuResult +FreeCallback(NuArchive* pArchive, void* args) +{ + free(args); + return kNuOK; +} + +/* + * Copy a thread, expanding and recompressing it. + * + * This assumes the library is configured for compression (it defaults + * to LZW/2, so this is a reasonable assumption). + */ +NuError +CopyThreadRecompressed(NuArchive* pInArchive, NuArchive* pOutArchive, + long flags, const NuThread* pThread, long newRecordIdx) +{ + NuError err = kNuErrNone; + NuDataSource* pDataSource = nil; + NuDataSink* pDataSink = nil; + uchar* buffer = nil; + + /* + * Allocate a buffer large enough to hold all the uncompressed data, and + * wrap a data sink around it. + * + * If the thread is zero bytes long, we can skip this part. + */ + if (pThread->actualThreadEOF) { + buffer = malloc(pThread->actualThreadEOF); + if (buffer == nil) { + err = kNuErrMalloc; + goto bail; + } + err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", + err); + goto bail; + } + + /* + * Expand the data. For a pre-sized thread, this grabs only the + * interesting part of the buffer. + */ + err = NuExtractThread(pInArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to extract thread %ld (err=%d)\n", + pThread->threadIdx, err); + goto bail; + } + } + + /* + * The expanded data is in the buffer, now create a data source that + * describes it. + * + * This is complicated by the existence of pre-sized threads, which + * require us to set "otherLen". + * + * We always use "actualThreadEOF" because "thThreadEOF" is broken + * for disk archives created by certain versions of ShrinkIt. + * + * It's okay to pass in a nil value for "buffer", so long as the + * amount of data in the buffer is also zero. The library will do + * the right thing. + */ + if (NuIsPresizedThreadID(NuGetThreadID(pThread))) { + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + pThread->thCompThreadEOF, buffer, 0, + pThread->actualThreadEOF, FreeCallback, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: unable to create pre-sized data source (err=%d)\n",err); + goto bail; + } + } else { + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + 0, buffer, 0, pThread->actualThreadEOF, + FreeCallback, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: unable to create data source (err=%d)\n", err); + goto bail; + } + } + buffer = nil; /* doClose was set, so it's owned by the data source */ + + /* + * Schedule the data for addition to the record. + */ + err = NuAddThread(pOutArchive, newRecordIdx, NuGetThreadID(pThread), + pDataSource, nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to add thread (err=%d)\n", err); + goto bail; + } + pDataSource = nil; /* library owns it now */ + +bail: + if (pDataSource != nil) + NuFreeDataSource(pDataSource); + if (pDataSink != nil) + NuFreeDataSink(pDataSink); + if (buffer != nil) + free(buffer); + return err; +} + +/* + * Copy a thread from one archive to another without disturbing the + * compression. + * + * There is a much more efficient way to do this: create an FP + * data source using an offset within the archive file itself. + * Since pInArchive->archiveFp isn't exposed, we can't use that, + * but under most operating systems you aren't prevented from + * opening the same file twice in read-only mode. The file offset + * in pThread tells us where the data is. + * + * The method used below is less memory-efficient but more portable. + * + * This always extracts based on the compThreadEOF, which is + * reliable but extracts a little more than we need on pre-sized + * threads (filenames, comments). + */ +NuError +CopyThreadUncompressed(NuArchive* pInArchive, NuArchive* pOutArchive, + long flags, const NuThread* pThread, long newRecordIdx) +{ + NuError err = kNuErrNone; + NuDataSource* pDataSource = nil; + NuDataSink* pDataSink = nil; + uchar* buffer = nil; + + /* + * If we have some data files that were left uncompressed, perhaps + * because of GSHK's "don't compress anything smaller than 512 bytes" + * rule, NufxLib will try to compress them. We disable this + * behavior by disabling compression. That way, stuff that is + * already compressed will remain that way, and stuff that isn't + * compressed won't be. (We really only need to do this once, at + * the start of the program, but it's illustrative to do it here.) + * + * [ I don't understand this comment. It's necessary to disable + * compression, but I don't see why uncompressed files are + * special. ++ATM 20040821 ] + */ + err = NuSetValue(pOutArchive, kNuValueDataCompression, kNuCompressNone); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to set compression (err=%d)\n", err); + goto bail; + } + + /* + * Allocate a buffer large enough to hold all the compressed data, and + * wrap a data sink around it. + */ + buffer = malloc(pThread->thCompThreadEOF); + if (buffer == nil) { + err = kNuErrMalloc; + goto bail; + } + err = NuCreateDataSinkForBuffer(false, kNuConvertOff, buffer, + pThread->thCompThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", + err); + goto bail; + } + + /* + * Get the compressed data. For a pre-sized thread, this grabs the + * entire contents of the buffer, including the padding. + */ + err = NuExtractThread(pInArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to extract thread %ld (err=%d)\n", + pThread->threadIdx, err); + goto bail; + } + + /* + * The (perhaps compressed) data is in the buffer, now create a data + * source that describes it. + * + * This is complicated by the existence of pre-sized threads. There + * are two possibilities: + * 1. We have a certain amount of non-pre-sized data (thCompThreadEOF) + * that will expand out to a certain length (actualThreadEOF). + * 2. We have a certain amount of pre-sized data (actualThreadEOF) + * that will fit within a buffer (thCompThreadEOF). + * As you can see, the arguments need to be reversed for pre-sized + * threads. + * + * We always use "actualThreadEOF" because "thThreadEOF" is broken + * for disk archives created by certain versions of ShrinkIt. + */ + if (NuIsPresizedThreadID(NuGetThreadID(pThread))) { + err = NuCreateDataSourceForBuffer(pThread->thThreadFormat, + pThread->thCompThreadEOF, buffer, 0, + pThread->actualThreadEOF, FreeCallback, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: unable to create pre-sized data source (err=%d)\n",err); + goto bail; + } + } else { + err = NuCreateDataSourceForBuffer(pThread->thThreadFormat, + pThread->actualThreadEOF, buffer, 0, + pThread->thCompThreadEOF, FreeCallback, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: unable to create data source (err=%d)\n", err); + goto bail; + } + } + buffer = nil; /* doClose was set, so it's owned by the data source */ + + /* yes, this is a kluge... sigh */ + err = NuDataSourceSetRawCrc(pDataSource, pThread->thThreadCRC); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: can't set source CRC (err=%d)\n", err); + goto bail; + } + + /* + * Schedule the data for addition to the record. + * + * Note that NuAddThread makes a copy of the data source, and clears + * "doClose" on our copy, so we are free to dispose of pDataSource. + */ + err = NuAddThread(pOutArchive, newRecordIdx, NuGetThreadID(pThread), + pDataSource, nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to add thread (err=%d)\n", err); + goto bail; + } + pDataSource = nil; /* library owns it now */ + +bail: + if (pDataSource != nil) + NuFreeDataSource(pDataSource); + if (pDataSink != nil) + NuFreeDataSink(pDataSink); + if (buffer != nil) + free(buffer); + return err; +} + + +/* + * Copy a thread from one archive to another. + * + * Depending on "flags", this will either copy it raw or uncompress and + * recompress. + */ +NuError +CopyThread(NuArchive* pInArchive, NuArchive* pOutArchive, long flags, + const NuThread* pThread, long newRecordIdx) +{ + if (flags & kFlagCopyOnly) { + return CopyThreadUncompressed(pInArchive, pOutArchive, flags, pThread, + newRecordIdx); + } else { + return CopyThreadRecompressed(pInArchive, pOutArchive, flags, pThread, + newRecordIdx); + } +} + + +/* + * Copy a record from the input to the output. + * + * This runs through the list of threads and copies each one individually. + * It will copy them in the original order or in reverse order (the latter + * of which will not usually have any effect since NufxLib imposes a + * specific thread ordering on most common types) depending on "flags". + */ +NuError +CopyRecord(NuArchive* pInArchive, NuArchive* pOutArchive, long flags, + NuRecordIdx recordIdx) +{ + NuError err = kNuErrNone; + const NuRecord* pRecord; + const NuThread* pThread; + NuFileDetails fileDetails; + NuRecordIdx newRecordIdx; + long numThreads; + int idx; + + /* + * Grab the original record and see how many threads it has. + */ + err = NuGetRecord(pInArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get recordIdx %ld\n", recordIdx); + goto bail; + } + + /* + * Pre-v3 records didn't put CRCs in the thread headers. If we just + * copy the thread over without reprocessing the data, we won't compute + * a CRC for the thread, and we will get CRC failures. + */ + if (!gSentRecordWarning && (flags & kFlagCopyOnly) && + pRecord->recVersionNumber < 3) + { + printf("WARNING: pre-v3 records that aren't recompressed may exhibit CRC failures\n"); + gSentRecordWarning = true; + } + + numThreads = NuRecordGetNumThreads(pRecord); + if (!numThreads) { + fprintf(stderr, "WARNING: recordIdx=%ld was empty\n", recordIdx); + goto bail; + } + + /* + * Create a new record that looks just like the original. + */ + memset(&fileDetails, 0, sizeof(fileDetails)); + fileDetails.storageName = pRecord->filename; + fileDetails.fileSysID = pRecord->recFileSysID; + fileDetails.fileSysInfo = pRecord->recFileSysInfo; + fileDetails.access = pRecord->recAccess; + fileDetails.fileType = pRecord->recFileType; + fileDetails.extraType = pRecord->recExtraType; + fileDetails.storageType = pRecord->recStorageType; + fileDetails.createWhen = pRecord->recCreateWhen; + fileDetails.modWhen = pRecord->recModWhen; + fileDetails.archiveWhen = pRecord->recArchiveWhen; + + err = NuAddRecord(pOutArchive, &fileDetails, &newRecordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: NuAddRecord failed (err=%d)\n", err); + goto bail; + } + + /* + * Copy the threads. + */ + if (flags & kFlagReverseThreads) { + for (idx = numThreads-1; idx >= 0; idx--) { + pThread = NuGetThread(pRecord, idx); + assert(pThread != nil); + + err = CopyThread(pInArchive, pOutArchive, flags, pThread, + newRecordIdx); + if (err != kNuErrNone) + goto bail; + } + } else { + for (idx = 0; idx < numThreads; idx++) { + pThread = NuGetThread(pRecord, idx); + assert(pThread != nil); + + err = CopyThread(pInArchive, pOutArchive, flags, pThread, + newRecordIdx); + if (err != kNuErrNone) + goto bail; + } + } + +bail: + return err; +} + + +/* + * Launder an archive from inFile to outFile. + * + * Returns 0 on success, nonzero on failure. + */ +int +LaunderArchive(const char* inFile, const char* outFile, NuValue compressMethod, + long flags) +{ + NuError err = kNuErrNone; + NuArchive* pInArchive = nil; + NuArchive* pOutArchive = nil; + const NuMasterHeader* pMasterHeader; + NuRecordIdx recordIdx; + long idx, flushStatus; + + err = NuOpenRO(inFile, &pInArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't open input archive '%s' (err=%d)\n", + inFile, err); + goto bail; + } + err = NuOpenRW(outFile, kTempFile, kNuOpenCreat|kNuOpenExcl, &pOutArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't open output archive '%s' (err=%d)\n", + outFile, err); + goto bail; + } + + /* turn off "mimic GSHK" */ + err = NuSetValue(pInArchive, kNuValueMimicSHK, true); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to disable GSHK quirks (err=%d)\n", + err); + goto bail; + } + + /* allow duplicates, in case the original archive has them */ + err = NuSetValue(pOutArchive, kNuValueAllowDuplicates, true); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't allow duplicates (err=%d)\n", err); + goto bail; + } + + /* set the compression method */ + err = NuSetValue(pOutArchive, kNuValueDataCompression, compressMethod); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to set compression (err=%d)\n", + err); + goto bail; + } + + if (flags & kFlagUseTmp) { + err = NuSetValue(pOutArchive, kNuValueModifyOrig, false); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: couldn't disable modify orig (err=%d)\n", err); + goto bail; + } + } + + err = NuGetMasterHeader(pInArchive, &pMasterHeader); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err); + goto bail; + } + + /* + * Iterate through the set of records. + */ + for (idx = 0; idx < (int)pMasterHeader->mhTotalRecords; idx++) { + err = NuGetRecordIdxByPosition(pInArchive, idx, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", + idx, err); + goto bail; + } + + err = CopyRecord(pInArchive, pOutArchive, flags, recordIdx); + if (err != kNuErrNone) + goto bail; + + /* + * If "frequent abort" is set, abort what we just did and redo it. + */ + if (flags & kFlagFrequentAbort) { + /*printf("(abort)\n");*/ + err = NuAbort(pOutArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: abort failed (err=%d)\n", err); + goto bail; + } + + err = CopyRecord(pInArchive, pOutArchive, flags, recordIdx); + if (err != kNuErrNone) + goto bail; + + } + + /* + * If "frequent abort" or "frequent flush" is set, flush after + * each record is copied. + */ + if ((flags & kFlagFrequentAbort) || (flags & kFlagFrequentFlush)) { + /*printf("(flush)\n");*/ + err = NuFlush(pOutArchive, &flushStatus); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: flush failed (err=%d, status=0x%04lx)\n", + err, flushStatus); + goto bail; + } + } + } + + /* first and only flush if frequent-flushing wasn't enabled */ + err = NuFlush(pOutArchive, &flushStatus); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: flush failed (err=%d, status=0x%04lx)\n", + err, flushStatus); + goto bail; + } + +bail: + if (pInArchive != nil) + NuClose(pInArchive); + if (pOutArchive != nil) { + if (err != kNuErrNone) + NuAbort(pOutArchive); + NuClose(pOutArchive); /* flush pending changes and close */ + } + return (err != kNuErrNone); +} + + + +/* + * Can't count on having getopt on non-UNIX platforms, so just use this + * quick version instead. May not work exactly like getopt(), but it + * does everything we need here. + */ +int myoptind = 0; +char* myoptarg = nil; +const char* curchar = nil; +int skipnext = false; + +int +mygetopt(int argc, char** argv, const char* optstr) +{ + if (!myoptind) { + myoptind = 1; + if (argc <= 1) + return EOF; + curchar = argv[myoptind]; + if (*curchar != '-') + return EOF; + } + + curchar++; + if (*curchar == '\0') { + myoptind++; + if (skipnext) + myoptind++; + if (myoptind >= argc) + return EOF; + curchar = argv[myoptind]; + if (*curchar != '-') + return EOF; + curchar++; + } + + while (*optstr != '\0') { + if (*optstr == *curchar) { + /*printf("MATCHED '%c'\n", *optstr);*/ + if (*(optstr+1) == ':') { + skipnext = true; + myoptarg = argv[myoptind+1]; + /*printf("ATTACHED '%s'\n", myoptarg);*/ + } + return *curchar; + } + + optstr++; + } + + fprintf(stderr, "Unrecognized option '%c'\n", *curchar); + return *curchar; +} + +/* + * Print usage info. + */ +void +Usage(const char* argv0) +{ + fprintf(stderr, "Usage: %s [-crfat] [-m method] infile.shk outfile.shk\n", + argv0); + fprintf(stderr, "\t-c : copy only, does not recompress data\n"); + fprintf(stderr, "\t-r : copy threads in reverse order to test ordering\n"); + fprintf(stderr, "\t-f : call Flush frequently to reduce memory usage\n"); + fprintf(stderr, "\t-a : exercise nufxlib Abort code frequently\n"); + fprintf(stderr, "\t-t : write to temp file instead of directly to outfile.shk\n"); + fprintf(stderr, + "\t[method] is one of {sq,lzw1,lzw2,lzc12,lzc16,deflate,bzip2}\n"); + fprintf(stderr, "\tIf not specified, method defaults to lzw2\n"); +} + +/* + * Grab the name of an archive to read. + */ +int +main(int argc, char** argv) +{ + NuValue compressMethod = kNuCompressLZW2; + long major, minor, bug; + const char* pBuildDate; + long flags = 0; + int errorFlag; + int ic; + int cc; + + (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil); + printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n", + major, minor, bug, pBuildDate); + + errorFlag = false; + while ((ic = mygetopt(argc, argv, "crfatm:")) != EOF) { + switch (ic) { + case 'c': flags |= kFlagCopyOnly; break; + case 'r': flags |= kFlagReverseThreads; break; + case 'f': flags |= kFlagFrequentFlush; break; + case 'a': flags |= kFlagFrequentAbort; break; + case 't': flags |= kFlagUseTmp; break; + case 'm': + { + struct { + const char* str; + NuValue val; + NuFeature feature; + } methods[] = { + { "sq", kNuCompressSQ, kNuFeatureCompressSQ }, + { "lzw1", kNuCompressLZW1, kNuFeatureCompressLZW }, + { "lzw2", kNuCompressLZW2, kNuFeatureCompressLZW }, + { "lzc12", kNuCompressLZC12, kNuFeatureCompressLZC }, + { "lzc16", kNuCompressLZC16, kNuFeatureCompressLZC }, + { "deflate", kNuCompressDeflate, kNuFeatureCompressDeflate}, + { "bzip2", kNuCompressBzip2, kNuFeatureCompressBzip2 }, + }; + char* methodStr = myoptarg; + int i; + + for (i = 0; i < NELEM(methods); i++) { + if (strcmp(methods[i].str, methodStr) == 0) { + compressMethod = methods[i].val; + break; + } + } + if (i == NELEM(methods)) { + fprintf(stderr, "ERROR: unknown method '%s'\n", methodStr); + errorFlag++; + break; + } + if (NuTestFeature(methods[i].feature) != kNuErrNone) { + fprintf(stderr, + "ERROR: compression method '%s' not supported\n", + methodStr); + errorFlag++; + break; + } + } + break; + default: + errorFlag++; + break; + } + } + + if (errorFlag || argc != myoptind+2) { + Usage(argv[0]); + exit(2); + } + + cc = LaunderArchive(argv[myoptind], argv[myoptind+1], compressMethod,flags); + + if (cc == 0) + printf("Success!\n"); + else + printf("Failed.\n"); + exit(cc != 0); +} + diff --git a/nufxlib/samples/Makefile.in b/nufxlib/samples/Makefile.in new file mode 100644 index 0000000..6590a08 --- /dev/null +++ b/nufxlib/samples/Makefile.in @@ -0,0 +1,91 @@ +# +# Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. +# This is free software; you can redistribute it and/or modify it under the +# terms of the BSD, see the file COPYING. +# +# Makefile for nufxlib tests (should work with non-GNU make). +# +# This is normally invoked from the nufxlib makefile. +# +# If you invoke this directly, LIB_PRODUCT won't be defined, and it +# won't automatically detect changes to the library. However, any +# changes to the library should cause a re-build in here anyway if +# you're running "make" from the library directory. +# +SHELL = /bin/sh +CC = @CC@ +AR = ar rcv +#OPT = @CFLAGS@ -DNDEBUG +OPT = @CFLAGS@ +#OPT = @CFLAGS@ -DDEBUG_MSGS +#OPT = @CFLAGS@ -DDEBUG_VERBOSE +GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow +CFLAGS = @BUILD_FLAGS@ -I. -I.. @DEFS@ + +#ALL_SRCS = $(wildcard *.c *.cpp) +ALL_SRCS = Exerciser.c ImgConv.c Launder.c TestBasic.c \ + TestExtract.c TestSimple.c TestTwirl.c + +NUFXLIB = -L.. -lnufx + +PRODUCTS = exerciser imgconv launder test-basic test-extract test-simple \ + test-twirl + +#ifdef PURIFY_BUILD +# PURIFY = purify +# CFLAGS += -DPURIFY +#endif +#ifdef QUANTIFY_BUILD +# QUANTIFY = quantify +# CFLAGS += -DQUANTIFY +#endif + +all: $(PRODUCTS) + @true + +#quantify: +# -rm -f $(PRODUCT) +# @$(MAKE) QUANTIFY_BUILD=1 +# +#purify: +# -rm -f $(PRODUCT) +# @$(MAKE) PURIFY_BUILD=1 + +exerciser: Exerciser.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ Exerciser.o $(NUFXLIB) @LIBS@ + +imgconv: ImgConv.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ ImgConv.o $(NUFXLIB) @LIBS@ + +launder: Launder.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ Launder.o $(NUFXLIB) @LIBS@ + +test-basic: TestBasic.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ TestBasic.o $(NUFXLIB) @LIBS@ + +test-simple: TestSimple.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ TestSimple.o $(NUFXLIB) @LIBS@ + +test-extract: TestExtract.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ TestExtract.o $(NUFXLIB) @LIBS@ + +test-twirl: TestTwirl.o $(LIB_PRODUCT) + $(PURIFY) $(QUANTIFY) $(CC) -o $@ TestTwirl.o $(NUFXLIB) @LIBS@ + +tags:: + ctags --totals -R ../* + @#ctags *.cpp ../*.c *.h ../*.h + +clean: + -rm -f *.o core + -rm -f $(PRODUCTS) + +distclean: clean + -rm -f tags + -rm -f Makefile Makefile.bak + +depend: + makedepend -- $(CFLAGS) -I/usr/local/include -- $(ALL_SRCS) + +# DO NOT DELETE THIS LINE -- make depend depends on it. + diff --git a/nufxlib/samples/Makefile.msc b/nufxlib/samples/Makefile.msc new file mode 100644 index 0000000..e8a4372 --- /dev/null +++ b/nufxlib/samples/Makefile.msc @@ -0,0 +1,113 @@ +# +# Makefile for Microsoft C compilers. Tested against Visual C++ 6.0. +# Not pretty but it seems to work. +# +# Run with "nmake /f Makefile.msc". Expects NufxLib to have been built +# in "..". +# +# To build without debugging info, use "nmake nodebug=1". +# To build with libz, use "nmake libz=1". +# To build with libbz2, use "nmake libbz2=1". +# If you're linking against nufxlib as a DLL, you don't need to specify +# libraries. You probably need to specify DLL=1 and the same setting +# of the NODEBUG flag as you used when building the DLL. If you don't, +# "test-extract" will fail in the fwrite() call in Nu_FWrite, because +# the non-debug /MD libc does something peculiar with FILE*. +# +# For libz/libbz2, you need to have the appropriate library either +# in this directory or in a standard location that the linker can find. +# + +# Windows magic +TARGETOS = BOTH +!include + +NUFXSRCDIR = .. +LIB_PRODUCT = $(NUFXSRCDIR)\nufxlib2.lib + +!ifdef DLL +### build using the same libc as the DLL +!ifdef NODEBUG +#OPT = /D NUFXLIB_DLL /D NDEBUG /MD /Ogityb2 +OPT = /D NUFXLIB_DLL /MD /Ogityb2 +LIB_FLAGS = /nodefaultlib:libcd.lib /nologo setargv.obj +!else +#OPT = /D NUFXLIB_DLL /MDd /Od +OPT = /D NUFXLIB_DLL /D DEBUG_MSGS /MDd /Od +LIB_FLAGS = /nodefaultlib:libc.lib /nologo setargv.obj +!endif +!else + +### build against static lib +!ifdef NODEBUG +#OPT = /D NDEBUG /ML /Ogityb2 +OPT = /ML /Ogityb2 +LIB_FLAGS = /nodefaultlib:libcd.lib /nologo libc.lib setargv.obj +!else +#OPT = /MLd /Od +OPT = /D DEBUG_MSGS /MLd /Od +LIB_FLAGS = /nodefaultlib:libc.lib /nologo libcd.lib setargv.obj +!endif +!endif + +BUILD_FLAGS = /W3 /GX /D "WIN32" /D "_CONSOLE" /I "$(NUFXSRCDIR)" +!MESSAGE Using OPT = $(OPT) + +!ifdef LIBZ +LIB_FLAGS = zlib.lib $(LIB_FLAGS) +!endif +!ifdef LIBBZ2 +LIB_FLAGS = libbz2.lib $(LIB_FLAGS) +!endif + +# how to compile sources +.c.obj: + @$(cc) $(cdebug) $(OPT) $(BUILD_FLAGS) $(cflags) $(cvars) -o $@ $< + + +PRODUCTS = exerciser.exe imgconv.exe launder.exe test-basic.exe test-extract.exe test-simple.exe test-twirl.exe + +all: $(PRODUCTS) + +exerciser.exe: Exerciser.obj $(LIB_PRODUCT) + $(link) $(ldebug) Exerciser.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +imgconv.exe: ImgConv.obj $(LIB_PRODUCT) + $(link) $(ldebug) ImgConv.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +launder.exe: Launder.obj $(LIB_PRODUCT) + $(link) $(ldebug) Launder.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +test-basic.exe: TestBasic.obj $(LIB_PRODUCT) + $(link) $(ldebug) TestBasic.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +test-simple.exe: TestSimple.obj $(LIB_PRODUCT) + $(link) $(ldebug) TestSimple.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +test-extract.exe: TestExtract.obj $(LIB_PRODUCT) + $(link) $(ldebug) TestExtract.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +test-twirl.exe: TestTwirl.obj $(LIB_PRODUCT) + $(link) $(ldebug) TestTwirl.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) + +clean: + -del *.obj + -del *.pdb + -del *.ilk + -del *.exp + -del exerciser.exe + -del imgconv.exe + -del launder.exe + -del test-basic.exe + -del test-simple.exe + -del test-extract.exe + -del test-twirl.exe + +Exerciser.obj: Exerciser.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h +ImgConv.obj: ImgConv.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h +Launder.obj: Launder.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h +TestBasic.obj: TestBasic.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h +TestSimple.obj: TestSimple.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h +TestExtract.obj: TestExtract.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h +TestTwirl.obj: TestTwirl.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h + diff --git a/nufxlib/samples/README-S.txt b/nufxlib/samples/README-S.txt new file mode 100644 index 0000000..3f432b2 --- /dev/null +++ b/nufxlib/samples/README-S.txt @@ -0,0 +1,119 @@ +NufxLib "samples" README + +This directory contains some test programs and useful sample code. + + +test-basic +========== + +Basic tests. Run this to verify that things are working. + + +exerciser +========= + +This program allows you to exercise all of NufxLib's basic functions. +Run it without arguments and hit "?" for a list of commands. + +If you think you have found a bug in NufxLib, you can use this to narrow +it down to a repeatable case. + + +imgconv +======= + +A 2IMG disk image converter. You can convert ".2MG" files to ShrinkIt +disk archives, and ShrinkIt disk archives to 2IMG format. imgconv uses +a creator type of "NFXL". + +You can use it like this: + + % imgconv file.shk file.2mg +or + % imgconv file.2mg file.shk + +It figures out what to do based on the filename. It will recognize ".sdk" +as a ShrinkIt archive. + +Limitations: works for DOS-ordered and ProDOS-ordered 2MG images, but +not for raw nibble images. Converting from .shk only works if the first +record in the archive is a disk image; you don't get to pick the one you +want from an archive with several in it. + + +launder +======= + +Run an archive through the laundry. This copies the entire contents of +an archive thread-by-thread, reconstructing it such that the data +matches the original even if the archive contents don't (e.g. records +are updated to version 3, files may be recompressed with LZW/2, option +lists are stripped out, etc). + +The basic usage is: + + % launder [-crfa] [-m method] infile.shk outfile.shk + +The flags are: + + -c Just copy data threads rather than recompressing them + -r Add threads in reverse order + -f Call NuFlush after every record + -a Call NuAbort after every record, then re-do the record and call NuFlush + -t Write to temp file, instead of writing directly into outfile.shk + +The "-m method" flag allows you to specify the compression method. Valid +values are sq (SQueeze), lzw1 (ShrinkIt LZW/1), lzw2 (ShrinkIt LZW/2), +lzc12 (12-bit UNIX "compress"), lzc16 (16-bit UNIX "compress"), deflate +(zlib deflate), and bzip2 (libbz2 compression). The default is lzw2. + +If you use the "-c" flag with an archive created by P8 ShrinkIt or NuLib, +the laundered archive may have CRC failures when you try to extract +from it. This is because "launder" creates version 3 records, which +are expected to have a valid CRC in the thread header. The only way +to compute the CRC is to uncompress the data, which "launder" doesn't +do when "-c" is set. The data itself is fine, it's just the thread CRC +that's wrong (if the data were hosed, the LZW/1 CRC would be bad too). +"launder" will issue a warning when it detects this situation. + +By default, launder will try to keep the entire archive in memory and flush +all of the operations at the end. If you find that you're running out +of memory on very large archives, you can reduce the memory requirements +by specifying the "-f" flag. + + +test-simple +=========== + +Simple test program. Give it the name of an archive, and it will display +the contents. + + +test-extract +============ + +Simple test program. Give it the name of an archive, and it will write +all filename threads into "out.buf", "out.fp", and "out.file" using three +different kinds of NuDataSinks. + + +test-twirl +========== + +Like "launder", but not meant to be useful. This recompresses the file "in +place", deleting and adding threads within existing records several times. +The changes are periodically flushed, but the archive is never closed. +The goal is to test repeated updates on an open archive. + +The CRC verification mechanism will fail on archives created with ProDOS +8 ShrinkIt. The older "version 1" records didn't have CRCs in the thread +headers, so you will get a series of messages that look like this: + +ERROR: CRC mismatch: 0 old=0x0000 new=0x681b +ERROR: CRC mismatch: 1 old=0x0000 new=0x5570 +ERROR: CRC mismatch: 2 old=0x0000 new=0x4ec5 + +This will leave the original archive alone, making a copy of it named +"TwirlCopy678" in the current directory. It overwrites its temp file, +"TwirlTmp789", without prompting. + diff --git a/nufxlib/samples/TestBasic.c b/nufxlib/samples/TestBasic.c new file mode 100644 index 0000000..44c85c3 --- /dev/null +++ b/nufxlib/samples/TestBasic.c @@ -0,0 +1,1178 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * Test basic features of the library. Run this without arguments. + */ +#include +#include +#include "NufxLib.h" +#include "Common.h" + +#define kTestArchive "nlbt.shk" +#define kTestTempFile "nlbt.tmp" + +#define kNumEntries 3 /* how many records are we going to add? */ +#define kTestEntryBytes "bytes" +#define kTestEntryBytesUPPER "BYTES" +#define kTestEntryEnglish "English" +#define kTestEntryLong "three|is a fairly long filename, complete with" \ + "punctuation and other nifty/bad stuff" +#define kLocalFssep '|' + +/* + * Globals. + */ +char gSuppressError = false; +#define FAIL_OK gSuppressError = true; +#define FAIL_BAD gSuppressError = false; + + +/* + * =========================================================================== + * Helper functions + * =========================================================================== + */ + +/* + * Get a single character of input from the user. + */ +static char +TGetReplyChar(char defaultReply) +{ + char tmpBuf[32]; + + if (fgets(tmpBuf, sizeof(tmpBuf), stdin) == nil) + return defaultReply; + if (tmpBuf[0] == '\n' || tmpBuf[0] == '\r') + return defaultReply; + + return tmpBuf[0]; +} + +NuError +AddSimpleRecord(NuArchive* pArchive, const char* filename, + NuRecordIdx* pRecordIdx) +{ + NuFileDetails fileDetails; + + memset(&fileDetails, 0, sizeof(fileDetails)); + fileDetails.storageName = filename; + fileDetails.fileSysInfo = kLocalFssep; + fileDetails.access = kNuAccessUnlocked; + + return NuAddRecord(pArchive, &fileDetails, pRecordIdx); +} + + +/* + * Display error messages... or not. + */ +NuResult +ErrorMessageHandler(NuArchive* pArchive, void* vErrorMessage) +{ + const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; + + if (gSuppressError) + return kNuOK; + + if (pErrorMessage->isDebug) { + fprintf(stderr, "%sNufxLib says: [%s:%d %s] %s\n", + pArchive == nil ? "GLOBAL>" : "", + pErrorMessage->file, pErrorMessage->line, pErrorMessage->function, + pErrorMessage->message); + } else { + fprintf(stderr, "%sNufxLib says: %s\n", + pArchive == nil ? "GLOBAL>" : "", + pErrorMessage->message); + } + + return kNuOK; +} + +/* + * This gets called when a buffer DataSource is no longer needed. + */ +NuResult +FreeCallback(NuArchive* pArchive, void* args) +{ + free(args); + return kNuOK; +} + + +/* + * =========================================================================== + * Tests + * =========================================================================== + */ + +/* + * Make sure the flags that control how we open the file work right, + * and verify that we handle existing zero-byte archive files correctly. + */ +int +Test_OpenFlags(void) +{ + NuError err; + FILE* fp = nil; + NuArchive* pArchive = nil; + + printf("... open zero-byte existing\n"); + fp = fopen(kTestArchive, kNuFileOpenWriteTrunc); + if (fp == nil) { + perror("fopen kTestArchive"); + goto failed; + } + fclose(fp); + fp = nil; + + FAIL_OK; + err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat|kNuOpenExcl, + &pArchive); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: file opened when it shouldn't have\n"); + goto failed; + } + + err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: file didn't open when it should have\n"); + goto failed; + } + + err = NuClose(pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: close failed\n"); + goto failed; + } + pArchive = nil; + + if (access(kTestArchive, F_OK) == 0) { + fprintf(stderr, "ERROR: archive should have been removed but wasn't\n"); + goto failed; + } + + return 0; + +failed: + if (pArchive != nil) { + NuAbort(pArchive); + NuClose(pArchive); + } + return -1; +} + + +/* + * Add some files to the archive. These will be used by later tests. + */ +int +Test_AddStuff(NuArchive* pArchive) +{ + NuError err; + uchar* buf = nil; + NuDataSource* pDataSource = nil; + NuRecordIdx recordIdx; + long status; + int i; + static const char* testMsg = + "This is a nice test message that has linefeeds in it so we can\n" + "see if the line conversion stuff is actually doing anything at\n" + "all. It's certainly nice to know that everything works the way\n" + "it's supposed to, which I suppose is why we have this nifty test\n" + "program available. It sure would be nice if everybody tested\n" + "there code, but where would Microsoft be without endless upgrades\n" + "and service packs? Bugs are what America was built on, and\n" + "anybody who says otherwise is a pinko commie lowlife. Verily.\n"; + + printf("... add 'bytes' record\n"); + buf = malloc(131072); + if (buf == nil) + goto failed; + for (i = 0; i < 131072; i++) + *(buf+i) = i & 0xff; + + FAIL_OK; + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + 0, nil, 0, 131072, FreeCallback, &pDataSource); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: that should've failed!\n"); + goto failed; + } + + /* + * Create a data source for the big batch of bytes. + */ + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + 0, buf, 0, 131072, FreeCallback, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: 'bytes' data source create failed (err=%d)\n", err); + goto failed; + } + buf = nil; /* now owned by library */ + + err = AddSimpleRecord(pArchive, kTestEntryBytes, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: 'bytes' record failed (err=%d)\n", err); + goto failed; + } + + err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, + nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: 'bytes' thread add failed (err=%d)\n", err); + goto failed; + } + pDataSource = nil; /* now owned by library */ + + + /* + * Create a data source for our lovely text message. + */ + printf("... add 'English' record\n"); + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + 0, (const uchar*)testMsg, 0, strlen(testMsg), nil, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: 'English' source create failed (err=%d)\n", err); + goto failed; + } + + FAIL_OK; + err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, + nil); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: 'English' add should've conflicted!\n"); + goto failed; + } + + FAIL_OK; + err = AddSimpleRecord(pArchive, kTestEntryBytes, &recordIdx); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: duplicates not allowed, should've failed\n"); + goto failed; + } + + err = AddSimpleRecord(pArchive, kTestEntryEnglish, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: 'English' record failed (err=%d)\n", err); + goto failed; + } + + err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, + nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: 'English' thread add failed (err=%d)\n", err); + goto failed; + } + pDataSource = nil; /* now owned by library */ + + + /* + * Create an empty file with a rather non-empty name. + */ + printf("... add 'long' record\n"); + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + 0, nil, 0, 0, nil, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: 'English' source create failed (err=%d)\n", err); + goto failed; + } + + err = AddSimpleRecord(pArchive, kTestEntryLong, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: 'long' record failed (err=%d)\n", err); + goto failed; + } + + err = NuAddThread(pArchive, recordIdx, kNuThreadIDRsrcFork, pDataSource, + nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: 'long' thread add failed (err=%d)\n", err); + goto failed; + } + pDataSource = nil; /* now owned by library */ + + + /* + * Flush changes. + */ + err = NuFlush(pArchive, &status); + if (err != kNuErrNone) { + fprintf(stderr,"ERROR: couldn't flush after add (err=%d, status=%ld)\n", + err, status); + goto failed; + } + + /* + * Flush again; should succeed since it doesn't have to do anything. + */ + err = NuFlush(pArchive, &status); + if (err != kNuErrNone) { + fprintf(stderr,"ERROR: second add flush failed (err=%d, status=%ld)\n", + err, status); + goto failed; + } + + return 0; +failed: + if (pDataSource != nil) + NuFreeDataSource(pDataSource); + if (buf != nil) + free(buf); + return -1; +} + + +/* + * Make sure that what we're seeing makes sense. + */ +NuResult +TestContentsCallback(NuArchive* pArchive, void* vpRecord) +{ + const NuRecord* pRecord = (NuRecord*) vpRecord; + + if (strcmp(pRecord->filename, kTestEntryBytes) == 0 || + strcmp(pRecord->filename, kTestEntryEnglish) == 0 || + strcmp(pRecord->filename, kTestEntryLong) == 0) + { + return kNuOK; + } + + fprintf(stderr, "ERROR: found mystery entry '%s'\n", pRecord->filename); + return kNuAbort; +} + + +/* + * Verify that the contents look about right. + */ +int +Test_Contents(NuArchive* pArchive) +{ + NuError err; + long posn; + NuRecordIdx recordIdx; + const NuRecord* pRecord; + int cc; + + /* + * First, do it with a callback. + */ + err = NuContents(pArchive, TestContentsCallback); + if (err != kNuErrNone) + goto failed; + + /* + * Now step through the records with get-by-position and verify that + * they're in the expected order. + */ + for (posn = 0; posn < kNumEntries; posn++) { + err = NuGetRecordIdxByPosition(pArchive, posn, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", + posn, err); + goto failed; + } + + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record index %ld (err=%d)\n", + recordIdx, err); + goto failed; + } + assert(pRecord != nil); + + switch (posn) { + case 0: + cc = strcmp(pRecord->filename, kTestEntryBytes); + break; + case 1: + cc = strcmp(pRecord->filename, kTestEntryEnglish); + break; + case 2: + cc = strcmp(pRecord->filename, kTestEntryLong); + if (!cc) + cc = !(pRecord->recStorageType == kNuStorageExtended); + break; + default: + fprintf(stderr, "ERROR: somebody forgot to put a case here (%ld)\n", + posn); + cc = -1; + } + + if (cc) { + fprintf(stderr, "ERROR: got '%s' for %ld (%ld), not expected\n", + pRecord->filename, posn, recordIdx); + goto failed; + } + } + + /* + * Read one more past the end, should fail. + */ + FAIL_OK; + err = NuGetRecordIdxByPosition(pArchive, posn, &recordIdx); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: too many records (%ld was ok)\n", posn); + goto failed; + } + + return 0; +failed: + return -1; +} + + +/* + * Selection callback filter for "test". This gets called once per record. + */ +NuResult +VerifySelectionCallback(NuArchive* pArchive, void* vpProposal) +{ + NuError err; + const NuSelectionProposal* pProposal = vpProposal; + long count; + + if (pProposal->pRecord == nil || pProposal->pThread == nil || + pProposal->pRecord->filename == nil) + { + fprintf(stderr, "ERROR: unexpected nil in proposal\n"); + goto failed; + } + + err = NuGetExtraData(pArchive, (void**) &count); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get extra data (err=%d)\n", err); + goto failed; + } + + count++; + + err = NuSetExtraData(pArchive, (void*) count); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to inc extra data (err=%d)\n", err); + goto failed; + } + + return kNuOK; +failed: + return kNuAbort; +} + +/* + * Verify the archive contents. + */ +int +Test_Verify(NuArchive* pArchive) +{ + NuError err; + long count; + + printf("... verifying CRCs\n"); + + if (NuSetSelectionFilter(pArchive, VerifySelectionCallback) == + kNuInvalidCallback) + { + fprintf(stderr, "ERROR: unable to set selection filter\n"); + goto failed; + } + + err = NuSetExtraData(pArchive, (void*) 0); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to set extra data (err=%d)\n", err); + goto failed; + } + + err = NuTest(pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: verify failed (err=%d)\n", err); + goto failed; + } + + err = NuGetExtraData(pArchive, (void**) &count); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: last extra data get failed (err=%d)\n", err); + goto failed; + } + + if (count != kNumEntries) { + fprintf(stderr, "ERROR: verified %ld when expecting %d\n", count, + kNumEntries); + goto failed; + } + + return 0; +failed: + return -1; +} + +/* + * Extract stuff. + */ +int +Test_Extract(NuArchive* pArchive) +{ + NuError err; + NuRecordIdx recordIdx; + const NuRecord* pRecord; + const NuThread* pThread; + NuDataSink* pDataSink = nil; + uchar* buf = nil; + + printf("... extracting files\n"); + + /* + * Tell it the current system uses CRLF, so it'll bloat up when we do + * a text conversion. + */ + err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCRLF); + + /* + * Extract "bytes". + */ + err = NuGetRecordIdxByName(pArchive, kTestEntryBytesUPPER, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", kTestEntryBytes, + err); + goto failed; + } + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record index %ld (err=%d)\n", + recordIdx, err); + goto failed; + } + assert(pRecord != nil); + + /* we're not using ShrinkIt compat mode, so there should not be a comment */ + pThread = NuGetThread(pRecord, 1); + assert(pThread != nil); + if (NuGetThreadID(pThread) != kNuThreadIDDataFork) { + fprintf(stderr, "ERROR: 'bytes' had unexpected threadID 0x%08lx\n", + NuGetThreadID(pThread)); + goto failed; + } + + buf = malloc(pThread->actualThreadEOF); + if (buf == nil) { + fprintf(stderr, "ERROR: malloc(%ld) failed\n",pThread->actualThreadEOF); + goto failed; + } + + /* + * Try to extract it with text conversion off. + */ + err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); + goto failed; + } + + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't extract 'bytes' (off) (err=%d)\n", + err); + goto failed; + } + NuFreeDataSink(pDataSink); + pDataSink = nil; + + /* + * Try to extract with "on" conversion, which should fail because the + * buffer is too small. + */ + err = NuCreateDataSinkForBuffer(true, kNuConvertOn, buf, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); + goto failed; + } + + FAIL_OK; + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: managed to extract bloated 'bytes'?\n"); + goto failed; + } + NuFreeDataSink(pDataSink); + pDataSink = nil; + + /* + * Try to extract with "auto" conversion, which should conclude that + * the input is text and not try to convert. + */ + err = NuCreateDataSinkForBuffer(true, kNuConvertAuto, buf, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); + goto failed; + } + + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't extract 'bytes' (auto) (err=%d)\n", + err); + goto failed; + } + NuFreeDataSink(pDataSink); + pDataSink = nil; + + + + free(buf); + buf = nil; + + + + /* + * Extract "English". + */ + err = NuGetRecordIdxByName(pArchive, kTestEntryEnglish, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", + kTestEntryEnglish, err); + goto failed; + } + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record index %ld (err=%d)\n", + recordIdx, err); + goto failed; + } + assert(pRecord != nil); + + /* we're not using ShrinkIt compat mode, so there should not be a comment */ + pThread = NuGetThread(pRecord, 1); + assert(pThread != nil); + if (NuGetThreadID(pThread) != kNuThreadIDDataFork) { + fprintf(stderr, "ERROR: 'English' had unexpected threadID 0x%08lx\n", + NuGetThreadID(pThread)); + goto failed; + } + + buf = malloc(pThread->actualThreadEOF); + if (buf == nil) { + fprintf(stderr, "ERROR: malloc(%ld) failed\n",pThread->actualThreadEOF); + goto failed; + } + + /* + * Try to extract it with text conversion off. + */ + err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); + goto failed; + } + + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't extract 'bytes' (off) (err=%d)\n", + err); + goto failed; + } + NuFreeDataSink(pDataSink); + pDataSink = nil; + + /* + * Try to extract with "auto" conversion, which should fail because the + * buffer is too small, and the input looks like text. + */ + err = NuCreateDataSinkForBuffer(true, kNuConvertAuto, buf, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); + goto failed; + } + + FAIL_OK; + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: managed to extract bloated 'English'?\n"); + goto failed; + } + NuFreeDataSink(pDataSink); + pDataSink = nil; + + + + /*Free(buf);*/ + /*buf = nil;*/ + + + + /* + * Extract "long" (which is zero bytes). + */ + err = NuGetRecordIdxByName(pArchive, kTestEntryLong, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", + kTestEntryLong, err); + goto failed; + } + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record index %ld (err=%d)\n", + recordIdx, err); + goto failed; + } + assert(pRecord != nil); + + /* we're not using ShrinkIt compat mode, so there should not be a comment */ + pThread = NuGetThread(pRecord, 1); + assert(pThread != nil); + if (NuGetThreadID(pThread) != kNuThreadIDRsrcFork) { + fprintf(stderr, "ERROR: 'Long' had unexpected threadID 0x%08lx\n", + NuGetThreadID(pThread)); + goto failed; + } + + /* + * Try it with text conversion on; shouldn't matter. + */ + err = NuCreateDataSinkForBuffer(true, kNuConvertOn, buf, + 1, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); + goto failed; + } + + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't extract 'Long' (off) (err=%d)\n", + err); + goto failed; + } + NuFreeDataSink(pDataSink); + pDataSink = nil; + + + + free(buf); + buf = nil; + + + + return 0; +failed: + if (buf != nil) + free(buf); + if (pDataSink != nil) + (void) NuFreeDataSink(pDataSink); + return -1; +} + +/* + * Delete the first and last records. Does *not* flush the archive. + */ +int +Test_Delete(NuArchive* pArchive) +{ + NuError err; + NuRecordIdx recordIdx; + const NuRecord* pRecord; + const NuThread* pThread = nil; + long count; + int idx; + + printf("... deleting first and last\n"); + + /* + * Delete all threads from the first record ("bytes"). + */ + err = NuGetRecordIdxByPosition(pArchive, 0, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't find #%d (err=%d)\n", 0, err); + goto failed; + } + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record index %ld (err=%d)\n", + recordIdx, err); + goto failed; + } + assert(pRecord != nil); + assert(pRecord->recTotalThreads > 0); + + for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { + pThread = NuGetThread(pRecord, idx); + assert(pThread != nil); + + err = NuDeleteThread(pArchive, pThread->threadIdx); + if (err != kNuErrNone) { + fprintf(stderr, + "ERROR: couldn't delete thread #%d (%ld) (err=%d)\n", + idx, recordIdx, err); + goto failed; + } + } + + /* try to re-delete the same thread */ + assert(pThread != nil); + FAIL_OK; + err = NuDeleteThread(pArchive, pThread->threadIdx); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, "ERROR: allowed to re-delete thread (%ld) (err=%d)\n", + recordIdx, err); + goto failed; + } + + /* try to delete the modified record */ + FAIL_OK; + err = NuDeleteRecord(pArchive, recordIdx); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, + "ERROR: able to delete modified record (%ld) (err=%d)\n", + recordIdx, err); + goto failed; + } + + /* + * Make sure the attr hasn't been updated yet. + */ + err = NuGetAttr(pArchive, kNuAttrNumRecords, (unsigned long*) &count); + if (count != kNumEntries) { + fprintf(stderr, "ERROR: kNuAttrNumRecords %ld vs %d\n", + count, kNumEntries); + goto failed; + } + + /* + * Delete the last record ("long"). + */ + err = NuGetRecordIdxByPosition(pArchive, kNumEntries-1, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't find #%d (err=%d)\n", 0, err); + goto failed; + } + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record index %ld (err=%d)\n", + recordIdx, err); + goto failed; + } + assert(pRecord != nil); + + /* grab the first thread before we whack the record */ + pThread = NuGetThread(pRecord, 0); + assert(pThread != nil); + + err = NuDeleteRecord(pArchive, recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to delete record #%d (%ld) (err=%d)\n", + kNumEntries-1, recordIdx, err); + goto failed; + } + + /* try to delete a thread from the deleted record */ + FAIL_OK; + err = NuDeleteThread(pArchive, pThread->threadIdx); + FAIL_BAD; + if (err == kNuErrNone) { + fprintf(stderr, + "ERROR: allowed to delete from deleted (%ld) (err=%d)\n", + pThread->threadIdx, err); + goto failed; + } + + return 0; +failed: + return -1; +} + + +/* + * Verify that the count in the master header has been updated. + */ +int +Test_MasterCount(NuArchive* pArchive, long expected) +{ + NuError err; + const NuMasterHeader* pMasterHeader; + + printf("... checking master count\n"); + + err = NuGetMasterHeader(pArchive, &pMasterHeader); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err); + goto failed; + } + + if (pMasterHeader->mhTotalRecords != (ulong)expected) { + fprintf(stderr, "ERROR: unexpected MH count (%ld vs %ld)\n", + pMasterHeader->mhTotalRecords, expected); + goto failed; + } + + return 0; +failed: + return -1; +} + + +/* + * Run some tests. + * + * Returns 0 on success, -1 on error. + */ +int +DoTests(void) +{ + NuError err; + NuArchive* pArchive = nil; + long status; + int cc, result = 0; + char answer; + + /* + * Make sure we're starting with a clean slate. + */ + if (access(kTestArchive, F_OK) == 0) { + printf("Test archive '%s' exists, remove (y/n)? ", kTestArchive); + fflush(stdout); + answer = TGetReplyChar('n'); + if (tolower(answer) != 'y') + goto failed; + cc = unlink(kTestArchive); + if (cc < 0) { + perror("unlink kTestArchive"); + goto failed; + } + } + if (access(kTestTempFile, F_OK) == 0) { + printf("Test temp file '%s' exists, remove (y/n)? ", kTestTempFile); + fflush(stdout); + answer = TGetReplyChar('n'); + if (tolower(answer) != 'y') + goto failed; + cc = unlink(kTestTempFile); + if (cc < 0) { + perror("unlink kTestTempFile"); + goto failed; + } + } + + /* + * Test some of the open flags. + */ + if (Test_OpenFlags() != 0) + goto failed; + + /* + * Create a new archive to play with. + */ + err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat|kNuOpenExcl, + &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: NuOpenRW failed (err=%d)\n", err); + goto failed; + } + if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == + kNuInvalidCallback) + { + fprintf(stderr, "ERROR: couldn't set message handler\n"); + goto failed; + } + + /* + * Add some test entries. + */ + if (Test_AddStuff(pArchive) != 0) + goto failed; + + /* + * Check the archive contents. + */ + printf("... checking contents\n"); + if (Test_Contents(pArchive) != 0) + goto failed; + + /* + * Reopen it read-only. + */ + printf("... reopening archive read-only\n"); + err = NuClose(pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: mid NuClose failed (err=%d)\n", err); + goto failed; + } + pArchive = nil; + + err = NuOpenRO(kTestArchive, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: NuOpenRO failed (err=%d)\n", err); + goto failed; + } + if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == + kNuInvalidCallback) + { + fprintf(stderr, "ERROR: couldn't set message handler\n"); + goto failed; + } + + /* + * Make sure the contents are still what we expect. + */ + printf("... checking contents\n"); + if (Test_Contents(pArchive) != 0) + goto failed; + + /* + * Verify the archive contents. + */ + if (Test_Verify(pArchive) != 0) + goto failed; + + /* + * Extract the files. + */ + if (Test_Extract(pArchive) != 0) + goto failed; + + /* + * Reopen it read-write. + */ + printf("... reopening archive read-write\n"); + err = NuClose(pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: late NuClose failed (err=%d)\n", err); + goto failed; + } + pArchive = nil; + + err = NuOpenRW(kTestArchive, kTestTempFile, 0, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: re-NuOpenRW failed (err=%d)\n", err); + goto failed; + } + if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == + kNuInvalidCallback) + { + fprintf(stderr, "ERROR: couldn't set message handler\n"); + goto failed; + } + + /* + * Contents shouldn't have changed. + */ + printf("... checking contents\n"); + if (Test_Contents(pArchive) != 0) + goto failed; + + /* + * Test deletion. + */ + if (Test_Delete(pArchive) != 0) + goto failed; + + /* + * Abort the changes and verify that nothing has changed. + */ + printf("... aborting changes\n"); + err = NuAbort(pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: abort failed (err=%d)\n", err); + goto failed; + } + + printf("... checking contents\n"); + if (Test_Contents(pArchive) != 0) + goto failed; + + /* + * Delete them again. + */ + if (Test_Delete(pArchive) != 0) + goto failed; + + /* + * Flush the deletions. This should remove the first and last records. + */ + err = NuFlush(pArchive, &status); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: flush failed (err=%d, status=%ld)\n", + err, status); + goto failed; + } + + /* + * Check count in master header. + */ + if (Test_MasterCount(pArchive, kNumEntries-2) != 0) + goto failed; + + /* + * That's all, folks... + */ + NuClose(pArchive); + pArchive = nil; + + printf("... removing '%s'\n", kTestArchive); + cc = unlink(kTestArchive); + if (cc < 0) { + perror("unlink kTestArchive"); + goto failed; + } + + +leave: + if (pArchive != nil) { + NuAbort(pArchive); + NuClose(pArchive); + } + return result; + +failed: + result = -1; + goto leave; +} + + +/* + * Crank away. + */ +int +main(void) +{ + long major, minor, bug; + const char* pBuildDate; + const char* pBuildFlags; + int cc; + + (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, &pBuildFlags); + printf("Using NuFX library v%ld.%ld.%ld, built on or after\n" + " %s with [%s]\n\n", + major, minor, bug, pBuildDate, pBuildFlags); + + if (NuSetGlobalErrorMessageHandler(ErrorMessageHandler) == + kNuInvalidCallback) + { + fprintf(stderr, "ERROR: can't set the global message handler"); + exit(1); + } + + printf("... starting tests\n"); + + cc = DoTests(); + + printf("... tests ended, %s\n", cc == 0 ? "SUCCESS" : "FAILURE"); + exit(cc != 0); +} + diff --git a/nufxlib/samples/TestExtract.c b/nufxlib/samples/TestExtract.c new file mode 100644 index 0000000..302c631 --- /dev/null +++ b/nufxlib/samples/TestExtract.c @@ -0,0 +1,492 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * Test extraction of individual threads in various ways. The net result + * of this is three files (out.file, out.fp, out.buf) that contain the + * result of writing all filenames in an archive to the same data sink. + * + * This gathers up information on the contents of the archive via a + * callback, and then emits all of the data at once. + * + * (This was originally written in C++, and converted to C after I repented.) + */ +#include +#include +#include +#include +#include "NufxLib.h" +#include "Common.h" + + +/*#define false 0*/ +/*#define true (!false)*/ + +#define kHappySize 2408 + + +/* + * =========================================================================== + * ArchiveRecord + * =========================================================================== + */ + +/* + * Track an archive record. + */ +typedef struct ArchiveRecord { + char* filename; + NuRecordIdx recordIdx; + + long numThreads; + NuThread* pThreads; + + struct ArchiveRecord* pNext; +} ArchiveRecord; + + +/* + * Alloc a new ArchiveRecord. + */ +ArchiveRecord* +ArchiveRecord_New(const NuRecord* pRecord) +{ + ArchiveRecord* pArcRec = nil; + + pArcRec = malloc(sizeof(*pArcRec)); + if (pArcRec == nil) + return nil; + + if (pRecord->filename == nil) + pArcRec->filename = strdup(""); + else + pArcRec->filename = strdup((char*)pRecord->filename); + + pArcRec->recordIdx = pRecord->recordIdx; + pArcRec->numThreads = NuRecordGetNumThreads(pRecord); + (void) NuRecordCopyThreads(pRecord, &pArcRec->pThreads); + + pArcRec->pNext = nil; + + return pArcRec; +} + +/* + * Free up an ArchiveRecord. + */ +void +ArchiveRecord_Free(ArchiveRecord* pArcRec) +{ + if (pArcRec == nil) + return; + + if (pArcRec->filename != nil) + free(pArcRec->filename); + if (pArcRec->pThreads != nil) + free(pArcRec->pThreads); + free(pArcRec); +} + +/* + * Find a thread with a matching NuThreadID. + */ +const NuThread* +ArchiveRecord_FindThreadByID(const ArchiveRecord* pArcRec, NuThreadID threadID) +{ + const NuThread* pThread; + int i; + + for (i = 0; i < pArcRec->numThreads; i++) { + pThread = NuThreadGetByIdx(pArcRec->pThreads, i); + if (NuGetThreadID(pThread) == threadID) + return pThread; + } + + return nil; +} + + +const char* +ArchiveRecord_GetFilename(const ArchiveRecord* pArcRec) +{ + return pArcRec->filename; +} + +NuRecordIdx +ArchiveRecord_GetRecordIdx(const ArchiveRecord* pArcRec) +{ + return pArcRec->recordIdx; +} + +long +ArchiveRecord_GetNumThreads(const ArchiveRecord* pArcRec) +{ + return pArcRec->numThreads; +} + +const NuThread* +ArchiveRecord_GetThread(const ArchiveRecord* pArcRec, int idx) +{ + if (idx < 0 || idx >= pArcRec->numThreads) + return nil; + return NuThreadGetByIdx(pArcRec->pThreads, idx); +} + +void +ArchiveRecord_SetNext(ArchiveRecord* pArcRec, ArchiveRecord* pNextRec) +{ + pArcRec->pNext = pNextRec; +} + +ArchiveRecord* +ArchiveRecord_GetNext(const ArchiveRecord* pArcRec) +{ + return pArcRec->pNext; +} + + +/* + * =========================================================================== + * ArchiveData + * =========================================================================== + */ + +/* + * A collection of records. + */ +typedef struct ArchiveData { + long numRecords; + ArchiveRecord* pRecordHead; + ArchiveRecord* pRecordTail; +} ArchiveData; + + +ArchiveData* +ArchiveData_New(void) +{ + ArchiveData* pArcData; + + pArcData = malloc(sizeof(*pArcData)); + if (pArcData == nil) + return nil; + + pArcData->numRecords = 0; + pArcData->pRecordHead = pArcData->pRecordTail = nil; + + return pArcData; +} + +void +ArchiveData_Free(ArchiveData* pArcData) +{ + ArchiveRecord* pNext; + + if (pArcData == nil) + return; + + printf("*** Deleting %ld records!\n", pArcData->numRecords); + while (pArcData->pRecordHead != nil) { + pNext = ArchiveRecord_GetNext(pArcData->pRecordHead); + ArchiveRecord_Free(pArcData->pRecordHead); + pArcData->pRecordHead = pNext; + } + + free(pArcData); +} + + +ArchiveRecord* +ArchiveData_GetRecordHead(const ArchiveData* pArcData) +{ + return pArcData->pRecordHead; +} + + +/* add an ArchiveRecord to the list pointed at by ArchiveData */ +void +ArchiveData_AddRecord(ArchiveData* pArcData, ArchiveRecord* pRecord) +{ + assert(pRecord != nil); + assert((pArcData->pRecordHead == nil && pArcData->pRecordTail == nil) || + (pArcData->pRecordHead != nil && pArcData->pRecordTail != nil)); + + if (pArcData->pRecordHead == nil) { + /* first */ + pArcData->pRecordHead = pArcData->pRecordTail = pRecord; + } else { + /* not first, add to end */ + ArchiveRecord_SetNext(pArcData->pRecordTail, pRecord); + pArcData->pRecordTail = pRecord; + } + + pArcData->numRecords++; +} + +/* dump the contents of the ArchiveData to stdout */ +void +ArchiveData_DumpContents(const ArchiveData* pArcData) +{ + ArchiveRecord* pArcRec; + + pArcRec = pArcData->pRecordHead; + while (pArcRec != nil) { + const NuThread* pThread; + int i, count; + + printf("%5ld '%s'\n", + ArchiveRecord_GetRecordIdx(pArcRec), + ArchiveRecord_GetFilename(pArcRec)); + + count = ArchiveRecord_GetNumThreads(pArcRec); + for (i = 0; i < count; i++) { + pThread = ArchiveRecord_GetThread(pArcRec, i); + printf(" %5ld 0x%04x 0x%04x\n", pThread->threadIdx, + pThread->thThreadClass, pThread->thThreadKind); + } + + pArcRec = ArchiveRecord_GetNext(pArcRec); + } +} + + +/* + * =========================================================================== + * Main stuff + * =========================================================================== + */ + +/* + * Callback function to collect archive information. + */ +NuResult +GatherContents(NuArchive* pArchive, void* vpRecord) +{ + NuRecord* pRecord = (NuRecord*) vpRecord; + ArchiveData* pArchiveData = nil; + ArchiveRecord* pArchiveRecord = ArchiveRecord_New(pRecord); + + NuGetExtraData(pArchive, (void**)&pArchiveData); + assert(pArchiveData != nil); + + printf("*** Filename = '%s'\n", + pRecord->filename == nil ? "":(const char*)pRecord->filename); + + ArchiveData_AddRecord(pArchiveData, pArchiveRecord); + + return kNuOK; +} + + +/* + * Copy the filename thread from every record to "pDataSink". + */ +NuError +ReadAllFilenameThreads(NuArchive* pArchive, ArchiveData* pArchiveData, + NuDataSink* pDataSink) +{ + NuError err = kNuErrNone; + ArchiveRecord* pArchiveRecord; + const NuThread* pThread; + + pArchiveRecord = ArchiveData_GetRecordHead(pArchiveData); + while (pArchiveRecord != nil) { + pThread = ArchiveRecord_FindThreadByID(pArchiveRecord, + kNuThreadIDFilename); + if (pThread != nil) { + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "*** Extract failed (%d)\n", err); + goto bail; + } + } + pArchiveRecord = ArchiveRecord_GetNext(pArchiveRecord); + } + +bail: + return err; +} + + +/* extract every filename thread into a single file, overwriting each time */ +NuError +ExtractToFile(NuArchive* pArchive, ArchiveData* pArchiveData) +{ + NuError err; + NuDataSink* pDataSink = nil; + + err = NuCreateDataSinkForFile(true, kNuConvertOff, "out.file", PATH_SEP, + &pDataSink); + if (err != kNuErrNone) + goto bail; + + err = NuSetValue(pArchive, kNuValueHandleExisting, kNuAlwaysOverwrite); + if (err != kNuErrNone) + goto bail; + + err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink); + if (err != kNuErrNone) + goto bail; + +bail: + (void) NuFreeDataSink(pDataSink); + if (err == kNuErrNone) + printf("*** File write complete\n"); + return err; +} + +/* extract every filename thread into a FILE*, appending */ +NuError +ExtractToFP(NuArchive* pArchive, ArchiveData* pArchiveData) +{ + NuError err; + FILE* fp = nil; + NuDataSink* pDataSink = nil; + + if ((fp = fopen("out.fp", kNuFileOpenWriteTrunc)) == nil) + return kNuErrFileOpen; + + err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink); + if (err != kNuErrNone) + goto bail; + + err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink); + if (err != kNuErrNone) + goto bail; + +bail: + (void) NuFreeDataSink(pDataSink); + if (fp != nil) + fclose(fp); + if (err == kNuErrNone) + printf("*** FP write complete\n"); + return err; +} + +/* extract every filename thread into a buffer, advancing as we go */ +NuError +ExtractToBuffer(NuArchive* pArchive, ArchiveData* pArchiveData) +{ + NuError err; + unsigned char buffer[kHappySize]; + NuDataSink* pDataSink = nil; + unsigned long count; + + err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer, kHappySize, + &pDataSink); + if (err != kNuErrNone) + goto bail; + + err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink); + if (err != kNuErrNone) { + if (err == kNuErrBufferOverrun) + fprintf(stderr, "*** Hey, buffer wasn't big enough!\n"); + goto bail; + } + + /* write the buffer to a file */ + (void) NuDataSinkGetOutCount(pDataSink, &count); + if (count > 0) { + FILE* fp; + if ((fp = fopen("out.buf", kNuFileOpenWriteTrunc)) != nil) { + + printf("*** Writing %ld bytes\n", count); + if (fwrite(buffer, count, 1, fp) != 1) + err = kNuErrFileWrite; + fclose(fp); + } + } else { + printf("*** No data found!\n"); + } + +bail: + (void) NuFreeDataSink(pDataSink); + return err; +} + + +/* + * Do file stuff. + */ +int +DoFileStuff(const char* filename) +{ + NuError err; + NuArchive* pArchive = nil; + ArchiveData* pArchiveData = ArchiveData_New(); + + err = NuOpenRO(filename, &pArchive); + if (err != kNuErrNone) + goto bail; + + NuSetExtraData(pArchive, pArchiveData); + + printf("*** Gathering contents!\n"); + err = NuContents(pArchive, GatherContents); + if (err != kNuErrNone) + goto bail; + + printf("*** Dumping contents!\n"); + ArchiveData_DumpContents(pArchiveData); + + err = ExtractToFile(pArchive, pArchiveData); + if (err != kNuErrNone) + goto bail; + err = ExtractToFP(pArchive, pArchiveData); + if (err != kNuErrNone) + goto bail; + err = ExtractToBuffer(pArchive, pArchiveData); + if (err != kNuErrNone) + goto bail; + +bail: + if (err != kNuErrNone) + fprintf(stderr, "*** ERROR: got error %d\n", err); + + if (pArchive != nil) { + NuError err2 = NuClose(pArchive); + if (err == kNuErrNone && err2 != kNuErrNone) + err = err2; + } + + ArchiveData_Free(pArchiveData); + + return err; +} + + +/* + * Grab the name of an archive to read. If no name was provided, use stdin. + */ +int +main(int argc, char** argv) +{ + long major, minor, bug; + const char* pBuildDate; + FILE* infp = nil; + int cc; + + (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil); + printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n", + major, minor, bug, pBuildDate); + + if (argc == 2) { + infp = fopen(argv[1], kNuFileOpenReadOnly); + if (infp == nil) { + perror("fopen failed"); + exit(1); + } + } else { + fprintf(stderr, "ERROR: you have to specify a filename\n"); + exit(2); + } + + cc = DoFileStuff(argv[1]); + + if (infp != nil) + fclose(infp); + + exit(cc != 0); +} + diff --git a/nufxlib/samples/TestSimple.c b/nufxlib/samples/TestSimple.c new file mode 100644 index 0000000..45e51df --- /dev/null +++ b/nufxlib/samples/TestSimple.c @@ -0,0 +1,105 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * Simple test program. Opens an archive, dumps the contents. + * + * If the first argument is "-", this will read from stdin. Otherwise, + * the first argument is taken to be an archive filename, and opened. + */ +#include +#include "NufxLib.h" +#include "Common.h" + + +/* + * Callback function to display the contents of a single record. + * + * "pRecord->filename" is the record's filename, whether from the record + * header, a filename thread, or a default value ("UNKNOWN", stuffed in + * when a record has no filename at all). + */ +NuResult +ShowContents(NuArchive* pArchive, void* vpRecord) +{ + const NuRecord* pRecord = (NuRecord*) vpRecord; + + printf("*** Filename = '%s'\n", pRecord->filename); + + return kNuOK; +} + + +/* + * Dump the contents from the streaming input. + * + * If we're not interested in handling an archive on stdin, we could just + * pass the filename in here and use NuOpenRO instead. + */ +int +DoStreamStuff(FILE* fp) +{ + NuError err; + NuArchive* pArchive = nil; + + err = NuStreamOpenRO(fp, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to open stream archive (err=%d)\n", err); + goto bail; + } + + printf("*** Streaming contents!\n"); + + err = NuContents(pArchive, ShowContents); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: NuContents failed (err=%d)\n", err); + goto bail; + } + +bail: + if (pArchive != nil) { + NuError err2 = NuClose(pArchive); + if (err == kNuErrNone) + err = err2; + } + + return err; +} + + +/* + * Grab the name of an archive to read. If "-" was given, use stdin. + */ +int +main(int argc, char** argv) +{ + long major, minor, bug; + const char* pBuildDate; + FILE* infp = nil; + int cc; + + (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil); + printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n", + major, minor, bug, pBuildDate); + + if (argc != 2) { + fprintf(stderr, "Usage: %s (archive-name|-)\n", argv[0]); + exit(2); + } + + if (strcmp(argv[1], "-") == 0) + infp = stdin; + else { + infp = fopen(argv[1], kNuFileOpenReadOnly); + if (infp == nil) { + fprintf(stderr, "ERROR: unable to open '%s'\n", argv[1]); + exit(1); + } + } + + cc = DoStreamStuff(infp); + exit(cc != 0); +} + diff --git a/nufxlib/samples/TestTwirl.c b/nufxlib/samples/TestTwirl.c new file mode 100644 index 0000000..be996a1 --- /dev/null +++ b/nufxlib/samples/TestTwirl.c @@ -0,0 +1,697 @@ +/* + * NuFX archive manipulation library + * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. + * This is free software; you can redistribute it and/or modify it under the + * terms of the BSD License, see the file COPYING.LIB. + * + * Recompress records in place, several times, possibly deleting records + * or threads as we go. The goal is to perform a large number of operations + * that modify the archive without closing and reopening it. + * + * Depending on which #defines are enabled, this can be very destructive, + * so a copy of the archive is made before processing begins. + */ +#include +#include +#include +#include +#include "NufxLib.h" +#include "Common.h" + +/* copy the archive to this file before starting */ +static const char* kWorkFileName = "TwirlCopy678"; +static const char* kTempFileName = "TwirlTmp789"; + +/* after loading this much stuff into memory, flush changes */ +const int kMaxHeldLen = 1024 * 1024; + +/* + * A list of CRCs. + */ +typedef struct CRCList { + int numEntries; + unsigned short* entries; +} CRCList; + + +/* + * Returns true if the compression type is supported, false otherwise. + */ +int +CompressionSupported(NuValue compression) +{ + int result; + + switch (compression) { + case kNuCompressNone: + result = true; + break; + case kNuCompressSQ: + result = (NuTestFeature(kNuFeatureCompressSQ) == kNuErrNone); + break; + case kNuCompressLZW1: + case kNuCompressLZW2: + result = (NuTestFeature(kNuFeatureCompressLZW) == kNuErrNone); + break; + case kNuCompressLZC12: + case kNuCompressLZC16: + result = (NuTestFeature(kNuFeatureCompressLZC) == kNuErrNone); + break; + case kNuCompressDeflate: + result = (NuTestFeature(kNuFeatureCompressDeflate) == kNuErrNone); + break; + case kNuCompressBzip2: + result = (NuTestFeature(kNuFeatureCompressBzip2) == kNuErrNone); + break; + default: + assert(false); + result = false; + } + + /*printf("Returning %d for %ld\n", result, compression);*/ + + return result; +} + +/* + * This gets called when a buffer DataSource is no longer needed. + */ +NuResult +FreeCallback(NuArchive* pArchive, void* args) +{ + free(args); + return kNuOK; +} + + +/* + * Dump a CRC list. + */ +void +DumpCRCs(const CRCList* pCRCList) +{ + int i; + + printf(" NumEntries: %d\n", pCRCList->numEntries); + + for (i = 0; i < pCRCList->numEntries; i++) + printf(" %5d: 0x%04x\n", i, pCRCList->entries[i]); +} + +/* + * Free a CRC list. + */ +void +FreeCRCs(CRCList* pCRCList) +{ + if (pCRCList == nil) + return; + + free(pCRCList->entries); + free(pCRCList); +} + +/* + * Gather a list of CRCs from the archive. + * + * We assume there are at most two data threads (e.g. data fork and rsrc + * fork) in a record. + * + * Returns the list on success, nil on failure. + */ +CRCList* +GatherCRCs(NuArchive* pArchive) +{ + NuError err = kNuErrNone; + const NuMasterHeader* pMasterHeader; + CRCList* pCRCList = nil; + unsigned short* pEntries = nil; + long recCount, maxCRCs; + long recIdx, crcIdx; + int i; + + pCRCList = malloc(sizeof(*pCRCList)); + if (pCRCList == nil) { + fprintf(stderr, "ERROR: couldn't alloc CRC list\n"); + err = kNuErrGeneric; + goto bail; + } + memset(pCRCList, 0, sizeof(*pCRCList)); + + /* get record count out of master header, just for fun */ + err = NuGetMasterHeader(pArchive, &pMasterHeader); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err); + goto bail; + } + recCount = pMasterHeader->mhTotalRecords; + maxCRCs = recCount * 2; + + pEntries = malloc(sizeof(*pEntries) * maxCRCs); + if (pEntries == nil) { + fprintf(stderr, "ERROR: unable to alloc CRC list (%ld entries)\n", + maxCRCs); + err = kNuErrGeneric; + goto bail; + } + pCRCList->entries = pEntries; + + for (i = 0; i < maxCRCs; i++) + pEntries[i] = 0xdead; + + /* + * Enumerate our way through the records. If something was disturbed + * we should end up in a different place and the CRCs will be off. + */ + crcIdx = 0; + for (recIdx = 0; recIdx < recCount; recIdx++) { + NuRecordIdx recordIdx; + const NuRecord* pRecord; + const NuThread* pThread; + + err = NuGetRecordIdxByPosition(pArchive, recIdx, &recordIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", + recIdx, err); + goto bail; + } + + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get recordIdx %ld\n", recordIdx); + goto bail; + } + + if (NuRecordGetNumThreads(pRecord) == 0) { + fprintf(stderr, "ERROR: not expecting empty record (%ld)!\n", + recordIdx); + err = kNuErrGeneric; + goto bail; + } + + for (i = 0; i < (int)NuRecordGetNumThreads(pRecord); i++) { + pThread = NuGetThread(pRecord, i); + if (pThread->thThreadClass == kNuThreadClassData) { + if (crcIdx >= maxCRCs) { + fprintf(stderr, "ERROR: CRC buffer exceeded\n"); + assert(false); + err = kNuErrGeneric; + goto bail; + } + + pEntries[crcIdx++] = pThread->thThreadCRC; + } + } + } + + pCRCList->numEntries = crcIdx; + + DumpCRCs(pCRCList); + +bail: + if (err != kNuErrNone) { + FreeCRCs(pCRCList); + pCRCList = nil; + } + return pCRCList; +} + + +/* + * Compare the current set of CRCs against our saved list. If any of + * the records or threads were deleted or rearranged, this will fail. + * I happen to think this is a *good* thing: if something is the least + * bit screwy, I want to know about it. + * + * Unfortunately, if we *deliberately* delete records, this can't + * help us with the survivors. + * + * Returns 0 on success, nonzero on failure. + */ +int +CompareCRCs(NuArchive* pArchive, const CRCList* pOldCRCList) +{ + CRCList* pNewCRCList = nil; + int result = -1; + int badCrc = 0; + int i; + + pNewCRCList = GatherCRCs(pArchive); + if (pNewCRCList == nil) { + fprintf(stderr, "ERROR: unable to gather new list\n"); + goto bail; + } + + if (pOldCRCList->numEntries != pNewCRCList->numEntries) { + fprintf(stderr, "ERROR: numEntries mismatch: %d vs %d\n", + pOldCRCList->numEntries, pNewCRCList->numEntries); + goto bail; + } + + for (i = 0; i < pNewCRCList->numEntries; i++) { + if (pOldCRCList->entries[i] != pNewCRCList->entries[i]) { + fprintf(stderr, "ERROR: CRC mismatch: %5d old=0x%04x new=0x%04x\n", + i, pOldCRCList->entries[i], pNewCRCList->entries[i]); + badCrc = 1; + } + } + if (!badCrc) { + printf(" Matched %d CRCs\n", pOldCRCList->numEntries); + result = 0; + } + +bail: + FreeCRCs(pNewCRCList); + return result; +} + + +/* + * Recompress a single thread. + * + * This entails (1) extracting the existing thread, (2) deleting the + * thread, and (3) adding the extracted data. + * + * All of this good stuff gets queued up until the next NuFlush call. + */ +NuError +RecompressThread(NuArchive* pArchive, const NuRecord* pRecord, + const NuThread* pThread) +{ + NuError err = kNuErrNone; + NuDataSource* pDataSource = nil; + NuDataSink* pDataSink = nil; + unsigned char* buf = nil; + + if (pThread->actualThreadEOF == 0) { + buf = malloc(1); + if (buf == nil) { + fprintf(stderr, "ERROR: failed allocating trivial buffer\n"); + err = kNuErrGeneric; + goto bail; + } + } else { + /* + * Create a buffer and data sink to hold the data. + */ + buf = malloc(pThread->actualThreadEOF); + if (buf == nil) { + fprintf(stderr, "ERROR: failed allocating %ld bytes\n", + pThread->actualThreadEOF); + err = kNuErrGeneric; + goto bail; + } + + err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, + pThread->actualThreadEOF, &pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n",err); + goto bail; + } + + /* + * Extract the data. + */ + err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: failed extracting thread %ld in '%s': %s\n", + pThread->threadIdx, pRecord->filename, NuStrError(err)); + goto bail; + } + } + + /* + * Delete the existing thread. + */ + err = NuDeleteThread(pArchive, pThread->threadIdx); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to delete thread %ld\n", + pThread->threadIdx); + goto bail; + } + + /* + * Create a data source for the new thread. Specify a callback to free + * the buffer when NufxLib is done with it. + */ + err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, + 0, buf, 0, pThread->actualThreadEOF, FreeCallback, &pDataSource); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to create data source (err=%d)\n", err); + goto bail; + } + buf = nil; + + /* + * Create replacement thread. + */ + err = NuAddThread(pArchive, pRecord->recordIdx, NuGetThreadID(pThread), + pDataSource, nil); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to add new thread ID=0x%08lx (err=%d)\n", + NuGetThreadID(pThread), err); + goto bail; + } + pDataSource = nil; /* now owned by NufxLib */ + +bail: + NuFreeDataSink(pDataSink); + NuFreeDataSource(pDataSource); + free(buf); + return err; +} + +/* + * Recompress a single record. + * + * The amount of data we're holding in memory as a result of the + * recompression is placed in "*pLen". + */ +NuError +RecompressRecord(NuArchive* pArchive, NuRecordIdx recordIdx, long* pLen) +{ + NuError err = kNuErrNone; + const NuRecord* pRecord; + const NuThread* pThread; + int i; + + printf(" Recompressing %ld\n", recordIdx); + + *pLen = 0; + + err = NuGetRecord(pArchive, recordIdx, &pRecord); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get record %ld (err=%d)\n", + recordIdx, err); + goto bail; + } + + for (i = 0; i < (int)NuRecordGetNumThreads(pRecord); i++) { + pThread = NuGetThread(pRecord, i); + if (pThread->thThreadClass == kNuThreadClassData) { + /*printf(" Recompressing %d (threadID=0x%08lx)\n", i, + NuGetThreadID(pThread));*/ + err = RecompressThread(pArchive, pRecord, pThread); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: failed recompressing thread %ld " + " in record %ld (err=%d)\n", + pThread->threadIdx, pRecord->recordIdx, err); + goto bail; + } + *pLen += pThread->actualThreadEOF; + } else { + /*printf(" Skipping %d (threadID=0x%08lx)\n", i, + NuGetThreadID(pThread));*/ + } + } + +bail: + return err; +} + +/* + * Recompress every data thread in the archive. + */ +NuError +RecompressArchive(NuArchive* pArchive, NuValue compression) +{ + NuError err = kNuErrNone; + NuRecordIdx* pIndices = nil; + NuAttr countAttr; + long heldLen; + long idx; + + err = NuSetValue(pArchive, kNuValueDataCompression, compression); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to set compression to %ld (err=%d)\n", + compression, err); + goto bail; + } + + printf("Recompressing threads with compression type %ld\n", compression); + + err = NuGetAttr(pArchive, kNuAttrNumRecords, &countAttr); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to get numRecords (err=%d)\n", err); + goto bail; + } + + if (countAttr == 0) { + printf("No records found!\n"); + goto bail; + } + + /* + * Get all of the indices up front. This way, if something causes a + * record to "disappear" during processing, we will know about it. + */ + pIndices = malloc(countAttr * sizeof(*pIndices)); + if (pIndices == nil) { + fprintf(stderr, "ERROR: malloc on %ld indices failed\n", countAttr); + err = kNuErrGeneric; + goto bail; + } + + for (idx = 0; idx < (int)countAttr; idx++) { + err = NuGetRecordIdxByPosition(pArchive, idx, &pIndices[idx]); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", + idx, err); + goto bail; + } + } + + /* + * Walk through the index list, handling each record individually. + */ + heldLen = 0; + for (idx = 0; idx < (int)countAttr; idx++) { + long recHeldLen; + + err = RecompressRecord(pArchive, pIndices[idx], &recHeldLen); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: failed recompressing record %ld (err=%d)\n", + pIndices[idx], err); + goto bail; + } + + heldLen += recHeldLen; + + if (heldLen > kMaxHeldLen) { + long statusFlags; + + printf(" (flush)\n"); + err = NuFlush(pArchive, &statusFlags); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: intra-recompress flush failed: %s\n", + NuStrError(err)); + goto bail; + } + + heldLen = 0; + } + } + +bail: + free(pIndices); + return err; +} + +/* + * Initiate the twirling. + */ +int +TwirlArchive(const char* filename) +{ + NuError err = kNuErrNone; + NuArchive* pArchive = nil; + CRCList* pCRCList = nil; + int compression; + int cc; + + /* + * Open the archive after removing any temp file remnants. + */ + cc = unlink(kTempFileName); + if (cc == 0) + printf("Removed stale temp file '%s'\n", kTempFileName); + + err = NuOpenRW(filename, kTempFileName, 0, &pArchive); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: unable to open archive '%s': %s\n", + filename, NuStrError(err)); + goto bail; + } + + /* + * Mask records with no data threads, so we don't have to + * special-case them. + */ + err = NuSetValue(pArchive, kNuValueMaskDataless, true); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: couldn't mask dataless (err=%d)\n", err); + goto bail; + } + + pCRCList = GatherCRCs(pArchive); + if (pCRCList == nil) { + fprintf(stderr, "ERROR: unable to get CRC list\n"); + goto bail; + } + + /* + * For each type of compression, recompress the entire archive. + */ + for (compression = kNuCompressNone; compression <= kNuCompressBzip2; + compression++) + { + long statusFlags; + + if (!CompressionSupported(compression)) + continue; + + err = RecompressArchive(pArchive, compression); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: recompress failed: %s\n", NuStrError(err)); + goto bail; + } + + err = NuFlush(pArchive, &statusFlags); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: post-recompress flush failed: %s\n", + NuStrError(err)); + goto bail; + } + } + + /* + * Same thing, reverse order. We want to start with the same one we + * ended on above, so we can practice skipping over things. + */ + for (compression = kNuCompressBzip2; compression >= kNuCompressNone; + compression--) + { + long statusFlags; + + if (!CompressionSupported(compression)) + continue; + + err = RecompressArchive(pArchive, compression); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: recompress2 failed: %s\n", NuStrError(err)); + goto bail; + } + + err = NuFlush(pArchive, &statusFlags); + if (err != kNuErrNone) { + fprintf(stderr, "ERROR: post-recompress flush2 failed: %s\n", + NuStrError(err)); + goto bail; + } + } + + if (CompareCRCs(pArchive, pCRCList) != 0) { + fprintf(stderr, "ERROR: CRCs didn't match\n"); + goto bail; + } + + printf("Done!\n"); + +bail: + FreeCRCs(pCRCList); + if (pArchive != nil) { + NuAbort(pArchive); + NuClose(pArchive); + } + + return (err != kNuErrNone); +} + + +/* + * Copy from the current offset in "srcfp" to a new file called + * "outFileName". Returns a writable file descriptor for the new file + * on success, or nil on error. + * + * (Note "CopyFile()" exists under Win32.) + */ +FILE* +MyCopyFile(const char* outFileName, FILE* srcfp) +{ + char buf[24576]; + FILE* outfp; + size_t count; + + outfp = fopen(outFileName, kNuFileOpenWriteTrunc); + if (outfp == nil) { + fprintf(stderr, "ERROR: unable to open '%s' (err=%d)\n", outFileName, + errno); + return nil; + } + + while (!feof(srcfp)) { + count = fread(buf, 1, sizeof(buf), srcfp); + if (count == 0) + break; + if (fwrite(buf, 1, count, outfp) != count) { + fprintf(stderr, "ERROR: failed writing outfp (err=%d)\n", errno); + fclose(outfp); + return nil; + } + } + + if (ferror(srcfp)) { + fprintf(stderr, "ERROR: failed reading srcfp (err=%d)\n", errno); + fclose(outfp); + return nil; + } + + return outfp; +} + +/* + * Let's get started. + */ +int +main(int argc, char** argv) +{ + long major, minor, bug; + const char* pBuildDate; + FILE* srcfp = nil; + FILE* infp = nil; + int cc; + + /* don't buffer output */ + setvbuf(stdout, nil, _IONBF, 0); + setvbuf(stderr, nil, _IONBF, 0); + + (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, nil); + printf("Using NuFX lib %ld.%ld.%ld built on or after %s\n\n", + major, minor, bug, pBuildDate); + + if (argc == 2) { + srcfp = fopen(argv[1], kNuFileOpenReadOnly); + if (srcfp == nil) { + perror("fopen failed"); + exit(1); + } + } else { + fprintf(stderr, "ERROR: you have to specify a filename\n"); + exit(2); + } + + printf("Copying '%s' to '%s'\n", argv[1], kWorkFileName); + + infp = MyCopyFile(kWorkFileName, srcfp); + if (infp == nil) { + fprintf(stderr, "Copy failed, bailing.\n"); + exit(1); + } + fclose(srcfp); + fclose(infp); + + cc = TwirlArchive(kWorkFileName); + + exit(cc != 0); +} + diff --git a/prebuilt/libnufx.a b/prebuilt/libnufx.a deleted file mode 100644 index df1ae5da568cb5c630839ac4baf74cb5b694fec6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 191682 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

R%7d+%-2F+B4_f@3EaW)th@&H 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 diff --git a/prebuilt/nufxlib2.dll b/prebuilt/nufxlib2.dll deleted file mode 100644 index b8230f5dc4ec300ef277305343d05686f654facd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110592 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 diff --git a/prebuilt/nufxlib2.lib b/prebuilt/nufxlib2.lib deleted file mode 100644 index ff6135a58adbfc04bdb1f7606cbbdb02c45457b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13282 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 F{{l9Z3O@h< diff --git a/prebuilt/nufxlib2D.dll b/prebuilt/nufxlib2D.dll deleted file mode 100644 index d0353d34ff2750819a746d9c247549eae3891521..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 331829 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 A`2YX_ diff --git a/prebuilt/nufxlib2D.lib b/prebuilt/nufxlib2D.lib deleted file mode 100644 index e3621810ad49de8930bbdccdc712130de8a9316f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13344 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 b?p3RU(7t!X^bLRX!~f^F#|N))UDW>pd_^Q; diff --git a/prebuilt/zdll.lib b/prebuilt/zdll.lib deleted file mode 100644 index 01f4e10e6ef05b1198d2e5b3410437611826e61a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10590 zcmcgy&2L*b68||Ln?!Y6*G~NXAv;d2^uw_wImrTxz;&F(26a=zNrPUtiXy+r!YY;_ z$t@}r-FvYpiXM9^iaq7fbB;l7TlBDpoVq}fzo3VrXp0`&ogs&O#ycM||gK;H(?Uk3&r0;!)g z4gLr)4W~5CJO!9$ztc4OIlwgZSkq{lc%qTJnugvXInl`1n$CX&DEgjCqS^N}ogXBg z=ooVt@P2*Pqrs)+;$x(o5W=GTH52Qyl^+?m? zJn=+RcQj3W12E0>YMR(0o+$ZD=tC-rrpZQ3<6mkT`xaoDIHqZ=PCU`+Uo{PWM(;~B z{EN^hR1%&0N@$fzqS=I|IeH(aG2)q0G}crqm%H)i`db^fbF0^HUE8>^e*3MBT%%mN zw}@Qs=G$v)&CU3`YS*o||MK?DH*%}5-+rC+OAmH(>ft*b580JdO2yHUFSyRSfIttW7+a=u7LY?kQ$3ZzvHYNQd? z{FZdsL{+~{3~nCv?7zEh#K^gI-9xca*$K$KO=+Z?Ml2O0Cui9ae<(H_#17Dn?!?2l*<=xGAjb*8FBUsAkA9Y(>ip)dEYU&@$OeZfuw8IlHWd zYtl&jD$HsfSvB&U&oycAV~$EwFT8I2o*aqS+bvq=>lJbv+o2=Tnl!^a;9Na_ugv!K zS*_E!u8k~Z=lMa#<_DJmZ1e&3e&FE%@DoL&9|tKS4FgYSfbV93&ne129s<%--W>tn zq59WUesmuAp6ET&8Km;cS>OwzkExG$&j3$|Qqe<0bT zDPW$;J5+u%0rZl5i^^xD^C8hg(*Kg4`*sXCM)Eq9zn%s@qkey(cl?Ctt8>6AQG(v% z4q+uohodSbKIp*_yoNyxVHoo`j^~lUAFzOn=*0_o4yUk$SMf5&Z~?F29L{1IqnN@B zl9<38&SM-Y%wiHJFo4s@;3b?y8jI-1i|9iZBe;Y!IQD-+IZ7Vfai$3Vyl!O5~^D+I}tYR zRJ?boea|RFB8<01Ms~o+*6eTX#8JpFz3TX7xEA5X2^D^j@k3X1d-uaYiwNlN`_U-l z1wh#m%}Wa#muBekIEvo3EppdUMC3S%Nch3fQ5>Ol6p?xJFS3^NZ`zvku%;clIS(=W zu^!-FSrW@_MsC-lS#@55WP~O)(s59H+D1KT6|pv)Fv?_7H^y>UGLvv-B^lOBbj5no z7O`6-GPzORu99NIreY?CFzJEKSoAt5HuMTGsa%*;nbgRnQzktNlNXcTcsZTPVTujo z#WsPHxwbJ_`>aTrQPy*KKe%f{OS7wL3@uImQ({YLi`ddgqelYC6`OR`!QilI^laRe~N$wQxCf0h6;^%+;XXRuMC<3Zq&-`X^Z?`ymOS8Z-RsLZZ zDVNc=GGE%v*YmS!(?kIrbQH%k5ges*84Hg^kCPJhqvJ7tBYLJd{bw1;$498zN3olK zr|h6o80b4e67G$ECn(k*@9lrHRH#;JmHYK%YU4t3?Z(YFbaUh{T??ouxd+NMh)xg& zC}-fB1;H{ePI}Sfy?@2;zZ*2x=O4e>5t#8VQ{u#uEhAAQkrM)d&WAY7b+W zpzZ7x7OqXd$S#UoL@?+eMZV!+q#cmXM`D~WfG@@P?Nu}!{ zkgc^lM=_Rr-oeqytL;6sVVgKhdw5)uJnjVt$4`s*kC%<{y%!nJKfHuMo$sZ4jEvV( zpF#@n5JDJRYtw#l@s01J)SW8dOW2M(Uqx{k{Tf3b<_>}p6AuP72A>lTf)EoAP78uM zf_4exjKWYMFDex_N3zIXK-rwOnvex_fGQIf8VcQ%g*a|B8dt z%^d$#2j|do{20G`&BfzO_0YXL-htxQ(tHF*-`9s=w$|=iiaAYOWGv@PfbBaRT$?Cy j(Px2Cadler to the adler32 checksum of all input read - so far (that is, total_in bytes). - - deflate() may update strm->data_type if it can make a good guess about - the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered - binary. This field is only for information purposes and does not affect - the compression algorithm in any manner. - - deflate() returns Z_OK if some progress has been made (more input - processed or more output produced), Z_STREAM_END if all input has been - consumed and all output has been produced (only when flush is set to - Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example - if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible - (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not - fatal, and deflate() can be called again with more input and more output - space to continue compressing. -*/ - - -ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the - stream state was inconsistent, Z_DATA_ERROR if the stream was freed - prematurely (some input or output was discarded). In the error case, - msg may be set but then points to a static string (which must not be - deallocated). -*/ - - -/* -ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); - - Initializes the internal stream state for decompression. The fields - next_in, avail_in, zalloc, zfree and opaque must be initialized before by - the caller. If next_in is not Z_NULL and avail_in is large enough (the exact - value depends on the compression method), inflateInit determines the - compression method from the zlib header and allocates all data structures - accordingly; otherwise the allocation will be deferred to the first call of - inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to - use default allocation functions. - - inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_VERSION_ERROR if the zlib library version is incompatible with the - version assumed by the caller. msg is set to null if there is no error - message. inflateInit does not perform any decompression apart from reading - the zlib header if present: this will be done by inflate(). (So next_in and - avail_in may be modified, but next_out and avail_out are unchanged.) -*/ - - -ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); -/* - inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may introduce - some output latency (reading input without producing any output) except when - forced to flush. - - The detailed semantics are as follows. inflate performs one or both of the - following actions: - - - Decompress more input starting at next_in and update next_in and avail_in - accordingly. If not all input can be processed (because there is not - enough room in the output buffer), next_in is updated and processing - will resume at this point for the next call of inflate(). - - - Provide more output starting at next_out and update next_out and avail_out - accordingly. inflate() provides as much output as possible, until there - is no more input data or no more space in the output buffer (see below - about the flush parameter). - - Before the call of inflate(), the application should ensure that at least - one of the actions is possible, by providing more input and/or consuming - more output, and updating the next_* and avail_* values accordingly. - The application can consume the uncompressed output when it wants, for - example when the output buffer is full (avail_out == 0), or after each - call of inflate(). If inflate returns Z_OK and with zero avail_out, it - must be called again after making room in the output buffer because there - might be more output pending. - - The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, - Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much - output as possible to the output buffer. Z_BLOCK requests that inflate() stop - if and when it gets to the next deflate block boundary. When decoding the - zlib or gzip format, this will cause inflate() to return immediately after - the header and before the first block. When doing a raw inflate, inflate() - will go ahead and process the first block, and will return when it gets to - the end of that block, or when it runs out of data. - - The Z_BLOCK option assists in appending to or combining deflate streams. - Also to assist in this, on return inflate() will set strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 - if inflate() is currently decoding the last block in the deflate stream, - plus 128 if inflate() returned immediately after decoding an end-of-block - code or decoding the complete header up to just before the first byte of the - deflate stream. The end-of-block will not be indicated until all of the - uncompressed data from that block has been written to strm->next_out. The - number of unused bits may in general be greater than seven, except when - bit 7 of data_type is set, in which case the number of unused bits will be - less than eight. - - inflate() should normally be called until it returns Z_STREAM_END or an - error. However if all decompression is to be performed in a single step - (a single call of inflate), the parameter flush should be set to - Z_FINISH. In this case all pending input is processed and all pending - output is flushed; avail_out must be large enough to hold all the - uncompressed data. (The size of the uncompressed data may have been saved - by the compressor for this purpose.) The next operation on this stream must - be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster approach - may be used for the single inflate() call. - - In this implementation, inflate() always flushes as much output as - possible to the output buffer, and always uses the faster approach on the - first call. So the only effect of the flush parameter in this implementation - is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK is used. - - If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the adler32 checksum of the dictionary - chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the adler32 checksum of all output produced so far (that is, - total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described - below. At the end of the stream, inflate() checks that its computed adler32 - checksum is equal to that saved by the compressor and returns Z_STREAM_END - only if the checksum is correct. - - inflate() will decompress and check either zlib-wrapped or gzip-wrapped - deflate data. The header type is detected automatically. Any information - contained in the gzip header is not retained, so applications that need that - information should instead use raw inflate, see inflateInit2() below, or - inflateBack() and perform their own processing of the gzip header and - trailer. - - inflate() returns Z_OK if some progress has been made (more input processed - or more output produced), Z_STREAM_END if the end of the compressed data has - been reached and all uncompressed output has been produced, Z_NEED_DICT if a - preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect check - value), Z_STREAM_ERROR if the stream structure was inconsistent (for example - if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, - Z_BUF_ERROR if no progress is possible or if there was not enough room in the - output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and - inflate() can be called again with more input and more output space to - continue decompressing. If Z_DATA_ERROR is returned, the application may then - call inflateSync() to look for a good compression block if a partial recovery - of the data is desired. -*/ - - -ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); -/* - All dynamically allocated data structures for this stream are freed. - This function discards any unprocessed input and does not flush any - pending output. - - inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state - was inconsistent. In the error case, msg may be set but then points to a - static string (which must not be deallocated). -*/ - - /* Advanced functions */ - -/* - The following functions are needed only in some special applications. -*/ - -/* -ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, - int level, - int method, - int windowBits, - int memLevel, - int strategy)); - - This is another version of deflateInit with more compression options. The - fields next_in, zalloc, zfree and opaque must be initialized before by - the caller. - - The method parameter is the compression method. It must be Z_DEFLATED in - this version of the library. - - The windowBits parameter is the base two logarithm of the window size - (the size of the history buffer). It should be in the range 8..15 for this - version of the library. Larger values of this parameter result in better - compression at the expense of memory usage. The default value is 15 if - deflateInit is used instead. - - windowBits can also be -8..-15 for raw deflate. In this case, -windowBits - determines the window size. deflate() will then generate raw deflate data - with no zlib header or trailer, and will not compute an adler32 check value. - - windowBits can also be greater than 15 for optional gzip encoding. Add - 16 to windowBits to write a simple gzip header and trailer around the - compressed data instead of a zlib wrapper. The gzip header will have no - file name, no extra data, no comment, no modification time (set to zero), - no header crc, and the operating system will be set to 255 (unknown). If a - gzip stream is being written, strm->adler is a crc32 instead of an adler32. - - The memLevel parameter specifies how much memory should be allocated - for the internal compression state. memLevel=1 uses minimum memory but - is slow and reduces compression ratio; memLevel=9 uses maximum memory - for optimal speed. The default value is 8. See zconf.h for total memory - usage as a function of windowBits and memLevel. - - The strategy parameter is used to tune the compression algorithm. Use the - value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as - Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy - parameter only affects the compression ratio but not the correctness of the - compressed output even if it is not set appropriately. Z_FIXED prevents the - use of dynamic Huffman codes, allowing for a simpler decoder for special - applications. - - deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid - method). msg is set to null if there is no error message. deflateInit2 does - not perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any - call of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). - - The dictionary should consist of strings (byte sequences) that are likely - to be encountered later in the data to be compressed, with the most commonly - used strings preferably put towards the end of the dictionary. Using a - dictionary is most useful when the data to be compressed is short and can be - predicted with good accuracy; the data can then be compressed better than - with the default empty dictionary. - - Depending on the size of the compression data structures selected by - deflateInit or deflateInit2, a part of the dictionary may in effect be - discarded, for example if the dictionary is larger than the window size in - deflate or deflate2. Thus the strings most likely to be useful should be - put at the end of the dictionary, not at the front. In addition, the - current implementation of deflate will use at most the window size minus - 262 bytes of the provided dictionary. - - Upon return of this function, strm->adler is set to the adler32 value - of the dictionary; the decompressor may later use this value to determine - which dictionary has been used by the compressor. (The adler32 value - applies to the whole dictionary even if only a subset of the dictionary is - actually used by the compressor.) If a raw deflate was requested, then the - adler32 value is not computed and strm->adler is not set. - - deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). -*/ - -ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when several compression strategies will be - tried, for example when there are several ways of pre-processing the input - data with a filter. The streams that will be discarded should then be freed - by calling deflateEnd. Note that deflateCopy duplicates the internal - compression state which can be quite large, so this strategy is slow and - can consume lots of memory. - - deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); -/* - This function is equivalent to deflateEnd followed by deflateInit, - but does not free and reallocate all the internal compression state. - The stream will keep the same compression level and any other attributes - that may have been set by deflateInit2. - - deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, - int level, - int strategy)); -/* - Dynamically update the compression level and compression strategy. The - interpretation of level and strategy is as in deflateInit2. This can be - used to switch between compression and straight copy of the input data, or - to switch to a different kind of input data requiring a different - strategy. If the compression level is changed, the input available so far - is compressed with the old level (and may be flushed); the new level will - take effect only at the next call of deflate(). - - Before the call of deflateParams, the stream state must be set as for - a call of deflate(), since the currently available input may have to - be compressed and flushed. In particular, strm->avail_out must be non-zero. - - deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source - stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR - if strm->avail_out was zero. -*/ - -ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, - int good_length, - int max_lazy, - int nice_length, - int max_chain)); -/* - Fine tune deflate's internal compression parameters. This should only be - used by someone who understands the algorithm used by zlib's deflate for - searching for the best matching string, and even then only by the most - fanatic optimizer trying to squeeze out the last compressed bit for their - specific input data. Read the deflate.c source code for the meaning of the - max_lazy, good_length, nice_length, and max_chain parameters. - - deflateTune() can be called after deflateInit() or deflateInit2(), and - returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. - */ - -ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, - uLong sourceLen)); -/* - deflateBound() returns an upper bound on the compressed size after - deflation of sourceLen bytes. It must be called after deflateInit() - or deflateInit2(). This would be used to allocate an output buffer - for deflation in a single pass, and so would be called before deflate(). -*/ - -ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - deflatePrime() inserts bits in the deflate output stream. The intent - is that this function is used to start off the deflate output with the - bits leftover from a previous deflate stream when appending to it. As such, - this function can only be used for raw deflate, and must be used before the - first deflate() call after a deflateInit2() or deflateReset(). bits must be - less than or equal to 16, and that many of the least significant bits of - value will be inserted in the output. - - deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, - gz_headerp head)); -/* - deflateSetHeader() provides gzip header information for when a gzip - stream is requested by deflateInit2(). deflateSetHeader() may be called - after deflateInit2() or deflateReset() and before the first call of - deflate(). The text, time, os, extra field, name, and comment information - in the provided gz_header structure are written to the gzip header (xflag is - ignored -- the extra flags are set according to the compression level). The - caller must assure that, if not Z_NULL, name and comment are terminated with - a zero byte, and that if extra is not Z_NULL, that extra_len bytes are - available there. If hcrc is true, a gzip header crc is included. Note that - the current versions of the command-line version of gzip (up through version - 1.3.x) do not support header crc's, and will report that it is a "multi-part - gzip file" and give up. - - If deflateSetHeader is not used, the default gzip header has text false, - the time set to zero, and os set to 255, with no extra, name, or comment - fields. The gzip header is returned to the default state by deflateReset(). - - deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, - int windowBits)); - - This is another version of inflateInit with an extra parameter. The - fields next_in, avail_in, zalloc, zfree and opaque must be initialized - before by the caller. - - The windowBits parameter is the base two logarithm of the maximum window - size (the size of the history buffer). It should be in the range 8..15 for - this version of the library. The default value is 15 if inflateInit is used - instead. windowBits must be greater than or equal to the windowBits value - provided to deflateInit2() while compressing, or it must be equal to 15 if - deflateInit2() was not used. If a compressed stream with a larger window - size is given as input, inflate() will return with the error code - Z_DATA_ERROR instead of trying to allocate a larger window. - - windowBits can also be -8..-15 for raw inflate. In this case, -windowBits - determines the window size. inflate() will then process raw deflate data, - not looking for a zlib or gzip header, not generating a check value, and not - looking for any check values for comparison at the end of the stream. This - is for use with other formats that use the deflate compressed data format - such as zip. Those formats provide their own check values. If a custom - format is developed using the raw deflate format for compressed data, it is - recommended that a check value such as an adler32 or a crc32 be applied to - the uncompressed data as is done in the zlib, gzip, and zip formats. For - most applications, the zlib format should be used as is. Note that comments - above on the use in deflateInit2() applies to the magnitude of windowBits. - - windowBits can also be greater than 15 for optional gzip decoding. Add - 32 to windowBits to enable zlib and gzip decoding with automatic header - detection, or add 16 to decode only the gzip format (the zlib format will - return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is - a crc32 instead of an adler32. - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg - is set to null if there is no error message. inflateInit2 does not perform - any decompression apart from reading the zlib header if present: this will - be done by inflate(). (So next_in and avail_in may be modified, but next_out - and avail_out are unchanged.) -*/ - -ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, - const Bytef *dictionary, - uInt dictLength)); -/* - Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate, - if that call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the adler32 value returned by that call of inflate. - The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called - immediately after inflateInit2() or inflateReset() and before any call of - inflate() to set the dictionary. The application must insure that the - dictionary that was used for compression is provided. - - inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a - parameter is invalid (such as NULL dictionary) or the stream state is - inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect adler32 value). inflateSetDictionary does not - perform any decompression: this will be done by subsequent calls of - inflate(). -*/ - -ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); -/* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all - available input is skipped. No output is provided. - - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been found, - or Z_STREAM_ERROR if the stream structure was inconsistent. In the success - case, the application may save the current current value of total_in which - indicates where valid compressed data was found. In the error case, the - application may repeatedly call inflateSync, providing more input each time, - until success or end of the input data. -*/ - -ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, - z_streamp source)); -/* - Sets the destination stream as a complete copy of the source stream. - - This function can be useful when randomly accessing a large stream. The - first pass through the stream can periodically record the inflate state, - allowing restarting inflate at those points when randomly accessing the - stream. - - inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_STREAM_ERROR if the source stream state was inconsistent - (such as zalloc being NULL). msg is left unchanged in both source and - destination. -*/ - -ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); -/* - This function is equivalent to inflateEnd followed by inflateInit, - but does not free and reallocate all the internal decompression state. - The stream will keep attributes that may have been set by inflateInit2. - - inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent (such as zalloc or state being NULL). -*/ - -ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, - int bits, - int value)); -/* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. - - inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, - gz_headerp head)); -/* - inflateGetHeader() requests that gzip header information be stored in the - provided gz_header structure. inflateGetHeader() may be called after - inflateInit2() or inflateReset(), and before the first call of inflate(). - As inflate() processes the gzip stream, head->done is zero until the header - is completed, at which time head->done is set to one. If a zlib stream is - being decoded, then head->done is set to -1 to indicate that there will be - no gzip header information forthcoming. Note that Z_BLOCK can be used to - force inflate() to return immediately after header processing is complete - and before any actual data is decompressed. - - The text, time, xflags, and os fields are filled in with the gzip header - contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When - any of extra, name, or comment are not Z_NULL and the respective field is - not present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. - - If inflateGetHeader is not used, then the header information is simply - discarded. The header is always checked for validity, including the header - CRC if present. inflateReset() will reset the process to discard the header - information. The application would need to call inflateGetHeader() again to - retrieve the header from the next gzip stream. - - inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. -*/ - -/* -ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, - unsigned char FAR *window)); - - Initialize the internal stream state for decompression using inflateBack() - calls. The fields zalloc, zfree and opaque in strm must be initialized - before the call. If zalloc and zfree are Z_NULL, then the default library- - derived memory allocation routines are used. windowBits is the base two - logarithm of the window size, in the range 8..15. window is a caller - supplied buffer of that size. Except for special applications where it is - assured that deflate was used with small window sizes, windowBits must be 15 - and a 32K byte window must be supplied to be able to decompress general - deflate streams. - - See inflateBack() for the usage of these routines. - - inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the paramaters are invalid, Z_MEM_ERROR if the internal state could not - be allocated, or Z_VERSION_ERROR if the version of the library does not - match the version of the header file. -*/ - -typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); -typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); - -ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, - in_func in, void FAR *in_desc, - out_func out, void FAR *out_desc)); -/* - inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is more efficient than inflate() for - file i/o applications in that it avoids copying between the output and the - sliding window by simply making the window itself the output buffer. This - function trusts the application to not change the output buffer passed by - the output function, at least until inflateBack() returns. - - inflateBackInit() must be called first to allocate the internal state - and to initialize the state with the user-provided window buffer. - inflateBack() may then be used multiple times to inflate a complete, raw - deflate stream with each call. inflateBackEnd() is then called to free - the allocated state. - - A raw deflate stream is one with no zlib or gzip header or trailer. - This routine would normally be used in a utility that reads zip or gzip - files and writes out uncompressed files. The utility would decode the - header and process the trailer on its own, hence this routine expects - only the raw deflate stream to decompress. This is different from the - normal behavior of inflate(), which expects either a zlib or gzip header and - trailer around the deflate stream. - - inflateBack() uses two subroutines supplied by the caller that are then - called by inflateBack() for input and output. inflateBack() calls those - routines until it reads a complete deflate stream and writes out all of the - uncompressed data, or until it encounters an error. The function's - parameters and return types are defined above in the in_func and out_func - typedefs. inflateBack() will call in(in_desc, &buf) which should return the - number of bytes of provided input, and a pointer to that input in buf. If - there is no input available, in() must return zero--buf is ignored in that - case--and inflateBack() will return a buffer error. inflateBack() will call - out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() - should return zero on success, or non-zero on failure. If out() returns - non-zero, inflateBack() will return with an error. Neither in() nor out() - are permitted to change the contents of the window provided to - inflateBackInit(), which is also the buffer that out() uses to write from. - The length written by out() will be at most the window size. Any non-zero - amount of input may be provided by in(). - - For convenience, inflateBack() can be provided input on the first call by - setting strm->next_in and strm->avail_in. If that input is exhausted, then - in() will be called. Therefore strm->next_in must be initialized before - calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called - immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in - must also be initialized, and then if strm->avail_in is not zero, input will - initially be taken from strm->next_in[0 .. strm->avail_in - 1]. - - The in_desc and out_desc parameters of inflateBack() is passed as the - first parameter of in() and out() respectively when they are called. These - descriptors can be optionally used to pass any information that the caller- - supplied in() and out() functions need to do their job. - - On return, inflateBack() will set strm->next_in and strm->avail_in to - pass back any unused input that was provided by the last in() call. The - return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR - if in() or out() returned an error, Z_DATA_ERROR if there was a format - error in the deflate stream (in which case strm->msg is set to indicate the - nature of the error), or Z_STREAM_ERROR if the stream was not properly - initialized. In the case of Z_BUF_ERROR, an input or output error can be - distinguished using strm->next_in which will be Z_NULL only if in() returned - an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to - out() returning non-zero. (in() will always be called before out(), so - strm->next_in is assured to be defined if out() returns non-zero.) Note - that inflateBack() cannot return Z_OK. -*/ - -ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); -/* - All memory allocated by inflateBackInit() is freed. - - inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream - state was inconsistent. -*/ - -ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); -/* Return flags indicating compile-time options. - - Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: - 1.0: size of uInt - 3.2: size of uLong - 5.4: size of voidpf (pointer) - 7.6: size of z_off_t - - Compiler, assembler, and debug options: - 8: DEBUG - 9: ASMV or ASMINF -- use ASM code - 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention - 11: 0 (reserved) - - One-time table building (smaller code, but not thread-safe if true): - 12: BUILDFIXED -- build static block decoding tables when needed - 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed - 14,15: 0 (reserved) - - Library content (indicates missing functionality): - 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking - deflate code when not needed) - 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect - and decode gzip streams (to avoid linking crc code) - 18-19: 0 (reserved) - - Operation variations (changes in library functionality): - 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate - 21: FASTEST -- deflate algorithm with only one, lowest compression level - 22,23: 0 (reserved) - - The sprintf variant used by gzprintf (zero is best): - 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! - 26: 0 = returns value, 1 = void -- 1 means inferred string length returned - - Remainder: - 27-31: 0 (reserved) - */ - - - /* utility functions */ - -/* - The following utility functions are implemented on top of the - basic stream-oriented functions. To simplify the interface, some - default options are assumed (compression level and memory usage, - standard memory allocation functions). The source code of these - utility functions can easily be modified if you need special options. -*/ - -ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Compresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be at least the value returned - by compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - This function can be used to compress a whole file at once if the - input file is mmap'ed. - compress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer. -*/ - -ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen, - int level)); -/* - Compresses the source buffer into the destination buffer. The level - parameter has the same meaning as in deflateInit. sourceLen is the byte - length of the source buffer. Upon entry, destLen is the total size of the - destination buffer, which must be at least the value returned by - compressBound(sourceLen). Upon exit, destLen is the actual size of the - compressed buffer. - - compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_BUF_ERROR if there was not enough room in the output buffer, - Z_STREAM_ERROR if the level parameter is invalid. -*/ - -ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); -/* - compressBound() returns an upper bound on the compressed size after - compress() or compress2() on sourceLen bytes. It would be used before - a compress() or compress2() call to allocate the destination buffer. -*/ - -ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, - const Bytef *source, uLong sourceLen)); -/* - Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total - size of the destination buffer, which must be large enough to hold the - entire uncompressed data. (The size of the uncompressed data must have - been saved previously by the compressor and transmitted to the decompressor - by some mechanism outside the scope of this compression library.) - Upon exit, destLen is the actual size of the compressed buffer. - This function can be used to decompress a whole file at once if the - input file is mmap'ed. - - uncompress returns Z_OK if success, Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. -*/ - - -typedef voidp gzFile; - -ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); -/* - Opens a gzip (.gz) file for reading or writing. The mode parameter - is as in fopen ("rb" or "wb") but can also include a compression level - ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for - Huffman only compression as in "wb1h", or 'R' for run-length encoding - as in "wb1R". (See the description of deflateInit2 for more information - about the strategy parameter.) - - gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. - - gzopen returns NULL if the file could not be opened or if there was - insufficient memory to allocate the (de)compression state; errno - can be checked to distinguish the two cases (if errno is zero, the - zlib error is Z_MEM_ERROR). */ - -ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); -/* - gzdopen() associates a gzFile with the file descriptor fd. File - descriptors are obtained from calls like open, dup, creat, pipe or - fileno (in the file has been previously opened with fopen). - The mode parameter is as in gzopen. - The next call of gzclose on the returned gzFile will also close the - file descriptor fd, just like fclose(fdopen(fd), mode) closes the file - descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). - gzdopen returns NULL if there was insufficient memory to allocate - the (de)compression state. -*/ - -ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); -/* - Dynamically update the compression level or strategy. See the description - of deflateInit2 for the meaning of these parameters. - gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not - opened for writing. -*/ - -ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); -/* - Reads the given number of uncompressed bytes from the compressed file. - If the input file was not in gzip format, gzread copies the given number - of bytes into the buffer. - gzread returns the number of uncompressed bytes actually read (0 for - end of file, -1 for error). */ - -ZEXTERN int ZEXPORT gzwrite OF((gzFile file, - voidpc buf, unsigned len)); -/* - Writes the given number of uncompressed bytes into the compressed file. - gzwrite returns the number of uncompressed bytes actually written - (0 in case of error). -*/ - -ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); -/* - Converts, formats, and writes the args to the compressed file under - control of the format string, as in fprintf. gzprintf returns the number of - uncompressed bytes actually written (0 in case of error). The number of - uncompressed bytes written is limited to 4095. The caller should assure that - this limit is not exceeded. If it is exceeded, then gzprintf() will return - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf() - because the secure snprintf() or vsnprintf() functions were not available. -*/ - -ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); -/* - Writes the given null-terminated string to the compressed file, excluding - the terminating null character. - gzputs returns the number of characters written, or -1 in case of error. -*/ - -ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); -/* - Reads bytes from the compressed file until len-1 characters are read, or - a newline character is read and transferred to buf, or an end-of-file - condition is encountered. The string is then terminated with a null - character. - gzgets returns buf, or Z_NULL in case of error. -*/ - -ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); -/* - Writes c, converted to an unsigned char, into the compressed file. - gzputc returns the value that was written, or -1 in case of error. -*/ - -ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); -/* - Reads one byte from the compressed file. gzgetc returns this byte - or -1 in case of end of file or error. -*/ - -ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); -/* - Push one character back onto the stream to be read again later. - Only one character of push-back is allowed. gzungetc() returns the - character pushed, or -1 on failure. gzungetc() will fail if a - character has been pushed but not read yet, or if c is -1. The pushed - character will be discarded if the stream is repositioned with gzseek() - or gzrewind(). -*/ - -ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); -/* - Flushes all pending output into the compressed file. The parameter - flush is as in the deflate() function. The return value is the zlib - error number (see function gzerror below). gzflush returns Z_OK if - the flush parameter is Z_FINISH and all output could be flushed. - gzflush should be called only when strictly necessary because it can - degrade compression. -*/ - -ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, - z_off_t offset, int whence)); -/* - Sets the starting position for the next gzread or gzwrite on the - given compressed file. The offset represents a number of bytes in the - uncompressed data stream. The whence parameter is defined as in lseek(2); - the value SEEK_END is not supported. - If the file is opened for reading, this function is emulated but can be - extremely slow. If the file is opened for writing, only forward seeks are - supported; gzseek then compresses a sequence of zeroes up to the new - starting position. - - gzseek returns the resulting offset location as measured in bytes from - the beginning of the uncompressed stream, or -1 in case of error, in - particular if the file is opened for writing and the new starting position - would be before the current position. -*/ - -ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); -/* - Rewinds the given file. This function is supported only for reading. - - gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) -*/ - -ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); -/* - Returns the starting position for the next gzread or gzwrite on the - given compressed file. This position represents a number of bytes in the - uncompressed data stream. - - gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) -*/ - -ZEXTERN int ZEXPORT gzeof OF((gzFile file)); -/* - Returns 1 when EOF has previously been detected reading the given - input stream, otherwise zero. -*/ - -ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); -/* - Returns 1 if file is being read directly without decompression, otherwise - zero. -*/ - -ZEXTERN int ZEXPORT gzclose OF((gzFile file)); -/* - Flushes all pending output if necessary, closes the compressed file - and deallocates all the (de)compression state. The return value is the zlib - error number (see function gzerror below). -*/ - -ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); -/* - Returns the error message for the last error which occurred on the - given compressed file. errnum is set to zlib error number. If an - error occurred in the file system and not in the compression library, - errnum is set to Z_ERRNO and the application may consult errno - to get the exact error code. -*/ - -ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); -/* - Clears the error and end-of-file flags for file. This is analogous to the - clearerr() function in stdio. This is useful for continuing to read a gzip - file that is being written concurrently. -*/ - - /* checksum functions */ - -/* - These functions are not related to compression but are exported - anyway because they might be useful in applications using the - compression library. -*/ - -ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); -/* - Update a running Adler-32 checksum with the bytes buf[0..len-1] and - return the updated checksum. If buf is NULL, this function returns - the required initial value for the checksum. - An Adler-32 checksum is almost as reliable as a CRC32 but can be computed - much faster. Usage example: - - uLong adler = adler32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - adler = adler32(adler, buffer, length); - } - if (adler != original_adler) error(); -*/ - -ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, - z_off_t len2)); -/* - Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 - and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for - each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. -*/ - -ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); -/* - Update a running CRC-32 with the bytes buf[0..len-1] and return the - updated CRC-32. If buf is NULL, this function returns the required initial - value for the for the crc. Pre- and post-conditioning (one's complement) is - performed within this function so it shouldn't be done by the application. - Usage example: - - uLong crc = crc32(0L, Z_NULL, 0); - - while (read_buffer(buffer, length) != EOF) { - crc = crc32(crc, buffer, length); - } - if (crc != original_crc) error(); -*/ - -ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); - -/* - Combine two CRC-32 check values into one. For two sequences of bytes, - seq1 and seq2 with lengths len1 and len2, CRC-32 check values were - calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 - check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. -*/ - - - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size)); -ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, - const char *version, int stream_size)); -ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, - unsigned char FAR *window, - const char *version, - int stream_size)); -#define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) -#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) -#define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) -#define inflateBackInit(strm, windowBits, window) \ - inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, sizeof(z_stream)) - - -#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) - struct internal_state {int dummy;}; /* hack for buggy compilers */ -#endif - -ZEXTERN const char * ZEXPORT zError OF((int)); -ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); -ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); - -#ifdef __cplusplus -} -#endif - -#endif /* ZLIB_H */ diff --git a/prebuilt/zlib1.dll b/prebuilt/zlib1.dll deleted file mode 100644 index 1cf8a476e5a1c6df7632d973b02bb5199f7c4698..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59904 zcmeFa3wRS%-akIggf_H+2^!q0QQ{J{YpUBd-Q7aUZc0ljw3L*#X%%QeAr`ENZ2~Az zDAQo4hq$<+>%KqNy+5z(`g?U(mlm&03#1ek3ahBNF6v$qA_%fVK_S1-_spbC0o}{H z@Bi{VzdlboXU?2C=X<{Adq0<%b?at96a+z!Upy`dUAWSpi~s*S|55~D?1bmX3eSyx z?V7HXve&MuYFN^2UEXx}9Zd`GvfjRM*|NKR*4q|aoBYeHOO{!SW>;G8x_i;$o5qYu zw`o<6TIqeM;4;I}#Bbv0D~}fP`_V^haJ^}?@#s{3)g3LuwI@CGs0-JkEABj6#;;JyWqutNp*EvgWZ=g2L?~!|&Ae5zy65hG$y{U<3hlH`$WTd2}3tNyg zN6jt%DXu2`p5gZ&qa4B?|H}bVaC85sf`G^P!%Otu&{e%Xc3cqVquionf{@Q2 zrwEVIK%D<)O%a|^D?0zlm)`@<+Ak?_lW*}VAD(~r`}jxjQD28Tfi(WCg0SGGrbP>V z3k6|TF)~o6P=ep1_$B?hKrT0_#RRHT5P$=M`4FXt|6GF5eN$6&)9uLOeKo1L5i=|PZ7l5A_cB#;>SSI0T-JlSGD5wrpFJ~P5Tp(L zsHN-aUoERU^4G?hq!mGO9-g&g~~@tz!K;a50j`W^dD zotnkYNP%J$m>)POwe_M$z>2itK_pmjD5C#=wF7d@l<0#GAf7i=Bf7*#iBXqMvNJ4P zml*X|NB+ql<$D(~h0cHeTNzl->jINPCK@P~~6sN1hoC93GRm8{~5-zN>>} zHbX_^-EmY-A2`|d89L;&qSC`Y37kB&*LNMK=aV7PXL0Q780XmA`T_VtV3voe%234M=q)Js|4}|?t*((a zTnYKg&V@c6&qeUgd)nJ_2o%}P^Z{PM4)zX4>AVVdsDL^25Azy&@ydS>1gc;p6vH5} zcN{w|06_y_2g~Ll;C;sqM=#rp1V=>z0n~XQ#$o1&VQ(cN;_|FX-`P|B5~&cW%>p`K zpZ5(Za48b<8k;Ro3K)>AVQXwg=NZ4DzUv$b*WO7rR;4D>pB|oW?S4=&DK%M>YHa?r z@O0ZD+=2~F^69wW2!UcsMa`5!!pY)%XKkwE$f<*|G4f|pi{T~IEkG;+4y!zvB5nFU z;E)GLOW#u{vBs8ZkOD1~{LCP2^7DiweXkJ-XTP**5oMf4X;M(A!{+RfHqEBYLA~_7 zVoH2w@Q-={l1pIIvpfymmkGks6nDJFN|MYNI4QMVhEmE(i?S?}?dDk6|7WEx2e8_> zS`E&ME9JSt(zN}DlybKm(k;~~#bqT*aaGr^pM3JkQ|}KPR=yK1wx9(;S(dGMYFSNI z^&GUI6z8$xY&JV-89!Cr{p<6WFH|8+>l&aRv7IW-w0?E>i>DzVEPsfx|(wJE$)aL%?;W^2jf!GKl;d zW#gkrDi5>~fRD7}|IT8YAu@-Mvt3^|w|;(axLE%mG|1uUnZ{#+DUxbTJnQu|_X%;z<#V9Bvf4ugVqqUkZGS}rtky!>_gzZ*Y?*}Ry_7D^luzrVw(UsEr&Ijt z@@oAJQs8&$qrfiTc;?Ef@wg*zo`ixg14V2%Dg`-mT$DB_RH!g})ESVUb8tl}i`dVK zr`YN|_8J=qMRg~B$@=B@#S_0^m3geRR=KlLxpSVhRi_k|>^~%JotfeYO)RX!onv3n zf0nH-K_OO{Cv7b<>DZmQ`~S*Hs~q8pAy!(1LSCjXU}q+tWp}!~O3_+CUcw5Cyo$MG zs`Jeiq58V{j_$d=xNvTmJIqm_L4A!Q?hO09%x!TV@YPRRW%b=UX_d`at*kODs|?Dj z%&;q)$eztKC^JoLW~OtGa-Vtas8eq%Gc!G`DU-c^>g_9c$?tEIL-!~%^^RTgIi2)y zNGZut?#qf?K9BS>MySrwUEj+As2>7=+6O({#7Zo{=k;s=Xf!AtFA_1hEYD-ih5a8O z&p319fLGbvg?y+=UJ0)Q6r*=^2lmydwiCP05{OG} zq~y=QfSZtDjTq}A(x&y4c~-w}HTxbBohO((H)x!}jKQDmg=YPW&iMc(e3t+L05W4~ z%A80Qr$)o1AJYrIZ!7#Nbw2VRD5(5^DwH}Oc$98KB!N~gA+bSjRRw)@-B^L3FvdWlJVPdsrw#q~q?Z9N%1ugn*7HQ{Qs5O_CV4WYfDMW8v>arjQZp+z?9>}3s416HY{ng9 zb(CMI;L?kG18Xs}V$1YEx6)#k0zcqy;Q zpkY>GW3}19=a$epaIgtHvZeRj?uqZ{cB?{>?&J>*beni6f2h~(mh%MZ;ci_YD>k$n z(|ETmtc9bXV}_pfwj1+!-z?O(jv1p^7*8n^oog{9`e^BxkxH3_R(hQ}+A$-Ive3JK zwySsrwRkijj0A*98wo7$m_^$78nTK$LL=r6H>4jo5K>;J)V_{RXA)GLjh6*+- z$6XPb^*6Hfjmor~aIqCQ6o5-ah=eGGNmzLf+erK@M-8F(jjbU8r0VQhYg0Rd!MQ>n zOkJDCHE7J_IEXvx;a>3_)*I6~xmNsLuT(6_e3T^ z#i_+EmpW(N%yveur<_?is&gK>9CsjB5}k63ewq|GNU#-y?@Soyww-hbt}nJ?C}1VR z+IRLeuL}Och6hA%&SIng+tm5{vIB9S#Ti_+M=PgIxEcnvBS8Z0g%zj8B*Z49C&mRdPHjb5#3?PHPFx>yj?-fSe#+CjWBQH z0NPkJ+{TVb#&CZIeKuAOD+pb(NLwxGAQa%t&~7mhQw0u!PyIiRje3DFJwzOTZU0U9tB0BZ{AmFG4i)%2{53;8FQ%a`H_F{Q3}YHdqx&=mg?Ws@5}mVuWp>A` z9AZ517=%EA@j7OG134s?R1vF@Iw}JrSWwNqRLwLKTzgx%I1A=nynZfBiTS-;*!v}z z5Sh**n|a+R7}5T53AxW^>HXu$Mnmb50w4bN)vVly%U7UPGKpS(iSBT1A+`Mnp&xd< z%Ct2>v&$1VKTHMyMG^*eJ~Sh89FTUQ0jnlkU=Vg<8Be76Z6UdCFAf@P!6am&!red5=uZTKX5zj{#Xz0`IN zrX+b7&}L*+vf|7@_qud+j|&u1WI8E8_BU|XQJq7iYBoS~BQaIMrKh9%85{|Iz-*`EGkI&Q^ zr}6Y@4r5&}a~VK@8DQZocidyc$dL>wD56Z*3+_2uCM256#WTyv6H-f(>d3pu>z=F% z?L9{~T7?F!*Ico>68X?s)v!&rh^nbWChE|GATAh3Mi{JZ0ZJEJU|zsomD*O|wz6j1 zSQ`4X++~D!F)fSy#%e2ji~L4-H{@NCccaX=Y#@lNXxM7kg%65YX&GBxbm0R7D>bs!hVv@A&%ofOG#l)D z#MhNllL|C0?6 zZ>l4YQ|M&7o>)%xGcTY(SPX1t*}x%XYCfA*yN2CY1Rm#B z^OI}?8FWFtD|ARFb+&T{qGLU>aKjyl4sum+!ySkY)qz;g9f+xGx)9iLXuGUlZ)ofnKBl88#giDU;nPQeY#}5bua7bhuMGZb@+!Oy2LWO-h#~rCmws z+@y3?QreP~HYKI?&T!LX_V6eI0n&C26o$_kQkE@Wkx@UNVJ?C9yM8{V5I!=n%IsZp9Zo7JHZ- z8i>gM0I)_)toE_eJn@`(8u{$F^O)kZ8CRr&gN^JMrbWnbzmrX~*k4!h+?9#wlK)aR z3regSSEk1_N?I~rB~ zLgp-kcuee5{DLwItm6~=*lDOErPSAC1oH4ITc6ozPxr(!Ug$1stV zP`3i#C#hUFA968S{#KCNZDfVUYPtKfDP&xFYAB{gsb)QiD^}=3*H)S$1*F3YeIyx{ zkZ3Ax1Ux99021K8vN2r9XA6V+6raJlSK9Ch5NY2V*rinZl)}a}X^5PN0~nEg_Rt(h zob8U>J)c*@(4%>((+Zmx{5s^i&V=i#I<6x{&q&9$A>p_lRvIpFT;aC54T{@Dc;NOl znGURS9xDXY+4i2+DB;N&k~UllOC_)mU}Drc;Z$%S`whh$QF=)^Dm{g*@~HjNMtqnsfIb0zRd!W#e=XihWJop+&zYe!?md z`_ebD!d#-|O4r0bsWW>ff4#^Hj8prFh!vpkAay=kI})R{@>a&U=XP4>`?h>q^nLrM zBq{^l)s?>VNa|8^H6hZ!NKK8$YyL}#YsXfz6Oo_84kxS? zE?|WV&=K~~PoQn24bzC!pC$d%(dPviUVGv4^-&R!dl*+p>{%*E(U=z~Fw}S)U>ZfN-tDkFvZ45oqeHbnzXewa#%2-v`qSq zJ;WonjOv9ra&*sh_Opl9gMeXfv10(#KIrUGAgK1NT!!v6Eh*vf16O&7K)S$nY%d~^ z={iUVEdt3_=OzasC!SG<;XYQ$hoP`!Vn1^DNR;wnpdmm@B{UqU-T#y51H?hZ{lxhg z)Vv^!$oh!wQn4e?fP`} zwm{mtSK$u`ZCu@}7bbIj@R4!sDuImI5@N^WrM`G!Bbi*Yq3*%jBbXS?R@i$uCkwZr z!7t-vij@76;s(rVIzLvHh?EU+b0;`Q4e5fL&*x&AAlda2XhU%B0I*5i?8br*4zI?| z){m08dE6w(g%Rw`Gq(K0{Cp32Di`qcFJPe%Kkxh>nWFWzH`q-7=MiS0>(eHIKlV>3ogp|Phu`0iSb9RoFKnI zq9w%#;UL04t!%VcE^j?Ut7=w=fRq)k!UJi;21tfUg$tzn@8#MMc1)uxG}sG7;6b0^ zY)9!TN4F+57C~y1Ky1`#VuQoJBQ8=L-ZkNsM^uD*rEm zx~cFqz#`$o1~e`}MDRfe6ZS}L%P+#N30|$L=EJWP&SN4*Ct@ReDT5Vo?f7lHIK)qd z4MeVIsc)#ZFt4LX7f*;4P0-9Fq9Nx&#Wh7)H3{84J=n$_3RPr#<0!~wj7&0j2F!v0 z<28SwQ2BM0+VB^e2mfxNf=-tDrYg8xIs=v<42EcgEHlXBLSI1%Fiqo((Nqe3(J@?S z14BM2R#FQcMTWRWrO9Gg)nr`eLJY@w?;(ZHlfT+1ULe9Ch*10a#>j(}8fG-{weYXW z;_DwVMlio1e^gtEl_17y$3d(#A**AN{94?(yG9|ibx>*>1A=n)N^PTX^(^(p#~Yu) z^f2aL_0AJg+eBi`B3mOOcuJj3smoI8ij=FBx)P<%sMML1I=xcoQtI-Qx_qT>ky6*7 zEJN8crOvI?)dt-un>!4y$-Dih?L98HS~XSNi^*B3t%nGs%GTJCKDC{fI@w+~?>9|( zl3#2upVyI=ztX4AU+CBGES|R#cR6c}`IFWeq?aS|k(}5!^SzkchV@K?6}vvaLA@jH zh+UpPN4 zIlk0T?TM^C6puFuE&w{10N>{POex0nveA!@_$Np1Lo0RlG~Xz1ukGc9)uM@IKF2+n zNLErK<0g>&M3m3Jds1DI4>4{N)f^w635+Y5Xn(o07h{!Kv33k#UOp#2-hwBwG+@q! z1UwJIoXfXqeapO+{%QYRX$s-YhmdR@MMgCok|&5(jC0(c$U6w}BLY-+VQh&SWX1VlX1&}O??vF0mQShp z4+B#`4JQA~i`-hf74h*_wNV6~iuL}>fw^#TK2co9Oe3l|8M68=!zuz)g#nJ2>sc!3 zT}hR;h7vxahaIb7EeOTwn!T7^7|l}AK!>S)-1*J!LEx?EdFtH$Gh%oS#j=K5$24cT z>tun^I@EFI(TjUo51eDgqsKY}<+A3~dZoA)alF*^c@v}n#iQ1*N?}r92Xzc19Xf|t zMx|1&2Q7X|5^pea6c;&eU~fqQG80es9#ZA?D~O<|>j@*fQDnA%@MN#vXN$b7zSfCL z=#W7i#C$_|Ht?Aq`32Q9825jCve)1n8+jPF$Vccn?*HUuugN!Fn{SD%9{EJ)8^i6G z$nD5SJFGmP=GN!R@1Lz#%Jb@a=OSR32datJ^&(YE$WM+JRyqzMmXO8+qjND1?f{}q zQw$h3U#dHP0OZZ%hQ6!8>ELpMC(?g79*?ExH~BL>kp~>al6v+Q1|HyAfb%>);-C!Q zSapcxysI!jwHt9;1UT2~s4T@qhiWfLzNs3%@$lB`S?|dBQ<05Fo-pTIL*n;A9foP` zye27dKT>Q~o_zYO)aFN8?vG;yP1{mRVlBp~dYS^j>Np?##*+vvWGd5)ir?gOv007I ze&3{a1DkJTYfWrcgR5}1verapV6IGCUI6l)Z&a!qm01nGaad3io|Z>=)WYa|u0d+c zB++mc$s#tb7A8TUfHG))*|EsWG$^xaW&R?>+P{x9%_oospN66Hf2%rj z)8)wX(Yh6Vcv=}yfDY2532Fy3Ra68%11-iffF+_kLOAB#*{m|mZ`D*pCZjz-4iV!# zj&?C`PSxC%C!am*8!exWOM(AF2CxhP!RKsH7!yP~77BE)_%4O2)*3O{I&w`6q&_{V zHUTk6E3?p^UOZ;sYp3WHfuK~IC{9N6LMyHUh9UAYE8JL`p<U`mB z+-ELL@l-~BhJnVQaM(Ihi}Ta{A3BfjOcmz(^?~@h)cQKM1O4OEDO~fY|1|w0wY`Qm zNe4-RUZnV98j28Oy$tM~WqPTt1=$cSq=r;2bvk&G>*&@J zNw}sY$X4G083>YsIa)EN9mZH>rTQuj9dsw2YJ;zqH?SU0aCwS4-q5dT`?P&0EuMtF zk2uwL6CH(sJVq)(YI|X5K!1Z2EY+IeA{#Wk=@r3+xay$qTCVL&+%-#6py@U9!Ws6j zW}XtoW7JFw%>Uks5#ob~VF7)cDm;;C?*iR*4XoT`f13s^PJ>q0;D@yO%r7b}c?cJ# z%Pn~+KE2~$V#=Hph}3o;_zgXk+U_C7Tam|hmYjO)%I^A!;6ds)w0Is;>P*WB-NEq( z>~NZO!n_MpTPoDd2^Tj43oryJ0wgF<+PR!)u~2Hwt4d3Q(z1w!Q5`F8a1O4~ z+XrJ~JP`_xaKx?o6^ybb*qZ3a_@sXDhcNSfirmr|_g%$2pn&C5Ve4q^DV_#yivdRh zM9|kq_HPjVqs0&<=wCX*MBoby{R5K#sC^L9=xsnlU8Eij4GmB5@u3BZr+m|a{3d?^ zu%x6;YN-WLDJ}Vl=3DeiOPPPPCo=aE-W689063|sn1lYJ*F#u9D=Ol&X23Q2r724V zcjOaQmY>%?JqLoXS-%QxU&%`HAj%a4Eg0A~FYQKojZzlx)dF zeMHv=@c1|qCt(4?B;G|+?XV>a!LHyCqXK2uCBmHndk$iG>Uvt2C*fsGZ*DtuM1W3aUt@)^OMU4uFUc|mak0WGc*mH(OUSbxwwLW7F_*~L z8LTpiXu+ty0JTu)##W;FU9PoUxE_&QuW7(+n~C!RQ*9DRh8~M zQR7=fQ!~z(&qsqJr@qLh3!#R+4xf0`u!i&DGj+wZH`i#JwNO+3+o{x+*u8LKp^Y%abth!kTD%Agx&Y-S??%U;|6LAkucKyZj>TzFeSE+8~FJx>aPh2%pi z15u?AIz>Jt&O+>VWnJ7B=mtEXnXriJ1oY7)AJIhzuY!V=Z6cqJa9RHDN5>ht98%4e~-H|cr`cd$}|i?75y7VmWCY*L}(621CA_Zbv9loQx!$vBNUzFv)NYY@v3bSws|0h z|ES#tXTXg1tl;K^4*>*PRlZ$=XXH!3VL*_25HMxpJu^VGA4A4oazaq01qCvJM5|Kj zk^-cbRI-hZL&Ngfv0q9*UqP8K!|T{2yh0j~NSP3djA{*~kd?6)Nu_iFTSpykl1f02B!{ z`G_dLpXOD{Dwm@G0>=$BTi@dhHMKc*A*bt7K;qon{8cWGlj=}kS=}(59dPbdRyQ<# z7?g9+i(qME@S$UH>*pvbN?}>Bu*zMr^wQw!070(uR32tPqJ1lCWL(ft_rAD z5H6-4ArTHGBm$@mI1ocTe2U7o9SVe)nG^T2!ZN1cJk-F-pK~C}Y+2wTuYB6ne2=vC zbLYp+(>a@7j5VaTJ}er(_#inyCdL^WqZhDQqOgHPo);r|1JGNz92xeHT_g^a!iMN? zKsr)jGcYJ^-5y+}Lj_=%<_ffaamJL|W~!~*P$RlNj)dVbnNZX8pkN3RbJMfDi+zy0 z5i|~NLfcZ?kE!L(qQYg=ASm{bD0hq4ZALQjL%;)Mi~}nCcLb-4&Pel2jt}e!g%f1P zO0;27fxR3Qx6s7a2xv&xOc{%R3F2q}orX}u z1r1?oYMi>aPVUYK9Zs|FK?vT4{t04~VP+3hxreQ85C@&7IbQCkb;+P&QbjhEKUOzj z;AqiN!}Jm`h5slkT*O~(5W~(sX@ir@QsKRg_ z#vLqNjxb21cVdENrlPUja6#tgVV0SUy0qM%;tr>em6qB-Ck8FsdY`!)z3zFl`{N^;a*FK3G#61vY;KLHEy4bTn zf+PtSY3n(x&c`ZA5^uj34>)NrwQ$U z!1h4afxT_l?LUl0p+@x&CN;YE2?`gvYvvI6N@21M5D{Ss>Pgdg6uR2e=q+#zZc)^tQdaaflzGDH%h0rUCEdE9 zGt}XpH$2>v@I*c&(ZZ6p<-iey!*o&xn}Mv0X-)^qqeX5bUMPf|ca{uEhL>rp9#QNb zjfh}t09#@|`bq-^l+aUb<^xuSRFgZQntrd2+zPK|@_xzS{a96Ufa#0rt@eEVqr0pNjgIfrM1OW^&O%$+aRsQ(zTx&H}bvw!>qSA|1HL(wn+R+Gm)TE8eu&w1KiV>g+ zvf$12kk@sJfkLsgaU0suOtrj3hb_e3Ar6NqcJ}dY%gz04z}|y(VidY8;SPdo@B4nE zmVfdk(&Mq8YnxW7!p)Bt3#Kj81(P^KFjbZdrdja1*5db7{Djn$R9$MSn5zEiN5Nmb z31tiMtH;lWpB68{*2!|(aB5-2@Z5akD_D6}MSL%Gg|-|YbGH|rFa;XP{~z$pVpFj- z%2%wESd^J&g|^0KhNtpfR|>W@>Xj0sQess~vXqi++6c?z1i^MPcoTkiNuyHYR!Yi} zH?#s3M!p|cYNIeMj~6_F1mXo|#0$uNpe`7lr=+%a+)Qe*DlJK|ftIXEEngNJ2>T7{ z+V%Qg4220hd5bM;)3D9=)Z4LfaG=iUr^=^u6t`h5tb9p6ZCLXa`Lwj|5|2`1Q�h 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 diff --git a/reformat/AWGS.cpp b/reformat/AWGS.cpp index b214020..ef49d8f 100644 --- a/reformat/AWGS.cpp +++ b/reformat/AWGS.cpp @@ -79,7 +79,7 @@ ReformatAWGS_WP::Process(const ReformatHolder* pHolder, } /* date/time are pascal strings */ - WMSG2("File saved at '%.26s' '%.10s'\n", srcBuf + 6, srcBuf + 32); + WMSG2("File saved at '%.26hs' '%.10s'\n", srcBuf + 6, srcBuf + 32); srcBuf += kWPGlobalsLen; srcLen -= kWPGlobalsLen; diff --git a/reformat/AWGS.h b/reformat/AWGS.h index 08f3ffc..b383332 100644 --- a/reformat/AWGS.h +++ b/reformat/AWGS.h @@ -6,8 +6,8 @@ /* * AWGS file handling. */ -#ifndef __LR_AWGS__ -#define __LR_AWGS__ +#ifndef REFORMAT_AWGS_H +#define REFORMAT_AWGS_H #include "ReformatBase.h" @@ -89,4 +89,4 @@ private: long* pSrcLen, int blockCount); }; -#endif /*__LR_AWGS__*/ +#endif /*REFORMAT_AWGS_H*/ diff --git a/reformat/AppleWorks.h b/reformat/AppleWorks.h index 78f5cf8..1b92502 100644 --- a/reformat/AppleWorks.h +++ b/reformat/AppleWorks.h @@ -6,8 +6,8 @@ /* * Reformat AppleWorks documents. */ -#ifndef __LR_APPLEWORKS__ -#define __LR_APPLEWORKS__ +#ifndef REFORMAT_APPLEWORKS_H +#define REFORMAT_APPLEWORKS_H #include "ReformatBase.h" @@ -242,4 +242,4 @@ private: double ConvertSANEDouble(const unsigned char* srcPtr); }; -#endif /*__LR_APPLEWORKS__*/ +#endif /*REFORMAT_APPLEWORKS_H*/ diff --git a/reformat/Asm.cpp b/reformat/Asm.cpp index 17bf112..114452b 100644 --- a/reformat/Asm.cpp +++ b/reformat/Asm.cpp @@ -861,7 +861,7 @@ ReformatLISA3::Process(const ReformatHolder* pHolder, OutputStart(); PrintSymEntry(ii); OutputFinish(); - WMSG2("%d: %s\n", ii, GetOutBuf()); + WMSG2("%d: %hs\n", ii, GetOutBuf()); } #endif diff --git a/reformat/Asm.h b/reformat/Asm.h index 440dd22..34ab509 100644 --- a/reformat/Asm.h +++ b/reformat/Asm.h @@ -6,8 +6,8 @@ /* * Reformat assembly-language source code. */ -#ifndef __LR_ASM__ -#define __LR_ASM__ +#ifndef REFORMAT_ASM_H +#define REFORMAT_ASM_H #include "ReformatBase.h" @@ -289,4 +289,4 @@ private: unsigned char fCpuType; }; -#endif /*__LR_ASM__*/ +#endif /*REFORMAT_ASM_H*/ diff --git a/reformat/BASIC.h b/reformat/BASIC.h index ebec1cf..6343eaa 100644 --- a/reformat/BASIC.h +++ b/reformat/BASIC.h @@ -6,8 +6,8 @@ /* * Reformat BASIC programs. */ -#ifndef __LR_BASIC__ -#define __LR_BASIC__ +#ifndef REFORMAT_BASIC_H +#define REFORMAT_BASIC_H #include "ReformatBase.h" @@ -61,4 +61,4 @@ public: static const char* GetBusinessTokens(void); }; -#endif /*__LR_BASIC__*/ \ No newline at end of file +#endif /*REFORMAT_BASIC_H*/ diff --git a/reformat/CPMFiles.h b/reformat/CPMFiles.h index 56143ca..a8b2112 100644 --- a/reformat/CPMFiles.h +++ b/reformat/CPMFiles.h @@ -6,8 +6,8 @@ /* * Reformat CP/M files. */ -#ifndef __LR_CPMFILES__ -#define __LR_CPMFILES__ +#ifndef REFORMAT_CPMFILES_H +#define REFORMAT_CPMFILES_H #include "ReformatBase.h" @@ -25,4 +25,4 @@ public: ReformatOutput* pOutput); }; -#endif /*__LR_CPMFILES__*/ +#endif /*REFORMAT_CPMFILES_H*/ diff --git a/reformat/Directory.cpp b/reformat/Directory.cpp index 22bb8db..e7d1dc6 100644 --- a/reformat/Directory.cpp +++ b/reformat/Directory.cpp @@ -108,21 +108,23 @@ ReformatDirectory::PrintDirEntries(const unsigned char* srcBuf, strncpy(fileName, (const char*)&pDirEntry[0x01], kMaxFileName); fileName[nameLen] = '\0'; - CString createStr, modStr; + CString createStrW, modStrW; A2FileProDOS::ProDate prodosDateTime; prodosDateTime = pDirEntry[0x18] | pDirEntry[0x19] << 8 | pDirEntry[0x1a] << 16 | pDirEntry[0x1b] << 24; - FormatDate(A2FileProDOS::ConvertProDate(prodosDateTime), &createStr); + FormatDate(A2FileProDOS::ConvertProDate(prodosDateTime), &createStrW); + CStringA createStr(createStrW); prodosDateTime = pDirEntry[0x21] | pDirEntry[0x22] << 8 | pDirEntry[0x23] << 16 | pDirEntry[0x24] << 24; - FormatDate(A2FileProDOS::ConvertProDate(prodosDateTime), &modStr); + FormatDate(A2FileProDOS::ConvertProDate(prodosDateTime), &modStrW); + CStringA modStr(modStrW); char lockedFlag = '*'; if (pDirEntry[0x1e] & 0x80) lockedFlag = ' '; - CString auxTypeStr; + CStringA auxTypeStr; unsigned short auxType = pDirEntry[0x1f] | pDirEntry[0x20] << 8; if (pDirEntry[0x10] == 0x06) // bin auxTypeStr.Format("A=$%04X", auxType); diff --git a/reformat/Directory.h b/reformat/Directory.h index 3a0f56a..86d2040 100644 --- a/reformat/Directory.h +++ b/reformat/Directory.h @@ -6,8 +6,8 @@ /* * Reformat subdirectories. */ -#ifndef __LR_DIRECTORY__ -#define __LR_DIRECTORY__ +#ifndef REFORMAT_DIRECTORY_H +#define REFORMAT_DIRECTORY_H #include "ReformatBase.h" @@ -29,4 +29,4 @@ private: long srcLen, bool showDeleted); }; -#endif /*__LR_DIRECTORY__*/ +#endif /*REFORMAT_DIRECTORY_H*/ diff --git a/reformat/Disasm.cpp b/reformat/Disasm.cpp index 1ef75e1..b277269 100644 --- a/reformat/Disasm.cpp +++ b/reformat/Disasm.cpp @@ -1221,13 +1221,13 @@ OMFSegmentHeader::Unpack(const unsigned char* srcBuf, long srcLen, segLabelLen = *segName++; memcpy(fSegName, segName, segLabelLen); fSegName[segLabelLen] = '\0'; - WMSG1(" OMF: Pascal segment label '%s'\n", fSegName); + WMSG1(" OMF: Pascal segment label '%hs'\n", fSegName); } else { /* C-style or non-terminated string */ segLabelLen = fLabLen; memcpy(fSegName, segName, segLabelLen); fSegName[segLabelLen] = '\0'; - WMSG1(" OMF: Std segment label '%s'\n", fSegName); + WMSG1(" OMF: Std segment label '%hs'\n", fSegName); } fReady = true; @@ -1299,7 +1299,7 @@ void OMFSegmentHeader::Dump(void) const { WMSG0("OMF segment header:\n"); - WMSG3(" segNum=%d loadName='%s' segName='%s'\n", + WMSG3(" segNum=%d loadName='%hs' segName='%hs'\n", fSegNum, fLoadName, fSegName); WMSG4(" blockCnt=%ld byteCnt=%ld resSpc=%ld length=%ld\n", fBlockCnt, fByteCnt, fResSpc, fLength); diff --git a/reformat/Disasm.h b/reformat/Disasm.h index 5be8f52..a00b134 100644 --- a/reformat/Disasm.h +++ b/reformat/Disasm.h @@ -9,8 +9,8 @@ * Lots of credit to the classic "Programming the 65816" book by David Eyes * and Ron Lichty. */ -#ifndef __LR_DISASM__ -#define __LR_DISASM__ +#ifndef REFORMAT_DISASM_H +#define REFORMAT_DISASM_H #include "ReformatBase.h" @@ -450,4 +450,4 @@ private: const unsigned char* srcBuf, long srcLen, bool shortRegs); }; -#endif /*__LR_DISASM__*/ \ No newline at end of file +#endif /*REFORMAT_DISASM_H*/ diff --git a/reformat/DisasmTable.cpp b/reformat/DisasmTable.cpp index 5212aae..021c423 100644 --- a/reformat/DisasmTable.cpp +++ b/reformat/DisasmTable.cpp @@ -771,7 +771,7 @@ ReformatDisasm65xxx::ValidateOpCodeDetails(void) if (strcasecmp(kOpCodeDetails[i].mnemonic, kOpCodeDetails[j].mnemonic) == 0) { - WMSG3("OpCodeDetails GLITCH: entries %d and %d match (%s)\n", + WMSG3("OpCodeDetails GLITCH: entries %d and %d match (%hs)\n", i, j, kOpCodeDetails[i].mnemonic); assert(false); return false; diff --git a/reformat/DoubleHiRes.h b/reformat/DoubleHiRes.h index fd18bd0..0a72fab 100644 --- a/reformat/DoubleHiRes.h +++ b/reformat/DoubleHiRes.h @@ -6,8 +6,8 @@ /* * Reformat double-hi-res graphics. */ -#ifndef __LR_DOUBLEHIRES__ -#define __LR_DOUBLEHIRES__ +#ifndef REFORMAT_DOUBLEHIRES_H +#define REFORMAT_DOUBLEHIRES_H #include "ReformatBase.h" @@ -51,4 +51,4 @@ public: unsigned int fColorLookup[4][kNumDHRColors]; }; -#endif /*__LR_DOUBLEHIRES__*/ +#endif /*REFORMAT_DOUBLEHIRES_H*/ diff --git a/reformat/HiRes.h b/reformat/HiRes.h index dcf786a..154735e 100644 --- a/reformat/HiRes.h +++ b/reformat/HiRes.h @@ -6,8 +6,8 @@ /* * Reformat hi-res images. */ -#ifndef __LR_HIRES__ -#define __LR_HIRES__ +#ifndef REFORMAT_HIRES_H +#define REFORMAT_HIRES_H #include "ReformatBase.h" @@ -41,4 +41,4 @@ public: bool fBlackWhite; }; -#endif /*__LR_HIRES__*/ +#endif /*REFORMAT_HIRES_H*/ diff --git a/reformat/MacPaint.h b/reformat/MacPaint.h index f8c2ab4..e5cdf2e 100644 --- a/reformat/MacPaint.h +++ b/reformat/MacPaint.h @@ -6,8 +6,8 @@ /* * Convert MacPaint files. */ -#ifndef __LR_MACPAINT__ -#define __LR_MACPAINT__ +#ifndef REFORMAT_MACPAINT_H +#define REFORMAT_MACPAINT_H #include "ReformatBase.h" @@ -38,4 +38,4 @@ private: }; }; -#endif /*__LR_MACPAINT__*/ +#endif /*REFORMAT_MACPAINT_H*/ diff --git a/reformat/NiftyList.cpp b/reformat/NiftyList.cpp index 8236886..e3225c3 100644 --- a/reformat/NiftyList.cpp +++ b/reformat/NiftyList.cpp @@ -51,7 +51,7 @@ * Load the NiftyList data file. */ /*static*/ bool -NiftyList::AppInit(const char* fileName) +NiftyList::AppInit(const WCHAR* fileName) { FILE* fp = nil; long fileLen; @@ -61,12 +61,12 @@ NiftyList::AppInit(const char* fileName) /* * Open the NList.Data file and load the contents into memory. */ - fp = fopen(fileName, "rb"); + fp = _wfopen(fileName, L"rb"); if (fp == nil) { - WMSG1("NL Unable to open '%s'\n", fileName); + WMSG1("NL Unable to open '%ls'\n", fileName); goto bail; } else { - WMSG1("NL Reading '%s'\n", fileName); + WMSG1("NL Reading '%ls'\n", fileName); } if (fseek(fp, 0, SEEK_END) < 0) { @@ -225,7 +225,7 @@ NiftyList::ReadSection(char** ppData, long* pRemLen, DataSet* pSet, } if (lineLen < 6 || pData[4] != ' ') { - WMSG1("Found garbled line '%.80s'\n", pData); + WMSG1("Found garbled line '%.80hs'\n", pData); return false; } if (pData[lineLen-2] == '\r' || pData[lineLen-2] == '\n') @@ -233,7 +233,7 @@ NiftyList::ReadSection(char** ppData, long* pRemLen, DataSet* pSet, else if (pData[lineLen-1] == '\r' || pData[lineLen-1] == '\n') pData[lineLen-1] = '\0'; else { - WMSG2("No EOL found on '%.80s' (%d)\n", pData, lineLen); + WMSG2("No EOL found on '%.80hs' (%d)\n", pData, lineLen); } assert(entry < numLines); @@ -341,7 +341,7 @@ NiftyList::DumpSection(const DataSet& dataSet) WMSG1("Dumping section (count=%ld)\n", dataSet.numEntries); for (ent = 0; ent < dataSet.numEntries; ent++) { - WMSG3("%4d: %04x '%s'\n", + WMSG3("%4d: %04x '%hs'\n", ent, dataSet.pEntries[ent].value, dataSet.pEntries[ent].name); } } diff --git a/reformat/PascalFiles.h b/reformat/PascalFiles.h index f5a9d8e..79a42d5 100644 --- a/reformat/PascalFiles.h +++ b/reformat/PascalFiles.h @@ -6,8 +6,8 @@ /* * Reformat UCSD Pascal files. */ -#ifndef __LR_PASCALFILES__ -#define __LR_PASCALFILES__ +#ifndef REFORMAT_PASCALFILES_H +#define REFORMAT_PASCALFILES_H #include "ReformatBase.h" @@ -100,4 +100,4 @@ private: void ProcessBlock(const unsigned char* srcBuf, long length); }; -#endif /*__LR_PASCALFILES__*/ +#endif /*REFORMAT_PASCALFILES_H*/ diff --git a/reformat/PrintShop.h b/reformat/PrintShop.h index 45c434a..be3c86b 100644 --- a/reformat/PrintShop.h +++ b/reformat/PrintShop.h @@ -6,8 +6,8 @@ /* * Convert Print Shop graphics. */ -#ifndef __LR_PRINTSHOP__ -#define __LR_PRINTSHOP__ +#ifndef REFORMAT_PRINTSHOP_H +#define REFORMAT_PRINTSHOP_H #include "ReformatBase.h" @@ -32,4 +32,4 @@ private: MyDIBitmap* ConvertColor(const unsigned char* srcBuf); }; -#endif /*__LR_PRINTSHOP__*/ +#endif /*REFORMAT_PRINTSHOP_H*/ diff --git a/reformat/Reformat.cpp b/reformat/Reformat.cpp index 819a549..f437b5a 100644 --- a/reformat/Reformat.cpp +++ b/reformat/Reformat.cpp @@ -102,164 +102,164 @@ ReformatHolder::GetReformatInstance(ReformatID id) * Would have been nice to embed these in the individual classes, but that's * harder to maintain. */ -/*static*/ const char* +/*static*/ const WCHAR* ReformatHolder::GetReformatName(ReformatID id) { - const char* descr = nil; + const WCHAR* descr = nil; switch (id) { case kReformatTextEOL_HA: - descr = "Converted Text"; + descr = L"Converted Text"; break; case kReformatRaw: - descr = "Raw"; + descr = L"Raw"; break; case kReformatHexDump: - descr = "Hex Dump"; + descr = L"Hex Dump"; break; case kReformatResourceFork: - descr = "Resource Fork"; + descr = L"Resource Fork"; break; case kReformatProDOSDirectory: - descr = "ProDOS Directory"; + descr = L"ProDOS Directory"; break; case kReformatPascalText: - descr = "Pascal Text"; + descr = L"Pascal Text"; break; case kReformatPascalCode: - descr = "Pascal Code"; + descr = L"Pascal Code"; break; case kReformatCPMText: - descr = "CP/M Text"; + descr = L"CP/M Text"; break; case kReformatApplesoft: - descr = "Applesoft BASIC"; + descr = L"Applesoft BASIC"; break; case kReformatApplesoft_Hilite: - descr = "Applesoft BASIC w/Highlighting"; + descr = L"Applesoft BASIC w/Highlighting"; break; case kReformatInteger: - descr = "Integer BASIC"; + descr = L"Integer BASIC"; break; case kReformatInteger_Hilite: - descr = "Integer BASIC w/Highlighting"; + descr = L"Integer BASIC w/Highlighting"; break; case kReformatBusiness: - descr = "Apple /// Business BASIC"; + descr = L"Apple /// Business BASIC"; break; case kReformatBusiness_Hilite: - descr = "Apple /// Business BASIC w/Highlighting"; + descr = L"Apple /// Business BASIC w/Highlighting"; break; case kReformatSCAssem: - descr = "S-C Assembler"; + descr = L"S-C Assembler"; break; case kReformatMerlin: - descr = "Merlin Assembler"; + descr = L"Merlin Assembler"; break; case kReformatLISA2: - descr = "LISA Assembler (v2)"; + descr = L"LISA Assembler (v2)"; break; case kReformatLISA3: - descr = "LISA Assembler (v3)"; + descr = L"LISA Assembler (v3)"; break; case kReformatLISA4: - descr = "LISA Assembler (v4/v5)"; + descr = L"LISA Assembler (v4/v5)"; break; case kReformatMonitor8: - descr = "//e monitor listing"; + descr = L"//e monitor listing"; break; case kReformatDisasmMerlin8: - descr = "8-bit disassembly (Merlin)"; + descr = L"8-bit disassembly (Merlin)"; break; case kReformatMonitor16Long: - descr = "IIgs monitor listing (long regs)"; + descr = L"IIgs monitor listing (long regs)"; break; case kReformatMonitor16Short: - descr = "IIgs monitor listing (short regs)"; + descr = L"IIgs monitor listing (short regs)"; break; case kReformatDisasmOrcam16: - descr = "16-bit disassembly (Orca/M)"; + descr = L"16-bit disassembly (Orca/M)"; break; case kReformatAWGS_WP: - descr = "AppleWorks GS Word Processor"; + descr = L"AppleWorks GS Word Processor"; break; case kReformatTeach: - descr = "Teach Text"; + descr = L"Teach Text"; break; case kReformatGWP: - descr = "Generic IIgs text document"; + descr = L"Generic IIgs text document"; break; case kReformatMagicWindow: - descr = "Magic Window"; + descr = L"Magic Window"; break; case kReformatGutenberg: - descr = "Gutenberg Word Processor"; + descr = L"Gutenberg Word Processor"; break; case kReformatAWP: - descr = "AppleWorks Word Processor"; + descr = L"AppleWorks Word Processor"; break; case kReformatADB: - descr = "AppleWorks Database"; + descr = L"AppleWorks Database"; break; case kReformatASP: - descr = "AppleWorks Spreadsheet"; + descr = L"AppleWorks Spreadsheet"; break; case kReformatHiRes: - descr = "Hi-Res / Color"; + descr = L"Hi-Res / Color"; break; case kReformatHiRes_BW: - descr = "Hi-Res / B&W"; + descr = L"Hi-Res / B&W"; break; case kReformatDHR_Latched: - descr = "Double Hi-Res / Latched"; + descr = L"Double Hi-Res / Latched"; break; case kReformatDHR_BW: - descr = "Double Hi-Res / B&W"; + descr = L"Double Hi-Res / B&W"; break; case kReformatDHR_Plain140: - descr = "Double Hi-Res / Plain140"; + descr = L"Double Hi-Res / Plain140"; break; case kReformatDHR_Window: - descr = "Double Hi-Res / Windowed"; + descr = L"Double Hi-Res / Windowed"; break; case kReformatSHR_PIC: - descr = "Super Hi-Res"; + descr = L"Super Hi-Res"; break; case kReformatSHR_JEQ: - descr = "JEQ Super Hi-Res"; + descr = L"JEQ Super Hi-Res"; break; case kReformatSHR_Paintworks: - descr = "Paintworks Super Hi-Res"; + descr = L"Paintworks Super Hi-Res"; break; case kReformatSHR_Packed: - descr = "Packed Super Hi-Res"; + descr = L"Packed Super Hi-Res"; break; case kReformatSHR_APF: - descr = "APF Super Hi-Res"; + descr = L"APF Super Hi-Res"; break; case kReformatSHR_3200: - descr = "3200-Color Super Hi-Res"; + descr = L"3200-Color Super Hi-Res"; break; case kReformatSHR_3201: - descr = "Packed 3200-Color Super Hi-Res"; + descr = L"Packed 3200-Color Super Hi-Res"; break; case kReformatSHR_DG256: - descr = "DreamGrafix 256-Color Super Hi-Res"; + descr = L"DreamGrafix 256-Color Super Hi-Res"; break; case kReformatSHR_DG3200: - descr = "DreamGrafix 3200-Color Super Hi-Res"; + descr = L"DreamGrafix 3200-Color Super Hi-Res"; break; case kReformatPrintShop: - descr = "Print Shop Clip Art"; + descr = L"Print Shop Clip Art"; break; case kReformatMacPaint: - descr = "MacPaint"; + descr = L"MacPaint"; break; case kReformatUnknown: case kReformatMAX: default: assert(false); - descr = "UNKNOWN"; + descr = L"UNKNOWN"; break; } @@ -279,13 +279,12 @@ ReformatHolder::SetSourceAttributes(long fileType, long auxType, fAuxType = auxType; fSourceFormat = sourceFormat; + assert(fNameExt == nil); if (nameExt == nil) { - assert(fNameExt == nil); fNameExt = new char[1]; fNameExt[0] = '\0'; } else { - fNameExt = new char[strlen(nameExt)+1]; - strcpy(fNameExt, nameExt); + fNameExt = strdup(nameExt); } } @@ -489,7 +488,7 @@ ReformatHolder::Apply(ReformatPart part, ReformatID id) if (fErrorBuf[part] != nil) { pOutput->SetTextBuf(fErrorBuf[part], strlen(fErrorBuf[part]), false); pOutput->SetOutputKind(ReformatOutput::kOutputErrorMsg); - pOutput->SetFormatDescr(_T("Error Message")); + pOutput->SetFormatDescr(L"Error Message"); return pOutput; } @@ -590,9 +589,13 @@ ReformatHolder::SetErrorMsg(ReformatPart part, const char* msg) assert(part > kPartUnknown && part < kPartMAX); assert(fErrorBuf[part] == nil); - fErrorBuf[part] = new char[strlen(msg) + 1]; - if (fErrorBuf[part] != nil) { - WMSG2("+++ set error message for part %d to '%s'\n", part, msg); - strcpy(fErrorBuf[part], msg); - } + fErrorBuf[part] = strdup(msg); + WMSG2("+++ set error message for part %d to '%hs'\n", part, msg); +} + +void +ReformatHolder::SetErrorMsg(ReformatPart part, const CString& str) +{ + CStringA stra(str); + SetErrorMsg(part, (LPCSTR) stra); } diff --git a/reformat/Reformat.h b/reformat/Reformat.h index 0a63945..0ddaf16 100644 --- a/reformat/Reformat.h +++ b/reformat/Reformat.h @@ -11,8 +11,8 @@ * Currently missing: a way to provide progress updates when reformatting * a large file. */ -#ifndef __LR_REFORMAT__ -#define __LR_REFORMAT__ +#ifndef REFORMAT_REFORMAT_H +#define REFORMAT_REFORMAT_H #include "../util/UtilLib.h" @@ -230,7 +230,10 @@ public: delete[] fNameExt; } - /* set attributes before calling TestApplicability */ + /** + * Set attributes before calling TestApplicability. We want the + * Apple II filename, so the name extension is passed as a narrow string. + */ void SetSourceAttributes(long fileType, long auxType, SourceFormat sourceFormat, const char* nameExt); /* run through the list of reformatters, testing each against the data */ @@ -257,12 +260,13 @@ public: /* use this to force "reformatted" output to show an error instead */ void SetErrorMsg(ReformatPart part, const char* msg); + void SetErrorMsg(ReformatPart part, const CString& str); /* give a pointer (allocated with new[]) for one of the inputs */ void SetSourceBuf(ReformatPart part, unsigned char* buf, long len); - static const char* GetReformatName(ReformatID id); + static const WCHAR* GetReformatName(ReformatID id); /* make these friends so they can call the "protected" stuff below */ @@ -374,7 +378,7 @@ public: ReformatOutput(void) : fOutputKind(kOutputUnknown), //fOutputID - fOutputFormatDescr(_T("(none)")), + fOutputFormatDescr(L"(none)"), fMultipleFonts(false), fTextBuf(nil), fTextLen(-1), @@ -394,7 +398,7 @@ public: const char* GetTextBuf(void) const { return fTextBuf; } long GetTextLen(void) const { return fTextLen; } const MyDIBitmap* GetDIB(void) const { return fpDIB; } - const char* GetFormatDescr(void) const { return fOutputFormatDescr; } + const WCHAR* GetFormatDescr(void) const { return fOutputFormatDescr; } // multiple-font flag currently not used bool GetMultipleFontsFlag(void) const { return fMultipleFonts; } @@ -402,12 +406,14 @@ public: * Setters, used by reformatters. */ /* set the format description; string must be persistent (static) */ - void SetFormatDescr(const char* str) { fOutputFormatDescr = str; } + void SetFormatDescr(const WCHAR* str) { fOutputFormatDescr = str; } /* set the kind of output we're providing */ void SetOutputKind(OutputKind kind) { fOutputKind = kind; } void SetMultipleFontsFlag(bool val) { fMultipleFonts = val; } /* set the output */ + // TODO: split into two different functions, one takes const char* and + // doesn't delete, the other char* and does delete void SetTextBuf(char* buf, long len, bool doDelete) { assert(fTextBuf == nil); fTextBuf = buf; @@ -424,7 +430,7 @@ private: /* what we're holding */ OutputKind fOutputKind; //ReformatID fOutputID; - const char* fOutputFormatDescr; + const WCHAR* fOutputFormatDescr; /* output RTF uses multiple fonts, so ignore font change request */ bool fMultipleFonts; @@ -447,7 +453,7 @@ private: class NiftyList { public: // one-time initialization - static bool AppInit(const char* fileName); + static bool AppInit(const WCHAR* fileName); // one-time cleanup static bool AppCleanup(void); @@ -521,4 +527,4 @@ private: static bool fDataReady; }; -#endif /*__LR_REFORMAT__*/ +#endif /*REFORMAT_REFORMAT_H*/ diff --git a/reformat/ReformatBase.cpp b/reformat/ReformatBase.cpp index 6c7daf2..4da5a54 100644 --- a/reformat/ReformatBase.cpp +++ b/reformat/ReformatBase.cpp @@ -11,14 +11,6 @@ #include -static float roundf(float val) -{ - if (val < 0.5f) - return ::floorf(val); - else - return ::ceilf(val); -} - /* * ========================================================================== * ReformatText @@ -221,7 +213,7 @@ ReformatText::SetResultBuffer(ReformatOutput* pOutput, bool multiFont) * worry about nil pointers. */ pOutput->SetOutputKind(ReformatOutput::kOutputRaw); - WMSG0("ReformatText returning a nil pointer\n"); + WMSG0("ReformatText returning a null pointer\n"); } else { if (fUseRTF) pOutput->SetOutputKind(ReformatOutput::kOutputRTF); diff --git a/reformat/ReformatBase.h b/reformat/ReformatBase.h index 2c531f5..eef3fd0 100644 --- a/reformat/ReformatBase.h +++ b/reformat/ReformatBase.h @@ -11,8 +11,8 @@ * that, but we'd have to figure out what that means when extracting a file * (i.e. figure out the RTF embedded bitmap format). */ -#ifndef __LR_REFORMAT_BASE__ -#define __LR_REFORMAT_BASE__ +#ifndef REFORMAT_REFORMATBASE +#define REFORMAT_REFORMATBASE #include "Reformat.h" @@ -387,4 +387,4 @@ private: TextColor fTextColor; }; -#endif /*__LR_REFORMAT_BASE__*/ +#endif /*REFORMAT_REFORMATBASE*/ diff --git a/reformat/ResourceFork.cpp b/reformat/ResourceFork.cpp index 2cc1914..c41839b 100644 --- a/reformat/ResourceFork.cpp +++ b/reformat/ResourceFork.cpp @@ -16,7 +16,7 @@ * NList.Data from NiftyList, which included some items that were * blanks in the Apple docs. */ -static const char* kUnknownSysRsrc = _T("(system resource)"); +static const char* kUnknownSysRsrc = "(system resource)"; static const char* kRsrc8000[0x30] = { // 0x8000 through 0x802f kUnknownSysRsrc, "rIcon", @@ -135,7 +135,7 @@ ReformatResourceFork::Process(const ReformatHolder* pHolder, else if (resType >= 0xc000 && resType < 0xc000 + NELEM(kRsrcC000)) typeDescr = kRsrcC000[resType - 0xc000]; else if (resType >= 0x0001 && resType <= 0x7fff) - typeDescr = _T("(application-defined resource)"); + typeDescr = "(application-defined resource)"; else typeDescr = kUnknownSysRsrc; diff --git a/reformat/ResourceFork.h b/reformat/ResourceFork.h index 4347f62..c0c5065 100644 --- a/reformat/ResourceFork.h +++ b/reformat/ResourceFork.h @@ -6,8 +6,8 @@ /* * Reformat a resource fork. */ -#ifndef __LR_RESOURCEFORK__ -#define __LR_RESOURCEFORK__ +#ifndef REFORMAT_RESOURCEFORK_H +#define REFORMAT_RESOURCEFORK_H #include "ReformatBase.h" @@ -36,4 +36,4 @@ private: bool* pLittleEndian); }; -#endif /*__LR_RESOURCEFORK__*/ \ No newline at end of file +#endif /*REFORMAT_RESOURCEFORK_H*/ diff --git a/reformat/Simple.h b/reformat/Simple.h index cfc755d..a079278 100644 --- a/reformat/Simple.h +++ b/reformat/Simple.h @@ -6,8 +6,8 @@ /* * Simple reformatters that can apply to anything. */ -#ifndef __LR_SIMPLE__ -#define __LR_SIMPLE__ +#ifndef REFORMAT_SIMPLE_H +#define REFORMAT_SIMPLE_H #include "ReformatBase.h" @@ -53,4 +53,4 @@ public: ReformatOutput* pOutput); }; -#endif /*__LR_SIMPLE__*/ +#endif /*REFORMAT_SIMPLE_H*/ diff --git a/reformat/StdAfx.cpp b/reformat/StdAfx.cpp index 393859d..d5903fd 100644 --- a/reformat/StdAfx.cpp +++ b/reformat/StdAfx.cpp @@ -8,4 +8,3 @@ // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" - diff --git a/reformat/StdAfx.h b/reformat/StdAfx.h index 4cb1275..198deea 100644 --- a/reformat/StdAfx.h +++ b/reformat/StdAfx.h @@ -19,6 +19,11 @@ #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#include "../app/targetver.h" + +// TODO: use the "secure" versions? +#define _CRT_SECURE_NO_WARNINGS + #include #include #include diff --git a/reformat/SuperHiRes.cpp b/reformat/SuperHiRes.cpp index a9f0b6d..65cdaa5 100644 --- a/reformat/SuperHiRes.cpp +++ b/reformat/SuperHiRes.cpp @@ -639,8 +639,8 @@ ReformatAPFSHR::Process(const ReformatHolder* pHolder, dataLen = blockLen - (nameLen+1 + 4); - WMSG4(" APFSHR block='%s' blockLen=%ld (dataLen=%ld) start=0x%08lx\n", - (LPCSTR) blockName, blockLen, dataLen, srcPtr); + WMSG4(" APFSHR block='%ls' blockLen=%ld (dataLen=%ld) start=0x%08lx\n", + (LPCWSTR) blockName, blockLen, dataLen, srcPtr); if (blockName == "MAIN") { if (UnpackMain(srcPtr, dataLen) != 0) @@ -652,7 +652,7 @@ ReformatAPFSHR::Process(const ReformatHolder* pHolder, } else if (blockName == "NOTE") { UnpackNote(srcPtr, dataLen); } else { - WMSG1(" APFSHR (ignoring segment '%s')\n", blockName); + WMSG1(" APFSHR (ignoring segment '%ls')\n", (LPCWSTR) blockName); } srcPtr = nextBlock; @@ -949,10 +949,11 @@ ReformatAPFSHR::UnpackNote(const unsigned char* srcPtr, long srcLen) } CString str; - while (srcLen--) + while (srcLen--) { str += *srcPtr++; + } - WMSG1(" APFSHR note: '%s'\n", str); + WMSG1(" APFSHR note: '%ls'\n", (LPCWSTR) str); } diff --git a/reformat/SuperHiRes.h b/reformat/SuperHiRes.h index f02d56e..da3a09d 100644 --- a/reformat/SuperHiRes.h +++ b/reformat/SuperHiRes.h @@ -6,8 +6,8 @@ /* * Convert the various Super HiRes formats. */ -#ifndef __LR_SUPERHIRES__ -#define __LR_SUPERHIRES__ +#ifndef REFORMAT_SUPERHIRES_H +#define REFORMAT_SUPERHIRES_H #include "ReformatBase.h" @@ -307,4 +307,4 @@ private: DreamGrafix fDG; }; -#endif /*__LR_SUPERHIRES__*/ \ No newline at end of file +#endif /*REFORMAT_SUPERHIRES_H*/ diff --git a/reformat/Teach.h b/reformat/Teach.h index 308a1e4..924ce57 100644 --- a/reformat/Teach.h +++ b/reformat/Teach.h @@ -6,8 +6,8 @@ /* * Teach Text handling. */ -#ifndef __LR_TEACH__ -#define __LR_TEACH__ +#ifndef REFORMAT_TEACH_H +#define REFORMAT_TEACH_H #include "ReformatBase.h" @@ -208,4 +208,4 @@ private: }; -#endif /*__LR_TEACH__*/ +#endif /*REFORMAT_TEACH_H*/ diff --git a/reformat/Text8.h b/reformat/Text8.h index 63b50e7..9f44dd4 100644 --- a/reformat/Text8.h +++ b/reformat/Text8.h @@ -7,8 +7,8 @@ /* * Reformat 8-bit word processor files. */ -#ifndef __LR_TEXT8__ -#define __LR_TEXT8__ +#ifndef REFORMAT_TEXT8_H +#define REFORMAT_TEXT8_H #include "ReformatBase.h" @@ -46,4 +46,4 @@ public: }; -#endif /*__LR_TEXT8__*/ +#endif /*REFORMAT_TEXT8_H*/ diff --git a/reformat/reformat.vcxproj b/reformat/reformat.vcxproj index 00d2ab8..17e32bc 100644 --- a/reformat/reformat.vcxproj +++ b/reformat/reformat.vcxproj @@ -27,7 +27,7 @@ StaticLibrary v120 Dynamic - MultiByte + Unicode @@ -43,12 +43,12 @@ <_ProjectFileVersion>12.0.30501.0 - .\Release\ - .\Release\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ - .\Debug\ - .\Debug\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ @@ -60,15 +60,16 @@ true Use stdafx.h - .\Release/reformat.pch - .\Release/ - .\Release/ - .\Release/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true + true - .\Release\reformat.lib + $(OutDir)$(TargetName)$(TargetExt) true @@ -84,16 +85,17 @@ MultiThreadedDebugDLL Use stdafx.h - .\Debug/reformat.pch - .\Debug/ - .\Debug/ - .\Debug/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true EditAndContinue + true - .\Debug\reformat.lib + $(OutDir)$(TargetName)$(TargetExt) true @@ -101,125 +103,6 @@ 0x0409 - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - Create - MaxSpeed - Create - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - @@ -242,6 +125,34 @@ + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + diff --git a/reformat/reformat.vcxproj.filters b/reformat/reformat.vcxproj.filters index 9e86120..fd56a68 100644 --- a/reformat/reformat.vcxproj.filters +++ b/reformat/reformat.vcxproj.filters @@ -10,6 +10,68 @@ h;hpp;hxx;hm;inl + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + Source Files @@ -81,66 +143,4 @@ Source Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - \ No newline at end of file diff --git a/util/CancelDialog.h b/util/CancelDialog.h index a8f9aed..c601cc4 100644 --- a/util/CancelDialog.h +++ b/util/CancelDialog.h @@ -9,8 +9,8 @@ * The user of this class must define a dialog with at least an IDCANCEL * button. */ -#ifndef __CANCELDIALOG__ -#define __CANCELDIALOG__ +#ifndef UTIL_CANCELDIALOG_H +#define UTIL_CANCELDIALOG_H /* * This class must be allocated on the heap. @@ -45,4 +45,4 @@ private: }; -#endif /*__CANCELDIALOG__*/ \ No newline at end of file +#endif /*UTIL_CANCELDIALOG_H*/ diff --git a/util/FaddenStd.h b/util/FaddenStd.h index 3f119f9..8546a3c 100644 --- a/util/FaddenStd.h +++ b/util/FaddenStd.h @@ -6,15 +6,16 @@ /* * Standard stuff. */ -#ifndef __FADDEN_STD__ -#define __FADDEN_STD__ +#ifndef UTIL_FADDENSTD_H +#define UTIL_FADDENSTD_H #define NELEM(x) ((int) (sizeof(x) / sizeof(x[0]))) +// TODO: nuke this #define nil NULL // Windows equivalents #define strcasecmp stricmp #define strncasecmp strnicmp -#endif /*__FADDEN_STD__*/ \ No newline at end of file +#endif /*UTIL_FADDENSTD_H*/ diff --git a/util/Modeless.h b/util/Modeless.h index ce870db..45e595e 100644 --- a/util/Modeless.h +++ b/util/Modeless.h @@ -6,8 +6,8 @@ /* * Trivial implementation of a modeless dialog box. */ -#ifndef __MODELESS__ -#define __MODELESS__ +#ifndef UTIL_MODELESS_H +#define UTIL_MODELESS_H /* * This class must be allocated on the heap, and destroyed by calling @@ -113,4 +113,4 @@ private: */ -#endif /*__MODELESS__*/ +#endif /*UTIL_MODELESS_H*/ diff --git a/util/MyBitmapButton.h b/util/MyBitmapButton.h index f89dff7..5ac5710 100644 --- a/util/MyBitmapButton.h +++ b/util/MyBitmapButton.h @@ -9,8 +9,8 @@ * An extension of CBitmap that updates the button's bitmap automatically when * the system colors change. Also handles installing itself into a dialog. */ -#ifndef __MYBITMAPBUTTON__ -#define __MYBITMAPBUTTON__ +#ifndef UTIL_MYBITMAPBUTTON_H +#define UTIL_MYBITMAPBUTTON_H class MyBitmapButton : public CButton { public: @@ -48,4 +48,4 @@ private: DECLARE_MESSAGE_MAP() }; -#endif /*__MYBITMAPBUTTON__*/ \ No newline at end of file +#endif /*UTIL_MYBITMAPBUTTON_H*/ diff --git a/util/MyDIBitmap.cpp b/util/MyDIBitmap.cpp index d2c20cf..1aa3bca 100644 --- a/util/MyDIBitmap.cpp +++ b/util/MyDIBitmap.cpp @@ -101,8 +101,8 @@ MyDIBitmap::Create(int width, int height, int bitsPerPixel, int colorsUsed, DWORD err = ::GetLastError(); //CString msg; //GetWin32ErrorString(err, &msg); - //WMSG2(" DIB CreateDIBSection failed (err=%d msg='%s')\n", - // err, msg); + //WMSG2(" DIB CreateDIBSection failed (err=%d msg='%ls')\n", + // err, (LPCWSTR) msg); WMSG1(" DIB CreateDIBSection failed (err=%d)\n", err); LogHexDump(&mBitmapInfoHdr, sizeof(BITMAPINFO)); WMSG1("&mpPixels = 0x%08lx\n", &mpPixels); @@ -150,15 +150,16 @@ MyDIBitmap::Create(int width, int height, int bitsPerPixel, int colorsUsed, * Open the file and call the FILE* version. */ int -MyDIBitmap::CreateFromFile(const char* fileName) +MyDIBitmap::CreateFromFile(const WCHAR* fileName) { FILE* fp = nil; int err; - fp = fopen(fileName, "rb"); + fp = _wfopen(fileName, L"rb"); if (fp == nil) { err = errno ? errno : -1; - WMSG2("Unable to read bitmap from file '%s' (err=%d)\n", fileName, err); + WMSG2("Unable to read bitmap from file '%ls' (err=%d)\n", + fileName, err); return err; } @@ -483,7 +484,7 @@ MyDIBitmap::ConvertBufToDIBSection(void) * Use MAKEINTRESOURCE to load a resource by ordinal. */ void* -MyDIBitmap::CreateFromResource(HINSTANCE hInstance, const char* rsrc) +MyDIBitmap::CreateFromResource(HINSTANCE hInstance, const WCHAR* rsrc) { mhBitmap = (HBITMAP) ::LoadImage(hInstance, rsrc, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR | LR_CREATEDIBSECTION); @@ -491,8 +492,8 @@ MyDIBitmap::CreateFromResource(HINSTANCE hInstance, const char* rsrc) DWORD err = ::GetLastError(); //CString msg; //GetWin32ErrorString(err, &msg); - //WMSG2(" DIB CreateDIBSection failed (err=%d msg='%s')\n", - // err, msg); + //WMSG2(" DIB CreateDIBSection failed (err=%d msg='%ls')\n", + // err, (LPCWSTR) msg); WMSG1(" DIB LoadImage failed (err=%d)\n", err); return nil; } @@ -538,7 +539,8 @@ MyDIBitmap::CreateFromResource(HINSTANCE hInstance, const char* rsrc) DWORD err = ::GetLastError(); CString buf; GetWin32ErrorString(err, &buf); - WMSG2(" DIB GetDIBColorTable failed (err=0x%x '%s')\n", err, buf); + WMSG2(" DIB GetDIBColorTable failed (err=0x%x '%ls')\n", + err, (LPCWSTR) buf); } SelectObject(memDC, oldBits); DeleteDC(memDC); @@ -1056,8 +1058,8 @@ MyDIBitmap::ConvertToDDB(HDC dc) const CString msg; GetWin32ErrorString(err, &msg); - WMSG2(" DIB CreateDIBSection failed (err=%d msg='%s')\n", - err, msg); + WMSG2(" DIB CreateDIBSection failed (err=%d msg='%ls')\n", + err, (LPCWSTR) msg); //ASSERT(false); // stop & examine this return nil; } @@ -1083,17 +1085,17 @@ MyDIBitmap::ConvertToDDB(HDC dc) const * function. */ int -MyDIBitmap::WriteToFile(const char* fileName) const +MyDIBitmap::WriteToFile(const WCHAR* fileName) const { FILE* fp = nil; int err; assert(fileName != nil); - fp = fopen(fileName, "wb"); + fp = _wfopen(fileName, L"wb"); if (fp == nil) { err = errno ? errno : -1; - WMSG2("Unable to open bitmap file '%s' (err=%d)\n", fileName, err); + WMSG2("Unable to open bitmap file '%ls' (err=%d)\n", fileName, err); return err; } diff --git a/util/MyDIBitmap.h b/util/MyDIBitmap.h index 0855d2f..06fb209 100644 --- a/util/MyDIBitmap.h +++ b/util/MyDIBitmap.h @@ -10,8 +10,8 @@ * want or need, but it's easier to knock the bugs out if everybody is * using roughly the same code. ] */ -#ifndef __LF_DIBITMAP__ -#define __LF_DIBITMAP__ +#ifndef UTIL_DIBITMAP_H +#define UTIL_DIBITMAP_H /* @@ -73,7 +73,7 @@ public: HBITMAP ConvertToDDB(HDC dc) const; // create a DIB from a file on disk - int CreateFromFile(const char* fileName); + int CreateFromFile(const WCHAR* fileName); int CreateFromFile(FILE* fp, long len); // create from memory buffer; set "doDelete" if object should own mem // (if "doDelete" is set, memory will be deleted if function fails) @@ -81,10 +81,10 @@ public: int CreateFromBuffer(void* buffer, long len, bool doDelete); // create a DIB from an embedded resource - void* CreateFromResource(HINSTANCE hInstance, const char* rsrc); + void* CreateFromResource(HINSTANCE hInstance, const WCHAR* rsrc); // write bitmap to file (for FILE*, make sure it's open in "wb" mode) - int WriteToFile(const char* fileName) const; + int WriteToFile(const WCHAR* fileName) const; int WriteToFile(FILE* fp) const; // simple getters @@ -173,4 +173,4 @@ private: DWORD mTransparentColor; // for single-bit alpha bitmaps }; -#endif /*__LF_DIBITMAP__*/ \ No newline at end of file +#endif /*UTIL_DIBITMAP_H*/ diff --git a/util/MyDebug.h b/util/MyDebug.h index 913dd48..39cc876 100644 --- a/util/MyDebug.h +++ b/util/MyDebug.h @@ -6,8 +6,8 @@ /* * My debug stuff. */ -#ifndef __MY_DEBUG__ -#define __MY_DEBUG__ +#ifndef UTIL_MYDEBUG_H +#define UTIL_MYDEBUG_H //#define _DEBUG_LOG /* set this to force logging in all builds */ @@ -74,4 +74,4 @@ extern int gPid; # define DebugBreak() ((void) 0) #endif -#endif /*__MY_DEBUG__*/ +#endif /*UTIL_MYDEBUG_H*/ diff --git a/util/MyEdit.h b/util/MyEdit.h index cbd9319..fc0f2cb 100644 --- a/util/MyEdit.h +++ b/util/MyEdit.h @@ -6,8 +6,8 @@ /* * My edit class. */ -#ifndef __MYEDIT__ -#define __MYEDIT__ +#ifndef UTIL_MYEDIT_H +#define UTIL_MYEDIT_H /* * Replace the edit box in a dialog with this code by calling MyEdit's @@ -52,4 +52,4 @@ private: //DECLARE_MESSAGE_MAP() }; -#endif /*__MYEDIT__*/ \ No newline at end of file +#endif /*UTIL_MYEDIT_H*/ diff --git a/util/MySpinCtrl.cpp b/util/MySpinCtrl.cpp index 3d1c5aa..d8e725d 100644 --- a/util/MySpinCtrl.cpp +++ b/util/MySpinCtrl.cpp @@ -43,11 +43,11 @@ void MySpinCtrl::PreSubclassWindow() * Returns "true" on success, "false" on error. */ bool -MySpinCtrl::ConvLong(const char* str, long* pVal) const +MySpinCtrl::ConvLong(const WCHAR* str, long* pVal) const { - char* endp; + WCHAR* endp; - *pVal = strtol(str, &endp, GetBase()); + *pVal = wcstol(str, &endp, GetBase()); if (endp == str || *endp != '\0') return false; @@ -86,9 +86,9 @@ MySpinCtrl::OnDeltaPos(NMHDR* pNMHDR, LRESULT* pResult) if (proposedVal != buddyVal) { /* set buddy control to new value */ if (GetBase() == 10) - buddyStr.Format("%d", proposedVal); + buddyStr.Format(L"%d", proposedVal); else - buddyStr.Format("%X", proposedVal); + buddyStr.Format(L"%X", proposedVal); GetBuddy()->SetWindowText(buddyStr); } @@ -115,9 +115,9 @@ MySpinCtrl::SetPos(int nPos) } if (GetBase() == 10) - buddyStr.Format("%d", nPos); + buddyStr.Format(L"%d", nPos); else - buddyStr.Format("%X", nPos); + buddyStr.Format(L"%X", nPos); GetBuddy()->SetWindowText(buddyStr); @@ -149,9 +149,9 @@ MySpinCtrl::GetPos() const // if they typed a "sloppy value" in, make it look nice CString reformatStr; if (GetBase() == 10) - reformatStr.Format("%d", val); + reformatStr.Format(L"%d", val); else - reformatStr.Format("%X", val); + reformatStr.Format(L"%X", val); if (buddyStr != reformatStr) GetBuddy()->SetWindowText(reformatStr); diff --git a/util/MySpinCtrl.h b/util/MySpinCtrl.h index 3cc011c..3b0ce69 100644 --- a/util/MySpinCtrl.h +++ b/util/MySpinCtrl.h @@ -9,8 +9,8 @@ * This is based on code originally written by Definitive Solutions, Inc. * It was rewritten because their code was miserable. */ -#ifndef __MYSPINCTRL__ -#define __MYSPINCTRL__ +#ifndef UTIL_MYSPINCTRL_H +#define UTIL_MYSPINCTRL_H #include // for CSpinButtonCtrl @@ -42,9 +42,9 @@ protected: private: int fLow, fHigh; - bool ConvLong(const char* str, long* pVal) const; + bool ConvLong(const WCHAR* str, long* pVal) const; DECLARE_MESSAGE_MAP() }; -#endif /*__MYSPINCTRL__*/ \ No newline at end of file +#endif /*UTIL_MYSPINCTRL_H*/ diff --git a/util/PathName.cpp b/util/PathName.cpp index a9ea57a..75e0a3b 100644 --- a/util/PathName.cpp +++ b/util/PathName.cpp @@ -45,12 +45,12 @@ * * Always returns a pointer to a string; never returns nil. */ -const char* -FilenameOnly(const char* pathname, char fssep) +const WCHAR* +PathName::FilenameOnly(const WCHAR* pathname, WCHAR fssep) { - const char* retstr; - const char* pSlash; - char* tmpStr = nil; + const WCHAR* retstr; + const WCHAR* pSlash; + WCHAR* tmpStr = nil; ASSERT(pathname != nil); if (fssep == '\0') { @@ -58,7 +58,7 @@ FilenameOnly(const char* pathname, char fssep) goto bail; } - pSlash = strrchr(pathname, fssep); + pSlash = wcsrchr(pathname, fssep); // strrchr if (pSlash == nil) { retstr = pathname; /* whole thing is the filename */ goto bail; @@ -66,16 +66,16 @@ FilenameOnly(const char* pathname, char fssep) pSlash++; if (*pSlash == '\0') { - if (strlen(pathname) < 2) { + if (wcslen(pathname) < 2) { retstr = pathname; /* the pathname is just "/"? Whatever */ goto bail; } /* some bonehead put an fssep on the very end; back up before it */ /* (not efficient, but this should be rare, and I'm feeling lazy) */ - tmpStr = strdup(pathname); - tmpStr[strlen(pathname)-1] = '\0'; - pSlash = strrchr(tmpStr, fssep); + tmpStr = wcsdup(pathname); + tmpStr[wcslen(pathname)-1] = '\0'; + pSlash = wcsrchr(tmpStr, fssep); if (pSlash == nil) { retstr = pathname; /* just a filename with a '/' after it */ @@ -110,11 +110,11 @@ bail: * * We guarantee that there is at least one character after the '.'. */ -const char* -FindExtension(const char* pathname, char fssep) +const WCHAR* +PathName::FindExtension(const WCHAR* pathname, WCHAR fssep) { - const char* pFilename; - const char* pExt; + const WCHAR* pFilename; + const WCHAR* pExt; /* * We have to isolate the filename so that we don't get excited @@ -122,7 +122,7 @@ FindExtension(const char* pathname, char fssep) */ pFilename = FilenameOnly(pathname, fssep); ASSERT(pFilename != nil); - pExt = strrchr(pFilename, kFilenameExtDelim); + pExt = wcsrchr(pFilename, kFilenameExtDelim); /* also check for "/blah/foo.", which doesn't count */ if (pExt != nil && *(pExt+1) != '\0') @@ -146,10 +146,10 @@ PathName::GetFileName(void) str += fExt; { - const char* ccp; + const WCHAR* ccp; ccp = FilenameOnly(fPathName, '\\'); - if (strcmp(ccp, str) != 0) { - WMSG2("NOTE: got different filenames '%s' vs '%s'\n", + if (wcscmp(ccp, str) != 0) { + WMSG2("NOTE: got different filenames '%ls' vs '%ls'\n", ccp, (LPCTSTR) str); } } @@ -203,12 +203,12 @@ PathName::GetExtension(void) SplitIFN(); { - const char* ccp; + const WCHAR* ccp; ccp = FindExtension(fPathName, '\\'); - if ((ccp == nil && strlen(fExt) > 0) || - (ccp != nil && strcmp(ccp, fExt) != 0)) + if ((ccp == nil && wcslen(fExt) > 0) || + (ccp != nil && wcscmp(ccp, fExt) != 0)) { - WMSG2("NOTE: got different extensions '%s' vs '%s'\n", + WMSG2("NOTE: got different extensions '%ls' vs '%ls'\n", ccp, (LPCTSTR) fExt); } } @@ -224,10 +224,10 @@ PathName::GetExtension(void) int PathName::SFNToLFN(void) { - char buf[MAX_PATH]; + WCHAR buf[MAX_PATH]; WIN32_FIND_DATA findFileData; HANDLE hFind; - char* cp; + WCHAR* cp; DWORD len; CString lfn; bool hadEndingSlash = false; @@ -237,10 +237,10 @@ PathName::SFNToLFN(void) return 0; /* fully expand it */ - len = ::GetFullPathName(fPathName, sizeof(buf), buf, &cp); + len = GetFullPathName(fPathName, NELEM(buf), buf, &cp); if (len == 0 || len >= sizeof(buf)) return -1; - //WMSG1(" FullPathName='%s'\n", buf); + //WMSG1(" FullPathName='%ls'\n", buf); if (buf[len-1] == '\\') { hadEndingSlash = true; @@ -269,12 +269,12 @@ PathName::SFNToLFN(void) hFind = ::FindFirstFile(buf, &findFileData); if (hFind == INVALID_HANDLE_VALUE) { DWORD err = ::GetLastError(); - WMSG2("FindFirstFile '%s' failed, err=%d\n", buf, err); + WMSG2("FindFirstFile '%ls' failed, err=%d\n", buf, err); return -1; } else { FindClose(hFind); } - //WMSG2(" COMPONENT '%s' [%s]\n", findFileData.cFileName, + //WMSG2(" COMPONENT '%ls' [%ls]\n", findFileData.cFileName, // findFileData.cAlternateFileName); lfn += findFileData.cFileName; lfn += "\\"; @@ -284,24 +284,24 @@ PathName::SFNToLFN(void) cp++; } - //WMSG1(" Interim name = '%s'\n", (LPCTSTR) lfn); + //WMSG1(" Interim name = '%ls'\n", (LPCTSTR) lfn); if (*(cp-1) != '\\') { /* there was some stuff after the last '\\'; handle it */ hFind = ::FindFirstFile(buf, &findFileData); if (hFind == INVALID_HANDLE_VALUE) { DWORD err = ::GetLastError(); - WMSG2("FindFirstFile '%s' failed, err=%d\n", buf, err); + WMSG2("FindFirstFile '%ls' failed, err=%d\n", buf, err); return -1; } else { FindClose(hFind); } - //WMSG2(" COMPONENT2 '%s' [%s]\n", findFileData.cFileName, + //WMSG2(" COMPONENT2 '%ls' [%ls]\n", findFileData.cFileName, // findFileData.cAlternateFileName); lfn += findFileData.cFileName; } - //WMSG1(" Almost done = '%s'\n", (LPCTSTR) lfn); + //WMSG1(" Almost done = '%ls'\n", (LPCTSTR) lfn); if (hadEndingSlash) lfn += "\\"; @@ -338,11 +338,11 @@ bool PathName::Exists(void) { // if (strncmp(fPathName, "\\\\", 2) == 0) { -// WMSG1("Refusing to check for network path '%s'\n", fPathName); +// WMSG1("Refusing to check for network path '%ls'\n", fPathName); // return false; // } - return (::access(fPathName, 0) != -1); + return (::_waccess(fPathName, 0) != -1); #if 0 WIN32_FIND_DATA fd; @@ -413,13 +413,13 @@ with * Invoke the system-dependent directory creation function. */ int -PathName::Mkdir(const char* dir) +PathName::Mkdir(const WCHAR* dir) { int err = 0; ASSERT(dir != nil); - if (mkdir(dir) < 0) + if (_wmkdir(dir) < 0) err = errno ? errno : -1; return err; @@ -434,10 +434,10 @@ PathName::Mkdir(const char* dir) * returns nonzero and result values are undefined. */ int -PathName::GetFileInfo(const char* pathname, struct stat* psb, +PathName::GetFileInfo(const WCHAR* pathname, struct _stat* psb, time_t* pModWhen, bool* pExists, bool* pIsReadable, bool* pIsDirectory) { - struct stat sbuf; + struct _stat sbuf; int cc; /* @@ -459,7 +459,7 @@ PathName::GetFileInfo(const char* pathname, struct stat* psb, if (pIsDirectory != nil) *pIsDirectory = false; - cc = stat(pathname, &sbuf); + cc = _wstat(pathname, &sbuf); if (psb != nil) *psb = sbuf; if (cc != 0) { @@ -485,7 +485,7 @@ PathName::GetFileInfo(const char* pathname, struct stat* psb, * slow way is to call access(2), the harder way is to figure out * what user/group we are and compare the appropriate file mode. */ - if (access(pathname, R_OK) < 0) + if (_waccess(pathname, R_OK) < 0) *pIsReadable = false; else *pIsReadable = true; @@ -498,7 +498,7 @@ PathName::GetFileInfo(const char* pathname, struct stat* psb, * Check the status of a file. */ int -PathName::CheckFileStatus(struct stat* psb, bool* pExists, bool* pIsReadable, +PathName::CheckFileStatus(struct _stat* psb, bool* pExists, bool* pIsReadable, bool* pIsDir) { return GetFileInfo(fPathName, psb, nil, pExists, pIsReadable, pIsDir); @@ -524,7 +524,7 @@ PathName::GetModWhen(void) int PathName::SetModWhen(time_t when) { - struct utimbuf utbuf; + struct _utimbuf utbuf; if (when == (time_t) -1 || when == kDateNone || when == kDateInvalid) { WMSG1("NOTE: not setting invalid date (%ld)\n", when); @@ -533,7 +533,7 @@ PathName::SetModWhen(time_t when) utbuf.actime = utbuf.modtime = when; - if (utime(fPathName, &utbuf) < 0) + if (_wutime(fPathName, &utbuf) < 0) return errno; return 0; @@ -548,11 +548,11 @@ PathName::SetModWhen(time_t when) * path component is therefore (pathEnd-pathStart+1). */ int -PathName::CreateSubdirIFN(const char* pathStart, const char* pathEnd, - char fssep) +PathName::CreateSubdirIFN(const WCHAR* pathStart, const WCHAR* pathEnd, + WCHAR fssep) { int err = 0; - char* tmpBuf = nil; + WCHAR* tmpBuf = nil; bool isDirectory; bool exists; @@ -561,16 +561,16 @@ PathName::CreateSubdirIFN(const char* pathStart, const char* pathEnd, ASSERT(fssep != '\0'); /* pathStart might have whole path, but we only want up to "pathEnd" */ - tmpBuf = strdup(pathStart); + tmpBuf = wcsdup(pathStart); tmpBuf[pathEnd - pathStart +1] = '\0'; err = GetFileInfo(tmpBuf, nil, nil, &exists, nil, &isDirectory); if (err != 0) { - WMSG1(" Could not get file info for '%s'\n", tmpBuf); + WMSG1(" Could not get file info for '%ls'\n", tmpBuf); goto bail; } else if (!exists) { /* dir doesn't exist; move up a level and check parent */ - pathEnd = strrchr(tmpBuf, fssep); + pathEnd = wcsrchr(tmpBuf, fssep); if (pathEnd != nil) { pathEnd--; ASSERT(pathEnd >= tmpBuf); @@ -586,7 +586,7 @@ PathName::CreateSubdirIFN(const char* pathStart, const char* pathEnd, } else { /* file does exist, make sure it's a directory */ if (!isDirectory) { - WMSG1("Existing file '%s' is not a directory\n", tmpBuf); + WMSG1("Existing file '%ls' is not a directory\n", tmpBuf); err = ENOTDIR; goto bail; } @@ -611,8 +611,8 @@ PathName::CreatePathIFN(void) { int err = 0; CString pathName(fPathName); - char* pathStart; - const char* pathEnd; + WCHAR* pathStart; + const WCHAR* pathEnd; ASSERT(fFssep != '\0'); @@ -622,8 +622,8 @@ PathName::CreatePathIFN(void) // pathStart++; /* remove trailing fssep */ - if (pathStart[strlen(pathStart)-1] == fFssep) - pathStart[strlen(pathStart)-1] = '\0'; + if (pathStart[wcslen(pathStart)-1] == fFssep) + pathStart[wcslen(pathStart)-1] = '\0'; /* work around bug in Win32 strrchr */ if (pathStart[0] == '\0' || pathStart[1] == '\0') { @@ -631,7 +631,7 @@ PathName::CreatePathIFN(void) goto bail; } - pathEnd = strrchr(pathStart, fFssep); + pathEnd = wcsrchr(pathStart, fFssep); if (pathEnd == nil) { /* no subdirectory components found */ goto bail; diff --git a/util/PathName.h b/util/PathName.h index b20e3ac..557369d 100644 --- a/util/PathName.h +++ b/util/PathName.h @@ -7,8 +7,8 @@ * Filename manipulations. Includes some basic file ops (e.g. tests for * file existence) as well. */ -#ifndef __PATHNAME__ -#define __PATHNAME__ +#ifndef UTIL_PATHNAME_H +#define UTIL_PATHNAME_H /* * Holds a full or partial pathname, manipulating it in various ways. @@ -22,13 +22,13 @@ */ class PathName { public: - PathName(const char* pathName = "", char fssep = '\\') { + PathName(const WCHAR* pathName = L"", WCHAR fssep = '\\') { ASSERT(fssep == '\\'); // not handling other cases yet fPathName = pathName; fFssep = fssep; fSplit = false; } - PathName(const CString& pathName, char fssep = '\\') { + PathName(const CString& pathName, WCHAR fssep = '\\') { ASSERT(fssep == '\\'); // not handling other cases yet fPathName = pathName; fFssep = fssep; @@ -39,13 +39,13 @@ public: /* * Name manipulations. */ - void SetPathName(const char* pathName, char fssep = '\\') { + void SetPathName(const WCHAR* pathName, WCHAR fssep = '\\') { ASSERT(fssep == '\\'); // not handling other cases yet fPathName = pathName; fFssep = fssep; fSplit = false; } - void SetPathName(const CString& pathName, char fssep = '\\') { + void SetPathName(const CString& pathName, WCHAR fssep = '\\') { ASSERT(fssep == '\\'); // not handling other cases yet fPathName = pathName; fFssep = fssep; @@ -85,7 +85,7 @@ public: bool Exists(void); // check the status of a file - int CheckFileStatus(struct stat* psb, bool* pExists, bool* pIsReadable, + int CheckFileStatus(struct _stat* psb, bool* pExists, bool* pIsReadable, bool* pIsDir); // get the modification date @@ -96,31 +96,31 @@ public: // create the path, if necessary int CreatePathIFN(void); + static const WCHAR* FindExtension(const WCHAR* pathname, WCHAR fssep); + static const WCHAR* FilenameOnly(const WCHAR* pathname, WCHAR fssep); + //int SFNToLFN(const char* sfn, CString* pLfn); + private: void SplitIFN(void) { if (!fSplit) { - _splitpath(fPathName, fDrive, fDir, fFileName, fExt); + _wsplitpath(fPathName, fDrive, fDir, fFileName, fExt); fSplit = true; } } - int Mkdir(const char* dir); - int GetFileInfo(const char* pathname, struct stat* psb, time_t* pModWhen, + int Mkdir(const WCHAR* dir); + int GetFileInfo(const WCHAR* pathname, struct _stat* psb, time_t* pModWhen, bool* pExists, bool* pIsReadable, bool* pIsDirectory); - int CreateSubdirIFN(const char* pathStart, const char* pathEnd, - char fssep); + int CreateSubdirIFN(const WCHAR* pathStart, const WCHAR* pathEnd, + WCHAR fssep); CString fPathName; - char fFssep; + WCHAR fFssep; bool fSplit; - char fDrive[_MAX_DRIVE]; // 3 - char fDir[_MAX_DIR]; // 256 - char fFileName[_MAX_FNAME]; // 256 - char fExt[_MAX_EXT]; // 256 + WCHAR fDrive[_MAX_DRIVE]; // 3 + WCHAR fDir[_MAX_DIR]; // 256 + WCHAR fFileName[_MAX_FNAME]; // 256 + WCHAR fExt[_MAX_EXT]; // 256 }; -const char* FindExtension(const char* pathname, char fssep); -const char* FilenameOnly(const char* pathname, char fssep); -//int SFNToLFN(const char* sfn, CString* pLfn); - -#endif /*__PATHNAME__*/ \ No newline at end of file +#endif /*UTIL_PATHNAME_H*/ diff --git a/util/Pidl.cpp b/util/Pidl.cpp index b1d0586..a259bae 100644 --- a/util/Pidl.cpp +++ b/util/Pidl.cpp @@ -119,7 +119,6 @@ Pidl::CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi) return lpiTemp; } - /* * Get the display name of a file in a ShellFolder. * @@ -130,48 +129,49 @@ Pidl::CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi) */ BOOL Pidl::GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, DWORD dwFlags, - LPSTR lpFriendlyName) + CString* pFriendlyName) { BOOL bSuccess=TRUE; STRRET str; - if (NOERROR == lpsf->GetDisplayNameOf(lpi, dwFlags, &str)) - { + if (NOERROR == lpsf->GetDisplayNameOf(lpi, dwFlags, &str)) { switch (str.uType) { case STRRET_WSTR: - WideCharToMultiByte(CP_ACP, // CodePage - 0, // dwFlags - str.pOleStr, // lpWideCharStr - -1, // cchWideChar - lpFriendlyName, // lpMultiByteStr - MAX_PATH, // cchMultiByte - NULL, // lpDefaultChar, - NULL); // lpUsedDefaultChar + //WideCharToMultiByte(CP_ACP, // CodePage + // 0, // dwFlags + // str.pOleStr, // lpWideCharStr + // -1, // cchWideChar + // lpFriendlyName, // lpMultiByteStr + // MAX_PATH, // cchMultiByte + // NULL, // lpDefaultChar, + // NULL); // lpUsedDefaultChar - //Once the the function returns, the wide string - //should be freed. CoTaskMemFree(str.pOleStr) seems - //to do the job as well. - LPMALLOC pMalloc; - SHGetMalloc(&pMalloc); - pMalloc->Free (str.pOleStr); - pMalloc->Release(); + ////Once the the function returns, the wide string + ////should be freed. CoTaskMemFree(str.pOleStr) seems + ////to do the job as well. + //LPMALLOC pMalloc; + //SHGetMalloc(&pMalloc); + //pMalloc->Free (str.pOleStr); + //pMalloc->Release(); + *pFriendlyName = str.pOleStr; + CoTaskMemFree(str.pOleStr); break; case STRRET_OFFSET: - lstrcpy(lpFriendlyName, (LPSTR)lpi+str.uOffset); + *pFriendlyName = (LPSTR)lpi+str.uOffset; break; case STRRET_CSTR: - lstrcpy(lpFriendlyName, (LPSTR)str.cStr); + *pFriendlyName = (LPSTR)str.cStr; break; default: bSuccess = FALSE; break; } + } else { + bSuccess = FALSE; } - else - bSuccess = FALSE; return bSuccess; } @@ -184,20 +184,23 @@ Pidl::GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, DWORD dwFlags, * This is a rather roundabout way of doing things (converting to a full * display name and then converting that to a PIDL). However, there doesn't * seem to be a way to just ask a ShellFolder for its fully qualified PIDL. + * TODO: see if there's a better way now. * * Pass in the parent ShellFolder and the item's partial PIDL. */ LPITEMIDLIST Pidl::GetFullyQualPidl(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi) { - char szBuff[MAX_PATH]; - OLECHAR szOleChar[MAX_PATH]; + //char szBuff[MAX_PATH]; + //OLECHAR szOleChar[MAX_PATH]; + CString name; + WCHAR pathBuf[MAX_PATH]; LPSHELLFOLDER lpsfDeskTop; LPITEMIDLIST lpifq; ULONG ulEaten, ulAttribs; HRESULT hr; - if (!GetName(lpsf, lpi, SHGDN_FORPARSING, szBuff)) + if (!GetName(lpsf, lpi, SHGDN_FORPARSING, &name)) return NULL; hr = SHGetDesktopFolder(&lpsfDeskTop); @@ -205,10 +208,11 @@ Pidl::GetFullyQualPidl(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi) if (FAILED(hr)) return NULL; - MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szBuff, -1, - (USHORT *)szOleChar, sizeof(szOleChar)); + //MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szBuff, -1, + // (USHORT *)szOleChar, sizeof(szOleChar)); + wcscpy_s(pathBuf, name); - hr = lpsfDeskTop->ParseDisplayName(NULL, NULL, szOleChar, + hr = lpsfDeskTop->ParseDisplayName(NULL, NULL, pathBuf, &ulEaten, &lpifq, &ulAttribs); lpsfDeskTop->Release(); @@ -228,7 +232,7 @@ BOOL Pidl::GetPath(LPCITEMIDLIST pidl, CString* pPath) { BOOL result; - char buf[MAX_PATH]; + WCHAR buf[MAX_PATH]; result = SHGetPathFromIDList(pidl, buf); if (result) @@ -343,8 +347,8 @@ Pidl::GetItemIcon(LPITEMIDLIST lpi, UINT uFlags) { SHFILEINFO sfi; - uFlags |= SHGFI_PIDL; - SHGetFileInfo((LPCSTR)lpi, 0, &sfi, sizeof(SHFILEINFO), uFlags); + uFlags |= SHGFI_PIDL; // we're passing a PIDL, not a pathname, in 1st arg + SHGetFileInfo((LPCWSTR)lpi, 0, &sfi, sizeof(SHFILEINFO), uFlags); return sfi.iIcon; } diff --git a/util/Pidl.h b/util/Pidl.h index e2ccd59..475cd8d 100644 --- a/util/Pidl.h +++ b/util/Pidl.h @@ -6,8 +6,8 @@ /* * A collection of functions for manipulating Pointers to ID Lists (PIDLs). */ -#ifndef __PIDL__ -#define __PIDL__ +#ifndef UTIL_PIDL_H +#define UTIL_PIDL_H /* * All functions are static; the class is more about namespace protection than @@ -20,7 +20,7 @@ public: static LPITEMIDLIST GetFullyQualPidl(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi); static LPITEMIDLIST CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi); static BOOL GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, DWORD dwFlags, - LPSTR lpFriendlyName); + CString* pFriendlyName); static LPITEMIDLIST CreatePidl(UINT cbSize); static UINT GetSize(LPCITEMIDLIST pidl); static LPITEMIDLIST Next(LPCITEMIDLIST pidl); @@ -32,4 +32,4 @@ public: static int GetItemIcon(LPITEMIDLIST lpi, UINT uFlags); }; -#endif /*__PIDL__*/ \ No newline at end of file +#endif /*UTIL_PIDL_H*/ diff --git a/util/ProgressCancelDialog.h b/util/ProgressCancelDialog.h index 5ca599d..7a5d61c 100644 --- a/util/ProgressCancelDialog.h +++ b/util/ProgressCancelDialog.h @@ -9,8 +9,8 @@ * The user of this class must define a dialog with at least an IDCANCEL * button. */ -#ifndef __PROGRESSCANCELDIALOG__ -#define __PROGRESSCANCELDIALOG__ +#ifndef UTIL_PROCESSCANCELDIALOG_H +#define UTIL_PROCESSCANCELDIALOG_H /* * This class must be allocated on the heap. @@ -80,4 +80,4 @@ private: int fProgressID; }; -#endif /*__PROGRESSCANCELDIALOG__*/ +#endif /*UTIL_PROCESSCANCELDIALOG_H*/ diff --git a/util/SelectFilesDialog.cpp b/util/SelectFilesDialog.cpp index 478d147..8ddb00d 100644 --- a/util/SelectFilesDialog.cpp +++ b/util/SelectFilesDialog.cpp @@ -151,7 +151,7 @@ SelectFilesDialog::HandleNotify(HWND hDlg, LPOFNOTIFY pofn) // } else { // OPENFILENAME* pOfn; // pOfn = (OPENFILENAME*) GetWindowLong(hDlg, GWL_USERDATA); -// WMSG1("Count=0, name='%s'\n", pOfn->lpstrFile); +// WMSG1("Count=0, name='%ls'\n", pOfn->lpstrFile); // } PrepEndDialog(); /* must do this every time, or it fails in funky ways */ @@ -408,13 +408,13 @@ SelectFilesDialog::PrepEndDialog(void) */ WMSG2("PrepEndDialog: got max=%d off=%d\n", m_ofn.nMaxFile, m_ofn.nFileOffset); if (m_ofn.nFileOffset != 0) { - char* buf = m_ofn.lpstrFile; + WCHAR* buf = m_ofn.lpstrFile; buf += m_ofn.nFileOffset; while (*buf != '\0') { if (buf > m_ofn.lpstrFile) *(buf-1) = '\\'; - WMSG1(" File '%s'\n", buf); - buf += strlen(buf) +1; + WMSG1(" File '%ls'\n", buf); + buf += wcslen(buf) +1; } //Sleep(1000); nextSpot = (buf - m_ofn.lpstrFile) -1; @@ -456,7 +456,7 @@ SelectFilesDialog::PrepEndDialog(void) int count = pList->GetSelectedCount(); if (count == 0) { if (nextSpot == 0) { - MessageBox("Please select one or more files and directories.", + MessageBox(L"Please select one or more files and directories.", m_ofn.lpstrTitle, MB_OK | MB_ICONWARNING); /* make it clear that we're ignoring the names they typed */ ClearFileName(); @@ -471,13 +471,13 @@ SelectFilesDialog::PrepEndDialog(void) if (nextSpot == 0) { fileNames = GetFolderPath(); /* add a trailing '\', which gets stomped to '\0' later on */ - if (fileNames.Right(1) != "\\") - fileNames += "\\"; + if (fileNames.Right(1) != L"\\") + fileNames += L"\\"; fFileNameOffset = fileNames.GetLength(); compare = false; } else { fileNames = m_ofn.lpstrFile; - ASSERT(fileNames.Right(1) == "\\"); + ASSERT(fileNames.Right(1) == L"\\"); fFileNameOffset = m_ofn.nFileOffset; compare = true; } @@ -492,38 +492,38 @@ SelectFilesDialog::PrepEndDialog(void) } while (posn != nil) { /* do this every time, because "fileNames" can be reallocated */ - const char* tailStr = fileNames; + const WCHAR* tailStr = fileNames; tailStr += fFileNameOffset-1; int num = pList->GetNextSelectedItem(posn); // posn is updated /* here we make a big assumption: that GetItemData returns a PIDL */ LPITEMIDLIST pidl; - char buf[MAX_PATH]; + WCHAR buf[MAX_PATH]; pidl = (LPITEMIDLIST) pList->GetItemData(num); if (SHGetPathFromIDList(pidl, buf)) { /* it's a relative PIDL but SHGetPathFromIDList wants a full one, so it returns CWD + filename... strip bogus path off */ - CString compareName("\\"); + CString compareName(L"\\"); PathName path(buf); compareName += path.GetFileName(); - compareName += "\\"; - //WMSG1(" Checking name='%s'\n", compareName); + compareName += L"\\"; + //WMSG1(" Checking name='%ls'\n", compareName); - if (compare && stristr(tailStr, compareName) != nil) { - WMSG1(" Matched '%s', not adding\n", compareName); + if (compare && Stristr(tailStr, compareName) != nil) { + WMSG1(" Matched '%ls', not adding\n", compareName); } else { if (compare) { - WMSG1(" No match on '%s', adding\n", compareName); + WMSG1(" No match on '%ls', adding\n", compareName); } else { - WMSG1(" Found '%s', adding\n", compareName); + WMSG1(" Found '%ls', adding\n", compareName); } fileNames += path.GetFileName(); - fileNames += "\\"; + fileNames += L"\\"; } } else { /* expected, for things like "Control Panels" or "My Network" */ - WMSG1(" No path for '%s'\n", + WMSG1(" No path for '%ls'\n", (LPCTSTR) pList->GetItemText(num, 0)); } } @@ -534,8 +534,8 @@ SelectFilesDialog::PrepEndDialog(void) } } - WMSG3("Final result: names at %d, len=%d, str='%s'\n", - fFileNameOffset, strlen(fileNames), fileNames); + WMSG3("Final result: names at %d, len=%d, str='%ls'\n", + fFileNameOffset, wcslen(fileNames), fileNames); /* * Null-terminate with extreme prejudice. Every filename should be @@ -549,8 +549,8 @@ SelectFilesDialog::PrepEndDialog(void) */ ASSERT(fFileNames != m_ofn.lpstrFile); delete[] fFileNames; - fFileNames = strdup(fileNames); - char* cp = fFileNames; + fFileNames = wcsdup(fileNames); + WCHAR* cp = fFileNames; cp += fFileNameOffset-1; while (*cp != '\0') { if (*cp == '\\') @@ -585,5 +585,5 @@ SelectFilesDialog::ClearFileName(void) { CWnd* pWnd = GetParent()->GetDlgItem(edt1); if (pWnd != nil) - pWnd->SetWindowText(""); + pWnd->SetWindowText(L""); } diff --git a/util/SelectFilesDialog.h b/util/SelectFilesDialog.h index e4efc59..a6eb7eb 100644 --- a/util/SelectFilesDialog.h +++ b/util/SelectFilesDialog.h @@ -7,8 +7,8 @@ * File selection dialog, a sub-class of "Open" that allows multiple selection * of both files and directories. */ -#ifndef __SELECTFILESDIALOG__ -#define __SELECTFILESDIALOG__ +#ifndef UTIL_SELECTFILESDIALOG_H +#define UTIL_SELECTFILESDIALOG_H /* * File selection, based on an "open" file dialog. @@ -22,14 +22,14 @@ class SelectFilesDialog : public CFileDialog { public: enum { kFileNameBufSize = 32768 }; - SelectFilesDialog(const char* rctmpl, CWnd* pParentWnd = NULL) : + SelectFilesDialog(const WCHAR* rctmpl, CWnd* pParentWnd = NULL) : CFileDialog(true, NULL, NULL, OFN_HIDEREADONLY, NULL, pParentWnd) { m_ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ALLOWMULTISELECT | OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; m_ofn.lpTemplateName = rctmpl; m_ofn.hInstance = AfxGetInstanceHandle(); - m_ofn.lpstrFile = new char[kFileNameBufSize]; + m_ofn.lpstrFile = new WCHAR[kFileNameBufSize]; m_ofn.lpstrFile[0] = m_ofn.lpstrFile[1] = '\0'; m_ofn.nMaxFile = kFileNameBufSize; m_ofn.Flags |= OFN_ENABLEHOOK; @@ -37,7 +37,7 @@ public: m_ofn.lCustData = (long)this; fExitStatus = IDABORT; - fFileNames = new char[2]; + fFileNames = new WCHAR[2]; fFileNames[0] = fFileNames[1] = '\0'; fFileNameOffset = 0; @@ -51,23 +51,22 @@ public: } int GetExitStatus(void) const { return fExitStatus; } - const char* GetFileNames(void) const { return fFileNames; } + const WCHAR* GetFileNames(void) const { return fFileNames; } int GetFileNameOffset(void) const { return fFileNameOffset; } // set the window title; must be called before DoModal - void SetWindowTitle(const char* title) { + void SetWindowTitle(const WCHAR* title) { m_ofn.lpstrTitle = title; } // stuff values into our filename holder - void SetFileNames(const char* fileNames, int len, int fileNameOffset) { - ASSERT(len > (int)strlen(fileNames)); + void SetFileNames(const WCHAR* fileNames, size_t len, int fileNameOffset) { + ASSERT(len > wcslen(fileNames)); ASSERT(fileNames[len] == '\0'); ASSERT(fileNames[len-1] == '\0'); - WMSG3("SetFileNames '%s' %d %d\n", fileNames, len, fileNameOffset); + WMSG3("SetFileNames '%ls' %d %d\n", fileNames, len, fileNameOffset); delete[] fFileNames; - fFileNames = new char[len]; - memcpy(fFileNames, fileNames, len); + fFileNames = wcsdup(fileNames); fFileNameOffset = fileNameOffset; } @@ -119,11 +118,11 @@ private: bool fReady; int fExitStatus; int fFileNameOffset; - char* fFileNames; + WCHAR* fFileNames; CRect fLastWinSize; //DECLARE_MESSAGE_MAP() }; -#endif /*__SELECTFILESDIALOG__*/ \ No newline at end of file +#endif /*UTIL_SELECTFILESDIALOG_H*/ diff --git a/util/ShellTree.cpp b/util/ShellTree.cpp index b1b5194..f89391a 100644 --- a/util/ShellTree.cpp +++ b/util/ShellTree.cpp @@ -186,10 +186,10 @@ ShellTree::FillTreeView(LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, #if 1 { /* DEBUG */ - char szBuff[MAX_PATH]; - if (Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, szBuff)) { - WMSG2(" Checking '%s' 0x%08lx\n", - szBuff, ulAttrs); + CString name; + if (Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, &name)) { + WMSG2(" Checking '%ls' 0x%08lx\n", + name, ulAttrs); } else { WMSG1(" Checking 0x%08lx\n", ulAttrs); @@ -297,14 +297,14 @@ ShellTree::FillTreeView(LPSHELLFOLDER lpsf, LPITEMIDLIST lpifq, tvi.hItem = hParent; tvi.mask = TVIF_CHILDREN; if (!GetItem(&tvi)) { - WMSG1("Could not get TV '%s'\n", name); + WMSG1("Could not get TV '%ls'\n", name); ASSERT(false); } else if (tvi.cChildren) { - WMSG2("Removing child count (%d) from '%s'\n", + WMSG2("Removing child count (%d) from '%ls'\n", tvi.cChildren, name); tvi.cChildren = 0; if (!SetItem(&tvi)) { - WMSG1("Could not set TV '%s'\n", name); + WMSG1("Could not set TV '%ls'\n", name); ASSERT(false); } } @@ -338,7 +338,8 @@ ShellTree::AddNode(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, LPITEMIDLIST lpifq, TVINSERTSTRUCT tvins; LPITEMIDLIST lpifqThisItem = nil; TVItemData* lptvid = nil; - char szBuff[MAX_PATH]; + WCHAR szBuff[MAX_PATH]; + CString name; LPMALLOC lpMalloc = nil; HRESULT hr; BOOL result = FALSE; @@ -348,11 +349,12 @@ ShellTree::AddNode(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, LPITEMIDLIST lpifq, return FALSE; //Now get the friendly name that we'll put in the treeview. - if (!Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, szBuff)) { + if (!Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, &name)) { WMSG0("HEY: failed getting friendly name\n"); goto bail; // Error - could not get friendly name. } - //WMSG2("AddNode '%s' ATTR=0x%08lx\n", szBuff, ulAttrs); + wcscpy_s(szBuff, name); + //WMSG2("AddNode '%ls' ATTR=0x%08lx\n", szBuff, ulAttrs); lptvid = (TVItemData*)lpMalloc->Alloc(sizeof(TVItemData)); if (!lptvid) @@ -492,13 +494,12 @@ ShellTree::AddFolderAtSelection(const CString& name) const TVItemData* parentTvid; TVItemData* newTvid = nil; HWND hwnd = ::GetParent(m_hWnd); - char szBuff[MAX_PATH]; HTREEITEM hPrev = nil; BOOL result = false; CString debugName; HRESULT hr; - WMSG1("AddFolderAtSelection '%s'\n", name); + WMSG1("AddFolderAtSelection '%ls'\n", name); // Allocate a shell memory object. hr = ::SHGetMalloc(&lpMalloc); @@ -533,26 +534,26 @@ ShellTree::AddFolderAtSelection(const CString& name) tvi.hItem = hParent; tvi.mask = TVIF_CHILDREN; if (!GetItem(&tvi)) { - WMSG1("Could not get TV '%s'\n", debugName); + WMSG1("Could not get TV '%ls'\n", debugName); ASSERT(false); } else { HTREEITEM child = GetChildItem(hParent); if (child == nil && tvi.cChildren) { - WMSG1(" Found unexpanded node, not adding %s\n", name); + WMSG1(" Found unexpanded node, not adding %ls\n", name); result = TRUE; goto bail; } else if (child == nil && !tvi.cChildren) { - WMSG1(" Found former leaf node, updating kids in %s\n", debugName); + WMSG1(" Found former leaf node, updating kids in %ls\n", debugName); tvi.cChildren = 1; if (!SetItem(&tvi)) { - WMSG1("Could not set TV '%s'\n", debugName); + WMSG1("Could not set TV '%ls'\n", debugName); ASSERT(false); } result = TRUE; goto bail; } else { ASSERT(child != nil && tvi.cChildren != 0); - WMSG2(" Found expanded branch node '%s', adding new '%s'\n", + WMSG2(" Found expanded branch node '%ls', adding new '%ls'\n", debugName, name); } } @@ -579,8 +580,9 @@ ShellTree::AddFolderAtSelection(const CString& name) // Enumerate throught the list of folder and non-folder objects. while (S_OK == lpe->Next(1, &lpi, nil)) { - if (Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, szBuff)) { - if (name.CompareNoCase(szBuff) == 0) { + CString pidlName; + if (Pidl::GetName(lpsf, lpi, SHGDN_NORMAL, &pidlName)) { + if (name.CompareNoCase(pidlName) == 0) { /* match! */ if (!AddNode(lpsf, lpi, parentTvid->lpifq, 0, hParent, &hPrev)) { WMSG0("AddNode failed!\n"); @@ -712,7 +714,7 @@ ShellTree::OnFolderSelected(NMHDR* pNMHDR, LRESULT* pResult, { TVItemData* lptvid; LPSHELLFOLDER lpsf2=NULL; - char szBuff[MAX_PATH]; + WCHAR szBuff[MAX_PATH]; HRESULT hr; BOOL bRet=false; HTREEITEM hItem=NULL; @@ -741,7 +743,7 @@ ShellTree::OnFolderSelected(NMHDR* pNMHDR, LRESULT* pResult, } if (bRet) { - WMSG1("Now selected: '%s'\n", szBuff); + WMSG1("Now selected: '%ls'\n", szBuff); } else { WMSG0("Now selected: \n"); } @@ -821,7 +823,7 @@ ShellTree::EnableImages() HIMAGELIST hImageList; SHFILEINFO sfi; - hImageList = (HIMAGELIST)SHGetFileInfo((LPCSTR)"C:\\", + hImageList = (HIMAGELIST)SHGetFileInfo(L"C:\\", 0, &sfi, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON); @@ -847,7 +849,7 @@ BOOL ShellTree::GetSelectedFolderPath(CString &szFolderPath) { TVItemData* lptvid; //Long pointer to TreeView item data LPSHELLFOLDER lpsf2=NULL; - char szBuff[MAX_PATH]; + WCHAR szBuff[MAX_PATH]; HTREEITEM hItem=NULL; HRESULT hr; BOOL bRet=false; @@ -961,14 +963,14 @@ LPITEMIDLIST ShellTree::GetFullyQualifiedID(HTREEITEM folderNode) void ShellTree::TunnelTree(CString path, CString* pResultStr) { - const char* str = path; + const WCHAR* str = path; int len; if (str[0] == '\\' && str[1] == '\\') { *pResultStr = "Can't expand network locations directly."; return; } - len = strlen(path); + len = path.GetLength(); if (len < 1) { *pResultStr = "You must enter a folder name."; return; @@ -981,7 +983,7 @@ ShellTree::TunnelTree(CString path, CString* pResultStr) /* if it doesn't exist, there's not much point in searching for it */ PathName pathName(path); if (!pathName.Exists()) { - *pResultStr = "Folder not found."; + *pResultStr = L"Folder not found."; return; } @@ -991,17 +993,17 @@ ShellTree::TunnelTree(CString path, CString* pResultStr) */ HTREEITEM myComputer = FindMyComputer(); if (myComputer == nil) { - *pResultStr = "Unable to locate My Computer in tree."; + *pResultStr = L"Unable to locate My Computer in tree."; return; } CString drive = pathName.GetDriveOnly(); - WMSG1("Searching for drive='%s'\n", drive); + WMSG1("Searching for drive='%ls'\n", drive); HTREEITEM node = FindDrive(myComputer, drive); if (node == nil) { /* unexpected -- couldn't find the drive */ - pResultStr->Format("Unable to find drive %s.", drive); + pResultStr->Format(L"Unable to find drive %ls.", drive); return; } @@ -1014,8 +1016,8 @@ ShellTree::TunnelTree(CString path, CString* pResultStr) if (node == nil) { /* unexpected -- file doesn't exist */ - pResultStr->Format("Unable to find file '%s'.", - pathName.GetPathOnly()); + pResultStr->Format(L"Unable to find file '%ls'.", + (LPCWSTR) pathName.GetPathOnly()); } else { Select(node, TVGN_CARET); EnsureVisible(node); @@ -1067,7 +1069,7 @@ ShellTree::FindMyComputer(void) hr = desktop->CompareIDs(0, myComputerPidl, pData->lpi); if (SUCCEEDED(hr) && HRESULT_CODE(hr) == 0) { - WMSG1("MATCHED on '%s'\n", itemText); + WMSG1("MATCHED on '%ls'\n", itemText); result = node; break; } @@ -1123,9 +1125,9 @@ ShellTree::FindDrive(HTREEITEM myComputer, const CString& drive) CString itemText = GetItemText(node); itemText.MakeUpper(); - //WMSG2("COMPARING '%s' vs '%s'\n", udrive, itemText); + //WMSG2("COMPARING '%ls' vs '%ls'\n", (LPCWSTR) udrive, (LPCWSTR) itemText); if (itemText.Find(udrive) != -1) { - WMSG2("MATCHED '%s' in '%s'\n", udrive, itemText); + WMSG2("MATCHED '%ls' in '%ls'\n", (LPCWSTR) udrive, (LPCWSTR) itemText); break; } node = GetNextSiblingItem(node); @@ -1144,17 +1146,17 @@ ShellTree::FindDrive(HTREEITEM myComputer, const CString& drive) HTREEITEM ShellTree::SearchTree(HTREEITEM treeNode, const CString& path) { - WMSG2("SearchTree node=0x%08lx path='%s'\n", + WMSG2("SearchTree node=0x%08lx path='%ls'\n", treeNode, (LPCTSTR) path); HTREEITEM node; CString mangle(path); - char* start; - char* end; + WCHAR* start; + WCHAR* end; /* make a copy of "path" that we can mess with */ start = mangle.GetBuffer(0); - if (start == nil || *start != '\\' || *(start + strlen(start)-1) != '\\') + if (start == nil || *start != '\\' || *(start + wcslen(start)-1) != '\\') return nil; start++; @@ -1164,7 +1166,7 @@ ShellTree::SearchTree(HTREEITEM treeNode, const CString& path) Expand(node, TVE_EXPAND); // need to fill in the tree node = GetChildItem(node); - end = strchr(start, '\\'); + end = wcschr(start, '\\'); if (end == nil) { ASSERT(false); return nil; @@ -1183,7 +1185,7 @@ ShellTree::SearchTree(HTREEITEM treeNode, const CString& path) node = GetNextSiblingItem(node); } if (node == nil) { - WMSG2("NOT FOUND '%s' '%s'\n", (LPCTSTR) path, start); + WMSG2("NOT FOUND '%ls' '%ls'\n", (LPCTSTR) path, start); break; } diff --git a/util/ShellTree.h b/util/ShellTree.h index 59ef7f0..ea1dcef 100644 --- a/util/ShellTree.h +++ b/util/ShellTree.h @@ -10,8 +10,8 @@ * interface". Enhanced by Selom Ofori as "ShellTree" class. Modified * extensively. */ -#ifndef __SHELLTREE__ -#define __SHELLTREE__ +#ifndef UTIL_SHELLTREE_H +#define UTIL_SHELLTREE_H /* @@ -81,4 +81,4 @@ protected: DECLARE_MESSAGE_MAP() }; -#endif /*__SHELLTREE__*/ \ No newline at end of file +#endif /*UTIL_SHELLTREE_H*/ diff --git a/util/SoundFile.cpp b/util/SoundFile.cpp index fe6c1bc..87b4647 100644 --- a/util/SoundFile.cpp +++ b/util/SoundFile.cpp @@ -36,15 +36,15 @@ MakeFourCC(unsigned char c0, unsigned char c1, unsigned char c2, * Returns 0 on success. */ int -SoundFile::Create(const char* fileName, CString* pErrMsg) +SoundFile::Create(const WCHAR* fileName, CString* pErrMsg) { FILE* fp = nil; long fileLen; - fp = fopen(fileName, "rb"); + fp = _wfopen(fileName, L"rb"); if (fp == nil) { int err = errno; - pErrMsg->Format("Unable to open '%s'", fileName); + pErrMsg->Format(L"Unable to open '%ls'", fileName); return err; } @@ -80,7 +80,7 @@ SoundFile::Create(FILE* fp, long len, bool doClose, CString* pErrMsg) if (fp == nil) return -1; if (len < kWAVMinSize) { - *pErrMsg = "File is too short to be WAV"; + *pErrMsg = L"File is too short to be WAV"; return -1; } @@ -94,14 +94,14 @@ SoundFile::Create(FILE* fp, long len, bool doClose, CString* pErrMsg) */ if (fread(&fileHeader, sizeof(fileHeader), 1, mFP) != 1) { err = errno ? errno : -1; - *pErrMsg = "Failed reading file header"; + *pErrMsg = L"Failed reading file header"; goto bail; } if (fileHeader.riff != MakeFourCC('R','I','F','F') || fileHeader.wav != MakeFourCC('W','A','V','E') || fileHeader.fileLen > (unsigned long) len) { - *pErrMsg = "File is not a WAV file"; + *pErrMsg = L"File is not a WAV file"; WMSG3("Not a valid WAV header (0x%08lx %d 0x%08lx)\n", fileHeader.riff, fileHeader.fileLen, fileHeader.wav); err = -1; @@ -125,7 +125,7 @@ SoundFile::Create(FILE* fp, long len, bool doClose, CString* pErrMsg) * than the structure size. */ if (chunkLen > sizeof(WAVEFORMATEX)) { - pErrMsg->Format("Bad WAV file: 'fmt ' size is %d, struct is %d", + pErrMsg->Format(L"Bad WAV file: 'fmt ' size is %d, struct is %d", chunkLen, sizeof(WAVEFORMATEX)); err = -1; goto bail; @@ -133,13 +133,13 @@ SoundFile::Create(FILE* fp, long len, bool doClose, CString* pErrMsg) //memset(&mFormat, 0, sizeof(mFormat)); // done in constructor if (fread(&mFormat, chunkLen, 1, mFP) != 1) { err = errno ? errno : -1; - *pErrMsg = "Failed reading WAVEFORMATEX"; + *pErrMsg = L"Failed reading WAVEFORMATEX"; goto bail; } /* check the format for compatibility */ if (mFormat.wFormatTag != WAVE_FORMAT_PCM) { - *pErrMsg = "WAV file is not PCM format"; + *pErrMsg = L"WAV file is not PCM format"; err = -1; goto bail; } @@ -202,7 +202,6 @@ SoundFile::SkipToHeader(unsigned long hdrID, unsigned long* pChunkLen) return err; } - /* * Read a block of data from the specified offset. */ diff --git a/util/SoundFile.h b/util/SoundFile.h index a35f128..6f55fbd 100644 --- a/util/SoundFile.h +++ b/util/SoundFile.h @@ -9,8 +9,8 @@ * * [ Copied from libfadden. ] */ -#ifndef __LF_SOUND_FILE__ -#define __LF_SOUND_FILE__ +#ifndef UTIL_SOUNDFILE_H +#define UTIL_SOUNDFILE_H #include @@ -42,7 +42,7 @@ public: } /* create the object from a file on disk; returns 0 on success */ - int Create(const char* fileName, CString* pErrMsg); + int Create(const WCHAR* fileName, CString* pErrMsg); /* create from FILE*; if doClose==true, file will be closed on error */ int Create(FILE* fp, long len, bool doClose, CString* pErrMsg); @@ -61,7 +61,7 @@ public: /* returns the #of bytes per sample (all channels) */ int GetBPS(void) const { - assert(mFP != nil); + ASSERT(mFP != nil); return ((mFormat.wBitsPerSample+7)/8) * mFormat.nChannels; } @@ -95,4 +95,4 @@ typedef struct tWAVEFORMATEX } WAVEFORMATEX; #endif -#endif /*__LF_SOUND_FILE__*/ +#endif /*UTIL_SOUNDFILE_H*/ diff --git a/util/StdAfx.cpp b/util/StdAfx.cpp index 422a7b1..253b410 100644 --- a/util/StdAfx.cpp +++ b/util/StdAfx.cpp @@ -8,4 +8,3 @@ // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" - diff --git a/util/StdAfx.h b/util/StdAfx.h index 0d8d012..a5ac2ae 100644 --- a/util/StdAfx.h +++ b/util/StdAfx.h @@ -17,6 +17,8 @@ #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#include "../app/targetver.h" + #include #include #include diff --git a/util/Util.cpp b/util/Util.cpp index 9a5c873..56a0c2f 100644 --- a/util/Util.cpp +++ b/util/Util.cpp @@ -25,7 +25,7 @@ BEGIN_MESSAGE_MAP(CGripper, CScrollBar) ON_WM_NCHITTEST() END_MESSAGE_MAP() -UINT +LRESULT CGripper::OnNcHitTest(CPoint point) { UINT ht = CScrollBar::OnNcHitTest(point); @@ -414,7 +414,7 @@ SetDlgButtonCheck(CWnd* pWnd, int id, int checkVal) * Create a font, using defaults for most things. */ void -CreateSimpleFont(CFont* pFont, CWnd* pWnd, const char* typeFace, +CreateSimpleFont(CFont* pFont, CWnd* pWnd, const WCHAR* typeFace, int pointSize) { CClientDC dc(pWnd); @@ -449,7 +449,7 @@ GetWin32ErrorString(DWORD err, CString* pStr) if (!count) { WMSG1("FormatMessage on err=0x%08lx failed\n", err); - pStr->Format("system error 0x%08lx.\n", err); + pStr->Format(L"system error 0x%08lx.\n", err); } else { *pStr = (LPCTSTR)lpMsgBuf; //MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION ); @@ -571,7 +571,7 @@ LogHexDump(const void* vbuf, long len) if (skipFirst) { skipFirst = false; } else { - WMSG1(" %s\n", outBuf); + WMSG1(" %hs\n", outBuf); addr += 16; } sprintf(outBuf, "%08lx: ", addr); @@ -583,7 +583,7 @@ LogHexDump(const void* vbuf, long len) } /* output whatever is left */ - WMSG1(" %s\n", outBuf); + WMSG1(" %hs\n", outBuf); } /* @@ -619,12 +619,12 @@ void FormatDate(time_t when, CString* pStr) { if (when == kDateNone) { - *pStr = "[No Date]"; + *pStr = L"[No Date]"; } else if (when == kDateInvalid) { - *pStr = ""; + *pStr = L""; } else { CTime modWhen(when); - *pStr = modWhen.Format("%d-%b-%y %H:%M"); + *pStr = modWhen.Format(L"%d-%b-%y %H:%M"); } } @@ -635,25 +635,25 @@ FormatDate(time_t when, CString* pStr) * The isalpha() stuff is an optimization, so they can skip the tolower() * in the outer loop comparison. */ -char* -stristr(const char* string1, const char* string2) +const WCHAR* +Stristr(const WCHAR* string1, const WCHAR* string2) { - char *cp1 = (char*) string1, *cp2, *cp1a; - char first; // get the first char in string to find + WCHAR *cp1 = (WCHAR*)string1, *cp2, *cp1a; + WCHAR first; // get the first char in string to find first = string2[0]; // first char often won't be alpha - if (isalpha(first)) { - first = tolower(first); + if (iswalpha(first)) { + first = towlower(first); for ( ; *cp1 != '\0'; cp1++) { - if (tolower(*cp1) == first) + if (towlower(*cp1) == first) { - for (cp1a = &cp1[1], cp2 = (char*) &string2[1]; ; + for (cp1a = &cp1[1], cp2 = (WCHAR*) &string2[1];; cp1a++, cp2++) { if (*cp2 == '\0') return cp1; - if (tolower(*cp1a) != tolower(*cp2)) + if (towlower(*cp1a) != towlower(*cp2)) break; } } @@ -665,12 +665,12 @@ stristr(const char* string1, const char* string2) { if (*cp1 == first) { - for (cp1a = &cp1[1], cp2 = (char*) &string2[1]; ; + for (cp1a = &cp1[1], cp2 = (WCHAR*) &string2[1];; cp1a++, cp2++) { if (*cp2 == '\0') return cp1; - if (tolower(*cp1a) != tolower(*cp2)) + if (towlower(*cp1a) != towlower(*cp2)) break; } } @@ -692,11 +692,11 @@ stristr(const char* string1, const char* string2) * allocated. */ void -VectorizeString(char* mangle, char** argv, int* pArgc) +VectorizeString(WCHAR* mangle, WCHAR** argv, int* pArgc) { bool inWhiteSpace = true; bool inQuote = false; - char* cp = mangle; + WCHAR* cp = mangle; int idx = 0; while (*cp != '\0') { @@ -711,7 +711,7 @@ VectorizeString(char* mangle, char** argv, int* pArgc) if (inWhiteSpace) { /* start of token */ if (idx >= *pArgc) { - //WMSG2("Max #of args (%d) exceeded, ignoring '%s'\n", + //WMSG2("Max #of args (%d) exceeded, ignoring '%ls'\n", // *pArgc, cp); break; } @@ -719,8 +719,8 @@ VectorizeString(char* mangle, char** argv, int* pArgc) } if (*cp == '"') { - /* consume the quote */ - memmove(cp, cp+1, strlen(cp)); // move the last '\0' down too + /* consume the quote; move the last '\0' down too */ + memmove(cp, cp+1, wcslen(cp) * sizeof(WCHAR)); cp--; inQuote = !inQuote; } @@ -746,26 +746,26 @@ static void DowncaseSubstring(CString* pStr, int startPos, int endPos, bool prevWasSpace) { - static const char* shortWords[] = { - "of", "the", "a", "an", "and", "to", "in" + static const WCHAR* shortWords[] = { + L"of", L"the", L"a", L"an", L"and", L"to", L"in" }; - static const char* leaveAlone[] = { - "BBS", "3D" + static const WCHAR* leaveAlone[] = { + L"BBS", L"3D" }; - static const char* justLikeThis[] = { - "ProDOS", "IIe", "IIc", "IIgs" + static const WCHAR* justLikeThis[] = { + L"ProDOS", L"IIe", L"IIc", L"IIgs" }; CString token; bool firstCap = true; int i; token = pStr->Mid(startPos, endPos - startPos); - //WMSG1(" TOKEN: '%s'\n", token); + //WMSG1(" TOKEN: '%ls'\n", (LPCWSTR) token); /* these words are left alone */ for (i = 0; i < NELEM(leaveAlone); i++) { if (token.CompareNoCase(leaveAlone[i]) == 0) { - //WMSG1(" Leaving alone '%s'\n", token); + //WMSG1(" Leaving alone '%ls'\n", (LPCWSTR) token); return; } } @@ -773,7 +773,7 @@ DowncaseSubstring(CString* pStr, int startPos, int endPos, /* words with specific capitalization */ for (i = 0; i < NELEM(justLikeThis); i++) { if (token.CompareNoCase(justLikeThis[i]) == 0) { - WMSG2(" Setting '%s' to '%s'\n", token, justLikeThis[i]); + WMSG2(" Setting '%ls' to '%ls'\n", token, justLikeThis[i]); for (int j = startPos; j < endPos; j++) pStr->SetAt(j, justLikeThis[i][j - startPos]); return; @@ -784,7 +784,7 @@ DowncaseSubstring(CString* pStr, int startPos, int endPos, if (prevWasSpace) { for (i = 0; i < NELEM(shortWords); i++) { if (token.CompareNoCase(shortWords[i]) == 0) { - //WMSG1(" No leading cap for '%s'\n", token); + //WMSG1(" No leading cap for '%ls'\n", token); firstCap = false; break; } @@ -792,9 +792,9 @@ DowncaseSubstring(CString* pStr, int startPos, int endPos, } /* check for roman numerals; we leave those capitalized */ - CString romanTest = token.SpanIncluding("IVX"); + CString romanTest = token.SpanIncluding(L"IVX"); if (romanTest.GetLength() == token.GetLength()) { - //WMSG1(" Looks like roman numerals '%s'\n", token); + //WMSG1(" Looks like roman numerals '%ls'\n", token); return; } @@ -815,17 +815,17 @@ void InjectLowercase(CString* pStr) { int len = pStr->GetLength(); - static const char* kGapChars = " .:&-+/\\()<>@*"; + static const WCHAR* kGapChars = L" .:&-+/\\()<>@*"; int startPos, endPos; //*pStr = "AND PRODOS FOR THE IIGS"; //len = pStr->GetLength(); - //WMSG1("InjectLowercase: '%s'\n", *pStr); + //WMSG1("InjectLowercase: '%ls'\n", (LPCWSTR) *pStr); for (int i = 0; i < len; i++) { - char ch = pStr->GetAt(i); + WCHAR ch = pStr->GetAt(i); if (ch >= 'a' && ch <= 'z') { - WMSG1("Found lowercase 0x%02x, skipping InjectLower\n", ch); + WMSG1("Found lowercase 0x%04x, skipping InjectLower\n", ch); return; } } @@ -833,10 +833,10 @@ InjectLowercase(CString* pStr) startPos = 0; while (startPos < len) { /* find start of token */ - char ch; + WCHAR ch; do { ch = pStr->GetAt(startPos); - if (strchr(kGapChars, ch) == nil) + if (wcschr(kGapChars, ch) == nil) break; startPos++; } while (startPos < len); @@ -847,7 +847,7 @@ InjectLowercase(CString* pStr) endPos = startPos + 1; while (endPos < len) { ch = pStr->GetAt(endPos); - if (strchr(kGapChars, ch) != nil) + if (wcschr(kGapChars, ch) != nil) break; endPos++; } @@ -873,7 +873,7 @@ InjectLowercase(CString* pStr) bool MatchSemicolonList(const CString set, const CString match) { - const char* cp; + const WCHAR* cp; CString mangle(set); int matchLen = match.GetLength(); @@ -882,10 +882,10 @@ MatchSemicolonList(const CString set, const CString match) mangle.Remove(' '); for (cp = mangle; *cp != '\0'; ) { - if (strncasecmp(cp, match, matchLen) == 0 && + if (wcsnicmp(cp, match, matchLen) == 0 && (cp[matchLen] == ';' || cp[matchLen] == '\0')) { - WMSG2("+++ Found '%s' at '%s'\n", (LPCTSTR) match, cp); + WMSG2("+++ Found '%ls' at '%ls'\n", (LPCWSTR) match, cp); return true; } @@ -895,7 +895,7 @@ MatchSemicolonList(const CString set, const CString match) cp++; } - WMSG2("--- No match for '%s' in '%s'\n", (LPCTSTR) match, (LPCTSTR) set); + WMSG2("--- No match for '%ls' in '%ls'\n", (LPCWSTR) match, (LPCWSTR) set); return false; } diff --git a/util/Util.h b/util/Util.h index 641e186..b7da485 100644 --- a/util/Util.h +++ b/util/Util.h @@ -6,15 +6,15 @@ /* * Miscellaneous utility classes. */ -#ifndef __UTIL__ -#define __UTIL__ +#ifndef UTIL_UTIL_H +#define UTIL_UTIL_H /* * Gripper for a resizable window. */ class CGripper: public CScrollBar { protected: - afx_msg UINT OnNcHitTest(CPoint point); + afx_msg LRESULT OnNcHitTest(CPoint point); DECLARE_MESSAGE_MAP() }; @@ -42,7 +42,7 @@ public: class ExpandBuffer { public: ExpandBuffer(long initialSize = 65536) { - assert(initialSize > 0); + ASSERT(initialSize > 0); fInitialSize = initialSize; fWorkBuf = nil; fWorkCount = fWorkMax = 0; @@ -106,7 +106,7 @@ HDWP MoveStretchControl(HDWP hdwp, CDialog* pDlg, int id, int moveX, int moveY, int stretchX, int stretchY, bool redraw = true); int GetDlgButtonCheck(CWnd* pWnd, int id); void SetDlgButtonCheck(CWnd* pWnd, int id, int checkVal); -void CreateSimpleFont(CFont* pFont, CWnd* pWnd, const char* typeFace, +void CreateSimpleFont(CFont* pFont, CWnd* pWnd, const WCHAR* typeFace, int pointSize); void GetWin32ErrorString(DWORD err, CString* pStr); void ShowFailureMsg(CWnd* pWnd, const CString& msg, int titleStrID); @@ -120,8 +120,8 @@ int GetPascalString(const char* buf, long maxLen, CString* pStr); void LogHexDump(const void* buf, long len); int ComputePercent(LONGLONG part, LONGLONG full); void FormatDate(time_t when, CString* pStr); -char* stristr(const char* string1, const char* string2); -void VectorizeString(char* mangle, char** argv, int* pArgc); +const WCHAR* Stristr(const WCHAR* string1, const WCHAR* string2); +void VectorizeString(WCHAR* mangle, WCHAR** argv, int* pArgc); void InjectLowercase(CString* pStr); bool MatchSemicolonList(const CString set, const CString match); char* StrcpyNew(const char* str); @@ -130,4 +130,4 @@ char* StrcpyNew(const char* str); #define kDateNone ((time_t) -2) #define kDateInvalid ((time_t) -1) // should match return from mktime() -#endif /*__UTIL__*/ \ No newline at end of file +#endif /*UTIL_UTIL_H*/ diff --git a/util/UtilLib.h b/util/UtilLib.h index 1abad9d..2d04ee2 100644 --- a/util/UtilLib.h +++ b/util/UtilLib.h @@ -6,8 +6,8 @@ /* * Include this to get headers for all contents of utility library. */ -#ifndef __UTIL_LIB__ -#define __UTIL_LIB__ +#ifndef UTIL_LIB_H +#define UTIL_LIB_H #include "FaddenStd.h" #include "MyDebug.h" @@ -28,4 +28,4 @@ #include "CancelDialog.h" #include "ProgressCancelDialog.h" -#endif /*__UTIL_LIB__*/ \ No newline at end of file +#endif /*UTIL_LIB_H*/ diff --git a/util/util.vcxproj b/util/util.vcxproj index ec0a515..22f6c26 100644 --- a/util/util.vcxproj +++ b/util/util.vcxproj @@ -27,7 +27,7 @@ StaticLibrary v120 Dynamic - MultiByte + Unicode @@ -43,12 +43,12 @@ <_ProjectFileVersion>12.0.30501.0 - .\Release\ - .\Release\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ - .\Debug\ - .\Debug\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ @@ -60,15 +60,15 @@ true Use stdafx.h - .\Release/util.pch - .\Release/ - .\Release/ - .\Release/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true - .\Release\util.lib + $(OutDir)$(TargetName)$(TargetExt) true @@ -84,16 +84,16 @@ MultiThreadedDebugDLL Use stdafx.h - .\Debug/util.pch - .\Debug/ - .\Debug/ - .\Debug/ + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)vc$(PlatformToolsetVersion).pdb Level3 true EditAndContinue - .\Debug\util.lib + $(OutDir)$(TargetName)$(TargetExt) true @@ -101,66 +101,6 @@ 0x0409 - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - Disabled - EnableFastChecks - MaxSpeed - - - - Disabled - EnableFastChecks - Create - MaxSpeed - Create - - - Disabled - EnableFastChecks - MaxSpeed - - @@ -181,6 +121,23 @@ + + + + + + + + + + + + + Create + Create + + + diff --git a/util/util.vcxproj.filters b/util/util.vcxproj.filters index 3da9fcf..aa30a22 100644 --- a/util/util.vcxproj.filters +++ b/util/util.vcxproj.filters @@ -10,44 +10,6 @@ h;hpp;hxx;hm;inl - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - Header Files @@ -104,4 +66,42 @@ Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/zlib/ChangeLog b/zlib/ChangeLog new file mode 100644 index 0000000..f22aaba --- /dev/null +++ b/zlib/ChangeLog @@ -0,0 +1,1472 @@ + + ChangeLog file for zlib + +Changes in 1.2.8 (28 Apr 2013) +- Update contrib/minizip/iowin32.c for Windows RT [Vollant] +- Do not force Z_CONST for C++ +- Clean up contrib/vstudio [Ro§] +- Correct spelling error in zlib.h +- Fix mixed line endings in contrib/vstudio + +Changes in 1.2.7.3 (13 Apr 2013) +- Fix version numbers and DLL names in contrib/vstudio/*/zlib.rc + +Changes in 1.2.7.2 (13 Apr 2013) +- Change check for a four-byte type back to hexadecimal +- Fix typo in win32/Makefile.msc +- Add casts in gzwrite.c for pointer differences + +Changes in 1.2.7.1 (24 Mar 2013) +- Replace use of unsafe string functions with snprintf if available +- Avoid including stddef.h on Windows for Z_SOLO compile [Niessink] +- Fix gzgetc undefine when Z_PREFIX set [Turk] +- Eliminate use of mktemp in Makefile (not always available) +- Fix bug in 'F' mode for gzopen() +- Add inflateGetDictionary() function +- Correct comment in deflate.h +- Use _snprintf for snprintf in Microsoft C +- On Darwin, only use /usr/bin/libtool if libtool is not Apple +- Delete "--version" file if created by "ar --version" [Richard G.] +- Fix configure check for veracity of compiler error return codes +- Fix CMake compilation of static lib for MSVC2010 x64 +- Remove unused variable in infback9.c +- Fix argument checks in gzlog_compress() and gzlog_write() +- Clean up the usage of z_const and respect const usage within zlib +- Clean up examples/gzlog.[ch] comparisons of different types +- Avoid shift equal to bits in type (caused endless loop) +- Fix unintialized value bug in gzputc() introduced by const patches +- Fix memory allocation error in examples/zran.c [Nor] +- Fix bug where gzopen(), gzclose() would write an empty file +- Fix bug in gzclose() when gzwrite() runs out of memory +- Check for input buffer malloc failure in examples/gzappend.c +- Add note to contrib/blast to use binary mode in stdio +- Fix comparisons of differently signed integers in contrib/blast +- Check for invalid code length codes in contrib/puff +- Fix serious but very rare decompression bug in inftrees.c +- Update inflateBack() comments, since inflate() can be faster +- Use underscored I/O function names for WINAPI_FAMILY +- Add _tr_flush_bits to the external symbols prefixed by --zprefix +- Add contrib/vstudio/vc10 pre-build step for static only +- Quote --version-script argument in CMakeLists.txt +- Don't specify --version-script on Apple platforms in CMakeLists.txt +- Fix casting error in contrib/testzlib/testzlib.c +- Fix types in contrib/minizip to match result of get_crc_table() +- Simplify contrib/vstudio/vc10 with 'd' suffix +- Add TOP support to win32/Makefile.msc +- Suport i686 and amd64 assembler builds in CMakeLists.txt +- Fix typos in the use of _LARGEFILE64_SOURCE in zconf.h +- Add vc11 and vc12 build files to contrib/vstudio +- Add gzvprintf() as an undocumented function in zlib +- Fix configure for Sun shell +- Remove runtime check in configure for four-byte integer type +- Add casts and consts to ease user conversion to C++ +- Add man pages for minizip and miniunzip +- In Makefile uninstall, don't rm if preceding cd fails +- Do not return Z_BUF_ERROR if deflateParam() has nothing to write + +Changes in 1.2.7 (2 May 2012) +- Replace use of memmove() with a simple copy for portability +- Test for existence of strerror +- Restore gzgetc_ for backward compatibility with 1.2.6 +- Fix build with non-GNU make on Solaris +- Require gcc 4.0 or later on Mac OS X to use the hidden attribute +- Include unistd.h for Watcom C +- Use __WATCOMC__ instead of __WATCOM__ +- Do not use the visibility attribute if NO_VIZ defined +- Improve the detection of no hidden visibility attribute +- Avoid using __int64 for gcc or solo compilation +- Cast to char * in gzprintf to avoid warnings [Zinser] +- Fix make_vms.com for VAX [Zinser] +- Don't use library or built-in byte swaps +- Simplify test and use of gcc hidden attribute +- Fix bug in gzclose_w() when gzwrite() fails to allocate memory +- Add "x" (O_EXCL) and "e" (O_CLOEXEC) modes support to gzopen() +- Fix bug in test/minigzip.c for configure --solo +- Fix contrib/vstudio project link errors [Mohanathas] +- Add ability to choose the builder in make_vms.com [Schweda] +- Add DESTDIR support to mingw32 win32/Makefile.gcc +- Fix comments in win32/Makefile.gcc for proper usage +- Allow overriding the default install locations for cmake +- Generate and install the pkg-config file with cmake +- Build both a static and a shared version of zlib with cmake +- Include version symbols for cmake builds +- If using cmake with MSVC, add the source directory to the includes +- Remove unneeded EXTRA_CFLAGS from win32/Makefile.gcc [Truta] +- Move obsolete emx makefile to old [Truta] +- Allow the use of -Wundef when compiling or using zlib +- Avoid the use of the -u option with mktemp +- Improve inflate() documentation on the use of Z_FINISH +- Recognize clang as gcc +- Add gzopen_w() in Windows for wide character path names +- Rename zconf.h in CMakeLists.txt to move it out of the way +- Add source directory in CMakeLists.txt for building examples +- Look in build directory for zlib.pc in CMakeLists.txt +- Remove gzflags from zlibvc.def in vc9 and vc10 +- Fix contrib/minizip compilation in the MinGW environment +- Update ./configure for Solaris, support --64 [Mooney] +- Remove -R. from Solaris shared build (possible security issue) +- Avoid race condition for parallel make (-j) running example +- Fix type mismatch between get_crc_table() and crc_table +- Fix parsing of version with "-" in CMakeLists.txt [Snider, Ziegler] +- Fix the path to zlib.map in CMakeLists.txt +- Force the native libtool in Mac OS X to avoid GNU libtool [Beebe] +- Add instructions to win32/Makefile.gcc for shared install [Torri] + +Changes in 1.2.6.1 (12 Feb 2012) +- Avoid the use of the Objective-C reserved name "id" +- Include io.h in gzguts.h for Microsoft compilers +- Fix problem with ./configure --prefix and gzgetc macro +- Include gz_header definition when compiling zlib solo +- Put gzflags() functionality back in zutil.c +- Avoid library header include in crc32.c for Z_SOLO +- Use name in GCC_CLASSIC as C compiler for coverage testing, if set +- Minor cleanup in contrib/minizip/zip.c [Vollant] +- Update make_vms.com [Zinser] +- Remove unnecessary gzgetc_ function +- Use optimized byte swap operations for Microsoft and GNU [Snyder] +- Fix minor typo in zlib.h comments [Rzesniowiecki] + +Changes in 1.2.6 (29 Jan 2012) +- Update the Pascal interface in contrib/pascal +- Fix function numbers for gzgetc_ in zlibvc.def files +- Fix configure.ac for contrib/minizip [Schiffer] +- Fix large-entry detection in minizip on 64-bit systems [Schiffer] +- Have ./configure use the compiler return code for error indication +- Fix CMakeLists.txt for cross compilation [McClure] +- Fix contrib/minizip/zip.c for 64-bit architectures [Dalsnes] +- Fix compilation of contrib/minizip on FreeBSD [Marquez] +- Correct suggested usages in win32/Makefile.msc [Shachar, Horvath] +- Include io.h for Turbo C / Borland C on all platforms [Truta] +- Make version explicit in contrib/minizip/configure.ac [Bosmans] +- Avoid warning for no encryption in contrib/minizip/zip.c [Vollant] +- Minor cleanup up contrib/minizip/unzip.c [Vollant] +- Fix bug when compiling minizip with C++ [Vollant] +- Protect for long name and extra fields in contrib/minizip [Vollant] +- Avoid some warnings in contrib/minizip [Vollant] +- Add -I../.. -L../.. to CFLAGS for minizip and miniunzip +- Add missing libs to minizip linker command +- Add support for VPATH builds in contrib/minizip +- Add an --enable-demos option to contrib/minizip/configure +- Add the generation of configure.log by ./configure +- Exit when required parameters not provided to win32/Makefile.gcc +- Have gzputc return the character written instead of the argument +- Use the -m option on ldconfig for BSD systems [Tobias] +- Correct in zlib.map when deflateResetKeep was added + +Changes in 1.2.5.3 (15 Jan 2012) +- Restore gzgetc function for binary compatibility +- Do not use _lseeki64 under Borland C++ [Truta] +- Update win32/Makefile.msc to build test/*.c [Truta] +- Remove old/visualc6 given CMakefile and other alternatives +- Update AS400 build files and documentation [Monnerat] +- Update win32/Makefile.gcc to build test/*.c [Truta] +- Permit stronger flushes after Z_BLOCK flushes +- Avoid extraneous empty blocks when doing empty flushes +- Permit Z_NULL arguments to deflatePending +- Allow deflatePrime() to insert bits in the middle of a stream +- Remove second empty static block for Z_PARTIAL_FLUSH +- Write out all of the available bits when using Z_BLOCK +- Insert the first two strings in the hash table after a flush + +Changes in 1.2.5.2 (17 Dec 2011) +- fix ld error: unable to find version dependency 'ZLIB_1.2.5' +- use relative symlinks for shared libs +- Avoid searching past window for Z_RLE strategy +- Assure that high-water mark initialization is always applied in deflate +- Add assertions to fill_window() in deflate.c to match comments +- Update python link in README +- Correct spelling error in gzread.c +- Fix bug in gzgets() for a concatenated empty gzip stream +- Correct error in comment for gz_make() +- Change gzread() and related to ignore junk after gzip streams +- Allow gzread() and related to continue after gzclearerr() +- Allow gzrewind() and gzseek() after a premature end-of-file +- Simplify gzseek() now that raw after gzip is ignored +- Change gzgetc() to a macro for speed (~40% speedup in testing) +- Fix gzclose() to return the actual error last encountered +- Always add large file support for windows +- Include zconf.h for windows large file support +- Include zconf.h.cmakein for windows large file support +- Update zconf.h.cmakein on make distclean +- Merge vestigial vsnprintf determination from zutil.h to gzguts.h +- Clarify how gzopen() appends in zlib.h comments +- Correct documentation of gzdirect() since junk at end now ignored +- Add a transparent write mode to gzopen() when 'T' is in the mode +- Update python link in zlib man page +- Get inffixed.h and MAKEFIXED result to match +- Add a ./config --solo option to make zlib subset with no libary use +- Add undocumented inflateResetKeep() function for CAB file decoding +- Add --cover option to ./configure for gcc coverage testing +- Add #define ZLIB_CONST option to use const in the z_stream interface +- Add comment to gzdopen() in zlib.h to use dup() when using fileno() +- Note behavior of uncompress() to provide as much data as it can +- Add files in contrib/minizip to aid in building libminizip +- Split off AR options in Makefile.in and configure +- Change ON macro to Z_ARG to avoid application conflicts +- Facilitate compilation with Borland C++ for pragmas and vsnprintf +- Include io.h for Turbo C / Borland C++ +- Move example.c and minigzip.c to test/ +- Simplify incomplete code table filling in inflate_table() +- Remove code from inflate.c and infback.c that is impossible to execute +- Test the inflate code with full coverage +- Allow deflateSetDictionary, inflateSetDictionary at any time (in raw) +- Add deflateResetKeep and fix inflateResetKeep to retain dictionary +- Fix gzwrite.c to accommodate reduced memory zlib compilation +- Have inflate() with Z_FINISH avoid the allocation of a window +- Do not set strm->adler when doing raw inflate +- Fix gzeof() to behave just like feof() when read is not past end of file +- Fix bug in gzread.c when end-of-file is reached +- Avoid use of Z_BUF_ERROR in gz* functions except for premature EOF +- Document gzread() capability to read concurrently written files +- Remove hard-coding of resource compiler in CMakeLists.txt [Blammo] + +Changes in 1.2.5.1 (10 Sep 2011) +- Update FAQ entry on shared builds (#13) +- Avoid symbolic argument to chmod in Makefile.in +- Fix bug and add consts in contrib/puff [Oberhumer] +- Update contrib/puff/zeros.raw test file to have all block types +- Add full coverage test for puff in contrib/puff/Makefile +- Fix static-only-build install in Makefile.in +- Fix bug in unzGetCurrentFileInfo() in contrib/minizip [Kuno] +- Add libz.a dependency to shared in Makefile.in for parallel builds +- Spell out "number" (instead of "nb") in zlib.h for total_in, total_out +- Replace $(...) with `...` in configure for non-bash sh [Bowler] +- Add darwin* to Darwin* and solaris* to SunOS\ 5* in configure [Groffen] +- Add solaris* to Linux* in configure to allow gcc use [Groffen] +- Add *bsd* to Linux* case in configure [Bar-Lev] +- Add inffast.obj to dependencies in win32/Makefile.msc +- Correct spelling error in deflate.h [Kohler] +- Change libzdll.a again to libz.dll.a (!) in win32/Makefile.gcc +- Add test to configure for GNU C looking for gcc in output of $cc -v +- Add zlib.pc generation to win32/Makefile.gcc [Weigelt] +- Fix bug in zlib.h for _FILE_OFFSET_BITS set and _LARGEFILE64_SOURCE not +- Add comment in zlib.h that adler32_combine with len2 < 0 makes no sense +- Make NO_DIVIDE option in adler32.c much faster (thanks to John Reiser) +- Make stronger test in zconf.h to include unistd.h for LFS +- Apply Darwin patches for 64-bit file offsets to contrib/minizip [Slack] +- Fix zlib.h LFS support when Z_PREFIX used +- Add updated as400 support (removed from old) [Monnerat] +- Avoid deflate sensitivity to volatile input data +- Avoid division in adler32_combine for NO_DIVIDE +- Clarify the use of Z_FINISH with deflateBound() amount of space +- Set binary for output file in puff.c +- Use u4 type for crc_table to avoid conversion warnings +- Apply casts in zlib.h to avoid conversion warnings +- Add OF to prototypes for adler32_combine_ and crc32_combine_ [Miller] +- Improve inflateSync() documentation to note indeterminancy +- Add deflatePending() function to return the amount of pending output +- Correct the spelling of "specification" in FAQ [Randers-Pehrson] +- Add a check in configure for stdarg.h, use for gzprintf() +- Check that pointers fit in ints when gzprint() compiled old style +- Add dummy name before $(SHAREDLIBV) in Makefile [Bar-Lev, Bowler] +- Delete line in configure that adds -L. libz.a to LDFLAGS [Weigelt] +- Add debug records in assmebler code [Londer] +- Update RFC references to use http://tools.ietf.org/html/... [Li] +- Add --archs option, use of libtool to configure for Mac OS X [Borstel] + +Changes in 1.2.5 (19 Apr 2010) +- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev] +- Default to libdir as sharedlibdir in configure [Nieder] +- Update copyright dates on modified source files +- Update trees.c to be able to generate modified trees.h +- Exit configure for MinGW, suggesting win32/Makefile.gcc +- Check for NULL path in gz_open [Homurlu] + +Changes in 1.2.4.5 (18 Apr 2010) +- Set sharedlibdir in configure [Torok] +- Set LDFLAGS in Makefile.in [Bar-Lev] +- Avoid mkdir objs race condition in Makefile.in [Bowler] +- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays +- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C +- Don't use hidden attribute when it is a warning generator (e.g. Solaris) + +Changes in 1.2.4.4 (18 Apr 2010) +- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok] +- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty +- Try to use bash or ksh regardless of functionality of /bin/sh +- Fix configure incompatibility with NetBSD sh +- Remove attempt to run under bash or ksh since have better NetBSD fix +- Fix win32/Makefile.gcc for MinGW [Bar-Lev] +- Add diagnostic messages when using CROSS_PREFIX in configure +- Added --sharedlibdir option to configure [Weigelt] +- Use hidden visibility attribute when available [Frysinger] + +Changes in 1.2.4.3 (10 Apr 2010) +- Only use CROSS_PREFIX in configure for ar and ranlib if they exist +- Use CROSS_PREFIX for nm [Bar-Lev] +- Assume _LARGEFILE64_SOURCE defined is equivalent to true +- Avoid use of undefined symbols in #if with && and || +- Make *64 prototypes in gzguts.h consistent with functions +- Add -shared load option for MinGW in configure [Bowler] +- Move z_off64_t to public interface, use instead of off64_t +- Remove ! from shell test in configure (not portable to Solaris) +- Change +0 macro tests to -0 for possibly increased portability + +Changes in 1.2.4.2 (9 Apr 2010) +- Add consistent carriage returns to readme.txt's in masmx86 and masmx64 +- Really provide prototypes for *64 functions when building without LFS +- Only define unlink() in minigzip.c if unistd.h not included +- Update README to point to contrib/vstudio project files +- Move projects/vc6 to old/ and remove projects/ +- Include stdlib.h in minigzip.c for setmode() definition under WinCE +- Clean up assembler builds in win32/Makefile.msc [Rowe] +- Include sys/types.h for Microsoft for off_t definition +- Fix memory leak on error in gz_open() +- Symbolize nm as $NM in configure [Weigelt] +- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt] +- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined +- Fix bug in gzeof() to take into account unused input data +- Avoid initialization of structures with variables in puff.c +- Updated win32/README-WIN32.txt [Rowe] + +Changes in 1.2.4.1 (28 Mar 2010) +- Remove the use of [a-z] constructs for sed in configure [gentoo 310225] +- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech] +- Restore "for debugging" comment on sprintf() in gzlib.c +- Remove fdopen for MVS from gzguts.h +- Put new README-WIN32.txt in win32 [Rowe] +- Add check for shell to configure and invoke another shell if needed +- Fix big fat stinking bug in gzseek() on uncompressed files +- Remove vestigial F_OPEN64 define in zutil.h +- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE +- Avoid errors on non-LFS systems when applications define LFS macros +- Set EXE to ".exe" in configure for MINGW [Kahle] +- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill] +- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev] +- Add DLL install in win32/makefile.gcc [Bar-Lev] +- Allow Linux* or linux* from uname in configure [Bar-Lev] +- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev] +- Add cross-compilation prefixes to configure [Bar-Lev] +- Match type exactly in gz_load() invocation in gzread.c +- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func +- Provide prototypes for *64 functions when building zlib without LFS +- Don't use -lc when linking shared library on MinGW +- Remove errno.h check in configure and vestigial errno code in zutil.h + +Changes in 1.2.4 (14 Mar 2010) +- Fix VER3 extraction in configure for no fourth subversion +- Update zlib.3, add docs to Makefile.in to make .pdf out of it +- Add zlib.3.pdf to distribution +- Don't set error code in gzerror() if passed pointer is NULL +- Apply destination directory fixes to CMakeLists.txt [Lowman] +- Move #cmakedefine's to a new zconf.in.cmakein +- Restore zconf.h for builds that don't use configure or cmake +- Add distclean to dummy Makefile for convenience +- Update and improve INDEX, README, and FAQ +- Update CMakeLists.txt for the return of zconf.h [Lowman] +- Update contrib/vstudio/vc9 and vc10 [Vollant] +- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc +- Apply license and readme changes to contrib/asm686 [Raiter] +- Check file name lengths and add -c option in minigzip.c [Li] +- Update contrib/amd64 and contrib/masmx86/ [Vollant] +- Avoid use of "eof" parameter in trees.c to not shadow library variable +- Update make_vms.com for removal of zlibdefs.h [Zinser] +- Update assembler code and vstudio projects in contrib [Vollant] +- Remove outdated assembler code contrib/masm686 and contrib/asm586 +- Remove old vc7 and vc8 from contrib/vstudio +- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe] +- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open() +- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant] +- Remove *64 functions from win32/zlib.def (they're not 64-bit yet) +- Fix bug in void-returning vsprintf() case in gzwrite.c +- Fix name change from inflate.h in contrib/inflate86/inffas86.c +- Check if temporary file exists before removing in make_vms.com [Zinser] +- Fix make install and uninstall for --static option +- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta] +- Update readme.txt in contrib/masmx64 and masmx86 to assemble + +Changes in 1.2.3.9 (21 Feb 2010) +- Expunge gzio.c +- Move as400 build information to old +- Fix updates in contrib/minizip and contrib/vstudio +- Add const to vsnprintf test in configure to avoid warnings [Weigelt] +- Delete zconf.h (made by configure) [Weigelt] +- Change zconf.in.h to zconf.h.in per convention [Weigelt] +- Check for NULL buf in gzgets() +- Return empty string for gzgets() with len == 1 (like fgets()) +- Fix description of gzgets() in zlib.h for end-of-file, NULL return +- Update minizip to 1.1 [Vollant] +- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c +- Note in zlib.h that gzerror() should be used to distinguish from EOF +- Remove use of snprintf() from gzlib.c +- Fix bug in gzseek() +- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant] +- Fix zconf.h generation in CMakeLists.txt [Lowman] +- Improve comments in zconf.h where modified by configure + +Changes in 1.2.3.8 (13 Feb 2010) +- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer] +- Use z_off64_t in gz_zero() and gz_skip() to match state->skip +- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t) +- Revert to Makefile.in from 1.2.3.6 (live with the clutter) +- Fix missing error return in gzflush(), add zlib.h note +- Add *64 functions to zlib.map [Levin] +- Fix signed/unsigned comparison in gz_comp() +- Use SFLAGS when testing shared linking in configure +- Add --64 option to ./configure to use -m64 with gcc +- Fix ./configure --help to correctly name options +- Have make fail if a test fails [Levin] +- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson] +- Remove assembler object files from contrib + +Changes in 1.2.3.7 (24 Jan 2010) +- Always gzopen() with O_LARGEFILE if available +- Fix gzdirect() to work immediately after gzopen() or gzdopen() +- Make gzdirect() more precise when the state changes while reading +- Improve zlib.h documentation in many places +- Catch memory allocation failure in gz_open() +- Complete close operation if seek forward in gzclose_w() fails +- Return Z_ERRNO from gzclose_r() if close() fails +- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL +- Return zero for gzwrite() errors to match zlib.h description +- Return -1 on gzputs() error to match zlib.h description +- Add zconf.in.h to allow recovery from configure modification [Weigelt] +- Fix static library permissions in Makefile.in [Weigelt] +- Avoid warnings in configure tests that hide functionality [Weigelt] +- Add *BSD and DragonFly to Linux case in configure [gentoo 123571] +- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212] +- Avoid access of uninitialized data for first inflateReset2 call [Gomes] +- Keep object files in subdirectories to reduce the clutter somewhat +- Remove default Makefile and zlibdefs.h, add dummy Makefile +- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_ +- Remove zlibdefs.h completely -- modify zconf.h instead + +Changes in 1.2.3.6 (17 Jan 2010) +- Avoid void * arithmetic in gzread.c and gzwrite.c +- Make compilers happier with const char * for gz_error message +- Avoid unused parameter warning in inflate.c +- Avoid signed-unsigned comparison warning in inflate.c +- Indent #pragma's for traditional C +- Fix usage of strwinerror() in glib.c, change to gz_strwinerror() +- Correct email address in configure for system options +- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser] +- Update zlib.map [Brown] +- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok] +- Apply various fixes to CMakeLists.txt [Lowman] +- Add checks on len in gzread() and gzwrite() +- Add error message for no more room for gzungetc() +- Remove zlib version check in gzwrite() +- Defer compression of gzprintf() result until need to +- Use snprintf() in gzdopen() if available +- Remove USE_MMAP configuration determination (only used by minigzip) +- Remove examples/pigz.c (available separately) +- Update examples/gun.c to 1.6 + +Changes in 1.2.3.5 (8 Jan 2010) +- Add space after #if in zutil.h for some compilers +- Fix relatively harmless bug in deflate_fast() [Exarevsky] +- Fix same problem in deflate_slow() +- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown] +- Add deflate_rle() for faster Z_RLE strategy run-length encoding +- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding +- Change name of "write" variable in inffast.c to avoid library collisions +- Fix premature EOF from gzread() in gzio.c [Brown] +- Use zlib header window size if windowBits is 0 in inflateInit2() +- Remove compressBound() call in deflate.c to avoid linking compress.o +- Replace use of errno in gz* with functions, support WinCE [Alves] +- Provide alternative to perror() in minigzip.c for WinCE [Alves] +- Don't use _vsnprintf on later versions of MSVC [Lowman] +- Add CMake build script and input file [Lowman] +- Update contrib/minizip to 1.1 [Svensson, Vollant] +- Moved nintendods directory from contrib to . +- Replace gzio.c with a new set of routines with the same functionality +- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above +- Update contrib/minizip to 1.1b +- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h + +Changes in 1.2.3.4 (21 Dec 2009) +- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility +- Update comments in configure and Makefile.in for default --shared +- Fix test -z's in configure [Marquess] +- Build examplesh and minigzipsh when not testing +- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h +- Import LDFLAGS from the environment in configure +- Fix configure to populate SFLAGS with discovered CFLAGS options +- Adapt make_vms.com to the new Makefile.in [Zinser] +- Add zlib2ansi script for C++ compilation [Marquess] +- Add _FILE_OFFSET_BITS=64 test to make test (when applicable) +- Add AMD64 assembler code for longest match to contrib [Teterin] +- Include options from $SFLAGS when doing $LDSHARED +- Simplify 64-bit file support by introducing z_off64_t type +- Make shared object files in objs directory to work around old Sun cc +- Use only three-part version number for Darwin shared compiles +- Add rc option to ar in Makefile.in for when ./configure not run +- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* +- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile +- Protect against _FILE_OFFSET_BITS being defined when compiling zlib +- Rename Makefile.in targets allstatic to static and allshared to shared +- Fix static and shared Makefile.in targets to be independent +- Correct error return bug in gz_open() by setting state [Brown] +- Put spaces before ;;'s in configure for better sh compatibility +- Add pigz.c (parallel implementation of gzip) to examples/ +- Correct constant in crc32.c to UL [Leventhal] +- Reject negative lengths in crc32_combine() +- Add inflateReset2() function to work like inflateEnd()/inflateInit2() +- Include sys/types.h for _LARGEFILE64_SOURCE [Brown] +- Correct typo in doc/algorithm.txt [Janik] +- Fix bug in adler32_combine() [Zhu] +- Catch missing-end-of-block-code error in all inflates and in puff + Assures that random input to inflate eventually results in an error +- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ +- Update ENOUGH and its usage to reflect discovered bounds +- Fix gzerror() error report on empty input file [Brown] +- Add ush casts in trees.c to avoid pedantic runtime errors +- Fix typo in zlib.h uncompress() description [Reiss] +- Correct inflate() comments with regard to automatic header detection +- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) +- Put new version of gzlog (2.0) in examples with interruption recovery +- Add puff compile option to permit invalid distance-too-far streams +- Add puff TEST command options, ability to read piped input +- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but + _LARGEFILE64_SOURCE not defined +- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart +- Fix deflateSetDictionary() to use all 32K for output consistency +- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) +- Clear bytes after deflate lookahead to avoid use of uninitialized data +- Change a limit in inftrees.c to be more transparent to Coverity Prevent +- Update win32/zlib.def with exported symbols from zlib.h +- Correct spelling errors in zlib.h [Willem, Sobrado] +- Allow Z_BLOCK for deflate() to force a new block +- Allow negative bits in inflatePrime() to delete existing bit buffer +- Add Z_TREES flush option to inflate() to return at end of trees +- Add inflateMark() to return current state information for random access +- Add Makefile for NintendoDS to contrib [Costa] +- Add -w in configure compile tests to avoid spurious warnings [Beucler] +- Fix typos in zlib.h comments for deflateSetDictionary() +- Fix EOF detection in transparent gzread() [Maier] + +Changes in 1.2.3.3 (2 October 2006) +- Make --shared the default for configure, add a --static option +- Add compile option to permit invalid distance-too-far streams +- Add inflateUndermine() function which is required to enable above +- Remove use of "this" variable name for C++ compatibility [Marquess] +- Add testing of shared library in make test, if shared library built +- Use ftello() and fseeko() if available instead of ftell() and fseek() +- Provide two versions of all functions that use the z_off_t type for + binary compatibility -- a normal version and a 64-bit offset version, + per the Large File Support Extension when _LARGEFILE64_SOURCE is + defined; use the 64-bit versions by default when _FILE_OFFSET_BITS + is defined to be 64 +- Add a --uname= option to configure to perhaps help with cross-compiling + +Changes in 1.2.3.2 (3 September 2006) +- Turn off silly Borland warnings [Hay] +- Use off64_t and define _LARGEFILE64_SOURCE when present +- Fix missing dependency on inffixed.h in Makefile.in +- Rig configure --shared to build both shared and static [Teredesai, Truta] +- Remove zconf.in.h and instead create a new zlibdefs.h file +- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant] +- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt] + +Changes in 1.2.3.1 (16 August 2006) +- Add watcom directory with OpenWatcom make files [Daniel] +- Remove #undef of FAR in zconf.in.h for MVS [Fedtke] +- Update make_vms.com [Zinser] +- Use -fPIC for shared build in configure [Teredesai, Nicholson] +- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] +- Use fdopen() (not _fdopen()) for Interix in zutil.h [BŠck] +- Add some FAQ entries about the contrib directory +- Update the MVS question in the FAQ +- Avoid extraneous reads after EOF in gzio.c [Brown] +- Correct spelling of "successfully" in gzio.c [Randers-Pehrson] +- Add comments to zlib.h about gzerror() usage [Brown] +- Set extra flags in gzip header in gzopen() like deflate() does +- Make configure options more compatible with double-dash conventions + [Weigelt] +- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen] +- Fix uninstall target in Makefile.in [Truta] +- Add pkgconfig support [Weigelt] +- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt] +- Replace set_data_type() with a more accurate detect_data_type() in + trees.c, according to the txtvsbin.txt document [Truta] +- Swap the order of #include and #include "zlib.h" in + gzio.c, example.c and minigzip.c [Truta] +- Shut up annoying VS2005 warnings about standard C deprecation [Rowe, + Truta] (where?) +- Fix target "clean" from win32/Makefile.bor [Truta] +- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe] +- Update zlib www home address in win32/DLL_FAQ.txt [Truta] +- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove] +- Enable browse info in the "Debug" and "ASM Debug" configurations in + the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta] +- Add pkgconfig support [Weigelt] +- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h, + for use in win32/zlib1.rc [Polushin, Rowe, Truta] +- Add a document that explains the new text detection scheme to + doc/txtvsbin.txt [Truta] +- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta] +- Move algorithm.txt into doc/ [Truta] +- Synchronize FAQ with website +- Fix compressBound(), was low for some pathological cases [Fearnley] +- Take into account wrapper variations in deflateBound() +- Set examples/zpipe.c input and output to binary mode for Windows +- Update examples/zlib_how.html with new zpipe.c (also web site) +- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems + that gcc became pickier in 4.0) +- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain + un-versioned, the patch adds versioning only for symbols introduced in + zlib-1.2.0 or later. It also declares as local those symbols which are + not designed to be exported." [Levin] +- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure +- Do not initialize global static by default in trees.c, add a response + NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess] +- Don't use strerror() in gzio.c under WinCE [Yakimov] +- Don't use errno.h in zutil.h under WinCE [Yakimov] +- Move arguments for AR to its usage to allow replacing ar [Marot] +- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson] +- Improve inflateInit() and inflateInit2() documentation +- Fix structure size comment in inflate.h +- Change configure help option from --h* to --help [Santos] + +Changes in 1.2.3 (18 July 2005) +- Apply security vulnerability fixes to contrib/infback9 as well +- Clean up some text files (carriage returns, trailing space) +- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] + +Changes in 1.2.2.4 (11 July 2005) +- Add inflatePrime() function for starting inflation at bit boundary +- Avoid some Visual C warnings in deflate.c +- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit + compile +- Fix some spelling errors in comments [Betts] +- Correct inflateInit2() error return documentation in zlib.h +- Add zran.c example of compressed data random access to examples + directory, shows use of inflatePrime() +- Fix cast for assignments to strm->state in inflate.c and infback.c +- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] +- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] +- Add cast in trees.c t avoid a warning [Oberhumer] +- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] +- Update make_vms.com [Zinser] +- Initialize state->write in inflateReset() since copied in inflate_fast() +- Be more strict on incomplete code sets in inflate_table() and increase + ENOUGH and MAXD -- this repairs a possible security vulnerability for + invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for + discovering the vulnerability and providing test cases. +- Add ia64 support to configure for HP-UX [Smith] +- Add error return to gzread() for format or i/o error [Levin] +- Use malloc.h for OS/2 [Necasek] + +Changes in 1.2.2.3 (27 May 2005) +- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile +- Typecast fread() return values in gzio.c [Vollant] +- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) +- Fix crc check bug in gzread() after gzungetc() [Heiner] +- Add the deflateTune() function to adjust internal compression parameters +- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) +- Remove an incorrect assertion in examples/zpipe.c +- Add C++ wrapper in infback9.h [Donais] +- Fix bug in inflateCopy() when decoding fixed codes +- Note in zlib.h how much deflateSetDictionary() actually uses +- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) +- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] +- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] +- Add gzdirect() function to indicate transparent reads +- Update contrib/minizip [Vollant] +- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] +- Add casts in crc32.c to avoid warnings [Oberhumer] +- Add contrib/masmx64 [Vollant] +- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] + +Changes in 1.2.2.2 (30 December 2004) +- Replace structure assignments in deflate.c and inflate.c with zmemcpy to + avoid implicit memcpy calls (portability for no-library compilation) +- Increase sprintf() buffer size in gzdopen() to allow for large numbers +- Add INFLATE_STRICT to check distances against zlib header +- Improve WinCE errno handling and comments [Chang] +- Remove comment about no gzip header processing in FAQ +- Add Z_FIXED strategy option to deflateInit2() to force fixed trees +- Add updated make_vms.com [Coghlan], update README +- Create a new "examples" directory, move gzappend.c there, add zpipe.c, + fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. +- Add FAQ entry and comments in deflate.c on uninitialized memory access +- Add Solaris 9 make options in configure [Gilbert] +- Allow strerror() usage in gzio.c for STDC +- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] +- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] +- Use z_off_t for adler32_combine() and crc32_combine() lengths +- Make adler32() much faster for small len +- Use OS_CODE in deflate() default gzip header + +Changes in 1.2.2.1 (31 October 2004) +- Allow inflateSetDictionary() call for raw inflate +- Fix inflate header crc check bug for file names and comments +- Add deflateSetHeader() and gz_header structure for custom gzip headers +- Add inflateGetheader() to retrieve gzip headers +- Add crc32_combine() and adler32_combine() functions +- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list +- Use zstreamp consistently in zlib.h (inflate_back functions) +- Remove GUNZIP condition from definition of inflate_mode in inflate.h + and in contrib/inflate86/inffast.S [Truta, Anderson] +- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] +- Update projects/README.projects and projects/visualc6 [Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] +- Deprecate Z_ASCII; use Z_TEXT instead [Truta] +- Use a new algorithm for setting strm->data_type in trees.c [Truta] +- Do not define an exit() prototype in zutil.c unless DEBUG defined +- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] +- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() +- Fix Darwin build version identification [Peterson] + +Changes in 1.2.2 (3 October 2004) +- Update zlib.h comments on gzip in-memory processing +- Set adler to 1 in inflateReset() to support Java test suite [Walles] +- Add contrib/dotzlib [Ravn] +- Update win32/DLL_FAQ.txt [Truta] +- Update contrib/minizip [Vollant] +- Move contrib/visual-basic.txt to old/ [Truta] +- Fix assembler builds in projects/visualc6/ [Truta] + +Changes in 1.2.1.2 (9 September 2004) +- Update INDEX file +- Fix trees.c to update strm->data_type (no one ever noticed!) +- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] +- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) +- Add limited multitasking protection to DYNAMIC_CRC_TABLE +- Add NO_vsnprintf for VMS in zutil.h [Mozilla] +- Don't declare strerror() under VMS [Mozilla] +- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize +- Update contrib/ada [Anisimkov] +- Update contrib/minizip [Vollant] +- Fix configure to not hardcode directories for Darwin [Peterson] +- Fix gzio.c to not return error on empty files [Brown] +- Fix indentation; update version in contrib/delphi/ZLib.pas and + contrib/pascal/zlibpas.pas [Truta] +- Update mkasm.bat in contrib/masmx86 [Truta] +- Update contrib/untgz [Truta] +- Add projects/README.projects [Truta] +- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] +- Update win32/DLL_FAQ.txt [Truta] +- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] +- Remove an unnecessary assignment to curr in inftrees.c [Truta] +- Add OS/2 to exe builds in configure [Poltorak] +- Remove err dummy parameter in zlib.h [Kientzle] + +Changes in 1.2.1.1 (9 January 2004) +- Update email address in README +- Several FAQ updates +- Fix a big fat bug in inftrees.c that prevented decoding valid + dynamic blocks with only literals and no distance codes -- + Thanks to "Hot Emu" for the bug report and sample file +- Add a note to puff.c on no distance codes case. + +Changes in 1.2.1 (17 November 2003) +- Remove a tab in contrib/gzappend/gzappend.c +- Update some interfaces in contrib for new zlib functions +- Update zlib version number in some contrib entries +- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] +- Support shared libraries on Hurd and KFreeBSD [Brown] +- Fix error in NO_DIVIDE option of adler32.c + +Changes in 1.2.0.8 (4 November 2003) +- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas +- Add experimental NO_DIVIDE #define in adler32.c + - Possibly faster on some processors (let me know if it is) +- Correct Z_BLOCK to not return on first inflate call if no wrap +- Fix strm->data_type on inflate() return to correctly indicate EOB +- Add deflatePrime() function for appending in the middle of a byte +- Add contrib/gzappend for an example of appending to a stream +- Update win32/DLL_FAQ.txt [Truta] +- Delete Turbo C comment in README [Truta] +- Improve some indentation in zconf.h [Truta] +- Fix infinite loop on bad input in configure script [Church] +- Fix gzeof() for concatenated gzip files [Johnson] +- Add example to contrib/visual-basic.txt [Michael B.] +- Add -p to mkdir's in Makefile.in [vda] +- Fix configure to properly detect presence or lack of printf functions +- Add AS400 support [Monnerat] +- Add a little Cygwin support [Wilson] + +Changes in 1.2.0.7 (21 September 2003) +- Correct some debug formats in contrib/infback9 +- Cast a type in a debug statement in trees.c +- Change search and replace delimiter in configure from % to # [Beebe] +- Update contrib/untgz to 0.2 with various fixes [Truta] +- Add build support for Amiga [Nikl] +- Remove some directories in old that have been updated to 1.2 +- Add dylib building for Mac OS X in configure and Makefile.in +- Remove old distribution stuff from Makefile +- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X +- Update links in README + +Changes in 1.2.0.6 (13 September 2003) +- Minor FAQ updates +- Update contrib/minizip to 1.00 [Vollant] +- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] +- Update POSTINC comment for 68060 [Nikl] +- Add contrib/infback9 with deflate64 decoding (unsupported) +- For MVS define NO_vsnprintf and undefine FAR [van Burik] +- Add pragma for fdopen on MVS [van Burik] + +Changes in 1.2.0.5 (8 September 2003) +- Add OF to inflateBackEnd() declaration in zlib.h +- Remember start when using gzdopen in the middle of a file +- Use internal off_t counters in gz* functions to properly handle seeks +- Perform more rigorous check for distance-too-far in inffast.c +- Add Z_BLOCK flush option to return from inflate at block boundary +- Set strm->data_type on return from inflate + - Indicate bits unused, if at block boundary, and if in last block +- Replace size_t with ptrdiff_t in crc32.c, and check for correct size +- Add condition so old NO_DEFLATE define still works for compatibility +- FAQ update regarding the Windows DLL [Truta] +- INDEX update: add qnx entry, remove aix entry [Truta] +- Install zlib.3 into mandir [Wilson] +- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] +- Adapt the zlib interface to the new DLL convention guidelines [Truta] +- Introduce ZLIB_WINAPI macro to allow the export of functions using + the WINAPI calling convention, for Visual Basic [Vollant, Truta] +- Update msdos and win32 scripts and makefiles [Truta] +- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] +- Add contrib/ada [Anisimkov] +- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] +- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] +- Add contrib/masm686 [Truta] +- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm + [Truta, Vollant] +- Update contrib/delphi; rename to contrib/pascal; add example [Truta] +- Remove contrib/delphi2; add a new contrib/delphi [Truta] +- Avoid inclusion of the nonstandard in contrib/iostream, + and fix some method prototypes [Truta] +- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip + [Truta] +- Avoid the use of backslash (\) in contrib/minizip [Vollant] +- Fix file time handling in contrib/untgz; update makefiles [Truta] +- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines + [Vollant] +- Remove contrib/vstudio/vc15_16 [Vollant] +- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] +- Update README.contrib [Truta] +- Invert the assignment order of match_head and s->prev[...] in + INSERT_STRING [Truta] +- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings + [Truta] +- Compare function pointers with 0, not with NULL or Z_NULL [Truta] +- Fix prototype of syncsearch in inflate.c [Truta] +- Introduce ASMINF macro to be enabled when using an ASM implementation + of inflate_fast [Truta] +- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] +- Modify test_gzio in example.c to take a single file name as a + parameter [Truta] +- Exit the example.c program if gzopen fails [Truta] +- Add type casts around strlen in example.c [Truta] +- Remove casting to sizeof in minigzip.c; give a proper type + to the variable compared with SUFFIX_LEN [Truta] +- Update definitions of STDC and STDC99 in zconf.h [Truta] +- Synchronize zconf.h with the new Windows DLL interface [Truta] +- Use SYS16BIT instead of __32BIT__ to distinguish between + 16- and 32-bit platforms [Truta] +- Use far memory allocators in small 16-bit memory models for + Turbo C [Truta] +- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in + zlibCompileFlags [Truta] +- Cygwin has vsnprintf [Wilson] +- In Windows16, OS_CODE is 0, as in MSDOS [Truta] +- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] + +Changes in 1.2.0.4 (10 August 2003) +- Minor FAQ updates +- Be more strict when checking inflateInit2's windowBits parameter +- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well +- Add gzip wrapper option to deflateInit2 using windowBits +- Add updated QNX rule in configure and qnx directory [Bonnefoy] +- Make inflate distance-too-far checks more rigorous +- Clean up FAR usage in inflate +- Add casting to sizeof() in gzio.c and minigzip.c + +Changes in 1.2.0.3 (19 July 2003) +- Fix silly error in gzungetc() implementation [Vollant] +- Update contrib/minizip and contrib/vstudio [Vollant] +- Fix printf format in example.c +- Correct cdecl support in zconf.in.h [Anisimkov] +- Minor FAQ updates + +Changes in 1.2.0.2 (13 July 2003) +- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons +- Attempt to avoid warnings in crc32.c for pointer-int conversion +- Add AIX to configure, remove aix directory [Bakker] +- Add some casts to minigzip.c +- Improve checking after insecure sprintf() or vsprintf() calls +- Remove #elif's from crc32.c +- Change leave label to inf_leave in inflate.c and infback.c to avoid + library conflicts +- Remove inflate gzip decoding by default--only enable gzip decoding by + special request for stricter backward compatibility +- Add zlibCompileFlags() function to return compilation information +- More typecasting in deflate.c to avoid warnings +- Remove leading underscore from _Capital #defines [Truta] +- Fix configure to link shared library when testing +- Add some Windows CE target adjustments [Mai] +- Remove #define ZLIB_DLL in zconf.h [Vollant] +- Add zlib.3 [Rodgers] +- Update RFC URL in deflate.c and algorithm.txt [Mai] +- Add zlib_dll_FAQ.txt to contrib [Truta] +- Add UL to some constants [Truta] +- Update minizip and vstudio [Vollant] +- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h +- Expand use of NO_DUMMY_DECL to avoid all dummy structures +- Added iostream3 to contrib [Schwardt] +- Replace rewind() with fseek() for WinCE [Truta] +- Improve setting of zlib format compression level flags + - Report 0 for huffman and rle strategies and for level == 0 or 1 + - Report 2 only for level == 6 +- Only deal with 64K limit when necessary at compile time [Truta] +- Allow TOO_FAR check to be turned off at compile time [Truta] +- Add gzclearerr() function [Souza] +- Add gzungetc() function + +Changes in 1.2.0.1 (17 March 2003) +- Add Z_RLE strategy for run-length encoding [Truta] + - When Z_RLE requested, restrict matches to distance one + - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE +- Correct FASTEST compilation to allow level == 0 +- Clean up what gets compiled for FASTEST +- Incorporate changes to zconf.in.h [Vollant] + - Refine detection of Turbo C need for dummy returns + - Refine ZLIB_DLL compilation + - Include additional header file on VMS for off_t typedef +- Try to use _vsnprintf where it supplants vsprintf [Vollant] +- Add some casts in inffast.c +- Enchance comments in zlib.h on what happens if gzprintf() tries to + write more than 4095 bytes before compression +- Remove unused state from inflateBackEnd() +- Remove exit(0) from minigzip.c, example.c +- Get rid of all those darn tabs +- Add "check" target to Makefile.in that does the same thing as "test" +- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in +- Update contrib/inflate86 [Anderson] +- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] +- Add msdos and win32 directories with makefiles [Truta] +- More additions and improvements to the FAQ + +Changes in 1.2.0 (9 March 2003) +- New and improved inflate code + - About 20% faster + - Does not allocate 32K window unless and until needed + - Automatically detects and decompresses gzip streams + - Raw inflate no longer needs an extra dummy byte at end + - Added inflateBack functions using a callback interface--even faster + than inflate, useful for file utilities (gzip, zip) + - Added inflateCopy() function to record state for random access on + externally generated deflate streams (e.g. in gzip files) + - More readable code (I hope) +- New and improved crc32() + - About 50% faster, thanks to suggestions from Rodney Brown +- Add deflateBound() and compressBound() functions +- Fix memory leak in deflateInit2() +- Permit setting dictionary for raw deflate (for parallel deflate) +- Fix const declaration for gzwrite() +- Check for some malloc() failures in gzio.c +- Fix bug in gzopen() on single-byte file 0x1f +- Fix bug in gzread() on concatenated file with 0x1f at end of buffer + and next buffer doesn't start with 0x8b +- Fix uncompress() to return Z_DATA_ERROR on truncated input +- Free memory at end of example.c +- Remove MAX #define in trees.c (conflicted with some libraries) +- Fix static const's in deflate.c, gzio.c, and zutil.[ch] +- Declare malloc() and free() in gzio.c if STDC not defined +- Use malloc() instead of calloc() in zutil.c if int big enough +- Define STDC for AIX +- Add aix/ with approach for compiling shared library on AIX +- Add HP-UX support for shared libraries in configure +- Add OpenUNIX support for shared libraries in configure +- Use $cc instead of gcc to build shared library +- Make prefix directory if needed when installing +- Correct Macintosh avoidance of typedef Byte in zconf.h +- Correct Turbo C memory allocation when under Linux +- Use libz.a instead of -lz in Makefile (assure use of compiled library) +- Update configure to check for snprintf or vsnprintf functions and their + return value, warn during make if using an insecure function +- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that + is lost when library is used--resolution is to build new zconf.h +- Documentation improvements (in zlib.h): + - Document raw deflate and inflate + - Update RFCs URL + - Point out that zlib and gzip formats are different + - Note that Z_BUF_ERROR is not fatal + - Document string limit for gzprintf() and possible buffer overflow + - Note requirement on avail_out when flushing + - Note permitted values of flush parameter of inflate() +- Add some FAQs (and even answers) to the FAQ +- Add contrib/inflate86/ for x86 faster inflate +- Add contrib/blast/ for PKWare Data Compression Library decompression +- Add contrib/puff/ simple inflate for deflate format description + +Changes in 1.1.4 (11 March 2002) +- ZFREE was repeated on same allocation on some error conditions. + This creates a security problem described in + http://www.zlib.org/advisory-2002-03-11.txt +- Returned incorrect error (Z_MEM_ERROR) on some invalid data +- Avoid accesses before window for invalid distances with inflate window + less than 32K. +- force windowBits > 8 to avoid a bug in the encoder for a window size + of 256 bytes. (A complete fix will be available in 1.1.5). + +Changes in 1.1.3 (9 July 1998) +- fix "an inflate input buffer bug that shows up on rare but persistent + occasions" (Mark) +- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) +- fix gzseek(..., SEEK_SET) in write mode +- fix crc check after a gzeek (Frank Faubert) +- fix miniunzip when the last entry in a zip file is itself a zip file + (J Lillge) +- add contrib/asm586 and contrib/asm686 (Brian Raiter) + See http://www.muppetlabs.com/~breadbox/software/assembly.html +- add support for Delphi 3 in contrib/delphi (Bob Dellaca) +- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) +- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) +- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) +- added a FAQ file + +- Support gzdopen on Mac with Metrowerks (Jason Linhart) +- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) +- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) +- avoid some warnings with Borland C (Tom Tanner) +- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) +- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) +- allow several arguments to configure (Tim Mooney, Frodo Looijaard) +- use libdir and includedir in Makefile.in (Tim Mooney) +- support shared libraries on OSF1 V4 (Tim Mooney) +- remove so_locations in "make clean" (Tim Mooney) +- fix maketree.c compilation error (Glenn, Mark) +- Python interface to zlib now in Python 1.5 (Jeremy Hylton) +- new Makefile.riscos (Rich Walker) +- initialize static descriptors in trees.c for embedded targets (Nick Smith) +- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) +- add the OS/2 files in Makefile.in too (Andrew Zabolotny) +- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) +- fix maketree.c to allow clean compilation of inffixed.h (Mark) +- fix parameter check in deflateCopy (Gunther Nikl) +- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) +- Many portability patches by Christian Spieler: + . zutil.c, zutil.h: added "const" for zmem* + . Make_vms.com: fixed some typos + . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists + . msdos/Makefile.msc: remove "default rtl link library" info from obj files + . msdos/Makefile.*: use model-dependent name for the built zlib library + . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: + new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) +- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) +- replace __far with _far for better portability (Christian Spieler, Tom Lane) +- fix test for errno.h in configure (Tim Newsham) + +Changes in 1.1.2 (19 March 98) +- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) + See http://www.winimage.com/zLibDll/unzip.html +- preinitialize the inflate tables for fixed codes, to make the code + completely thread safe (Mark) +- some simplifications and slight speed-up to the inflate code (Mark) +- fix gzeof on non-compressed files (Allan Schrum) +- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) +- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) +- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) +- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) +- do not wrap extern "C" around system includes (Tom Lane) +- mention zlib binding for TCL in README (Andreas Kupries) +- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) +- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) +- allow "configure --prefix $HOME" (Tim Mooney) +- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) +- move Makefile.sas to amiga/Makefile.sas + +Changes in 1.1.1 (27 Feb 98) +- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) +- remove block truncation heuristic which had very marginal effect for zlib + (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the + compression ratio on some files. This also allows inlining _tr_tally for + matches in deflate_slow. +- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) + +Changes in 1.1.0 (24 Feb 98) +- do not return STREAM_END prematurely in inflate (John Bowler) +- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) +- compile with -DFASTEST to get compression code optimized for speed only +- in minigzip, try mmap'ing the input file first (Miguel Albrecht) +- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain + on Sun but significant on HP) + +- add a pointer to experimental unzip library in README (Gilles Vollant) +- initialize variable gcc in configure (Chris Herborth) + +Changes in 1.0.9 (17 Feb 1998) +- added gzputs and gzgets functions +- do not clear eof flag in gzseek (Mark Diekhans) +- fix gzseek for files in transparent mode (Mark Diekhans) +- do not assume that vsprintf returns the number of bytes written (Jens Krinke) +- replace EXPORT with ZEXPORT to avoid conflict with other programs +- added compress2 in zconf.h, zlib.def, zlib.dnt +- new asm code from Gilles Vollant in contrib/asm386 +- simplify the inflate code (Mark): + . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() + . ZALLOC the length list in inflate_trees_fixed() instead of using stack + . ZALLOC the value area for huft_build() instead of using stack + . Simplify Z_FINISH check in inflate() + +- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 +- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) +- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with + the declaration of FAR (Gilles VOllant) +- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) +- read_buf buf parameter of type Bytef* instead of charf* +- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) +- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) +- fix check for presence of directories in "make install" (Ian Willis) + +Changes in 1.0.8 (27 Jan 1998) +- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) +- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) +- added compress2() to allow setting the compression level +- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) +- use constant arrays for the static trees in trees.c instead of computing + them at run time (thanks to Ken Raeburn for this suggestion). To create + trees.h, compile with GEN_TREES_H and run "make test". +- check return code of example in "make test" and display result +- pass minigzip command line options to file_compress +- simplifying code of inflateSync to avoid gcc 2.8 bug + +- support CC="gcc -Wall" in configure -s (QingLong) +- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) +- fix test for shared library support to avoid compiler warnings +- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) +- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) +- do not use fdopen for Metrowerks on Mac (Brad Pettit)) +- add checks for gzputc and gzputc in example.c +- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) +- use const for the CRC table (Ken Raeburn) +- fixed "make uninstall" for shared libraries +- use Tracev instead of Trace in infblock.c +- in example.c use correct compressed length for test_sync +- suppress +vnocompatwarnings in configure for HPUX (not always supported) + +Changes in 1.0.7 (20 Jan 1998) +- fix gzseek which was broken in write mode +- return error for gzseek to negative absolute position +- fix configure for Linux (Chun-Chung Chen) +- increase stack space for MSC (Tim Wegner) +- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) +- define EXPORTVA for gzprintf (Gilles Vollant) +- added man page zlib.3 (Rick Rodgers) +- for contrib/untgz, fix makedir() and improve Makefile + +- check gzseek in write mode in example.c +- allocate extra buffer for seeks only if gzseek is actually called +- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) +- add inflateSyncPoint in zconf.h +- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def + +Changes in 1.0.6 (19 Jan 1998) +- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and + gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) +- Fix a deflate bug occurring only with compression level 0 (thanks to + Andy Buckler for finding this one). +- In minigzip, pass transparently also the first byte for .Z files. +- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() +- check Z_FINISH in inflate (thanks to Marc Schluper) +- Implement deflateCopy (thanks to Adam Costello) +- make static libraries by default in configure, add --shared option. +- move MSDOS or Windows specific files to directory msdos +- suppress the notion of partial flush to simplify the interface + (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) +- suppress history buffer provided by application to simplify the interface + (this feature was not implemented anyway in 1.0.4) +- next_in and avail_in must be initialized before calling inflateInit or + inflateInit2 +- add EXPORT in all exported functions (for Windows DLL) +- added Makefile.nt (thanks to Stephen Williams) +- added the unsupported "contrib" directory: + contrib/asm386/ by Gilles Vollant + 386 asm code replacing longest_match(). + contrib/iostream/ by Kevin Ruland + A C++ I/O streams interface to the zlib gz* functions + contrib/iostream2/ by Tyge Løvset + Another C++ I/O streams interface + contrib/untgz/ by "Pedro A. Aranda Guti\irrez" + A very simple tar.gz file extractor using zlib + contrib/visual-basic.txt by Carlos Rios + How to use compress(), uncompress() and the gz* functions from VB. +- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression + level) in minigzip (thanks to Tom Lane) + +- use const for rommable constants in deflate +- added test for gzseek and gztell in example.c +- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) +- add undocumented function zError to convert error code to string + (for Tim Smithers) +- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. +- Use default memcpy for Symantec MSDOS compiler. +- Add EXPORT keyword for check_func (needed for Windows DLL) +- add current directory to LD_LIBRARY_PATH for "make test" +- create also a link for libz.so.1 +- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) +- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) +- added -soname for Linux in configure (Chun-Chung Chen, +- assign numbers to the exported functions in zlib.def (for Windows DLL) +- add advice in zlib.h for best usage of deflateSetDictionary +- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) +- allow compilation with ANSI keywords only enabled for TurboC in large model +- avoid "versionString"[0] (Borland bug) +- add NEED_DUMMY_RETURN for Borland +- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). +- allow compilation with CC +- defined STDC for OS/2 (David Charlap) +- limit external names to 8 chars for MVS (Thomas Lund) +- in minigzip.c, use static buffers only for 16-bit systems +- fix suffix check for "minigzip -d foo.gz" +- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) +- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) +- added makelcc.bat for lcc-win32 (Tom St Denis) +- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) +- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. +- check for unistd.h in configure (for off_t) +- remove useless check parameter in inflate_blocks_free +- avoid useless assignment of s->check to itself in inflate_blocks_new +- do not flush twice in gzclose (thanks to Ken Raeburn) +- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h +- use NO_ERRNO_H instead of enumeration of operating systems with errno.h +- work around buggy fclose on pipes for HP/UX +- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) +- fix configure if CC is already equal to gcc + +Changes in 1.0.5 (3 Jan 98) +- Fix inflate to terminate gracefully when fed corrupted or invalid data +- Use const for rommable constants in inflate +- Eliminate memory leaks on error conditions in inflate +- Removed some vestigial code in inflate +- Update web address in README + +Changes in 1.0.4 (24 Jul 96) +- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF + bit, so the decompressor could decompress all the correct data but went + on to attempt decompressing extra garbage data. This affected minigzip too. +- zlibVersion and gzerror return const char* (needed for DLL) +- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) +- use z_error only for DEBUG (avoid problem with DLLs) + +Changes in 1.0.3 (2 Jul 96) +- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS + small and medium models; this makes the library incompatible with previous + versions for these models. (No effect in large model or on other systems.) +- return OK instead of BUF_ERROR if previous deflate call returned with + avail_out as zero but there is nothing to do +- added memcmp for non STDC compilers +- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) +- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) +- better check for 16-bit mode MSC (avoids problem with Symantec) + +Changes in 1.0.2 (23 May 96) +- added Windows DLL support +- added a function zlibVersion (for the DLL support) +- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) +- Bytef is define's instead of typedef'd only for Borland C +- avoid reading uninitialized memory in example.c +- mention in README that the zlib format is now RFC1950 +- updated Makefile.dj2 +- added algorithm.doc + +Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] +- fix array overlay in deflate.c which sometimes caused bad compressed data +- fix inflate bug with empty stored block +- fix MSDOS medium model which was broken in 0.99 +- fix deflateParams() which could generated bad compressed data. +- Bytef is define'd instead of typedef'ed (work around Borland bug) +- added an INDEX file +- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), + Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) +- speed up adler32 for modern machines without auto-increment +- added -ansi for IRIX in configure +- static_init_done in trees.c is an int +- define unlink as delete for VMS +- fix configure for QNX +- add configure branch for SCO and HPUX +- avoid many warnings (unused variables, dead assignments, etc...) +- no fdopen for BeOS +- fix the Watcom fix for 32 bit mode (define FAR as empty) +- removed redefinition of Byte for MKWERKS +- work around an MWKERKS bug (incorrect merge of all .h files) + +Changes in 0.99 (27 Jan 96) +- allow preset dictionary shared between compressor and decompressor +- allow compression level 0 (no compression) +- add deflateParams in zlib.h: allow dynamic change of compression level + and compression strategy. +- test large buffers and deflateParams in example.c +- add optional "configure" to build zlib as a shared library +- suppress Makefile.qnx, use configure instead +- fixed deflate for 64-bit systems (detected on Cray) +- fixed inflate_blocks for 64-bit systems (detected on Alpha) +- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) +- always return Z_BUF_ERROR when deflate() has nothing to do +- deflateInit and inflateInit are now macros to allow version checking +- prefix all global functions and types with z_ with -DZ_PREFIX +- make falloc completely reentrant (inftrees.c) +- fixed very unlikely race condition in ct_static_init +- free in reverse order of allocation to help memory manager +- use zlib-1.0/* instead of zlib/* inside the tar.gz +- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith + -Wconversion -Wstrict-prototypes -Wmissing-prototypes" +- allow gzread on concatenated .gz files +- deflateEnd now returns Z_DATA_ERROR if it was premature +- deflate is finally (?) fully deterministic (no matches beyond end of input) +- Document Z_SYNC_FLUSH +- add uninstall in Makefile +- Check for __cpluplus in zlib.h +- Better test in ct_align for partial flush +- avoid harmless warnings for Borland C++ +- initialize hash_head in deflate.c +- avoid warning on fdopen (gzio.c) for HP cc -Aa +- include stdlib.h for STDC compilers +- include errno.h for Cray +- ignore error if ranlib doesn't exist +- call ranlib twice for NeXTSTEP +- use exec_prefix instead of prefix for libz.a +- renamed ct_* as _tr_* to avoid conflict with applications +- clear z->msg in inflateInit2 before any error return +- initialize opaque in example.c, gzio.c, deflate.c and inflate.c +- fixed typo in zconf.h (_GNUC__ => __GNUC__) +- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) +- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) +- in fcalloc, normalize pointer if size > 65520 bytes +- don't use special fcalloc for 32 bit Borland C++ +- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... +- use Z_BINARY instead of BINARY +- document that gzclose after gzdopen will close the file +- allow "a" as mode in gzopen. +- fix error checking in gzread +- allow skipping .gz extra-field on pipes +- added reference to Perl interface in README +- put the crc table in FAR data (I dislike more and more the medium model :) +- added get_crc_table +- added a dimension to all arrays (Borland C can't count). +- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast +- guard against multiple inclusion of *.h (for precompiled header on Mac) +- Watcom C pretends to be Microsoft C small model even in 32 bit mode. +- don't use unsized arrays to avoid silly warnings by Visual C++: + warning C4746: 'inflate_mask' : unsized array treated as '__far' + (what's wrong with far data in far model?). +- define enum out of inflate_blocks_state to allow compilation with C++ + +Changes in 0.95 (16 Aug 95) +- fix MSDOS small and medium model (now easier to adapt to any compiler) +- inlined send_bits +- fix the final (:-) bug for deflate with flush (output was correct but + not completely flushed in rare occasions). +- default window size is same for compression and decompression + (it's now sufficient to set MAX_WBITS in zconf.h). +- voidp -> voidpf and voidnp -> voidp (for consistency with other + typedefs and because voidnp was not near in large model). + +Changes in 0.94 (13 Aug 95) +- support MSDOS medium model +- fix deflate with flush (could sometimes generate bad output) +- fix deflateReset (zlib header was incorrectly suppressed) +- added support for VMS +- allow a compression level in gzopen() +- gzflush now calls fflush +- For deflate with flush, flush even if no more input is provided. +- rename libgz.a as libz.a +- avoid complex expression in infcodes.c triggering Turbo C bug +- work around a problem with gcc on Alpha (in INSERT_STRING) +- don't use inline functions (problem with some gcc versions) +- allow renaming of Byte, uInt, etc... with #define. +- avoid warning about (unused) pointer before start of array in deflate.c +- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c +- avoid reserved word 'new' in trees.c + +Changes in 0.93 (25 June 95) +- temporarily disable inline functions +- make deflate deterministic +- give enough lookahead for PARTIAL_FLUSH +- Set binary mode for stdin/stdout in minigzip.c for OS/2 +- don't even use signed char in inflate (not portable enough) +- fix inflate memory leak for segmented architectures + +Changes in 0.92 (3 May 95) +- don't assume that char is signed (problem on SGI) +- Clear bit buffer when starting a stored block +- no memcpy on Pyramid +- suppressed inftest.c +- optimized fill_window, put longest_match inline for gcc +- optimized inflate on stored blocks. +- untabify all sources to simplify patches + +Changes in 0.91 (2 May 95) +- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h +- Document the memory requirements in zconf.h +- added "make install" +- fix sync search logic in inflateSync +- deflate(Z_FULL_FLUSH) now works even if output buffer too short +- after inflateSync, don't scare people with just "lo world" +- added support for DJGPP + +Changes in 0.9 (1 May 95) +- don't assume that zalloc clears the allocated memory (the TurboC bug + was Mark's bug after all :) +- let again gzread copy uncompressed data unchanged (was working in 0.71) +- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented +- added a test of inflateSync in example.c +- moved MAX_WBITS to zconf.h because users might want to change that. +- document explicitly that zalloc(64K) on MSDOS must return a normalized + pointer (zero offset) +- added Makefiles for Microsoft C, Turbo C, Borland C++ +- faster crc32() + +Changes in 0.8 (29 April 95) +- added fast inflate (inffast.c) +- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this + is incompatible with previous versions of zlib which returned Z_OK. +- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) + (actually that was not a compiler bug, see 0.81 above) +- gzread no longer reads one extra byte in certain cases +- In gzio destroy(), don't reference a freed structure +- avoid many warnings for MSDOS +- avoid the ERROR symbol which is used by MS Windows + +Changes in 0.71 (14 April 95) +- Fixed more MSDOS compilation problems :( There is still a bug with + TurboC large model. + +Changes in 0.7 (14 April 95) +- Added full inflate support. +- Simplified the crc32() interface. The pre- and post-conditioning + (one's complement) is now done inside crc32(). WARNING: this is + incompatible with previous versions; see zlib.h for the new usage. + +Changes in 0.61 (12 April 95) +- workaround for a bug in TurboC. example and minigzip now work on MSDOS. + +Changes in 0.6 (11 April 95) +- added minigzip.c +- added gzdopen to reopen a file descriptor as gzFile +- added transparent reading of non-gziped files in gzread. +- fixed bug in gzread (don't read crc as data) +- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). +- don't allocate big arrays in the stack (for MSDOS) +- fix some MSDOS compilation problems + +Changes in 0.5: +- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but + not yet Z_FULL_FLUSH. +- support decompression but only in a single step (forced Z_FINISH) +- added opaque object for zalloc and zfree. +- added deflateReset and inflateReset +- added a variable zlib_version for consistency checking. +- renamed the 'filter' parameter of deflateInit2 as 'strategy'. + Added Z_FILTERED and Z_HUFFMAN_ONLY constants. + +Changes in 0.4: +- avoid "zip" everywhere, use zlib instead of ziplib. +- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush + if compression method == 8. +- added adler32 and crc32 +- renamed deflateOptions as deflateInit2, call one or the other but not both +- added the method parameter for deflateInit2. +- added inflateInit2 +- simplied considerably deflateInit and inflateInit by not supporting + user-provided history buffer. This is supported only in deflateInit2 + and inflateInit2. + +Changes in 0.3: +- prefix all macro names with Z_ +- use Z_FINISH instead of deflateEnd to finish compression. +- added Z_HUFFMAN_ONLY +- added gzerror() diff --git a/zlib/FAQ b/zlib/FAQ new file mode 100644 index 0000000..99b7cf9 --- /dev/null +++ b/zlib/FAQ @@ -0,0 +1,368 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://zlib.net/ which may have more recent information. +The lastest zlib FAQ is at http://zlib.net/zlib_faq.html + + + 1. Is zlib Y2K-compliant? + + Yes. zlib doesn't handle dates. + + 2. Where can I get a Windows DLL version? + + The zlib sources can be compiled without change to produce a DLL. See the + file win32/DLL_FAQ.txt in the zlib distribution. Pointers to the + precompiled DLL are found in the zlib web site at http://zlib.net/ . + + 3. Where can I get a Visual Basic interface to zlib? + + See + * http://marknelson.us/1997/01/01/zlib-engine/ + * win32/DLL_FAQ.txt in the zlib distribution + + 4. compress() returns Z_BUF_ERROR. + + Make sure that before the call of compress(), the length of the compressed + buffer is equal to the available size of the compressed buffer and not + zero. For Visual Basic, check that this parameter is passed by reference + ("as any"), not by value ("as long"). + + 5. deflate() or inflate() returns Z_BUF_ERROR. + + Before making the call, make sure that avail_in and avail_out are not zero. + When setting the parameter flush equal to Z_FINISH, also make sure that + avail_out is big enough to allow processing all pending input. Note that a + Z_BUF_ERROR is not fatal--another call to deflate() or inflate() can be + made with more input or output space. A Z_BUF_ERROR may in fact be + unavoidable depending on how the functions are used, since it is not + possible to tell whether or not there is more output pending when + strm.avail_out returns with zero. See http://zlib.net/zlib_how.html for a + heavily annotated example. + + 6. Where's the zlib documentation (man pages, etc.)? + + It's in zlib.h . Examples of zlib usage are in the files test/example.c + and test/minigzip.c, with more in examples/ . + + 7. Why don't you use GNU autoconf or libtool or ...? + + Because we would like to keep zlib as a very small and simple package. + zlib is rather portable and doesn't need much configuration. + + 8. I found a bug in zlib. + + Most of the time, such problems are due to an incorrect usage of zlib. + Please try to reproduce the problem with a small program and send the + corresponding source to us at zlib@gzip.org . Do not send multi-megabyte + data files without prior agreement. + + 9. Why do I get "undefined reference to gzputc"? + + If "make test" produces something like + + example.o(.text+0x154): undefined reference to `gzputc' + + check that you don't have old files libz.* in /usr/lib, /usr/local/lib or + /usr/X11R6/lib. Remove any old versions, then do "make install". + +10. I need a Delphi interface to zlib. + + See the contrib/delphi directory in the zlib distribution. + +11. Can zlib handle .zip archives? + + Not by itself, no. See the directory contrib/minizip in the zlib + distribution. + +12. Can zlib handle .Z files? + + No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt + the code of uncompress on your own. + +13. How can I make a Unix shared library? + + By default a shared (and a static) library is built for Unix. So: + + make distclean + ./configure + make + +14. How do I install a shared zlib library on Unix? + + After the above, then: + + make install + + However, many flavors of Unix come with a shared zlib already installed. + Before going to the trouble of compiling a shared version of zlib and + trying to install it, you may want to check if it's already there! If you + can #include , it's there. The -lz option will probably link to + it. You can check the version at the top of zlib.h or with the + ZLIB_VERSION symbol defined in zlib.h . + +15. I have a question about OttoPDF. + + We are not the authors of OttoPDF. The real author is on the OttoPDF web + site: Joel Hainley, jhainley@myndkryme.com. + +16. Can zlib decode Flate data in an Adobe PDF file? + + Yes. See http://www.pdflib.com/ . To modify PDF forms, see + http://sourceforge.net/projects/acroformtool/ . + +17. Why am I getting this "register_frame_info not found" error on Solaris? + + After installing zlib 1.1.4 on Solaris 2.6, running applications using zlib + generates an error such as: + + ld.so.1: rpm: fatal: relocation error: file /usr/local/lib/libz.so: + symbol __register_frame_info: referenced symbol not found + + The symbol __register_frame_info is not part of zlib, it is generated by + the C compiler (cc or gcc). You must recompile applications using zlib + which have this problem. This problem is specific to Solaris. See + http://www.sunfreeware.com for Solaris versions of zlib and applications + using zlib. + +18. Why does gzip give an error on a file I make with compress/deflate? + + The compress and deflate functions produce data in the zlib format, which + is different and incompatible with the gzip format. The gz* functions in + zlib on the other hand use the gzip format. Both the zlib and gzip formats + use the same compressed data format internally, but have different headers + and trailers around the compressed data. + +19. Ok, so why are there two different formats? + + The gzip format was designed to retain the directory information about a + single file, such as the name and last modification date. The zlib format + on the other hand was designed for in-memory and communication channel + applications, and has a much more compact header and trailer and uses a + faster integrity check than gzip. + +20. Well that's nice, but how do I make a gzip file in memory? + + You can request that deflate write the gzip format instead of the zlib + format using deflateInit2(). You can also request that inflate decode the + gzip format using inflateInit2(). Read zlib.h for more details. + +21. Is zlib thread-safe? + + Yes. However any library routines that zlib uses and any application- + provided memory allocation routines must also be thread-safe. zlib's gz* + functions use stdio library routines, and most of zlib's functions use the + library memory allocation routines by default. zlib's *Init* functions + allow for the application to provide custom memory allocation routines. + + Of course, you should only operate on any given zlib or gzip stream from a + single thread at a time. + +22. Can I use zlib in my commercial application? + + Yes. Please read the license in zlib.h. + +23. Is zlib under the GNU license? + + No. Please read the license in zlib.h. + +24. The license says that altered source versions must be "plainly marked". So + what exactly do I need to do to meet that requirement? + + You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In + particular, the final version number needs to be changed to "f", and an + identification string should be appended to ZLIB_VERSION. Version numbers + x.x.x.f are reserved for modifications to zlib by others than the zlib + maintainers. For example, if the version of the base zlib you are altering + is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and + ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also + update the version strings in deflate.c and inftrees.c. + + For altered source distributions, you should also note the origin and + nature of the changes in zlib.h, as well as in ChangeLog and README, along + with the dates of the alterations. The origin should include at least your + name (or your company's name), and an email address to contact for help or + issues with the library. + + Note that distributing a compiled zlib library along with zlib.h and + zconf.h is also a source distribution, and so you should change + ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes + in zlib.h as you would for a full source distribution. + +25. Will zlib work on a big-endian or little-endian architecture, and can I + exchange compressed data between them? + + Yes and yes. + +26. Will zlib work on a 64-bit machine? + + Yes. It has been tested on 64-bit machines, and has no dependence on any + data types being limited to 32-bits in length. If you have any + difficulties, please provide a complete problem report to zlib@gzip.org + +27. Will zlib decompress data from the PKWare Data Compression Library? + + No. The PKWare DCL uses a completely different compressed data format than + does PKZIP and zlib. However, you can look in zlib's contrib/blast + directory for a possible solution to your problem. + +28. Can I access data randomly in a compressed stream? + + No, not without some preparation. If when compressing you periodically use + Z_FULL_FLUSH, carefully write all the pending data at those points, and + keep an index of those locations, then you can start decompression at those + points. You have to be careful to not use Z_FULL_FLUSH too often, since it + can significantly degrade compression. Alternatively, you can scan a + deflate stream once to generate an index, and then use that index for + random access. See examples/zran.c . + +29. Does zlib work on MVS, OS/390, CICS, etc.? + + It has in the past, but we have not heard of any recent evidence. There + were working ports of zlib 1.1.4 to MVS, but those links no longer work. + If you know of recent, successful applications of zlib on these operating + systems, please let us know. Thanks. + +30. Is there some simpler, easier to read version of inflate I can look at to + understand the deflate format? + + First off, you should read RFC 1951. Second, yes. Look in zlib's + contrib/puff directory. + +31. Does zlib infringe on any patents? + + As far as we know, no. In fact, that was originally the whole point behind + zlib. Look here for some more information: + + http://www.gzip.org/#faq11 + +32. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit only + if the compiler's "long" type is 32 bits. If the compiler's "long" type is + 64 bits, then the limit is 16 exabytes. + +33. Does zlib have any security vulnerabilities? + + The only one that we are aware of is potentially in gzprintf(). If zlib is + compiled to use sprintf() or vsprintf(), then there is no protection + against a buffer overflow of an 8K string space (or other value as set by + gzbuffer()), other than the caller of gzprintf() assuring that the output + will not exceed 8K. On the other hand, if zlib is compiled to use + snprintf() or vsnprintf(), which should normally be the case, then there is + no vulnerability. The ./configure script will display warnings if an + insecure variation of sprintf() will be used by gzprintf(). Also the + zlibCompileFlags() function will return information on what variant of + sprintf() is used by gzprintf(). + + If you don't have snprintf() or vsnprintf() and would like one, you can + find a portable implementation here: + + http://www.ijs.si/software/snprintf/ + + Note that you should be using the most recent version of zlib. Versions + 1.1.3 and before were subject to a double-free vulnerability, and versions + 1.2.1 and 1.2.2 were subject to an access exception when decompressing + invalid compressed data. + +34. Is there a Java version of zlib? + + Probably what you want is to use zlib in Java. zlib is already included + as part of the Java SDK in the java.util.zip package. If you really want + a version of zlib written in the Java language, look on the zlib home + page for links: http://zlib.net/ . + +35. I get this or that compiler or source-code scanner warning when I crank it + up to maximally-pedantic. Can't you guys write proper code? + + Many years ago, we gave up attempting to avoid warnings on every compiler + in the universe. It just got to be a waste of time, and some compilers + were downright silly as well as contradicted each other. So now, we simply + make sure that the code always works. + +36. Valgrind (or some similar memory access checker) says that deflate is + performing a conditional jump that depends on an uninitialized value. + Isn't that a bug? + + No. That is intentional for performance reasons, and the output of deflate + is not affected. This only started showing up recently since zlib 1.2.x + uses malloc() by default for allocations, whereas earlier versions used + calloc(), which zeros out the allocated memory. Even though the code was + correct, versions 1.2.4 and later was changed to not stimulate these + checkers. + +37. Will zlib read the (insert any ancient or arcane format here) compressed + data format? + + Probably not. Look in the comp.compression FAQ for pointers to various + formats and associated software. + +38. How can I encrypt/decrypt zip files with zlib? + + zlib doesn't support encryption. The original PKZIP encryption is very + weak and can be broken with freely available programs. To get strong + encryption, use GnuPG, http://www.gnupg.org/ , which already includes zlib + compression. For PKZIP compatible "encryption", look at + http://www.info-zip.org/ + +39. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? + + "gzip" is the gzip format, and "deflate" is the zlib format. They should + probably have called the second one "zlib" instead to avoid confusion with + the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 + correctly points to the zlib specification in RFC 1950 for the "deflate" + transfer encoding, there have been reports of servers and browsers that + incorrectly produce or expect raw deflate data per the deflate + specification in RFC 1951, most notably Microsoft. So even though the + "deflate" transfer encoding using the zlib format would be the more + efficient approach (and in fact exactly what the zlib format was designed + for), using the "gzip" transfer encoding is probably more reliable due to + an unfortunate choice of name on the part of the HTTP 1.1 authors. + + Bottom line: use the gzip format for HTTP 1.1 encoding. + +40. Does zlib support the new "Deflate64" format introduced by PKWare? + + No. PKWare has apparently decided to keep that format proprietary, since + they have not documented it as they have previous compression formats. In + any case, the compression improvements are so modest compared to other more + modern approaches, that it's not worth the effort to implement. + +41. I'm having a problem with the zip functions in zlib, can you help? + + There are no zip functions in zlib. You are probably using minizip by + Giles Vollant, which is found in the contrib directory of zlib. It is not + part of zlib. In fact none of the stuff in contrib is part of zlib. The + files in there are not supported by the zlib authors. You need to contact + the authors of the respective contribution for help. + +42. The match.asm code in contrib is under the GNU General Public License. + Since it's part of zlib, doesn't that mean that all of zlib falls under the + GNU GPL? + + No. The files in contrib are not part of zlib. They were contributed by + other authors and are provided as a convenience to the user within the zlib + distribution. Each item in contrib has its own license. + +43. Is zlib subject to export controls? What is its ECCN? + + zlib is not subject to export controls, and so is classified as EAR99. + +44. Can you please sign these lengthy legal documents and fax them back to us + so that we can use your software in our product? + + No. Go away. Shoo. diff --git a/zlib/INDEX b/zlib/INDEX new file mode 100644 index 0000000..2ba0641 --- /dev/null +++ b/zlib/INDEX @@ -0,0 +1,68 @@ +CMakeLists.txt cmake build file +ChangeLog history of changes +FAQ Frequently Asked Questions about zlib +INDEX this file +Makefile dummy Makefile that tells you to ./configure +Makefile.in template for Unix Makefile +README guess what +configure configure script for Unix +make_vms.com makefile for VMS +test/example.c zlib usages examples for build testing +test/minigzip.c minimal gzip-like functionality for build testing +test/infcover.c inf*.c code coverage for build coverage testing +treebuild.xml XML description of source file dependencies +zconf.h.cmakein zconf.h template for cmake +zconf.h.in zconf.h template for configure +zlib.3 Man page for zlib +zlib.3.pdf Man page in PDF format +zlib.map Linux symbol information +zlib.pc.in Template for pkg-config descriptor +zlib.pc.cmakein zlib.pc template for cmake +zlib2ansi perl script to convert source files for C++ compilation + +amiga/ makefiles for Amiga SAS C +as400/ makefiles for AS/400 +doc/ documentation for formats and algorithms +msdos/ makefiles for MSDOS +nintendods/ makefile for Nintendo DS +old/ makefiles for various architectures and zlib documentation + files that have not yet been updated for zlib 1.2.x +qnx/ makefiles for QNX +watcom/ makefiles for OpenWatcom +win32/ makefiles for Windows + + zlib public header files (required for library use): +zconf.h +zlib.h + + private source files used to build the zlib library: +adler32.c +compress.c +crc32.c +crc32.h +deflate.c +deflate.h +gzclose.c +gzguts.h +gzlib.c +gzread.c +gzwrite.c +infback.c +inffast.c +inffast.h +inffixed.h +inflate.c +inflate.h +inftrees.c +inftrees.h +trees.c +trees.h +uncompr.c +zutil.c +zutil.h + + source files for sample programs +See examples/README.examples + + unsupported contributions by third parties +See contrib/README.contrib diff --git a/zlib/README b/zlib/README new file mode 100644 index 0000000..5ca9d12 --- /dev/null +++ b/zlib/README @@ -0,0 +1,115 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.8 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://tools.ietf.org/html/rfc1950 (zlib format), rfc1951 (deflate format) and +rfc1952 (gzip format). + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file test/example.c which also tests that +the library is working correctly. Another example is given in the file +test/minigzip.c. The compression library itself is composed of all source +files in the root directory. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile.in. In short "./configure; make test", and if that goes +well, "make install" should work for most flavors of Unix. For Windows, use +one of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use +make_vms.com. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://zlib.net/ . Before reporting a problem, please check this site to +verify that you have the latest version of zlib; otherwise get the latest +version and check whether the problem still exists or not. + +PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available at +http://marknelson.us/1997/01/01/zlib-engine/ . + +The changes made in version 1.2.8 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory contrib/ . + +zlib is available in Java using the java.util.zip package, documented at +http://java.sun.com/developer/technicalArticles/Programming/compression/ . + +A Perl interface to zlib written by Paul Marquess is available +at CPAN (Comprehensive Perl Archive Network) sites, including +http://search.cpan.org/~pmqs/IO-Compress-Zlib/ . + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://docs.python.org/library/zlib.html . + +zlib is built into tcl: http://wiki.tcl.tk/4610 . + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS or BEOS. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate and + zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; they + are too numerous to cite here. + +Copyright notice: + + (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* receiving +lengthy legal documents to sign. The sources are provided for free but without +warranty of any kind. The library has been entirely written by Jean-loup +Gailly and Mark Adler; it does not include third-party code. + +If you redistribute modified sources, we would appreciate that you include in +the file ChangeLog history information documenting your changes. Please read +the FAQ for more information on the distribution of modified source versions. diff --git a/zlib/adler32.c b/zlib/adler32.c new file mode 100644 index 0000000..a868f07 --- /dev/null +++ b/zlib/adler32.c @@ -0,0 +1,179 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); + +#define BASE 65521 /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ +#ifdef NO_DIVIDE +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ + do { \ + CHOP(a); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD(a) \ + do { \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD28(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + + /* the derivation of this formula is left as an exercise for the reader */ + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/zlib/compress.c b/zlib/compress.c new file mode 100644 index 0000000..6e97626 --- /dev/null +++ b/zlib/compress.c @@ -0,0 +1,80 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; +} diff --git a/zlib/crc32.c b/zlib/crc32.c new file mode 100644 index 0000000..979a719 --- /dev/null +++ b/zlib/crc32.c @@ -0,0 +1,425 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif +#ifdef BYFOUR + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local z_crc_t FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const z_crc_t FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + z_crc_t c; + int n, k; + z_crc_t poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (z_crc_t)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = ZSWAP32(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = ZSWAP32(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const z_crc_t FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const z_crc_t FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const z_crc_t FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const z_crc_t FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + z_crc_t endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = (z_crc_t)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = ZSWAP32((z_crc_t)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(ZSWAP32(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/zlib/crc32.h b/zlib/crc32.h new file mode 100644 index 0000000..9e0c778 --- /dev/null +++ b/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/zlib/deflate.c b/zlib/deflate.c new file mode 100644 index 0000000..6969577 --- /dev/null +++ b/zlib/deflate.c @@ -0,0 +1,1967 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://tools.ietf.org/html/rfc1951 + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; + s = strm->state; + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) + strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; + } + + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); + } + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateResetKeep (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePending (strm, pending, bits) + unsigned *pending; + int *bits; + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + deflate_state *s; + int put; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + if (err == Z_BUF_ERROR && s->pending == 0) + err = Z_OK; + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len; + deflate_state *s = strm->state; + + _tr_flush_bits(s); + len = s->pending; + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if ((long)s->strstart > s->block_start) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s->lookahead <= MAX_MATCH) { + fill_window(s); + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} diff --git a/zlib/deflate.h b/zlib/deflate.h new file mode 100644 index 0000000..ce0299e --- /dev/null +++ b/zlib/deflate.h @@ -0,0 +1,346 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2012 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* can only be DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + uInt insert; /* bytes at end of window left to insert */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/zlib/gzclose.c b/zlib/gzclose.c new file mode 100644 index 0000000..caeb99a --- /dev/null +++ b/zlib/gzclose.c @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/zlib/gzguts.h b/zlib/gzguts.h new file mode 100644 index 0000000..d87659d --- /dev/null +++ b/zlib/gzguts.h @@ -0,0 +1,209 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99, yet still not supported by + Microsoft more than a decade later!), _snprintf does not guarantee null + termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/zlib/gzlib.c b/zlib/gzlib.c new file mode 100644 index 0000000..fae202e --- /dev/null +++ b/zlib/gzlib.c @@ -0,0 +1,634 @@ +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#if defined(_WIN32) && !defined(__BORLANDC__) +# define LSEEK _lseeki64 +#else +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const void *, int, const char *)); + +#if defined UNDER_CE + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZLIB_INTERNAL *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + state->x.have = 0; /* no output data available */ + if (state->mode == GZ_READ) { /* for reading ... */ + state->eof = 0; /* not at end of file */ + state->past = 0; /* have not read past end yet */ + state->how = LOOK; /* look for gzip header */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->x.pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const void *path; + int fd; + const char *mode; +{ + gz_statep state; + size_t len; + int oflag; +#ifdef O_CLOEXEC + int cloexec = 0; +#endif +#ifdef O_EXCL + int exclusive = 0; +#endif + + /* check input */ + if (path == NULL) + return NULL; + + /* allocate gzFile structure to return */ + state = (gz_statep)malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + state->direct = 0; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; +#ifdef O_CLOEXEC + case 'e': + cloexec = 1; + break; +#endif +#ifdef O_EXCL + case 'x': + exclusive = 1; + break; +#endif + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + break; + case 'T': + state->direct = 1; + break; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* can't force transparent read */ + if (state->mode == GZ_READ) { + if (state->direct) { + free(state); + return NULL; + } + state->direct = 1; /* for empty file */ + } + + /* save the path name for error messages */ +#ifdef _WIN32 + if (fd == -2) { + len = wcstombs(NULL, path, 0); + if (len == (size_t)-1) + len = 0; + } + else +#endif + len = strlen((const char *)path); + state->path = (char *)malloc(len + 1); + if (state->path == NULL) { + free(state); + return NULL; + } +#ifdef _WIN32 + if (fd == -2) + if (len) + wcstombs(state->path, path, len + 1); + else + *(state->path) = 0; + else +#endif +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(state->path, len + 1, "%s", (const char *)path); +#else + strcpy(state->path, path); +#endif + + /* compute the flags for open() */ + oflag = +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif +#ifdef O_CLOEXEC + (cloexec ? O_CLOEXEC : 0) | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | +#ifdef O_EXCL + (exclusive ? O_EXCL : 0) | +#endif + (state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))); + + /* open the file with the appropriate flags (or just use fd) */ + state->fd = fd > -1 ? fd : ( +#ifdef _WIN32 + fd == -2 ? _wopen(path, oflag, 0666) : +#endif + open((const char *)path, oflag, 0666)); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) + state->mode = GZ_WRITE; /* simplify later checks */ + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(path, 7 + 3 * sizeof(int), "", fd); /* for debugging */ +#else + sprintf(path, "", fd); /* for debugging */ +#endif + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +#ifdef _WIN32 +gzFile ZEXPORT gzopen_w(path, mode) + const wchar_t *path; + const char *mode; +{ + return gz_open(path, -2, mode); +} +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if (size < 2) + size = 2; /* need two bytes to check magic header */ + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->x.pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->x.pos + offset >= 0) { + ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR); + if (ret == -1) + return -1; + state->x.have = 0; + state->eof = 0; + state->past = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->x.pos += offset; + return state->x.pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->x.pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? + (unsigned)offset : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->x.pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->x.pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? state->past : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->err == Z_MEM_ERROR ? "out of memory" : + (state->msg == NULL ? "" : state->msg); +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) { + state->eof = 0; + state->past = 0; + } + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */ + if (err != Z_OK && err != Z_BUF_ERROR) + state->x.have = 0; + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, return literal string when requested */ + if (err == Z_MEM_ERROR) + return; + + /* construct error message with path */ + if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) == + NULL) { + state->err = Z_MEM_ERROR; + return; + } +#if !defined(NO_snprintf) && !defined(NO_vsnprintf) + snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, + "%s%s%s", state->path, ": ", msg); +#else + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); +#endif + return; +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZLIB_INTERNAL gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/zlib/gzread.c b/zlib/gzread.c new file mode 100644 index 0000000..bf4538e --- /dev/null +++ b/zlib/gzread.c @@ -0,0 +1,594 @@ +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_look OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_fetch OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, len - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + If strm->avail_in != 0, then the current data is moved to the beginning of + the input buffer, and then the remainder of the buffer is loaded with the + available data from the input file. */ +local int gz_avail(state) + gz_statep state; +{ + unsigned got; + z_streamp strm = &(state->strm); + + if (state->err != Z_OK && state->err != Z_BUF_ERROR) + return -1; + if (state->eof == 0) { + if (strm->avail_in) { /* copy what's there to the start */ + unsigned char *p = state->in; + unsigned const char *q = strm->next_in; + unsigned n = strm->avail_in; + do { + *p++ = *q++; + } while (--n); + } + if (gz_load(state, state->in + strm->avail_in, + state->size - strm->avail_in, &got) == -1) + return -1; + strm->avail_in += got; + strm->next_in = state->in; + } + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->x.have must be 0. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression. If direct copying, then leftover input + data from the input buffer will be copied to the output buffer. In that + case, all further file reads will be directly to either the output buffer or + a user buffer. If decompressing, the inflate state will be initialized. + gz_look() will return 0 on success or -1 on failure. */ +local int gz_look(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = (unsigned char *)malloc(state->want); + state->out = (unsigned char *)malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), 15 + 16) != Z_OK) { /* gunzip */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get at least the magic bytes in the input buffer */ + if (strm->avail_in < 2) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for gzip magic bytes -- if there, do gzip decoding (note: there is + a logical dilemma here when considering the case of a partially written + gzip file, to wit, if a single 31 byte is written, then we cannot tell + whether this is a single-byte file, or just a partially written gzip + file -- for here we assume that if a gzip file is being written, then + the header will be written in a single operation, so that reading a + single byte is sufficient indication that it is not a gzip file) */ + if (strm->avail_in > 1 && + strm->next_in[0] == 31 && strm->next_in[1] == 139) { + inflateReset(strm); + state->how = GZIP; + state->direct = 0; + return 0; + } + + /* no gzip header -- if we were decoding gzip before, then this is trailing + garbage. Ignore the trailing garbage and finish. */ + if (state->direct == 0) { + strm->avail_in = 0; + state->eof = 1; + state->x.have = 0; + return 0; + } + + /* doing raw i/o, copy any leftover input to output -- this assumes that + the output buffer is larger than the input buffer, which also assures + space for gzungetc() */ + state->x.next = state->out; + if (strm->avail_in) { + memcpy(state->x.next, strm->next_in, strm->avail_in); + state->x.have = strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + On return, state->x.have and state->x.next point to the just decompressed + data. If the gzip stream completes, state->how is reset to LOOK to look for + the next gzip stream or raw data, once state->x.have is depleted. Returns 0 + on success, -1 on failure. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret = Z_OK; + unsigned had; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_BUF_ERROR, "unexpected end of file"); + break; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output */ + state->x.have = had - strm->avail_out; + state->x.next = strm->next_out - state->x.have; + + /* if the gzip stream completed successfully, look for another */ + if (ret == Z_STREAM_END) + state->how = LOOK; + + /* good decompression */ + return 0; +} + +/* Fetch data and put it in the output buffer. Assumes state->x.have is 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for to determine whether to copy or decompress. Returns -1 on error, + otherwise 0. gz_fetch() will leave state->how as COPY or GZIP unless the + end of the input file has been reached and all data has been processed. */ +local int gz_fetch(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + do { + switch(state->how) { + case LOOK: /* -> LOOK, COPY (only if never GZIP), or GZIP */ + if (gz_look(state) == -1) + return -1; + if (state->how == LOOK) + return 0; + break; + case COPY: /* -> COPY */ + if (gz_load(state, state->out, state->size << 1, &(state->x.have)) + == -1) + return -1; + state->x.next = state->out; + return 0; + case GZIP: /* -> GZIP or LOOK (if end of gzip stream) */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + } while (state->x.have == 0 && (!state->eof || strm->avail_in)); + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->x.have) { + n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > len ? + (unsigned)len : state->x.have; + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + unsigned got, n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return -1; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (state->x.have) { + n = state->x.have > len ? len : state->x.have; + memcpy(buf, state->x.next, n); + state->x.next += n; + state->x.have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && strm->avail_in == 0) { + state->past = 1; /* tried to read past end */ + break; + } + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || len < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_fetch(state) == -1) + return -1; + continue; /* no progress yet -- go back to copy above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, (unsigned char *)buf, len, &n) == -1) + return -1; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + strm->avail_out = len; + strm->next_out = (unsigned char *)buf; + if (gz_decomp(state) == -1) + return -1; + n = state->x.have; + state->x.have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->x.pos += n; + } while (len); + + /* return number of bytes read into user buffer (will fit in int) */ + return (int)got; +} + +/* -- see zlib.h -- */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +#else +# undef gzgetc +#endif +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->x.have) { + state->x.have--; + state->x.pos++; + return *(state->x.next)++; + } + + /* nothing there -- try gzread() */ + ret = gzread(file, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +int ZEXPORT gzgetc_(file) +gzFile file; +{ + return gzgetc(file); +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->x.have == 0) { + state->x.have = 1; + state->x.next = state->out + (state->size << 1) - 1; + state->x.next[0] = c; + state->x.pos--; + state->past = 0; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->x.have == (state->size << 1)) { + gz_error(state, Z_DATA_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->x.next == state->out) { + unsigned char *src = state->out + state->x.have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->x.next = dest; + } + state->x.have++; + state->x.next--; + state->x.next[0] = c; + state->x.pos--; + state->past = 0; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no (serious) error */ + if (state->mode != GZ_READ || + (state->err != Z_OK && state->err != Z_BUF_ERROR)) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->x.have == 0 && gz_fetch(state) == -1) + return NULL; /* error */ + if (state->x.have == 0) { /* end of file */ + state->past = 1; /* read past end */ + break; /* return what we have */ + } + + /* look for end-of-line in current output buffer */ + n = state->x.have > left ? left : state->x.have; + eol = (unsigned char *)memchr(state->x.next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->x.next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->x.next, n); + state->x.have -= n; + state->x.next += n; + state->x.pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* return terminated string, or if nothing, end of file */ + if (buf == str) + return NULL; + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0) + (void)gz_look(state); + + /* return 1 if transparent, 0 if processing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret, err; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + err = state->err == Z_BUF_ERROR ? Z_BUF_ERROR : Z_OK; + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : err; +} diff --git a/zlib/gzwrite.c b/zlib/gzwrite.c new file mode 100644 index 0000000..aa767fb --- /dev/null +++ b/zlib/gzwrite.c @@ -0,0 +1,577 @@ +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on failure or 0 on success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input buffer */ + state->in = (unsigned char *)malloc(state->want); + if (state->in == NULL) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* only need output buffer and deflate state if compressing */ + if (!state->direct) { + /* allocate output buffer */ + state->out = (unsigned char *)malloc(state->want); + if (state->out == NULL) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy); + if (ret != Z_OK) { + free(state->out); + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer if compressing */ + if (!state->direct) { + strm->avail_out = state->size; + strm->next_out = state->out; + state->x.next = strm->next_out; + } + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file, otherwise 0. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. If gz->direct + is true, then simply write to the output file without compressing, and + ignore flush. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, got; + unsigned have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* write directly if requested */ + if (state->direct) { + got = write(state->fd, strm->next_in, strm->avail_in); + if (got < 0 || (unsigned)got != strm->avail_in) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + strm->avail_in = 0; + return 0; + } + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = (unsigned)(strm->next_out - state->x.next); + if (have && ((got = write(state->fd, state->x.next, have)) < 0 || + (unsigned)got != have)) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->x.next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on error, 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->x.pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + unsigned put = len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_DATA_ERROR, "requested length does not fit in int"); + return 0; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + unsigned have, copy; + + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + copy = state->size - have; + if (copy > len) + copy = len; + memcpy(state->in + have, buf, copy); + strm->avail_in += copy; + state->x.pos += copy; + buf = (const char *)buf + copy; + len -= copy; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (z_const Bytef *)buf; + state->x.pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned have; + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + have = (unsigned)((strm->next_in + strm->avail_in) - state->in); + if (have < state->size) { + state->in[have] = c; + strm->avail_in++; + state->x.pos++; + return c & 0xff; + } + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = c; + if (gzwrite(file, buf, 1) != 1) + return -1; + return c & 0xff; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + unsigned len; + + /* write string */ + len = (unsigned)strlen(str); + ret = gzwrite(file, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +#include + +/* -- see zlib.h -- */ +int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va) +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf((char *)(state->in), format, va); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = vsprintf((char *)(state->in), format, va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf((char *)(state->in), size, format, va); + len = strlen((char *)(state->in)); +# else + len = vsnprintf((char *)(state->in), size, format, va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->x.pos += len; + return len; +} + +int ZEXPORTVA gzprintf(gzFile file, const char *format, ...) +{ + va_list va; + int ret; + + va_start(va, format); + ret = gzvprintf(file, format, va); + va_end(va); + return ret; +} + +#else /* !STDC && !Z_HAVE_STDARG_H */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that can really pass pointer in ints */ + if (sizeof(int) != sizeof(void *)) + return 0; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = sprintf((char *)(state->in), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen((char *)(state->in)); +# else + len = snprintf((char *)(state->in), size, format, a1, a2, a3, a4, a5, a6, + a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->x.pos += len; + return len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* compress remaining data with requested flush */ + gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = Z_OK; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + ret = state->err; + } + + /* flush, free memory, and close file */ + if (gz_comp(state, Z_FINISH) == -1) + ret = state->err; + if (state->size) { + if (!state->direct) { + (void)deflateEnd(&(state->strm)); + free(state->out); + } + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + if (close(state->fd) == -1) + ret = Z_ERRNO; + free(state); + return ret; +} diff --git a/zlib/infback.c b/zlib/infback.c new file mode 100644 index 0000000..f3833c2 --- /dev/null +++ b/zlib/infback.c @@ -0,0 +1,640 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/zlib/inffast.c b/zlib/inffast.c new file mode 100644 index 0000000..bda59ce --- /dev/null +++ b/zlib/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/zlib/inffast.h b/zlib/inffast.h new file mode 100644 index 0000000..e5c1aa4 --- /dev/null +++ b/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/zlib/inffixed.h b/zlib/inffixed.h new file mode 100644 index 0000000..d628327 --- /dev/null +++ b/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/zlib/inflate.c b/zlib/inflate.c new file mode 100644 index 0000000..870f89b --- /dev/null +++ b/zlib/inflate.c @@ -0,0 +1,1512 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateResetKeep(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, end, copy) +z_streamp strm; +const Bytef *end; +unsigned copy; +{ + struct inflate_state FAR *state; + unsigned dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state->wsize) { + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, end - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = ZSWAP32(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (const code FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + ZSWAP32(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long dictid; + int ret; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary identifier */ + if (state->mode == DICT) { + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { + state->mode = MEM; + return Z_MEM_ERROR; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +const unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/zlib/inflate.h b/zlib/inflate.h new file mode 100644 index 0000000..95f4986 --- /dev/null +++ b/zlib/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/zlib/inftrees.c b/zlib/inftrees.c new file mode 100644 index 0000000..44d89cf --- /dev/null +++ b/zlib/inftrees.c @@ -0,0 +1,306 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/zlib/inftrees.h b/zlib/inftrees.h new file mode 100644 index 0000000..baa53a0 --- /dev/null +++ b/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/zlib/trees.c b/zlib/trees.c new file mode 100644 index 0000000..1fd7759 --- /dev/null +++ b/zlib/trees.c @@ -0,0 +1,1226 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2012 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {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,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {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}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(s) + deflate_state *s; +{ + bi_flush(s); +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + const ct_data *ltree; /* literal tree */ + const ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/zlib/trees.h b/zlib/trees.h new file mode 100644 index 0000000..d35639d --- /dev/null +++ b/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 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, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +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, 0 +}; + +local const int base_dist[D_CODES] = { + 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 +}; + diff --git a/zlib/uncompr.c b/zlib/uncompr.c new file mode 100644 index 0000000..242e949 --- /dev/null +++ b/zlib/uncompr.c @@ -0,0 +1,59 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/prebuilt/zconf.h b/zlib/zconf.h similarity index 54% rename from prebuilt/zconf.h rename to zlib/zconf.h index e3b0c96..9987a77 100644 --- a/prebuilt/zconf.h +++ b/zlib/zconf.h @@ -1,332 +1,511 @@ -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2005 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#ifndef ZCONF_H -#define ZCONF_H - -/* - * If you *really* need a unique prefix for all types and library functions, - * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. - */ -#ifdef Z_PREFIX -# define deflateInit_ z_deflateInit_ -# define deflate z_deflate -# define deflateEnd z_deflateEnd -# define inflateInit_ z_inflateInit_ -# define inflate z_inflate -# define inflateEnd z_inflateEnd -# define deflateInit2_ z_deflateInit2_ -# define deflateSetDictionary z_deflateSetDictionary -# define deflateCopy z_deflateCopy -# define deflateReset z_deflateReset -# define deflateParams z_deflateParams -# define deflateBound z_deflateBound -# define deflatePrime z_deflatePrime -# define inflateInit2_ z_inflateInit2_ -# define inflateSetDictionary z_inflateSetDictionary -# define inflateSync z_inflateSync -# define inflateSyncPoint z_inflateSyncPoint -# define inflateCopy z_inflateCopy -# define inflateReset z_inflateReset -# define inflateBack z_inflateBack -# define inflateBackEnd z_inflateBackEnd -# define compress z_compress -# define compress2 z_compress2 -# define compressBound z_compressBound -# define uncompress z_uncompress -# define adler32 z_adler32 -# define crc32 z_crc32 -# define get_crc_table z_get_crc_table -# define zError z_zError - -# define alloc_func z_alloc_func -# define free_func z_free_func -# define in_func z_in_func -# define out_func z_out_func -# define Byte z_Byte -# define uInt z_uInt -# define uLong z_uLong -# define Bytef z_Bytef -# define charf z_charf -# define intf z_intf -# define uIntf z_uIntf -# define uLongf z_uLongf -# define voidpf z_voidpf -# define voidp z_voidp -#endif - -#if defined(__MSDOS__) && !defined(MSDOS) -# define MSDOS -#endif -#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) -# define OS2 -#endif -#if defined(_WINDOWS) && !defined(WINDOWS) -# define WINDOWS -#endif -#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) -# ifndef WIN32 -# define WIN32 -# endif -#endif -#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) -# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) -# ifndef SYS16BIT -# define SYS16BIT -# endif -# endif -#endif - -/* - * Compile with -DMAXSEG_64K if the alloc function cannot allocate more - * than 64k bytes at a time (needed on systems with 16-bit int). - */ -#ifdef SYS16BIT -# define MAXSEG_64K -#endif -#ifdef MSDOS -# define UNALIGNED_OK -#endif - -#ifdef __STDC_VERSION__ -# ifndef STDC -# define STDC -# endif -# if __STDC_VERSION__ >= 199901L -# ifndef STDC99 -# define STDC99 -# endif -# endif -#endif -#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) -# define STDC -#endif -#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) -# define STDC -#endif -#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) -# define STDC -#endif -#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) -# define STDC -#endif - -#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ -# define STDC -#endif - -#ifndef STDC -# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ -# define const /* note: need a more gentle solution here */ -# endif -#endif - -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) -# define NO_DUMMY_DECL -#endif - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -# ifdef STDC -# define OF(args) args -# else -# define OF(args) () -# endif -#endif - -/* The following definitions for FAR are needed only for MSDOS mixed - * model programming (small or medium model with some far allocations). - * This was tested only with MSC; for other MSDOS compilers you may have - * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, - * just define FAR to be empty. - */ -#ifdef SYS16BIT -# if defined(M_I86SM) || defined(M_I86MM) - /* MSC small or medium model */ -# define SMALL_MEDIUM -# ifdef _MSC_VER -# define FAR _far -# else -# define FAR far -# endif -# endif -# if (defined(__SMALL__) || defined(__MEDIUM__)) - /* Turbo C small or medium model */ -# define SMALL_MEDIUM -# ifdef __BORLANDC__ -# define FAR _far -# else -# define FAR far -# endif -# endif -#endif - -#if defined(WINDOWS) || defined(WIN32) - /* If building or using zlib as a DLL, define ZLIB_DLL. - * This is not mandatory, but it offers a little performance increase. - */ -# ifdef ZLIB_DLL -# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) -# ifdef ZLIB_INTERNAL -# define ZEXTERN extern __declspec(dllexport) -# else -# define ZEXTERN extern __declspec(dllimport) -# endif -# endif -# endif /* ZLIB_DLL */ - /* If building or using zlib with the WINAPI/WINAPIV calling convention, - * define ZLIB_WINAPI. - * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. - */ -# ifdef ZLIB_WINAPI -# ifdef FAR -# undef FAR -# endif -# include - /* No need for _export, use ZLIB.DEF instead. */ - /* For complete Windows compatibility, use WINAPI, not __stdcall. */ -# define ZEXPORT WINAPI -# ifdef WIN32 -# define ZEXPORTVA WINAPIV -# else -# define ZEXPORTVA FAR CDECL -# endif -# endif -#endif - -#if defined (__BEOS__) -# ifdef ZLIB_DLL -# ifdef ZLIB_INTERNAL -# define ZEXPORT __declspec(dllexport) -# define ZEXPORTVA __declspec(dllexport) -# else -# define ZEXPORT __declspec(dllimport) -# define ZEXPORTVA __declspec(dllimport) -# endif -# endif -#endif - -#ifndef ZEXTERN -# define ZEXTERN extern -#endif -#ifndef ZEXPORT -# define ZEXPORT -#endif -#ifndef ZEXPORTVA -# define ZEXPORTVA -#endif - -#ifndef FAR -# define FAR -#endif - -#if !defined(__MACTYPES__) -typedef unsigned char Byte; /* 8 bits */ -#endif -typedef unsigned int uInt; /* 16 bits or more */ -typedef unsigned long uLong; /* 32 bits or more */ - -#ifdef SMALL_MEDIUM - /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ -# define Bytef Byte FAR -#else - typedef Byte FAR Bytef; -#endif -typedef char FAR charf; -typedef int FAR intf; -typedef uInt FAR uIntf; -typedef uLong FAR uLongf; - -#ifdef STDC - typedef void const *voidpc; - typedef void FAR *voidpf; - typedef void *voidp; -#else - typedef Byte const *voidpc; - typedef Byte FAR *voidpf; - typedef Byte *voidp; -#endif - -#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ -# include /* for off_t */ -# include /* for SEEK_* and off_t */ -# ifdef VMS -# include /* for off_t */ -# endif -# define z_off_t off_t -#endif -#ifndef SEEK_SET -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif -#ifndef z_off_t -# define z_off_t long -#endif - -#if defined(__OS400__) -# define NO_vsnprintf -#endif - -#if defined(__MVS__) -# define NO_vsnprintf -# ifdef FAR -# undef FAR -# endif -#endif - -/* MVS linker does not support external names larger than 8 bytes */ -#if defined(__MVS__) -# pragma map(deflateInit_,"DEIN") -# pragma map(deflateInit2_,"DEIN2") -# pragma map(deflateEnd,"DEEND") -# pragma map(deflateBound,"DEBND") -# pragma map(inflateInit_,"ININ") -# pragma map(inflateInit2_,"ININ2") -# pragma map(inflateEnd,"INEND") -# pragma map(inflateSync,"INSY") -# pragma map(inflateSetDictionary,"INSEDI") -# pragma map(compressBound,"CMBND") -# pragma map(inflate_table,"INTABL") -# pragma map(inflate_fast,"INFA") -# pragma map(inflate_copyright,"INCOPY") -#endif - -#endif /* ZCONF_H */ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/zlib/zlib.h b/zlib/zlib.h new file mode 100644 index 0000000..3e0c767 --- /dev/null +++ b/zlib/zlib.h @@ -0,0 +1,1768 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/zlib/zlib.vcxproj b/zlib/zlib.vcxproj new file mode 100644 index 0000000..93edd86 --- /dev/null +++ b/zlib/zlib.vcxproj @@ -0,0 +1,112 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {B66109F4-217B-43C0-86AA-EB55657E5AC0} + Win32Proj + zlib + + + + DynamicLibrary + true + v120 + Unicode + + + DynamicLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;ZLIB_EXPORTS;ZLIB_DLL;%(PreprocessorDefinitions) + + + Windows + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;ZLIB_EXPORTS;ZLIB_DLL;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/zlib/zlib.vcxproj.filters b/zlib/zlib.vcxproj.filters new file mode 100644 index 0000000..86536e9 --- /dev/null +++ b/zlib/zlib.vcxproj.filters @@ -0,0 +1,99 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/zlib/zutil.c b/zlib/zutil.c new file mode 100644 index 0000000..23d2ebe --- /dev/null +++ b/zlib/zutil.c @@ -0,0 +1,324 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +z_const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifndef Z_SOLO + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/zlib/zutil.h b/zlib/zutil.h new file mode 100644 index 0000000..24ab06b --- /dev/null +++ b/zlib/zutil.h @@ -0,0 +1,253 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */