Stroika Library 3.0d20
 
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
11#if qStroika_HasComponent_LZMA
13#endif
14#if qStroika_HasComponent_zlib
17#endif
20#include "Stroika/Foundation/Execution/CommandLine.h"
25#include "Stroika/Foundation/Streams/MemoryStream.h"
26
27using namespace std;
28
29using namespace Stroika::Foundation;
31#if qStroika_HasComponent_LZMA || qStroika_HasComponent_zlib
33#endif
34using namespace Stroika::Foundation::Execution;
35using namespace Stroika::Foundation::Streams;
36
38using Memory::BLOB;
39
40namespace {
41 struct Options_ {
42 enum class Operation {
43 eList,
44 eExtract,
45 eCreate,
46 eUpdate
47 };
48 Operation fOperation;
49 filesystem::path fArchiveFileName;
50 optional<filesystem::path> fOutputDirectory; // applies only if extract
51 optional<Sequence<String>> fFiles2Add;
52 optional<bool> fNoFailOnMissingLibrary; // for regression tests
53 };
54
55 using StandardCommandLineOptions::kHelp;
56
57 const CommandLine::Option kNoFailOnMissingO_{.fLongName = "no-fail-on-missing-library"sv,
58 .fHelpOptionText = "just warns when we fail because of missing library"sv};
59 const CommandLine::Option kListO_{.fLongName = "list"sv, .fHelpOptionText = "prints all the files in the argument archive"sv};
60 const CommandLine::Option kCreateO_{.fLongName = "create"sv, .fHelpOptionText = "creates the argument ARCHIVE-FILE and adds the argument FILE(s) to it"sv};
61 const CommandLine::Option kExtractO_{
62 .fLongName = "extract"sv,
63 .fHelpOptionText = "extracts all the files from the argument ARCHIVE-FILE and to the output directory specified by --ouptutDirectory "sv};
64 const CommandLine::Option kUpdateO_{.fLongName = "update"sv,
65 .fHelpOptionText = "adds to the argument ARCHIVE-FILE and adds the argument FILE(s) to it "sv};
66 const CommandLine::Option kArchiveFileO_{
67 .fRequired = true,
68 .fHelpArgName = "ARCHIVE-FILE"sv,
69 .fHelpOptionText = "ARCHIVE-FILE can be the single character - to designate stdin"sv // NYI stdin part...
70 };
71 const CommandLine::Option kOtherFilenamesO_{
72 .fRepeatable = true,
73 .fSkipFirstNArguments = 1,
74 .fHelpArgName = "FILE"sv,
75 };
76 const CommandLine::Option kOutputDirO_{.fLongName = "outputDirectory"sv, .fSupportsArgument = true, .fHelpOptionText = "(defaulting to .)"sv};
77
78 const initializer_list<CommandLine::Option> kAllOptions_{
79 kHelp, kNoFailOnMissingO_, kListO_, kCreateO_, kExtractO_, kUpdateO_, kOutputDirO_, kArchiveFileO_, kOtherFilenamesO_,
80 };
81
82 // Emits errors to stderr, and Usage, etc, if needed, and not Optional<> has_value()
83 optional<Options_> ParseOptions_ (int argc, const char* argv[])
84 {
85 Options_ result{};
86 CommandLine cmdLine{argc, argv};
87
88 try {
89 cmdLine.Validate (kAllOptions_);
90 }
91 catch (...) {
92 cerr << ToString (current_exception ()) << endl;
93 cerr << cmdLine.GenerateUsage (kAllOptions_);
94 return optional<Options_>{};
95 }
96 if (cmdLine.Has (kHelp)) {
97 cerr << cmdLine.GenerateUsage (kAllOptions_);
98 return optional<Options_>{};
99 }
100
101 if (cmdLine.Has (kListO_)) {
102 result.fOperation = Options_::Operation::eList;
103 }
104 else if (cmdLine.Has (kCreateO_)) {
105 result.fOperation = Options_::Operation::eCreate;
106 }
107 else if (cmdLine.Has (kExtractO_)) {
108 result.fOperation = Options_::Operation::eExtract;
109 }
110 else if (cmdLine.Has (kUpdateO_)) {
111 result.fOperation = Options_::Operation::eUpdate;
112 }
113 else {
114 cerr << "Missing operation" << endl;
115 cerr << cmdLine.GenerateUsage (kAllOptions_);
116 return optional<Options_>{};
117 }
118 result.fArchiveFileName = Memory::ValueOf (cmdLine.GetArgument (kArchiveFileO_)).As<filesystem::path> ();
119 result.fFiles2Add = cmdLine.GetArguments (kOtherFilenamesO_);
120 if (auto o = cmdLine.GetArgument (kOutputDirO_)) {
121 result.fOutputDirectory = o->As<filesystem::path> ();
122 }
123 // @todo add more.. - update support etc...
124 result.fNoFailOnMissingLibrary = cmdLine.Has (kNoFailOnMissingO_);
125 return result;
126 }
127}
128
129namespace {
130 DataExchange::Archive::Reader::Ptr OpenArchive_ (const filesystem::path& archiveName)
131 {
132// @todo - must support other formats, have a registry, and autodetect
133#if qStroika_HasComponent_LZMA
134 if (String{archiveName}.EndsWith (".7z"sv, Characters::eCaseInsensitive)) {
135 return Archive::_7z::Reader::New (IO::FileSystem::FileInputStream::New (archiveName));
136 }
137#endif
138#if qStroika_HasComponent_zlib
139 if (String{archiveName}.EndsWith (".zip"sv, Characters::eCaseInsensitive)) {
140 return Archive::Zip::Reader::New (IO::FileSystem::FileInputStream::New (archiveName));
141 }
142#endif
143 Throw (Exception{"Unrecognized format"sv});
144 }
145}
146
147namespace {
148 DataExchange::Archive::Writer::Ptr CreateWritingArchive_ (const filesystem::path& archiveName)
149 {
150#if qStroika_HasComponent_zlib
151 // for now require just zip
152 if (String{archiveName}.EndsWith (".zip"sv, Characters::eCaseInsensitive)) {
153 return DataExchange::Archive::Zip::Writer::New (IO::FileSystem::FileOutputStream::New (archiveName));
154 }
155#endif
156 Throw (Exception{"Unrecognized format"sv});
157 }
158}
159
160namespace {
161 void ListArchive_ (const filesystem::path& archiveName)
162 {
163 for (String i : OpenArchive_ (archiveName).GetContainedFiles ()) {
164 cout << i << endl;
165 }
166 }
167 void ExtractArchive_ (const filesystem::path& archiveName, const filesystem::path& toDirectory)
168 {
169 Debug::TraceContextBumper ctx{"ExtractArchive_",
170 Stroika_Foundation_Debug_OptionalizeTraceArgs ("(archiveName={}, toDir={})"_f, archiveName, toDirectory)};
171 DataExchange::Archive::Reader::Ptr archive{OpenArchive_ (archiveName)};
172 for (String i : archive.GetContainedFiles ()) {
173 String srcFileName = i;
174 filesystem::path trgFileName = toDirectory / srcFileName.As<filesystem::path> ();
175 BLOB b = archive.GetData (srcFileName);
176 create_directories (trgFileName.parent_path ());
177 IO::FileSystem::FileOutputStream::Ptr ostream = IO::FileSystem::FileOutputStream::New (trgFileName);
178 ostream.Write (b);
179 }
180 }
181 void CreateArchive_ (const filesystem::path& archiveName, const Sequence<String>& files2Add)
182 {
183 Debug::TraceContextBumper ctx{"CreateArchive_",
184 Stroika_Foundation_Debug_OptionalizeTraceArgs ("(archiveName={}, files2Add={})"_f, archiveName, files2Add)};
185 DataExchange::Archive::Writer::Ptr archive = CreateWritingArchive_ (archiveName);
186 for (String f2a : files2Add) {
187 archive.Add (f2a, IO::FileSystem::FileInputStream::New (f2a.As<filesystem::path> ()).ReadAll ());
188 }
189 }
190}
191
192int main (int argc, const char* argv[])
193{
195 if (optional<Options_> o = ParseOptions_ (argc, argv)) {
196 try {
197 switch (o->fOperation) {
198 case Options_::Operation::eList:
199 ListArchive_ (o->fArchiveFileName);
200 break;
201 case Options_::Operation::eExtract:
202 ExtractArchive_ (o->fArchiveFileName, o->fOutputDirectory.value_or ("."sv));
203 break;
204 case Options_::Operation::eCreate:
205 CreateArchive_ (o->fArchiveFileName, o->fFiles2Add.value_or (Sequence<String>{}));
206 break;
207 default:
208 cerr << "that option NYI" << endl;
209 break;
210 }
211 }
212 catch (const InvalidCommandLineArgument&) {
213 cerr << "Exception: " << Characters::ToString (current_exception ()) << endl;
214 return EXIT_FAILURE;
215 }
216 catch (...) {
217 String exceptMsg = Characters::ToString (current_exception ());
218 cerr << "Exception: " << exceptMsg << " - terminating..." << endl;
219 if (o->fNoFailOnMissingLibrary.value_or (false)) {
220#if !qStroika_HasComponent_LZMA || !qStroika_HasComponent_zlib
221 if (exceptMsg.Contains ("Unrecognized format"sv)) {
222 return EXIT_SUCCESS;
223 }
224#endif
225 }
226 return EXIT_FAILURE;
227 }
228 }
229 else {
230 return EXIT_FAILURE;
231 }
232 return EXIT_SUCCESS;
233}
#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 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(const 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
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:465
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
STL namespace.