static void parseCommandLine(int argc, char *argv[], Settings &settings) { using namespace Sawyer::CommandLine; Parser parser; parser .purpose("generate a program with a certain kind of structure") .version(std::string(ROSE_SCM_VERSION_ID).substr(0, 8), ROSE_CONFIGURE_DATE) .chapter(1, "RSOE Command-line Tools") .doc("Synopsis", "@prop{programName} [@v{switches}]") .doc("Description", "Generates a program, written in C, on standard output, that has a certain structure specified by the " "command-line switches described here."); SwitchGroup gen = CommandlineProcessing::genericSwitches(); gen.insert(Switch("tree-depth", 't') .argument("n", nonNegativeIntegerParser(settings.binaryTreeDepth)) .doc("Width in bits of the 'if' tree.")); gen.insert(Switch("sequence-length", 'l') .argument("n", nonNegativeIntegerParser(settings.sequenceLength)) .doc("Number of times to repeat the sub-program.")); if (!parser.with(gen).parse(argc, argv).apply().unreachedArgs().empty()) throw std::runtime_error("incorrect usage; see --help"); }
Sawyer::CommandLine::ParserResult parseCommandLine(int argc, char *argv[], Settings &settings) { using namespace Sawyer::CommandLine; SwitchGroup switches; // --delta <arg> switches.insert(Switch("delta") .argument("delta", integerParser<int>(settings.delta))); Parser parser; return parser.with(switches).parse(argc, argv).apply(); }
// class method Sawyer::CommandLine::SwitchGroup Trigger::switches(Settings &settings) { using namespace Sawyer::CommandLine; using namespace StringUtility; SwitchGroup switches; switches.insert(Switch("activate") .argument("interval", addressIntervalParser(settings.when)) .doc("Restricts when this action should be triggered in terms of number of calls. When an action's " "other settings indicate that the action should be performed, the specified interval is consulted " "to determine whether to actually invoke the action. Whether the action is invoked or not, its " "number-of-calls counter is incremented. The default is to " + std::string(settings.when.isEmpty() ? "never invoke the action." : settings.when.isSingleton() ? ("invoke the action only on call " + numberToString(settings.when.least()) + ".") : settings.when.least()==0 ?("invoke the action " + plural(settings.when.size(), "times") + " beginning immediately.") : ("invoke the action on calls " + numberToString(settings.when.least()) + " through " + numberToString(settings.when.greatest() + ", inclusive."))))); return switches; }
int main(int argc, char *argv[]) { SwitchGroup general; // general switches for all git commands general.insert(Switch("version") .action(showVersion("1.0.0")) .doc("Prints the git suite version that the git program came from.")); general.insert(Switch("help") .action(showHelp()) .action(exitProgram(0)) .doc("Prints the synopsis and a list of the most commonly used commands. If the option @s all " "or @s a is given then all available commands are printed. If a git command is named, this " "option will bring up the manual page for that command.\n\n" "Other options are available to control how the manual page is displayed. See @man git-help 1 " "for more information, because '@prop{programName} @s{help} ...' is converted internally into " "'@prop{programName} help ...'")); general.insert(Switch("", 'c') .argument("name=value") .doc("Pass a configuration parameter to the command. The value given will override values from " "configuration files. The @v name is expected in the same format as listed by @man git-config 1 " "(subkeys separated by dots).")); general.insert(Switch("html-path") .doc("Print the path, without trailing slash, where git's HTML documentation is installed and exit.")); general.insert(Switch("man-path") .doc("Print the manpath (see @man<man><1>) for the man pages for this version of git and exit.")); general.insert(Switch("info-path") .doc("Print the path where the Info files documenting this version of git are installed and exit.")); general.insert(Switch("paginate", 'p') .doc("Pipe all output into @man less 1 (or if set, $PAGER) if standard output is a terminal. This " "overrides the pager.<cmd> configuration options (see the 'Configuration Mechanism' section " "below).")); general.insert(Switch("no-pager") .key("paginate") // same as the --paginate switch .intrinsicValue("false", booleanParser<bool>()) // but with opposite value from the default .doc("Do not pipe git output into a pager.")); general.insert(Switch("git-dir") .argument("path") .doc("Set the path to the repository. This can also be controlled by setting the GIT_DIR environment " "variable. It can be an absolute path or relative path to current working directory.")); general.insert(Switch("namespace") .argument("path") .doc("Set the git namespace. See @man(gitnamespaces)(7) for more details. Equivalent to setting the " "GIT_NAMESPACE environment variable.")); general.insert(Switch("bare") .doc("Treat the repository as a bare repository. If GIT_DIR environment is not set, it is set to the " "current working directory.")); general.insert(Switch("no-replace-objects") .doc("Do not use replacement refs to replace git objects. See @man<git-replace>(1) for more information.")); SwitchGroup cmd; // switches particular to the git-clone command cmd.insert(Switch("local", 'l') .doc("When the repository to clone from is on a local machine, this flag bypasses the normal 'git aware' " "transport mechanism and clones the repository by making a copy of HEAD and everything under objects " "and refs directories. The files under .git/objects/ directory are hardlinked to save space when " "possible. This is now the default when the source repository is specified with /path/to/repo syntax, " "so it essentially is a no-op option. To force copying instead of hardlinking (which may be desirable " "if you are trying to make a back-up of your repository), but still avoid the usual 'git aware' " "transport mechanism, @s no-hardlinks can be used.")); cmd.insert(Switch("no-hardlinks") .doc("Optimize the cloning process from a repository on a local filesystem by copying files under " ".git/objects directory.")); cmd.insert(Switch("shared", 's') .doc("When the repository to clone is on the local machine, instead of using hard links, automatically " "setup .git/objects/info/alternates to share the objects with the source repository. The resulting " "repository starts out without any object of its own.\n\n" "@b NOTE: this is a possibly dangerous operation; do not use it unless you understand what it does. If " "you clone your repository using this option and then delete branches (or use any other git command " "that makes any existing commit unreferenced) in the source repository, some objects may become " "unreferenced (or dangling). These objects may be removed by normal git operations (such as " "'@prop{commandName} commit') which automatically call '@prop{commandName} gc @s auto'. " " (See @man{git-gc}{1}.) If these objects are removed and were referenced by the cloned repository, then " "the cloned repository will become corrupt.\n\n" "Note that running '@prop{commandName} repack' without the @s{l} option in a repository cloned with " "@s{s} will copy objects from the source repository into a pack in the cloned repository, removing the " "disk space savings of clone @s{s}. It is safe, however, to run '@prop{commandName} gc', which uses the " "@s{l} option by default.\n\n" "If you want to break the dependency of a repository cloned with @s{s} on its source repository, you can " "simply run '@prop{commandName} repack @s{a}' to copy all objects from the source repository into a " "pack in the cloned repository.")); cmd.insert(Switch("reference") .argument("repository") .doc("If the reference repository is on the local machine, automatically setup .git/objects/info/alternates " "to obtain objects from the reference repository. Using an already existing repository as an alternate " "will require fewer objects to be copied from the repository being cloned, reducing network and local " "storage costs.\n\n" "@b{NOTE:} see the NOTE for the @s shared option.")); cmd.insert(Switch("quiet", 'q') .doc("Operate quietly. Progress is not reported to the standard error stream. This flag is also passed to " "the 'rsync' command when given.")); cmd.insert(Switch("verbose", 'v') .doc("Run verbosely. Does not affect the reporting of progress status to the standard error stream.")); cmd.insert(Switch("progress") .doc("Progress status is reported on the standard error stream by default when it is attached to a " "terminal, unless @s{q} is specified. This flag forces progress status even if the standard error stream " "is not directed to a terminal.")); cmd.insert(Switch("no-checkout", 'n') .doc("No checkout of HEAD is performed after the clone is complete.")); cmd.insert(Switch("bare") .doc("Make a bare GIT repository. That is, instead of creating @v directory and placing the administrative " "files in @v{directory}/.git, make the @v directory itself the $GIT_DIR. This obviously implies the @s{n} " "because there is nowhere to check out the working tree. Also the branch heads at the remote are " "copied directly to corresponding local branch heads, without mapping them to refs/remotes/origin/. " "When this option is used, neither remote-tracking branches nor the related configuration variables " "are created.")); cmd.insert(Switch("mirror") .doc("Set up a mirror of the source repository. This implies @s{bare}. Compared to @s{bare}, @s mirror not only " "maps local branches of the source to local branches of the target, it maps all refs (including " "remote-tracking branches, notes etc.) and sets up a refspec configuration such that all these refs " "are overwritten by a '@prop{commandName} remote update' in the target repository.")); cmd.insert(Switch("origin", 'b') .argument("name") .doc("Instead of pointing the newly created HEAD to the branch pointed to by the cloned repository's HEAD, " "point to @v name branch instead. @s branch can also take tags and treat them like detached HEAD. In a " "non-bare repository, this is the branch that will be checked out.")); cmd.insert(Switch("upload-pack", 'u') .argument("upload-pack") .doc("When given, and the repository to clone from is accessed via ssh, this specifies a non-default path " "for the command run on the other end.")); cmd.insert(Switch("template") .argument("template-directory") .doc("Specify the directory from which templates will be used; (See the 'TEMPLATE DIRECTORY' section of " "@man{git-init}{1}.)")); cmd.insert(Switch("config", 'c') .argument("key=value") .doc("Set a configuration variable in the newly-created repository; this takes effect immediately after the " "repository is initialized, but before the remote history is fetched or any files checked out. The key " "is in the same format as expected by @man{git-config}{1} (e.g., core.eol=true). If multiple values are " "given for the same key, each value will be written to the config file. This makes it safe, for " "example, to add additional fetch refspecs to the origin remote.")); cmd.insert(Switch("depth") .argument("depth", nonNegativeIntegerParser<unsigned>()) .doc("Create a shallow clone with a history truncated to the specified number of revisions. A shallow " "repository has a number of limitations (you cannot clone or fetch from it, nor push from nor into " "it), but is adequate if you are only interested in the recent history of a large project with a long " "history, and would want to send in fixes as patches.")); cmd.insert(Switch("single-branch") .doc("Clone only the history leading to the tip of a single branch, either specified by the @s branch option " "or the primary branch remote's HEAD points at. When creating a shallow clone with the @s depth option, " "this is the default, unless @s no-single-branch is given to fetch the histories near the tips of all " "branches.")); cmd.insert(Switch("no-single-branch") .key("single-branch") .intrinsicValue("false", booleanParser<bool>()) .hidden(true)); // already documented in "single-branch" cmd.insert(Switch("recursive") .longName("recursive-submodules") // alternate name for same switch .doc("After the clone is created, initialize all submodules within, using their default settings. This is " "equivalent to running '@prop{commandName} submodule update @s init @s{recursive}' immediately after " "the clone is " "finished. This option is ignored if the cloned repository does not have a worktree/checkout (i.e. if " "any of @s{no-checkout}/@s{n}, @s{bare}, or @s{mirror} is given)")); cmd.insert(Switch("separate-git-dir") .argument("git-dir") .doc("Instead of placing the cloned repository where it is supposed to be, place the cloned repository at " "the specified directory, then make a filesytem-agnostic git symbolic link to there. The result is git " "repository can be separated from working tree.")); Parser parser; parser // These are the switch groups from above that we want to be able to parse. // FIXME[Robb Matzke 2014-02-26]: order matters, so document it .with(general) .with(cmd) #if 0 /* enable these to turn this into a Windows-like command-parser, and notice how documentation also changes */ .resetLongPrefixes("/") .resetShortPrefixes("/") #endif // Basic information about the program. We could specify a program name here too, but Sawyer is smart // enough to get the name from the operating system, and that way your documentation will be relevant to // what the user actually typed to run the command. .chapter(1, "Sawyer Demonstration") // man page header and footer, i.e., "test11(1)" .purpose("Clone a repository into a new directory") // the Name section for the benefit of makewhatis(1) .version("1.7.10.4", "09/29/2012") // appears in the manpage footer // The default synopsis is like "foo [SWITCHES...]". Capitalization is only for the sake of the output (but for nroff // it doesn't matter since it'll be upper-cased). Sawyer itself is not sensitive to how you capitalize the names; it // will treat "Synopsis" and "synopsis" the same way. .doc("Synopsis", "@prop{programName} clone [@v{switches}] @v{repository} @v{directory}") // An optional description gets output between the Synopsis and Options sections. Of course, you don't have // do declare it in this order, but source code is more readable if you do. .doc("Description", "Clones a repository into a newly created directory, creates remote-tracking branches for each branch in " "the cloned repository (visible using '@prop{programName} branch @s{r}'), and creates and checks out an initial " "branch that is forked from the cloned repository's currently active branch.\n\n" "After the clone, a plain '@prop{programName} fetch' without arguments will update all the remote-tracking " "branches, and a '@prop{programName} pull' without arguments will in addition merge the remote master branch " "into the current master branch, if any.\n\n" "This default configuration is achieved by creating references to the remote branch heads under " "refs/remotes/origin and by initializing remote.origin.url and remote.origin.fetch configuration " "variables.\n\n" "This is version @prop{versionString}.") .doc("Options", "This paragraph is extra text for the Options section of the man page. It will appear above the list of " "program switches. The common practice is to name this section 'Options' even though it mostly describes " "program switches. The remainder of this section is generated automatically.") // I always like to put the nitty gritty details after the command-line switches because I'm looking at a man // page because I forgot how to use a program, not because I forgot what it does. .doc("details", "1", "More details... Any number of user-defined sections are possible and their order can be controled.") // Extra stuff, some standard and some funny. Sawyer doesn't care what sections you define. .doc("Bugs", "2", "User-defined sections can be in any order you want.") .doc("More Bugs", "3", "...and named anything you want.") // The See Also section is automatically generated and contains all the @man references that appear prior to // this section (that is, prior in the output, not the C++ source code). You can also insert your own text. .doc("See Also", "zzz", "And you can augment sections that are created automatically.") // The markup parser is decently smart about deciding when a "@" is part of a tag and when it should be copied // to the output. For instance, the "@" in the email address works fine. Also, the two @man references here show // up in the See Also section since "authors" comes before "zzz" alphabetically. .doc("Authors", "Git was started by Linus Torvalds, and is currently maintained by Junio C Hamano. Numerous contributions " "have come from the git mailing list @b{<*****@*****.**>}. For a more complete list of contributors, see " "@i{http://git-scm.com/about}. If you have a clone of git.git itself, the output of @man{git-shortlog}(1) " " and @man{git-blame}(1) can show you the authors for specific parts of the project.") // You can delete some sections (this example's a bit stupid because you wouldn't normally delete a section you // just created, but you might want to delete a section if the parser was created in source code you don't "own". // You can't delete the sections Sawyer creates automatically (you'd only delete your own text in those sections). .doc("Bogus", "Something we're about to delete...") .doc("BOGUS", "") // case is insignificant // Normally if an error occurs Sawyer will throw an STL runtime_error containing the error message. But if you set // the errorStream property to a Sawyer::Message::Stream your error will go there instead and the program exits with // non-zero status. If you prefer, you can catch the runtime_error and print it yourself, or even just rely on the C++ // runtime to print it. Also, if an errorStream is used and a "help" switch is defined, Sawyer will print "invoke // with '--help' for usage information." (or you can customize it to something else with the exitMessage property. .errorStream(Message::mlog[Message::FATAL]) // exit on error instead of throwing std::runtime_error // Finally, parse the command line! This returns a ParserResult which we don't use in this example. .parse(argc, argv); // If our demo didn't exit by now (no --help, which we said should cause an exit), then print the raw nroff man page // for debugging purposes. This is also an easy way to create a manpage for your program. std::cout <<parser.manpage(); }
// Class method Sawyer::CommandLine::ParserResult Application::parseCommandLine(int argc, char *argv[], Settings &settings) { using namespace Sawyer::CommandLine; // Generic switches SwitchGroup gen = CommandlineProcessing::genericSwitches(); gen.insert(Switch("config") .argument("names", listParser(anyParser(settings.configurationNames), ":")) .whichValue(SAVE_ALL) .explosiveLists(true) .doc("Directories containing configuration files, or configuration files themselves. A directory is searched " "recursively searched for files whose names end with \".json\" or and each file is parsed and used to " "to configure the partitioner. The JSON file contents is defined by the Carnegie Mellon University " "Software Engineering Institute. It should have a top-level \"config.exports\" table whose keys are " "function names and whose values are have a \"function.delta\" integer. The delta does not include " "popping the return address from the stack in the final RET instruction. Function names of the form " "\"lib:func\" are translated to the ROSE format \"func@lib\".")); // Switches for disassembly SwitchGroup dis("Disassembly switches"); dis.insert(Switch("remove-zeros") .argument("size", nonNegativeIntegerParser(settings.deExecuteZeros), "128") .doc("This switch causes execute permission to be removed from sequences of contiguous zero bytes. The " "switch argument is the minimum number of consecutive zeros that will trigger the removal, and " "defaults to 128. An argument of zero disables the removal. When this switch is not specified at " "all, this tool assumes a value of " + StringUtility::plural(settings.deExecuteZeros, "bytes") + ".")); // Switches for HTTP server SwitchGroup server("Server switches"); server.insert(Switch("http-address") .argument("IP-address", anyParser(settings.httpAddress)) .doc("IP address to bind to server listening socket. The default is " + settings.httpAddress)); server.insert(Switch("http-port") .argument("TCP-port", nonNegativeIntegerParser(settings.httpPort)) .doc("TCP port at which the HTTP server will listen. The default is " + boost::lexical_cast<std::string>(settings.httpPort) + ".")); server.insert(Switch("docroot") .argument("directory", anyParser(settings.docRoot)) .doc("Name of root directory for serving HTTP documents. The default is \"" + settings.docRoot + "\".")); server.insert(Switch("allow-downloads") .intrinsicValue(true, settings.allowDownloads) .doc("Allow parts of the binary specimen to be downloaded. The @s{no-allow-downloads} disables this " "features. The default is to " + std::string(settings.allowDownloads?"":"not ") + "allow downloads.")); server.insert(Switch("no-allow-downloads") .key("allow-downloads") .intrinsicValue(false, settings.allowDownloads) .hidden(true)); Parser parser; parser .purpose("binary ROSE on-line workbench for specimen exploration") .doc("synopsis", "@prop{programName} [@v{switches}] @v{specimen_names}") .doc("description", "This is a web server for viewing the contents of a binary specimen.") .doc("Specimens", P2::Engine::specimenNameDocumentation()); return parser.with(gen).with(dis).with(server).parse(argc, argv).apply(); }