mirror of
https://github.com/elua/elua.git
synced 2025-01-25 01:02:54 +08:00
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)
This commit is contained in:
parent
9e7f1ab4cd
commit
389ce9b41a
22
inc/common.h
22
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 );
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
18
src/common.c
18
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 )
|
||||
{
|
||||
|
253
src/common_fs.c
Normal file
253
src/common_fs.c
Normal file
@ -0,0 +1,253 @@
|
||||
// Common file system related operations
|
||||
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
#include "devman.h"
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
469
src/shell.c
469
src/shell.c
@ -20,6 +20,7 @@
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#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( "<DIR>" );
|
||||
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( "<DIR>\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 <source> <destination>\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 <source> <destination> [-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 )
|
||||
|
Loading…
x
Reference in New Issue
Block a user