Code Monkey home page Code Monkey logo

ccli-gen's Introduction

C CLI parser generator

A command line arguments parser generator, with a focus on subcommands. Inspired by python's CLI.

The generated code will expose an entrypoint function parse, which takes the argc and argv, parses the arguments and calls the handler function. All unused arguments will be passed as extra argc and argv.

Currently, the generated code only calls the target command, doesn't call handlers as they are found: on the example below, if it finds that the (sub)command is the foo one, it will only call handle_base_foo.

There is nothing fancy here (exclusivity, defaults, validation, required, etc...), the handlers should (and can) handle it themselves.

The ccli-gen tool takes a file (e.g. params) and generates .c and .h files, named after the input file (e.g. params.c and params.h).

Usage

ccli-gen <params-file>

Example of input file

# a comment

# this specifies the base command name
base {
    # this specifies the C function that handles it
    # along with a description of the command
    @handle_base the base command does x and y

    # options, short and envvar are optional
    # specifying a placeholder for the variable is also optional
    # this also needs the type (str, int, bool)
    # along with a description
    -H, --host=HOST, $APP_HOST, str: this is the description of this option
    --debug, bool: shows verbose/debug output

    # a subcommand
    foo {
        @handle_base_foo the foo subcommand does that

        -n, --name=NAME, str: some name needed for some reason
    }

    # another subcommand
    bar {
        # description is optional
        @handle_base_bar
        -i, --id=ID, int: some id needed
    }
}

Example of output header file

#ifndef _GENERATED_H_
#define _GENERATED_H_

int parse(int argc, char**argv);

int handle_base(
    char * host,
    int verbose,
    int argc_left,
    char** argv_len
);
int handle_base_foo(
    char * name,
    char * host,
    int verbose,
    int argc_left,
    char** argv_len
);
int handle_base_bar(
    int id,
    char * host,
    int verbose,
    int argc_left,
    char** argv_len
);

#endif

The handlers will be passed their declared options, along with the parents' (all of them) in order:

  • my options;
  • parent options;
  • grand-parent options;
  • ...
  • argc_left, argv_left

Example of C file that uses the generated code:

See test.sh for a more updated example

#include <stdio.h>
#include "generated.h"

static void print_args(int argc, char**argv)
{
    for(int i=0;i<argc;i++) {
        printf(", %s", argv[i]);
    }
    fflush(stdout);
}

int handle_base(char* host, int verbose, int argc, char**argv)
{
    printf("handle_base(%d, %s, %d", verbose, host, argc);
    fflush(stdout);
    print_args(argc, argv);
    printf(")\n");

    return 0;
}

int handle_base_foo(char *name, char *host, int verbose, int argc, char**argv)
{
    printf("handle_base_foo(%s, %d, %s, %d", name, verbose, host, argc);
    print_args(argc, argv);
    printf(")\n");

    return 0;
}

int handle_base_bar(int id, char *host, int verbose, int argc, char**argv)
{
    printf("handle_base_bar(%d, %d, %s, %d", id, verbose, host, argc);
    print_args(argc, argv);
    printf(")\n");
    return 0;
}

int main(int argc, char**argv)
{
    return parse(argc, argv);
}

Features

  • Sub commands;
  • Order of options doesn't matter: base --name=dred foo is the same as base foo --name=dred. If two options have the same name, the order would indeed matter;
  • Env vars: if an option that provides a envvar name isn't provided, the parser will try to get from env;
  • No runtime, code is generated.

SIMDITF

something I may do in the future

  • Implement --help/-h (is it useful here?);
  • Fix Remove the special features;
  • Generate main function.

Bugs Special features

  • If an argument (not an option) appears before a subcommand name, it will still be passed to the subcommand's handler: ./base a-argument foo --name=dred is the same as ./base foo a-argument. This happens because, if an option (... --opt value ...) is from a child, the parser doesn't know if the --opt is boolean or not (if it consumes the value). This clearly opens a possibility for bugs :)

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.