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 */
29 #define DEBUG_FA 1 /* set to 1 to debug */
31 #define debug_cp(fmt, args...) \
32 debug_cond(DEBUG_CP, fmt, ##args)
33 #define debug_ls(fmt, args...) \
34 debug_cond(DEBUG_LS, fmt, ##args)
35 #define debug_rm(fmt, args...) \
36 debug_cond(DEBUG_RM, fmt, ##args)
37 #define debug_fa(fmt, args...) \
38 debug_cond(DEBUG_FA, fmt, ##args)
42 /* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
43 /* TODO: detect_ramsize() should be moved to z80-if.c */
44 #define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
45 #define BUFFER_SIZE 512
46 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
50 char *p_end
; /* pointer to NULL at end of path */
51 char p_path
[MAX_PATHLEN
+ 1]; /* pointer to the start of a path */
55 * Multible (fat) partitions per physical drive are not supported,
56 * but we have up to 2 sdcard slots.
65 command_ret_t command_ret
;
69 #define F_FLAG (1<<3) // overwrite existing file ignoring write protection
70 #define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
71 #define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
72 #define P_FLAG (1<<4) // preserve attributes and timestamps
73 #define R_FLAG (1<<0) // copy directories recursively
74 #define V_FLAG (1<<5) // explain what is being done
78 void setup_fatfs(void)
80 f_mount(&FatFs0
, "0:", 0);
81 f_mount(&FatFs1
, "1:", 0);
84 DWORD
get_fattime (void)
90 gmtime_r(&timer
, &tm_timer
);
92 return fatfs_time(&tm_timer
);
96 static bool check_abort(void)
101 printf_P(PSTR("Abort\n"));
107 static const FLASH
char * const FLASH rc_strings
[] = {
110 FSTR("internal error"),
114 FSTR("invalid name"),
117 FSTR("invalid object"),
118 FSTR("write protected"),
119 FSTR("invalid drive"),
121 FSTR("no file system"),
122 FSTR("mkfs aborted"),
125 FSTR("not enough core"),
126 FSTR("too many open files"),
127 FSTR("invalid parameter")
130 static const FLASH
char * const FLASH rc_names
[] = {
137 FSTR("INVALID_NAME"),
140 FSTR("INVALID_OBJECT"),
141 FSTR("WRITE_PROTECTED"),
142 FSTR("INVALID_DRIVE"),
144 FSTR("NO_FILE_SYSTEM"),
145 FSTR("MKFS_ABORTED"),
148 FSTR("NOT_ENOUGH_CORE"),
149 FSTR("TOO_MANY_OPEN_FILES"),
150 FSTR("INVALID_PARAMETER")
154 void put_rc (FRESULT rc
)
157 printf_P(PSTR("rc=%u FR_"), rc
);
158 my_puts_P(rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
159 my_puts_P(PSTR("\n"));
161 printf_P(PSTR("rc=%u FR_%S\n"), rc
,
162 rc
< ARRAY_SIZE(rc_names
) ? rc_names
[rc
] : PSTR(" Unknown Error"));
166 const FLASH
char * rctostr(FRESULT rc
)
168 return rc
< ARRAY_SIZE(rc_strings
) ? rc_strings
[rc
] : PSTR(" Unknown Error");
171 void err(const char *fmt
, ...)
175 printf_P(PSTR("fat %s: "), cmdname
);
176 vfprintf_P(stdout
, fmt
, ap
);
178 printf_P(PSTR("\n"));
180 command_ret
= CMD_RET_FAILURE
;
183 /******************************************************************************/
186 * These functions manipulate paths in PATH_T structures.
188 * They eliminate multiple slashes in paths when they notice them,
189 * and keep the path non-slash terminated.
191 * Both path_set() and path_append() return 0 if the requested name
196 static void path_init(void)
198 from
.p_path
[0] = '\0'; from
.p_end
= from
.p_path
;
199 to
.p_path
[0] = '\0'; to
.p_end
= to
.p_path
;
202 static char *path_skip_heading(char *p
)
204 if ((p
[0] & 0x38) == '0' && p
[1] == ':') {
213 static void strip_trailing_slash(PATH_T
*p
)
215 char *beg
= path_skip_heading(p
->p_path
);
216 char *end
= p
->p_end
;
218 while (end
> beg
&& end
[-1] == '/')
225 * Move specified string into path. Convert "" to "." to handle BSD
226 * semantics for a null path. Strip trailing slashes.
229 path_set(PATH_T
*p
, char *string
)
231 if (strlen(string
) > MAX_PATHLEN
) {
232 err(PSTR("set: '%s': name too long"), string
);
236 (void)strcpy(p
->p_path
, string
);
237 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
239 if (p
->p_path
== p
->p_end
) {
244 strip_trailing_slash(p
);
249 * Append specified string to path, inserting '/' if necessary. Return a
250 * pointer to the old end of path for restoration.
253 path_append(PATH_T
*p
, char *name
)
255 char *old
= p
->p_end
;
256 int len
= strlen(name
);
258 /* The "+ 1" accounts for the '/' between old path and name. */
259 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
260 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
265 * This code should always be executed, since paths shouldn't
268 if (p
->p_end
[-1] != '/') {
273 strncat(p
->p_end
, name
, len
);
277 strip_trailing_slash(p
);
282 * Restore path to previous value. (As returned by path_append.)
285 path_restore(PATH_T
*p
, char *old
)
292 * Return basename of path.
294 char *path_basename(PATH_T
*p
)
296 char *basename
= strrchr(p
->p_path
, '/');
301 basename
= p
->p_path
;
302 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
310 char *path_basename_pattern(PATH_T
*p
)
312 char *pattern
= path_basename(p
);
313 if (strpbrk_P(pattern
, PSTR("*?"))) {
314 memmove(pattern
+1, pattern
, strlen(pattern
)+1);
317 //p->p_pattern = p->p_end + 1;
318 pattern
= p
->p_end
+ 1;
328 * Return basename/pattern of path.
331 char *path_split_pattern(PATH_T
*p
)
333 char *pp
= path_skip_heading(p
->p_path
);
334 char *pattern
= strrchr(pp
, '/');
336 if (pattern
== NULL
) {
343 memmove(pattern
+2, pattern
, strlen(pattern
)+1);
350 void path_fix(PATH_T
*p
)
352 char *pp
= path_skip_heading(p
->p_path
);
354 if (pp
!= p
->p_end
) {
360 void path_unfix(PATH_T
*p
)
362 char *pp
= path_skip_heading(p
->p_path
);
364 if (pp
!= p
->p_end
) {
369 static void swirl(void)
371 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
372 static uint_fast8_t cnt
;
373 static uint32_t tstamp
;
375 if (get_timer(0) > tstamp
) {
376 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
377 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
378 tstamp
= get_timer(0) + 250;
383 * pwd - Print current directory of the current drive.
386 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
391 command_ret
= CMD_RET_SUCCESS
;
393 res
= f_getcwd(from
.p_path
, MAX_PATHLEN
); /* Get current directory path */
398 err(PSTR("Error: %S"), rctostr(res
));
405 * cd - Change the current/working directory.
408 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
414 command_ret
= CMD_RET_SUCCESS
;
417 arg
= getenv_str(PSTR(ENV_HOME
));
419 err(PSTR("'%S' is not set"), PSTR(ENV_HOME
));
426 res
= f_chdrive(arg
);
430 err(PSTR("'%s': %S"), arg
, rctostr(res
));
436 static int decode_arg(const char *arg
)
441 while ((c
= *++arg
) != '\0') {
444 attr
|= AM_ARC
; /* Archive */
447 attr
|= AM_HID
; /* Hidden */
450 attr
|= AM_RDO
; /* Read only */
453 attr
|= AM_SYS
; /* System */
456 err(PSTR("unknown attribute: '%c'"), c
);
463 static void print_attrib(char *path
, FILINFO
*f
)
465 printf_P(PSTR("%c%c%c%c%c %s%s\n"),
466 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
467 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
468 (f
->fattrib
& AM_HID
) ? 'H' : '-',
469 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
470 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
474 command_ret_t
do_attrib(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, UNUSED
char * const argv
[])
476 DIR Dir
; /* Directory object */
483 command_ret
= CMD_RET_SUCCESS
;
491 return CMD_RET_USAGE
;
492 if (arg
[0] != '-' && arg
[0] != '+')
494 attr
= decode_arg(arg
);
496 return CMD_RET_FAILURE
;
504 if (!path_set(&from
, *argv
)) {
507 char *pattern
= path_split_pattern(&from
);
508 if (*pattern
== '\0')
510 debug_fa("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
511 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
512 debug_fa("==== findfirst %d\n", res
);
513 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
515 err(PSTR("'%s%s': No such file or directory"), from
.p_path
, pattern
);
518 if (set_mask
| clear_mask
) {
519 if ((res
= f_chmod(Finfo
.fname
, set_mask
, set_mask
| clear_mask
)) != FR_OK
) {
521 err(PSTR("'%s%s': %S"), from
.p_path
, Finfo
.fname
, rctostr(res
));
526 print_attrib(from
.p_path
, &Finfo
);
530 res
= f_findnext(&Dir
, &Finfo
);
531 //debug_fa("==== findnext %d\n", res);
532 } while (res
== FR_OK
&& Finfo
.fname
[0]);
540 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
542 DIR Dir
; /* Directory object */
547 command_ret
= CMD_RET_SUCCESS
;
554 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
563 return CMD_RET_USAGE
;
571 err(PSTR("missing operand"));
573 for (int i
= 0; i
< argc
; i
++) {
574 if (!path_set(&from
, argv
[i
])) {
577 char *pattern
= path_split_pattern(&from
);
579 debug_rm("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
581 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
582 debug_rm("==== findfirst %d\n", res
);
584 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
586 err(PSTR("cannot remove '%s%s': No such file or directory"), from
.p_path
, pattern
);
589 if (Finfo
.fattrib
& AM_DIR
) {
591 err(PSTR("cannot remove '%s%s': Is a directory"), from
.p_path
, Finfo
.fname
);
593 if (!(flags
& N_FLAG
)) {
594 if ((res
= f_unlink(Finfo
.fname
)) == FR_OK
) {
597 printf_P(PSTR("removed '%s%s'\n"), from
.p_path
, Finfo
.fname
);
601 err(PSTR("cannot remove '%s%s': %S"), from
.p_path
, Finfo
.fname
, rctostr(res
));
605 printf_P(PSTR("not removed '%s%s'\n"), from
.p_path
, Finfo
.fname
);
609 res
= f_findnext(&Dir
, &Finfo
);
610 //debug_rm("==== findnext %d\n", res);
611 } while (res
== FR_OK
&& Finfo
.fname
[0]);
619 return CMD_RET_FAILURE
;
625 command_ret_t
do_rmdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
628 command_ret
= CMD_RET_SUCCESS
;
633 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
636 command_ret
= CMD_RET_SUCCESS
;
642 static void print_dirent(FILINFO
*f
)
644 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
645 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
646 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
647 (f
->fattrib
& AM_HID
) ? 'H' : '-',
648 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
649 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
650 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
651 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
656 * ls path - Directory listing
659 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
662 DIR Dir
; /* Directory object */
669 command_ret
= CMD_RET_SUCCESS
;
673 if (!path_set(&from
, argv
[1])) {
678 char *pattern
= path_basename_pattern(&from
);
680 char *pattern
= path_split_pattern(&from
);
681 if (*pattern
== '\0')
684 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
687 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
688 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
690 err(PSTR("'%s%s': No such file or directory"), from
.p_path
, pattern
);
693 if (Finfo
.fattrib
& AM_DIR
) {
696 s1
++; p1
+= Finfo
.fsize
;
698 print_dirent(&Finfo
);
701 res
= f_findnext(&Dir
, &Finfo
);
702 } while (res
== FR_OK
&& Finfo
.fname
[0]);
706 if (res
== FR_OK
&& command_ret
== CMD_RET_SUCCESS
) {
707 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
708 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
709 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
712 if (res
&& command_ret
== CMD_RET_SUCCESS
) {
714 return CMD_RET_FAILURE
;
721 * tst path - for debugging: test access with different functions
724 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
726 DIR Dir
; /* Directory object */
732 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
734 char * buf
= (char *) malloc(BUFFER_SIZE
);
736 printf_P(PSTR("tst: Out of Memory!\n"));
737 return CMD_RET_FAILURE
;
739 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
742 printf_P(PSTR("cwd: '%s'\n"), buf
);
747 return CMD_RET_FAILURE
;
755 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
756 printf_P(PSTR("==== f_stat: "));
757 res
= f_stat(path
, &Finfo
);
760 print_dirent(&Finfo
);
763 printf_P(PSTR("==== f_findfirst: "));
764 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
767 print_dirent(&Finfo
);
771 printf_P(PSTR("==== f_opendir: "));
772 res
= f_opendir(&Dir
, path
);
776 return CMD_RET_SUCCESS
;
779 /******************************************************************************/
786 fr
= f_utime(to
.p_path
, fs
);
788 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
789 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
791 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
795 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
802 if (blockbuf
== NULL
) {
803 blockbuf_size
= get_freemem() / 512 * 512;
804 if (blockbuf_size
!= 0)
805 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
806 if (blockbuf
== NULL
) {
807 err(PSTR("Not enough memory!\n"));
812 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne
, blockbuf_size
, get_freemem());
813 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
816 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
817 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
822 * If the file exists and we're interactive, verify with the user.
825 if (flags
& N_FLAG
) {
827 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
830 } if (flags
& I_FLAG
) {
831 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
832 if (!confirm_yes()) {
837 if (flags
& F_FLAG
) {
838 /* Remove existing destination file name create a new file. */
839 f_chmod(to
.p_path
,0, AM_RDO
);
841 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
843 /* Overwrite existing destination file name. */
844 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
847 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
849 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
852 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
857 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
859 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
860 if (fr
|| wcount
< rcount
) {
861 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
866 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
869 if ((fr
= f_close(&to_fd
)) != FR_OK
)
870 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
878 static void copy_dir(void)
880 DIR Dir
; /* Directory object */
882 char *old_from
, *old_to
;
884 char *pattern
= {"*"};
886 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
887 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
891 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
893 command_ret
= CMD_RET_FAILURE
;
897 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
898 res
== FR_OK
&& Finfo
.fname
[0];
899 res
= f_findnext(&Dir
, &Finfo
)) {
901 if (!(Finfo
.fattrib
& AM_DIR
) &&
902 (old_from
= path_append(&from
, Finfo
.fname
))) {
903 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
905 path_restore(&to
, old_to
);
907 path_restore(&from
, old_from
);
911 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
912 res
== FR_OK
&& Finfo
.fname
[0];
913 res
= f_findnext(&Dir
, &Finfo
)) {
915 if ((Finfo
.fattrib
& AM_DIR
) &&
916 (old_from
= path_append(&from
, Finfo
.fname
))) {
917 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
919 path_restore(&to
, old_to
);
921 path_restore(&from
, old_from
);
928 * copy file or directory at "from" to "to".
932 FILINFO from_stat
, to_stat
;
936 debug_cp("==== copy(); freemem: %u\n", get_freemem());
937 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
939 fr
= f_stat(from
.p_path
, &from_stat
);
941 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
945 /* not an error, but need to remember it happened */
946 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
949 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
950 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
951 to
.p_path
, from
.p_path
);
952 command_ret
= CMD_RET_FAILURE
;
958 if(from_stat
.fattrib
& AM_DIR
) {
959 if (!(flags
& R_FLAG
)) {
960 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
962 command_ret
= CMD_RET_FAILURE
;
967 * If the directory doesn't exist, create the new one.
969 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
970 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
973 } else if (!(to_stat
.fattrib
& AM_DIR
)) {
974 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
983 copy_file(&from_stat
, dne
);
986 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
989 FRESULT fr
; /* Return value */
990 //DIR dj; /* Directory search object */
991 FILINFO to_stat
; /* File information */
997 command_ret
= CMD_RET_SUCCESS
;
1003 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
1006 tflags
&= ~(I_FLAG
| N_FLAG
);
1010 tflags
&= ~(F_FLAG
| N_FLAG
);
1014 tflags
&= ~(F_FLAG
| I_FLAG
);
1027 return CMD_RET_USAGE
;
1036 return CMD_RET_USAGE
;
1040 /* last argument is destination */
1041 if (!path_set(&to
, argv
[--argc
]))
1045 * Cp has two distinct cases:
1047 * % cp [-rip] source target
1048 * % cp [-rip] source1 ... directory
1050 * In both cases, source can be either a file or a directory.
1052 * In (1), the target becomes a copy of the source. That is, if the
1053 * source is a file, the target will be a file, and likewise for
1056 * In (2), the real target is not directory, but "directory/source".
1059 fr
= f_stat(to
.p_path
, &to_stat
);
1060 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
1061 fr
, to_stat
.fattrib
, flags
, get_freemem());
1062 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
1064 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
1065 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
1066 command_ret
= CMD_RET_FAILURE
;
1069 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
1071 * Case (1). Target is not a directory.
1074 err(PSTR("target '%s' is not a directory"), to
.p_path
);
1075 //command_ret = CMD_RET_USAGE;
1078 if (!path_set(&from
, *argv
)) {
1079 command_ret
= CMD_RET_FAILURE
;
1086 * Case (2). Target is a directory.
1089 if (!path_set(&from
, *argv
))
1091 if (!(old_to
= path_append(&to
, path_basename(&from
))))
1096 path_restore(&to
, old_to
);
1110 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
1115 /******************************************************************************/
1118 * Work register for stat command
1122 WORD AccFiles
, AccDirs
;
1127 FRESULT
scan_files (
1128 char *path
, /* Pointer to the working buffer with start path */
1129 struct stat_dat_s
*statp
1137 res
= f_opendir(&dirs
, path
);
1141 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
1142 statp
->Finfo
.fname
[0]) {
1143 if (_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1145 fn
= statp
->Finfo
.fname
;
1146 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1149 strcpy(path
+i
+1, fn
);
1150 res
= scan_files(path
, statp
);
1155 //printf_P(PSTR("%s/%s\n"), path, fn);
1157 statp
->AccSize
+= statp
->Finfo
.fsize
;
1159 if (check_abort()) {
1171 * fatstat path - Show logical drive status
1174 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1181 struct stat_dat_s statp
;
1183 buf
= (char *) malloc(BUFFER_SIZE
);
1185 printf_P(PSTR("fat stat: Out of Memory!\n"));
1186 return CMD_RET_FAILURE
;
1191 res
= f_getfree(path
, &nfreeclst
, &fs
);
1195 "Bytes/Cluster: %lu\n"
1196 "Number of FATs: %u\n"
1197 "Root DIR entries: %u\n"
1198 "Sectors/FAT: %lu\n"
1199 "Number of clusters: %lu\n"
1200 "FAT start (lba): %lu\n"
1201 "DIR start (lba,cluster): %lu\n"
1202 "Data start (lba): %lu\n"),
1203 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1204 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1205 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1209 res
= f_getlabel(path
, buf
, &serial
);
1213 "Volume S/N: %04X-%04X\n"),
1214 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1218 my_puts_P(PSTR("\nCounting... "));
1219 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1222 res
= scan_files(buf
, &statp
);
1225 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1226 "%lu KB total disk space.\n%lu KB available.\n"),
1227 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1228 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1236 return CMD_RET_FAILURE
;
1238 return CMD_RET_SUCCESS
;
1242 * fatread/write - load binary file to/from a dos filesystem
1243 * read <d:/path/filename> <addr> [bytes [pos]]
1244 * write <d:/path/filename> <addr> <bytes>
1246 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1250 unsigned long bytes
;
1252 unsigned long bytes_rw
;
1254 bool dowrite
= (argv
[0][0] == 'w');
1255 FRESULT res
= FR_OK
;
1260 if (argc
< (dowrite
? 4 : 3))
1261 return CMD_RET_USAGE
;
1263 addr
= eval_arg(argv
[2], NULL
);
1264 if (addr
>= MAX_MEMORY
) {
1265 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1266 return CMD_RET_FAILURE
;
1269 bytes
= eval_arg(argv
[3], NULL
);
1273 pos
= eval_arg(argv
[4], NULL
);
1277 if (addr
+ bytes
> MAX_MEMORY
)
1278 bytes
= MAX_MEMORY
- addr
;
1280 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1281 if (buffer
== NULL
) {
1282 printf_P(PSTR("fatstat: Out of Memory!\n"));
1284 return CMD_RET_FAILURE
;
1288 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1292 res
= f_lseek(&File
, pos
);
1295 timer
= get_timer(0);
1297 unsigned int cnt
, br
;
1299 if (bytes
>= BUFFER_SIZE
) {
1301 bytes
-= BUFFER_SIZE
;
1303 cnt
= bytes
; bytes
= 0;
1306 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1310 z80_read_block(buffer
, addr
, cnt
);
1311 z80_bus_cmd(Release
);
1312 res
= f_write(&File
, buffer
, cnt
, &br
);
1316 res
= f_read(&File
, buffer
, cnt
, &br
);
1319 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1323 z80_write_block(buffer
, addr
, br
);
1324 z80_bus_cmd(Release
);
1331 printf_P(PSTR("Disk full?\n"));
1338 FRESULT fr
= f_close(&File
);
1341 timer
= get_timer(timer
);
1342 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1343 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1351 my_puts_P(PSTR("Bus timeout\n"));
1355 return CMD_RET_FAILURE
;
1357 return CMD_RET_SUCCESS
;
1361 * command table for fat subcommands
1364 cmd_tbl_t cmd_tbl_fat
[] = {
1366 stat
, 2, CTBL_RPT
, do_stat
,
1367 "Show logical drive status",
1371 pwd
, 1, CTBL_RPT
, do_pwd
,
1372 "Print name of current/working directory",
1376 attrib
, CONFIG_SYS_MAXARGS
, 0, do_attrib
,
1377 "Display or change attributes on a FAT filesystem",
1378 "[+-ahrs] files...\n"
1380 " - Clear attributes\n"
1381 " + Set attributes\n"
1392 "Change the current/working directory.",
1396 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
1398 "[OPTION]... [FILE]...\n"
1399 //" -i prompt before removal\n"
1400 " -v explain what is being done\n"
1402 "rm does not remove directories."
1405 rmdir
, CONFIG_SYS_MAXARGS
, 0, do_rmdir
,
1406 "Remove the DIRECTORY(ies), if they are empty",
1407 "[OPTION]... DIRECTORY..."
1410 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
1411 "Create the DIRECTORY(ies), if they do not already exist.",
1412 "[OPTION]... DIRECTORY..."
1415 ls
, 2, CTBL_RPT
, do_ls
,
1416 "Directory listing",
1420 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
1421 "FatFS test function",
1426 "load binary file from a dos filesystem",
1427 "<d:/path/filename> <addr> [bytes [pos]]\n"
1428 " - Load binary file 'path/filename' on logical drive 'd'\n"
1429 " to address 'addr' from dos filesystem.\n"
1430 " 'pos' gives the file position to start loading from.\n"
1431 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1432 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1433 " the load stops on end of file."
1437 "write file into a dos filesystem",
1438 "<d:/path/filename> <addr> <bytes>\n"
1439 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1440 " starting at address 'addr'.\n"
1444 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1446 "[-f | -i | -n] [-prv] source_file target_file\n"
1447 // "[-f | -i | -n] [-prv] source_file target_file\n"
1448 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1449 " -f overwrite existing file ignoring write protection\n"
1450 " this option is ignored when the -n option is also used\n"
1451 " -i prompt before overwrite (overrides a previous -n option)\n"
1452 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1453 " -p preserve attributes and timestamps\n"
1454 " -r copy directories recursively\n"
1455 " -v explain what is being done\n"
1459 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1460 "Print sub command description/usage",
1462 " - print brief description of all sub commands\n"
1463 "fat help command ...\n"
1464 " - print detailed usage of sub cmd 'command'"
1467 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1468 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1469 FSTR("Alias for 'help'"),
1470 #ifdef CONFIG_SYS_LONGHELP
1472 #endif /* CONFIG_SYS_LONGHELP */
1474 #ifdef CONFIG_AUTO_COMPLETE
1478 /* Mark end of table */
1479 CMD_TBL_END(cmd_tbl_fat
)
1483 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1485 puts_P(PSTR("Huch?"));
1487 return CMD_RET_USAGE
;