From 389ce9b41aa6eebf0b469a595845b8fb94b5fb2b Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Wed, 19 Sep 2012 00:09:39 +0300 Subject: [PATCH 01/15] More improvements to the shell (WIP) - file masks ('*' and '?' are now accepted) - recursive operations for ls/cp - new FS-specific functions (src/common_fs.c) --- inc/common.h | 22 +++ inc/newlib/devman.h | 2 + src/common.c | 18 ++ src/common_fs.c | 253 ++++++++++++++++++++++++ src/shell.c | 469 ++++++++++++++++++++++++++++++++++---------- 5 files changed, 658 insertions(+), 106 deletions(-) create mode 100644 src/common_fs.c diff --git a/inc/common.h b/inc/common.h index 6440427b..44913ca3 100644 --- a/inc/common.h +++ b/inc/common.h @@ -6,6 +6,7 @@ #include "elua_int.h" #include "lua.h" #include "platform.h" +#include "devman.h" // Virtual timers data // VTMR_FIRST_ID must be LARGER than PLATFORM_TIMER_SYS_ID (as declared in platform.h) @@ -13,6 +14,20 @@ #define VTMR_GET_ID( x ) ( ( x ) - VTMR_FIRST_ID ) #define TIMER_IS_VIRTUAL( x ) ( ( VTMR_NUM_TIMERS > 0 ) && ( ( x ) >= VTMR_FIRST_ID ) && ( ( x ) < VTMR_NUM_TIMERS + VTMR_FIRST_ID ) ) +// FS interface +#define CMN_FS_INFO_BEFORE_READDIR 0 +#define CMN_FS_INFO_INSIDE_READDIR 1 +#define CMN_FS_INFO_BEFORE_CLOSEDIR 2 +#define CMN_FS_INFO_MEMORY_ERROR 3 +#define CMN_FS_INFO_OPENDIR_FAILED 4 +#define CMN_FS_INFO_READDIR_FAILED 5 +#define CMN_FS_TYPE_DIR 0 +#define CMN_FS_TYPE_FILE 1 +#define CMN_FS_TYPE_PATTERN 2 +#define CMN_FS_TYPE_ERROR 3 + +typedef int ( *p_cmn_fs_walker_cb )( const char*, const struct dm_dirent*, void*, int ); + // Functions exported by the common platform layer void cmn_platform_init(); void cmn_virtual_timer_cb(); @@ -27,10 +42,17 @@ void cmn_systimer_set_interrupt_freq( u32 freq_hz ); void cmn_systimer_set_interrupt_period_us( u32 period ); void cmn_systimer_periodic(); timer_data_type cmn_systimer_get(); +// Filesystem-related functions +int cmn_fs_walkdir( const char *path, p_cmn_fs_walker_cb cb, void *pdata, int recursive ); +char* cmn_fs_split_path( const char *path, const char **pmask ); +int cmn_fs_get_type( const char* path ); +char* cmn_fs_path_join( const char *first, ... ); void cmn_uart_setup_sermux(); unsigned int intlog2( unsigned int v ); +char lastchar( const char *s ); +char firstchar( const char *s ); const char* cmn_str64( u64 x ); void cmn_get_timeout_data( lua_State *L, int pidx, unsigned *pid, timer_data_type *ptimeout ); diff --git a/inc/newlib/devman.h b/inc/newlib/devman.h index 8825994c..734fcd07 100644 --- a/inc/newlib/devman.h +++ b/inc/newlib/devman.h @@ -45,6 +45,8 @@ typedef struct { void *userdata; } DM_DIR; +#define DM_DIRENT_IS_DIR( ent ) ( ( ( ent )->flags & DM_DIRENT_FLAG_DIR ) != 0 ) + // A device structure with pointers to all the device functions typedef int mkdir_mode_t; diff --git a/src/common.c b/src/common.c index 9f7078d9..61e1c4a7 100644 --- a/src/common.c +++ b/src/common.c @@ -573,6 +573,24 @@ unsigned int intlog2( unsigned int v ) return r; } +char lastchar( const char *s ) +{ + unsigned len; + + if( !s ) + return '\0'; + len = strlen( s ); + if( len == 0 ) + return '\0'; + else + return s[ len - 1 ]; +} + +char firstchar( const char *s ) +{ + return s ? s[ 0 ]: '\0'; +} + // 64-bits integer printf support seems to be broken in some versions of Newlib... const char* cmn_str64( u64 x ) { diff --git a/src/common_fs.c b/src/common_fs.c new file mode 100644 index 00000000..eb286afe --- /dev/null +++ b/src/common_fs.c @@ -0,0 +1,253 @@ +// Common file system related operations + +#include "common.h" +#include +#include "devman.h" +#include +#include +#include +#include +#include + +// Returns 1 if 'str' matches the given pattern, 0 otherwise +// '?' matches a single char +// "*" matches one or more chars, NON-GREEDY +static int cmn_pattern_matches( const char *str, const char *pattern ) +{ + int pidx, sidx; + + pidx = sidx = 0; + while( pattern[ pidx ] != 0 ) + { + if( pattern[ pidx ] == '*' ) + { + pidx ++; + // Eat all '?' chars after the '*' + while( pattern[ pidx ] == '?' ) + pidx ++; + if( pattern[ pidx ] == 0 ) // '*' in the last position matches everything + return 1; + while( str[ sidx ] != pattern[ pidx ] && str[ sidx ] != 0 ) + sidx ++; + if( str[ sidx ++ ] == 0 ) + return 0; + } + else if( pattern[ pidx ] == '?' ) + { + if( str[ sidx ++ ] == 0 ) + return 0; + } + else + { + if( pattern[ pidx ] != str[ sidx ++ ] ) + return 0; + } + pidx ++; + } + return str[ sidx ] == 0; +} + +// Splits a path into "directory" and "filemask" name +// Returns a pointer to the directory name. The pointer is dynamically +// allocated and it MUST be freed by the caller! +// Also returns a pointer to the file mask in "pmask" as a side effect +// Returns NULL on error +char *cmn_fs_split_path( const char *path, const char **pmask ) +{ + static const char *all_mask = "*"; + FILE *fp; + + if( !path || strlen( path ) < 2 || path[ 0 ] != '/' ) + return NULL; + // /fs -> always a directory ('/'), mask is * + if( strchr( path + 1, '/' ) == NULL ) + { + *pmask = all_mask; + return strdup( path ); + } + // /fs/dir1/dir2/dirn/ -> always a directory (note the final '/' ), mask is '*' + if( path[ strlen( path ) - 1 ] == '/' ) + { + *pmask = all_mask; + return strndup( path, strlen( path ) - 1 ); + } + // At this point, we don't know if 'path' reffers to a file or to a directory + // So try to open it. If it can be opened, it is a file. Otherwise it is a directory. + // But first check for '*' or '?' chars in path (which means it is definitely a file spec) + *pmask = strrchr( path, '/' ) + 1; + if( strchr( path, '*' ) || strchr( path, '?' ) ) + return strndup( path, *pmask - path - 1 ); + if( ( fp = fopen( path, "r" ) ) != NULL ) + { + fclose( fp ); + return strndup( path, *pmask - path - 1 ); + } + // It is a path, so return it as-is + *pmask = all_mask; + return strdup( path ); +} + +// Returns the type of the path given as argument +// This can be either CMN_FS_TYPE_DIR, CMN_FS_TYPE_FILE or CMN_FS_TYPE_PATTERN +int cmn_fs_get_type( const char *path ) +{ + FILE *fp; + + if( !path || strlen( path ) < 2 || path[ 0 ] != '/' ) + return CMN_FS_TYPE_ERROR; + // /fs -> always a directory ('/'), mask is * + if( strchr( path + 1, '/' ) == NULL ) + return CMN_FS_TYPE_DIR; + // /fs/dir1/dir2/dirn/ -> always a directory (note the final '/' ), mask is '*' + if( path[ strlen( path ) - 1 ] == '/' ) + return CMN_FS_TYPE_DIR; + // At this point, we don't know if 'path' reffers to a file or to a directory + // So try to open it. If it can be opened, it is a file. Otherwise it is a directory. + // But first check for '*' or '?' chars in path (which means it is definitely a file spec) + if( strchr( path, '*' ) || strchr( path, '?' ) ) + return CMN_FS_TYPE_PATTERN; + if( ( fp = fopen( path, "r" ) ) != NULL ) + { + fclose( fp ); + return CMN_FS_TYPE_FILE; + } + return CMN_FS_TYPE_DIR; +} + +// Join the given elements in a single PATH +// The memory for the final buffer is dynamically allocated and MUST be +// freed by the caller +char *cmn_fs_path_join( const char *first, ... ) +{ + unsigned total = 0; + va_list ap; + const char *tmp = first; + char *res; + + va_start( ap, first ); + // Get total length first + while( tmp ) + { + if( strlen( tmp ) > 0 ) + { + total += strlen( tmp ) + ( lastchar( tmp ) == '/' ? 0 : 1 ); + if( firstchar( tmp ) == '/' ) + total --; + } + tmp = va_arg( ap, const char* ); + } + va_end( ap ); + if( total == 0 || ( ( res = ( char* )malloc( total + 2 ) ) == NULL ) ) + return NULL; + strcpy( res, "/" ); + va_start( ap, first ); + tmp = first; + while( tmp ) + { + if( strlen( tmp ) > 0 ) + { + strcat( res, tmp + ( firstchar( tmp ) == '/' ? 1 : 0 ) ); + if( lastchar( res ) != '/' ) + strcat( res, "/" ); + } + tmp = va_arg( ap, const char* ); + } + res[ strlen( res ) - 1 ] = '\0'; + va_end( ap ); + return res; +} + +// This is a generic file/directory walker. It is used by all shell +// operations that have something to do with files (ls, cp, rm...) +// It receives an initial path, which may contain also a pattern specification +// (see cmn_pattern_matches above). If "recursive" is true, it will +// recursively find all files and directories underneath 'path' that match +// the optional pattern in 'path'. When a file is called, the "callback" is +// called. The callback is a function with the following signature: +// void walker_cb( const char *path, const struct dm_entry *ent, void *pdata, int info ); +// 'path' is the full path of the file/directory found by the walker +// 'ent' is the directory entry (as defined in devman.h) +// 'pdata' is the callback state, passed to the callback function exactly +// as given to the walker function. +// 'info' gives more information about why/where the callback was called: +// CMN_FS_INFO_BEFORE_READDIR +// CMN_FS_INFO_INSIDE_READDIR +// CMN_FS_INFO_BEFORE_CLOSEDIR +// CMN_FS_INFO_MEMORY_ERROR -> called before a memory allocation failed +// CMN_FS_INFO_OPENDIR_FAILED +// CMN_FS_INFO_READDIR_FAILED +// The callback can return 0 (stop walking the directory and calling the callback) +// or 1 (keep on calling the callback). If a 0 was returned, this will also be the +// result returned by the walker, otherwise 1 is returned +static int cmn_fs_actual_walkdir( const char *path, const char *pattern, p_cmn_fs_walker_cb cb, void *pdata, int recursive ) +{ + DM_DIR *d; + struct dm_dirent *ent; + int hasdirs, isdir; + char *fullname; + + if( ( d = dm_opendir( path ) ) != NULL ) + { + if( cb( path, NULL, pdata, CMN_FS_INFO_BEFORE_READDIR ) == 0 ) + goto abort; + while( ( ent = dm_readdir( d ) ) != NULL ) + { + isdir = ( ent->flags & DM_DIRENT_FLAG_DIR ) != 0; + if( !cmn_pattern_matches( ent->fname, pattern ) ) + continue; + if( cb( path, ent, pdata, CMN_FS_INFO_INSIDE_READDIR ) == 0 ) + goto abort; + if( isdir ) + hasdirs = 1; + } + if( cb( path, ent, pdata, CMN_FS_INFO_BEFORE_CLOSEDIR ) == 0 ) + goto abort; + dm_closedir( d ); + if( recursive && hasdirs ) + { + if( ( d = dm_opendir( path ) ) != NULL ) + { + while( ( ent = dm_readdir( d ) ) != NULL ) + { + if( ent->flags & DM_DIRENT_FLAG_DIR ) + { + if( ( fullname = cmn_fs_path_join( path, ent->fname, NULL ) ) != NULL ) + { + hasdirs = cmn_fs_actual_walkdir( fullname, pattern, cb, pdata, 1 ); + free( fullname ); + if( hasdirs == 0 ) + goto abort; + } + else + if( cb( path, NULL, pdata, CMN_FS_INFO_MEMORY_ERROR ) == 0 ) + goto abort; + } + } + dm_closedir( d ); + } + else + if( cb( path, NULL, pdata, CMN_FS_INFO_OPENDIR_FAILED ) == 0 ) + return 0; + } + } + else + if( cb( path, NULL, pdata, CMN_FS_INFO_OPENDIR_FAILED ) == 0 ) + return 0; + return 1; +abort: + dm_closedir( d ); + return 0; +} + +int cmn_fs_walkdir( const char *path, p_cmn_fs_walker_cb cb, void *pdata, int recursive ) +{ + char *actpath; + const char *pattern; + + if( ( actpath = cmn_fs_split_path( path, &pattern ) ) == NULL ) + return 0; + cmn_fs_actual_walkdir( actpath, pattern, cb, pdata, recursive ); + free( actpath ); + return 1; +} + diff --git a/src/shell.c b/src/shell.c index 8de8a1b9..26bb42af 100644 --- a/src/shell.c +++ b/src/shell.c @@ -20,6 +20,7 @@ #include #include #include +#include "common.h" #if defined( USE_GIT_REVISION ) #include "git_version.h" @@ -57,7 +58,39 @@ static char* shell_prog; // **************************************************************************** // Shell functions +// ---------------------------------------------------------------------------- +// Helpers + +// Helper: ask yes/no +// Returns 1 for yes, 0 for no +static int shellh_ask_yes_no( const char *prompt ) +{ + int c; + + if( prompt ) + printf( "%s ", prompt ); + while( 1 ) + { + c = term_getch( TERM_INPUT_WAIT ); + if( c == 'y' || c == 'Y' ) + { + printf( "y\n" ); + return 1; + } + if( c == 'n' || c == 'N' ) + { + printf( "n\n" ); + return 0; + } + } + // Will never get here + return 0; +} + + +// ---------------------------------------------------------------------------- // 'help' handler + static void shell_help( int argc, char **argv ) { ( void )argc; @@ -74,7 +107,9 @@ static void shell_help( int argc, char **argv ) printf( " ver - print eLua version\n" ); } +// ---------------------------------------------------------------------------- // 'lua' handler + static void shell_lua( int argc, char **argv ) { printf( "Press " SHELL_EOF_STRING " to exit Lua\n" ); @@ -82,7 +117,9 @@ static void shell_lua( int argc, char **argv ) clearerr( stdin ); } +// ---------------------------------------------------------------------------- // 'recv' handler + static void shell_recv( int argc, char **argv ) { #ifndef BUILD_XMODEM @@ -162,7 +199,9 @@ static void shell_recv( int argc, char **argv ) #endif // #ifndef BUILD_XMODEM } +// ---------------------------------------------------------------------------- // 'ver' handler + static void shell_ver( int argc, char **argv ) { ( void )argc; @@ -171,105 +210,94 @@ static void shell_ver( int argc, char **argv ) printf( "For more information visit www.eluaproject.net and wiki.eluaproject.net\n" ); } -// ls helper: does the recursive path walk for '-R' -static u32 shell_ls_helper( const char *crtname, int recursive, int *phasdirs ) -{ - DM_DIR *d; - u32 total = 0; - struct dm_dirent *ent; - unsigned i; - char *fullname; - int ndirs = 0; +// ---------------------------------------------------------------------------- +// 'ls'/'dir' handler - if( ( d = dm_opendir( crtname ) ) != NULL ) - { - total = 0; - printf( "\n%s", crtname ); - while( ( ent = dm_readdir( d ) ) != NULL ) - { - printf( "\n%s", ent->fname ); - for( i = strlen( ent->fname ); i <= DM_MAX_FNAME_LENGTH; i++ ) - printf( " " ); - if( ent->flags & DM_DIRENT_FLAG_DIR ) - { - printf( "" ); - ndirs = ndirs + 1; - if( phasdirs ) - *phasdirs = 1; - } - else - { - printf( "%u bytes", ( unsigned )ent->fsize ); - total = total + ent->fsize; - } - } - dm_closedir( d ); - printf( "\nTotal on %s: %u bytes\n", crtname, ( unsigned )total ); - if( recursive && ( ndirs > 0 ) ) - { - if( ( d = dm_opendir( crtname ) ) != NULL ) - { - while( ( ent = dm_readdir( d ) ) != NULL ) - { - if( ent->flags & DM_DIRENT_FLAG_DIR ) - { - if( asprintf( &fullname, "%s/%s", crtname, ent->fname ) > 0 ) - { - total += shell_ls_helper( fullname, 1, phasdirs ); - free( fullname ); - } - else - printf( "ERROR: unable to open directory '%s/%s' (not enough memory?)\n", crtname, ent->fname ); - } - } - dm_closedir( d ); - } - } - } - else - printf( "Error: unable to open directory '%s'", crtname ); - return total; -} +// State for walkdir +typedef struct +{ + u32 dir_total; + u32 total; + u8 ndirs; +} SHELL_LS_STATE; // 'ls' and 'dir' handler -// Syntax: ls [dir] [-R] +// Syntax: ls [dir] [-R] +// directory walker callback +static int shellh_ls_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) +{ + SHELL_LS_STATE *ps = ( SHELL_LS_STATE* )pdata; + + switch( info ) + { + case CMN_FS_INFO_BEFORE_READDIR: + ps->dir_total = 0; + printf( "%s\n", path ); + if( ps->ndirs != 0xFF ) + ps->ndirs ++; + break; + + case CMN_FS_INFO_INSIDE_READDIR: + printf( " %-30s", pent->fname ); + if( DM_DIRENT_IS_DIR( pent ) ) + printf( "\n" ); + else + { + printf( "%u bytes\n", ( unsigned )pent->fsize ); + ps->dir_total += pent->fsize; + } + break; + + case CMN_FS_INFO_BEFORE_CLOSEDIR: + printf( "Total on %s: %u bytes\n\n", path, ( unsigned )ps->dir_total ); + ps->total += ps->dir_total; + break; + + case CMN_FS_INFO_OPENDIR_FAILED: + printf( "WARNING: unable to open %s\n", path ); + break; + } + return 1; +} + static void shell_ls( int argc, char **argv ) { const DM_INSTANCE_DATA *pinst; - unsigned dev; - u32 i; - int recursive = 0, hasdirs; + unsigned i; + int recursive = 0; char *pname = NULL; const char *crtname; + SHELL_LS_STATE state; for( i = 1; i < argc; i ++ ) { if( !strcmp( argv[ i ], "-R" ) ) recursive = 1; - else if( argv[ i ][ 0 ] == '/' ) + else if( argv[ i ][ 0 ] == '/' && !pname ) pname = argv[ i ]; else printf( "Warning: ignoring argument '%s' of ls", argv[ i ] ); } // Iterate through all devices, looking for the ones that can do "opendir" // or the ones that match 'pname' (if that is specified) - for( dev = 0; dev < dm_get_num_devices(); dev ++ ) + for( i = 0; i < dm_get_num_devices(); i ++ ) { - pinst = dm_get_instance_at( dev ); + pinst = dm_get_instance_at( i ); if( pinst->pdev->p_opendir_r == NULL || pinst->pdev->p_readdir_r == NULL || pinst->pdev->p_closedir_r == NULL ) continue; if( pname && strncmp( pinst->name, pname, strlen( pinst->name ) ) ) continue; crtname = pname ? pname : pinst->name; - hasdirs = 0; - i = shell_ls_helper( crtname, recursive, &hasdirs ); - if( recursive && hasdirs ) - printf( "\nTotal on %s with all subdirectories: %u bytes\n", crtname, ( unsigned )i ); + memset( &state, 0, sizeof( state ) ); + cmn_fs_walkdir( crtname, shellh_ls_walkdir_cb, &state, recursive ); + if( recursive && ( state.ndirs > 1 ) ) + printf( "Total on %s with all subdirectories: %u bytes\n\n", crtname, ( unsigned )state.total ); } - printf( "\n" ); } +// ---------------------------------------------------------------------------- // 'cat' and 'type' handler + static void shell_cat( int argc, char **argv ) { FILE *fp; @@ -293,69 +321,296 @@ static void shell_cat( int argc, char **argv ) } fclose ( fp ); } - else + else printf( "Unable to open '%s'\n", argv[ i ] ); } } -// 'copy' handler +// ---------------------------------------------------------------------------- +// 'cp' handler + #ifdef BUILD_RFS #define SHELL_COPY_BUFSIZE ( ( 1 << RFS_BUFFER_SIZE ) - ELUARPC_WRITE_REQUEST_EXTRA ) #else #define SHELL_COPY_BUFSIZE 256 #endif -static void shell_cp( int argc, char **argv ) + +// 'cp' flags +#define SHELL_CP_FLAG_RECURSIVE 1 +#define SHELL_CP_FORCE_DESTINATION 2 +#define SHELL_CP_ASK_CONFIRMATION 4 +#define SHELL_CP_SIMULATE_ONLY 8 + +typedef struct +{ + const char *pdestdir; + const char *psrcdir; + u8 flags; +} SHELL_CP_STATE; + +// Helper: copy one file to another file +// Return 1 for success, 0 for error +static int shellh_cp_one_file( const char *psrcname, const char *pdestname, int flags ) { FILE *fps = NULL, *fpd = NULL; - void *buf = NULL; - size_t datalen, datawrote, total = 0; + int res = 0; + char *buf = NULL; + u32 datalen, datawrote, total = 0; - if( argc != 3 ) + // If operation confirmation is enabled, ask the user first + if( flags & SHELL_CP_ASK_CONFIRMATION ) { - printf( "Usage: cp \n" ); - return; + printf( "Copy '%s' to '%s' ? [y/n] ", psrcname, pdestname ); + if( shellh_ask_yes_no( NULL ) == 0 ) + goto done; } - if( ( fps = fopen( argv[ 1 ], "rb" ) ) == NULL ) - printf( "Unable to open %s for reading\n", argv[ 1 ] ); - else + // Open source file + if( ( fps = fopen( psrcname, "r" ) ) == NULL ) { - if( ( fpd = fopen( argv[ 2 ], "wb" ) ) == NULL ) - printf( "Unable to open %s for writing\n", argv[ 2 ] ); - else + printf( "Error: unable to open source file '%s'\n", psrcname ); + goto done; + } + // If the destination exists and we need to ask for confirmation, do it now + if( ( flags & SHELL_CP_FORCE_DESTINATION ) == 0 ) + { + if( ( fpd = fopen( pdestname, "r" ) ) != NULL ) { - // Alloc memory - if( ( buf = malloc( SHELL_COPY_BUFSIZE ) ) == NULL ) - printf( "Not enough memory\n" ); - else - { - // Do the actual copy - while( 1 ) - { - datalen = fread( buf, 1, SHELL_COPY_BUFSIZE, fps ); - datawrote = fwrite( buf, 1, datalen, fpd ); - if( datawrote < datalen ) - { - printf( "Copy error (no space left on target?)\n" ); - break; - } - total += datalen; - if( datalen < SHELL_COPY_BUFSIZE ) - break; - } - fflush( fpd ); - printf( "%u bytes copied\n", ( unsigned int )total ); - } + fclose( fpd ); + fpd = NULL; + printf( "Destination '%s' already exists, are you sure you want to overwrite it ? [y/n] ", pdestname ); + if( shellh_ask_yes_no( NULL ) == 0 ) + goto done; } } + // Allocate buffer + if( ( buf = ( char* )malloc( SHELL_COPY_BUFSIZE ) ) == NULL ) + { + printf( "ERROR: unable to allocate buffer for copy operation.\n" ); + goto done; + } + printf( "Copying '%s' to '%s' ... ", psrcname, pdestname ); + if( ( flags & SHELL_CP_SIMULATE_ONLY ) == 0 ) + { + // Open destination file + if( ( fpd = fopen( pdestname, "w" ) ) == NULL ) + { + printf( "ERROR: unable to open '%s' for writing.\n", pdestname ); + goto done; + } + // Do the actual copy + while( 1 ) + { + datalen = fread( buf, 1, SHELL_COPY_BUFSIZE, fps ); + datawrote = fwrite( buf, 1, datalen, fpd ); + if( datawrote < datalen ) + { + printf( "Copy error (no space left on target?)\n" ); + goto done; + } + total += datalen; + if( datalen < SHELL_COPY_BUFSIZE ) + break; + } + fflush( fpd ); + } + printf( "done (%u bytes).\n", ( unsigned )total ); +done: if( fps ) fclose( fps ); if( fpd ) fclose( fpd ); if( buf ) free( buf ); + return res; } +// copy callback +static int shellh_cp_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) +{ + SHELL_CP_STATE *ps = ( SHELL_CP_STATE* )pdata; + char *tmp = NULL, *tmp2 = NULL; + int res = 1; + DM_DIR *d = NULL; + + switch( info ) + { + case CMN_FS_INFO_BEFORE_READDIR: + if( strstr( path, ps->psrcdir ) != path ) + { + printf( "ERROR: unable to handle directory '%s' (internal error?), aborting.\n", path ); + goto done_err; + } + // Need to create this directory if it does not exist + if( ( tmp = ( char* )cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ), NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + if( ( d = dm_opendir( tmp ) ) != NULL ) + goto done; + printf( "Creating directory %s ... ", tmp ); + if( ( ps->flags & SHELL_CP_SIMULATE_ONLY ) == 0 ) + { + if( mkdir( tmp, 0 ) == -1 ) + { + printf( "ERROR! (aborting).\n" ); + goto done_err; + } + else + printf( "done.\n" ); + } + else + printf( "done.\n" ); + goto done; + + case CMN_FS_INFO_INSIDE_READDIR: + if( !DM_DIRENT_IS_DIR( pent ) ) + { + if( strstr( path, ps->psrcdir ) != path ) + { + printf( "ERROR: unable to handler directory '%s' (internal error?), aborting.\n", path ); + goto done_err; + } + if( ( tmp = cmn_fs_path_join( path, pent->fname, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + if( ( tmp2 = cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ), pent->fname, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + shellh_cp_one_file( tmp, tmp2, ps->flags ); + } + goto done; + + case CMN_FS_INFO_OPENDIR_FAILED: + printf( "ERROR: unable to read directory '%s', aborting.\n", path ); + goto done_err; + + default: + goto done; + } +done_err: + res = 0; +done: + if( tmp ) + free( tmp ); + if( tmp2 ) + free( tmp2 ); + if( d ) + dm_closedir( d ); + return res; +} + +static void shell_cp( int argc, char **argv ) +{ + const char *srcpath = NULL, *dstpath = NULL; + unsigned i, flags = 0; + int srctype, dsttype; + char *srcdir = NULL, *dstdir = NULL, *tmp = NULL; + const char *srcfile, *dstfile; + SHELL_CP_STATE state; + + if( argc < 3 ) + { + printf( "Usage: cp [-R] [-f] [-c]\n" ); + printf( " [-R]: recursive\n" ); + printf( " [-f]: force destination override (default is to ask confirmation).\n" ); + printf( " [-c]: confirm each file copy.\n" ); + printf( " [-s]: simulate only (no actual operation).\n" ); + return; + } + for( i = 1; i < argc; i ++ ) + { + if( !strcmp( argv[ i ], "-R" ) ) + flags |= SHELL_CP_FLAG_RECURSIVE; + else if( !strcmp( argv[ i ], "-f" ) ) + flags |= SHELL_CP_FORCE_DESTINATION; + else if( !strcmp( argv[ i ], "-c" ) ) + flags |= SHELL_CP_ASK_CONFIRMATION; + else if( !strcmp( argv[ i ], "-s" ) ) + flags |= SHELL_CP_SIMULATE_ONLY; + else if( argv[ i ][ 0 ] == '/' ) + { + if( !srcpath ) + srcpath = argv[ i ]; + else if( !dstpath ) + dstpath = argv[ i ]; + else + printf( "WARNING: ignoring argument '%s'\n", argv[ i ] ); + } + else + printf( "WARNING: ignoring argument '%s'\n", argv[ i ] ); + } + if( !srcpath || !dstpath ) + { + printf( "Source and/or destination not specified.\n" ); + return; + } + srctype = cmn_fs_get_type( srcpath ); + if( ( dsttype = cmn_fs_get_type( dstpath ) ) == CMN_FS_TYPE_PATTERN ) + { + printf( "Invalid destination '%s'.\n", dstpath ); + goto done; + } + if( srctype == CMN_FS_TYPE_ERROR || dsttype == CMN_FS_TYPE_ERROR ) + { + printf( "Invalid source and/or destination.\n" ); + return; + } + srcdir = cmn_fs_split_path( srcpath, &srcfile ); + dstdir = cmn_fs_split_path( dstpath, &dstfile ); + // Check valid source/destination combinations + if( srctype == CMN_FS_TYPE_FILE ) + { + if( dsttype == CMN_FS_TYPE_FILE ) // direct file-to-file copy + { + shellh_cp_one_file( srcpath, dstpath, flags ); + goto done; + } + else if( dsttype == CMN_FS_TYPE_DIR ) // copy to destination dir with the same name + { + if( ( tmp = cmn_fs_path_join( dstdir, srcfile, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done; + } + shellh_cp_one_file( srcpath, tmp, flags ); + goto done; + } + else + { + printf( "Invalid arguments.\n" ); + goto done; + } + } + else + { + if( dsttype == CMN_FS_TYPE_FILE ) + { + printf( "Invalid destination '%s'.\n", dstpath ); + goto done; + } + memset( &state, 0, sizeof( state ) ); + state.flags = flags; + state.pdestdir = dstdir; + state.psrcdir = srcdir; + cmn_fs_walkdir( srcpath, shellh_cp_walkdir_cb, &state, flags & SHELL_CP_FLAG_RECURSIVE ); + } +done: + if( srcdir ) + free( srcdir ); + if( dstdir ) + free( dstdir ); + if( tmp ) + free( tmp ); +} + +// ---------------------------------------------------------------------------- // 'wofmt' handler + static void shell_wofmt( int argc, char **argv ) { #ifndef BUILD_WOFS @@ -386,7 +641,9 @@ static void shell_wofmt( int argc, char **argv ) #endif // #ifndef BUILD_WOFS } +// ---------------------------------------------------------------------------- // mkdir handler + static void shell_mkdir( int argc, char **argv ) { if( argc != 2 ) From 7923ad6188e1debe50189bf5f48886927110a0ab Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Mon, 1 Oct 2012 00:30:59 +0300 Subject: [PATCH 02/15] added MMCFS emulation under the simulator --- src/elua_mmc.c | 5 +- src/elua_mmc_sim.c | 218 +++++++++++++++++++++++++++++++ src/platform/sim/platform_conf.h | 1 + 3 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 src/elua_mmc_sim.c diff --git a/src/elua_mmc.c b/src/elua_mmc.c index fe6838da..ec3ffb00 100644 --- a/src/elua_mmc.c +++ b/src/elua_mmc.c @@ -6,7 +6,7 @@ // web site by Jesus Alvarez & James Snyder for eLua. #include "platform_conf.h" -#ifdef BUILD_MMCFS +#if defined( BUILD_MMCFS ) && !defined( ELUA_SIMULATOR ) #include "platform.h" #include "diskio.h" @@ -602,4 +602,5 @@ DWORD get_fattime (void) ; } -#endif /* ifdef BUILD_MMCFS */ +#endif // #if defined( BUILD_MMCFS ) && !defined( ELUA_SIMULATOR ) + diff --git a/src/elua_mmc_sim.c b/src/elua_mmc_sim.c new file mode 100644 index 00000000..eb6a9307 --- /dev/null +++ b/src/elua_mmc_sim.c @@ -0,0 +1,218 @@ +/*-----------------------------------------------------------------------*/ +/* MMC/SD emulator for the eLua simulator */ +/*-----------------------------------------------------------------------*/ + +#include "platform_conf.h" +#if defined( BUILD_MMCFS ) && defined( ELUA_SIMULATOR ) +#include "platform.h" +#include "hostif.h" +#include "diskio.h" +#include + +#define SD_CARD_SIM_NAME "sdcard.img" + +/*-------------------------------------------------------------------------- + Module Private Functions +---------------------------------------------------------------------------*/ + +static volatile +DSTATUS Stat = STA_NOINIT; /* Disk status */ + +static +BYTE PowerFlag = 0; /* indicates if "power" is on */ + +static int fd = -1; +static long imgsize; + +static +void power_on (void) +{ + PowerFlag = 1; +} + +static +void power_off (void) +{ + PowerFlag = 0; +} + +static +int chk_power(void) /* Socket power state: 0=off, 1=on */ +{ + return PowerFlag; +} + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Initialize Disk Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if( drv ) + return STA_NOINIT; + + if( fd != -1 ) + return Stat; + + fd = hostif_open( SD_CARD_SIM_NAME, 2, 0666 ); + + if( fd == -1 ) + printf( "[SDSIM] Unable to open " SD_CARD_SIM_NAME "\n" ); + else + { + Stat = 0; + imgsize = hostif_lseek( fd, 0, SEEK_END ); + hostif_lseek( fd, 0, SEEK_SET ); + printf( "[SDSIM] found SD card image, size=%ld bytes\n", imgsize ); + } + return Stat; +} + +/*-----------------------------------------------------------------------*/ +/* Get Disk Status */ +/*-----------------------------------------------------------------------*/ + + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0) */ +) +{ + if (drv) return STA_NOINIT; /* Supports only single drive */ + return Stat; +} + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE *buff, /* Pointer to the data buffer to store read data */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if( hostif_lseek( fd, sector * 512, SEEK_SET ) == -1 ) + return RES_ERROR; + if( hostif_read( fd, buff, count * 512 ) != count * 512 ) + return RES_ERROR; + return RES_OK; +} + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0) */ + const BYTE *buff, /* Pointer to the data to be written */ + DWORD sector, /* Start sector number (LBA) */ + BYTE count /* Sector count (1..255) */ +) +{ + if (drv || !count) return RES_PARERR; + if (Stat & STA_NOINIT) return RES_NOTRDY; + + if( hostif_lseek( fd, sector * 512, SEEK_SET ) == -1 ) + return RES_ERROR; + if( hostif_write( fd, buff, count * 512 ) != count * 512 ) + return RES_ERROR; + return RES_OK; +} + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + BYTE *ptr = buff; + + if (drv) return RES_PARERR; + res = RES_ERROR; + + if (ctrl == CTRL_POWER) { + switch (*ptr) { + case 0: /* Sub control code == 0 (POWER_OFF) */ + if (chk_power()) + power_off(); /* Power off */ + res = RES_OK; + break; + case 1: /* Sub control code == 1 (POWER_ON) */ + power_on(); /* Power on */ + res = RES_OK; + break; + case 2: /* Sub control code == 2 (POWER_GET) */ + *(ptr+1) = (BYTE)chk_power(); + res = RES_OK; + break; + default : + res = RES_PARERR; + } + } + else { + if (Stat & STA_NOINIT) return RES_NOTRDY; + + switch (ctrl) { + case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ + *(DWORD*)buff = imgsize / 512; + res = RES_OK; + break; + + case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */ + *(WORD*)buff = 512; + res = RES_OK; + break; + + case CTRL_SYNC : /* Make sure that data has been written */ + res = RES_OK; + break; + + default: + printf( "[SDSIM] unhandled IOCTL %d\n", ctrl ); + res = RES_PARERR; + } + } + + return res; +} + + +/*---------------------------------------------------------*/ +/* User Provided Timer Function for FatFs module */ +/*---------------------------------------------------------*/ +/* This is a real time clock service to be called from */ +/* FatFs module. Any valid time must be returned even if */ +/* the system does not support a real time clock. */ + +DWORD get_fattime (void) +{ + + return ((2007UL-1980) << 25) // Year = 2007 + | (6UL << 21) // Month = June + | (5UL << 16) // Day = 5 + | (11U << 11) // Hour = 11 + | (38U << 5) // Min = 38 + | (0U >> 1) // Sec = 0 + ; + +} +#endif // #if defined( BUILD_MMCFS ) && defined( ELUA_SIMULATOR ) + diff --git a/src/platform/sim/platform_conf.h b/src/platform/sim/platform_conf.h index 7677a580..7780aa7b 100644 --- a/src/platform/sim/platform_conf.h +++ b/src/platform/sim/platform_conf.h @@ -17,6 +17,7 @@ #define BUILD_TERM //#define BUILD_RFS #define BUILD_WOFS +#define BUILD_MMCFS #define TERM_LINES 25 #define TERM_COLS 80 From 2402627a109df21def69552276e7aa9a1aee20cd Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Wed, 26 Sep 2012 22:07:59 +0300 Subject: [PATCH 03/15] Adding rm and rmdir commands to the shell (WIP) --- inc/common.h | 5 +- inc/newlib/devman.h | 2 + src/common_fs.c | 29 ++++++- src/mmcfs.c | 9 ++- src/newlib/genstd.c | 4 +- src/newlib/stdtcp.c | 4 +- src/newlib/stubs.c | 64 +++++++++++++-- src/remotefs/elua_rfs.c | 4 +- src/romfs.c | 4 +- src/semifs.c | 4 +- src/shell.c | 167 +++++++++++++++++++++++++++++++++++----- 11 files changed, 260 insertions(+), 36 deletions(-) diff --git a/inc/common.h b/inc/common.h index 44913ca3..20ca4f5f 100644 --- a/inc/common.h +++ b/inc/common.h @@ -17,10 +17,12 @@ // FS interface #define CMN_FS_INFO_BEFORE_READDIR 0 #define CMN_FS_INFO_INSIDE_READDIR 1 -#define CMN_FS_INFO_BEFORE_CLOSEDIR 2 +#define CMN_FS_INFO_AFTER_CLOSEDIR 2 #define CMN_FS_INFO_MEMORY_ERROR 3 #define CMN_FS_INFO_OPENDIR_FAILED 4 #define CMN_FS_INFO_READDIR_FAILED 5 +#define CMN_FS_INFO_DIRECTORY_DONE 6 + #define CMN_FS_TYPE_DIR 0 #define CMN_FS_TYPE_FILE 1 #define CMN_FS_TYPE_PATTERN 2 @@ -47,6 +49,7 @@ int cmn_fs_walkdir( const char *path, p_cmn_fs_walker_cb cb, void *pdata, int re char* cmn_fs_split_path( const char *path, const char **pmask ); int cmn_fs_get_type( const char* path ); char* cmn_fs_path_join( const char *first, ... ); +int cmn_fs_is_root_dir( const char *path ); void cmn_uart_setup_sermux(); diff --git a/inc/newlib/devman.h b/inc/newlib/devman.h index 734fcd07..2fcd1dcc 100644 --- a/inc/newlib/devman.h +++ b/inc/newlib/devman.h @@ -62,6 +62,8 @@ typedef struct int ( *p_closedir_r )( struct _reent *r, void* dir, void *pdata ); const char* ( *p_getaddr_r )( struct _reent *r, int fd, void *pdata ); int ( *p_mkdir_r )( struct _reent *r, const char *pathname, mkdir_mode_t mode, void *pdata ); + int ( *p_unlink_r )( struct _reent *r, const char *fname, void *pdata ); + int ( *p_rmdir_r )( struct _reent *r, const char *fname, void *pdata ); } DM_DEVICE; // Additional registration data for each FS (per FS instance) diff --git a/src/common_fs.c b/src/common_fs.c index eb286afe..88807452 100644 --- a/src/common_fs.c +++ b/src/common_fs.c @@ -89,6 +89,10 @@ char *cmn_fs_split_path( const char *path, const char **pmask ) // Returns the type of the path given as argument // This can be either CMN_FS_TYPE_DIR, CMN_FS_TYPE_FILE or CMN_FS_TYPE_PATTERN +// Note that CMN_FS_TYPE_DIR is NOT actually checked. A name with a '/' in the +// last position or a name with a single '/' (for example /rom/ and /rom respectively) +// is assumed to be a directory. Otherwise, if the 'path' is not a mask and also +// not a regular file, it is assumed to be a directory int cmn_fs_get_type( const char *path ) { FILE *fp; @@ -172,8 +176,9 @@ char *cmn_fs_path_join( const char *first, ... ) // 'info' gives more information about why/where the callback was called: // CMN_FS_INFO_BEFORE_READDIR // CMN_FS_INFO_INSIDE_READDIR -// CMN_FS_INFO_BEFORE_CLOSEDIR -// CMN_FS_INFO_MEMORY_ERROR -> called before a memory allocation failed +// CMN_FS_INFO_AFTER_CLOSEDIR +// CMN_FS_INFO_DIRECTORY_DONE +// CMN_FS_INFO_MEMORY_ERROR -> called after a memory allocation failed // CMN_FS_INFO_OPENDIR_FAILED // CMN_FS_INFO_READDIR_FAILED // The callback can return 0 (stop walking the directory and calling the callback) @@ -200,9 +205,9 @@ static int cmn_fs_actual_walkdir( const char *path, const char *pattern, p_cmn_f if( isdir ) hasdirs = 1; } - if( cb( path, ent, pdata, CMN_FS_INFO_BEFORE_CLOSEDIR ) == 0 ) - goto abort; dm_closedir( d ); + if( cb( path, ent, pdata, CMN_FS_INFO_AFTER_CLOSEDIR ) == 0 ) + goto abort; if( recursive && hasdirs ) { if( ( d = dm_opendir( path ) ) != NULL ) @@ -229,6 +234,8 @@ static int cmn_fs_actual_walkdir( const char *path, const char *pattern, p_cmn_f if( cb( path, NULL, pdata, CMN_FS_INFO_OPENDIR_FAILED ) == 0 ) return 0; } + if( cb( path, ent, pdata, CMN_FS_INFO_DIRECTORY_DONE ) == 0 ) + goto abort; } else if( cb( path, NULL, pdata, CMN_FS_INFO_OPENDIR_FAILED ) == 0 ) @@ -251,3 +258,17 @@ int cmn_fs_walkdir( const char *path, p_cmn_fs_walker_cb cb, void *pdata, int re return 1; } +int cmn_fs_is_root_dir( const char *path ) +{ + + if( !path ) + return 0; + if( path[ 0 ] != '/' ) + return 0; + if( ( path = strchr( path + 1, '/' ) ) == NULL ) + return 1; + if( *( path + 1 ) == 0 ) + return 1; + return 0; +} + diff --git a/src/mmcfs.c b/src/mmcfs.c index a8e54e45..8f82e560 100644 --- a/src/mmcfs.c +++ b/src/mmcfs.c @@ -275,6 +275,11 @@ static int mmcfs_mkdir_r( struct _reent *r, const char *name, mkdir_mode_t mode, return f_mkdir( name ); } +static int mmcfs_unlink_r( struct _reent *r, const char *fname, void *pdata ) +{ + return f_unlink( fname ); +} + // MMC device descriptor structure static const DM_DEVICE mmcfs_device = { @@ -287,7 +292,9 @@ static const DM_DEVICE mmcfs_device = mmcfs_readdir_r, // readdir mmcfs_closedir_r, // closedir NULL, // getaddr - mmcfs_mkdir_r // mkdir + mmcfs_mkdir_r, // mkdir + mmcfs_unlink_r, // unlink + mmcfs_unlink_r // rmdir }; int mmcfs_init() diff --git a/src/newlib/genstd.c b/src/newlib/genstd.c index 1491e3d7..667f05ef 100644 --- a/src/newlib/genstd.c +++ b/src/newlib/genstd.c @@ -143,7 +143,9 @@ static const DM_DEVICE std_device = NULL, // readdir NULL, // closedir NULL, // getaddr - NULL // mkdir + NULL, // mkdir + NULL, // unlink + NULL // rmdir }; int std_register() diff --git a/src/newlib/stdtcp.c b/src/newlib/stdtcp.c index 62fa7f6f..4f816772 100644 --- a/src/newlib/stdtcp.c +++ b/src/newlib/stdtcp.c @@ -94,7 +94,9 @@ static const DM_DEVICE std_device = NULL, // readdir NULL, // closedir NULL, // getaddr - NULL // mkdir + NULL, // mkdir + NULL, // unlink + NULL // rmdir }; diff --git a/src/newlib/stubs.c b/src/newlib/stubs.c index 342ce460..07e6dff6 100644 --- a/src/newlib/stubs.c +++ b/src/newlib/stubs.c @@ -186,7 +186,7 @@ _ssize_t _write_r( struct _reent *r, int file, const void *ptr, size_t len ) int _mkdir_r( struct _reent *r, const char *path, mkdir_mode_t mode ) { char* actname; - int res, devid; + int devid; const DM_INSTANCE_DATA *pinst; // Look for device, return error if not found or if function not implemented @@ -211,6 +211,62 @@ int mkdir( const char *path, mode_t mode ) return _mkdir_r( _REENT, path, mode ); } +// **************************************************************************** +// _unlink_r +int _unlink_r( struct _reent *r, const char *fname ) +{ + char* actname; + int devid; + const DM_INSTANCE_DATA *pinst; + + // Look for device, return error if not found or if function not implemented + if( ( devid = find_dm_entry( fname, &actname ) ) == -1 ) + { + r->_errno = ENODEV; + return -1; + } + pinst = dm_get_instance_at( devid ); + if( pinst->pdev->p_unlink_r == NULL ) + { + r->_errno = ENOSYS; + return -1; + } + + // Device found, call its function + return pinst->pdev->p_unlink_r( r, actname, pinst->pdata ); +} + +int unlink( const char *path ) +{ + return _unlink_r( _REENT, path ); +} + +// **************************************************************************** +// rmdir + +int rmdir( const char *path ) +{ + char* actname; + int devid; + const DM_INSTANCE_DATA *pinst; + + // Look for device, return error if not found or if function not implemented + if( ( devid = find_dm_entry( path, &actname ) ) == -1 ) + { + _REENT->_errno = ENODEV; + return -1; + } + pinst = dm_get_instance_at( devid ); + if( pinst->pdev->p_rmdir_r == NULL ) + { + _REENT->_errno = ENOSYS; + return -1; + } + + // Device found, call its function + return pinst->pdev->p_rmdir_r( _REENT, actname, pinst->pdata ); +} + // **************************************************************************** // Miscellaneous functions @@ -245,12 +301,6 @@ clock_t _times_r( struct _reent* r, struct tms *buf ) return 0; } -int _unlink_r( struct _reent *r, const char *name ) -{ - r->_errno = ENOSYS; - return -1; -} - int _link_r( struct _reent *r, const char *c1, const char *c2 ) { r->_errno = ENOSYS; diff --git a/src/remotefs/elua_rfs.c b/src/remotefs/elua_rfs.c index 399d65da..a35d3d5c 100644 --- a/src/remotefs/elua_rfs.c +++ b/src/remotefs/elua_rfs.c @@ -181,7 +181,9 @@ static const DM_DEVICE rfs_device = rfs_readdir_r, // readdir rfs_closedir_r, // closedir NULL, // getaddr - NULL // mkdir + NULL, // mkdir + NULL, // unlink + NULL // rmdir }; int remotefs_init() diff --git a/src/romfs.c b/src/romfs.c index cd4e3ad5..be13fdbd 100644 --- a/src/romfs.c +++ b/src/romfs.c @@ -427,7 +427,9 @@ static const DM_DEVICE romfs_device = romfs_readdir_r, // readdir romfs_closedir_r, // closedir romfs_getaddr_r, // getaddr - NULL // mkdir + NULL, // mkdir + NULL, // unlink + NULL // rmdir }; // **************************************************************************** diff --git a/src/semifs.c b/src/semifs.c index b9f61e30..db061a9d 100644 --- a/src/semifs.c +++ b/src/semifs.c @@ -259,7 +259,9 @@ static const DM_DEVICE semifs_device = semifs_readdir_r, // readdir semifs_closedir_r, // closedir NULL, // getaddr - NULL // mkdir + NULL, // mkdir + NULL, // unlink + NULL // rmdir }; int semifs_init() diff --git a/src/shell.c b/src/shell.c index 26bb42af..61bbd484 100644 --- a/src/shell.c +++ b/src/shell.c @@ -55,6 +55,12 @@ typedef struct // Shell data static char* shell_prog; +// Flags for various operations +#define SHELL_F_RECURSIVE 1 +#define SHELL_F_FORCE_DESTINATION 2 +#define SHELL_F_ASK_CONFIRMATION 4 +#define SHELL_F_SIMULATE_ONLY 8 + // **************************************************************************** // Shell functions @@ -248,7 +254,7 @@ static int shellh_ls_walkdir_cb( const char *path, const struct dm_dirent *pent, } break; - case CMN_FS_INFO_BEFORE_CLOSEDIR: + case CMN_FS_INFO_AFTER_CLOSEDIR: printf( "Total on %s: %u bytes\n\n", path, ( unsigned )ps->dir_total ); ps->total += ps->dir_total; break; @@ -335,16 +341,11 @@ static void shell_cat( int argc, char **argv ) #define SHELL_COPY_BUFSIZE 256 #endif -// 'cp' flags -#define SHELL_CP_FLAG_RECURSIVE 1 -#define SHELL_CP_FORCE_DESTINATION 2 -#define SHELL_CP_ASK_CONFIRMATION 4 -#define SHELL_CP_SIMULATE_ONLY 8 - typedef struct { const char *pdestdir; const char *psrcdir; + const char *dloc; u8 flags; } SHELL_CP_STATE; @@ -357,8 +358,13 @@ static int shellh_cp_one_file( const char *psrcname, const char *pdestname, int char *buf = NULL; u32 datalen, datawrote, total = 0; + if( !strcasecmp( psrcname, pdestname ) ) + { + printf( "Cannot copy '%s' into itself.\n", psrcname ); + goto done; + } // If operation confirmation is enabled, ask the user first - if( flags & SHELL_CP_ASK_CONFIRMATION ) + if( flags & SHELL_F_ASK_CONFIRMATION ) { printf( "Copy '%s' to '%s' ? [y/n] ", psrcname, pdestname ); if( shellh_ask_yes_no( NULL ) == 0 ) @@ -371,7 +377,7 @@ static int shellh_cp_one_file( const char *psrcname, const char *pdestname, int goto done; } // If the destination exists and we need to ask for confirmation, do it now - if( ( flags & SHELL_CP_FORCE_DESTINATION ) == 0 ) + if( ( flags & SHELL_F_FORCE_DESTINATION ) == 0 ) { if( ( fpd = fopen( pdestname, "r" ) ) != NULL ) { @@ -389,7 +395,7 @@ static int shellh_cp_one_file( const char *psrcname, const char *pdestname, int goto done; } printf( "Copying '%s' to '%s' ... ", psrcname, pdestname ); - if( ( flags & SHELL_CP_SIMULATE_ONLY ) == 0 ) + if( ( flags & SHELL_F_SIMULATE_ONLY ) == 0 ) { // Open destination file if( ( fpd = fopen( pdestname, "w" ) ) == NULL ) @@ -441,7 +447,7 @@ static int shellh_cp_walkdir_cb( const char *path, const struct dm_dirent *pent, goto done_err; } // Need to create this directory if it does not exist - if( ( tmp = ( char* )cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ), NULL ) ) == NULL ) + if( ( tmp = ( char* )cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ) - strlen( ps->dloc ), NULL ) ) == NULL ) { printf( "Not enough memory.\n" ); goto done_err; @@ -449,7 +455,7 @@ static int shellh_cp_walkdir_cb( const char *path, const struct dm_dirent *pent, if( ( d = dm_opendir( tmp ) ) != NULL ) goto done; printf( "Creating directory %s ... ", tmp ); - if( ( ps->flags & SHELL_CP_SIMULATE_ONLY ) == 0 ) + if( ( ps->flags & SHELL_F_SIMULATE_ONLY ) == 0 ) { if( mkdir( tmp, 0 ) == -1 ) { @@ -476,7 +482,7 @@ static int shellh_cp_walkdir_cb( const char *path, const struct dm_dirent *pent, printf( "Not enough memory.\n" ); goto done_err; } - if( ( tmp2 = cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ), pent->fname, NULL ) ) == NULL ) + if( ( tmp2 = cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ) - strlen( ps->dloc ), pent->fname, NULL ) ) == NULL ) { printf( "Not enough memory.\n" ); goto done_err; @@ -525,13 +531,13 @@ static void shell_cp( int argc, char **argv ) for( i = 1; i < argc; i ++ ) { if( !strcmp( argv[ i ], "-R" ) ) - flags |= SHELL_CP_FLAG_RECURSIVE; + flags |= SHELL_F_RECURSIVE; else if( !strcmp( argv[ i ], "-f" ) ) - flags |= SHELL_CP_FORCE_DESTINATION; + flags |= SHELL_F_FORCE_DESTINATION; else if( !strcmp( argv[ i ], "-c" ) ) - flags |= SHELL_CP_ASK_CONFIRMATION; + flags |= SHELL_F_ASK_CONFIRMATION; else if( !strcmp( argv[ i ], "-s" ) ) - flags |= SHELL_CP_SIMULATE_ONLY; + flags |= SHELL_F_SIMULATE_ONLY; else if( argv[ i ][ 0 ] == '/' ) { if( !srcpath ) @@ -597,7 +603,10 @@ static void shell_cp( int argc, char **argv ) state.flags = flags; state.pdestdir = dstdir; state.psrcdir = srcdir; - cmn_fs_walkdir( srcpath, shellh_cp_walkdir_cb, &state, flags & SHELL_CP_FLAG_RECURSIVE ); + state.dloc = strchr( srcdir + 1, '/' ); + if( state.dloc == NULL ) + state.dloc = ""; + cmn_fs_walkdir( srcpath, shellh_cp_walkdir_cb, &state, flags & SHELL_F_RECURSIVE ); } done: if( srcdir ) @@ -655,6 +664,127 @@ static void shell_mkdir( int argc, char **argv ) printf( "Error creating directory '%s'\n", argv[ 1 ] ); } +// ---------------------------------------------------------------------------- +// rm handler + +// helper: remove a single file and/or directory +static void shellh_rm_one( const char* path, int flags ) +{ + int ftype = cmn_fs_get_type( path ); + int res = 0; + + if( flags & SHELL_F_ASK_CONFIRMATION ) + { + printf( "Are you sure you want to remove %s ? [y/n] ", path ); + if( shellh_ask_yes_no( NULL ) == 0 ) + return; + } + if( ( flags & SHELL_F_SIMULATE_ONLY ) == 0 ) + { + if( ftype == CMN_FS_TYPE_FILE ) + res = unlink( path ); + else if( ftype == CMN_FS_TYPE_DIR ) + res = rmdir( path ); + else + { + printf( "WARNING: invalid argument '%s'\n", path ); + return; + } + } + if( res ) + printf( "WARNING: unable to remove %s\n", path ); + else + printf( "Removed %s\n", path ); +} + +static int shellh_rm_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) +{ + u8 *pflags = ( u8* )pdata; + char *tmp = NULL; + int res = 1; + + switch( info ) + { + case CMN_FS_INFO_BEFORE_READDIR: + goto done; + + case CMN_FS_INFO_INSIDE_READDIR: + if( ( tmp = cmn_fs_path_join( path, pent->fname, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + if( cmn_fs_get_type( tmp ) == CMN_FS_TYPE_FILE ) + shellh_rm_one( tmp, *pflags ); + goto done; + + case CMN_FS_INFO_OPENDIR_FAILED: + printf( "ERROR: unable to read directory '%s', aborting.\n", path ); + goto done_err; + + case CMN_FS_INFO_DIRECTORY_DONE: + if( *pflags & SHELL_F_RECURSIVE && !cmn_fs_is_root_dir( path ) ) + shellh_rm_one( path, *pflags ); + goto done; + + default: + goto done; + } +done_err: + res = 0; +done: + if( tmp ) + free( tmp ); + return res; +} + +static void shell_rm( int argc, char **argv ) +{ + const char *fmask = NULL; + unsigned i, flags = 0; + int masktype; + + if( argc < 2 ) + { + printf( "Usage: rm [-R] [-c] [-s]\n" ); + printf( " -R: remove recursively.\n" ); + printf( " -c: confirm each remove.\n" ); + printf( " -s: simulate only (no actual operation).\n" ); + return; + } + for( i = 1; i < argc; i ++ ) + { + if( argv[ i ][ 0 ] == '/' ) + { + if( !fmask ) + fmask = argv[ i ]; + else + printf( "Warning: ignoring argument '%s'\n", argv[ i ] ); + } + else if( !strcmp( argv[ i ], "-R" ) ) + flags |= SHELL_F_RECURSIVE; + else if( !strcmp( argv[ i ], "-c" ) ) + flags |= SHELL_F_ASK_CONFIRMATION; + else if( !strcmp( argv[ i ], "-s" ) ) + flags |= SHELL_F_SIMULATE_ONLY; + else + printf( "Warning: ignoring argument '%s'\n", argv[ i ] ); + } + if( !fmask ) + { + printf( "rm target not specified.\n" ); + return; + } + masktype = cmn_fs_get_type( fmask ); + if( masktype == CMN_FS_TYPE_FILE ) + shellh_rm_one( fmask, flags ); + else + cmn_fs_walkdir( fmask, shellh_rm_walkdir_cb, &flags, flags & SHELL_F_RECURSIVE ); +} + +// **************************************************************************** +// Public interface + // Insert shell commands here static const SHELL_COMMAND shell_commands[] = { @@ -670,6 +800,7 @@ static const SHELL_COMMAND shell_commands[] = { "cp", shell_cp }, { "wofmt", shell_wofmt }, { "mkdir", shell_mkdir }, + { "rm", shell_rm }, { NULL, NULL } }; From e42b88561ac5b1bc88def258579a55e4fe48d552 Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Sun, 7 Oct 2012 23:54:28 +0300 Subject: [PATCH 04/15] 'mv' command and shell code refactoring There is a new command in the shell ('mv' - move/rename files). Also, the shell code was refactored into its own directory (src/shell). The 'help' command in the shell was updated and supports 'help '. There is a new BUILD_ADVANCED_SHELL build time macro that enables the advanced shell features (currently 'cp' with recursion (and other improvements), 'mv' and 'rm'). --- .gitignore | 3 + inc/common.h | 3 + inc/newlib/devman.h | 1 + inc/shell.h | 27 +- inc/validate.h | 5 + src/common_fs.c | 21 + src/mmcfs.c | 8 +- src/newlib/genstd.c | 3 +- src/newlib/stdtcp.c | 3 +- src/newlib/stubs.c | 58 ++ src/platform/sim/platform_conf.h | 1 + src/remotefs/elua_rfs.c | 3 +- src/romfs.c | 3 +- src/semifs.c | 3 +- src/shell.c | 984 ------------------------------- src/shell/shell.c | 405 +++++++++++++ src/shell/shell_adv_cp_mv.c | 294 +++++++++ src/shell/shell_adv_rm.c | 153 +++++ src/shell/shell_cat.c | 47 ++ src/shell/shell_help.c | 113 ++++ src/shell/shell_ls.c | 100 ++++ src/shell/shell_lua.c | 36 ++ src/shell/shell_mkdir.c | 29 + src/shell/shell_recv.c | 106 ++++ src/shell/shell_ver.c | 34 ++ src/shell/shell_wofmt.c | 52 ++ 26 files changed, 1500 insertions(+), 995 deletions(-) delete mode 100644 src/shell.c create mode 100644 src/shell/shell.c create mode 100644 src/shell/shell_adv_cp_mv.c create mode 100644 src/shell/shell_adv_rm.c create mode 100644 src/shell/shell_cat.c create mode 100644 src/shell/shell_help.c create mode 100644 src/shell/shell_ls.c create mode 100644 src/shell/shell_lua.c create mode 100644 src/shell/shell_mkdir.c create mode 100644 src/shell/shell_recv.c create mode 100644 src/shell/shell_ver.c create mode 100644 src/shell/shell_wofmt.c diff --git a/.gitignore b/.gitignore index fca85992..807ab5dd 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ inc/git_version.h *~ *.*~ luac.cross* +sdcard.img +core + diff --git a/inc/common.h b/inc/common.h index 20ca4f5f..64c9e1a6 100644 --- a/inc/common.h +++ b/inc/common.h @@ -27,6 +27,8 @@ #define CMN_FS_TYPE_FILE 1 #define CMN_FS_TYPE_PATTERN 2 #define CMN_FS_TYPE_ERROR 3 +#define CMN_FS_TYPE_FILE_NOT_FOUND 4 +#define CMN_FS_TYPE_DIR_NOT_FOUND 5 typedef int ( *p_cmn_fs_walker_cb )( const char*, const struct dm_dirent*, void*, int ); @@ -50,6 +52,7 @@ char* cmn_fs_split_path( const char *path, const char **pmask ); int cmn_fs_get_type( const char* path ); char* cmn_fs_path_join( const char *first, ... ); int cmn_fs_is_root_dir( const char *path ); +int cmn_fs_check_directory( const char *path ); void cmn_uart_setup_sermux(); diff --git a/inc/newlib/devman.h b/inc/newlib/devman.h index 2fcd1dcc..04bc1216 100644 --- a/inc/newlib/devman.h +++ b/inc/newlib/devman.h @@ -64,6 +64,7 @@ typedef struct int ( *p_mkdir_r )( struct _reent *r, const char *pathname, mkdir_mode_t mode, void *pdata ); int ( *p_unlink_r )( struct _reent *r, const char *fname, void *pdata ); int ( *p_rmdir_r )( struct _reent *r, const char *fname, void *pdata ); + int ( *p_rename_r )( struct _reent *r, const char *oldname, const char *newname, void *pdata ); } DM_DEVICE; // Additional registration data for each FS (per FS instance) diff --git a/inc/shell.h b/inc/shell.h index f38f6ca3..a437606c 100644 --- a/inc/shell.h +++ b/inc/shell.h @@ -3,13 +3,30 @@ #ifndef __SHELL_H__ #define __SHELL_H__ -#define SHELL_WELCOMEMSG "\neLua %s Copyright (C) 2007-2011 www.eluaproject.net\n" -#define SHELL_PROMPT "eLua# " -#define SHELL_ERRMSG "Invalid command, type 'help' for help\n" -#define SHELL_MAXSIZE 50 -#define SHELL_MAX_LUA_ARGS 8 +#define SHELL_WELCOMEMSG "\neLua %s Copyright (C) 2007-2011 www.eluaproject.net\n" +#define SHELL_PROMPT "eLua# " +#define SHELL_ERRMSG "Invalid command, type 'help' for help\n" +#define SHELL_MAXSIZE 50 +#define SHELL_MAX_LUA_ARGS 8 int shell_init(); void shell_start(); +int shellh_cp_file( const char *src, const char *dst, int flags ); +void shellh_not_implemented_handler( int argc, char **argv ); +void shellh_show_help( const char *cmd, const char *helptext ); + +#define SHELL_SHOW_HELP( cmd ) shellh_show_help( #cmd, shell_help_##cmd ) + +// Helpers for various functions +int shellh_ask_yes_no(); + +// Flags for various operations +#define SHELL_F_RECURSIVE 1 +#define SHELL_F_FORCE_DESTINATION 2 +#define SHELL_F_ASK_CONFIRMATION 4 +#define SHELL_F_SIMULATE_ONLY 8 +#define SHELL_F_SILENT 16 +#define SHELL_F_MOVE 32 #endif // #ifndef __SHELL_H__ + diff --git a/inc/validate.h b/inc/validate.h index e94b0347..b6b08b1d 100644 --- a/inc/validate.h +++ b/inc/validate.h @@ -87,6 +87,11 @@ #error "Console buffering needs CON_UART_ID defined to the UART ID of the console device" #endif #endif + +// BUILD_ADVANCED_SHELL needs BUILD_SHELL +#if defined( BUILD_ADVANCED_SHELL ) && !defined( BUILD_SHELL ) + #error "BUILD_ADVANCED_SHELL needs BUILD_SHELL" +#endif #endif // #ifndef __VALIDATE_H__ diff --git a/src/common_fs.c b/src/common_fs.c index 88807452..adfa6994 100644 --- a/src/common_fs.c +++ b/src/common_fs.c @@ -96,6 +96,7 @@ char *cmn_fs_split_path( const char *path, const char **pmask ) int cmn_fs_get_type( const char *path ) { FILE *fp; + DM_DIR *d; if( !path || strlen( path ) < 2 || path[ 0 ] != '/' ) return CMN_FS_TYPE_ERROR; @@ -104,7 +105,12 @@ int cmn_fs_get_type( const char *path ) return CMN_FS_TYPE_DIR; // /fs/dir1/dir2/dirn/ -> always a directory (note the final '/' ), mask is '*' if( path[ strlen( path ) - 1 ] == '/' ) + { + if( ( d = dm_opendir( path ) ) == NULL ) + return CMN_FS_TYPE_DIR_NOT_FOUND; + dm_closedir( d ); return CMN_FS_TYPE_DIR; + } // At this point, we don't know if 'path' reffers to a file or to a directory // So try to open it. If it can be opened, it is a file. Otherwise it is a directory. // But first check for '*' or '?' chars in path (which means it is definitely a file spec) @@ -115,6 +121,9 @@ int cmn_fs_get_type( const char *path ) fclose( fp ); return CMN_FS_TYPE_FILE; } + if( ( d = dm_opendir( path ) ) == NULL ) + return CMN_FS_TYPE_FILE_NOT_FOUND; + dm_closedir( d ); return CMN_FS_TYPE_DIR; } @@ -272,3 +281,15 @@ int cmn_fs_is_root_dir( const char *path ) return 0; } +// Check if the directory in the given argument (if applicable) +// can be opened +int cmn_fs_check_directory( const char *path ) +{ + DM_DIR *d; + + if( ( d = dm_opendir( path ) ) == NULL ) + return 0; + dm_closedir( d ); + return 1; +} + diff --git a/src/mmcfs.c b/src/mmcfs.c index 8f82e560..7489cc49 100644 --- a/src/mmcfs.c +++ b/src/mmcfs.c @@ -280,6 +280,11 @@ static int mmcfs_unlink_r( struct _reent *r, const char *fname, void *pdata ) return f_unlink( fname ); } +static int mmcfs_rename_r( struct _reent *r, const char *oldname, const char *newname, void *pdata ) +{ + return f_rename( oldname, newname ); +} + // MMC device descriptor structure static const DM_DEVICE mmcfs_device = { @@ -294,7 +299,8 @@ static const DM_DEVICE mmcfs_device = NULL, // getaddr mmcfs_mkdir_r, // mkdir mmcfs_unlink_r, // unlink - mmcfs_unlink_r // rmdir + mmcfs_unlink_r, // rmdir + mmcfs_rename_r // rename }; int mmcfs_init() diff --git a/src/newlib/genstd.c b/src/newlib/genstd.c index 667f05ef..60fde5e7 100644 --- a/src/newlib/genstd.c +++ b/src/newlib/genstd.c @@ -145,7 +145,8 @@ static const DM_DEVICE std_device = NULL, // getaddr NULL, // mkdir NULL, // unlink - NULL // rmdir + NULL, // rmdir + NULL // rename }; int std_register() diff --git a/src/newlib/stdtcp.c b/src/newlib/stdtcp.c index 4f816772..8a07c33a 100644 --- a/src/newlib/stdtcp.c +++ b/src/newlib/stdtcp.c @@ -96,7 +96,8 @@ static const DM_DEVICE std_device = NULL, // getaddr NULL, // mkdir NULL, // unlink - NULL // rmdir + NULL, // rmdir + NULL // rename }; diff --git a/src/newlib/stubs.c b/src/newlib/stubs.c index 07e6dff6..c455d986 100644 --- a/src/newlib/stubs.c +++ b/src/newlib/stubs.c @@ -14,6 +14,7 @@ #include "genstd.h" #include "utils.h" #include "salloc.h" +#include "shell.h" #ifdef USE_MULTIPLE_ALLOCATOR #include "dlmalloc.h" @@ -96,6 +97,11 @@ int _open_r( struct _reent *r, const char *name, int flags, int mode ) return DM_MAKE_DESC( devid, res ); } +int open( const char *name, int flags, mode_t mode ) +{ + return _open_r( _REENT, name, flags, 0 ); +} + // ***************************************************************************** // _close_r int _close_r( struct _reent *r, int file ) @@ -114,6 +120,11 @@ int _close_r( struct _reent *r, int file ) return pinst->pdev->p_close_r( r, DM_GET_FD( file ), pinst->pdata ); } +int close( int file ) +{ + return _close_r( _REENT, file ); +} + // ***************************************************************************** // _fstat_r (not implemented) int _fstat_r( struct _reent *r, int file, struct stat *st ) @@ -163,6 +174,11 @@ _ssize_t _read_r( struct _reent *r, int file, void *ptr, size_t len ) return pinst->pdev->p_read_r( r, DM_GET_FD( file ), ptr, len, pinst->pdata ); } +_ssize_t read( int file, void *ptr, size_t len ) +{ + return _read_r( _REENT, file, ptr, len ); +} + // ***************************************************************************** // _write_r _ssize_t _write_r( struct _reent *r, int file, const void *ptr, size_t len ) @@ -181,6 +197,11 @@ _ssize_t _write_r( struct _reent *r, int file, const void *ptr, size_t len ) return pinst->pdev->p_write_r( r, DM_GET_FD( file ), ptr, len, pinst->pdata ); } +_ssize_t write( int file, const void *ptr, size_t len ) +{ + return _write_r( _REENT, file, ptr, len ); +} + // **************************************************************************** // _mkdir_r int _mkdir_r( struct _reent *r, const char *path, mkdir_mode_t mode ) @@ -267,6 +288,43 @@ int rmdir( const char *path ) return pinst->pdev->p_rmdir_r( _REENT, actname, pinst->pdata ); } +// **************************************************************************** +// rename + +int _rename_r( struct _reent *r, const char *oldname, const char *newname ) +{ + char *actname_old, *actname_new; + int devid_old, devid_new; + const DM_INSTANCE_DATA *pinst; + + // Look for device, return error if not found or if function not implemented + if( ( devid_old = find_dm_entry( oldname, &actname_old ) ) == -1 ) + { + r->_errno = ENODEV; + return -1; + } + if( ( devid_new = find_dm_entry( newname, &actname_new ) ) == -1 ) + { + r->_errno = ENODEV; + return -1; + } + if( devid_old == devid_new ) + { + pinst = dm_get_instance_at( devid_old ); + if( pinst->pdev->p_rename_r == NULL ) + { + r->_errno = EPERM; + return -1; + } + + // Device found, call its function + return pinst->pdev->p_rename_r( r, actname_old, actname_new, pinst->pdata ); + } + // Cannot rename between different devices (EXDEV) + r->_errno = EXDEV; + return -1; +} + // **************************************************************************** // Miscellaneous functions diff --git a/src/platform/sim/platform_conf.h b/src/platform/sim/platform_conf.h index 7780aa7b..cabc45fd 100644 --- a/src/platform/sim/platform_conf.h +++ b/src/platform/sim/platform_conf.h @@ -12,6 +12,7 @@ // Define here what components you want for this platform #define BUILD_SHELL +#define BUILD_ADVANCED_SHELL #define BUILD_ROMFS #define BUILD_CON_GENERIC #define BUILD_TERM diff --git a/src/remotefs/elua_rfs.c b/src/remotefs/elua_rfs.c index a35d3d5c..ae003b34 100644 --- a/src/remotefs/elua_rfs.c +++ b/src/remotefs/elua_rfs.c @@ -183,7 +183,8 @@ static const DM_DEVICE rfs_device = NULL, // getaddr NULL, // mkdir NULL, // unlink - NULL // rmdir + NULL, // rmdir + NULL // rename }; int remotefs_init() diff --git a/src/romfs.c b/src/romfs.c index be13fdbd..8282f1ab 100644 --- a/src/romfs.c +++ b/src/romfs.c @@ -429,7 +429,8 @@ static const DM_DEVICE romfs_device = romfs_getaddr_r, // getaddr NULL, // mkdir NULL, // unlink - NULL // rmdir + NULL, // rmdir + NULL // rename }; // **************************************************************************** diff --git a/src/semifs.c b/src/semifs.c index db061a9d..e7d9ff25 100644 --- a/src/semifs.c +++ b/src/semifs.c @@ -261,7 +261,8 @@ static const DM_DEVICE semifs_device = NULL, // getaddr NULL, // mkdir NULL, // unlink - NULL // rmdir + NULL, // rmdir + NULL // rename }; int semifs_init() diff --git a/src/shell.c b/src/shell.c deleted file mode 100644 index 61bbd484..00000000 --- a/src/shell.c +++ /dev/null @@ -1,984 +0,0 @@ -// eLua shell - -#include "shell.h" -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" -#include "xmodem.h" -#include -#include -#include -#include "platform.h" -#include "elua_net.h" -#include "devman.h" -#include "buf.h" -#include "remotefs.h" -#include "eluarpc.h" -#include "linenoise.h" -#include "term.h" -#include "romfs.h" -#include -#include -#include -#include "common.h" - -#if defined( USE_GIT_REVISION ) -#include "git_version.h" -#else -#include "version.h" -#endif - -#include "platform_conf.h" -#ifdef BUILD_SHELL - -// Shell alternate ' ' char -#define SHELL_ALT_SPACE '\x07' -#define SHELL_MAX_ARGS 10 - -// EOF is different in UART mode and TCP/IP mode -#ifdef BUILD_CON_GENERIC - #define SHELL_EOF_STRING "CTRL+Z" -#else - #define SHELL_EOF_STRING "CTRL+D" -#endif - -// Shell command handler function -typedef void( *p_shell_handler )( int argc, char **argv ); - -// Command/handler pair structure -typedef struct -{ - const char* cmd; - p_shell_handler handler_func; -} SHELL_COMMAND; - -// Shell data -static char* shell_prog; - -// Flags for various operations -#define SHELL_F_RECURSIVE 1 -#define SHELL_F_FORCE_DESTINATION 2 -#define SHELL_F_ASK_CONFIRMATION 4 -#define SHELL_F_SIMULATE_ONLY 8 - -// **************************************************************************** -// Shell functions - -// ---------------------------------------------------------------------------- -// Helpers - -// Helper: ask yes/no -// Returns 1 for yes, 0 for no -static int shellh_ask_yes_no( const char *prompt ) -{ - int c; - - if( prompt ) - printf( "%s ", prompt ); - while( 1 ) - { - c = term_getch( TERM_INPUT_WAIT ); - if( c == 'y' || c == 'Y' ) - { - printf( "y\n" ); - return 1; - } - if( c == 'n' || c == 'N' ) - { - printf( "n\n" ); - return 0; - } - } - // Will never get here - return 0; -} - - -// ---------------------------------------------------------------------------- -// 'help' handler - -static void shell_help( int argc, char **argv ) -{ - ( void )argc; - ( void )argv; - printf( "Shell commands:\n" ); - printf( " exit - exit from this shell\n" ); - printf( " help - print this help\n" ); - printf( " ls or dir - lists filesystems files and sizes\n" ); - printf( " cat or type - lists file contents\n" ); - printf( " lua [args] - run Lua with the given arguments\n" ); - printf( " recv [path] - receive a file via XMODEM. If path is given save it there, otherwise run it.\n"); - printf( " cp - copy source file 'src' to 'dst'\n" ); - printf( " wofmt - format the internal WOFS\n" ); - printf( " ver - print eLua version\n" ); -} - -// ---------------------------------------------------------------------------- -// 'lua' handler - -static void shell_lua( int argc, char **argv ) -{ - printf( "Press " SHELL_EOF_STRING " to exit Lua\n" ); - lua_main( argc, argv ); - clearerr( stdin ); -} - -// ---------------------------------------------------------------------------- -// 'recv' handler - -static void shell_recv( int argc, char **argv ) -{ -#ifndef BUILD_XMODEM - printf( "XMODEM support not compiled, unable to recv\n" ); -#else // #ifndef BUILD_XMODEM - - char *p; - long actsize; - lua_State* L; - - if( argc > 2 ) - { - printf( "Usage: recv [path]\n" ); - return; - } - - if( ( shell_prog = malloc( XMODEM_INITIAL_BUFFER_SIZE ) ) == NULL ) - { - printf( "Unable to allocate memory\n" ); - return; - } - printf( "Waiting for file ... " ); - if( ( actsize = xmodem_receive( &shell_prog ) ) < 0 ) - { - free( shell_prog ); - shell_prog = NULL; - if( actsize == XMODEM_ERROR_OUTOFMEM ) - printf( "file too big\n" ); - else - printf( "XMODEM error\n" ); - return; - } - // Eliminate the XMODEM padding bytes - p = shell_prog + actsize - 1; - while( *p == '\x1A' ) - p --; - p ++; - printf( "done, got %u bytes\n", ( unsigned )( p - shell_prog ) ); - - // we've received an argument, try saving it to a file - if( argc == 2 ) - { - FILE *foutput = fopen( argv[ 1 ], "w" ); - size_t file_sz = p - shell_prog; - if( foutput == NULL ) - { - printf( "unable to open file %s\n", argv[ 1 ] ); - free( shell_prog ); - shell_prog = NULL; - return; - } - if( fwrite( shell_prog, sizeof( char ), file_sz, foutput ) == file_sz ) - printf( "received and saved as %s\n", argv[ 1 ] ); - else - printf( "unable to save file %s (no space left on target?)\n", argv[ 1 ] ); - fclose( foutput ); - } - else // no arg, running the file with lua. - { - if( ( L = lua_open() ) == NULL ) - { - printf( "Unable to create Lua state\n" ); - free( shell_prog ); - shell_prog = NULL; - return; - } - luaL_openlibs( L ); - if( luaL_loadbuffer( L, shell_prog, p - shell_prog, "xmodem" ) != 0 ) - printf( "Error: %s\n", lua_tostring( L, -1 ) ); - else - if( lua_pcall( L, 0, LUA_MULTRET, 0 ) != 0 ) - printf( "Error: %s\n", lua_tostring( L, -1 ) ); - lua_close( L ); - } - free( shell_prog ); - shell_prog = NULL; -#endif // #ifndef BUILD_XMODEM -} - -// ---------------------------------------------------------------------------- -// 'ver' handler - -static void shell_ver( int argc, char **argv ) -{ - ( void )argc; - ( void )argv; - printf( "eLua version %s\n", ELUA_STR_VERSION ); - printf( "For more information visit www.eluaproject.net and wiki.eluaproject.net\n" ); -} - -// ---------------------------------------------------------------------------- -// 'ls'/'dir' handler - -// State for walkdir -typedef struct -{ - u32 dir_total; - u32 total; - u8 ndirs; -} SHELL_LS_STATE; - -// 'ls' and 'dir' handler -// Syntax: ls [dir] [-R] -// directory walker callback -static int shellh_ls_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) -{ - SHELL_LS_STATE *ps = ( SHELL_LS_STATE* )pdata; - - switch( info ) - { - case CMN_FS_INFO_BEFORE_READDIR: - ps->dir_total = 0; - printf( "%s\n", path ); - if( ps->ndirs != 0xFF ) - ps->ndirs ++; - break; - - case CMN_FS_INFO_INSIDE_READDIR: - printf( " %-30s", pent->fname ); - if( DM_DIRENT_IS_DIR( pent ) ) - printf( "\n" ); - else - { - printf( "%u bytes\n", ( unsigned )pent->fsize ); - ps->dir_total += pent->fsize; - } - break; - - case CMN_FS_INFO_AFTER_CLOSEDIR: - printf( "Total on %s: %u bytes\n\n", path, ( unsigned )ps->dir_total ); - ps->total += ps->dir_total; - break; - - case CMN_FS_INFO_OPENDIR_FAILED: - printf( "WARNING: unable to open %s\n", path ); - break; - } - return 1; -} - -static void shell_ls( int argc, char **argv ) -{ - const DM_INSTANCE_DATA *pinst; - unsigned i; - int recursive = 0; - char *pname = NULL; - const char *crtname; - SHELL_LS_STATE state; - - for( i = 1; i < argc; i ++ ) - { - if( !strcmp( argv[ i ], "-R" ) ) - recursive = 1; - else if( argv[ i ][ 0 ] == '/' && !pname ) - pname = argv[ i ]; - else - printf( "Warning: ignoring argument '%s' of ls", argv[ i ] ); - } - // Iterate through all devices, looking for the ones that can do "opendir" - // or the ones that match 'pname' (if that is specified) - for( i = 0; i < dm_get_num_devices(); i ++ ) - { - pinst = dm_get_instance_at( i ); - if( pinst->pdev->p_opendir_r == NULL || pinst->pdev->p_readdir_r == NULL || pinst->pdev->p_closedir_r == NULL ) - continue; - if( pname && strncmp( pinst->name, pname, strlen( pinst->name ) ) ) - continue; - crtname = pname ? pname : pinst->name; - memset( &state, 0, sizeof( state ) ); - cmn_fs_walkdir( crtname, shellh_ls_walkdir_cb, &state, recursive ); - if( recursive && ( state.ndirs > 1 ) ) - printf( "Total on %s with all subdirectories: %u bytes\n\n", crtname, ( unsigned )state.total ); - } -} - -// ---------------------------------------------------------------------------- -// 'cat' and 'type' handler - -static void shell_cat( int argc, char **argv ) -{ - FILE *fp; - int c; - unsigned i; - - if( argc < 2 ) - { - printf( "Usage: cat (or type) [ ...]\n" ); - return; - } - for( i = 1; i < argc; i ++ ) - { - if( ( fp = fopen( argv[ i ] , "rb" ) ) != NULL ) - { - c = fgetc( fp ); - while( c != EOF ) - { - printf("%c", (char) c ); - c = fgetc( fp ); - } - fclose ( fp ); - } - else - printf( "Unable to open '%s'\n", argv[ i ] ); - } -} - -// ---------------------------------------------------------------------------- -// 'cp' handler - -#ifdef BUILD_RFS -#define SHELL_COPY_BUFSIZE ( ( 1 << RFS_BUFFER_SIZE ) - ELUARPC_WRITE_REQUEST_EXTRA ) -#else -#define SHELL_COPY_BUFSIZE 256 -#endif - -typedef struct -{ - const char *pdestdir; - const char *psrcdir; - const char *dloc; - u8 flags; -} SHELL_CP_STATE; - -// Helper: copy one file to another file -// Return 1 for success, 0 for error -static int shellh_cp_one_file( const char *psrcname, const char *pdestname, int flags ) -{ - FILE *fps = NULL, *fpd = NULL; - int res = 0; - char *buf = NULL; - u32 datalen, datawrote, total = 0; - - if( !strcasecmp( psrcname, pdestname ) ) - { - printf( "Cannot copy '%s' into itself.\n", psrcname ); - goto done; - } - // If operation confirmation is enabled, ask the user first - if( flags & SHELL_F_ASK_CONFIRMATION ) - { - printf( "Copy '%s' to '%s' ? [y/n] ", psrcname, pdestname ); - if( shellh_ask_yes_no( NULL ) == 0 ) - goto done; - } - // Open source file - if( ( fps = fopen( psrcname, "r" ) ) == NULL ) - { - printf( "Error: unable to open source file '%s'\n", psrcname ); - goto done; - } - // If the destination exists and we need to ask for confirmation, do it now - if( ( flags & SHELL_F_FORCE_DESTINATION ) == 0 ) - { - if( ( fpd = fopen( pdestname, "r" ) ) != NULL ) - { - fclose( fpd ); - fpd = NULL; - printf( "Destination '%s' already exists, are you sure you want to overwrite it ? [y/n] ", pdestname ); - if( shellh_ask_yes_no( NULL ) == 0 ) - goto done; - } - } - // Allocate buffer - if( ( buf = ( char* )malloc( SHELL_COPY_BUFSIZE ) ) == NULL ) - { - printf( "ERROR: unable to allocate buffer for copy operation.\n" ); - goto done; - } - printf( "Copying '%s' to '%s' ... ", psrcname, pdestname ); - if( ( flags & SHELL_F_SIMULATE_ONLY ) == 0 ) - { - // Open destination file - if( ( fpd = fopen( pdestname, "w" ) ) == NULL ) - { - printf( "ERROR: unable to open '%s' for writing.\n", pdestname ); - goto done; - } - // Do the actual copy - while( 1 ) - { - datalen = fread( buf, 1, SHELL_COPY_BUFSIZE, fps ); - datawrote = fwrite( buf, 1, datalen, fpd ); - if( datawrote < datalen ) - { - printf( "Copy error (no space left on target?)\n" ); - goto done; - } - total += datalen; - if( datalen < SHELL_COPY_BUFSIZE ) - break; - } - fflush( fpd ); - } - printf( "done (%u bytes).\n", ( unsigned )total ); -done: - if( fps ) - fclose( fps ); - if( fpd ) - fclose( fpd ); - if( buf ) - free( buf ); - return res; -} - -// copy callback -static int shellh_cp_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) -{ - SHELL_CP_STATE *ps = ( SHELL_CP_STATE* )pdata; - char *tmp = NULL, *tmp2 = NULL; - int res = 1; - DM_DIR *d = NULL; - - switch( info ) - { - case CMN_FS_INFO_BEFORE_READDIR: - if( strstr( path, ps->psrcdir ) != path ) - { - printf( "ERROR: unable to handle directory '%s' (internal error?), aborting.\n", path ); - goto done_err; - } - // Need to create this directory if it does not exist - if( ( tmp = ( char* )cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ) - strlen( ps->dloc ), NULL ) ) == NULL ) - { - printf( "Not enough memory.\n" ); - goto done_err; - } - if( ( d = dm_opendir( tmp ) ) != NULL ) - goto done; - printf( "Creating directory %s ... ", tmp ); - if( ( ps->flags & SHELL_F_SIMULATE_ONLY ) == 0 ) - { - if( mkdir( tmp, 0 ) == -1 ) - { - printf( "ERROR! (aborting).\n" ); - goto done_err; - } - else - printf( "done.\n" ); - } - else - printf( "done.\n" ); - goto done; - - case CMN_FS_INFO_INSIDE_READDIR: - if( !DM_DIRENT_IS_DIR( pent ) ) - { - if( strstr( path, ps->psrcdir ) != path ) - { - printf( "ERROR: unable to handler directory '%s' (internal error?), aborting.\n", path ); - goto done_err; - } - if( ( tmp = cmn_fs_path_join( path, pent->fname, NULL ) ) == NULL ) - { - printf( "Not enough memory.\n" ); - goto done_err; - } - if( ( tmp2 = cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ) - strlen( ps->dloc ), pent->fname, NULL ) ) == NULL ) - { - printf( "Not enough memory.\n" ); - goto done_err; - } - shellh_cp_one_file( tmp, tmp2, ps->flags ); - } - goto done; - - case CMN_FS_INFO_OPENDIR_FAILED: - printf( "ERROR: unable to read directory '%s', aborting.\n", path ); - goto done_err; - - default: - goto done; - } -done_err: - res = 0; -done: - if( tmp ) - free( tmp ); - if( tmp2 ) - free( tmp2 ); - if( d ) - dm_closedir( d ); - return res; -} - -static void shell_cp( int argc, char **argv ) -{ - const char *srcpath = NULL, *dstpath = NULL; - unsigned i, flags = 0; - int srctype, dsttype; - char *srcdir = NULL, *dstdir = NULL, *tmp = NULL; - const char *srcfile, *dstfile; - SHELL_CP_STATE state; - - if( argc < 3 ) - { - printf( "Usage: cp [-R] [-f] [-c]\n" ); - printf( " [-R]: recursive\n" ); - printf( " [-f]: force destination override (default is to ask confirmation).\n" ); - printf( " [-c]: confirm each file copy.\n" ); - printf( " [-s]: simulate only (no actual operation).\n" ); - return; - } - for( i = 1; i < argc; i ++ ) - { - if( !strcmp( argv[ i ], "-R" ) ) - flags |= SHELL_F_RECURSIVE; - else if( !strcmp( argv[ i ], "-f" ) ) - flags |= SHELL_F_FORCE_DESTINATION; - else if( !strcmp( argv[ i ], "-c" ) ) - flags |= SHELL_F_ASK_CONFIRMATION; - else if( !strcmp( argv[ i ], "-s" ) ) - flags |= SHELL_F_SIMULATE_ONLY; - else if( argv[ i ][ 0 ] == '/' ) - { - if( !srcpath ) - srcpath = argv[ i ]; - else if( !dstpath ) - dstpath = argv[ i ]; - else - printf( "WARNING: ignoring argument '%s'\n", argv[ i ] ); - } - else - printf( "WARNING: ignoring argument '%s'\n", argv[ i ] ); - } - if( !srcpath || !dstpath ) - { - printf( "Source and/or destination not specified.\n" ); - return; - } - srctype = cmn_fs_get_type( srcpath ); - if( ( dsttype = cmn_fs_get_type( dstpath ) ) == CMN_FS_TYPE_PATTERN ) - { - printf( "Invalid destination '%s'.\n", dstpath ); - goto done; - } - if( srctype == CMN_FS_TYPE_ERROR || dsttype == CMN_FS_TYPE_ERROR ) - { - printf( "Invalid source and/or destination.\n" ); - return; - } - srcdir = cmn_fs_split_path( srcpath, &srcfile ); - dstdir = cmn_fs_split_path( dstpath, &dstfile ); - // Check valid source/destination combinations - if( srctype == CMN_FS_TYPE_FILE ) - { - if( dsttype == CMN_FS_TYPE_FILE ) // direct file-to-file copy - { - shellh_cp_one_file( srcpath, dstpath, flags ); - goto done; - } - else if( dsttype == CMN_FS_TYPE_DIR ) // copy to destination dir with the same name - { - if( ( tmp = cmn_fs_path_join( dstdir, srcfile, NULL ) ) == NULL ) - { - printf( "Not enough memory.\n" ); - goto done; - } - shellh_cp_one_file( srcpath, tmp, flags ); - goto done; - } - else - { - printf( "Invalid arguments.\n" ); - goto done; - } - } - else - { - if( dsttype == CMN_FS_TYPE_FILE ) - { - printf( "Invalid destination '%s'.\n", dstpath ); - goto done; - } - memset( &state, 0, sizeof( state ) ); - state.flags = flags; - state.pdestdir = dstdir; - state.psrcdir = srcdir; - state.dloc = strchr( srcdir + 1, '/' ); - if( state.dloc == NULL ) - state.dloc = ""; - cmn_fs_walkdir( srcpath, shellh_cp_walkdir_cb, &state, flags & SHELL_F_RECURSIVE ); - } -done: - if( srcdir ) - free( srcdir ); - if( dstdir ) - free( dstdir ); - if( tmp ) - free( tmp ); -} - -// ---------------------------------------------------------------------------- -// 'wofmt' handler - -static void shell_wofmt( int argc, char **argv ) -{ -#ifndef BUILD_WOFS - printf( "WOFS not enabled.\n" ); -#else // #ifndef BUILD_WOFS - int c; - - printf( "Formatting the internal WOFS will DESTROY ALL THE FILES FROM WOFS.\n" ); - while( 1 ) - { - printf( "Are you sure you want to continue? [y/n] " ); - c = term_getch( TERM_INPUT_WAIT ); - printf( "%c\n", isprint( c ) ? c : ' ' ); - c = tolower( c ); - if( c == 'n' ) - return; - else if( c == 'y' ) - break; - } - printf( "Formatting ... " ); - if( !wofs_format() ) - { - printf( "\ni*** ERROR ***: unable to erase the internal flash. WOFS might be compromised.\n" ); - printf( "It is advised to re-flash the eLua image.\n" ); - } - else - printf( " done.\n" ); -#endif // #ifndef BUILD_WOFS -} - -// ---------------------------------------------------------------------------- -// mkdir handler - -static void shell_mkdir( int argc, char **argv ) -{ - if( argc != 2 ) - { - printf( "Usage: mkdir \n" ); - return; - } - if( mkdir( argv[ 1 ], 0 ) != 0 ) - printf( "Error creating directory '%s'\n", argv[ 1 ] ); -} - -// ---------------------------------------------------------------------------- -// rm handler - -// helper: remove a single file and/or directory -static void shellh_rm_one( const char* path, int flags ) -{ - int ftype = cmn_fs_get_type( path ); - int res = 0; - - if( flags & SHELL_F_ASK_CONFIRMATION ) - { - printf( "Are you sure you want to remove %s ? [y/n] ", path ); - if( shellh_ask_yes_no( NULL ) == 0 ) - return; - } - if( ( flags & SHELL_F_SIMULATE_ONLY ) == 0 ) - { - if( ftype == CMN_FS_TYPE_FILE ) - res = unlink( path ); - else if( ftype == CMN_FS_TYPE_DIR ) - res = rmdir( path ); - else - { - printf( "WARNING: invalid argument '%s'\n", path ); - return; - } - } - if( res ) - printf( "WARNING: unable to remove %s\n", path ); - else - printf( "Removed %s\n", path ); -} - -static int shellh_rm_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) -{ - u8 *pflags = ( u8* )pdata; - char *tmp = NULL; - int res = 1; - - switch( info ) - { - case CMN_FS_INFO_BEFORE_READDIR: - goto done; - - case CMN_FS_INFO_INSIDE_READDIR: - if( ( tmp = cmn_fs_path_join( path, pent->fname, NULL ) ) == NULL ) - { - printf( "Not enough memory.\n" ); - goto done_err; - } - if( cmn_fs_get_type( tmp ) == CMN_FS_TYPE_FILE ) - shellh_rm_one( tmp, *pflags ); - goto done; - - case CMN_FS_INFO_OPENDIR_FAILED: - printf( "ERROR: unable to read directory '%s', aborting.\n", path ); - goto done_err; - - case CMN_FS_INFO_DIRECTORY_DONE: - if( *pflags & SHELL_F_RECURSIVE && !cmn_fs_is_root_dir( path ) ) - shellh_rm_one( path, *pflags ); - goto done; - - default: - goto done; - } -done_err: - res = 0; -done: - if( tmp ) - free( tmp ); - return res; -} - -static void shell_rm( int argc, char **argv ) -{ - const char *fmask = NULL; - unsigned i, flags = 0; - int masktype; - - if( argc < 2 ) - { - printf( "Usage: rm [-R] [-c] [-s]\n" ); - printf( " -R: remove recursively.\n" ); - printf( " -c: confirm each remove.\n" ); - printf( " -s: simulate only (no actual operation).\n" ); - return; - } - for( i = 1; i < argc; i ++ ) - { - if( argv[ i ][ 0 ] == '/' ) - { - if( !fmask ) - fmask = argv[ i ]; - else - printf( "Warning: ignoring argument '%s'\n", argv[ i ] ); - } - else if( !strcmp( argv[ i ], "-R" ) ) - flags |= SHELL_F_RECURSIVE; - else if( !strcmp( argv[ i ], "-c" ) ) - flags |= SHELL_F_ASK_CONFIRMATION; - else if( !strcmp( argv[ i ], "-s" ) ) - flags |= SHELL_F_SIMULATE_ONLY; - else - printf( "Warning: ignoring argument '%s'\n", argv[ i ] ); - } - if( !fmask ) - { - printf( "rm target not specified.\n" ); - return; - } - masktype = cmn_fs_get_type( fmask ); - if( masktype == CMN_FS_TYPE_FILE ) - shellh_rm_one( fmask, flags ); - else - cmn_fs_walkdir( fmask, shellh_rm_walkdir_cb, &flags, flags & SHELL_F_RECURSIVE ); -} - -// **************************************************************************** -// Public interface - -// Insert shell commands here -static const SHELL_COMMAND shell_commands[] = -{ - { "help", shell_help }, - { "lua", shell_lua }, - { "recv", shell_recv }, - { "ver", shell_ver }, - { "exit", NULL }, - { "ls", shell_ls }, - { "dir", shell_ls }, - { "cat", shell_cat }, - { "type", shell_cat }, - { "cp", shell_cp }, - { "wofmt", shell_wofmt }, - { "mkdir", shell_mkdir }, - { "rm", shell_rm }, - { NULL, NULL } -}; - -// Execute the eLua "shell" in an infinite loop -void shell_start() -{ - char cmd[ SHELL_MAXSIZE + 1 ]; - char *p, *temp; - const SHELL_COMMAND* pcmd; - int i, inside_quotes; - char quote_char; - int argc; - char *argv[ SHELL_MAX_ARGS ]; - - printf( SHELL_WELCOMEMSG, ELUA_STR_VERSION ); - while( 1 ) - { - while( linenoise_getline( LINENOISE_ID_SHELL, cmd, SHELL_MAXSIZE - 1, SHELL_PROMPT ) == -1 ) - { - printf( "\n" ); - clearerr( stdin ); - } - if( strlen( cmd ) == 0 ) - continue; - linenoise_addhistory( LINENOISE_ID_SHELL, cmd ); - if( cmd[ strlen( cmd ) - 1 ] != '\n' ) - strcat( cmd, "\n" ); - - // 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. We do this to let the user execute something - // like "lua -e 'quoted string'" without disturbing the quoted - // string in any way. - 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" ); - continue; - } - - // 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, " " ) ) - continue; - - // Skip over the initial space char if it exists - p = cmd; - if( *p == ' ' ) - p ++; - - // Add a final space if it does not exist - if( p[ strlen( p ) - 1 ] != ' ' ) - strcat( p, " " ); - - // Compute argc/argv - for( argc = 0; argc < SHELL_MAX_ARGS; argc ++ ) - argv[ argc ] = NULL; - argc = 0; - while( ( temp = strchr( p, ' ' ) ) != NULL ) - { - *temp = 0; - if( argc == SHELL_MAX_ARGS ) - { - printf( "Error: too many arguments\n" ); - argc = -1; - break; - } - argv[ argc ++ ] = p; - p = temp + 1; - } - - if( argc == -1 ) - continue; - - // 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( !strcasecmp( pcmd->cmd, argv[ 0 ] ) ) - { - // Special case: the "exit" command has a NULL handler - if( pcmd->handler_func ) - pcmd->handler_func( argc, argv ); - break; - } - i ++; - } - // Check for 'exit' command - if( pcmd->cmd && !pcmd->handler_func ) -#ifdef BUILD_UIP - { - if( ( i = elua_net_get_telnet_socket() ) != -1 ) - elua_net_close( i ); - } -#else - break; -#endif - - } - // Shell exit point - if( shell_prog ) - free( shell_prog ); -} - -// Initialize the shell, returning 1 for OK and 0 for error -int shell_init() -{ - shell_prog = NULL; - return 1; -} - -#else // #ifdef BUILD_SHELL - -int shell_init() -{ - return 0; -} - -void shell_start() -{ -} - -#endif // #ifdef BUILD_SHELL - diff --git a/src/shell/shell.c b/src/shell/shell.c new file mode 100644 index 00000000..e135fd87 --- /dev/null +++ b/src/shell/shell.c @@ -0,0 +1,405 @@ +// eLua shell + +#include "shell.h" +#include +#include +#include +#include "platform.h" +#include "elua_net.h" +#include "linenoise.h" +#include "term.h" +#include +#include +#include +#include +#include "common.h" + +#include "platform_conf.h" +#ifdef BUILD_SHELL + +#if defined( USE_GIT_REVISION ) +#include "git_version.h" +#else +#include "version.h" +#endif + +// Shell alternate ' ' char +#define SHELL_ALT_SPACE '\x07' +#define SHELL_MAX_ARGS 10 + +// External shell function declaration +#define SHELL_FUNC( func ) extern void func( int argc, char **argv ) + +// Shell command handler function +typedef void( *p_shell_handler )( int argc, char **argv ); + +// Command/handler pair structure +typedef struct +{ + const char* cmd; + p_shell_handler handler_func; +} SHELL_COMMAND; + +// Shell data +static char* shell_prog; + +// Extern implementations of shell functions +SHELL_FUNC( shell_ls ); +SHELL_FUNC( shell_cp ); +SHELL_FUNC( shell_adv_mv ); +SHELL_FUNC( shell_adv_rm ); +SHELL_FUNC( shell_recv ); +SHELL_FUNC( shell_help ); +SHELL_FUNC( shell_cat ); +SHELL_FUNC( shell_lua ); +SHELL_FUNC( shell_ver ); +SHELL_FUNC( shell_mkdir ); +SHELL_FUNC( shell_wofmt ); + +// ---------------------------------------------------------------------------- +// Helpers + +// Helper: ask yes/no +// Returns 1 for yes, 0 for no +int shellh_ask_yes_no( const char *prompt ) +{ + int c; + + if( prompt ) + printf( "%s ", prompt ); + while( 1 ) + { + c = term_getch( TERM_INPUT_WAIT ); + if( c == 'y' || c == 'Y' ) + { + printf( "y\n" ); + return 1; + } + if( c == 'n' || c == 'N' ) + { + printf( "n\n" ); + return 0; + } + } + // Will never get here + return 0; +} + +#ifdef BUILD_RFS +#define SHELL_COPY_BUFSIZE ( ( 1 << RFS_BUFFER_SIZE ) - ELUARPC_WRITE_REQUEST_EXTRA ) +#else +#define SHELL_COPY_BUFSIZE 256 +#endif + +// Dummy log function +static int shellh_dummy_printf( const char *fmt, ... ) +{ + return 0; +} + +typedef int ( *p_logf )( const char *fmt, ... ); + +// Helper: copy one file to another file +// Return 1 for success, 0 for error +int shellh_cp_file( const char *psrcname, const char *pdestname, int flags ) +{ + int fds = -1, fdd = -1; + int res = 0; + char *buf = NULL; + ssize_t datalen, datawrote; + u32 total = 0; + p_logf plog = ( flags & SHELL_F_SILENT ) ? shellh_dummy_printf : printf; + + if( !strcasecmp( psrcname, pdestname ) ) + { + plog( "Cannot copy '%s' into itself.\n", psrcname ); + goto done; + } + // If operation confirmation is enabled, ask the user first + if( flags & SHELL_F_ASK_CONFIRMATION ) + { + printf( "Copy '%s' to '%s' ? [y/n] ", psrcname, pdestname ); + if( shellh_ask_yes_no( NULL ) == 0 ) + goto done; + } + // Open source file + if( ( fds = open( psrcname, O_RDONLY, 0 ) ) == -1 ) + { + plog( "Error: unable to open source file '%s'\n", psrcname ); + goto done; + } + // If the destination exists and we need to ask for confirmation, do it now + if( ( flags & SHELL_F_FORCE_DESTINATION ) == 0 ) + { + if( ( fdd = open( pdestname, O_RDONLY, 0 ) ) != -1 ) + { + close( fdd ); + fdd = -1; + printf( "Destination '%s' already exists, are you sure you want to overwrite it ? [y/n] ", pdestname ); + if( shellh_ask_yes_no( NULL ) == 0 ) + goto done; + } + } + // Allocate buffer + if( ( buf = ( char* )malloc( SHELL_COPY_BUFSIZE ) ) == NULL ) + { + plog( "ERROR: unable to allocate buffer for copy operation.\n" ); + goto done; + } + plog( "Copying '%s' to '%s' ... ", psrcname, pdestname ); + if( ( flags & SHELL_F_SIMULATE_ONLY ) == 0 ) + { + // Open destination file + if( ( fdd = open( pdestname, O_WRONLY | O_CREAT | O_TRUNC, 0 ) ) == -1 ) + { + plog( "ERROR: unable to open '%s' for writing.\n", pdestname ); + goto done; + } + // Do the actual copy + while( 1 ) + { + if( ( datalen = read( fds, buf, SHELL_COPY_BUFSIZE ) ) == -1 ) + { + plog( "Error reading source file '%s'.\n", psrcname ); + goto done; + } + if( ( datawrote = write( fdd, buf, datalen ) ) == -1 ) + { + plog( "Error writing destination file '%s'.\n", pdestname ); + goto done; + } + if( datawrote < datalen ) + { + plog( "Copy error (no space left on target?)\n" ); + goto done; + } + total += datalen; + if( datalen < SHELL_COPY_BUFSIZE ) + break; + } + } + plog( "done (%u bytes).\n", ( unsigned )total ); + res = 1; +done: + if( fds != -1 ) + close( fds ); + if( fdd != -1 ) + close( fdd ); + if( buf ) + free( buf ); + return res; +} + +// 'Not implemented' handler for shell comands +void shellh_not_implemented_handler( int argc, char **argv ) +{ + printf( SHELL_ERRMSG ); +} + +// Shows the help for the given command +void shellh_show_help( const char *cmd, const char *helptext ) +{ + printf( "Usage: %s %s", cmd, helptext ); +} + +// **************************************************************************** +// Public interface + +// Insert shell commands here +static const SHELL_COMMAND shell_commands[] = +{ + { "help", shell_help }, + { "lua", shell_lua }, + { "recv", shell_recv }, + { "ver", shell_ver }, + { "exit", NULL }, + { "ls", shell_ls }, + { "dir", shell_ls }, + { "cat", shell_cat }, + { "type", shell_cat }, + { "cp", shell_cp }, + { "wofmt", shell_wofmt }, + { "mkdir", shell_mkdir }, + { "rm", shell_adv_rm }, + { "mv", shell_adv_mv }, + { NULL, NULL } +}; + +// Execute the eLua "shell" in an infinite loop +void shell_start() +{ + char cmd[ SHELL_MAXSIZE + 1 ]; + char *p, *temp; + const SHELL_COMMAND* pcmd; + int i, inside_quotes; + char quote_char; + int argc; + char *argv[ SHELL_MAX_ARGS ]; + + printf( SHELL_WELCOMEMSG, ELUA_STR_VERSION ); + while( 1 ) + { + while( linenoise_getline( LINENOISE_ID_SHELL, cmd, SHELL_MAXSIZE - 1, SHELL_PROMPT ) == -1 ) + { + printf( "\n" ); + clearerr( stdin ); + } + if( strlen( cmd ) == 0 ) + continue; + linenoise_addhistory( LINENOISE_ID_SHELL, cmd ); + if( cmd[ strlen( cmd ) - 1 ] != '\n' ) + strcat( cmd, "\n" ); + + // 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. We do this to let the user execute something + // like "lua -e 'quoted string'" without disturbing the quoted + // string in any way. + 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" ); + continue; + } + + // 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, " " ) ) + continue; + + // Skip over the initial space char if it exists + p = cmd; + if( *p == ' ' ) + p ++; + + // Add a final space if it does not exist + if( p[ strlen( p ) - 1 ] != ' ' ) + strcat( p, " " ); + + // Compute argc/argv + for( argc = 0; argc < SHELL_MAX_ARGS; argc ++ ) + argv[ argc ] = NULL; + argc = 0; + while( ( temp = strchr( p, ' ' ) ) != NULL ) + { + *temp = 0; + if( argc == SHELL_MAX_ARGS ) + { + printf( "Error: too many arguments\n" ); + argc = -1; + break; + } + argv[ argc ++ ] = p; + p = temp + 1; + } + + if( argc == -1 ) + continue; + + // 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( !strcasecmp( pcmd->cmd, argv[ 0 ] ) ) + { + // Special case: the "exit" command has a NULL handler + if( pcmd->handler_func ) + pcmd->handler_func( argc, argv ); + break; + } + i ++; + } + // Check for 'exit' command + if( pcmd->cmd && !pcmd->handler_func ) +#ifdef BUILD_UIP + { + if( ( i = elua_net_get_telnet_socket() ) != -1 ) + elua_net_close( i ); + } +#else + break; +#endif + + } + // Shell exit point + if( shell_prog ) + free( shell_prog ); +} + +// Initialize the shell, returning 1 for OK and 0 for error +int shell_init() +{ + shell_prog = NULL; + return 1; +} + +#else // #ifdef BUILD_SHELL + +int shell_init() +{ + return 0; +} + +void shell_start() +{ +} + +#endif // #ifdef BUILD_SHELL + diff --git a/src/shell/shell_adv_cp_mv.c b/src/shell/shell_adv_cp_mv.c new file mode 100644 index 00000000..133e5eab --- /dev/null +++ b/src/shell/shell_adv_cp_mv.c @@ -0,0 +1,294 @@ +// Advanced shell: 'cp' and 'mv' implementation + +#include +#include +#include +#include +#include +#include +#include +#include "shell.h" +#include "common.h" +#include "type.h" +#include "platform_conf.h" + +#ifdef BUILD_ADVANCED_SHELL + +typedef struct +{ + const char *pdestdir; + const char *psrcdir; + const char *dloc; + u8 flags; +} SHELL_CP_STATE; + +const char shell_help_cp[] = " [-R] [-f] [-c] [-s]\n" + " : source file/directory/file mask.\n" + " : destination file/directory.\n" + " [-R]: recursive\n" + " [-f]: force destination override (default is to ask confirmation).\n" + " [-c]: confirm each operation.\n" + " [-s]: simulate only (no actual operation).\n"; +const char shell_help_summary_cp[] = "copy files"; + +#define shell_help_mv shell_help_cp +const char shell_help_summary_mv[] = "move/rename files"; + +int shellh_cp_or_mv_file( const char *psrc, const char *pdest, int flags ) +{ + if( flags & SHELL_F_MOVE ) + { + if( rename( psrc, pdest ) == -1 ) + { + if( errno != EXDEV ) + { + printf( "Unable to rename '%s'.\n", psrc ); + return 0; + } + } + else + { + printf( "Moved '%s' to '%s'.\n", psrc, pdest ); + return 1; + } + } + if( !shellh_cp_file( psrc, pdest, flags ) ) + return 0; + if( flags & SHELL_F_MOVE ) + { + if( unlink( psrc ) == -1 ) + { + printf( "Unable to remove original file '%s'.\n", psrc ); + return 0; + } + } + return 1; +} + +// copy/move callback +static int shellh_cp_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) +{ + SHELL_CP_STATE *ps = ( SHELL_CP_STATE* )pdata; + char *tmp = NULL, *tmp2 = NULL; + int res = 1; + DM_DIR *d = NULL; + + switch( info ) + { + case CMN_FS_INFO_BEFORE_READDIR: + if( strstr( path, ps->psrcdir ) != path ) + { + printf( "ERROR: unable to handle directory '%s' (internal error?), aborting.\n", path ); + goto done_err; + } + // Need to create this directory if it does not exist + if( ( tmp = ( char* )cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ) - strlen( ps->dloc ), NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + if( ( d = dm_opendir( tmp ) ) != NULL ) + goto done; + printf( "Creating directory %s ... ", tmp ); + if( ( ps->flags & SHELL_F_SIMULATE_ONLY ) == 0 ) + { + if( mkdir( tmp, 0 ) == -1 ) + { + printf( "ERROR! (aborting).\n" ); + goto done_err; + } + else + printf( "done.\n" ); + } + else + printf( "done.\n" ); + goto done; + + case CMN_FS_INFO_INSIDE_READDIR: + if( !DM_DIRENT_IS_DIR( pent ) ) + { + if( strstr( path, ps->psrcdir ) != path ) + { + printf( "ERROR: unable to handle directory '%s' (internal error?), aborting.\n", path ); + goto done_err; + } + if( ( tmp = cmn_fs_path_join( path, pent->fname, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + if( ( tmp2 = cmn_fs_path_join( ps->pdestdir, path + strlen( ps->psrcdir ) - strlen( ps->dloc ), pent->fname, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + shellh_cp_or_mv_file( tmp, tmp2, ps->flags ); + } + goto done; + + case CMN_FS_INFO_OPENDIR_FAILED: + printf( "ERROR: unable to read directory '%s', aborting.\n", path ); + goto done_err; + + default: + goto done; + } +done_err: + res = 0; +done: + if( tmp ) + free( tmp ); + if( tmp2 ) + free( tmp2 ); + if( d ) + dm_closedir( d ); + return res; +} + +static void shellh_adv_cp_mv_common( int argc, char **argv, int is_move ) +{ + const char *srcpath = NULL, *dstpath = NULL; + unsigned i, flags = is_move ? SHELL_F_MOVE : 0; + int srctype, dsttype; + char *srcdir = NULL, *dstdir = NULL, *tmp = NULL; + const char *srcfile, *dstfile; + SHELL_CP_STATE state; + + if( argc < 3 ) + { + if( is_move ) + SHELL_SHOW_HELP( mv ); + else + SHELL_SHOW_HELP( cp ); + return; + } + for( i = 1; i < argc; i ++ ) + { + if( !strcmp( argv[ i ], "-R" ) ) + flags |= SHELL_F_RECURSIVE; + else if( !strcmp( argv[ i ], "-f" ) ) + flags |= SHELL_F_FORCE_DESTINATION; + else if( !strcmp( argv[ i ], "-c" ) ) + flags |= SHELL_F_ASK_CONFIRMATION; + else if( !strcmp( argv[ i ], "-s" ) ) + flags |= SHELL_F_SIMULATE_ONLY; + else if( argv[ i ][ 0 ] == '/' ) + { + if( !srcpath ) + srcpath = argv[ i ]; + else if( !dstpath ) + dstpath = argv[ i ]; + else + printf( "WARNING: ignoring argument '%s'\n", argv[ i ] ); + } + else + printf( "WARNING: ignoring argument '%s'\n", argv[ i ] ); + } + if( !srcpath || !dstpath ) + { + printf( "Source and/or destination not specified.\n" ); + return; + } + srctype = cmn_fs_get_type( srcpath ); + if( ( dsttype = cmn_fs_get_type( dstpath ) ) == CMN_FS_TYPE_PATTERN ) + { + printf( "Invalid destination '%s'.\n", dstpath ); + goto done; + } + if( srctype == CMN_FS_TYPE_ERROR || srctype == CMN_FS_TYPE_FILE_NOT_FOUND || + srctype == CMN_FS_TYPE_DIR_NOT_FOUND || dsttype == CMN_FS_TYPE_ERROR || + dsttype == CMN_FS_TYPE_DIR_NOT_FOUND ) + { + printf( "Invalid source and/or destination.\n" ); + return; + } + srcdir = cmn_fs_split_path( srcpath, &srcfile ); + dstdir = cmn_fs_split_path( dstpath, &dstfile ); + // Check valid source/destination combinations + if( srctype == CMN_FS_TYPE_FILE ) + { + if( dsttype == CMN_FS_TYPE_FILE || dsttype == CMN_FS_TYPE_FILE_NOT_FOUND ) // direct file-to-file operation + { + shellh_cp_or_mv_file( srcpath, dstpath, flags ); + goto done; + } + else if( dsttype == CMN_FS_TYPE_DIR ) // copy/move to destination dir with the same name + { + if( ( tmp = cmn_fs_path_join( dstdir, srcfile, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done; + } + shellh_cp_or_mv_file( srcpath, tmp, flags ); + goto done; + } + else + { + printf( "Invalid destination.\n" ); + goto done; + } + } + else + { + if( dsttype == CMN_FS_TYPE_FILE || dsttype == CMN_FS_TYPE_FILE_NOT_FOUND ) + { + printf( "Invalid destination '%s'.\n", dstpath ); + goto done; + } + memset( &state, 0, sizeof( state ) ); + state.dloc = NULL; + if( ( flags & SHELL_F_RECURSIVE ) != 0 ) + state.dloc = strrchr( srcdir, '/' ); + if( state.dloc == NULL ) + state.dloc = ""; + state.flags = flags; + state.pdestdir = dstdir; + state.psrcdir = srcdir; + cmn_fs_walkdir( srcpath, shellh_cp_walkdir_cb, &state, flags & SHELL_F_RECURSIVE ); + } +done: + if( srcdir ) + free( srcdir ); + if( dstdir ) + free( dstdir ); + if( tmp ) + free( tmp ); +} + +void shell_cp( int argc, char **argv ) +{ + shellh_adv_cp_mv_common( argc, argv, 0 ); +} + +void shell_adv_mv( int argc, char **argv ) +{ + shellh_adv_cp_mv_common( argc, argv, 1 ); +} + +#else // #ifdef BUILD_ADVANCED_SHELL + +const char shell_help_cp[] = "cp \n" + " : source file/directory/file mask.\n" + " : destination file/directory.\n"; +const char shell_help_summary_cp[] = "copy files"; + +const char shell_help_mv[] = ""; +const char shell_help_summary_mv[] = ""; + +void shell_cp( int argc, char **argv ) +{ + if( argc != 3 ) + { + SHELL_SHOW_HELP( cp ); + return; + } + shellh_cp_file( argv[ 1 ], argv[ 2 ], 0 ); +} + +void shell_adv_mv( int argc, char **argv ) +{ + shellh_not_implemented_handler( argc, argv ); +} + +#endif // #ifdef BUILD_ADVANCED_SHELL + diff --git a/src/shell/shell_adv_rm.c b/src/shell/shell_adv_rm.c new file mode 100644 index 00000000..dbe3354a --- /dev/null +++ b/src/shell/shell_adv_rm.c @@ -0,0 +1,153 @@ +// Advanced shell: 'rm' implementation + +#include +#include +#include +#include +#include +#include +#include "shell.h" +#include "common.h" +#include "type.h" +#include "platform_conf.h" + +#ifdef BUILD_ADVANCED_SHELL + +const char shell_help_rm[] = " [-R] [-c] [-s]\n" + " : file, directory or file mask to remove.\n" + " -R: remove recursively.\n" + " -c: confirm each remove.\n" + " -s: simulate only (no actual operation).\n"; +const char shell_help_summary_rm[] = "remove files"; + +// helper: remove a single file and/or directory +static void shellh_rm_one( const char* path, int flags ) +{ + int ftype = cmn_fs_get_type( path ); + int res = 0; + + if( flags & SHELL_F_ASK_CONFIRMATION ) + { + printf( "Are you sure you want to remove %s ? [y/n] ", path ); + if( shellh_ask_yes_no( NULL ) == 0 ) + return; + } + if( ( flags & SHELL_F_SIMULATE_ONLY ) == 0 ) + { + if( ftype == CMN_FS_TYPE_FILE ) + res = unlink( path ); + else if( ftype == CMN_FS_TYPE_DIR ) + res = rmdir( path ); + else + { + printf( "WARNING: invalid argument '%s'\n", path ); + return; + } + } + if( res ) + { + if( ( flags & SHELL_F_SILENT ) == 0 ) + printf( "WARNING: unable to remove %s\n", path ); + } + else + printf( "Removed %s\n", path ); +} + +static int shellh_rm_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) +{ + u8 *pflags = ( u8* )pdata; + char *tmp = NULL; + int res = 1; + + switch( info ) + { + case CMN_FS_INFO_BEFORE_READDIR: + goto done; + + case CMN_FS_INFO_INSIDE_READDIR: + if( ( tmp = cmn_fs_path_join( path, pent->fname, NULL ) ) == NULL ) + { + printf( "Not enough memory.\n" ); + goto done_err; + } + if( cmn_fs_get_type( tmp ) == CMN_FS_TYPE_FILE ) + shellh_rm_one( tmp, *pflags ); + goto done; + + case CMN_FS_INFO_OPENDIR_FAILED: + printf( "ERROR: unable to read directory '%s', aborting.\n", path ); + goto done_err; + + case CMN_FS_INFO_DIRECTORY_DONE: + if( ( *pflags & SHELL_F_RECURSIVE ) && !cmn_fs_is_root_dir( path ) ) + shellh_rm_one( path, *pflags | SHELL_F_SILENT ); + goto done; + + default: + goto done; + } +done_err: + res = 0; +done: + if( tmp ) + free( tmp ); + return res; +} + +void shell_adv_rm( int argc, char **argv ) +{ + const char *fmask = NULL; + unsigned i, flags = 0; + int masktype; + + if( argc < 2 ) + { + SHELL_SHOW_HELP( rm ); + return; + } + for( i = 1; i < argc; i ++ ) + { + if( argv[ i ][ 0 ] == '/' ) + { + if( !fmask ) + fmask = argv[ i ]; + else + printf( "Warning: ignoring argument '%s'\n", argv[ i ] ); + } + else if( !strcmp( argv[ i ], "-R" ) ) + flags |= SHELL_F_RECURSIVE; + else if( !strcmp( argv[ i ], "-c" ) ) + flags |= SHELL_F_ASK_CONFIRMATION; + else if( !strcmp( argv[ i ], "-s" ) ) + flags |= SHELL_F_SIMULATE_ONLY; + else + printf( "Warning: ignoring argument '%s'\n", argv[ i ] ); + } + if( !fmask ) + { + printf( "rm target not specified.\n" ); + return; + } + masktype = cmn_fs_get_type( fmask ); + if( masktype == CMN_FS_TYPE_FILE ) + shellh_rm_one( fmask, flags ); + else if( masktype == CMN_FS_TYPE_ERROR || masktype == CMN_FS_TYPE_FILE_NOT_FOUND || masktype == CMN_FS_TYPE_DIR_NOT_FOUND ) + printf( "Invalid argument '%s'.\n", fmask ); + else if( masktype == CMN_FS_TYPE_DIR && ( ( flags & SHELL_F_RECURSIVE ) == 0 ) ) + printf( "'%s': unable to remove directory (use '-R').\n", fmask ); + else + cmn_fs_walkdir( fmask, shellh_rm_walkdir_cb, &flags, flags & SHELL_F_RECURSIVE ); +} + +#else // #ifdef BUILD_ADVANCED_SHELL + +const char shell_help_rm[] = ""; +const char shell_help_summary_rm[] = ""; + +void shell_adv_rm( int argc, char **argv ) +{ + shellh_not_implemented_handler( argc, argv ); +} + +#endif // #ifdef BUILD_ADVANCED_SHELL + diff --git a/src/shell/shell_cat.c b/src/shell/shell_cat.c new file mode 100644 index 00000000..25b01d95 --- /dev/null +++ b/src/shell/shell_cat.c @@ -0,0 +1,47 @@ +// Shell: 'cat' implementation + +#include +#include +#include +#include +#include +#include +#include "shell.h" +#include "common.h" +#include "type.h" +#include "platform_conf.h" + +const char shell_help_cat[] = " [] ... []" + " : the file to list.\n" + " [] ... []: other files to list.\n"; +const char shell_help_summary_cat[] = "list the contents of a file"; + +void shell_cat( int argc, char **argv ) +{ + FILE *fp; + int c; + unsigned i; + + if( argc < 2 ) + { + shellh_show_help( argv[ 0 ], shell_help_cat ); + return; + } + for( i = 1; i < argc; i ++ ) + { + if( ( fp = fopen( argv[ i ] , "rb" ) ) != NULL ) + { + c = fgetc( fp ); + while( c != EOF ) + { + printf("%c", (char) c ); + c = fgetc( fp ); + } + fclose ( fp ); + } + else + printf( "Unable to open '%s'\n", argv[ i ] ); + } +} + + diff --git a/src/shell/shell_help.c b/src/shell/shell_help.c new file mode 100644 index 00000000..05b49214 --- /dev/null +++ b/src/shell/shell_help.c @@ -0,0 +1,113 @@ +// Shell: 'help' implementation + +#include +#include +#include +#include +#include +#include +#include "shell.h" +#include "common.h" +#include "type.h" +#include "platform_conf.h" + +// External help text declaration +#define SHELL_HELP( topic ) extern const char shell_help_##topic[];\ + extern const char shell_help_summary_##topic[] +#define SHELL_INFO( cmd ) { #cmd, shell_help_summary_##cmd, shell_help_##cmd } +#define SHELL_INFO_ALIAS( cmd, alias ) { #cmd, shell_help_summary_##alias, shell_help_##alias } + +// Help data +typedef struct +{ + const char *cmd; + const char *help_summary; + const char *help_full; +} SHELL_HELP_DATA; + +SHELL_HELP( cp ); +SHELL_HELP( rm ); +SHELL_HELP( ls ); +SHELL_HELP( recv ); +SHELL_HELP( cat ); +SHELL_HELP( lua ); +SHELL_HELP( ver ); +SHELL_HELP( mkdir ); +SHELL_HELP( wofmt ); +// 'mv' is special, as it uses the main help text from 'cp' +extern const char shell_help_summary_mv[]; + +// 'Help' help data is local for ovious reasons +static const char shell_help_help[] = "[]\n" + " [] - the command to get help on.\n" + "Without arguments it shows a summary of all the shell commands.\n"; +static const char shell_help_summary_help[] = "shell help"; + +// Also put the help for 'exit' here +static const char shell_help_exit[] = "\n" + "Exits the shell.\n"; +static const char shell_help_summary_exit[] = "exit the shell"; + +static const SHELL_HELP_DATA shell_help_data[] = +{ + SHELL_INFO( help ), + SHELL_INFO( lua ), + SHELL_INFO( ls ), + SHELL_INFO_ALIAS( dir, ls ), + SHELL_INFO( cat ), + SHELL_INFO_ALIAS( type, cat ), + SHELL_INFO( recv ), + SHELL_INFO( cp ), + // Yes, 'mv' is still special + { "mv", shell_help_summary_mv, shell_help_cp }, + SHELL_INFO( rm ), + SHELL_INFO( ver ), + SHELL_INFO( mkdir ), + SHELL_INFO( wofmt ), + SHELL_INFO( exit ), + { NULL, NULL, NULL } +}; + +void shell_help( int argc, char **argv ) +{ + const SHELL_HELP_DATA *ph; + + if( argc > 2 ) + { + SHELL_SHOW_HELP( help ); + 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) + printf( "Shell commands:\n" ); + while( 1 ) + { + if( ph->cmd == NULL ) + break; + if( strlen( ph->help_summary ) > 0 ) + printf( " %-6s - %s\n", ph->cmd, ph->help_summary ); + ph ++; + } + printf( "For more information use 'help '.\n" ); + } + else + { + while( 1 ) + { + if( ph->cmd == NULL ) + break; + if( !strcmp( ph->cmd, argv[ 1 ] ) && strlen( ph->help_summary ) > 0 ) + { + printf( "%s - %s\nUsage: %s %s", ph->cmd, ph->help_summary, ph->cmd, ph->help_full ); + return; + } + ph ++; + } + printf( "Unknown command '%s'.\n", argv[ 1 ] ); + } +} + diff --git a/src/shell/shell_ls.c b/src/shell/shell_ls.c new file mode 100644 index 00000000..658acf99 --- /dev/null +++ b/src/shell/shell_ls.c @@ -0,0 +1,100 @@ +// Shell: 'ls' implementation + +#include +#include +#include +#include +#include +#include +#include "shell.h" +#include "common.h" +#include "type.h" +#include "platform_conf.h" + +// State for walkdir +typedef struct +{ + u32 dir_total; + u32 total; + u8 ndirs; +} SHELL_LS_STATE; + +const char shell_help_ls[] = "[] [-R]\n" + " []: path to list.\n" + " [-R]: recursive\n"; +const char shell_help_summary_ls[] = "lists files and directories"; + +// 'ls' and 'dir' handler +// Syntax: ls [dir] [-R] +// directory walker callback +static int shellh_ls_walkdir_cb( const char *path, const struct dm_dirent *pent, void *pdata, int info ) +{ + SHELL_LS_STATE *ps = ( SHELL_LS_STATE* )pdata; + + switch( info ) + { + case CMN_FS_INFO_BEFORE_READDIR: + ps->dir_total = 0; + printf( "%s\n", path ); + if( ps->ndirs != 0xFF ) + ps->ndirs ++; + break; + + case CMN_FS_INFO_INSIDE_READDIR: + printf( " %-30s", pent->fname ); + if( DM_DIRENT_IS_DIR( pent ) ) + printf( "\n" ); + else + { + printf( "%u bytes\n", ( unsigned )pent->fsize ); + ps->dir_total += pent->fsize; + } + break; + + case CMN_FS_INFO_AFTER_CLOSEDIR: + printf( "Total on %s: %u bytes\n\n", path, ( unsigned )ps->dir_total ); + ps->total += ps->dir_total; + break; + + case CMN_FS_INFO_OPENDIR_FAILED: + printf( "WARNING: unable to open %s\n", path ); + break; + } + return 1; +} + +void shell_ls( int argc, char **argv ) +{ + const DM_INSTANCE_DATA *pinst; + unsigned i; + int recursive = 0; + char *pname = NULL; + const char *crtname; + SHELL_LS_STATE state; + + for( i = 1; i < argc; i ++ ) + { + if( !strcmp( argv[ i ], "-R" ) ) + recursive = 1; + else if( argv[ i ][ 0 ] == '/' && !pname ) + pname = argv[ i ]; + else + printf( "Warning: ignoring argument '%s' of ls", argv[ i ] ); + } + // Iterate through all devices, looking for the ones that can do "opendir" + // or the ones that match 'pname' (if that is specified) + for( i = 0; i < dm_get_num_devices(); i ++ ) + { + pinst = dm_get_instance_at( i ); + if( pinst->pdev->p_opendir_r == NULL || pinst->pdev->p_readdir_r == NULL || pinst->pdev->p_closedir_r == NULL ) + continue; + if( pname && strncmp( pinst->name, pname, strlen( pinst->name ) ) ) + continue; + crtname = pname ? pname : pinst->name; + memset( &state, 0, sizeof( state ) ); + cmn_fs_walkdir( crtname, shellh_ls_walkdir_cb, &state, recursive ); + if( recursive && ( state.ndirs > 1 ) ) + printf( "Total on %s with all subdirectories: %u bytes\n\n", crtname, ( unsigned )state.total ); + } +} + diff --git a/src/shell/shell_lua.c b/src/shell/shell_lua.c new file mode 100644 index 00000000..078682b7 --- /dev/null +++ b/src/shell/shell_lua.c @@ -0,0 +1,36 @@ +// Shell: 'lua' implementation + +#include +#include +#include +#include +#include +#include +#include "shell.h" +#include "common.h" +#include "type.h" +#include "platform_conf.h" + +// EOF is different in UART mode and TCP/IP mode +#ifdef BUILD_CON_GENERIC + #define SHELL_EOF_STRING "CTRL+Z" +#else + #define SHELL_EOF_STRING "CTRL+D" +#endif + +const char shell_help_lua[] = "[-e ] [-l ] [-i] [-v] [