Improve coding style in CLI code for parsing specified fields
* Use `std::string_view` instead of C-style strings and e.g. `strncmp` * Use consistent coding style, e.g. for initialization
This commit is contained in:
parent
ef637cb361
commit
a21d91e716
161
cli/helper.cpp
161
cli/helper.cpp
|
@ -535,65 +535,70 @@ bool applyTargetConfiguration(TagTarget &target, std::string_view configStr)
|
|||
|
||||
FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
|
||||
{
|
||||
FieldDenotations fields;
|
||||
auto fields = FieldDenotations();
|
||||
if (!fieldsArg.isPresent()) {
|
||||
return fields;
|
||||
}
|
||||
|
||||
const vector<const char *> &fieldDenotations = fieldsArg.values();
|
||||
FieldScope scope;
|
||||
const std::vector<const char *> &fieldDenotations = fieldsArg.values();
|
||||
auto scope = FieldScope();
|
||||
|
||||
for (const char *fieldDenotationString : fieldDenotations) {
|
||||
for (std::string_view fieldDenotationString : fieldDenotations) {
|
||||
// check for tag or target specifier
|
||||
const auto fieldDenotationLen = strlen(fieldDenotationString);
|
||||
if (!strncmp(fieldDenotationString, "tag=", 4)) {
|
||||
if (fieldDenotationLen == 4) {
|
||||
if (startsWith(fieldDenotationString, "tag=")) {
|
||||
const auto tagTypeString = fieldDenotationString.substr(4);
|
||||
if (tagTypeString.empty()) {
|
||||
cerr << Phrases::Error << "The \"tag\"-specifier has been used with no value(s)." << Phrases::End
|
||||
<< "note: Possible values are id3,id3v1,id3v2,itunes,vorbis,matroska and all." << endl;
|
||||
exit(-1);
|
||||
} else {
|
||||
TagType tagType = TagType::Unspecified;
|
||||
for (const auto &part : splitString(fieldDenotationString + 4, ",", EmptyPartsTreat::Omit)) {
|
||||
if (part == "id3v1") {
|
||||
tagType |= TagType::Id3v1Tag;
|
||||
} else if (part == "id3v2") {
|
||||
tagType |= TagType::Id3v2Tag;
|
||||
} else if (part == "id3") {
|
||||
tagType |= TagType::Id3v1Tag | TagType::Id3v2Tag;
|
||||
} else if (part == "itunes" || part == "mp4") {
|
||||
tagType |= TagType::Mp4Tag;
|
||||
} else if (part == "vorbis") {
|
||||
tagType |= TagType::VorbisComment | TagType::OggVorbisComment;
|
||||
} else if (part == "matroska") {
|
||||
tagType |= TagType::MatroskaTag;
|
||||
} else if (part == "all" || part == "any") {
|
||||
tagType = TagType::Unspecified;
|
||||
break;
|
||||
} else {
|
||||
cerr << Phrases::Error << "The value \"" << part << " for the \"tag\"-specifier is invalid." << Phrases::End
|
||||
<< "note: Possible values are id3,id3v1,id3v2,itunes,vorbis,matroska and all." << endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
scope.tagType = tagType;
|
||||
scope.allTracks = false;
|
||||
scope.trackIds.clear();
|
||||
std::exit(-1);
|
||||
}
|
||||
auto tagType = TagType::Unspecified;
|
||||
for (const auto &part : splitStringSimple<std::vector<std::string_view>>(tagTypeString, ",")) {
|
||||
if (part.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (part == "id3v1") {
|
||||
tagType |= TagType::Id3v1Tag;
|
||||
} else if (part == "id3v2") {
|
||||
tagType |= TagType::Id3v2Tag;
|
||||
} else if (part == "id3") {
|
||||
tagType |= TagType::Id3v1Tag | TagType::Id3v2Tag;
|
||||
} else if (part == "itunes" || part == "mp4") {
|
||||
tagType |= TagType::Mp4Tag;
|
||||
} else if (part == "vorbis") {
|
||||
tagType |= TagType::VorbisComment | TagType::OggVorbisComment;
|
||||
} else if (part == "matroska") {
|
||||
tagType |= TagType::MatroskaTag;
|
||||
} else if (part == "all" || part == "any") {
|
||||
tagType = TagType::Unspecified;
|
||||
break;
|
||||
} else {
|
||||
cerr << Phrases::Error << "The value \"" << part << " for the \"tag\"-specifier is invalid." << Phrases::End
|
||||
<< "note: Possible values are id3,id3v1,id3v2,itunes,vorbis,matroska and all." << endl;
|
||||
std::exit(-1);
|
||||
}
|
||||
}
|
||||
scope.tagType = tagType;
|
||||
scope.allTracks = false;
|
||||
scope.trackIds.clear();
|
||||
continue;
|
||||
} else if (applyTargetConfiguration(scope.tagTarget, fieldDenotationString)) {
|
||||
continue;
|
||||
} else if (!strcmp(fieldDenotationString, "target-matching=exact")) {
|
||||
} else if (fieldDenotationString == "target-matching=exact") {
|
||||
scope.exactTargetMatching = true;
|
||||
continue;
|
||||
} else if (!strcmp(fieldDenotationString, "target-matching=relaxed")) {
|
||||
} else if (fieldDenotationString == "target-matching=relaxed") {
|
||||
scope.exactTargetMatching = false;
|
||||
continue;
|
||||
} else if (!strncmp(fieldDenotationString, "track-id=", 9)) {
|
||||
const vector<string> parts = splitString<vector<string>>(fieldDenotationString + 9, ",", EmptyPartsTreat::Omit);
|
||||
bool allTracks = false;
|
||||
vector<std::uint64_t> trackIds;
|
||||
} else if (startsWith(fieldDenotationString, "track-id=")) {
|
||||
const auto parts = splitStringSimple<std::vector<std::string_view>>(fieldDenotationString.substr(9), ",");
|
||||
auto allTracks = false;
|
||||
auto trackIds = vector<std::uint64_t>();
|
||||
trackIds.reserve(parts.size());
|
||||
for (const auto &part : parts) {
|
||||
if (part.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (part == "all" || part == "any") {
|
||||
allTracks = true;
|
||||
break;
|
||||
|
@ -603,7 +608,7 @@ FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
|
|||
} catch (const ConversionException &) {
|
||||
cerr << Phrases::Error << "The value provided with the \"track\"-specifier is invalid." << Phrases::End
|
||||
<< "note: It must be a comma-separated list of track IDs." << endl;
|
||||
exit(-1);
|
||||
std::exit(-1);
|
||||
}
|
||||
}
|
||||
scope.allTracks = allTracks;
|
||||
|
@ -612,18 +617,18 @@ FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
|
|||
}
|
||||
|
||||
// check whether field name starts with + indicating an additional value
|
||||
bool additionalValue = *fieldDenotationString == '+';
|
||||
auto additionalValue = !fieldDenotationString.empty() && fieldDenotationString.front() == '+';
|
||||
if (additionalValue) {
|
||||
++fieldDenotationString;
|
||||
fieldDenotationString = fieldDenotationString.substr(1);
|
||||
}
|
||||
|
||||
// read field name
|
||||
const auto equationPos = strchr(fieldDenotationString, '=');
|
||||
auto fieldNameLen = equationPos ? static_cast<size_t>(equationPos - fieldDenotationString) : strlen(fieldDenotationString);
|
||||
const auto equationPos = fieldDenotationString.find('=');
|
||||
auto fieldNameLen = equationPos != std::string_view::npos ? equationPos : fieldDenotationString.size();
|
||||
// field name might denote increment ("+") or path disclosure (">")
|
||||
auto type = DenotationType::Normal;
|
||||
if (fieldNameLen && equationPos) {
|
||||
switch (*(equationPos - 1)) {
|
||||
if (fieldNameLen && equationPos != std::string_view::npos && equationPos) {
|
||||
switch (fieldDenotationString[equationPos - 1]) {
|
||||
case '+':
|
||||
type = DenotationType::Increment;
|
||||
--fieldNameLen;
|
||||
|
@ -637,10 +642,9 @@ FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
|
|||
}
|
||||
|
||||
// field name might specify a file index
|
||||
unsigned int fileIndex = 0, mult = 1;
|
||||
for (const char *digitPos = fieldDenotationString + fieldNameLen - 1; fieldNameLen && isDigit(*digitPos);
|
||||
--fieldNameLen, --digitPos, mult *= 10) {
|
||||
fileIndex += static_cast<unsigned int>(*digitPos - '0') * mult;
|
||||
auto fileIndex = 0u, mult = 1u;
|
||||
for (auto digitPos = fieldNameLen - 1; fieldNameLen && isDigit(fieldDenotationString[digitPos]); --fieldNameLen, --digitPos, mult *= 10) {
|
||||
fileIndex += static_cast<unsigned int>(fieldDenotationString[digitPos] - '0') * mult;
|
||||
}
|
||||
if (!fieldNameLen) {
|
||||
cerr << Phrases::Error << "The field denotation \"" << fieldDenotationString << "\" has no field name." << Phrases::EndFlush;
|
||||
|
@ -648,17 +652,17 @@ FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
|
|||
}
|
||||
|
||||
// parse the denoted field ID
|
||||
const auto fieldName = fieldDenotationString.substr(0, fieldNameLen);
|
||||
try {
|
||||
if (scope.isTrack()) {
|
||||
scope.field = FieldId::fromTrackDenotation(fieldDenotationString, fieldNameLen);
|
||||
scope.field = FieldId::fromTrackDenotation(fieldName);
|
||||
} else {
|
||||
scope.field = FieldId::fromTagDenotation(fieldDenotationString, fieldNameLen);
|
||||
scope.field = FieldId::fromTagDenotation(fieldName);
|
||||
}
|
||||
} catch (const ConversionException &e) {
|
||||
// unable to parse field ID denotation -> discard the field denotation
|
||||
cerr << Phrases::Error << "The field denotation \"" << string(fieldDenotationString, fieldNameLen)
|
||||
<< "\" could not be parsed: " << e.what() << Phrases::EndFlush;
|
||||
exit(-1);
|
||||
cerr << Phrases::Error << "The field denotation \"" << fieldName << "\" could not be parsed: " << e.what() << Phrases::EndFlush;
|
||||
std::exit(-1);
|
||||
}
|
||||
|
||||
// read cover always from file
|
||||
|
@ -669,24 +673,24 @@ FieldDenotations parseFieldDenotations(const Argument &fieldsArg, bool readOnly)
|
|||
// add field denotation scope
|
||||
auto &fieldValues = fields[scope];
|
||||
// add value to the scope (if present)
|
||||
if (equationPos) {
|
||||
if (equationPos != std::string_view::npos) {
|
||||
if (readOnly) {
|
||||
cerr << Phrases::Error << "A value has been specified for \"" << string(fieldDenotationString, fieldNameLen) << "\"." << Phrases::End
|
||||
cerr << Phrases::Error << "A value has been specified for \"" << fieldName << "\"." << Phrases::End
|
||||
<< "note: This is only possible when the \"set\"-operation is used." << endl;
|
||||
exit(-1);
|
||||
std::exit(-1);
|
||||
} else {
|
||||
// file index might have been specified explicitly
|
||||
// if not (mult == 1) use the index of the last value and increase it by one if the value is not an additional one
|
||||
// if there are no previous values, just use the index 0
|
||||
fieldValues.allValues.emplace_back(type,
|
||||
mult == 1 ? (fieldValues.allValues.empty() ? 0 : fieldValues.allValues.back().fileIndex + (additionalValue ? 0 : 1)) : fileIndex,
|
||||
equationPos + 1);
|
||||
fieldDenotationString.substr(equationPos + 1));
|
||||
}
|
||||
}
|
||||
if (additionalValue && readOnly) {
|
||||
cerr << Phrases::Error << "Indication of an additional value for \"" << string(fieldDenotationString, fieldNameLen) << "\" is invalid."
|
||||
<< Phrases::End << "note: This is only possible when the \"set\"-operation is used." << endl;
|
||||
exit(-1);
|
||||
cerr << Phrases::Error << "Indication of an additional value for \"" << fieldName << "\" is invalid." << Phrases::End
|
||||
<< "note: This is only possible when the \"set\"-operation is used." << endl;
|
||||
std::exit(-1);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
|
@ -744,34 +748,33 @@ template <class ConcreteTag, TagType tagTypeMask> FieldId FieldId::fromNativeFie
|
|||
std::bind(&knownFieldForNativeField<ConcreteTag, tagTypeMask>, nativeFieldId, _1, _2));
|
||||
}
|
||||
|
||||
FieldId FieldId::fromTagDenotation(const char *denotation, size_t denotationSize)
|
||||
FieldId FieldId::fromTagDenotation(std::string_view denotation)
|
||||
{
|
||||
// check for native, format-specific denotation
|
||||
if (!strncmp(denotation, "mkv:", 4)) {
|
||||
return FieldId::fromNativeField<MatroskaTag>(std::string_view(denotation + 4, denotationSize - 4));
|
||||
} else if (!strncmp(denotation, "mp4:", 4)) {
|
||||
return FieldId::fromNativeField<Mp4Tag>(std::string_view(denotation + 4, denotationSize - 4));
|
||||
} else if (!strncmp(denotation, "vorbis:", 7)) {
|
||||
return FieldId::fromNativeField<VorbisComment, TagType::VorbisComment | TagType::OggVorbisComment>(
|
||||
std::string_view(denotation + 7, denotationSize - 7));
|
||||
} else if (!strncmp(denotation, "id3:", 7)) {
|
||||
return FieldId::fromNativeField<Id3v2Tag>(std::string_view(denotation + 4, denotationSize - 4));
|
||||
} else if (!strncmp(denotation, "generic:", 8)) {
|
||||
if (startsWith(denotation, "mkv:")) {
|
||||
return FieldId::fromNativeField<MatroskaTag>(denotation.substr(4));
|
||||
} else if (startsWith(denotation, "mp4:")) {
|
||||
return FieldId::fromNativeField<Mp4Tag>(denotation.substr(4));
|
||||
} else if (startsWith(denotation, "vorbis:")) {
|
||||
return FieldId::fromNativeField<VorbisComment, TagType::VorbisComment | TagType::OggVorbisComment>(denotation.substr(7));
|
||||
} else if (startsWith(denotation, "id3:")) {
|
||||
return FieldId::fromNativeField<Id3v2Tag>(denotation.substr(4));
|
||||
} else if (startsWith(denotation, "generic:")) {
|
||||
// allow prefix 'generic:' for consistency
|
||||
denotation += 8, denotationSize -= 8;
|
||||
denotation = denotation.substr(8);
|
||||
}
|
||||
|
||||
// determine KnownField for generic denotation
|
||||
const auto knownField(FieldMapping::knownField(denotation, denotationSize));
|
||||
const auto knownField(FieldMapping::knownField(denotation.data(), denotation.size()));
|
||||
if (knownField == KnownField::Invalid) {
|
||||
throw ConversionException("generic field name is unknown");
|
||||
}
|
||||
return FieldId(knownField, nullptr, 0);
|
||||
}
|
||||
|
||||
FieldId FieldId::fromTrackDenotation(const char *denotation, size_t denotationSize)
|
||||
FieldId FieldId::fromTrackDenotation(std::string_view denotation)
|
||||
{
|
||||
return FieldId(KnownField::Invalid, denotation, denotationSize);
|
||||
return FieldId(KnownField::Invalid, denotation.data(), denotation.size());
|
||||
}
|
||||
|
||||
std::pair<std::vector<const TagValue *>, bool> FieldId::values(const Tag *tag, TagType tagType) const
|
||||
|
|
|
@ -59,8 +59,8 @@ class FieldId {
|
|||
|
||||
public:
|
||||
explicit FieldId(KnownField m_knownField = KnownField::Invalid, const char *denotation = nullptr, std::size_t denotationSize = 0);
|
||||
static FieldId fromTagDenotation(const char *denotation, std::size_t denotationSize);
|
||||
static FieldId fromTrackDenotation(const char *denotation, std::size_t denotationSize);
|
||||
static FieldId fromTagDenotation(std::string_view denotation);
|
||||
static FieldId fromTrackDenotation(std::string_view denotation);
|
||||
bool operator==(const FieldId &other) const;
|
||||
KnownField knownField() const;
|
||||
const char *name() const;
|
||||
|
@ -150,13 +150,13 @@ inline bool FieldScope::isTrack() const
|
|||
}
|
||||
|
||||
struct FieldValue {
|
||||
FieldValue(DenotationType type, unsigned int fileIndex, const char *value);
|
||||
FieldValue(DenotationType type, unsigned int fileIndex, std::string_view value);
|
||||
DenotationType type;
|
||||
unsigned int fileIndex;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
inline FieldValue::FieldValue(DenotationType type, unsigned int fileIndex, const char *value)
|
||||
inline FieldValue::FieldValue(DenotationType type, unsigned int fileIndex, std::string_view value)
|
||||
: type(type)
|
||||
, fileIndex(fileIndex)
|
||||
, value(value)
|
||||
|
|
Loading…
Reference in New Issue