From bec9e7f3180ccb87f345e18d6cc1490f56057f02 Mon Sep 17 00:00:00 2001 From: Andrew Ayer Date: Sun, 29 Jun 2014 13:49:10 -0700 Subject: [PATCH] Add parse_options helper for parsing cmd line args --- Makefile | 2 +- commands.cpp | 59 ++++++----------------- git-crypt.cpp | 4 ++ parse_options.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++++++ parse_options.hpp | 60 +++++++++++++++++++++++ 5 files changed, 198 insertions(+), 45 deletions(-) create mode 100644 parse_options.cpp create mode 100644 parse_options.hpp diff --git a/Makefile b/Makefile index 2de93f9..0035245 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CXXFLAGS := -Wall -pedantic -Wno-long-long -O2 LDFLAGS := -lcrypto PREFIX := /usr/local -OBJFILES = git-crypt.o commands.o crypto.o gpg.o key.o util.o +OBJFILES = git-crypt.o commands.o crypto.o gpg.o key.o util.o parse_options.o all: git-crypt diff --git a/commands.cpp b/commands.cpp index 85a60b1..652865a 100644 --- a/commands.cpp +++ b/commands.cpp @@ -33,6 +33,7 @@ #include "util.hpp" #include "key.hpp" #include "gpg.hpp" +#include "parse_options.hpp" #include #include #include @@ -890,58 +891,28 @@ int refresh (int argc, char** argv) // TODO: do a force checkout, much like in u int status (int argc, char** argv) { - int argi = 0; - // Usage: // git-crypt status -r [-z] Show repo status // git-crypt status [-e | -u] [-z] [FILE ...] Show encrypted status of files // git-crypt status -f Fix unencrypted blobs - // Flags: - // -e show encrypted files only - // -u show unencrypted files only - // -f fix problems - // -z machine-parseable output - // -r show repo status only - // TODO: help option / usage output - bool repo_status_only = false; - bool show_encrypted_only = false; - bool show_unencrypted_only = false; - bool fix_problems = false; - bool machine_output = false; + bool repo_status_only = false; // -r show repo status only + bool show_encrypted_only = false; // -e show encrypted files only + bool show_unencrypted_only = false; // -u show unencrypted files only + bool fix_problems = false; // -f fix problems + bool machine_output = false; // -z machine-parseable output - while (argi < argc && argv[argi][0] == '-') { - if (std::strcmp(argv[argi], "--") == 0) { - ++argi; - break; - } - const char* flags = argv[argi] + 1; - while (char flag = *flags++) { - switch (flag) { - case 'r': - repo_status_only = true; - break; - case 'e': - show_encrypted_only = true; - break; - case 'u': - show_unencrypted_only = true; - break; - case 'f': - fix_problems = true; - break; - case 'z': - machine_output = true; - break; - default: - std::clog << "Error: unknown option `" << flag << "'" << std::endl; - return 2; - } - } - ++argi; - } + Options_list options; + options.push_back(Option_def("-r", &repo_status_only)); + options.push_back(Option_def("-e", &show_encrypted_only)); + options.push_back(Option_def("-u", &show_unencrypted_only)); + options.push_back(Option_def("-f", &fix_problems)); + options.push_back(Option_def("--fix", &fix_problems)); + options.push_back(Option_def("-z", &machine_output)); + + int argi = parse_options(options, argc, argv); if (repo_status_only) { if (show_encrypted_only || show_unencrypted_only) { diff --git a/git-crypt.cpp b/git-crypt.cpp index b4e7261..d270675 100644 --- a/git-crypt.cpp +++ b/git-crypt.cpp @@ -34,6 +34,7 @@ #include "crypto.hpp" #include "key.hpp" #include "gpg.hpp" +#include "parse_options.hpp" #include #include #include @@ -188,6 +189,9 @@ try { } catch (const Crypto_error& e) { std::cerr << "git-crypt: Crypto error: " << e.where << ": " << e.message << std::endl; return 1; +} catch (const Option_error& e) { + std::cerr << "git-crypt: Error: " << e.option_name << ": " << e.message << std::endl; + return 1; } catch (Key_file::Incompatible) { std::cerr << "git-crypt: This repository contains a incompatible key file. Please upgrade git-crypt." << std::endl; return 1; diff --git a/parse_options.cpp b/parse_options.cpp new file mode 100644 index 0000000..dc93133 --- /dev/null +++ b/parse_options.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2014 Andrew Ayer + * + * This file is part of git-crypt. + * + * git-crypt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * git-crypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with git-crypt. If not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify the Program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, the licensors of the Program + * grant you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#include "parse_options.hpp" +#include + + +static const Option_def* find_option (const Options_list& options, const std::string& name) +{ + for (Options_list::const_iterator opt(options.begin()); opt != options.end(); ++opt) { + if (opt->name == name) { + return &*opt; + } + } + return 0; +} + +int parse_options (const Options_list& options, int argc, char** argv) +{ + int argi = 0; + + while (argi < argc && argv[argi][0] == '-') { + if (std::strcmp(argv[argi], "--") == 0) { + ++argi; + break; + } else if (std::strncmp(argv[argi], "--", 2) == 0) { + std::string option_name; + const char* option_value = 0; + if (char* eq = std::strchr(argv[argi], '=')) { + option_name.assign(argv[argi], eq); + option_value = eq + 1; + } else { + option_name = argv[argi]; + } + ++argi; + + const Option_def* opt(find_option(options, option_name)); + if (!opt) { + throw Option_error(option_name, "Invalid option"); + } + + if (opt->is_set) { + *opt->is_set = true; + } + if (opt->value) { + if (option_value) { + *opt->value = option_value; + } else { + if (argi >= argc) { + throw Option_error(option_name, "Option requires a value"); + } + *opt->value = argv[argi]; + ++argi; + } + } else { + if (option_value) { + throw Option_error(option_name, "Option takes no value"); + } + } + } else { + const char* arg = argv[argi] + 1; + ++argi; + while (*arg) { + std::string option_name("-"); + option_name.push_back(*arg); + ++arg; + + const Option_def* opt(find_option(options, option_name)); + if (!opt) { + throw Option_error(option_name, "Invalid option"); + } + if (opt->is_set) { + *opt->is_set = true; + } + if (opt->value) { + if (*arg) { + *opt->value = arg; + } else { + if (argi >= argc) { + throw Option_error(option_name, "Option requires a value"); + } + *opt->value = argv[argi]; + ++argi; + } + break; + } + } + } + } + return argi; +} diff --git a/parse_options.hpp b/parse_options.hpp new file mode 100644 index 0000000..d02ddaa --- /dev/null +++ b/parse_options.hpp @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Andrew Ayer + * + * This file is part of git-crypt. + * + * git-crypt is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * git-crypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with git-crypt. If not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * If you modify the Program, or any covered work, by linking or + * combining it with the OpenSSL project's OpenSSL library (or a + * modified version of that library), containing parts covered by the + * terms of the OpenSSL or SSLeay licenses, the licensors of the Program + * grant you additional permission to convey the resulting work. + * Corresponding Source for a non-source form of such a combination + * shall include the source code for the parts of OpenSSL used as well + * as that of the covered work. + */ + +#ifndef PARSE_OPTIONS_HPP +#define PARSE_OPTIONS_HPP + +#include +#include + +struct Option_def { + std::string name; + bool* is_set; + const char** value; + + Option_def () : is_set(0), value(0) { } + Option_def (const std::string& arg_name, bool* arg_is_set) + : name(arg_name), is_set(arg_is_set), value(0) { } + Option_def (const std::string& arg_name, const char** arg_value) + : name(arg_name), is_set(0), value(arg_value) { } +}; + +typedef std::vector Options_list; + +int parse_options (const Options_list& options, int argc, char** argv); + +struct Option_error { + std::string option_name; + std::string message; + + Option_error (const std::string& n, const std::string& m) : option_name(n), message(m) { } +}; + +#endif