Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
ArchiveUtility.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <iostream>
7
12#include "Stroika/Foundation/Execution/CommandLine.h"
13#if qStroika_HasComponent_LZMA
15#endif
16#if qStroika_HasComponent_zlib
18#endif
23#include "Stroika/Foundation/Streams/MemoryStream.h"
24
25using namespace std;
26
27using namespace Stroika::Foundation;
29#if qStroika_HasComponent_LZMA || qStroika_HasComponent_zlib
31#endif
32using namespace Stroika::Foundation::Streams;
33
35using Memory::BLOB;
36
37namespace {
38 struct Options_ {
39 enum class Operation {
40 eList,
41 eExtract,
42 eCreate,
43 eUpdate
44 };
45 Operation fOperation;
46 filesystem::path fArchiveFileName;
47 optional<filesystem::path> fOutputDirectory; // applies only if extract
48 optional<Sequence<String>> fFiles2Add;
49 optional<bool> fNoFailOnMissingLibrary; // for regression tests
50 };
51
52 using Execution::StandardCommandLineOptions::kHelp;
53
54 const Execution::CommandLine::Option kNoFailOnMissingO_{.fLongName = "no-fail-on-missing-library"sv,
55 .fHelpOptionText = "just warns when we fail because of missing library"sv};
56 const Execution::CommandLine::Option kListO_{.fLongName = "list"sv, .fHelpOptionText = "prints all the files in the argument archive"sv};
57 const Execution::CommandLine::Option kCreateO_{.fLongName = "create"sv,
58 .fHelpOptionText = "creates the argument ARCHIVE-FILE and adds the argument FILE(s) to it"sv};
59 const Execution::CommandLine::Option kExtractO_{
60 .fLongName = "extract"sv,
61 .fHelpOptionText = "extracts all the files from the argument ARCHIVE-FILE and to the output directory specified by --ouptutDirectory "sv};
62 const Execution::CommandLine::Option kUpdateO_{.fLongName = "update"sv,
63 .fHelpOptionText = "adds to the argument ARCHIVE-FILE and adds the argument FILE(s) to it "sv};
64 const Execution::CommandLine::Option kArchiveFileO_{
65 .fSupportsArgument = true,
66 .fRequired = true,
67 .fHelpArgName = "ARCHIVE-FILE"sv,
68 .fHelpOptionText = "ARCHIVE-FILE can be the single character - to designate stdin"sv // NYI stdin part...
69 };
70 const Execution::CommandLine::Option kOtherFilenamesO_{
71 .fSupportsArgument = true,
72 .fRepeatable = true,
73 .fHelpArgName = "FILE"sv,
74 };
75 const Execution::CommandLine::Option kOutputDirO_{.fLongName = "outputDirectory"sv, .fSupportsArgument = true, .fHelpOptionText = "(defaulting to .)"sv};
76
77 const initializer_list<Execution::CommandLine::Option> kAllOptions_{
78 kHelp, kNoFailOnMissingO_, kListO_, kCreateO_, kExtractO_, kUpdateO_, kOutputDirO_, kArchiveFileO_, kOtherFilenamesO_,
79 };
80
81 // Emits errors to stderr, and Usage, etc, if needed, and not Optional<> has_value()
82 optional<Options_> ParseOptions_ (int argc, const char* argv[])
83 {
84 Options_ result{};
85 Execution::CommandLine cmdLine{argc, argv};
86
87 try {
88 cmdLine.Validate (kAllOptions_);
89 }
90 catch (...) {
91 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString ();
92 return optional<Options_>{};
93 }
94 if (cmdLine.Has (kHelp)) {
95 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString ();
96 return optional<Options_>{};
97 }
98
99 if (cmdLine.Has (kListO_)) {
100 result.fOperation = Options_::Operation::eList;
101 }
102 else if (cmdLine.Has (kCreateO_)) {
103 result.fOperation = Options_::Operation::eCreate;
104 }
105 else if (cmdLine.Has (kExtractO_)) {
106 result.fOperation = Options_::Operation::eExtract;
107 }
108 else if (cmdLine.Has (kUpdateO_)) {
109 result.fOperation = Options_::Operation::eUpdate;
110 }
111 else {
112 cerr << "Missing operation" << endl;
113 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString ();
114 return optional<Options_>{};
115 }
116 result.fArchiveFileName = Memory::ValueOf (cmdLine.GetArgument (kArchiveFileO_)).As<filesystem::path> ();
117 if (auto o = cmdLine.GetArgument (kOutputDirO_)) {
118 result.fOutputDirectory = o->As<filesystem::path> ();
119 }
120 // @todo add more.. - files etc
121 result.fNoFailOnMissingLibrary = cmdLine.Has (kNoFailOnMissingO_);
122 return result;
123 }
124}
125
126namespace {
127 DataExchange::Archive::Reader OpenArchive_ (const filesystem::path& archiveName)
128 {
129// @todo - must support other formats, have a registry, and autodetect
130#if qStroika_HasComponent_LZMA
131 if (String{archiveName}.EndsWith (".7z"sv, Characters::eCaseInsensitive)) {
132 return move (Archive::_7z::Reader{IO::FileSystem::FileInputStream::New (archiveName)});
133 }
134#endif
135#if qStroika_HasComponent_zlib
136 if (String{archiveName}.EndsWith (".zip"sv, Characters::eCaseInsensitive)) {
137 return move (Archive::Zip::Reader{IO::FileSystem::FileInputStream::New (archiveName)});
138 }
139#endif
140 Execution::Throw (Execution::Exception{"Unrecognized format"sv});
141 }
142}
143
144namespace {
145 void ListArchive_ (const filesystem::path& archiveName)
146 {
147 for (String i : OpenArchive_ (archiveName).GetContainedFiles ()) {
148 cout << i.AsNarrowSDKString () << endl;
149 }
150 }
151 void ExtractArchive_ (const filesystem::path& archiveName, const filesystem::path& toDirectory)
152 {
153 Debug::TraceContextBumper ctx{"ExtractArchive_"};
154 DbgTrace ("(archiveName={}, toDir={})"_f, archiveName, toDirectory);
155 DataExchange::Archive::Reader archive{OpenArchive_ (archiveName)};
156 for (String i : archive.GetContainedFiles ()) {
157 String srcFileName = i;
158 filesystem::path trgFileName = toDirectory / srcFileName.As<filesystem::path> ();
159 //DbgTrace ("(srcFileName={}, trgFileName={})"_f, srcFileName, trgFileName);
160 BLOB b = archive.GetData (srcFileName);
161 //DbgTrace (L"IO::FileSystem::GetFileDirectory (trgFileName)=%s", IO::FileSystem::GetFileDirectory (trgFileName).c_str ());
162 create_directories (trgFileName.parent_path ());
163 IO::FileSystem::FileOutputStream::Ptr ostream = IO::FileSystem::FileOutputStream::New (trgFileName);
164 ostream.Write (b);
165 }
166 }
167}
168
169int main (int argc, const char* argv[])
170{
172 if (optional<Options_> o = ParseOptions_ (argc, argv)) {
173 try {
174 switch (o->fOperation) {
175 case Options_::Operation::eList:
176 ListArchive_ (o->fArchiveFileName);
177 break;
178 case Options_::Operation::eExtract:
179 ExtractArchive_ (o->fArchiveFileName, o->fOutputDirectory.value_or ("."sv));
180 break;
181 default:
182 cerr << "that option NYI" << endl;
183 break;
184 }
185 }
187 String exceptMsg = Characters::ToString (current_exception ());
188 cerr << "Exception: " << exceptMsg.AsNarrowSDKString () << endl;
189 return EXIT_FAILURE;
190 }
191 catch (...) {
192 String exceptMsg = Characters::ToString (current_exception ());
193 cerr << "Exception: " << exceptMsg.AsNarrowSDKString () << " - terminating..." << endl;
194 if (o->fNoFailOnMissingLibrary.value_or (false)) {
195#if !qStroika_HasComponent_LZMA || !qStroika_HasComponent_zlib
196 if (exceptMsg.Contains ("Unrecognized format"sv)) {
197 return EXIT_SUCCESS;
198 }
199#endif
200 }
201 return EXIT_FAILURE;
202 }
203 }
204 else {
205 return EXIT_FAILURE;
206 }
207 return EXIT_SUCCESS;
208}
#define DbgTrace
Definition Trace.h:309
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:270
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual bool Contains(Character c, CompareOptions co=eWithCase) const
Definition String.inl:697
nonvirtual string AsNarrowSDKString() const
Definition String.inl:834
nonvirtual bool EndsWith(const Character &c, CompareOptions co=eWithCase) const
Definition String.cpp:1088
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual void Validate(Iterable< Option > options) const
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:157
STL namespace.