Skip to content

Commit

Permalink
add option parser - fix #48
Browse files Browse the repository at this point in the history
  • Loading branch information
rbouqueau committed Jul 10, 2023
1 parent 363d8d3 commit 907327b
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 10 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ $(BIN)/cw_version.cpp: version
#------------------------------------------------------------------------------
SRCS_CW+=\
src/app/cw.cpp\
src/app/options.cpp\
src/app/report_std.cpp\
src/app/report_json.cpp\
src/utils/common_boxes.cpp\
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,26 @@ An online version is available [here for HEIF/MIAF](https://gpac.github.io/Compl

### Usage

```
$ bin/cw.exe -h
Compliance Warden, version v32-master-rev14-g363d8d3
Usage:
-s, --spec Specification name.
-f, --format Output format: "raw" (default), or "json"
-l, --list List available specifications or available rules.
-v, --version Print version and exit.
-h, --help Print usage and exit.
-t, --test Don't print warnings when switching to legacy mode.
```

[We need an option parser](https://github.com/gpac/ComplianceWarden/issues/48):

The old usage is deprecated and will be removed soon:

```
$ bin/cw.exe
Compliance Warden, version v28-master-rev3-g01d9486.
Compliance Warden, version v32-master-rev14-g363d8d3
Usage:
- Run conformance: bin/cw.exe <spec> input.mp4 [json]
Expand Down
73 changes: 71 additions & 2 deletions src/app/cw.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <cstring> // strcmp

#include "box_reader_impl.h"
#include "options.h"

extern const char *g_version;

Expand Down Expand Up @@ -85,7 +86,7 @@ bool specCheck(const SpecDesc *spec, const char *filename, uint8_t *data, size_t

void fprintVersion(FILE *const stream)
{
fprintf(stream, "%s, version %s.\n", g_appName, g_version);
fprintf(stream, "%s, version %s\n", g_appName, g_version);
fflush(stream);
}

Expand All @@ -103,7 +104,8 @@ void printUsageAndExit(const char *progName)

#ifndef CW_WASM

int main(int argc, const char *argv[])
// TODO: remove: introduce in July 2023 for v32
int mainLegacy(int argc, const char *argv[])
{
if(argc < 2 || argc > 4)
printUsageAndExit(argv[0]);
Expand Down Expand Up @@ -133,6 +135,73 @@ int main(int argc, const char *argv[])
return specCheck(spec, argv[2], buf.data(), (int)buf.size(), outputJson);
}

int main(int argc, const char *argv[])
{
bool help = false, list = false, version = false, testMode = false;
std::string specName, format = "text";

OptionHandler opt;
opt.add("s", "spec", &specName, "Specification name.");
opt.add("f", "format", &format, "Output format: \"raw\" (default), or \"json\"");
opt.addFlag("l", "list", &list, "List available specifications or available rules.");
opt.addFlag("v", "version", &version, "Print version and exit.");
opt.addFlag("h", "help", &help, "Print usage and exit.");
// TODO: remove (used to support deprecated legacy mode)
opt.addFlag("t", "test", &testMode, "Don't print warnings when switching to legacy mode.");

auto urls = opt.parse(argc, argv);

if(help) {
fprintVersion(stdout);
opt.printHelp(stdout);
return 0;
}

if(version) {
fprintVersion(stdout);
return 0;
}

if(list) {
if(specName.empty()) {
for(auto &spec : g_allSpecs())
printSpecDescription(spec);
} else {
auto spec = specFind(specName.c_str());
specListRules(spec);
}
return 0;
}

if(format != "text" && format != "json") {
fprintf(stderr, "invalid format, only \"text\" or \"json\" are supported");
return 1;
}

if(specName.empty() || urls.size() != 1) {
if(!testMode) {
fprintf(stderr, "/!\\ Failed argument parsing. Switching to legacy mode. /!\\\n\n");
opt.printHelp(stderr);
} else {
for(int i = 1, j = 1; i < argc; ++i, ++j) {
if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--test")) {
j--;
continue;
}
argv[j] = argv[i];
}
argc--;
}
return mainLegacy(argc, argv);
// fprintf(stderr, "expected one input file, got %" urls.size());
// return 1;
}

auto spec = specFind(specName.c_str());
auto buf = loadFile(urls[0].c_str());
return specCheck(spec, urls[0].c_str(), buf.data(), (int)buf.size(), format == "json");
}

#else

/* ***** emscripten exports ***** */
Expand Down
50 changes: 50 additions & 0 deletions src/app/options.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "options.h"

std::vector<std::string> OptionHandler::parse(int argc, const char *argv[])
{
std::vector<std::string> remaining;

ArgQueue args;
for(int i = 1; i < argc; ++i) // skip argv[0]
args.push(argv[i]);

while(!args.empty()) {
auto word = args.front();
args.pop();

if(word.substr(0, 1) != "-") {
remaining.push_back(word);
continue;
}

AbstractOption *opt = nullptr;

for(auto &o : m_Options) {
if(word == o->shortName || word == o->longName) {
opt = o.get();
break;
}
}

if(!opt) {
fprintf(stderr, "Unknown option: \"%s\"\n", word.c_str());
exit(1);
}

opt->parse(args);
}

return remaining;
}

void OptionHandler::printHelp(FILE *f)
{
fprintf(f, "\nUsage:\n");
for(auto &o : m_Options) {
auto s = o->shortName + ", " + o->longName;
while(s.size() < 40)
s += " ";
fprintf(f, " %s%s\n", s.c_str(), o->desc.c_str());
}
fprintf(f, "\n");
}
75 changes: 75 additions & 0 deletions src/app/options.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#pragma once

#include <memory>
#include <queue>
#include <string>
#include <vector>

typedef std::queue<std::string> ArgQueue;

static inline std::string safePop(ArgQueue &args)
{
if(args.empty()) {
fprintf(stderr, "unexpected end of command line\n");
exit(1);
}

auto val = args.front();
args.pop();
return val;
}

static inline void parseValue(bool &var, ArgQueue &)
{
var = true;
}

static inline void parseValue(std::string &var, ArgQueue &args)
{
var = safePop(args);
}

template<typename Element>
static inline void parseValue(std::vector<Element> &var, ArgQueue &args)
{
Element e{};
parseValue(e, args);
var.push_back(e);
}

struct OptionHandler {
void addFlag(std::string shortName, std::string longName, bool *pVar, std::string desc = "")
{
add(shortName, longName, pVar, desc);
}

template<typename T>
void add(std::string shortName, std::string longName, T *pVar, std::string desc = "")
{
auto opt = std::make_unique<TypedOption<T>>();
opt->pVar = pVar;
opt->shortName = "-" + shortName;
opt->longName = "--" + longName;
opt->desc = desc;
m_Options.push_back(std::move(opt));
}

std::vector<std::string> parse(int argc, const char *argv[]);
void printHelp(FILE *);

private:
struct AbstractOption {
virtual ~AbstractOption() = default;
std::string shortName, longName;
std::string desc;
virtual void parse(ArgQueue &args) = 0;
};

std::vector<std::unique_ptr<AbstractOption>> m_Options;

template<typename T>
struct TypedOption : AbstractOption {
T *pVar;
void parse(ArgQueue &args) { parseValue(*pVar, args); }
};
};
14 changes: 7 additions & 7 deletions tests/run
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,30 @@ function run_test

function real_mp4_aac
{
$BIN/cw.exe isobmff $scriptDir/aac.mp4 > $tmpDir/aac.new
$BIN/cw.exe -t isobmff $scriptDir/aac.mp4 > $tmpDir/aac.new
compare $scriptDir/aac.ref $tmpDir/aac.new
}

function real_mp4_avc
{
$BIN/cw.exe isobmff $scriptDir/avc.mp4 > $tmpDir/avc.new
$BIN/cw.exe -t isobmff $scriptDir/avc.mp4 > $tmpDir/avc.new
compare $scriptDir/avc.ref $tmpDir/avc.new
}

function real_mp4_heif
{
#generated with 'isoifftool -m 0 -t avc -i avc.264 -o heif.mp4 -s 320 240'
$BIN/cw.exe heif $scriptDir/heif.mp4 > $tmpDir/heif.new || true
$BIN/cw.exe -t heif $scriptDir/heif.mp4 > $tmpDir/heif.new || true
compare $scriptDir/heif.ref $tmpDir/heif.new

$BIN/cw.exe miaf $scriptDir/heif.mp4 > $tmpDir/miaf.new || true
$BIN/cw.exe -t miaf $scriptDir/heif.mp4 > $tmpDir/miaf.new || true
compare $scriptDir/miaf.ref $tmpDir/miaf.new
}

function miaf_file_extension
{
cp $scriptDir/heif.mp4 $tmpDir/heif_no_ext
$BIN/cw.exe miaf $tmpDir/heif_no_ext > $tmpDir/no_file_extension.new || true
$BIN/cw.exe -t miaf $tmpDir/heif_no_ext > $tmpDir/no_file_extension.new || true
compare $scriptDir/no_file_extension.ref $tmpDir/no_file_extension.new
}

Expand All @@ -71,9 +71,9 @@ function check_rules
nasm $scriptDir/$name.asm -f bin -o $filepath

if [[ $name == *invalid* ]] ; then
$BIN/cw.exe $spec $filepath > $tmpDir/$name.new 2>&1 || true
$BIN/cw.exe -t $spec $filepath > $tmpDir/$name.new 2>&1 || true
else
$BIN/cw.exe $spec $filepath > $tmpDir/$name.new || true
$BIN/cw.exe -t $spec $filepath > $tmpDir/$name.new || true
fi

compare $scriptDir/$name.ref $tmpDir/$name.new
Expand Down

0 comments on commit 907327b

Please sign in to comment.