Extension

XWSH Extension Mechanism: CherryRL library integration details, callback function descriptions, and history implementation.

CherryRL Library Integration

XWSH uses the CherryRL library to provide professional command line editing features, including history, auto-completion, ANSI escape sequence processing, etc. The integration code is located in xwmd/cli/xwsh/readline.c.

CherryRL Instance

// readline.c:35-37
chry_readline_t xwsh_cherryrl;                    // CherryRL instance
char xwsh_cherryrl_prompt[XWSH_RL_PROMPT_MAXSIZE]; // Prompt buffer (64 bytes)
char xwsh_cherryrl_history[XWSH_RL_HISTORY_MAXSIZE]; // History buffer (1024 bytes)

Buffer size requirements:

  • History buffer size must be a power of 2 (1024 qualifies)
  • Prompt buffer should be large enough to hold a complete ANSI escape sequence

Initialization Process

The xwsh_cherryrl_init() function (readline.c:125) completes the following steps:

  1. Configure initialization structure

    chry_readline_init_t init = {
            .prompt = xwsh_cherryrl_prompt,
            .pptsize = sizeof(xwsh_cherryrl_prompt),
            .history = xwsh_cherryrl_history,
            .histsize = sizeof(xwsh_cherryrl_history),
            .sget = xwsh_cherryrl_sget,  // Input callback
            .sput = xwsh_cherryrl_sput,  // Output callback
    };
    
  2. Initialize CherryRL

    rc = chry_readline_init(&xwsh_cherryrl, &init);
    if (0 != rc) {
            XwshLogE("chry_readline_init() ... <%ld>\n", rc);
    }
    
  3. Set callback functions

    chry_readline_set_completion_cb(&xwsh_cherryrl, xwsh_cherryrl_acb);
    chry_readline_set_user_cb(&xwsh_cherryrl, xwsh_cherryrl_ucb);
    
  4. Configure colored prompt

    // Green "xwsh"
    chry_readline_prompt_edit(&xwsh_cherryrl, 0,
                              (chry_readline_sgr_t){
                                      .foreground = CHRY_READLINE_SGR_GREEN,
                                      .bold = 1,
                              }.raw,
                              "xwsh");
    
    // White "@"
    chry_readline_prompt_edit(&xwsh_cherryrl, 1, 0, "@");
    
    // Blue "xwos"
    chry_readline_prompt_edit(&xwsh_cherryrl, 2,
                              (chry_readline_sgr_t){
                                      .foreground = CHRY_READLINE_SGR_BLUE,
                                      .bold = 1,
                              }.raw,
                              "xwos");
    
    // White ":>"
    chry_readline_prompt_edit(&xwsh_cherryrl, 3, 0, ":>");
    

Final prompt effect: xwsh@xwos:>

Callback Function Details

1. User Event Callback (xwsh_cherryrl_ucb())

Location: readline.c:39

Function: Handles special events generated by CherryRL, such as signals and function keys.

int xwsh_cherryrl_ucb(chry_readline_t * rl, uint8_t exec)
{
        /* User event callback does not auto-output newline */
        chry_readline_newline(rl);

        switch (exec) {
        case CHRY_READLINE_EXEC_EOF:
                XwshLogI("EOF\r\n");
                break;
        case CHRY_READLINE_EXEC_SIGINT:
                chry_readline_ignore(rl, false);
                XwshLogI("SIGINT\r\n");
                break;
        case CHRY_READLINE_EXEC_SIGQUIT:
                XwshLogI("SIGQUIT\r\n");
                break;
        case CHRY_READLINE_EXEC_SIGCONT:
                XwshLogI("SIGCONT\r\n");
                break;
        case CHRY_READLINE_EXEC_SIGSTOP:
                XwshLogI("SIGSTOP\r\n");
                break;
        case CHRY_READLINE_EXEC_SIGTSTP:
                XwshLogI("SIGTSTP\r\n");
                break;
        case CHRY_READLINE_EXEC_F1 ... CHRY_READLINE_EXEC_F12:
                XwshLogI("F%d event\r\n", exec - CHRY_READLINE_EXEC_F1 + 1);
                break;
        default:
                XwshLogI("ERROR EXEC %d\r\n", exec);
                return -1;  // Error, terminate readline
        }

        /* Return value meaning:
         *  1: Do not refresh display
         *  0: Refresh entire line (including prompt)
         * -1: Terminate readline (error)
         */
        return 0;
}

Supported Events:

Event Constant Value Trigger Condition Handling
CHRY_READLINE_EXEC_EOF 0 End of file (Ctrl+D) Log
CHRY_READLINE_EXEC_SIGINT 1 Interrupt (Ctrl+C) Reset ignore flag
CHRY_READLINE_EXEC_SIGQUIT 2 Quit (Ctrl+) Log
CHRY_READLINE_EXEC_SIGCONT 3 Continue signal Log
CHRY_READLINE_EXEC_SIGSTOP 4 Stop signal Log
CHRY_READLINE_EXEC_SIGTSTP 5 Terminal stop (Ctrl+Z) Log
CHRY_READLINE_EXEC_F1F12 16-27 Function keys F1-F12 Log

2. Auto-completion Callback (xwsh_cherryrl_acb())

Location: readline.c:78

Function: Provides auto-completion suggestions for commands and arguments (currently empty implementation, reserved extension interface).

uint8_t xwsh_cherryrl_acb(chry_readline_t * rl, char * pre,
                          uint16_t * size, const char ** argv,
                          uint8_t * argl, uint8_t argcmax)
{
        XWOS_UNUSED(rl);
        XWOS_UNUSED(pre);
        XWOS_UNUSED(size);
        XWOS_UNUSED(argv);
        XWOS_UNUSED(argl);
        XWOS_UNUSED(argcmax);
        return 0;  // No completion suggestions
}

Parameter Descriptions:

Parameter Type Description
rl chry_readline_t * CherryRL instance
pre char * Prefix string to complete
size uint16_t * Number of completion suggestions (output)
argv const char ** Completion suggestion string array (output)
argl uint8_t * Lengths of suggestion strings (output)
argcmax uint8_t Maximum number of completion suggestions

Extension Example (implementing command completion):

uint8_t xwsh_cherryrl_acb(chry_readline_t * rl, char * pre,
                          uint16_t * size, const char ** argv,
                          uint8_t * argl, uint8_t argcmax)
{
        xwsz_t i;
        uint8_t count = 0;
        
        // Complete built-in commands
        for (i = 0; i < xwsh_cmd_table_size; i++) {
                if (strncmp(pre, xwsh_cmd_table[i].name, strlen(pre)) == 0) {
                        if (count < argcmax) {
                                argv[count] = xwsh_cmd_table[i].name;
                                argl[count] = strlen(xwsh_cmd_table[i].name);
                                count++;
                        }
                }
        }
        
        // Complete external commands
        if (xwsh_ext_cmd_table) {
                for (i = 0; i < xwsh_ext_cmd_table_size; i++) {
                        if (strncmp(pre, xwsh_ext_cmd_table[i].name, strlen(pre)) == 0) {
                                if (count < argcmax) {
                                        argv[count] = xwsh_ext_cmd_table[i].name;
                                        argl[count] = strlen(xwsh_ext_cmd_table[i].name);
                                        count++;
                                }
                        }
                }
        }
        
        *size = count;
        return 0;
}

3. Stream Output Callback (xwsh_cherryrl_sput())

Location: readline.c:91

Function: Sends output generated by CherryRL to standard output.

uint16_t xwsh_cherryrl_sput(chry_readline_t * rl, const void * data, uint16_t size)
{
        int ret;
        const char * buf;
        (void)rl;

        buf = data;
        if (size > 0) {
                ret = fwrite(buf, size, 1, stdout);  // Write to stdout
                if (ret > 0) {
                        fflush(stdout);  // Flush immediately
                } else {
                        ret = 0;
                }
        } else {
                ret = 0;
        }
        return (uint16_t)ret;
}

Features:

  • Supports ANSI escape sequence output
  • Immediately flushes buffer for real-time display
  • Returns the actual number of bytes written

4. Stream Input Callback (xwsh_cherryrl_sget())

Location: readline.c:111

Function: Reads data from standard input for CherryRL processing.

uint16_t xwsh_cherryrl_sget(chry_readline_t * rl, void * data, uint16_t size)
{
        int ret;
        char * buf;
        (void)rl;

        buf = data;
        ret = fread(buf, size, 1, stdin);  // Read from stdin
        if (ret < 0) {
                ret = 0;
        }
        return (uint16_t)ret;
}

Note:

  • Uses blocking reads, waiting for user input
  • Requires terminal support for standard input (e.g. serial port, virtual console)
  • Returns the actual number of bytes read

History Implementation

Ring Buffer Principle

CherryRL uses a ring buffer to store history, with a size of XWSH_RL_HISTORY_MAXSIZE (1024 bytes).

Working Principle:

Initial state: [empty]
Record "cmd1": ["cmd1"]
Record "cmd2": ["cmd1", "cmd2"]
...
Buffer full: Overwrites the oldest record

Operation Modes:

  • Up arrow: Browse the previous command in history
  • Down arrow: Browse the next command in history
  • Enter: Add the current command to history

History Management

// Read a command from history
char * prev_cmd = chry_readline_history_prev(&xwsh_cherryrl);

// Clear history
chry_readline_history_clear(&xwsh_cherryrl);

// Get the history count
uint16_t hist_count = chry_readline_history_count(&xwsh_cherryrl);

Line Reading Interface

xwsh_cherryrl_readline()

Location: readline.c:161

Function: Reads a line of user input, handling editing operations.

char * xwsh_cherryrl_readline(char buffer[])
{
        char linebuf[XWSH_MAXINPUT];  // 128-byte temporary buffer
        char * line;
        xwu16_t linesize;
        char * ret;

        line = chry_readline(&xwsh_cherryrl, linebuf, XWSH_MAXINPUT, &linesize);
        
        if (line == NULL) {
                XwshLogE("chry_readline() ... string error\n");
                ret = NULL;
        } else if (line == (void *)-1) {
                // Cancel input (e.g. Ctrl+C)
                chry_readline_erase_line(&xwsh_cherryrl);
                chry_readline_edit_refresh(&xwsh_cherryrl);
                buffer[0] = '\0';
                ret = buffer;
        } else if (linesize) {
                // Normal input
                memcpy(buffer, line, linesize);
                buffer[linesize] = '\0';
                ret = buffer;
        } else {
                // Empty input
                buffer[0] = '\0';
                ret = NULL;
        }
        
        return ret;
}

Return Value Handling:

Return Value Meaning Handling
NULL Error Log error
(void *)-1 Cancel input Clear line and refresh display
Valid pointer Normal input Copy to buffer and add \0

ANSI Escape Sequence Support

CherryRL supports full ANSI escape sequences. XWSH uses this functionality to implement colored prompts.

Common Escape Sequences

Sequence Function Example
\e[2J Clear entire screen Used by clear command
\e[K Clear from cursor to end of line Used during line editing
\e[0;0H Move cursor to (0,0) Used by clear command
\e[1;32m Green bold text Prompt “xwsh”
\e[1;34m Blue bold text Prompt “xwos”
\e[0m Reset all attributes Prompt suffix

Color Configuration Structure

chry_readline_sgr_t sgr = {
        .foreground = CHRY_READLINE_SGR_GREEN,  // Foreground: green
        .background = CHRY_READLINE_SGR_BLACK,  // Background: black
        .bold = 1,      // Bold
        .italic = 0,    // Not italic
        .underline = 0, // No underline
        .blink = 0,     // Not blinking
        .reverse = 0,   // Not reversed
};
uint32_t sgr_raw = sgr.raw;  // Convert to raw value

Performance Optimization

1. Buffer Reuse

// Static buffers, avoids dynamic allocation
static char xwsh_cherryrl_prompt[XWSH_RL_PROMPT_MAXSIZE];
static char xwsh_cherryrl_history[XWSH_RL_HISTORY_MAXSIZE];

2. Reducing System Calls

  • Use fflush(stdout) to ensure timely display output
  • Batch process input/output to reduce context switches
  • Avoid time-consuming operations in callback functions

3. Memory Footprint Optimization

Component Memory Size Optimization Measure
Prompt buffer 64 bytes Static allocation, avoids fragmentation
History buffer 1024 bytes Ring buffer, fixed size
CherryRL instance ~100 bytes Compact structure member layout
Temporary buffer 128 bytes Stack allocated, auto-released

Debugging Support

Log Output

#include <xwos/lib/xwlog.h>
#define LOGTAG "XWSH.RL"

/* #define XWSH_DBG */
#if defined(XWSH_DBG)
#  define XwshLogD(fmt, ...) xwlogf(D, LOGTAG, fmt, ##__VA_ARGS__)
#else
#  define XwshLogD(fmt, ...)
#endif
#define XwshLogI(fmt, ...) xwlogf(I, LOGTAG, fmt, ##__VA_ARGS__)
#define XwshLogE(fmt, ...) xwlogf(E, LOGTAG, fmt, ##__VA_ARGS__)

Enabling Debug Mode

Uncomment readline.c:23 to enable detailed debug output:

#define XWSH_DBG  // Uncomment to enable debugging

Compatibility Notes

Terminal Requirements

  • ANSI escape sequence support: Most modern terminals support this
  • UTF-8 encoding: Supports multi-byte characters (if needed)
  • Standard input/output: Requires full stdio support

Platform Limitations

  • No dynamic memory: Suitable for resource-constrained embedded environments
  • Single-threaded access: Callback functions are not thread-safe
  • Blocking I/O: Depends on blocking standard input

Extension Suggestions

  1. Custom completion: Implement xwsh_cherryrl_acb() to provide intelligent completion
  2. Color themes: Modify chry_readline_prompt_edit() to adjust color scheme
  3. Multi-line editing: Extend to support multi-line command input
  4. Macros: Add command macros and alias functionality