barrygu / libyaarg

Yet Another Argv Parser - c++ library for simple, modular, argv parsing

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

What is libyaarg?
=================

YAARG stands for Yet Another Argv parsing library. That's right,
this is just a library to read flags from the command line.

I wrote it after years of using getopt or other libraries, mostly
to meet the needs of a few programs I'm working on.


Where do I find it?
===================

libyaarg is currently hosted on github. You can find it here:

   site: http://github.com/ccontavalli/libyaarg

If you have issues with libyaarg, you should file bugs here:

   http://github.com/ccontavalli/libyaarg/issues

There currently is no mailing list, but don't be shy, email
me if you have problems: <ccontavalli at inscatolati.net>.


Why should I use libyaarg?
==========================

Meh, don't use it if you don't want to, but here's a few principles that
drove libyaarg design:

  o  simple to use
  o  simple to modify
  o  self contained, little to no dependenies
  o  designed to support modular programming

But I gues this is no surprise. Find me a library whose design principles
have been "hard to use", "hard to modify", and with "lots of dependencies",
and I'll be happy to pay you a beer the first time I meet you in person.

So, here's what this all means to me:

  o  If I use a library, it has to save me time. Common use cases should
     not require too much boilerplate, and should be trivial to understand
     and use even without fully understanding how the library works.

  o  I don't like libraries for which I can't easily and quickly
     understand the code. If I need to fix a bug, I need to be able
     to do so quickly. If I want an extra feature, it has to be
     easy to add. Often, the efforts to make a library easy to use
     turn to fancy constructs and magic C++ features, with the purpose
     of adding syntactic sugar or expanding the flexibility of the
     code. Over elegance of the constructs being used, cleanness of
     the OO model, and fancy constructs, I prefer simpler constructs
     that even if less flexible, allow anyone to easily understand and
     fix the code.

When I talk about "modular programming", I refer to the fact that much
(most?) code written today is modular in design. OO programming has sold
inheritance to the masses, and it's not uncommon to have software that,
for example:

   o  Receive different flags or parameters depending on which other
      parameters were specified on the command line (eg, "ip addr show").

   o  Use multiple instances of classes that need parameters that the user
      should be able to override or change from the command line.

   o  Have different pieces of code that need to parse the command line
      in different ways, or at different stages.

I have always found getopt() and various other flags parsing libraries
hard to use in practice as they use globals, don't allow to perform
parsing in multiple stages, require too much code even for simple use
cases, ... libyaarg: 

   o  Does not use global variables for parsing at all.
   o  Allows to parse arbitrary vectors for flags.
   o  Keeps the options and the parsing confined to each class
      using it.

   [...]


Installation
============

At time of writing, libyaarg is not really meant for installation.
Just copy the files in your project, and use them. It's self contained,
it just requires a relatively recent compiler like gcc. It has not
been tested on other compilers, though. If you find issues, let me
know, and I'll be happy to fix them (see below).

You are free to include the code of this library in your project
as long as you follow the terms of the BSD LICENSE included.

Note that the BSD LICENSE allows you to sell, modify, or otherwise
distribute derived work both as a binary and as source code, without
need of disclosing the source of your derived work. The only thing
you have to do is retain the copyright notice on top of the source
files, and if you distribute it in binary form, provide a copy
in the documentation.

But just follow the terms of that license, and you'll be grand.

There is a Makefile included, which is mostly provided as an
example. If you run:

$ make

on your linux system with gcc, you'll get a .so file for libyaarg,
eg, a dynamic library that you can then link with the -lyaarg flag.
You can also use 'make static', to get a static .a library out
instead.

Again, this is just provided as an example, we might provide a
configure and something fancier in the future.


How do I use it?
================

Don't be shy, the .h files are reasonably well documented.
Peek there if you need to know something more, open issues on
the github tracker, or send me emails.

In any case, here's a small example that might help you getting
started.

Let's say you just wrote a program that lives mostly in a class
called 'Discover'. This programs takes a few flags:

   --interface, or -i, to specify a network interface.
   --file, or -r, to specify a file to read.
   --method, or -m, which can have value "libpcap", or "raw".

Now, let's look at the class definition in the .h file:

| // This header file provides definitions for all the flags you
| // can receive. .cc / .h file taking options/flags, should only
| // include this header file.
| # include "config-parser-options.h"
| 
| class Discover {
|  public:
|   Discover(ConfigParser* parser);
| 
|   [...]
| 
|  private:
|   StringOption interface_;
|   StringOption file_;
|   OneOfOption method_;
| }

Now, let's look at the .cc file (well, it could also live in the .h,
that's up to your coding style):

| # include "header-file-edited-in-the-snippet-above.h"
| 
| Discover::Discover(ConfigParser* parser)
|       // interface is a string option (as per .h file), if not specified
|       // on the command line, it defaults to value "wlan0".
|       // The other parameters are a description of the flag, specifying
|       // that you can supply it with --interface or -i.
|     : interface_(parser, Option::Default, "interface", "i", "wlan0",
| 		 "Specifies the interface to use"),
| 
|       // Same as above, nothing new.
|       file_(parser, Option::Default, "file", "r",
| 	    "Specifies the file to read packets from."),
| 
|       // method_ is a OneOf flag. The user must specify --method=libpcap or
|       // --method=raw, or no --method (as it's not marked as mandatory, via
|       // the Option::... argument). The 0 below is the default value,
|       // corresponds to the first element in the list of allowed values.
|       // Eg, 0 -> default is libpcap, 1 -> default is raw.
|       method_(parser, Option::Default, "method", "m",
| 	      {"libpcap", "raw"}, 0, 
| 	      "How to get packets from the interface.") {
| 
|   // All my fancy code goes here.
| 
| }


Now, here's an example of you to use the flags in your code:

| void Discover::Start() {
|   Sniffer* sniffer = NULL;
|   const string* interface = NULL;
| 
|   // Did the user specify a value via --file or -r?
|   // flag_.HasBeenSet() will return true if flag has been specified,
|   // false otherwise.
|   if (file_.HasBeenSet()) {
| 
|     // Read the value of --method. As indicated above,
|     // 0 corresponds to libpcap, 1 corresponds to raw.
|     // The method .Get() returns an integer.
|     if (method_.Get() == 0)
|       sniffer = new PcapFileSniffer();
|     else
|       sniffer = new RawPcapFileSniffer();
| 
|     // And read the name of the file. In this case, .Get()
|     // returns a const string.
|     interface = &file_.Get();
|   } else {
|     // Nothing new here and below.
|     if (method_.Get() == 0)
|       sniffer = new PcapDeviceSniffer();
|     else
|       sniffer = new LinuxInterfaceSniffer();
| 
|     interface = &interface_.Get();
|   }
| 
|   [...]
| }

Finally, here's what your main will look like:

| // In the main, you should include config-parser-argv. Note that
| // libyaarg is structured such as it would be simple to support
| // parsing .ini files or to provide other mechanisms (even combine
| // them) to read options. Code has not been implemented yet, but
| // the idea is that code parsing flags should include the .h
| // corresponding to the parser being used. .cc / .h files receiving
| // parsed options, should just include "config-parser-options.h".
| #include "config-parser-argv.h"
| 
| int main(int argc, const char** argv) {
|   // Create an argv parser. The two arguments are:
|   //   - some flags, that can control the behavior of the parser.
|   //     examples: by usin gConfigParser::RequiresOptions, parsing
|   //     will fail unless at least one option was specified, ...
|   //     See config-parser.h, class CommandHolder, Flags enum,
|   //     for documentation about which flags are available.
|   //   - the description of the tool. What does this program do?
|   //     displayed when the user provides --help.
|   ConfigParserArgv parser(
|       ConfigParser::Default, "THIS IS THE DESCRIPTION OF THE TOOL");
| 
|   // This will tell the argv parser to accept some standard options,
|   // like --help, or --dump-configs (to show the value of flags parsed
|   // so far). You don't have to do this, infact you can just register
|   // an 'HelpOption help(&parser, Option::Default, "doh", "g");' to
|   // have the behavior of --help with --doh or -g, if you so desire.
|   StandardOptions options(&parser);
|   
|   // This tells the parser about all the options Discover cares about.
|   // Note that this has to happen before we start parsing.
|   Discover discover(&parser); 
| 
|   // Now, let's rock: actually parse the command line. You can call
|   // this multiple times, with different vectors. It's up to you.
|   // Or don't call it at all, and enjoy a larger binary for the sake
|   // of shipping libyaarg in your products.
|   parser.Parse(argc, argv);
| 
|   // Is there anything the library needs to tell the user? not errors,
|   // just friendly messages that could go to stdout or logs?
|   if (parser.HasMessages())
|     // Send them to stdout, using cout. You can use whichever stream
|     // you like here (how surprising, uh?). You can also use something
|     // like GetMessages(&mystring), to get the messages in a string.
|     parser.PrintMessages(&cout);
| 
|   // Same as above. Are there errors? those should go to stderr.
|   if (parser.HasErrors())
|     // Those should go to stderr. You can always use GetErrors() instead.
|     parser.PrintErrors(&cerr);
| 
|   // Should we exit the program now? think about --help, the answer
|   // is probably yes. Or think about a fatal error in parsing the flags.
|   if (parser.ShouldExit()) {
|     int exitstatus;
|     // Meh, do we exit happily or angrily? what do we tell the OS?
|     if (parser.HasErrors())
|       exitstatus = 1;
|     else
|       exitstatus = 0;
| 
|     exit(exitstatus);
|   }
| }

Now, let's see what happens:

| $ ./discover --help
| THIS IS THE DESCRIPTION OF THE TOOL
|     --dump-configs Dumps all the options that have been provided to this binary.
| -r, --file         Specifies the file to read packets from. (string)
|     --help         Shows this help screen.
| -i, --interface    Specifies the interface to sniff packets from. (string)
| -m, --method       How to get packets from the interface. ("libpcap" or "raw")
| 
| $ ./discover --dump-configs
| --dump-configs
| --file=
| --help
| --interface=wlan0
| --method=libpcap
| [.. software keeps running ..]
| 
| $ ./discover -m raw --dump-configs
| --dump-configs
| --file=
| --help
| --interface=wlan0
| --method=raw
| [.. note the difference in --method ..]


Known Issues
============

The code isn't really complete. It's usuable, I use it on a few projects,
but there's a lot of FIXME and TODOs. I will fix them as time goes on.


Hacking
=======

Please do hack the library as you wish. Please send me
emails / issues / whatever if you find it useful.
If you modified the library to make it more useful to you,
it will be likely more useful to others. Send me patches,
tell me where to find your code, fork and push, but let
me know, and I'll be happy to integrate your code in the
main line. My email is: <ccontavalli at inscatolati.net>

About

Yet Another Argv Parser - c++ library for simple, modular, argv parsing