|
|
@@ -1,439 +1,438 @@
|
|
|
-/**************************************************************************/
|
|
|
-/*!
|
|
|
- @file cli.c
|
|
|
- @author hathach (tinyusb.org)
|
|
|
-
|
|
|
- @section LICENSE
|
|
|
-
|
|
|
- Software License Agreement (BSD License)
|
|
|
-
|
|
|
- Copyright (c) 2013, hathach (tinyusb.org)
|
|
|
- All rights reserved.
|
|
|
-
|
|
|
- Redistribution and use in source and binary forms, with or without
|
|
|
- modification, are permitted provided that the following conditions are met:
|
|
|
- 1. Redistributions of source code must retain the above copyright
|
|
|
- notice, this list of conditions and the following disclaimer.
|
|
|
- 2. Redistributions in binary form must reproduce the above copyright
|
|
|
- notice, this list of conditions and the following disclaimer in the
|
|
|
- documentation and/or other materials provided with the distribution.
|
|
|
- 3. Neither the name of the copyright holders nor the
|
|
|
- names of its contributors may be used to endorse or promote products
|
|
|
- derived from this software without specific prior written permission.
|
|
|
-
|
|
|
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
|
|
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
|
|
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
-*/
|
|
|
-/**************************************************************************/
|
|
|
-
|
|
|
-#include "cli.h"
|
|
|
-#include "ctype.h"
|
|
|
-
|
|
|
-#if TUSB_CFG_HOST_MSC
|
|
|
-
|
|
|
-#include "ff.h"
|
|
|
-#include "diskio.h"
|
|
|
-#include "boards/ansi_escape.h"
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// MACRO CONSTANT TYPEDEF
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-#define CLI_MAX_BUFFER 256
|
|
|
-#define CLI_FILE_READ_BUFFER (4*1024)
|
|
|
-
|
|
|
-enum {
|
|
|
- ASCII_BACKSPACE = 8,
|
|
|
-};
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- CLI_ERROR_NONE = 0,
|
|
|
- CLI_ERROR_INVALID_PARA,
|
|
|
- CLI_ERROR_INVALID_PATH,
|
|
|
- CLI_ERROR_FILE_EXISTED,
|
|
|
- CLI_ERROR_FAILED
|
|
|
-}cli_error_t;
|
|
|
-
|
|
|
-static char const * const cli_error_message[] =
|
|
|
-{
|
|
|
- [CLI_ERROR_NONE ] = 0,
|
|
|
- [CLI_ERROR_INVALID_PARA ] = "Invalid parameter(s)",
|
|
|
- [CLI_ERROR_INVALID_PATH ] = "No such file or directory",
|
|
|
- [CLI_ERROR_FILE_EXISTED ] = "file or directory already exists",
|
|
|
- [CLI_ERROR_FAILED ] = "failed to execute"
|
|
|
-};
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// CLI Database definition
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-
|
|
|
-// command, function, description
|
|
|
-#define CLI_COMMAND_TABLE(ENTRY) \
|
|
|
- ENTRY(unknown , cli_cmd_unknown , NULL ) \
|
|
|
- ENTRY(help , cli_cmd_help , NULL ) \
|
|
|
- ENTRY(cls , cli_cmd_clear , "Clear the screen." ) \
|
|
|
- ENTRY(ls , cli_cmd_list , "List information about the FILEs (the current directory by default).") \
|
|
|
- ENTRY(cd , cli_cmd_changedir, "change the current directory." ) \
|
|
|
- ENTRY(cat , cli_cmd_cat , "display contents of a file." ) \
|
|
|
- ENTRY(cp , cli_cmd_copy , "Copies one or more files to another location." ) \
|
|
|
- ENTRY(mkdir , cli_cmd_mkdir , "Create a DIRECTORY, if it does not already exist." ) \
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Expands the function to have the standard function signature
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-#define CLI_PROTOTYPE_EXPAND(command, function, description) \
|
|
|
- cli_error_t function(char *);
|
|
|
-
|
|
|
-CLI_COMMAND_TABLE(CLI_PROTOTYPE_EXPAND);
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Expand to enum value
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-#define CLI_ENUM_EXPAND(command, function, description) CLI_CMDTYPE_##command,
|
|
|
-typedef enum {
|
|
|
- CLI_COMMAND_TABLE(CLI_ENUM_EXPAND)
|
|
|
- CLI_CMDTYPE_COUNT
|
|
|
-}cli_cmdtype_t;
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Expand to string table
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-#define CLI_STRING_EXPAND(command, function, description) #command,
|
|
|
-char const* const cli_string_tbl[] =
|
|
|
-{
|
|
|
- CLI_COMMAND_TABLE(CLI_STRING_EXPAND)
|
|
|
- 0
|
|
|
-};
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Expand to Description table
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-#define CLI_DESCRIPTION_EXPAND(command, function, description) description,
|
|
|
-char const* const cli_description_tbl[] =
|
|
|
-{
|
|
|
- CLI_COMMAND_TABLE(CLI_DESCRIPTION_EXPAND)
|
|
|
- 0
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Expand to Command Lookup Table
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-#define CMD_LOOKUP_EXPAND(command, function, description)\
|
|
|
- [CLI_CMDTYPE_##command] = function,\
|
|
|
-
|
|
|
-typedef cli_error_t (* const cli_cmdfunc_t)(char *);
|
|
|
-static cli_cmdfunc_t cli_command_tbl[] =
|
|
|
-{
|
|
|
- CLI_COMMAND_TABLE(CMD_LOOKUP_EXPAND)
|
|
|
-};
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// INTERNAL OBJECT & FUNCTION DECLARATION
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-static char cli_buffer[CLI_MAX_BUFFER];
|
|
|
-uint8_t fileread_buffer[CLI_FILE_READ_BUFFER] TUSB_CFG_ATTR_USBRAM;
|
|
|
-static char volume_label[20];
|
|
|
-
|
|
|
-static inline void drive_number2letter(char * p_path) ATTR_ALWAYS_INLINE;
|
|
|
-static inline void drive_number2letter(char * p_path)
|
|
|
-{
|
|
|
- if (p_path[1] == ':')
|
|
|
- {
|
|
|
- p_path[0] = 'E' + p_path[0] - '0' ;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static inline void drive_letter2number(char * p_path) ATTR_ALWAYS_INLINE;
|
|
|
-static inline void drive_letter2number(char * p_path)
|
|
|
-{
|
|
|
- if (p_path[1] == ':')
|
|
|
- {
|
|
|
- p_path[0] = p_path[0] - 'E' + '0';
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// IMPLEMENTATION
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// NOTES: prompt re-use cli_buffer --> should not be called when cli_buffer has contents
|
|
|
-void cli_command_prompt(void)
|
|
|
-{
|
|
|
- f_getcwd(cli_buffer, CLI_MAX_BUFFER);
|
|
|
- drive_number2letter(cli_buffer);
|
|
|
- printf("\n%s %s\n$ ",
|
|
|
- (volume_label[0] !=0) ? volume_label : "No Label",
|
|
|
- cli_buffer);
|
|
|
-
|
|
|
- memclr_(cli_buffer, CLI_MAX_BUFFER);
|
|
|
-}
|
|
|
-
|
|
|
-void cli_init(void)
|
|
|
-{
|
|
|
- memclr_(cli_buffer, CLI_MAX_BUFFER);
|
|
|
- f_getlabel(NULL, volume_label, NULL);
|
|
|
- cli_command_prompt();
|
|
|
-}
|
|
|
-
|
|
|
-void cli_poll(char ch)
|
|
|
-{
|
|
|
- if ( isprint(ch) )
|
|
|
- { // accumulate & echo
|
|
|
- if (strlen(cli_buffer) < CLI_MAX_BUFFER)
|
|
|
- {
|
|
|
- cli_buffer[ strlen(cli_buffer) ] = ch;
|
|
|
- putchar(ch);
|
|
|
- }else
|
|
|
- {
|
|
|
- puts("cli buffer overflows");
|
|
|
- memclr_(cli_buffer, CLI_MAX_BUFFER);
|
|
|
- }
|
|
|
- }
|
|
|
- else if ( ch == ASCII_BACKSPACE && strlen(cli_buffer))
|
|
|
- {
|
|
|
- printf(ANSI_CURSOR_BACKWARD(1) ANSI_ERASE_LINE(0) ); // move cursor back & clear to the end of line
|
|
|
- cli_buffer[ strlen(cli_buffer)-1 ] = 0;
|
|
|
- }
|
|
|
- else if ( ch == '\r')
|
|
|
- { // execute command
|
|
|
- //------------- Separate Command & Parameter -------------//
|
|
|
- putchar('\n');
|
|
|
- char* p_space = strchr(cli_buffer, ' ');
|
|
|
- uint32_t command_len = (p_space == NULL) ? strlen(cli_buffer) : (p_space - cli_buffer);
|
|
|
- char* p_para = (p_space == NULL) ? (cli_buffer+command_len) : (p_space+1); // point to NULL-character or after space
|
|
|
-
|
|
|
- //------------- Find entered command in lookup table & execute it -------------//
|
|
|
- cli_cmdtype_t cmd_id;
|
|
|
- for(cmd_id = CLI_CMDTYPE_COUNT - 1; cmd_id > CLI_CMDTYPE_unknown; cmd_id--)
|
|
|
- {
|
|
|
- if( 0 == strncmp(cli_buffer, cli_string_tbl[cmd_id], command_len) )
|
|
|
- {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- cli_error_t error = cli_command_tbl[cmd_id]( p_para ); // command execution, (unknown command if cannot find)
|
|
|
-
|
|
|
- if (CLI_ERROR_NONE != error) puts(cli_error_message[error]); // error message output if any
|
|
|
-
|
|
|
- //------------- print out current path -------------//
|
|
|
- cli_command_prompt();
|
|
|
- }
|
|
|
- else if (ch=='\t') // \t may be used for auto-complete later
|
|
|
- {
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// UNKNOWN Command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_unknown(char * para)
|
|
|
-{
|
|
|
- puts("unknown command, please type \"help\" for list of supported commands");
|
|
|
- return CLI_ERROR_NONE;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// HELP command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_help(char * para)
|
|
|
-{
|
|
|
- puts("current supported commands are:");
|
|
|
- for(cli_cmdtype_t cmd_id = CLI_CMDTYPE_help+1; cmd_id < CLI_CMDTYPE_COUNT; cmd_id++)
|
|
|
- {
|
|
|
- printf("%s\t%s\n", cli_string_tbl[cmd_id], cli_description_tbl[cmd_id]);
|
|
|
- }
|
|
|
-
|
|
|
- return CLI_ERROR_NONE;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Clear Screen Command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_clear(char* p_para)
|
|
|
-{
|
|
|
- printf(ANSI_ERASE_SCREEN(2) ANSI_CURSOR_POSITION(1,1) );
|
|
|
- return CLI_ERROR_NONE;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// LS Command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_list(char * p_para)
|
|
|
-{
|
|
|
- if ( strlen(p_para) == 0 ) // list current directory
|
|
|
- {
|
|
|
- DIR target_dir;
|
|
|
- if ( FR_OK != f_opendir(&target_dir, ".") ) return CLI_ERROR_FAILED;
|
|
|
-
|
|
|
- TCHAR long_filename[_MAX_LFN];
|
|
|
- FILINFO dir_entry =
|
|
|
- {
|
|
|
- .lfname = long_filename,
|
|
|
- .lfsize = _MAX_LFN
|
|
|
- };
|
|
|
- while( (f_readdir(&target_dir, &dir_entry) == FR_OK) && dir_entry.fname[0] != 0)
|
|
|
- {
|
|
|
- if ( dir_entry.fname[0] != '.' ) // ignore . and .. entry
|
|
|
- {
|
|
|
- TCHAR const * const p_name = (dir_entry.lfname[0] != 0) ? dir_entry.lfname : dir_entry.fname;
|
|
|
- if ( dir_entry.fattrib & AM_DIR ) // directory
|
|
|
- {
|
|
|
- printf("/%s", p_name);
|
|
|
- }else
|
|
|
- {
|
|
|
- printf("%-40s%d KB", p_name, dir_entry.fsize / 1000);
|
|
|
- }
|
|
|
- putchar('\n');
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-// (void) f_closedir(&target_dir);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- puts("ls only supports list current directory only, try to cd to that folder first");
|
|
|
- return CLI_ERROR_INVALID_PARA;
|
|
|
- }
|
|
|
-
|
|
|
- return CLI_ERROR_NONE;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// CD Command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_changedir(char * p_para)
|
|
|
-{
|
|
|
- if ( strlen(p_para) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
-
|
|
|
- drive_letter2number(p_para);
|
|
|
-
|
|
|
- if ( FR_OK != f_chdir(p_para) )
|
|
|
- {
|
|
|
- return CLI_ERROR_INVALID_PATH;
|
|
|
- }
|
|
|
-
|
|
|
- if ( p_para[1] == ':')
|
|
|
- { // path has drive letter --> change drive, update volume label
|
|
|
- f_chdrive(p_para[0] - '0');
|
|
|
- f_getlabel(NULL, volume_label, NULL);
|
|
|
- }
|
|
|
-
|
|
|
- return CLI_ERROR_NONE;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// CAT Command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_cat(char *p_para)
|
|
|
-{
|
|
|
- if ( strlen(p_para) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
-
|
|
|
- FIL file;
|
|
|
-
|
|
|
- switch( f_open(&file, p_para, FA_READ) )
|
|
|
- {
|
|
|
- case FR_OK:
|
|
|
- {
|
|
|
- uint32_t bytes_read = 0;
|
|
|
-
|
|
|
- if ( (FR_OK == f_read(&file, fileread_buffer, CLI_FILE_READ_BUFFER, &bytes_read)) && (bytes_read > 0) )
|
|
|
- {
|
|
|
- if ( file.fsize < 0x80000 ) // ~ 500KB
|
|
|
- {
|
|
|
- putchar('\n');
|
|
|
- do {
|
|
|
- for(uint32_t i=0; i<bytes_read; i++) putchar( fileread_buffer[i] );
|
|
|
- }while( (FR_OK == f_read(&file, fileread_buffer, CLI_FILE_READ_BUFFER, &bytes_read)) && (bytes_read > 0) );
|
|
|
- }else
|
|
|
- { // not display file contents if first character is not printable (high chance of binary file)
|
|
|
- printf("%s 's contents is too large\n", p_para);
|
|
|
- }
|
|
|
- }
|
|
|
- f_close(&file);
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case FR_INVALID_NAME:
|
|
|
- return CLI_ERROR_INVALID_PATH;
|
|
|
-
|
|
|
- default :
|
|
|
- return CLI_ERROR_FAILED;
|
|
|
- }
|
|
|
-
|
|
|
- return CLI_ERROR_NONE;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// Make Directory command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_mkdir(char *p_para)
|
|
|
-{
|
|
|
- if ( strlen(p_para) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
-
|
|
|
- return (f_mkdir(p_para) == FR_OK) ? CLI_ERROR_NONE : CLI_ERROR_FAILED;
|
|
|
-}
|
|
|
-
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-// COPY command
|
|
|
-//--------------------------------------------------------------------+
|
|
|
-cli_error_t cli_cmd_copy(char *p_para)
|
|
|
-{
|
|
|
- char* p_space = strchr(p_para, ' ');
|
|
|
- if ( p_space == NULL ) return CLI_ERROR_INVALID_PARA;
|
|
|
-
|
|
|
- *p_space = 0; // replace space by NULL-character
|
|
|
- char* p_dest = p_space+1;
|
|
|
-
|
|
|
- if ( strlen(p_dest) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
-
|
|
|
- drive_letter2number(p_para);
|
|
|
- drive_letter2number(p_dest);
|
|
|
-
|
|
|
- //------------- Check Existence of source & dest file -------------//
|
|
|
- cli_error_t error = CLI_ERROR_NONE;
|
|
|
- FIL src_file, dest_file;
|
|
|
-
|
|
|
- if ( FR_OK != f_open(&src_file , p_para, FA_READ) ) return CLI_ERROR_INVALID_PATH;
|
|
|
- switch ( f_open(&dest_file, p_dest, FA_WRITE | FA_CREATE_NEW) )
|
|
|
- {
|
|
|
- case FR_EXIST:
|
|
|
- error = CLI_ERROR_FILE_EXISTED;
|
|
|
- break;\
|
|
|
-
|
|
|
- case FR_OK:
|
|
|
- while(1)
|
|
|
- {
|
|
|
- uint32_t bytes_read = 0;
|
|
|
- uint32_t bytes_write = 0;
|
|
|
- FRESULT res;
|
|
|
-
|
|
|
- res = f_read(&src_file, fileread_buffer, CLI_FILE_READ_BUFFER, &bytes_read); /* Read a chunk of src file */
|
|
|
- if ( (res != FR_OK) || (bytes_read == 0) ) break; /* error or eof */
|
|
|
-
|
|
|
- res = f_write(&dest_file, fileread_buffer, bytes_read, &bytes_write); /* Write it to the dst file */
|
|
|
- if ( (res != FR_OK) || (bytes_write < bytes_read) ) break; /* error or disk full */
|
|
|
- }
|
|
|
-
|
|
|
- f_close(&dest_file);
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- error = CLI_ERROR_FAILED;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- f_close(&src_file);
|
|
|
-
|
|
|
- return error;
|
|
|
-}
|
|
|
-#endif
|
|
|
+/**************************************************************************/
|
|
|
+/*!
|
|
|
+ @file cli.c
|
|
|
+ @author hathach (tinyusb.org)
|
|
|
+
|
|
|
+ @section LICENSE
|
|
|
+
|
|
|
+ Software License Agreement (BSD License)
|
|
|
+
|
|
|
+ Copyright (c) 2013, hathach (tinyusb.org)
|
|
|
+ All rights reserved.
|
|
|
+
|
|
|
+ Redistribution and use in source and binary forms, with or without
|
|
|
+ modification, are permitted provided that the following conditions are met:
|
|
|
+ 1. Redistributions of source code must retain the above copyright
|
|
|
+ notice, this list of conditions and the following disclaimer.
|
|
|
+ 2. Redistributions in binary form must reproduce the above copyright
|
|
|
+ notice, this list of conditions and the following disclaimer in the
|
|
|
+ documentation and/or other materials provided with the distribution.
|
|
|
+ 3. Neither the name of the copyright holders nor the
|
|
|
+ names of its contributors may be used to endorse or promote products
|
|
|
+ derived from this software without specific prior written permission.
|
|
|
+
|
|
|
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
|
|
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
|
|
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+*/
|
|
|
+/**************************************************************************/
|
|
|
+
|
|
|
+#include "cli.h"
|
|
|
+#include "ctype.h"
|
|
|
+
|
|
|
+#if TUSB_CFG_HOST_MSC
|
|
|
+
|
|
|
+#include "ff.h"
|
|
|
+#include "diskio.h"
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// MACRO CONSTANT TYPEDEF
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+#define CLI_MAX_BUFFER 256
|
|
|
+#define CLI_FILE_READ_BUFFER (4*1024)
|
|
|
+
|
|
|
+enum {
|
|
|
+ ASCII_BACKSPACE = 8,
|
|
|
+};
|
|
|
+
|
|
|
+typedef enum {
|
|
|
+ CLI_ERROR_NONE = 0,
|
|
|
+ CLI_ERROR_INVALID_PARA,
|
|
|
+ CLI_ERROR_INVALID_PATH,
|
|
|
+ CLI_ERROR_FILE_EXISTED,
|
|
|
+ CLI_ERROR_FAILED
|
|
|
+}cli_error_t;
|
|
|
+
|
|
|
+static char const * const cli_error_message[] =
|
|
|
+{
|
|
|
+ [CLI_ERROR_NONE ] = 0,
|
|
|
+ [CLI_ERROR_INVALID_PARA ] = "Invalid parameter(s)",
|
|
|
+ [CLI_ERROR_INVALID_PATH ] = "No such file or directory",
|
|
|
+ [CLI_ERROR_FILE_EXISTED ] = "file or directory already exists",
|
|
|
+ [CLI_ERROR_FAILED ] = "failed to execute"
|
|
|
+};
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// CLI Database definition
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+
|
|
|
+// command, function, description
|
|
|
+#define CLI_COMMAND_TABLE(ENTRY) \
|
|
|
+ ENTRY(unknown , cli_cmd_unknown , NULL ) \
|
|
|
+ ENTRY(help , cli_cmd_help , NULL ) \
|
|
|
+ ENTRY(cls , cli_cmd_clear , "Clear the screen." ) \
|
|
|
+ ENTRY(ls , cli_cmd_list , "List information about the FILEs (the current directory by default).") \
|
|
|
+ ENTRY(cd , cli_cmd_changedir, "change the current directory." ) \
|
|
|
+ ENTRY(cat , cli_cmd_cat , "display contents of a file." ) \
|
|
|
+ ENTRY(cp , cli_cmd_copy , "Copies one or more files to another location." ) \
|
|
|
+ ENTRY(mkdir , cli_cmd_mkdir , "Create a DIRECTORY, if it does not already exist." ) \
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Expands the function to have the standard function signature
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+#define CLI_PROTOTYPE_EXPAND(command, function, description) \
|
|
|
+ cli_error_t function(char *);
|
|
|
+
|
|
|
+CLI_COMMAND_TABLE(CLI_PROTOTYPE_EXPAND);
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Expand to enum value
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+#define CLI_ENUM_EXPAND(command, function, description) CLI_CMDTYPE_##command,
|
|
|
+typedef enum {
|
|
|
+ CLI_COMMAND_TABLE(CLI_ENUM_EXPAND)
|
|
|
+ CLI_CMDTYPE_COUNT
|
|
|
+}cli_cmdtype_t;
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Expand to string table
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+#define CLI_STRING_EXPAND(command, function, description) #command,
|
|
|
+char const* const cli_string_tbl[] =
|
|
|
+{
|
|
|
+ CLI_COMMAND_TABLE(CLI_STRING_EXPAND)
|
|
|
+ 0
|
|
|
+};
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Expand to Description table
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+#define CLI_DESCRIPTION_EXPAND(command, function, description) description,
|
|
|
+char const* const cli_description_tbl[] =
|
|
|
+{
|
|
|
+ CLI_COMMAND_TABLE(CLI_DESCRIPTION_EXPAND)
|
|
|
+ 0
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Expand to Command Lookup Table
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+#define CMD_LOOKUP_EXPAND(command, function, description)\
|
|
|
+ [CLI_CMDTYPE_##command] = function,\
|
|
|
+
|
|
|
+typedef cli_error_t (* const cli_cmdfunc_t)(char *);
|
|
|
+static cli_cmdfunc_t cli_command_tbl[] =
|
|
|
+{
|
|
|
+ CLI_COMMAND_TABLE(CMD_LOOKUP_EXPAND)
|
|
|
+};
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// INTERNAL OBJECT & FUNCTION DECLARATION
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+static char cli_buffer[CLI_MAX_BUFFER];
|
|
|
+uint8_t fileread_buffer[CLI_FILE_READ_BUFFER] TUSB_CFG_ATTR_USBRAM;
|
|
|
+static char volume_label[20];
|
|
|
+
|
|
|
+static inline void drive_number2letter(char * p_path) ATTR_ALWAYS_INLINE;
|
|
|
+static inline void drive_number2letter(char * p_path)
|
|
|
+{
|
|
|
+ if (p_path[1] == ':')
|
|
|
+ {
|
|
|
+ p_path[0] = 'E' + p_path[0] - '0' ;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static inline void drive_letter2number(char * p_path) ATTR_ALWAYS_INLINE;
|
|
|
+static inline void drive_letter2number(char * p_path)
|
|
|
+{
|
|
|
+ if (p_path[1] == ':')
|
|
|
+ {
|
|
|
+ p_path[0] = p_path[0] - 'E' + '0';
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// IMPLEMENTATION
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// NOTES: prompt re-use cli_buffer --> should not be called when cli_buffer has contents
|
|
|
+void cli_command_prompt(void)
|
|
|
+{
|
|
|
+ f_getcwd(cli_buffer, CLI_MAX_BUFFER);
|
|
|
+ drive_number2letter(cli_buffer);
|
|
|
+ printf("\n%s %s\n$ ",
|
|
|
+ (volume_label[0] !=0) ? volume_label : "No Label",
|
|
|
+ cli_buffer);
|
|
|
+
|
|
|
+ memclr_(cli_buffer, CLI_MAX_BUFFER);
|
|
|
+}
|
|
|
+
|
|
|
+void cli_init(void)
|
|
|
+{
|
|
|
+ memclr_(cli_buffer, CLI_MAX_BUFFER);
|
|
|
+ f_getlabel(NULL, volume_label, NULL);
|
|
|
+ cli_command_prompt();
|
|
|
+}
|
|
|
+
|
|
|
+void cli_poll(char ch)
|
|
|
+{
|
|
|
+ if ( isprint(ch) )
|
|
|
+ { // accumulate & echo
|
|
|
+ if (strlen(cli_buffer) < CLI_MAX_BUFFER)
|
|
|
+ {
|
|
|
+ cli_buffer[ strlen(cli_buffer) ] = ch;
|
|
|
+ putchar(ch);
|
|
|
+ }else
|
|
|
+ {
|
|
|
+ puts("cli buffer overflows");
|
|
|
+ memclr_(cli_buffer, CLI_MAX_BUFFER);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ( ch == ASCII_BACKSPACE && strlen(cli_buffer))
|
|
|
+ {
|
|
|
+ printf(ANSI_CURSOR_BACKWARD(1) ANSI_ERASE_LINE(0) ); // move cursor back & clear to the end of line
|
|
|
+ cli_buffer[ strlen(cli_buffer)-1 ] = 0;
|
|
|
+ }
|
|
|
+ else if ( ch == '\r')
|
|
|
+ { // execute command
|
|
|
+ //------------- Separate Command & Parameter -------------//
|
|
|
+ putchar('\n');
|
|
|
+ char* p_space = strchr(cli_buffer, ' ');
|
|
|
+ uint32_t command_len = (p_space == NULL) ? strlen(cli_buffer) : (p_space - cli_buffer);
|
|
|
+ char* p_para = (p_space == NULL) ? (cli_buffer+command_len) : (p_space+1); // point to NULL-character or after space
|
|
|
+
|
|
|
+ //------------- Find entered command in lookup table & execute it -------------//
|
|
|
+ cli_cmdtype_t cmd_id;
|
|
|
+ for(cmd_id = CLI_CMDTYPE_COUNT - 1; cmd_id > CLI_CMDTYPE_unknown; cmd_id--)
|
|
|
+ {
|
|
|
+ if( 0 == strncmp(cli_buffer, cli_string_tbl[cmd_id], command_len) )
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ cli_error_t error = cli_command_tbl[cmd_id]( p_para ); // command execution, (unknown command if cannot find)
|
|
|
+
|
|
|
+ if (CLI_ERROR_NONE != error) puts(cli_error_message[error]); // error message output if any
|
|
|
+
|
|
|
+ //------------- print out current path -------------//
|
|
|
+ cli_command_prompt();
|
|
|
+ }
|
|
|
+ else if (ch=='\t') // \t may be used for auto-complete later
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// UNKNOWN Command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_unknown(char * para)
|
|
|
+{
|
|
|
+ puts("unknown command, please type \"help\" for list of supported commands");
|
|
|
+ return CLI_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// HELP command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_help(char * para)
|
|
|
+{
|
|
|
+ puts("current supported commands are:");
|
|
|
+ for(cli_cmdtype_t cmd_id = CLI_CMDTYPE_help+1; cmd_id < CLI_CMDTYPE_COUNT; cmd_id++)
|
|
|
+ {
|
|
|
+ printf("%s\t%s\n", cli_string_tbl[cmd_id], cli_description_tbl[cmd_id]);
|
|
|
+ }
|
|
|
+
|
|
|
+ return CLI_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Clear Screen Command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_clear(char* p_para)
|
|
|
+{
|
|
|
+ printf(ANSI_ERASE_SCREEN(2) ANSI_CURSOR_POSITION(1,1) );
|
|
|
+ return CLI_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// LS Command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_list(char * p_para)
|
|
|
+{
|
|
|
+ if ( strlen(p_para) == 0 ) // list current directory
|
|
|
+ {
|
|
|
+ DIR target_dir;
|
|
|
+ if ( FR_OK != f_opendir(&target_dir, ".") ) return CLI_ERROR_FAILED;
|
|
|
+
|
|
|
+ TCHAR long_filename[_MAX_LFN];
|
|
|
+ FILINFO dir_entry =
|
|
|
+ {
|
|
|
+ .lfname = long_filename,
|
|
|
+ .lfsize = _MAX_LFN
|
|
|
+ };
|
|
|
+ while( (f_readdir(&target_dir, &dir_entry) == FR_OK) && dir_entry.fname[0] != 0)
|
|
|
+ {
|
|
|
+ if ( dir_entry.fname[0] != '.' ) // ignore . and .. entry
|
|
|
+ {
|
|
|
+ TCHAR const * const p_name = (dir_entry.lfname[0] != 0) ? dir_entry.lfname : dir_entry.fname;
|
|
|
+ if ( dir_entry.fattrib & AM_DIR ) // directory
|
|
|
+ {
|
|
|
+ printf("/%s", p_name);
|
|
|
+ }else
|
|
|
+ {
|
|
|
+ printf("%-40s%d KB", p_name, dir_entry.fsize / 1000);
|
|
|
+ }
|
|
|
+ putchar('\n');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// (void) f_closedir(&target_dir);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ puts("ls only supports list current directory only, try to cd to that folder first");
|
|
|
+ return CLI_ERROR_INVALID_PARA;
|
|
|
+ }
|
|
|
+
|
|
|
+ return CLI_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// CD Command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_changedir(char * p_para)
|
|
|
+{
|
|
|
+ if ( strlen(p_para) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
+
|
|
|
+ drive_letter2number(p_para);
|
|
|
+
|
|
|
+ if ( FR_OK != f_chdir(p_para) )
|
|
|
+ {
|
|
|
+ return CLI_ERROR_INVALID_PATH;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( p_para[1] == ':')
|
|
|
+ { // path has drive letter --> change drive, update volume label
|
|
|
+ f_chdrive(p_para[0] - '0');
|
|
|
+ f_getlabel(NULL, volume_label, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ return CLI_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// CAT Command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_cat(char *p_para)
|
|
|
+{
|
|
|
+ if ( strlen(p_para) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
+
|
|
|
+ FIL file;
|
|
|
+
|
|
|
+ switch( f_open(&file, p_para, FA_READ) )
|
|
|
+ {
|
|
|
+ case FR_OK:
|
|
|
+ {
|
|
|
+ uint32_t bytes_read = 0;
|
|
|
+
|
|
|
+ if ( (FR_OK == f_read(&file, fileread_buffer, CLI_FILE_READ_BUFFER, &bytes_read)) && (bytes_read > 0) )
|
|
|
+ {
|
|
|
+ if ( file.fsize < 0x80000 ) // ~ 500KB
|
|
|
+ {
|
|
|
+ putchar('\n');
|
|
|
+ do {
|
|
|
+ for(uint32_t i=0; i<bytes_read; i++) putchar( fileread_buffer[i] );
|
|
|
+ }while( (FR_OK == f_read(&file, fileread_buffer, CLI_FILE_READ_BUFFER, &bytes_read)) && (bytes_read > 0) );
|
|
|
+ }else
|
|
|
+ { // not display file contents if first character is not printable (high chance of binary file)
|
|
|
+ printf("%s 's contents is too large\n", p_para);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ f_close(&file);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case FR_INVALID_NAME:
|
|
|
+ return CLI_ERROR_INVALID_PATH;
|
|
|
+
|
|
|
+ default :
|
|
|
+ return CLI_ERROR_FAILED;
|
|
|
+ }
|
|
|
+
|
|
|
+ return CLI_ERROR_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// Make Directory command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_mkdir(char *p_para)
|
|
|
+{
|
|
|
+ if ( strlen(p_para) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
+
|
|
|
+ return (f_mkdir(p_para) == FR_OK) ? CLI_ERROR_NONE : CLI_ERROR_FAILED;
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+// COPY command
|
|
|
+//--------------------------------------------------------------------+
|
|
|
+cli_error_t cli_cmd_copy(char *p_para)
|
|
|
+{
|
|
|
+ char* p_space = strchr(p_para, ' ');
|
|
|
+ if ( p_space == NULL ) return CLI_ERROR_INVALID_PARA;
|
|
|
+
|
|
|
+ *p_space = 0; // replace space by NULL-character
|
|
|
+ char* p_dest = p_space+1;
|
|
|
+
|
|
|
+ if ( strlen(p_dest) == 0 ) return CLI_ERROR_INVALID_PARA;
|
|
|
+
|
|
|
+ drive_letter2number(p_para);
|
|
|
+ drive_letter2number(p_dest);
|
|
|
+
|
|
|
+ //------------- Check Existence of source & dest file -------------//
|
|
|
+ cli_error_t error = CLI_ERROR_NONE;
|
|
|
+ FIL src_file, dest_file;
|
|
|
+
|
|
|
+ if ( FR_OK != f_open(&src_file , p_para, FA_READ) ) return CLI_ERROR_INVALID_PATH;
|
|
|
+ switch ( f_open(&dest_file, p_dest, FA_WRITE | FA_CREATE_NEW) )
|
|
|
+ {
|
|
|
+ case FR_EXIST:
|
|
|
+ error = CLI_ERROR_FILE_EXISTED;
|
|
|
+ break;\
|
|
|
+
|
|
|
+ case FR_OK:
|
|
|
+ while(1)
|
|
|
+ {
|
|
|
+ uint32_t bytes_read = 0;
|
|
|
+ uint32_t bytes_write = 0;
|
|
|
+ FRESULT res;
|
|
|
+
|
|
|
+ res = f_read(&src_file, fileread_buffer, CLI_FILE_READ_BUFFER, &bytes_read); /* Read a chunk of src file */
|
|
|
+ if ( (res != FR_OK) || (bytes_read == 0) ) break; /* error or eof */
|
|
|
+
|
|
|
+ res = f_write(&dest_file, fileread_buffer, bytes_read, &bytes_write); /* Write it to the dst file */
|
|
|
+ if ( (res != FR_OK) || (bytes_write < bytes_read) ) break; /* error or disk full */
|
|
|
+ }
|
|
|
+
|
|
|
+ f_close(&dest_file);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ error = CLI_ERROR_FAILED;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ f_close(&src_file);
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+#endif
|