GithubHelp home page GithubHelp logo

dpse / tcli Goto Github PK

View Code? Open in Web Editor NEW
6.0 2.0 1.0 552 KB

Tiny command line interface for embedded systems

License: MIT License

CMake 0.27% C 99.73%
command-line console readline terminal uart vt100 serial telnet usart cli

tcli's Introduction

TinyCLI

TinyCLI is a command line interface for embedded systems that is intended to be very easy to use. Typical applications would e.g. be to provide a simple CLI over a serial line or over a Telnet connection.

Sample

Features

  • No dynamic allocations.
  • Configurable prompt(s).
  • Output buffering.
  • Command history in ringbuffer.
  • Backwards history search.
  • Tab-completion.
  • Custom SIGINT handler.
  • Functions for printing e.g. log data without disturbing the prompt.

History and output buffering can be disabled to reduce memory requirements for use on smaller systems.

Wrapper Extensions (tclie)

  • Extension functions for automatic handling of users, commands and tab-completion.
  • Optional pattern matching system:
    • Automatic validation of command syntax and command options.
    • Context-sensitive tab-completion.
  • Default commands (help, clear, login, and logout)

Users can be registered so that only certain commands are available to certain users. Login is possible with either password only or with usernames and optional passwords. Only users with a level matching the minimum required level for a command can execute the command.

Commands are registered with a command, the minimum user level required (if users are enabled), a description, and optionally a pattern with some options (if pattern matching is enabled). The description along with any pattern and options (if used) are automatically printed when the built-in help command is called.

Pattern Matching

The following syntax can be used in patterns:

Pattern Description
abc Matches abc.
"a b" or 'a b' Matches a b including whitespace.
[abc] Optionally matches abc.
a|b|cd Matches a, b or cd.
<abc> Wildcard; matches any word.
[<abc>] Optional wildcard; optionally matches any word.
a|(b c) or a|{b c} Matches a or b c.
... Matches none or all remaining tokens.
  • The pattern matching system currently only supports matching word-tokens (i.e. no matches inside words).
  • The pattern matcher is recursive and stack requirements will increase with pattern complexity.

Usage

  1. Define output function and initialize:
#include "tclie.h"

void output(void * arg, const char * str)
{
    printf("%s", str); // Or send through serial interface
}

tclie_t tclie;
tclie_init(&tclie, output, NULL);
  1. Register user table (if needed):
static const tclie_user_t users[] = {
    // Name,  password, level
    {"debug", NULL,     1}, // No password required
    {"admin", "12345",  2}
};

tclie_reg_users(&tclie, users, 2);
  1. Register command table:
int echo(void * arg, int argc, const char ** argv)
{
    if(argc > 1)
        printf("%s\r\n", argv[1]);
    
    return 0;
}

static const tclie_cmd_t cmds[] = {
    // Name, callback, minimum user level, description (for help)    
    {"echo", echo, 1, "Echo input."}
};

tclie_reg_cmds(&tclie, cmds, 1);
  1. Feed input characters:
while (1) {
    char c = getchar(); // Read e.g. serial input
    tclie_input_char(&tclie, c);
}

See the examples directory for more details.

Logging

In a multithreaded environment it can be useful to be able to log stuff without disturbing the prompt:

tclie_log(&tclie, "Some message...\r\n");

char buf[64];
tclie_log_printf(&tclie, buf, sizeof(buf), "Hello %s\r\n", "world!");

Supported Keyboard Shortcuts

Shortcut Description
Ctrl+a Move cursor to line start.
Ctrl+b Move cursor back one character.
Ctrl+c Sends SIGINT to registred handler.
Ctrl+d Delete current character.
Ctrl+e Move cursor to line end.
Ctrl+f Move cursor forward one character.
Ctrl+g Exit reverse search mode.
Ctrl+h Delete previous character.
Ctrl+i Equivalent to the tab key.
Ctrl+j Equivalent to the enter key.
Ctrl+k Clear line after cursor.
Ctrl+l Clear screen content.
Ctrl+n Recall next command.
Ctrl+p Recall previous command.
Ctrl+r Reverse search through command history.
Ctrl+u Clear line before cursor.
Ctrl+w Clear word before cursor.
Alt+b Move cursor backward one word.
Alt+d Delete word after cursor.
Alt+f Move cursor forward one word.
Alt+r Cancel changes to history line.
Tab Tab-complete at cursor or select from multiple matches.
Esc Exit tab-completion or reverse search mode.

Note! Esc needs to be pressed twice since it is impossible to differentiate from an escape sequence otherwise.

Miscellaneous

Telnet

Telnet newlines (<CR><NUL>) are automatically handled but it may be necessary to tell connecting clients (e.g. PuTTY) how to behave. This can be done by sending the following sequences to the client:

  • IAC DO ECHO: Tell client to echo received characters from server.
  • IAC WILL ECHO: Tell client that the server will echo back received characters.
  • IAC DO SUPPRESS-GO-AHEAD: Tell client to not send GO AHEAD when transmitting.
  • IAC WILL SUPPRESS-GO-AHEAD: Tell client that the server won't send GO AHEAD when transmitting.
const char options[] = {255, 253, 1,  // IAC DO ECHO
                        255, 251, 1,  // IAC WILL ECHO
                        255, 253, 3,  // IAC DO SUPPRESS-GO-AHEAD
                        255, 251, 3}; // IAC WILL SUPPRESS-GO-AHEAD

tcli's People

Contributors

dpse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

starrysec

tcli's Issues

tclie_pattern_match: Add missing #if TCLI_COMPLETE

Add missing TCLI_COMPLETE preprocessor barrier:

diff -u a/source/tclie.c b/source/tclie.c
--- a/source/tclie.c    2022-09-20 15:36:47.499476363 -0400
+++ b/source/tclie.c    2022-09-20 15:37:59.739855525 -0400
@@ -645,8 +645,10 @@ static bool tclie_pattern_match(tclie_t
    };
 
    const bool matches = tclie_pattern_match_token(&token, &p);
+#if TCLI_COMPLETE
    if (arg_index != 0)
        tclie_pattern_match_complete_options(&p);
+#endif
    return matches && arg_index == argc;
 }
 #endif

feature request: pattern matched commands

A feature I've used in past embedded CLI systems was a regularly formed syntax for the command usage description, which the command parser used to determine the validity of arguments.

Here's some examples of the 'usage descriptions':

Description Comment
reset Single word command, must match exactly.
config save Two word command, spaces around the words are ignored.
can speed <rate> Two word command, with mandatory argument.
set <attr> [value] One word command, with mandatory and optional argument.
echo ... One word command, with arbitrary number of arguments.
<reg> = <value> One word command ('=') embedded between mandatory arguments.
when <reg> is <value> echo ... Three word command, with two mandatory arguments and arbitrary optional.

The command handler worked just as yours does now, passing in all words in the argument as argc, argv.

The improvements over what you have now is:

  • Encourgaing usage document strings along with description
  • min_args/max_args can go away (determined when matching the input against the usage string)
  • Supports commands embedded between arguments (ie the <reg> = <value> case)

config structs: struct typedef should not itself be 'const'

The typedef for a struct should not itself be 'const', ie:

This is bad:

typedef const struct foo_s {
} foo_t;

This is preferred:

typedef struct foo_s {
} foo_t;

Rationale is that with the former, it is not possible to dynamically construct command definitions at runtime.

tclie_pattern_compare: Do not assume all strings have unique storage.

The assert(target != subject) in tclie_pattern_compare() fails on systems when the compiler optimizes const char * strings that have the same content to the same memory location (ie GCC optimization level -Os or -fmerge-constants).

If two pointers are equal, then by definition the strings are the same. Return true instead of assert.

Suggested patch:

diff -u a/source/tclie.c b/source/tclie.c
--- a/source/tclie.c    2022-09-20 15:36:47.499476363 -0400
+++ b/source/tclie.c    2022-09-20 15:37:59.739855525 -0400
@@ -112,7 +112,10 @@ static bool tclie_pattern_compare(const
 {
    assert(target);
    assert(subject);
-   assert(target != subject);
+
+   if (target == subject) {
+       return true;
+    }
 
    while (target_len != 0 && *target != '\0' && *subject != '\0') {
        if (*target++ != *subject++)

tclie_init(): Output function gets &tclie_t, not arg as expected.

When using tclie_init(&tclie, output_fn, output_fn_arg), the output function is being called with &tclie instead of arg.

Is this expected?

When using tcli_init(&tcli, output_fn, output_fn_arg), the output function is called with output_fn_arg, as I would expect.

Release tag?

Could you tag a commit you are particularly happy with to make a release?

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.