1
0
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:
Bogdan Marinescu 2012-09-19 00:09:39 +03:00
parent 9e7f1ab4cd
commit 389ce9b41a
5 changed files with 658 additions and 106 deletions

View File

@ -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 );

View File

@ -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;

View File

@ -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
View 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;
}

View File

@ -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 )