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 char *path_skip_heading(char *p
)
201 if ((p
[0] & 0x38) == '0' && p
[1] == ':') {
210 static void strip_trailing_slash(PATH_T
*p
)
212 char *beg
= path_skip_heading(p
->p_path
);
213 char *end
= p
->p_end
;
215 while (end
> beg
&& end
[-1] == '/')
222 * Move specified string into path. Convert "" to "." to handle BSD
223 * semantics for a null path. Strip trailing slashes.
226 path_set(PATH_T
*p
, char *string
)
228 if (strlen(string
) > MAX_PATHLEN
) {
229 err(PSTR("set: '%s': name too long"), string
);
233 (void)strcpy(p
->p_path
, string
);
234 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
236 if (p
->p_path
== p
->p_end
) {
241 strip_trailing_slash(p
);
246 * Append specified string to path, inserting '/' if necessary. Return a
247 * pointer to the old end of path for restoration.
250 path_append(PATH_T
*p
, char *name
)
252 char *old
= p
->p_end
;
253 int len
= strlen(name
);
255 /* The "+ 1" accounts for the '/' between old path and name. */
256 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
257 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
262 * This code should always be executed, since paths shouldn't
265 if (p
->p_end
[-1] != '/') {
270 strncat(p
->p_end
, name
, len
);
274 strip_trailing_slash(p
);
279 * Restore path to previous value. (As returned by path_append.)
282 path_restore(PATH_T
*p
, char *old
)
289 * Return basename of path.
291 char *path_basename(PATH_T
*p
)
293 char *basename
= strrchr(p
->p_path
, '/');
298 basename
= p
->p_path
;
299 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
306 char *path_basename_pattern(PATH_T
*p
)
308 char *pattern
= path_basename(p
);
309 if (strpbrk_P(pattern
, PSTR("*?"))) {
310 memmove(pattern
+1, pattern
, strlen(pattern
)+1);
313 //p->p_pattern = p->p_end + 1;
314 pattern
= p
->p_end
+ 1;
323 * Return basename/pattern of path.
326 static char *path_split_pattern(PATH_T
*p
)
328 char *pp
= path_skip_heading(p
->p_path
);
329 char *pattern
= strrchr(pp
, '/');
331 if (pattern
== NULL
) {
338 memmove(pattern
+2, pattern
, strlen(pattern
)+1);
345 static void path_fix(PATH_T
*p
)
347 char *pp
= path_skip_heading(p
->p_path
);
349 if (pp
!= p
->p_end
) {
355 static void path_unfix(PATH_T
*p
)
357 char *pp
= path_skip_heading(p
->p_path
);
359 if (pp
!= p
->p_end
) {
364 static void swirl(void)
366 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
367 static uint_fast8_t cnt
;
368 static uint32_t tstamp
;
370 if (get_timer(0) > tstamp
) {
371 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
372 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
373 tstamp
= get_timer(0) + 250;
378 * pwd - Print current directory of the current drive.
381 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
386 command_ret
= CMD_RET_SUCCESS
;
388 res
= f_getcwd(from
.p_path
, MAX_PATHLEN
); /* Get current directory path */
393 err(PSTR("Error: %S"), rctostr(res
));
400 * cd - Change the current/working directory.
403 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
409 command_ret
= CMD_RET_SUCCESS
;
412 arg
= getenv_str(PSTR(ENV_HOME
));
414 err(PSTR("'%S' is not set"), PSTR(ENV_HOME
));
421 res
= f_chdrive(arg
);
425 err(PSTR("'%s': %S"), arg
, rctostr(res
));
432 * Remove trailing slashes,
433 * but keep a leading slash (absolute path)
435 void strip_trailing_slash_relpath(char *p
)
439 if (n
>= 2 && (p
[0] & 0x38) == '0' && p
[1] == ':') {
443 if (n
>= 1 && p
[0] == '/') {
447 while (n
-- != 0 && p
[n
] == '/')
452 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
454 DIR Dir
; /* Directory object */
459 command_ret
= CMD_RET_SUCCESS
;
466 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
475 return CMD_RET_USAGE
;
483 err(PSTR("missing operand"));
485 for (int i
= 0; i
< argc
; i
++) {
486 if (!path_set(&from
, argv
[i
])) {
489 char *pattern
= path_split_pattern(&from
);
491 debug_rm("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
493 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
494 debug_rm("==== findfirst %d\n", res
);
496 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
498 err(PSTR("cannot remove '%s%s': No such file or directory"), from
.p_path
, pattern
);
501 if (Finfo
.fattrib
& AM_DIR
) {
503 err(PSTR("cannot remove '%s%s': Is a directory"), from
.p_path
, Finfo
.fname
);
505 if (!(flags
& N_FLAG
)) {
506 if ((res
= f_unlink(Finfo
.fname
)) == FR_OK
) {
509 printf_P(PSTR("removed '%s%s'\n"), from
.p_path
, Finfo
.fname
);
513 err(PSTR("cannot remove '%s%s': %S"), from
.p_path
, Finfo
.fname
, rctostr(res
));
517 printf_P(PSTR("not removed '%s%s'\n"), from
.p_path
, Finfo
.fname
);
521 res
= f_findnext(&Dir
, &Finfo
);
522 //debug_rm("==== findnext %d\n", res);
523 } while (res
== FR_OK
&& Finfo
.fname
[0]);
531 return CMD_RET_FAILURE
;
537 command_ret_t
do_rmdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
540 command_ret
= CMD_RET_SUCCESS
;
545 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
548 command_ret
= CMD_RET_SUCCESS
;
554 int print_dirent(FILINFO
*f
)
556 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
557 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
558 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
559 (f
->fattrib
& AM_HID
) ? 'H' : '-',
560 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
561 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
562 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
563 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
568 * ls path - Directory listing
571 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
574 DIR Dir
; /* Directory object */
585 if (!path_set(&from
, argv
[1])) {
589 char *pattern
= path_basename_pattern(&from
);
591 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
594 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
595 while (res
== FR_OK
&& Finfo
.fname
[0]) {
596 if (Finfo
.fattrib
& AM_DIR
) {
599 s1
++; p1
+= Finfo
.fsize
;
601 print_dirent(&Finfo
);
604 res
= f_findnext(&Dir
, &Finfo
);
609 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
610 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
611 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
616 return CMD_RET_FAILURE
;
619 return CMD_RET_SUCCESS
;
623 * tst path - for debugging: test access with different functions
626 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
628 DIR Dir
; /* Directory object */
634 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
636 char * buf
= (char *) malloc(BUFFER_SIZE
);
638 printf_P(PSTR("tst: Out of Memory!\n"));
639 return CMD_RET_FAILURE
;
641 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
644 printf_P(PSTR("cwd: '%s'\n"), buf
);
649 return CMD_RET_FAILURE
;
657 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
658 printf_P(PSTR("==== f_stat: "));
659 res
= f_stat(path
, &Finfo
);
662 print_dirent(&Finfo
);
665 printf_P(PSTR("==== f_findfirst: "));
666 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
669 print_dirent(&Finfo
);
673 printf_P(PSTR("==== f_opendir: "));
674 res
= f_opendir(&Dir
, path
);
678 return CMD_RET_SUCCESS
;
681 /******************************************************************************/
688 fr
= f_utime(to
.p_path
, fs
);
690 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
691 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
693 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
697 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
704 if (blockbuf
== NULL
) {
705 blockbuf_size
= get_freemem() / 512 * 512;
706 if (blockbuf_size
!= 0)
707 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
708 if (blockbuf
== NULL
) {
709 err(PSTR("Not enough memory!\n"));
714 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne
, blockbuf_size
, get_freemem());
715 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
718 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
719 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
724 * If the file exists and we're interactive, verify with the user.
727 if (flags
& N_FLAG
) {
729 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
732 } if (flags
& I_FLAG
) {
733 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
734 if (!confirm_yes()) {
739 if (flags
& F_FLAG
) {
740 /* Remove existing destination file name create a new file. */
741 f_chmod(to
.p_path
,0, AM_RDO
);
743 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
745 /* Overwrite existing destination file name. */
746 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
749 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
751 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
754 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
759 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
761 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
762 if (fr
|| wcount
< rcount
) {
763 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
768 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
771 if ((fr
= f_close(&to_fd
)) != FR_OK
)
772 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
780 static void copy_dir(void)
782 DIR Dir
; /* Directory object */
784 char *old_from
, *old_to
;
786 char *pattern
= {"*"};
788 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
789 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
793 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
795 command_ret
= CMD_RET_FAILURE
;
799 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
800 res
== FR_OK
&& Finfo
.fname
[0];
801 res
= f_findnext(&Dir
, &Finfo
)) {
803 if (!(Finfo
.fattrib
& AM_DIR
) &&
804 (old_from
= path_append(&from
, Finfo
.fname
))) {
805 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
807 path_restore(&to
, old_to
);
809 path_restore(&from
, old_from
);
813 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
814 res
== FR_OK
&& Finfo
.fname
[0];
815 res
= f_findnext(&Dir
, &Finfo
)) {
817 if ((Finfo
.fattrib
& AM_DIR
) &&
818 (old_from
= path_append(&from
, Finfo
.fname
))) {
819 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
821 path_restore(&to
, old_to
);
823 path_restore(&from
, old_from
);
830 * copy file or directory at "from" to "to".
834 FILINFO from_stat
, to_stat
;
838 debug_cp("==== copy(); freemem: %u\n", get_freemem());
839 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
841 fr
= f_stat(from
.p_path
, &from_stat
);
843 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
847 /* not an error, but need to remember it happened */
848 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
851 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
852 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
853 to
.p_path
, from
.p_path
);
854 command_ret
= CMD_RET_FAILURE
;
860 if(from_stat
.fattrib
& AM_DIR
) {
861 if (!(flags
& R_FLAG
)) {
862 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
864 command_ret
= CMD_RET_FAILURE
;
869 * If the directory doesn't exist, create the new one.
871 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
872 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
875 } else if (!(to_stat
.fattrib
& AM_DIR
)) {
876 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
885 copy_file(&from_stat
, dne
);
888 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
891 FRESULT fr
; /* Return value */
892 //DIR dj; /* Directory search object */
893 FILINFO to_stat
; /* File information */
899 command_ret
= CMD_RET_SUCCESS
;
905 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
908 tflags
&= ~(I_FLAG
| N_FLAG
);
912 tflags
&= ~(F_FLAG
| N_FLAG
);
916 tflags
&= ~(F_FLAG
| I_FLAG
);
929 return CMD_RET_USAGE
;
938 return CMD_RET_USAGE
;
942 /* last argument is destination */
943 if (!path_set(&to
, argv
[--argc
]))
947 * Cp has two distinct cases:
949 * % cp [-rip] source target
950 * % cp [-rip] source1 ... directory
952 * In both cases, source can be either a file or a directory.
954 * In (1), the target becomes a copy of the source. That is, if the
955 * source is a file, the target will be a file, and likewise for
958 * In (2), the real target is not directory, but "directory/source".
961 fr
= f_stat(to
.p_path
, &to_stat
);
962 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
963 fr
, to_stat
.fattrib
, flags
, get_freemem());
964 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
966 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
967 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
968 command_ret
= CMD_RET_FAILURE
;
971 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
973 * Case (1). Target is not a directory.
976 err(PSTR("target '%s' is not a directory"), to
.p_path
);
977 //command_ret = CMD_RET_USAGE;
980 if (!path_set(&from
, *argv
)) {
981 command_ret
= CMD_RET_FAILURE
;
988 * Case (2). Target is a directory.
991 if (!path_set(&from
, *argv
))
993 if (!(old_to
= path_append(&to
, path_basename(&from
))))
998 path_restore(&to
, old_to
);
1012 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
1017 /******************************************************************************/
1020 * Work register for stat command
1024 WORD AccFiles
, AccDirs
;
1029 FRESULT
scan_files (
1030 char *path
, /* Pointer to the working buffer with start path */
1031 struct stat_dat_s
*statp
1039 res
= f_opendir(&dirs
, path
);
1043 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
1044 statp
->Finfo
.fname
[0]) {
1045 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1047 fn
= statp
->Finfo
.fname
;
1048 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1051 strcpy(path
+i
+1, fn
);
1052 res
= scan_files(path
, statp
);
1057 //printf_P(PSTR("%s/%s\n"), path, fn);
1059 statp
->AccSize
+= statp
->Finfo
.fsize
;
1061 if (check_abort()) {
1073 * fatstat path - Show logical drive status
1076 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1083 struct stat_dat_s statp
;
1085 buf
= (char *) malloc(BUFFER_SIZE
);
1087 printf_P(PSTR("fat stat: Out of Memory!\n"));
1088 return CMD_RET_FAILURE
;
1093 res
= f_getfree(path
, &nfreeclst
, &fs
);
1097 "Bytes/Cluster: %lu\n"
1098 "Number of FATs: %u\n"
1099 "Root DIR entries: %u\n"
1100 "Sectors/FAT: %lu\n"
1101 "Number of clusters: %lu\n"
1102 "FAT start (lba): %lu\n"
1103 "DIR start (lba,cluster): %lu\n"
1104 "Data start (lba): %lu\n"),
1105 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1106 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1107 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1111 res
= f_getlabel(path
, buf
, &serial
);
1115 "Volume S/N: %04X-%04X\n"),
1116 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1120 my_puts_P(PSTR("\nCounting... "));
1121 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1124 res
= scan_files(buf
, &statp
);
1127 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1128 "%lu KB total disk space.\n%lu KB available.\n"),
1129 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1130 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1138 return CMD_RET_FAILURE
;
1140 return CMD_RET_SUCCESS
;
1144 * fatread/write - load binary file to/from a dos filesystem
1145 * read <d:/path/filename> <addr> [bytes [pos]]
1146 * write <d:/path/filename> <addr> <bytes>
1148 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1152 unsigned long bytes
;
1154 unsigned long bytes_rw
;
1156 bool dowrite
= (argv
[0][0] == 'w');
1157 FRESULT res
= FR_OK
;
1162 if (argc
< (dowrite
? 4 : 3))
1163 return CMD_RET_USAGE
;
1165 addr
= eval_arg(argv
[2], NULL
);
1166 if (addr
>= MAX_MEMORY
) {
1167 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1168 return CMD_RET_FAILURE
;
1171 bytes
= eval_arg(argv
[3], NULL
);
1175 pos
= eval_arg(argv
[4], NULL
);
1179 if (addr
+ bytes
> MAX_MEMORY
)
1180 bytes
= MAX_MEMORY
- addr
;
1182 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1183 if (buffer
== NULL
) {
1184 printf_P(PSTR("fatstat: Out of Memory!\n"));
1186 return CMD_RET_FAILURE
;
1190 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1194 res
= f_lseek(&File
, pos
);
1197 timer
= get_timer(0);
1199 unsigned int cnt
, br
;
1201 if (bytes
>= BUFFER_SIZE
) {
1203 bytes
-= BUFFER_SIZE
;
1205 cnt
= bytes
; bytes
= 0;
1208 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1212 z80_read_block(buffer
, addr
, cnt
);
1213 z80_bus_cmd(Release
);
1214 res
= f_write(&File
, buffer
, cnt
, &br
);
1218 res
= f_read(&File
, buffer
, cnt
, &br
);
1221 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1225 z80_write_block(buffer
, addr
, br
);
1226 z80_bus_cmd(Release
);
1233 printf_P(PSTR("Disk full?\n"));
1240 FRESULT fr
= f_close(&File
);
1243 timer
= get_timer(timer
);
1244 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1245 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1253 my_puts_P(PSTR("Bus timeout\n"));
1257 return CMD_RET_FAILURE
;
1259 return CMD_RET_SUCCESS
;
1263 * command table for fat subcommands
1266 cmd_tbl_t cmd_tbl_fat
[] = {
1268 stat
, 2, CTBL_RPT
, do_stat
,
1269 "Show logical drive status",
1273 pwd
, 1, CTBL_RPT
, do_pwd
,
1274 "Print name of current/working directory",
1279 "Change the current/working directory.",
1283 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
1285 "[OPTION]... [FILE]...\n"
1286 //" -i prompt before removal\n"
1287 " -v explain what is being done\n"
1289 "rm does not remove directories."
1292 rmdir
, CONFIG_SYS_MAXARGS
, 0, do_rmdir
,
1293 "Remove the DIRECTORY(ies), if they are empty",
1294 "[OPTION]... DIRECTORY..."
1297 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
1298 "Create the DIRECTORY(ies), if they do not already exist.",
1299 "[OPTION]... DIRECTORY..."
1302 ls
, 2, CTBL_RPT
, do_ls
,
1303 "Directory listing",
1307 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
1308 "FatFS test function",
1313 "load binary file from a dos filesystem",
1314 "<d:/path/filename> <addr> [bytes [pos]]\n"
1315 " - Load binary file 'path/filename' on logical drive 'd'\n"
1316 " to address 'addr' from dos filesystem.\n"
1317 " 'pos' gives the file position to start loading from.\n"
1318 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1319 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1320 " the load stops on end of file."
1324 "write file into a dos filesystem",
1325 "<d:/path/filename> <addr> <bytes>\n"
1326 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1327 " starting at address 'addr'.\n"
1331 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1333 "[-f | -i | -n] [-prv] source_file target_file\n"
1334 // "[-f | -i | -n] [-prv] source_file target_file\n"
1335 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1336 " -f overwrite existing file ignoring write protection\n"
1337 " this option is ignored when the -n option is also used\n"
1338 " -i prompt before overwrite (overrides a previous -n option)\n"
1339 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1340 " -p preserve attributes and timestamps\n"
1341 " -r copy directories recursively\n"
1342 " -v explain what is being done\n"
1346 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1347 "Print sub command description/usage",
1349 " - print brief description of all sub commands\n"
1350 "fat help command ...\n"
1351 " - print detailed usage of sub cmd 'command'"
1354 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1355 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1356 FSTR("Alias for 'help'"),
1357 #ifdef CONFIG_SYS_LONGHELP
1359 #endif /* CONFIG_SYS_LONGHELP */
1361 #ifdef CONFIG_AUTO_COMPLETE
1365 /* Mark end of table */
1366 CMD_TBL_END(cmd_tbl_fat
)
1370 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1372 puts_P(PSTR("Huch?"));
1374 return CMD_RET_USAGE
;