Skip to content

Single-header library for writing CLI applications in C/C++

Notifications You must be signed in to change notification settings

ronen25/libcmdf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 

Repository files navigation

libcmdf.h

A simple library for writing command-line applications, inspired by Python's cmd module.


Latest version: v.1.4.0 (2022-12-09)


Features

  1. Written using 100% ANSI C
  2. Header only: no linkage! No separate compilation!
  3. Cross-platform
  4. GNU Readline support
  5. Can be used from C++ (without -fpermissive)

Requirements

  1. Any ANSI C/ISO C90-compliant compiler
    Tested on GCC 5.4+, clang 4.0+, Apple Clang 14, and MSVC 14.0
  2. Linux/Windows/Mac
    Tested on Ubuntu 16.04 - 20.04, Fedora 26 - 30, Windows 10 (all AMD64) and Mac (M1)
  3. GNU Readline development libraries (optional)
    Required for GNU Readline support, if enabled.

Usage

Being a header-only library, you don't need to do any complex linkage - just drop it in your project tree and you're done!

You will, however, need to define LIBCMDF_IMPL only once, and before you include the library, like this:

#define LIBCMDF_IMPL
#include <libcmdf.h>

...

API in a nutshell

First of all, you must initialize libcmdf by calling either cmdf_init_quick() or cmdf_init:

void cmdf_init(const char *prompt, const char *intro, const char *doc_header,
               const char *undoc_header, char ruler, int use_default_exit);
#define cmdf_init_quick() cmdf_init(NULL, NULL, NULL, NULL, 0, 1)

The two most important parameters are the prompt and the intro:

prompt - The prompt for every command.
intro - A text that is displayed to the user on program startup.

After initialization is done, you must then register some command callbacks. A command callback has a command name associated with it, and that can be executed by the user, which in turn will execute the associated command callback.

The command callback has the following format:

typedef CMDF_RETURN (* cmdf_command_callback)(cmdf_arglist *arglist);

CMDF_RETURN is a typedefd integer specifying a return code. arglist is a pointer to the arguments passed by the user along with the command, which libcmdf transperantly handles behind the scenes. It is destroyed by libcmdf when the command callback returns.

This simple structure contains two elements:

/* libcmdf command list and arglist */
typedef struct cmdf___arglist_s {
     char **args;                /* NULL-terminated string list */
     size_t count;               /* Argument list count */
} cmdf_arglist;

This way you can quickly iterate the command-line arguments and act accordingly.

After you have your command callback, simply register it using cmdf_register_command:

CMDF_RETURN cmdf_register_command(cmdf_command_callback callback, const char *cmdname,
                                  const char *help);

Note that you may provide an optional help message. If you do, the user will be able to see it when and if he will request it using the help command.

After that, initialization of the library is pretty much complete, so you can just call the main command loop:

cmdf_commandloop();

In any case you may refer to test.c for a working example.

Configuration

The library can be configured by #defineing any of the following definitions only once, before including the library:

Definition Description Default
CMDF_MAX_COMMANDS Maxmium amount of allowed commands. 24
CMDF_TAB_TO_SPACES If a tab is encountered in a command's help string, expand it to N spaces. 8
CMDF_READLINE_SUPPORT Enable/disable GNU readline support (Linux only, requires readline development libraries) (Disabled)
CMDF_FGETS A fgets()-like function, to be used for command-line input. fgets()
CMDF_MALLOC A malloc()-like function, to be used for memory allocations1. malloc()
CMDF_FREE A free()-like function, to be used for memory deallocations1. free()
CMDF_MAX_INPUT_BUFFER_LENGTH The maximum length of the input buffer used to get user input1. 256
CMDF_STDOUT A FILE * to be used as standard output. stdout
CMDF_STDIN A FILE * to be used as standard input. stdin

1 Note: GNU Readline will not use any custom memory allocation functions, but rather the standard library's malloc and free. Also, you may have to provide additional linker flags to link against readline.

To configure libcmdf simply define any configuration definitions once and before LIBCMDF_IMPL, like so:

#define CMDF_READLINE_SUPPORT   /* Enable readline support */
#define LIBCMDF_IMPL
#include <libcmdf.h>

...

Feedback

I tested the library to the best of my abilities, but there might still be some bugs.
If you do find them, please contact me or open an issue!

FAQ

Is the library thread-safe?

No, but it's just handling user input for CLI, so I honestly don't think it should be.

Why is cmdf_quit not implemented?

At the moment, the initialization routines don't allocate any memory, or perform any weird initialization tricks that require deinitalization.

This might change in the future, though, so make sure to call cmdf_quit when you're done with it!

Any plans to implement a proper C++ API, Linenoise/anything else?

I initially had plans to implement a lot of features. However, I'm not working with C/C++ professionally anymore so my interest in these languages and the ecosystem has somewhat dwindled.

This does not affect the development status of this library - it will still be maintained and developed.


License

This software is dual-licensed to the public domain and under the following license: you are granted a perpetual, irrevocable license to copy, modify, publish and distribute this file as you see fit.