Coder Social home page Coder Social logo

hroptatyr / yuck Goto Github PK

View Code? Open in Web Editor NEW
62.0 9.0 7.0 752 KB

Your Umbrella Command Kit, a bog-standard command line option parser for C with all the knickknackery and whatnots.

License: Other

Makefile 8.85% C 57.94% Shell 11.51% M4 20.42% Roff 1.28%
yuck c option-parser

yuck's Introduction

yuck

Build Status Licence

Your Umbrella Command Kit

yuck is a bog-standard command line option parser for C that works with only household ingredients (a C compiler and the m4 macro processor) and comes with all the knickknackery and whatnots:

  • GNU-style long options (--blah)
  • condensable short options (-xab for -x -a -b)
  • optional arguments to long and short options (--foo[=BAR])
  • multiple occurrence of options (-vvv)
  • does not depend on libc's getopt() nor getopt_long()
  • licensed under BSD 3-clause licence

And getting started is as easy as munching cake -- let yuck do the actual baking for you: Just feed it the --help output you'd like to see and yuck will happily try and generate a parser from it.

To build yuck from the git sources:

$ autoreconf -fi
$ ./configure
$ make
$ make install

For tarball builds omit the autoreconf -fi line.

That all? I need more highlights

yuck can also generate parsers for umbrella tools, i.e. tools that take a command as argument (think git(1), ip(8), etc.).

yuck has no exotic build time or run time dependencies, a C99 compiler and the m4 macro processor is enough.

yuck can be used in other projects by copying 4 files and setting up a simple Makefile rule.

yuck can generate man pages based on the definition files (the --help output), much like help2man.

yuck can automatically determine (and make use of) version numbers in git controlled projects.

But why?

There's AutoOpts, there's gengetopt, lately even glibc takes on arg parsing (see their argp section in the manual); makes you wonder how we dare create yet another thing for something as simple as command line argument parsing.

Well, the killer feature, as we see it, is yuck's approach to specifying the parser in question. You expect your users to grasp your --help output? Well, there you go, if your users can understand it so can you! Just type up what you'd like to see in your --help output and yuck will generate a parser that does exactly that.

No, the other why?

yuck has been crafted by a heavy gengetopt user, so both the procedure and the handling is quite similar to the ggo workflow.

While gengetopt does a great job most of the time, it becomes annoying in some corner cases, is largely undermaintained, counts on libc for the actual getopt()'ing, is GPL licensed but first and foremost it is certainly not the right tool for the job if the job is parsing options for umbrella programs.

And what about docopt?

While docopt is based on essentially the same idea as yuck, its grammar is formal and doesn't allow for descriptive texts. Also, docopt's C parser (yuck's primary target) is not fully functional.

However, if you're currently using docopt and you feel comfortable with it, there's no need to switch to yuck.

Got an example?

Consider the following .yuck file:

Usage: xmpl
Shows off yuck.

  -x, --extra        Produce some extra bling.
  -o, --output=FILE  Output bling to file.

Process with:

$ yuck gen xmpl.yuck > xmpl.yucc

Then include in your xmpl.c:

#include <stdio.h>
#include "xmpl.yucc"

int main(int argc, char *argv[])
{
        yuck_t argp[1];

        yuck_parse(argp, argc, argv);

        if (argp->extra_flag) {
                puts("BLING BLING!");
        }

        yuck_free(argp);
        return 0;
}

And that's it. Some example calls:

$ xmpl --help
Usage: xmpl [OPTION]...

Shows off yuck.

  -h, --help            display this help and exit
  -V, --version         output version information and exit
  -x, --extra           Produce some extra bling.
  -o, --output=FILE     Output bling to file.

$ xmpl -x
BLING BLING!
$

More details, please

The example above results in an auxiliary struct:

struct yuck_s {
        enum yuck_cmds_e cmd;

        /* left-over arguments, the command string is never a part of this */
        size_t nargs;
        char *const *args;

        /* slots common to all commands */

        /* help is handled automatically */
        /* version is handled automatically */
        unsigned int extra_flag;
        const char *output_arg;
};

which is filled in when yuck_parse() is run. As there are no subcommands defined this struct will directly be typedef'd to yuck_t and the cmd slot at the top will always hold YUCK_NOCMD.

Every occurrence of -x or --extra on the command line will increase the count in extra_flag, yuck does not distinguish between optional flags (to occur at most once), flags to occur exactly once, or flags that can occur multiple times.

Same goes for every occurrence of -o or --output, however, the pointer in output_arg will point to the last occurrence on the commandline.

Left-over positional arguments will be counted in nargs and collected into args. It is never an error to pass in positional arguments. It is up to the caller of yuck_parse() to check the yuck_t representation of the command line for integrity.

In a similar fashion, yuck's only types are options with arguments (which are mapped to const char* or const char** in case of multi-args) and flags (mapped to unsigned int, representing the number of occurrences on the command line). Again, it is up to the postprocessing code to interpret arguments suitably, e.g. convert integer strings to integers, or constrain a HOSTNAME argument to its legal characters, etc.

All const char* objects point straight to members of argv, i.e. they are not strdup()ed. Changing strings in argv will therefore change the strings in the yuck_t representation also, and vice versa (after by-passing the const qualifier).

So what about subcommands?

yuck's command-line interface is generated by yuck itself, so for an hands-on example have a look there.

Subcommands are specified through extra usage clauses:

Usage: xmpl
Shows off yuck.

  -x, --extra        Produce some extra bling.
  -o, --output=FILE  Output bling to file.

Usage: xmpl turbo [FILE]...
Run xmpl in turbo mode

  -x, --extra-turbo  Use more turbos than normal.

Again, generate a C parser:

$ yuck gen xmpl-subcommands.yuck

The auxiliaries generated will now look like:

typedef union yuck_u yuck_t;

/* convenience structure for `turbo' */
struct yuck_cmd_turbo_s {
        enum yuck_cmds_e cmd;

        /* left-over arguments, the command string is never a part of this */
        size_t nargs;
        char *const *args;

        /* help is handled automatically */
        /* version is handled automatically */
        unsigned int extra_flag;
        const char *output_arg;

        unsigned int extra_turbo_flag;
};

union yuck_u {
        /* generic struct */
        struct {
                enum yuck_cmds_e cmd;

                /* left-over arguments,
                 * the command string is never a part of this */
                size_t nargs;
                char *const *args;

                /* slots common to all commands */
                /* help is handled automatically */
                /* version is handled automatically */
                unsigned int extra_flag;
                const char *output_arg;
        };

        /* depending on CMD at most one of the following structs is filled in
         * if CMD is YUCK_NONE no slots of this union must be accessed */
        struct yuck_cmd_turbo_s turbo;
};

and when yuck_parse() is run, provided the turbo command is given, the struct yuck_cmd_turbo_s object will be filled in, cmd will be set to XMPL_CMD_TURBO in such case. If no command is given the generic struct at the top of the union will be filled in and cmd is set to XMPL_CMD_NONE.

The structs are carefully generated in a way that allows you to simple cast a yuck_t* to a struct yuck_cmd_turbo_s*.

And now handing the --help output over to help2man again?!

Nope. Of course not. If we can create something as formal and definitive as a command-line option parser, we sure as hell can create something sloppy and informal as a man page (that is, no offence, for human eyes only anyway).

yuck comes with a template yuck.man.m4 for that purpose which is materialised through the genman command:

$ yuck genman xmpl-subcommands.yuck

would produce xmpl-subcommands.man (which here for obvious reasons has been run through man2html first).

This is all superfluous and utter rubbish because ...

Don't let me stop you there. I'm all ears for feature requests, patches, criticism and insults, oh, and death threats, of course.

Best to use the bug tracker, or drop me an email, or just put a huuuge graffiti on my house.

yuck's People

Contributors

daniel-araujo avatar hroptatyr avatar mitchty avatar rudimeier avatar shenlanting avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yuck's Issues

Specify program-wide options?

Hello,
I would like to be able to define options that would apply to all commands, just like --help and --version. Here is my current YUCK file

Usage: cz [OPTION]...
A simple tool to reorganize your code.

    -               do not modify the file, write to stdout instead
    -v, --verbose   output more info
    -s, --silent    output less info
    
Usage: cz copy [FILE] [SECTION] [LINE] [OPTION]...
Copy a section to a target line number

Usage: cz move [FILE] [SECTION] [LINE] [OPTION]...
Move a section to a target line number

Usage: cz rm [FILE] [SECTION] [OPTION]...
Delete a section. 

    -c, --cache=DIR Change the cache directory to DIR

Usage: cz restore [FILE] [SECTION] [OPTION]...
Restore a cached section to where it previously was.

    -c, --cache=DIR Change the cache directory to DIR

I would like options -, --verbose and --silent to apply on any subcommand.

No LICENSE/COPYING file

It's a standard to include LICENSE or COPYING file to indicate users about LICENSE of the project. From README it looks like it's using BSD 3-clause licence, but it's good practice to explicitly state it in separate file called LICENSE or COPYING.

Also, list item BSD 3-clause licence pointing at some license, isn't a clear legal statement about project licensing. It's more like a mention, that BSD 3-clause licence exists. It doesn't mean, that project is licensed under BSD 3-clause licence.

In order not to worry about possible legal issues in future, I would pick only libraries with explicit statement about licensing.

Nested subcommands

Is it possible to have subcommands within subcommands?

I'd like the ability to do something like remote channel change 5, or remote channel read, where channel is the main subcommand and change and read are subcommands that are part of the channel subcommand.

I've attempted to do it with a yuck file like this:

Usage: remote
Interact with a television.

Usage: remote power on
Power on the television

Usage: remote power off
Power off the television

Usage: remote channel change [CHANNEL]...
Change channel to CHANNEL

Usage: remote channel read
Read current channel

Which generates the following:

$ ./remote --help

Usage: remote [OPTION]... COMMAND

Interact with a television.

COMMAND may be one of:
  power       Power off the television
  channel     Read current channel

Options accepted by all commands:
  -h, --help            display this help and exit
  -V, --version         output version information and exit
$ ./remote power --help

Usage: remote power off

Power off the television

Common options:
  -h, --help            display this help and exit
  -V, --version         output version information and exit
$ ./remote channel --help
Usage: remote channel read

Read current channel

Common options:
  -h, --help            display this help and exit
  -V, --version         output version information and exit

As you can see, it seems to only register the last sub-subcommand (e.g., power off and channel read) in the yuck file.

Is this functionality already present and I'm doing it incorrectly? If it isn't present, would the current architecture allow for it to be easily added? I would be happy to contribute.

Can not create multiple subcommands

My yuck file tries to define two subcommands:

Usage: rrcsctl
Lists available subcommands.

Usage: rrcsctl zone [zone]
Zone operations

-l, --list list all zones
-k, --kill=zone kill zone

Usage: rrcsctl shm
Shared Memory operations

-s, --stats show statistics

However, the generated yucc file merges the shm and zone commands together:

$ ./rrcsctl -h
Usage: rrcsctl [OPTION]... COMMAND

Lists available subcommands.

COMMAND may be one of:
zone Zone operations

Options accepted by all commands:
-h, --help display this help and exit
-V, --version output version information and exit

$ ./rrcsctl zone -h
Usage: rrcsctl zone [OPTION]... [zone]

Zone operations

Common options:
-h, --help display this help and exit
-V, --version output version information and exit

Command-specific options:
-l, --list list all zones
-k, --kill=zone kill zone
Usage: rrcsctl shm
Shared Memory operations
-s, --stats show statistics

Latest yuck version:
$ yuck/src/yuck -V
yuck 0.2.0.git53.c27c78c

What am I doing wrong?

Thanks.

yuck gen -Hfile.h usage.yuck

yuck gen -Hfile.h usage.yuck
Produces a header file that includes itself, which then fails to compile.

#if defined HAVE_VERSION_H
# include "version.h"
#endif	/* HAVE_VERSION_H */
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include "file.h"  <<<<<<<<<<<<<<<<<<<<<<<<<<

#if defined __INTEL_COMPILER
# pragma warning (push)
# pragma warning (disable:177)
# pragma warning (disable:111)
# pragma warning (disable:3280)
#elif defined __GNUC__

Create Debian package for better project adoption

This project seems to be useful, I've found it, when I was looking for better alternative to GNU getopt. It seems to be able to build git-like command-line interface, which is great! I was looking for something like Python Click, of course without annotation, but I'm about CLI style.

Currently, dropt isn't available as debian package: https://packages.debian.org/search?searchon=names&keywords=yuck.

Open-source developers want to get their software packaged into official distribution repositories, because it makes it easier for users to use their software. And, it is much more complicated to package software, which uses library, which isn't in official distribution repositories.

Debian packages are automatically imported into Ubuntu package archives, which is one of the most popular distributions.

You should also consider getting project into official repositories of other distributions. I'm creating issue for Debian only, as I use Debian based distributions.

yuck generated manpage cause lintian issues

In the dateutils package the yuck generated manpages produce warnings, although this does not appear to affect the parsing of the manpage itself. I'm no expert on this but I think troff/nroff has a different format for comments. I've run this through groff a few times and I don't see any errors or backtraces. Let me know what you think.

W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dadd.1.gz 1: warning: macro /*' not defined W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dconv.1.gz 1: warning: macro/' not defined
W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.ddiff.1.gz 1: warning: macro /_' not defined W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dgrep.1.gz 1: warning: macro /*' not defined
W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dround.1.gz 1: warning: macro/_' not defined W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dseq.1.gz 1: warning: macro /
' not defined
W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dsort.1.gz 1: warning: macro /*' not defined W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dtest.1.gz 1: warning: macro/_' not defined
W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.dzone.1.gz 1: warning: macro /_' not defined W: dateutils: manpage-has-errors-from-man usr/share/man/man1/dateutils.strptime.1.gz 1: warning: macro /*' not defined

do not exit when parse error or help

I'm using yuck in a embedded system, This is a shell like program, It has several commands, So using yuck to parse option for them. But when using '--help' on these command, It call exit(..). This cause the main program crash.

So, How to change yuck behavior when encounters '--help' or parse error: Just return a error code.

I'm not familiar with m4, It looks like just change 'exit' to 'return' in "src/yuck-coru.c.m4" is enough. Right?

Maybe need to call yuck_free() before return?

"extra operand" is not handled

I think this line

$ yuck gen --no-auto-actions=bla  ./src/yuck.yuck

should give you an error like getopt would print:

yuck: option '--no-auto-actions' doesn't allow an argument

Also would be nice to be able to create commands without any non-option arguments which automatically exit on error, like

$ hostid bla
hostid: extra operand ‘bla’

is this the only c option parser that supports subcommands?

first of all, thanks for the Umbrella team for bring us such a useful library.
but, i'm so curious why there are no other tool or lib (i checked autoapts, gengetopt and getopt func) in c that can handle subcommand except this library. (at least this is what i found today)

Am i right? i'm grateful to hear from you guys.
i'd like to see if there're other tool/libs that supports subcommand, thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.