5
mirror of https://gitlab.com/zephray/glider.git synced 2026-01-11 15:11:13 +00:00
glider/fw/User/shell/shell.c

520 lines
13 KiB
C

//
// Grimoire
// Copyright 2025 Wenting Zhang
//
// Original copyright information:
// Copyright (c) 2022 - Analog Devices Inc. All Rights Reserved.
//
// This file is licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#include "shell.h"
#include "shell_string.h"
#include "shell_platform.h"
#include "shell_printf.h"
#include "linenoise.h"
#include "term.h"
#include "version.h"
#ifdef printf
#undef printf
#endif
#define printf(...) shell_printf(ctx, __VA_ARGS__)
// Shell alternate ' ' char
#define SHELL_ALT_SPACE '\x07'
// Helper macros
#define SHELL_FUNC( func ) extern void func( shell_context_t *ctx, int argc, char **argv )
#define SHELL_HELP( cmd ) \
extern const char shell_help_##cmd[]; \
extern const char shell_help_summary_##cmd[]
#define SHELL_INFO( cmd ) { #cmd, shell_help_summary_##cmd, shell_help_##cmd }
// Command / handler pair structure
typedef struct
{
const char* cmd;
p_shell_handler handler_func;
} SHELL_COMMAND;
// Help data
typedef struct
{
const char *cmd;
const char *help_summary;
const char *help_full;
} SHELL_HELP_DATA;
// Add additional shell commands here
SHELL_FUNC( shell_help );
SHELL_FUNC( shell_ver );
SHELL_FUNC( shell_syslog );
SHELL_FUNC( shell_stacks );
SHELL_FUNC( shell_test );
SHELL_FUNC( shell_i2c_probe );
SHELL_FUNC( shell_recv );
SHELL_FUNC( shell_send );
SHELL_FUNC( shell_df );
SHELL_FUNC( shell_format );
SHELL_FUNC( shell_fdump );
SHELL_FUNC( shell_rm );
SHELL_FUNC( shell_setvolt );
SHELL_FUNC( shell_setcfg );
SHELL_FUNC( shell_sensor );
SHELL_HELP( help );
SHELL_HELP( ver );
SHELL_HELP( syslog );
SHELL_HELP( stacks );
SHELL_HELP( test );
SHELL_HELP( i2c_probe );
SHELL_HELP( recv );
SHELL_HELP( send );
SHELL_HELP( df );
SHELL_HELP( format );
SHELL_HELP( fdump );
SHELL_HELP( rm );
SHELL_HELP( setvolt );
SHELL_HELP( setcfg );
SHELL_HELP( sensor );
//static const SHELL_COMMAND shell_commands[] =
const SHELL_COMMAND shell_commands[] =
{
{ "help", shell_help },
{ "ver", shell_ver },
{ "syslog", shell_syslog },
{ "stacks", shell_stacks },
{ "test", shell_test },
{ "i2c_probe", shell_i2c_probe },
{ "recv", shell_recv },
{ "send", shell_send },
{ "df", shell_df },
{ "format", shell_format },
{ "fdump", shell_fdump },
{ "rm", shell_rm },
{ "setvolt", shell_setvolt },
{ "setcfg", shell_setcfg },
{ "sensor", shell_sensor },
{ "exit", NULL },
{ NULL, NULL }
};
static const SHELL_HELP_DATA shell_help_data[] =
{
SHELL_INFO( help ),
SHELL_INFO( ver ),
SHELL_INFO( syslog ),
SHELL_INFO( stacks ),
SHELL_INFO( test ),
SHELL_INFO( i2c_probe ),
SHELL_INFO( recv ),
SHELL_INFO( send ),
SHELL_INFO( df ),
SHELL_INFO( format ),
SHELL_INFO( fdump ),
SHELL_INFO( rm ),
SHELL_INFO( setvolt ),
SHELL_INFO( setcfg ),
SHELL_INFO( sensor ),
{ NULL, NULL, NULL }
};
// ****************************************************************************
// Built-in help functions
// ****************************************************************************
static void shell_alphabetize(shell_context_t *ctx)
{
bool bSwaped;
bool bSorted;
uint16_t u16HelpTableIdx;
uint16_t u16HelpTableSize;
uint16_t u16HelpTableCurIdx;
uint16_t u16IndicesTableIdx;
uint16_t u16HelpTablePrevIdx;
/* Local Inits */
u16HelpTableIdx = 1U;
bSwaped = false;
bSorted = false;
u16HelpTableSize = sizeof(shell_help_data)/sizeof(SHELL_HELP_DATA);
/* Allocate array based on the size of the help table */
ctx->uHelpIndicies = SHELL_MALLOC(u16HelpTableSize * sizeof(*ctx->uHelpIndicies));
if(ctx->uHelpIndicies != NULL)
{
/* Initialize array */
for(u16IndicesTableIdx = 0U; u16IndicesTableIdx < u16HelpTableSize; u16IndicesTableIdx++)
{
ctx->uHelpIndicies[u16IndicesTableIdx] = u16IndicesTableIdx;
}
/* The last element in the table is intentionally NULL so exclude from alphabetization */
u16HelpTableSize--;
/* Perform alphabetization, sorting the indices in the array */
do
{
/* Find our starting point */
u16HelpTableCurIdx = ctx->uHelpIndicies[u16HelpTableIdx];
u16HelpTablePrevIdx = ctx->uHelpIndicies[u16HelpTableIdx - 1U];
if(strcmp(shell_help_data[u16HelpTableCurIdx].cmd, shell_help_data[u16HelpTablePrevIdx].cmd) < 0)
{
ctx->uHelpIndicies[u16HelpTableIdx] = u16HelpTablePrevIdx;
ctx->uHelpIndicies[u16HelpTableIdx - 1U] = u16HelpTableCurIdx;
bSwaped = true;
}
u16HelpTableIdx++;
/* Exit if we are done sorting - otherwise loop back around again */
if(u16HelpTableIdx == u16HelpTableSize)
{
if(bSwaped == false)
{
bSorted = true;
}
else
{
/* Reset parameters */
bSwaped = false;
u16HelpTableIdx = 1U;
}
}
}while(bSorted == false);
}
}
// 'Help' help data
const char shell_help_help[] = "[<command>]\n"
" [<command>] - the command to get help on.\n"
"Without arguments it shows a summary of all the shell commands.\n";
const char shell_help_summary_help[] = "shell help";
void shell_help( shell_context_t *ctx, int argc, char **argv )
{
const SHELL_HELP_DATA *ph;
uint16_t u16CmdIdx;
uint16_t u16TableIdx;
if( argc > 2 )
{
printf( "Invalid arguments. Type help [<command>] for usage.\n" );
return;
}
ph = shell_help_data;
if( argc == 1 )
{
// List commands and their summary
// It is assumed that a command with an empty summary does not
// actually exist (helpful for conditional compilation)
// Noting that if we could not alphabetize due to constrained memory,
// just print the table as-is.
u16CmdIdx = 0U;
u16TableIdx = (ctx->uHelpIndicies == NULL) ? u16CmdIdx : ctx->uHelpIndicies[u16CmdIdx];
printf( "Shell commands:\n" );
while( 1 )
{
if( ph[u16TableIdx].cmd == NULL )
break;
if( strlen( ph[u16TableIdx].help_summary ) > 0 )
printf( " %-10s - %s\n", ph[u16TableIdx].cmd, ph[u16TableIdx].help_summary );
u16CmdIdx++;
u16TableIdx = (ctx->uHelpIndicies == NULL) ? u16CmdIdx : ctx->uHelpIndicies[u16CmdIdx];
}
printf( "For more information use 'help <command>'.\n" );
}
else
{
while( 1 )
{
if( ph->cmd == NULL )
break;
if( !strcmp( ph->cmd, argv[ 1 ] ) && strlen( ph->help_summary ) > 0 )
{
printf( "%s - %s", ph->cmd, ph->help_summary );
printf( "\n");
printf( "Usage: %s %s", ph->cmd, ph->help_full );
return;
}
ph ++;
}
printf( "Unknown command '%s'.\n", argv[ 1 ] );
}
}
// ****************************************************************************
// Built-in version function
// ****************************************************************************
const char shell_help_ver[] = "\n";
const char shell_help_summary_ver[] = "show version information";
void shell_ver( shell_context_t *ctx, int argc, char **argv )
{
if( argc != 1 )
{
printf( "Invalid arguments. Type help [<command>] for usage.\n" );
return;
}
printf( SHELL_WELCOMEMSG, STR_VERSION, __DATE__, __TIME__);
}
// ****************************************************************************
// Shell functions
// ****************************************************************************
// 'Not implemented' handler for shell comands
void shellh_not_implemented_handler( shell_context_t *ctx, int argc, char **argv )
{
printf( SHELL_ERRMSG );
}
// Executes the given shell command
// 'interactive_mode' is 1 if invoked directly from the interactive shell,
// 0 otherwise
// Returns a pointer to the shell_command that was executed, NULL for error
const SHELL_COMMAND* shellh_execute_command( shell_context_t *ctx, char* cmd, int interactive_mode )
{
char *p, *temp;
const SHELL_COMMAND* pcmd;
int i, inside_quotes;
char quote_char;
int argc;
char *argv[ SHELL_MAX_ARGS ];
if( strlen( cmd ) == 0 )
return NULL;
// Change '\r', '\n' and '\t' chars to ' ' to ease processing
p = cmd;
while( *p )
{
if( *p == '\r' || *p == '\n' || *p == '\t' )
*p = ' ';
p ++;
}
// Transform ' ' characters inside a '' or "" quoted string in
// a 'special' char.
for( i = 0, inside_quotes = 0, quote_char = '\0'; i < strlen( cmd ); i ++ )
if( ( cmd[ i ] == '\'' ) || ( cmd[ i ] == '"' ) )
{
if( !inside_quotes )
{
inside_quotes = 1;
quote_char = cmd[ i ];
}
else
{
if( cmd[ i ] == quote_char )
{
inside_quotes = 0;
quote_char = '\0';
}
}
}
else if( ( cmd[ i ] == ' ' ) && inside_quotes )
cmd[ i ] = SHELL_ALT_SPACE;
if( inside_quotes )
{
printf( "Invalid quoted string\n" );
return NULL;
}
// Transform consecutive sequences of spaces into a single space
p = strchr( cmd, ' ' );
while( p )
{
temp = p + 1;
while( *temp && *temp == ' ' )
memmove( temp, temp + 1, strlen( temp ) );
p = strchr( p + 1, ' ' );
}
if( !strcmp( cmd, " " ) )
return NULL;
// Skip over the trailing space char if it exists
p = cmd + strlen(cmd) - 1;
if( *p == ' ' )
*p = 0;
// Skip over the initial space char if it exists
p = cmd;
if( *p == ' ' )
p ++;
// Compute argc/argv
for( argc = 0; argc < SHELL_MAX_ARGS; argc ++ )
argv[ argc ] = NULL;
argc = 0;
while( ( temp = strchr( p, ' ' ) ) != NULL )
{
if( argc < SHELL_MAX_ARGS)
{
*temp = 0;
argv[ argc ++ ] = p;
p = temp + 1;
}
else
{
break;
}
}
if (argc < SHELL_MAX_ARGS)
{
argv[ argc ++ ] = p;
}
else
{
printf( "Error: too many arguments\n" );
return NULL;
}
// Additional argument processing happens here
for( i = 0; i < argc; i ++ )
{
p = argv[ i ];
// Put back spaces if needed
for( inside_quotes = 0; inside_quotes < strlen( argv[ i ] ); inside_quotes ++ )
{
if( p[ inside_quotes ] == SHELL_ALT_SPACE )
argv[ i ][ inside_quotes ] = ' ';
}
// Remove quotes
if( ( p[ 0 ] == '\'' || p [ 0 ] == '"' ) && ( p[ 0 ] == p[ strlen( p ) - 1 ] ) )
{
argv[ i ] = p + 1;
p[ strlen( p ) - 1 ] = '\0';
}
}
// Match user command with shell's commands
i = 0;
while( 1 )
{
pcmd = shell_commands + i;
if( pcmd->cmd == NULL )
{
printf( SHELL_ERRMSG );
break;
}
if( !strcmp( pcmd->cmd, argv[ 0 ] ) )
{
if( pcmd->handler_func )
pcmd->handler_func( ctx, argc, argv );
break;
}
i ++;
}
return pcmd;
}
void shell_exec( shell_context_t *ctx, const char *command )
{
char *cmd;
unsigned len;
int interactive;
// Make a copy of the command since it gets overwritten
len = strlen(command) + 1;
cmd = SHELL_MALLOC(len);
memcpy(cmd, command, len);
// Save interactive status; set non-interactive
interactive = ctx->interactive;
ctx->interactive = 0;
// Execute the command
shellh_execute_command(ctx, cmd, 0);
// Restore interactive status
ctx->interactive = interactive;
// Free the copy memory
SHELL_FREE(cmd);
}
void shell_start( shell_context_t *ctx )
{
//term_reset_mode(&ctx->t);
//term_sync_size(&ctx->t);
printf("\n");
shellh_execute_command(ctx, "ver", 0);
shell_poll(ctx);
}
void shell_poll( shell_context_t *ctx )
{
const SHELL_COMMAND *pcmd;
int result;
while( 1 )
{
do {
result = linenoise_getline( ctx, ctx->cmd, SHELL_MAX_LINE_LEN - 1, SHELL_PROMPT );
if (result == LINENOISE_CONTINUE) {
return;
}
if (result == LINENOISE_EOF) {
printf( "\n" );
clearerr( stdin );
break;
}
} while (result == LINENOISE_EOF);
if( strlen( ctx->cmd ) == 0 )
continue;
linenoise_addhistory( ctx, ctx->cmd );
pcmd = shellh_execute_command( ctx, ctx->cmd, 1 );
// Check for 'exit' command
if( pcmd && pcmd->cmd && !pcmd->handler_func )
break;
}
}
// Initialize the shell, returning 1 for OK and 0 for error
int shell_init( shell_context_t *ctx, p_term_out term_out, p_term_in term_in, int blocking, void *usr )
{
memset(ctx, 0, sizeof(*ctx));
ctx->blocking = blocking;
ctx->usr = usr;
ctx->interactive = 1;
shell_platform_init(ctx, term_out, term_in);
linenoise_init(ctx);
shell_alphabetize(ctx);
return 1;
}
void shell_deinit( shell_context_t *ctx )
{
if (ctx->uHelpIndicies) {
SHELL_FREE(ctx->uHelpIndicies);
ctx->uHelpIndicies = NULL;
}
linenoise_cleanup(ctx);
shell_platform_deinit(ctx);
}