Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Project: GDAL Utilities 4 : * Purpose: Command line application to build seek-optimized ZIP files 5 : * Author: Even Rouault <even dot rouault at spatialys.com> 6 : * 7 : ****************************************************************************** 8 : * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com> 9 : * 10 : * SPDX-License-Identifier: MIT 11 : ****************************************************************************/ 12 : 13 : #include "cpl_string.h" 14 : #include "gdalalgorithm.h" 15 : #include "commonutils.h" 16 : #include "gdalargumentparser.h" 17 : 18 : #include <cassert> 19 : 20 : /************************************************************************/ 21 : /* main() */ 22 : /************************************************************************/ 23 : 24 11 : MAIN_START(nArgc, papszArgv) 25 : { 26 11 : EarlySetConfigOptions(nArgc, papszArgv); 27 11 : nArgc = GDALGeneralCmdLineProcessor(nArgc, &papszArgv, 0); 28 21 : CPLStringList aosArgv; 29 11 : aosArgv.Assign(papszArgv, /* bTakeOwnership= */ true); 30 11 : if (nArgc < 1) 31 1 : std::exit(-nArgc); 32 : 33 30 : GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true); 34 : 35 10 : argParser.add_description(_("Generate a seek-optimized ZIP (SOZip) file.")); 36 : 37 : argParser.add_epilog( 38 10 : _("For more details, consult https://gdal.org/programs/sozip.html")); 39 : 40 20 : std::string osZipFilename; 41 10 : argParser.add_argument("zip_filename") 42 20 : .metavar("<zip_filename>") 43 10 : .store_into(osZipFilename) 44 10 : .help(_("ZIP filename.")); 45 : 46 10 : bool bRecurse = false; 47 10 : argParser.add_argument("-r", "--recurse-paths") 48 10 : .store_into(bRecurse) 49 : .help(_("Travels the directory structure of the specified directories " 50 10 : "recursively.")); 51 : 52 10 : bool bOverwrite = false; 53 : { 54 10 : auto &group = argParser.add_mutually_exclusive_group(); 55 10 : group.add_argument("-g", "--grow") 56 10 : .flag() // Default mode. Nothing to do 57 : .help( 58 : _("Grow an existing zip file with the content of the specified " 59 10 : "filename(s). Default mode.")); 60 10 : group.add_argument("--overwrite") 61 10 : .store_into(bOverwrite) 62 10 : .help(_("Overwrite the target zip file if it already exists.")); 63 : } 64 : 65 10 : bool bList = false; 66 10 : bool bValidate = false; 67 20 : std::string osOptimizeFrom; 68 20 : std::vector<std::string> aosFiles; 69 : { 70 10 : auto &group = argParser.add_mutually_exclusive_group(); 71 10 : group.add_argument("-l", "--list") 72 10 : .store_into(bList) 73 10 : .help(_("List the files contained in the zip file.")); 74 10 : group.add_argument("--validate") 75 10 : .store_into(bValidate) 76 10 : .help(_("Validates a ZIP/SOZip file.")); 77 10 : group.add_argument("--optimize-from") 78 20 : .metavar("<input.zip>") 79 10 : .store_into(osOptimizeFrom) 80 : .help( 81 10 : _("Re-process {input.zip} to generate a SOZip-optimized .zip")); 82 10 : group.add_argument("input_files") 83 20 : .metavar("<input_files>") 84 10 : .store_into(aosFiles) 85 20 : .help(_("Filename of the file to add.")) 86 10 : .nargs(argparse::nargs_pattern::any); 87 : } 88 : 89 10 : bool bQuiet = false; 90 10 : bool bVerbose = false; 91 10 : argParser.add_group("Advanced options"); 92 : { 93 10 : auto &group = argParser.add_mutually_exclusive_group(); 94 10 : group.add_argument("--quiet").store_into(bQuiet).help( 95 : _("Quiet mode. No progress message is emitted on the standard " 96 10 : "output.")); 97 10 : group.add_argument("--verbose") 98 10 : .store_into(bVerbose) 99 10 : .help(_("Verbose mode.")); 100 : } 101 10 : bool bJunkPaths = false; 102 10 : argParser.add_argument("-j", "--junk-paths") 103 10 : .store_into(bJunkPaths) 104 : .help( 105 : _("Store just the name of a saved file (junk the path), and do not " 106 10 : "store directory names.")); 107 : 108 20 : CPLStringList aosOptions; 109 10 : argParser.add_argument("--enable-sozip") 110 10 : .choices("auto", "yes", "no") 111 20 : .metavar("auto|yes|no") 112 3 : .action([&aosOptions](const std::string &s) 113 13 : { aosOptions.SetNameValue("SOZIP_ENABLED", s.c_str()); }) 114 : .help(_("In auto mode, a file is seek-optimized only if its size is " 115 : "above the value of\n" 116 : "--sozip-chunk-size. In yes mode, all input files will be " 117 : "seek-optimized.\n" 118 10 : "In no mode, no input files will be seek-optimized.")); 119 10 : argParser.add_argument("--sozip-chunk-size") 120 20 : .metavar("<value in bytes or with K/M suffix>") 121 4 : .action([&aosOptions](const std::string &s) 122 14 : { aosOptions.SetNameValue("SOZIP_CHUNK_SIZE", s.c_str()); }) 123 : .help(_( 124 10 : "Chunk size for a seek-optimized file. Defaults to 32768 bytes.")); 125 10 : argParser.add_argument("--sozip-min-file-size") 126 20 : .metavar("<value in bytes or with K/M/G suffix>") 127 1 : .action([&aosOptions](const std::string &s) 128 11 : { aosOptions.SetNameValue("SOZIP_MIN_FILE_SIZE", s.c_str()); }) 129 : .help( 130 : _("Minimum file size to decide if a file should be seek-optimized. " 131 10 : "Defaults to 1 MB byte.")); 132 10 : argParser.add_argument("--content-type") 133 20 : .metavar("<string>") 134 1 : .action([&aosOptions](const std::string &s) 135 11 : { aosOptions.SetNameValue("CONTENT_TYPE", s.c_str()); }) 136 10 : .help(_("Store the Content-Type for the file being added.")); 137 : 138 : try 139 : { 140 10 : argParser.parse_args(aosArgv); 141 : } 142 0 : catch (const std::exception &err) 143 : { 144 0 : argParser.display_error_and_usage(err); 145 0 : std::exit(1); 146 : } 147 : 148 10 : if (!bList && !bValidate && osOptimizeFrom.empty() && aosFiles.empty()) 149 : { 150 0 : std::cerr << _("Missing source filename(s)") << std::endl << std::endl; 151 0 : std::cerr << argParser << std::endl; 152 0 : std::exit(1); 153 : } 154 : 155 10 : const char *pszZipFilename = osZipFilename.c_str(); 156 10 : if (!EQUAL(CPLGetExtensionSafe(pszZipFilename).c_str(), "zip")) 157 : { 158 0 : std::cerr << _("Extension of zip filename should be .zip") << std::endl 159 0 : << std::endl; 160 0 : std::cerr << argParser << std::endl; 161 0 : std::exit(1); 162 : } 163 : 164 10 : auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate( 165 30 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 166 10 : assert(alg); 167 : 168 20 : std::vector<std::string> args; 169 10 : args.push_back("vsi"); 170 10 : args.push_back("sozip"); 171 : 172 10 : if (bValidate) 173 : { 174 3 : args.push_back("validate"); 175 3 : if (bVerbose) 176 1 : args.push_back("--verbose"); 177 3 : args.push_back(pszZipFilename); 178 : } 179 7 : else if (bList) 180 : { 181 1 : args.push_back("list"); 182 1 : args.push_back(pszZipFilename); 183 : } 184 : else 185 : { 186 6 : args.push_back(osOptimizeFrom.empty() ? "create" : "optimize"); 187 6 : if (bRecurse) 188 1 : args.push_back("--recurse"); 189 6 : if (bJunkPaths) 190 5 : args.push_back("--junk-paths"); 191 6 : if (bOverwrite) 192 1 : args.push_back("--overwrite"); 193 6 : if (const char *val = aosOptions.FetchNameValue("SOZIP_ENABLED")) 194 : { 195 3 : args.push_back("--enable-sozip"); 196 3 : args.push_back(val); 197 : } 198 6 : if (const char *val = aosOptions.FetchNameValue("SOZIP_CHUNK_SIZE")) 199 : { 200 4 : args.push_back("--sozip-chunk-size"); 201 4 : args.push_back(val); 202 : } 203 6 : if (const char *val = aosOptions.FetchNameValue("SOZIP_MIN_FILE_SIZE")) 204 : { 205 1 : args.push_back("--sozip-min-file-size"); 206 1 : args.push_back(val); 207 : } 208 6 : if (const char *val = aosOptions.FetchNameValue("CONTENT_TYPE")) 209 : { 210 1 : args.push_back("--content-type"); 211 1 : args.push_back(val); 212 : } 213 6 : if (osOptimizeFrom.empty()) 214 : { 215 10 : for (const auto &s : aosFiles) 216 : { 217 5 : args.push_back(s); 218 : } 219 : } 220 : else 221 : { 222 1 : args.push_back(std::move(osOptimizeFrom)); 223 : } 224 6 : args.push_back(pszZipFilename); 225 : } 226 : 227 10 : if (!alg->ParseCommandLineArguments(args)) 228 : { 229 0 : fprintf(stderr, "%s", alg->GetUsageForCLI(true).c_str()); 230 0 : return 1; 231 : } 232 : 233 : { 234 10 : const auto stdoutArg = alg->GetActualAlgorithm().GetArg("stdout"); 235 10 : if (stdoutArg && stdoutArg->GetType() == GAAT_BOOLEAN) 236 9 : stdoutArg->Set(true); 237 : } 238 : 239 : GDALProgressFunc pfnProgress = 240 10 : alg->IsProgressBarRequested() ? GDALTermProgress : nullptr; 241 10 : void *pProgressData = nullptr; 242 : 243 10 : alg->SetCalledFromCommandLine(); 244 : 245 10 : int ret = 0; 246 10 : if (alg->Run(pfnProgress, pProgressData) && alg->Finalize()) 247 : { 248 : const auto outputArg = 249 10 : alg->GetActualAlgorithm().GetArg("output-string"); 250 20 : if (outputArg && outputArg->GetType() == GAAT_STRING && 251 10 : outputArg->IsOutput()) 252 : { 253 10 : printf("%s", outputArg->Get<std::string>().c_str()); 254 : } 255 : } 256 : else 257 : { 258 0 : ret = 1; 259 : } 260 : 261 10 : return ret; 262 : } 263 : 264 0 : MAIN_END