Extension
Categories:
7 minute read
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:
-
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 }; -
Initialize CherryRL
rc = chry_readline_init(&xwsh_cherryrl, &init); if (0 != rc) { XwshLogE("chry_readline_init() ... <%ld>\n", rc); } -
Set callback functions
chry_readline_set_completion_cb(&xwsh_cherryrl, xwsh_cherryrl_acb); chry_readline_set_user_cb(&xwsh_cherryrl, xwsh_cherryrl_ucb); -
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_F1…F12 |
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
- Custom completion: Implement
xwsh_cherryrl_acb()to provide intelligent completion - Color themes: Modify
chry_readline_prompt_edit()to adjust color scheme - Multi-line editing: Extend to support multi-line command input
- Macros: Add command macros and alias functionality