blob: 223f6e0d38070ae1a25acc203752db2afe90ac78 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <getopt.h>
#include <flasher/args.hpp>
#include <flasher/convert.hpp>
#include <flasher/device.hpp>
#include <flasher/file.hpp>
#include <flasher/modutil.hpp>
#include <flasher/mutate.hpp>
#include <stdplus/print.hpp>
#include <format>
#include <map>
#include <stdexcept>
#include <string>
#include <string_view>
namespace flasher
{
const std::map<std::string_view, Args::Op> string_to_op = {
{"auto", Args::Op::Automatic}, {"read", Args::Op::Read},
{"write", Args::Op::Write}, {"erase", Args::Op::Erase},
{"verify", Args::Op::Verify},
};
ModTypeMap<DeviceType>& getDeviceTypes() noexcept;
ModTypeMap<MutateType>& getMutateTypes() noexcept;
ModTypeMap<FileType>& getFileTypes() noexcept;
Args::Args(int argc, char* argv[])
{
static const char opts[] = ":m:no:O:rs:S:v";
static const struct option longopts[] = {
{"mutate", required_argument, nullptr, 'm'},
{"and-verify", no_argument, nullptr, 'n'},
{"dev-offset", required_argument, nullptr, 'o'},
{"file-offset", required_argument, nullptr, 'O'},
{"no-read", no_argument, nullptr, 'r'},
{"size", required_argument, nullptr, 's'},
{"stride", required_argument, nullptr, 'S'},
{"verbose", no_argument, nullptr, 'v'},
{nullptr, 0, nullptr, 0},
};
int c;
optind = 0;
while ((c = getopt_long(argc, argv, opts, longopts, nullptr)) > 0)
{
switch (c)
{
case 'm':
mutate.emplace_back(optarg);
break;
case 'n':
verify = true;
break;
case 'o':
dev_offset = toUint32(optarg);
break;
case 'O':
file_offset = toUint32(optarg);
break;
case 'r':
noread = true;
break;
case 's':
max_size = toUint32(optarg);
break;
case 'S':
stride = toUint32(optarg);
break;
case 'v':
verbose++;
break;
case ':':
throw std::runtime_error(
std::format("Missing argument for `{}`", argv[optind - 1]));
break;
default:
throw std::runtime_error(std::format(
"Invalid command line argument `{}`", argv[optind - 1]));
}
}
if (optind == argc)
{
throw std::runtime_error("Mising flasher operation");
}
auto it = string_to_op.find(argv[optind]);
if (it == string_to_op.end())
{
throw std::runtime_error(
std::format("Invalid operation: {}", argv[optind]));
}
optind++;
op = it->second;
switch (op)
{
case Args::Op::Automatic:
case Args::Op::Write:
case Args::Op::Verify:
if (optind + 2 != argc)
{
throw std::runtime_error("Must specify FILE and DEV");
}
file.emplace(argv[optind]);
dev.emplace(argv[optind + 1]);
break;
case Args::Op::Read:
if (optind + 2 != argc)
{
throw std::runtime_error("Must specify DEV and FILE");
}
dev.emplace(argv[optind]);
file.emplace(argv[optind + 1]);
break;
case Args::Op::Erase:
if (optind + 1 != argc)
{
throw std::runtime_error("Must specify DEV");
}
dev.emplace(argv[optind]);
break;
}
}
void Args::printHelp(const char* arg0)
{
stdplus::println(stderr, "Usage: {} [OPTION]... auto FILE DEV", arg0);
stdplus::println(stderr, " or: {} [OPTION]... read DEV FILE", arg0);
stdplus::println(stderr, " or: {} [OPTION]... write FILE DEV", arg0);
stdplus::println(stderr, " or: {} [OPTION]... erase DEV", arg0);
stdplus::println(stderr, " or: {} [OPTION]... verify FILE DEV", arg0);
stdplus::println(stderr, "");
stdplus::println(stderr, "Optional Arguments:");
stdplus::println(
stderr,
" -m, --mutate[=MUTATE_OPTS] Applies another mutation from the file"
"contents during operation");
stdplus::println(stderr,
" -n, --and-verify Verifies the flash "
"contents during operation");
stdplus::println(
stderr,
" -o, --dev-offset[=OFFSET] The device offset starting point");
stdplus::println(
stderr,
" -O, --file-offset[=OFFSET] The file offset starting point");
stdplus::println(stderr,
" -r, --no-read Doesn't read the flash "
"before erasing or writing");
stdplus::println(
stderr,
" -s, --size[=SIZE] The max size to write to the flash");
stdplus::println(
stderr,
" -S, --stride[=SIZE] The number of bytes per operation");
stdplus::println(stderr,
" -v, --verbose Increases the verbosity "
"level of error message output");
stdplus::println(stderr, "");
stdplus::println(stderr,
"FILE options (separated by ,) (simple is the default):");
for (const auto& [_, type] : getFileTypes())
{
type->printHelp();
}
stdplus::println(stderr, "");
stdplus::println(stderr, "MUTATION options (separated by ,)");
for (const auto& [_, type] : getMutateTypes())
{
type->printHelp();
}
stdplus::println(stderr, "");
stdplus::println(stderr, "DEVICE options (separated by ,)");
for (const auto& [_, type] : getDeviceTypes())
{
type->printHelp();
}
stdplus::println(stderr, "");
stdplus::println(stderr, "Ex: {} -n -m rot128 auto image.bin mtd,/dev/mtd7",
arg0);
stdplus::println(
stderr, "Ex: {} erase fake,type=nor,erase=4096,size=16384,fake.img",
arg0);
stdplus::println(stderr, "");
}
Args Args::argsOrHelp(int argc, char* argv[])
{
try
{
return Args(argc, argv);
}
catch (...)
{
printHelp(argv[0]);
throw;
}
}
} // namespace flasher