2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
4 * SPDX-License-Identifier: GPL-2.0
8 * FAT filesystem commands
12 #include <util/delay.h>
17 #include "con-utils.h"
18 #include "print-utils.h"
23 #include "getopt-min.h"
26 #define DEBUG_CP 1 /* set to 1 to debug */
27 #define DEBUG_LS 1 /* set to 1 to debug */
28 #define DEBUG_RM 1 /* set to 1 to debug */
30 #define debug_cp(fmt, args...) \
31 debug_cond(DEBUG_CP, fmt, ##args)
32 #define debug_ls(fmt, args...) \
33 debug_cond(DEBUG_LS, fmt, ##args)
34 #define debug_rm(fmt, args...) \
35 debug_cond(DEBUG_RM, fmt, ##args)
39 /* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
40 /* TODO: detect_ramsize() should be moved to z80-if.c */
41 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
42 #define BUFFER_SIZE 512
43 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
47 char *p_end
; /* pointer to NULL at end of path */
48 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
52 * Multible (fat) partitions per physical drive are not supported,
53 * but we have up to 2 sdcard slots.
62 command_ret_t command_ret
;
66 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
67 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
68 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
69 #define P_FLAG (1<<4) // preserve attributes and timestamps
70 #define R_FLAG (1<<0) // copy directories recursively
71 #define V_FLAG (1<<5) // explain what is being done
75 void setup_fatfs(void)
77 f_mount(&FatFs0
, "0:", 0);
78 f_mount(&FatFs1
, "1:", 0);
81 DWORD
get_fattime (void)
87 gmtime_r(&timer
, &tm_timer
);
89 return fatfs_time(&tm_timer
);
93 static bool check_abort(void)
98 printf_P(PSTR("Abort\n"));
104 static const FLASH
char * const FLASH rc_strings
[] = {
107 FSTR("internal error"),
111 FSTR("invalid name"),
114 FSTR("invalid object"),
115 FSTR("write protected"),
116 FSTR("invalid drive"),
118 FSTR("no file system"),
119 FSTR("mkfs aborted"),
122 FSTR("not enough core"),
123 FSTR("too many open files"),
124 FSTR("invalid parameter")
127 static const FLASH
char * const FLASH rc_names
[] = {
134 FSTR("INVALID_NAME"),
137 FSTR("INVALID_OBJECT"),
138 FSTR("WRITE_PROTECTED"),
139 FSTR("INVALID_DRIVE"),
141 FSTR("NO_FILE_SYSTEM"),
142 FSTR("MKFS_ABORTED"),
145 FSTR("NOT_ENOUGH_CORE"),
146 FSTR("TOO_MANY_OPEN_FILES"),
147 FSTR("INVALID_PARAMETER")
151 void put_rc (FRESULT rc
)
154 printf_P(PSTR("rc=%u FR_"), rc
);
155 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
156 my_puts_P(PSTR("\n"));
158 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
159 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
163 const FLASH
char * rctostr(FRESULT rc
)
165 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
168 void err(const char *fmt
, ...)
172 printf_P(PSTR("fat %s: "), cmdname
);
173 vfprintf_P(stdout
, fmt
, ap
);
175 printf_P(PSTR("\n"));
177 command_ret
= CMD_RET_FAILURE
;
180 /******************************************************************************/
183 * These functions manipulate paths in PATH_T structures.
185 * They eliminate multiple slashes in paths when they notice them,
186 * and keep the path non-slash terminated.
188 * Both path_set() and path_append() return 0 if the requested name
193 static void path_init(void)
195 from
.p_path
[0] = '\0'; from
.p_end
= from
.p_path
;
196 to
.p_path
[0] = '\0'; to
.p_end
= to
.p_path
;
199 static void strip_trailing_slash(PATH_T
*p
)
201 char *beg
= p
->p_path
;
202 char *end
= p
->p_end
;
203 if ((beg
+ 2) < end
&& (*beg
& 0x38) == '0' && *(beg
+1) == ':')
205 if (beg
< end
&& *beg
== '/')
207 while (end
> beg
&& end
[-1] == '/')
214 * Move specified string into path. Convert "" to "." to handle BSD
215 * semantics for a null path. Strip trailing slashes.
218 path_set(PATH_T
*p
, char *string
)
220 if (strlen(string
) > MAX_PATHLEN
) {
221 err(PSTR("set: '%s': name too long"), string
);
225 (void)strcpy(p
->p_path
, string
);
226 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
228 if (p
->p_path
== p
->p_end
) {
233 strip_trailing_slash(p
);
238 * Append specified string to path, inserting '/' if necessary. Return a
239 * pointer to the old end of path for restoration.
242 path_append(PATH_T
*p
, char *name
)
244 char *old
= p
->p_end
;
245 int len
= strlen(name
);
247 /* The "+ 1" accounts for the '/' between old path and name. */
248 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
249 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
254 * This code should always be executed, since paths shouldn't
257 if (p
->p_end
[-1] != '/') {
262 strncat(p
->p_end
, name
, len
);
266 strip_trailing_slash(p
);
271 * Restore path to previous value. (As returned by path_append.)
274 path_restore(PATH_T
*p
, char *old
)
281 * Return basename of path.
283 char *path_basename(PATH_T
*p
)
285 char *basename
= strrchr(p
->p_path
, '/');
290 basename
= p
->p_path
;
291 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
298 char *path_basename_pattern(PATH_T
*p
)
300 char *pattern
= path_basename(p
);
301 if (strpbrk_P(pattern
, PSTR("*?"))) {
302 memmove(pattern
+1, pattern
, strlen(pattern
)+1);
305 //p->p_pattern = p->p_end + 1;
306 pattern
= p
->p_end
+ 1;
313 static void swirl(void)
315 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
316 static uint_fast8_t cnt
;
317 static uint32_t tstamp
;
319 if (get_timer(0) > tstamp
) {
320 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
321 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
322 tstamp
= get_timer(0) + 250;
327 * pwd - Print current directory of the current drive.
330 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
335 command_ret
= CMD_RET_SUCCESS
;
337 res
= f_getcwd(from
.p_path
, MAX_PATHLEN
); /* Get current directory path */
342 err(PSTR("Error: %S"), rctostr(res
));
349 * cd - Change the current/working directory.
352 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
358 command_ret
= CMD_RET_SUCCESS
;
361 arg
= getenv_str(PSTR(ENV_HOME
));
363 err(PSTR("'%S' is not set"), PSTR(ENV_HOME
));
370 res
= f_chdrive(arg
);
374 err(PSTR("'%s': %S"), arg
, rctostr(res
));
381 * Remove trailing slashes,
382 * but keep a leading slash (absolute path)
384 void strip_trailing_slash_relpath(char *p
)
388 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
392 if (n
>= 1 && p
[0] == '/') {
396 while (n
-- != 0 && p
[n
] == '/')
401 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
403 DIR Dir
; /* Directory object */
414 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
423 return CMD_RET_USAGE
;
430 command_ret
= CMD_RET_SUCCESS
;
432 err(PSTR("missing operand"));
434 for (int i
= 0; i
< argc
; i
++) {
435 if (!path_set(&from
, argv
[i
])) {
438 char *pattern
= path_basename_pattern(&from
);
440 debug_rm("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
442 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
443 debug_rm("==== findfirst %d\n", res
);
444 while (res
== FR_OK
&& Finfo
.fname
[0]) {
445 if (Finfo
.fattrib
& AM_DIR
) {
446 err(PSTR("cannot remove '%s': Is a directory"), Finfo
.fname
);
448 if (!(flags
& N_FLAG
)) {
449 if ((res
= f_unlink(Finfo
.fname
)) == FR_OK
) {
451 printf_P(PSTR("removed '%s'\n"), Finfo
.fname
);
453 err(PSTR("cannot remove '%s': Read only?"), Finfo
.fname
);
457 printf_P(PSTR("not removed '%s'\n"), Finfo
.fname
);
460 res
= f_findnext(&Dir
, &Finfo
);
461 debug_rm("==== findnext %d\n", res
);
469 return CMD_RET_FAILURE
;
475 command_ret_t
do_rmdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
479 return CMD_RET_SUCCESS
;
482 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
486 return CMD_RET_SUCCESS
;
490 int print_dirent(FILINFO
*f
)
492 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
493 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
494 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
495 (f
->fattrib
& AM_HID
) ? 'H' : '-',
496 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
497 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
498 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
499 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
504 * ls path - Directory listing
507 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
510 DIR Dir
; /* Directory object */
521 if (!path_set(&from
, argv
[1])) {
525 char *pattern
= path_basename_pattern(&from
);
527 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
530 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
531 while (res
== FR_OK
&& Finfo
.fname
[0]) {
532 if (Finfo
.fattrib
& AM_DIR
) {
535 s1
++; p1
+= Finfo
.fsize
;
537 print_dirent(&Finfo
);
540 res
= f_findnext(&Dir
, &Finfo
);
545 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
546 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
547 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
552 return CMD_RET_FAILURE
;
555 return CMD_RET_SUCCESS
;
559 * tst path - for debugging: test access with different functions
562 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
564 DIR Dir
; /* Directory object */
570 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
572 char * buf
= (char *) malloc(BUFFER_SIZE
);
574 printf_P(PSTR("tst: Out of Memory!\n"));
575 return CMD_RET_FAILURE
;
577 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
580 printf_P(PSTR("cwd: '%s'\n"), buf
);
585 return CMD_RET_FAILURE
;
593 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
594 printf_P(PSTR("==== f_stat: "));
595 res
= f_stat(path
, &Finfo
);
598 print_dirent(&Finfo
);
601 printf_P(PSTR("==== f_findfirst: "));
602 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
605 print_dirent(&Finfo
);
609 printf_P(PSTR("==== f_opendir: "));
610 res
= f_opendir(&Dir
, path
);
614 return CMD_RET_SUCCESS
;
617 /******************************************************************************/
624 fr
= f_utime(to
.p_path
, fs
);
626 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
627 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
629 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
633 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
640 if (blockbuf
== NULL
) {
641 blockbuf_size
= get_freemem() / 512 * 512;
642 if (blockbuf_size
!= 0)
643 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
644 if (blockbuf
== NULL
) {
645 err(PSTR("Not enough memory!\n"));
650 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne
, blockbuf_size
, get_freemem());
651 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
654 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
655 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
660 * If the file exists and we're interactive, verify with the user.
663 if (flags
& N_FLAG
) {
665 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
668 } if (flags
& I_FLAG
) {
669 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
670 if (!confirm_yes()) {
675 if (flags
& F_FLAG
) {
676 /* Remove existing destination file name create a new file. */
677 f_chmod(to
.p_path
,0, AM_RDO
);
679 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
681 /* Overwrite existing destination file name. */
682 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
685 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
687 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
690 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
695 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
697 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
698 if (fr
|| wcount
< rcount
) {
699 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
704 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
707 if ((fr
= f_close(&to_fd
)) != FR_OK
)
708 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
716 static void copy_dir(void)
718 DIR Dir
; /* Directory object */
720 char *old_from
, *old_to
;
722 char *pattern
= {"*"};
724 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
725 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
729 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
731 command_ret
= CMD_RET_FAILURE
;
735 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
736 res
== FR_OK
&& Finfo
.fname
[0];
737 res
= f_findnext(&Dir
, &Finfo
)) {
739 if (!(Finfo
.fattrib
& AM_DIR
) &&
740 (old_from
= path_append(&from
, Finfo
.fname
))) {
741 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
743 path_restore(&to
, old_to
);
745 path_restore(&from
, old_from
);
749 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
750 res
== FR_OK
&& Finfo
.fname
[0];
751 res
= f_findnext(&Dir
, &Finfo
)) {
753 if ((Finfo
.fattrib
& AM_DIR
) &&
754 (old_from
= path_append(&from
, Finfo
.fname
))) {
755 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
757 path_restore(&to
, old_to
);
759 path_restore(&from
, old_from
);
766 * copy file or directory at "from" to "to".
770 FILINFO from_stat
, to_stat
;
774 debug_cp("==== copy(); freemem: %u\n", get_freemem());
775 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
777 fr
= f_stat(from
.p_path
, &from_stat
);
779 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
783 /* not an error, but need to remember it happened */
784 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
787 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
788 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
789 to
.p_path
, from
.p_path
);
790 command_ret
= CMD_RET_FAILURE
;
796 if(from_stat
.fattrib
& AM_DIR
) {
797 if (!(flags
& R_FLAG
)) {
798 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
800 command_ret
= CMD_RET_FAILURE
;
805 * If the directory doesn't exist, create the new one.
807 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
808 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
811 } else if (!(to_stat
.fattrib
& AM_DIR
)) {
812 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
821 copy_file(&from_stat
, dne
);
824 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
827 FRESULT fr
; /* Return value */
828 //DIR dj; /* Directory search object */
829 FILINFO to_stat
; /* File information */
835 command_ret
= CMD_RET_SUCCESS
;
841 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
844 tflags
&= ~(I_FLAG
| N_FLAG
);
848 tflags
&= ~(F_FLAG
| N_FLAG
);
852 tflags
&= ~(F_FLAG
| I_FLAG
);
865 return CMD_RET_USAGE
;
874 return CMD_RET_USAGE
;
878 /* last argument is destination */
879 if (!path_set(&to
, argv
[--argc
]))
883 * Cp has two distinct cases:
885 * % cp [-rip] source target
886 * % cp [-rip] source1 ... directory
888 * In both cases, source can be either a file or a directory.
890 * In (1), the target becomes a copy of the source. That is, if the
891 * source is a file, the target will be a file, and likewise for
894 * In (2), the real target is not directory, but "directory/source".
897 fr
= f_stat(to
.p_path
, &to_stat
);
898 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
899 fr
, to_stat
.fattrib
, flags
, get_freemem());
900 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
902 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
903 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
904 command_ret
= CMD_RET_FAILURE
;
907 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
909 * Case (1). Target is not a directory.
912 err(PSTR("target '%s' is not a directory"), to
.p_path
);
913 //command_ret = CMD_RET_USAGE;
916 if (!path_set(&from
, *argv
)) {
917 command_ret
= CMD_RET_FAILURE
;
924 * Case (2). Target is a directory.
927 if (!path_set(&from
, *argv
))
929 if (!(old_to
= path_append(&to
, path_basename(&from
))))
934 path_restore(&to
, old_to
);
948 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
953 /******************************************************************************/
956 * Work register for stat command
960 WORD AccFiles
, AccDirs
;
966 char *path
, /* Pointer to the working buffer with start path */
967 struct stat_dat_s
*statp
975 res
= f_opendir(&dirs
, path
);
979 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
980 statp
->Finfo
.fname
[0]) {
981 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
983 fn
= statp
->Finfo
.fname
;
984 if (statp
->Finfo
.fattrib
& AM_DIR
) {
987 strcpy(path
+i
+1, fn
);
988 res
= scan_files(path
, statp
);
993 //printf_P(PSTR("%s/%s\n"), path, fn);
995 statp
->AccSize
+= statp
->Finfo
.fsize
;
1009 * fatstat path - Show logical drive status
1012 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1019 struct stat_dat_s statp
;
1021 buf
= (char *) malloc(BUFFER_SIZE
);
1023 printf_P(PSTR("fat stat: Out of Memory!\n"));
1024 return CMD_RET_FAILURE
;
1029 res
= f_getfree(path
, &nfreeclst
, &fs
);
1033 "Bytes/Cluster: %lu\n"
1034 "Number of FATs: %u\n"
1035 "Root DIR entries: %u\n"
1036 "Sectors/FAT: %lu\n"
1037 "Number of clusters: %lu\n"
1038 "FAT start (lba): %lu\n"
1039 "DIR start (lba,cluster): %lu\n"
1040 "Data start (lba): %lu\n"),
1041 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1042 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1043 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1047 res
= f_getlabel(path
, buf
, &serial
);
1051 "Volume S/N: %04X-%04X\n"),
1052 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1056 my_puts_P(PSTR("\nCounting... "));
1057 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1060 res
= scan_files(buf
, &statp
);
1063 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1064 "%lu KB total disk space.\n%lu KB available.\n"),
1065 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1066 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1074 return CMD_RET_FAILURE
;
1076 return CMD_RET_SUCCESS
;
1080 * fatread/write - load binary file to/from a dos filesystem
1081 * read <d:/path/filename> <addr> [bytes [pos]]
1082 * write <d:/path/filename> <addr> <bytes>
1084 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1088 unsigned long bytes
;
1090 unsigned long bytes_rw
;
1092 bool dowrite
= (argv
[0][0] == 'w');
1093 FRESULT res
= FR_OK
;
1098 if (argc
< (dowrite
? 4 : 3))
1099 return CMD_RET_USAGE
;
1101 addr
= eval_arg(argv
[2], NULL
);
1102 if (addr
>= MAX_MEMORY
) {
1103 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1104 return CMD_RET_FAILURE
;
1107 bytes
= eval_arg(argv
[3], NULL
);
1111 pos
= eval_arg(argv
[4], NULL
);
1115 if (addr
+ bytes
> MAX_MEMORY
)
1116 bytes
= MAX_MEMORY
- addr
;
1118 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1119 if (buffer
== NULL
) {
1120 printf_P(PSTR("fatstat: Out of Memory!\n"));
1122 return CMD_RET_FAILURE
;
1126 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1130 res
= f_lseek(&File
, pos
);
1133 timer
= get_timer(0);
1135 unsigned int cnt
, br
;
1137 if (bytes
>= BUFFER_SIZE
) {
1139 bytes
-= BUFFER_SIZE
;
1141 cnt
= bytes
; bytes
= 0;
1144 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1148 z80_read_block(buffer
, addr
, cnt
);
1149 z80_bus_cmd(Release
);
1150 res
= f_write(&File
, buffer
, cnt
, &br
);
1154 res
= f_read(&File
, buffer
, cnt
, &br
);
1157 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1161 z80_write_block(buffer
, addr
, br
);
1162 z80_bus_cmd(Release
);
1169 printf_P(PSTR("Disk full?\n"));
1176 FRESULT fr
= f_close(&File
);
1179 timer
= get_timer(timer
);
1180 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1181 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1189 my_puts_P(PSTR("Bus timeout\n"));
1193 return CMD_RET_FAILURE
;
1195 return CMD_RET_SUCCESS
;
1199 * command table for fat subcommands
1202 cmd_tbl_t cmd_tbl_fat
[] = {
1204 stat
, 2, CTBL_RPT
, do_stat
,
1205 "Show logical drive status",
1209 pwd
, 1, CTBL_RPT
, do_pwd
,
1210 "Print name of current/working directory",
1215 "Change the current/working directory.",
1219 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
1221 "[OPTION]... [FILE]..."
1224 rmdir
, CONFIG_SYS_MAXARGS
, 0, do_rmdir
,
1225 "Remove the DIRECTORY(ies), if they are empty",
1226 "[OPTION]... DIRECTORY..."
1229 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
1230 "Create the DIRECTORY(ies), if they do not already exist.",
1231 "[OPTION]... DIRECTORY..."
1234 ls
, 2, CTBL_RPT
, do_ls
,
1235 "Directory listing",
1239 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
1240 "FatFS test function",
1245 "load binary file from a dos filesystem",
1246 "<d:/path/filename> <addr> [bytes [pos]]\n"
1247 " - Load binary file 'path/filename' on logical drive 'd'\n"
1248 " to address 'addr' from dos filesystem.\n"
1249 " 'pos' gives the file position to start loading from.\n"
1250 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1251 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1252 " the load stops on end of file."
1256 "write file into a dos filesystem",
1257 "<d:/path/filename> <addr> <bytes>\n"
1258 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1259 " starting at address 'addr'.\n"
1263 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1265 "[-f | -i | -n] [-prv] source_file target_file\n"
1266 // "[-f | -i | -n] [-prv] source_file target_file\n"
1267 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1268 " -f overwrite existing file ignoring write protection\n"
1269 " this option is ignored when the -n option is also used\n"
1270 " -i prompt before overwrite (overrides a previous -n option)\n"
1271 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1272 " -p preserve attributes and timestamps\n"
1273 " -r copy directories recursively\n"
1274 " -v explain what is being done\n"
1278 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1279 "Print sub command description/usage",
1281 " - print brief description of all sub commands\n"
1282 "fat help command ...\n"
1283 " - print detailed usage of sub cmd 'command'"
1286 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1287 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1288 FSTR("Alias for 'help'"),
1289 #ifdef CONFIG_SYS_LONGHELP
1291 #endif /* CONFIG_SYS_LONGHELP */
1293 #ifdef CONFIG_AUTO_COMPLETE
1297 /* Mark end of table */
1298 CMD_TBL_END(cmd_tbl_fat
)
1302 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1304 puts_P(PSTR("Huch?"));
1306 return CMD_RET_USAGE
;