diff --git a/CMakeLists.txt b/CMakeLists.txt index c752ba8..2fddb57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(META_APP_NAME "Tag Editor") set(META_APP_CATEGORIES "Utility;Audio;Video;") set(META_APP_AUTHOR "Martchus") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") -set(META_APP_DESCRIPTION "A tageditor with Qt GUI and command line interface. Supports MP4 (iTunes), ID3, Vorbis, Opus and Matroska.") +set(META_APP_DESCRIPTION "A tageditor with Qt GUI and command line interface. Supports MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska.") set(META_VERSION_MAJOR 1) set(META_VERSION_MINOR 4) set(META_VERSION_PATCH 1) @@ -130,6 +130,10 @@ set(ICON_FILES resources/icons/hicolor/scalable/apps/${META_PROJECT_NAME}.svg ) +set(DOC_FILES + README.md +) + # find c++utilities find_package(c++utilities 3.3.0 REQUIRED) use_cpp_utilities() diff --git a/application/main.cpp b/application/main.cpp index 2a55062..2bc1a6d 100644 --- a/application/main.cpp +++ b/application/main.cpp @@ -30,26 +30,29 @@ namespace Cli { SetTagInfoArgs::SetTagInfoArgs(Argument &filesArg, Argument &verboseArg) : filesArg(filesArg), verboseArg(verboseArg), - docTitleArg("doc-title", "d", "specifies the document title (has no affect if not supported by the container)"), - removeOtherFieldsArg("remove-other-fields", nullptr, "if present ALL unspecified tag fields will be removed (to remove a specific field use eg. \"album=\")"), - treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", nullptr, "if present unknown files will be treatet as MP3 files"), - id3v1UsageArg("id3v1-usage", nullptr, "specifies the ID3v1 usage (only used when already present by default); only relevant when dealing with MP3 files (or files treated as such)"), - id3v2UsageArg("id3v2-usage", nullptr, "specifies the ID3v2 usage (always used by default); only relevant when dealing with MP3 files (or files treated as such)"), - mergeMultipleSuccessiveTagsArg("merge-successive-tags", nullptr, "if present multiple successive ID3v2 tags will be merged"), - id3v2VersionArg("id3v2-version", nullptr, "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used"), - encodingArg("encoding", nullptr, "specifies the preferred encoding"), - removeTargetsArg("remove-targets", nullptr, "removes all tags with the specified targets (which must be separated by \",\")"), - attachmentsArg("attachments", nullptr, "specifies attachments to be added/updated/removed (multiple attachments must be separated by \",\""), - removeExistingAttachmentsArg("remove-existing-attachments", "ra", "specifies names/IDs of existing attachments to be removed"), - minPaddingArg("min-padding", nullptr, "specifies the minimum padding before the media data"), - maxPaddingArg("max-padding", nullptr, "specifies the maximum padding before the media data"), - prefPaddingArg("preferred-padding", nullptr, "specifies the preferred padding before the media data"), - tagPosArg("tag-pos", nullptr, "specifies the preferred tag position"), - forceTagPosArg("force-tag-pos", nullptr, "forces the specified tag postion to be used even if it requires the file to be rewritten"), - indexPosArg("index-pos", nullptr, "specifies the preferred index position"), - forceIndexPosArg("force-index-pos", nullptr, "forces the specified index postion to be used even if it requires the file to be rewritten"), - forceRewriteArg("force-rewrite", nullptr, "forces the file to rewritten from the scratch"), - setTagInfoArg("set-tag-info", "set", "sets the values of all specified tag fields") + docTitleArg("doc-title", 'd', "specifies the document title (has no affect if not supported by the container)"), + removeOtherFieldsArg("remove-other-fields", '\0', "if present ALL unspecified tag fields will be removed (to remove a specific field use eg. \"album=\")"), + treatUnknownFilesAsMp3FilesArg("treat-unknown-as-mp3", '\0', "if present unknown files will be treatet as MP3 files"), + id3v1UsageArg("id3v1-usage", '\0', "specifies the ID3v1 usage (only used when already present by default); only relevant when dealing with MP3 files (or files treated as such)"), + id3v2UsageArg("id3v2-usage", '\0', "specifies the ID3v2 usage (always used by default); only relevant when dealing with MP3 files (or files treated as such)"), + mergeMultipleSuccessiveTagsArg("merge-successive-tags", '\0', "if present multiple successive ID3v2 tags will be merged"), + id3v2VersionArg("id3v2-version", '\0', "forces a specific ID3v2 version to be used; only relevant when ID3v2 is used"), + encodingArg("encoding", '\0', "specifies the preferred encoding"), + removeTargetsArg("remove-targets", '\0', "removes all tags with the specified targets (which must be separated by \",\")"), + attachmentsArg("attachments", '\0', "specifies attachments to be added/updated/removed (multiple attachments must be separated by \",\""), + removeExistingAttachmentsArg("remove-existing-attachments", 'r', "specifies names/IDs of existing attachments to be removed"), + minPaddingArg("min-padding", '\0', "specifies the minimum padding before the media data"), + maxPaddingArg("max-padding", '\0', "specifies the maximum padding before the media data"), + prefPaddingArg("preferred-padding", '\0', "specifies the preferred padding before the media data"), + tagPosValueArg("value", 'v', "specifies the position, either front, back or current"), + forceTagPosArg("force", 'f', "forces the specified position even if the file to be rewritten"), + tagPosArg("tag-pos", '\0', "specifies the preferred tag position"), + indexPosValueArg("value", 'v', "specifies the position, either front, back or current"), + forceIndexPosArg("force", 'f', "forces the specified position even if the file to be rewritten"), + indexPosArg("index-pos", '\0', "specifies the preferred index position"), + forceRewriteArg("force-rewrite", '\0', "forces the file to rewritten from the scratch"), + valuesArg("fields", 'n', "specifies the values to be set"), + setTagInfoArg("set", 's', "sets the values of all specified tag fields") { docTitleArg.setCombinable(true); docTitleArg.setRequiredValueCount(-1); @@ -85,20 +88,23 @@ SetTagInfoArgs::SetTagInfoArgs(Argument &filesArg, Argument &verboseArg) : prefPaddingArg.setRequiredValueCount(1); prefPaddingArg.setValueNames({"preferred padding in byte"}); prefPaddingArg.setCombinable(true); - tagPosArg.setRequiredValueCount(1); + tagPosValueArg.setRequiredValueCount(1); + forceTagPosArg.setCombinable(true); tagPosArg.setValueNames({"front/back/current"}); tagPosArg.setCombinable(true); - tagPosArg.setSecondaryArguments({&forceTagPosArg}); - indexPosArg.setRequiredValueCount(1); + tagPosArg.setSubArguments({&tagPosValueArg, &forceTagPosArg}); + indexPosValueArg.setRequiredValueCount(1); + forceIndexPosArg.setCombinable(true); indexPosArg.setValueNames({"front/back/current"}); indexPosArg.setCombinable(true); - indexPosArg.setSecondaryArguments({&forceIndexPosArg}); + indexPosArg.setSubArguments({&indexPosValueArg, &forceIndexPosArg}); forceRewriteArg.setCombinable(true); + valuesArg.setValueNames({"title=foo", "album=bar", "cover=/path/to/file"}); + valuesArg.setRequiredValueCount(-1); + valuesArg.setImplicit(true); setTagInfoArg.setDenotesOperation(true); setTagInfoArg.setCallback(std::bind(Cli::setTagInfo, _1, std::cref(*this))); - setTagInfoArg.setRequiredValueCount(-1); - setTagInfoArg.setValueNames({"title=foo", "album=bar", "cover=/path/to/file"}); - setTagInfoArg.setSecondaryArguments({&filesArg, &docTitleArg, &removeOtherFieldsArg, &treatUnknownFilesAsMp3FilesArg, &id3v1UsageArg, &id3v2UsageArg, + setTagInfoArg.setSubArguments({&valuesArg, &filesArg, &docTitleArg, &removeOtherFieldsArg, &treatUnknownFilesAsMp3FilesArg, &id3v1UsageArg, &id3v2UsageArg, &mergeMultipleSuccessiveTagsArg, &id3v2VersionArg, &encodingArg, &removeTargetsArg, &attachmentsArg, &removeExistingAttachmentsArg, &minPaddingArg, &maxPaddingArg, &prefPaddingArg, &tagPosArg, &indexPosArg, &forceRewriteArg, &verboseArg}); @@ -114,86 +120,92 @@ int main(int argc, char *argv[]) QT_CONFIG_ARGUMENTS qtConfigArgs; HelpArgument helpArg(parser); // verbose option - Argument verboseArg("verbose", "v", "be verbose"); + Argument verboseArg("verbose", 'v', "be verbose"); verboseArg.setCombinable(true); // recursive option - Argument recursiveArg("recursive", "r", "includes subdirectories"); + Argument recursiveArg("recursive", 'r', "includes subdirectories"); // input/output file/files - Argument filesArg("files", "f", "specifies the path of the file(s) to be opened"); - filesArg.setValueNames({"path 1", "path 2"}); - filesArg.setRequiredValueCount(-1); - filesArg.setRequired(false); - filesArg.setImplicit(true); - qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(&filesArg); - Argument fileArg("file", "if", "specifies the path of the input file"); + Argument fileArg("file", 'f', "specifies the path of the file to be opened"); fileArg.setValueNames({"path"}); fileArg.setRequiredValueCount(1); - fileArg.setRequired(true); - Argument outputFileArg("output-file", "of", "specifies the path of the output file"); + fileArg.setCombinable(true); + Argument defaultFileArg(fileArg); + defaultFileArg.setImplicit(true); + Argument filesArg("files", 'f', "specifies the path of the file(s) to be opened"); + filesArg.setValueNames({"path 1", "path 2"}); + filesArg.setRequiredValueCount(-1); + filesArg.setCombinable(true); + Argument outputFileArg("output-file", 'o', "specifies the path of the output file"); outputFileArg.setValueNames({"path"}); outputFileArg.setRequiredValueCount(1); outputFileArg.setRequired(true); outputFileArg.setCombinable(true); // print field names - Argument printFieldNamesArg("print-field-names", nullptr, "prints available field names"); + Argument printFieldNamesArg("print-field-names", '\0', "prints available field names"); printFieldNamesArg.setCallback(Cli::printFieldNames); // display general file info - Argument displayFileInfoArg("display-file-info", "info", "displays general file information"); + Argument displayFileInfoArg("display-file-info", 'i', "displays general file information"); displayFileInfoArg.setDenotesOperation(true); displayFileInfoArg.setCallback(std::bind(Cli::displayFileInfo, _1, std::cref(filesArg), std::cref(verboseArg))); - displayFileInfoArg.setSecondaryArguments({&filesArg, &verboseArg}); + displayFileInfoArg.setSubArguments({&filesArg, &verboseArg}); // display tag info - Argument displayTagInfoArg("display-tag-info", "get", "displays the values of all specified tag fields (displays all fields if none specified)"); + Argument fieldsArg("fields", 'n', "specifies the field names to be displayed"); + fieldsArg.setValueNames({"title", "album", "artist", "trackpos"}); + fieldsArg.setRequiredValueCount(-1); + fieldsArg.setImplicit(true); + Argument displayTagInfoArg("get", 'g', "displays the values of all specified tag fields (displays all fields if none specified)"); displayTagInfoArg.setDenotesOperation(true); displayTagInfoArg.setCallback(std::bind(Cli::displayTagInfo, _1, std::cref(filesArg), std::cref(verboseArg))); - displayTagInfoArg.setRequiredValueCount(-1); - displayTagInfoArg.setValueNames({"title", "album", "artist", "trackpos"}); - displayTagInfoArg.setSecondaryArguments({&filesArg, &verboseArg}); + displayTagInfoArg.setSubArguments({&fieldsArg, &filesArg, &verboseArg}); // set tag info Cli::SetTagInfoArgs setTagInfoArgs(filesArg, verboseArg); // extract cover - Argument extractFieldArg("extract", "ext", "extracts the specified field from the specified file"); - extractFieldArg.setRequiredValueCount(1); - extractFieldArg.setValueNames({"field"}); - extractFieldArg.setSecondaryArguments({&fileArg, &outputFileArg, &verboseArg}); + Argument fieldArg("fields", 'n', "specifies the field to be extracted"); + fieldArg.setValueNames({"field name"}); + fieldArg.setRequiredValueCount(1); + fieldArg.setImplicit(true); + Argument extractFieldArg("extract", 'e', "extracts the specified field from the specified file"); + extractFieldArg.setSubArguments({&fieldArg, &fileArg, &outputFileArg, &verboseArg}); extractFieldArg.setDenotesOperation(true); extractFieldArg.setCallback(std::bind(Cli::extractField, _1, std::cref(fileArg), std::cref(outputFileArg), std::cref(verboseArg))); // file info - Argument validateArg("validate", "c", "validates the file integrity as accurately as possible; the structure of the file will be parsed completely"); - validateArg.setDenotesOperation(true); + Argument validateArg("validate", 'c', "validates the file integrity as accurately as possible; the structure of the file will be parsed completely"); validateArg.setCombinable(true); - Argument genInfoArg("html-info", nullptr, "generates technical information about the specified file as HTML document"); + Argument genInfoArg("html-info", '\0', "generates technical information about the specified file as HTML document"); genInfoArg.setDenotesOperation(true); - genInfoArg.setSecondaryArguments({&fileArg, &validateArg, &outputFileArg}); + genInfoArg.setSubArguments({&fileArg, &validateArg, &outputFileArg}); genInfoArg.setCallback(std::bind(Cli::generateFileInfo, _1, std::cref(fileArg), std::cref(outputFileArg), std::cref(validateArg))); // remove backup files - Argument remBackupFilesArg("remove-backup-files", nullptr, "removes all files with \".bak\" suffix in the given directory and in subdirectories if recursive option is present"); + Argument directoryArg("directory", 'd', "specifies the directory"); + directoryArg.setRequiredValueCount(1); + directoryArg.setValueNames({"path"}); + directoryArg.setImplicit(true); + Argument remBackupFilesArg("remove-backup-files", '\0', "removes all files with \".bak\" suffix in the given directory and in subdirectories if recursive option is present"); remBackupFilesArg.setDenotesOperation(true); remBackupFilesArg.setCallback(std::bind(Cli::removeBackupFiles, _1, std::cref(recursiveArg))); - remBackupFilesArg.setValueNames({"directory"}); - remBackupFilesArg.setRequiredValueCount(1); - remBackupFilesArg.setSecondaryArguments({&recursiveArg}); + remBackupFilesArg.setSubArguments({&directoryArg, &recursiveArg}); // renaming utility - Argument renamingUtilityArg("renaming-utility", nullptr, "launches the renaming utility instead of the main GUI"); + Argument renamingUtilityArg("renaming-utility", '\0', "launches the renaming utility instead of the main GUI"); renamingUtilityArg.setCombinable(true); // set arguments to parser - qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(&filesArg); - qtConfigArgs.qtWidgetsGuiArg().addSecondaryArgument(&renamingUtilityArg); - parser.setMainArguments({&printFieldNamesArg, &displayFileInfoArg, &displayTagInfoArg, &setTagInfoArgs.setTagInfoArg, &extractFieldArg, &genInfoArg, &remBackupFilesArg, &qtConfigArgs.qtWidgetsGuiArg(), &helpArg}); + qtConfigArgs.qtWidgetsGuiArg().setAbbreviation('\0'); + qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&defaultFileArg); + qtConfigArgs.qtWidgetsGuiArg().addSubArgument(&renamingUtilityArg); + parser.setMainArguments({&qtConfigArgs.qtWidgetsGuiArg(), &printFieldNamesArg, &displayFileInfoArg, &displayTagInfoArg, &setTagInfoArgs.setTagInfoArg, &extractFieldArg, &genInfoArg, &remBackupFilesArg, &helpArg}); // parse given arguments try { parser.parseArgs(argc, argv); if(qtConfigArgs.areQtGuiArgsPresent()) { #ifdef GUI_QTWIDGETS - return QtGui::runWidgetsGui(argc, argv, qtConfigArgs, filesArg.values().empty() ? QString() : QString::fromLocal8Bit(filesArg.values().front().data()), renamingUtilityArg.isPresent()); + return QtGui::runWidgetsGui(argc, argv, qtConfigArgs, defaultFileArg.isPresent() && !defaultFileArg.values().empty() ? QString::fromLocal8Bit(defaultFileArg.values().front()) : QString(), renamingUtilityArg.isPresent()); #else CMD_UTILS_START_CONSOLE; - cout << "Application has not been build with Qt widgets GUI support." << endl; + cerr << "Application has not been build with Qt widgets GUI support." << endl; #endif } } catch(const Failure &ex) { CMD_UTILS_START_CONSOLE; - cout << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl; + cerr << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl; } return 0; } diff --git a/cli/mainfeatures.cpp b/cli/mainfeatures.cpp index f00a0f0..6c992c7 100644 --- a/cli/mainfeatures.cpp +++ b/cli/mainfeatures.cpp @@ -151,7 +151,7 @@ void printNotifications(const MediaFileInfo &fileInfo, const char *head = nullpt printNotifications(notifications, head, beVerbose); } -void printFieldNames(const StringVector ¶meterValues) +void printFieldNames(const std::vector ¶meterValues) { CMD_UTILS_START_CONSOLE; VAR_UNUSED(parameterValues) @@ -160,7 +160,7 @@ void printFieldNames(const StringVector ¶meterValues) "recordlabel cover composer rating description" << endl; } -void removeBackupFiles(const StringVector ¶meterValues, const Argument &recursiveArg) +void removeBackupFiles(const std::vector ¶meterValues, const Argument &recursiveArg) { CMD_UTILS_START_CONSOLE; QDir dir(QString::fromStdString(parameterValues.at(0))); @@ -173,11 +173,11 @@ TagUsage parseUsageDenotation(const Argument &usageArg, TagUsage defaultUsage) { if(usageArg.isPresent()) { const auto &val = usageArg.values().front(); - if(val == "never") { + if(!strcmp(val, "never")) { return TagUsage::Never; - } else if(val == "keepexisting") { + } else if(!strcmp(val, "keepexisting")) { return TagUsage::KeepExisting; - } else if(val == "always") { + } else if(!strcmp(val, "always")) { return TagUsage::Always; } else { cout << "Warning: The specified tag usage \"" << val << "\" is invalid and will be ignored." << endl; @@ -190,15 +190,15 @@ TagTextEncoding parseEncodingDenotation(const Argument &encodingArg, TagTextEnco { if(encodingArg.isPresent()) { const auto &val = encodingArg.values().front(); - if(val == "utf8") { + if(!strcmp(val, "utf8")) { return TagTextEncoding::Utf8; - } else if(val == "latin1") { + } else if(!strcmp(val, "latin1")) { return TagTextEncoding::Latin1; - } else if(val == "utf16be") { + } else if(!strcmp(val, "utf16be")) { return TagTextEncoding::Utf16BigEndian; - } else if(val == "utf16le") { + } else if(!strcmp(val, "utf16le")) { return TagTextEncoding::Utf16LittleEndian; - } else if(val == "auto") { + } else if(!strcmp(val, "auto")) { } else { cout << "Warning: The specified encoding \"" << val << "\" is invalid and will be ignored." << endl; } @@ -210,11 +210,11 @@ ElementPosition parsePositionDenotation(const Argument &posArg, ElementPosition { if(posArg.isPresent()) { const auto &val = posArg.values().front(); - if(val == "front") { + if(!strcmp(val, "front")) { return ElementPosition::BeforeData; - } else if(val == "back") { + } else if(!strcmp(val, "back")) { return ElementPosition::AfterData; - } else if(val == "keep") { + } else if(!strcmp(val, "keep")) { return ElementPosition::Keep; } else { cout << "Warning: The specified position \"" << val << "\" is invalid and will be ignored." << endl; @@ -227,8 +227,8 @@ uint64 parseUInt64(const Argument &arg, uint64 defaultValue) { if(arg.isPresent()) { try { - if(startsWith(arg.values().front(), "0x")) { - return stringToNumber(arg.values().front().substr(2), 16); + if(*arg.values().front() == '0' && *(arg.values().front() + 1) == 'x') { + return stringToNumber(arg.values().front() + 2, 16); } else { return stringToNumber(arg.values().front()); } @@ -284,20 +284,21 @@ bool applyTargetConfiguration(TagTarget &target, const std::string &configStr) } } -vector parseFieldDenotations(const StringVector &fieldDenotations, bool readOnly) +vector parseFieldDenotations(const std::vector &fieldDenotations, bool readOnly) { vector fields; fields.reserve(fieldDenotations.size()); TagType currentTagType = TagType::Unspecified; TagTarget currentTagTarget; - for(const string &fieldDenotationString : fieldDenotations) { + for(const char *fieldDenotationString : fieldDenotations) { // check for tag or target specifier - if(strncmp(fieldDenotationString.c_str(), "tag:", 4) == 0) { - if(fieldDenotationString.size() == 4) { + const auto fieldDenotationLen = strlen(fieldDenotationString); + if(strncmp(fieldDenotationString, "tag:", 4) == 0) { + if(fieldDenotationLen == 4) { cout << "Warning: The \"tag\"-specifier has been used with no value(s) and hence is ignored. Possible values are: id3,id3v1,id3v2,itunes,vorbis,matroska,all" << endl; } else { TagType tagType = TagType::Unspecified; - for(const auto &part : splitString(fieldDenotationString.substr(4), ",", EmptyPartsTreat::Omit)) { + for(const auto &part : splitString(fieldDenotationString + 4, ",", EmptyPartsTreat::Omit)) { if(part == "id3v1") { tagType |= TagType::Id3v1Tag; } else if(part == "id3v2") { @@ -326,8 +327,8 @@ vector parseFieldDenotations(const StringVector &fieldDenotatio continue; } // read field name - auto equationPos = fieldDenotationString.find('='); - auto fieldName = equationPos != string::npos ? fieldDenotationString.substr(0, equationPos) : fieldDenotationString; + const auto equationPos = strchr(fieldDenotationString, '='); + auto fieldName = equationPos ? string(fieldDenotationString, static_cast(equationPos - fieldDenotationString)) : fieldDenotationString; // field name might denote increment ("+") or path disclosure (">") auto fieldNamePos = fieldName.size(); DenotationType type = DenotationType::Normal; @@ -354,7 +355,7 @@ vector parseFieldDenotations(const StringVector &fieldDenotatio cout << "Warning: Ignoring field denotation \"" << fieldDenotationString << "\" because no field name has been specified." << endl; continue; } else if(++fieldNamePos < fieldName.size()) { - fieldName = fieldName.substr(0, fieldNamePos); + fieldName = string(fieldName, fieldNamePos); } // parse the denoted filed KnownField field; @@ -424,11 +425,11 @@ vector parseFieldDenotations(const StringVector &fieldDenotatio fieldDenotation.type = type; fieldDenotation.tagType = currentTagType; fieldDenotation.tagTarget = currentTagTarget; - if(equationPos != string::npos) { + if(equationPos) { if(readOnly) { cout << "Warning: Specified value for \"" << fieldName << "\" will be ignored." << endl; } else { - fieldDenotation.values.emplace_back(make_pair(mult == 1 ? fieldDenotation.values.size() : fileIndex, QString::fromLocal8Bit(fieldDenotationString.data() + equationPos + 1))); + fieldDenotation.values.emplace_back(make_pair(mult == 1 ? fieldDenotation.values.size() : fileIndex, QString::fromLocal8Bit(equationPos + 1))); } } } @@ -569,7 +570,7 @@ bool AttachmentInfo::next(AbstractContainer *container) return true; } -void generateFileInfo(const StringVector ¶meterValues, const Argument &inputFileArg, const Argument &outputFileArg, const Argument &validateArg) +void generateFileInfo(const std::vector ¶meterValues, const Argument &inputFileArg, const Argument &outputFileArg, const Argument &validateArg) { CMD_UTILS_START_CONSOLE; VAR_UNUSED(parameterValues) @@ -581,7 +582,7 @@ void generateFileInfo(const StringVector ¶meterValues, const Argument &input inputFileInfo.parseEverything(); cout << "Saving file info of \"" << inputFileArg.values().front() << "\" ..." << endl; NotificationList origNotify; - QFile file(QString::fromLocal8Bit(outputFileArg.values().front().c_str())); + QFile file(QString::fromLocal8Bit(outputFileArg.values().front())); if(file.open(QFile::WriteOnly) && file.write(HtmlInfo::generateInfo(inputFileInfo, origNotify)) && file.flush()) { cout << "File information has been saved to \"" << outputFileArg.values().front() << "\"." << endl; } else { @@ -639,10 +640,10 @@ void printProperty(const char *propName, const intType value, const char *suffix } } -void displayFileInfo(const StringVector &, const Argument &filesArg, const Argument &verboseArg) +void displayFileInfo(const std::vector &, const Argument &filesArg, const Argument &verboseArg) { CMD_UTILS_START_CONSOLE; - if(!filesArg.valueCount()) { + if(filesArg.values().empty()) { cout << "Error: No files have been specified." << endl; return; } @@ -766,10 +767,10 @@ void displayFileInfo(const StringVector &, const Argument &filesArg, const Argum } } -void displayTagInfo(const StringVector ¶meterValues, const Argument &filesArg, const Argument &verboseArg) +void displayTagInfo(const std::vector ¶meterValues, const Argument &filesArg, const Argument &verboseArg) { CMD_UTILS_START_CONSOLE; - if(!filesArg.valueCount()) { + if(filesArg.values().empty()) { cout << "Error: No files have been specified." << endl; return; } @@ -864,10 +865,10 @@ void displayTagInfo(const StringVector ¶meterValues, const Argument &filesAr } } -void setTagInfo(const StringVector ¶meterValues, const SetTagInfoArgs &args) +void setTagInfo(const std::vector ¶meterValues, const SetTagInfoArgs &args) { CMD_UTILS_START_CONSOLE; - if(!args.setTagInfoArg.valueCount()) { + if(args.setTagInfoArg.values().empty()) { cout << "Error: No files have been specified." << endl; return; } @@ -888,7 +889,7 @@ void setTagInfo(const StringVector ¶meterValues, const SetTagInfoArgs &args) targetsToRemove.emplace_back(); bool validRemoveTargetsSpecified = false; for(const auto &targetDenotation : args.removeTargetsArg.values()) { - if(targetDenotation == ",") { + if(!strcmp(targetDenotation, ",")) { if(validRemoveTargetsSpecified) { targetsToRemove.emplace_back(); } @@ -906,7 +907,7 @@ void setTagInfo(const StringVector ¶meterValues, const SetTagInfoArgs &args) if(id3v2Version < 1 || id3v2Version > 4) { throw ConversionException(); } - } catch (ConversionException &) { + } catch (const ConversionException &) { id3v2Version = 3; cout << "Warning: The specified ID3v2 version \"" << args.id3v2VersionArg.values().front() << "\" is invalid and will be ingored." << endl; } @@ -1041,36 +1042,35 @@ void setTagInfo(const StringVector ¶meterValues, const SetTagInfoArgs &args) } // add/update/remove attachments explicitely AttachmentInfo currentInfo; - for(const auto &value : args.attachmentsArg.values()) { - const auto *data = value.data(); - if(value == ",") { + for(const char *value : args.attachmentsArg.values()) { + if(!strcmp(value, ",")) { attachmentsModified |= currentInfo.next(container); - } else if(value == "add") { + } else if(!strcmp(value, "add")) { currentInfo.action = AttachmentAction::Add; - } else if(value == "update-by-id") { + } else if(!strcmp(value, "update-by-id")) { currentInfo.action = AttachmentAction::UpdateById; - } else if(value == "update-by-name") { + } else if(!strcmp(value, "update-by-name")) { currentInfo.action = AttachmentAction::UpdateByName; - } else if(value == "remove-by-id") { + } else if(!strcmp(value, "remove-by-id")) { currentInfo.action = AttachmentAction::RemoveById; - } else if(value == "remove-by-name") { + } else if(!strcmp(value, "remove-by-name")) { currentInfo.action = AttachmentAction::RemoveByName; - } else if(!strncmp(data, "id=", 3)) { + } else if(!strncmp(value, "id=", 3)) { try { - currentInfo.id = stringToNumber(data + 3); + currentInfo.id = stringToNumber(value + 3); } catch(const ConversionException &) { - container->addNotification(NotificationType::Warning, "The specified attachment ID \"" + string(data + 3) + "\" is invalid.", context); + container->addNotification(NotificationType::Warning, "The specified attachment ID \"" + string(value + 3) + "\" is invalid.", context); } - } else if(!strncmp(data, "path=", 5)) { - currentInfo.path = data + 5; - } else if(!strncmp(data, "name=", 5)) { - currentInfo.name = data + 5; - } else if(!strncmp(data, "mime=", 5)) { - currentInfo.mime = data + 5; - } else if(!strncmp(data, "desc=", 5)) { - currentInfo.desc = data + 5; + } else if(!strncmp(value, "path=", 5)) { + currentInfo.path = value + 5; + } else if(!strncmp(value, "name=", 5)) { + currentInfo.name = value + 5; + } else if(!strncmp(value, "mime=", 5)) { + currentInfo.mime = value + 5; + } else if(!strncmp(value, "desc=", 5)) { + currentInfo.desc = value + 5; } else { - container->addNotification(NotificationType::Warning, "The attachment specification \"" + value + "\" is invalid and will be ignored.", context); + container->addNotification(NotificationType::Warning, "The attachment specification \"" + string(value) + "\" is invalid and will be ignored.", context); } } attachmentsModified |= currentInfo.next(container); @@ -1105,7 +1105,7 @@ void setTagInfo(const StringVector ¶meterValues, const SetTagInfoArgs &args) } } -void extractField(const StringVector ¶meterValues, const Argument &inputFileArg, const Argument &outputFileArg, const Argument &verboseArg) +void extractField(const std::vector ¶meterValues, const Argument &inputFileArg, const Argument &outputFileArg, const Argument &verboseArg) { CMD_UTILS_START_CONSOLE; const auto fields = parseFieldDenotations(parameterValues, true); diff --git a/cli/mainfeatures.h b/cli/mainfeatures.h index 1810ae3..a3c743b 100644 --- a/cli/mainfeatures.h +++ b/cli/mainfeatures.h @@ -8,10 +8,7 @@ #include namespace ApplicationUtilities { - -typedef std::vector StringVector; class Argument; - } namespace Cli { @@ -35,21 +32,24 @@ struct SetTagInfoArgs ApplicationUtilities::Argument minPaddingArg; ApplicationUtilities::Argument maxPaddingArg; ApplicationUtilities::Argument prefPaddingArg; - ApplicationUtilities::Argument tagPosArg; + ApplicationUtilities::Argument tagPosValueArg; ApplicationUtilities::Argument forceTagPosArg; - ApplicationUtilities::Argument indexPosArg; + ApplicationUtilities::Argument tagPosArg; + ApplicationUtilities::Argument indexPosValueArg; ApplicationUtilities::Argument forceIndexPosArg; + ApplicationUtilities::Argument indexPosArg; ApplicationUtilities::Argument forceRewriteArg; + ApplicationUtilities::Argument valuesArg; ApplicationUtilities::Argument setTagInfoArg; }; -void printFieldNames(const ApplicationUtilities::StringVector ¶meterValues); -void displayFileInfo(const ApplicationUtilities::StringVector &, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); -void generateFileInfo(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &validateArg); -void displayTagInfo(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); -void setTagInfo(const ApplicationUtilities::StringVector ¶meterValues, const Cli::SetTagInfoArgs &args); -void extractField(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &verboseArg); -void removeBackupFiles(const ApplicationUtilities::StringVector ¶meterValues, const ApplicationUtilities::Argument &recursiveArg); +void printFieldNames(const std::vector ¶meterValues); +void displayFileInfo(const std::vector &, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); +void generateFileInfo(const std::vector ¶meterValues, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &validateArg); +void displayTagInfo(const std::vector ¶meterValues, const ApplicationUtilities::Argument &filesArg, const ApplicationUtilities::Argument &verboseArg); +void setTagInfo(const std::vector ¶meterValues, const Cli::SetTagInfoArgs &args); +void extractField(const std::vector ¶meterValues, const ApplicationUtilities::Argument &inputFileArg, const ApplicationUtilities::Argument &outputFileArg, const ApplicationUtilities::Argument &verboseArg); +void removeBackupFiles(const std::vector ¶meterValues, const ApplicationUtilities::Argument &recursiveArg); }