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] == ':') {
211 if (*q
== '\0' || *q
== '/')
222 static void strip_trailing_slash(PATH_T
*p
)
224 char *beg
= path_skip_heading(p
->p_path
);
225 char *end
= p
->p_end
;
227 while (end
> beg
&& end
[-1] == '/')
234 * Move specified string into path. Convert "" to "." to handle BSD
235 * semantics for a null path. Strip trailing slashes.
238 path_set(PATH_T
*p
, char *string
)
240 if (strlen(string
) > MAX_PATHLEN
) {
241 err(PSTR("set: '%s': name too long"), string
);
245 (void)strcpy(p
->p_path
, string
);
246 p
->p_end
= p
->p_path
+ strlen(p
->p_path
);
248 if (p
->p_path
== p
->p_end
) {
253 strip_trailing_slash(p
);
258 * Append specified string to path, inserting '/' if necessary. Return a
259 * pointer to the old end of path for restoration.
262 path_append(PATH_T
*p
, char *name
)
264 char *old
= p
->p_end
;
265 int len
= strlen(name
);
267 /* The "+ 1" accounts for the '/' between old path and name. */
268 if ((len
+ p
->p_end
- p
->p_path
+ 1) > MAX_PATHLEN
) {
269 err(PSTR("append: '%s/%s': name too long"), p
->p_path
, name
);
274 * This code should always be executed, since paths shouldn't
277 if (p
->p_end
[-1] != '/') {
282 strncat(p
->p_end
, name
, len
);
286 strip_trailing_slash(p
);
291 * Restore path to previous value. (As returned by path_append.)
294 path_restore(PATH_T
*p
, char *old
)
301 * Return basename of path.
303 char *path_basename(PATH_T
*p
)
305 char *basename
= strrchr(p
->p_path
, '/');
310 basename
= p
->p_path
;
311 if ((basename
[0] & 0x38) == '0' && basename
[1] == ':')
319 char *path_basename_pattern(PATH_T
*p
)
321 char *pattern
= path_basename(p
);
322 if (strpbrk_P(pattern
, PSTR("*?"))) {
323 memmove(pattern
+1, pattern
, strlen(pattern
)+1);
326 //p->p_pattern = p->p_end + 1;
327 pattern
= p
->p_end
+ 1;
337 * Return basename/pattern of path.
340 char *path_split_pattern(PATH_T
*p
)
342 char *pp
= path_skip_heading(p
->p_path
);
343 char *pattern
= strrchr(pp
, '/');
345 if (pattern
== NULL
) {
352 memmove(pattern
+2, pattern
, strlen(pattern
)+1);
359 void path_fix(PATH_T
*p
)
361 char *pp
= path_skip_heading(p
->p_path
);
363 if (pp
!= p
->p_end
) {
369 void path_unfix(PATH_T
*p
)
371 char *pp
= path_skip_heading(p
->p_path
);
373 if (pp
!= p
->p_end
) {
378 static void swirl(void)
380 static const FLASH
char swirlchar
[] = { '-','\\','|','/' };
381 static uint_fast8_t cnt
;
382 static uint32_t tstamp
;
384 if (get_timer(0) > tstamp
) {
385 printf_P(PSTR("\b%c"), swirlchar
[cnt
]);
386 cnt
= (cnt
+1) % ARRAY_SIZE(swirlchar
);
387 tstamp
= get_timer(0) + 250;
392 * pwd - Print current directory of the current drive.
395 command_ret_t
do_pwd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
400 command_ret
= CMD_RET_SUCCESS
;
402 res
= f_getcwd(from
.p_path
, MAX_PATHLEN
); /* Get current directory path */
407 err(PSTR("Error: %S"), rctostr(res
));
414 * cd - Change the current/working directory.
417 command_ret_t
do_cd(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
423 command_ret
= CMD_RET_SUCCESS
;
426 arg
= getenv_str(PSTR(ENV_HOME
));
428 err(PSTR("'%S' is not set"), PSTR(ENV_HOME
));
435 res
= f_chdrive(arg
);
439 err(PSTR("'%s': %S"), arg
, rctostr(res
));
445 static int decode_arg(const char *arg
)
450 while ((c
= *++arg
) != '\0') {
453 attr
|= AM_ARC
; /* Archive */
456 attr
|= AM_HID
; /* Hidden */
459 attr
|= AM_RDO
; /* Read only */
462 attr
|= AM_SYS
; /* System */
465 err(PSTR("unknown attribute: '%c'"), c
);
472 static void print_attrib(char *path
, FILINFO
*f
)
474 printf_P(PSTR("%c%c%c%c%c %s%s\n"),
475 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
476 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
477 (f
->fattrib
& AM_HID
) ? 'H' : '-',
478 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
479 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
483 command_ret_t
do_attrib(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, UNUSED
char * const argv
[])
485 DIR Dir
; /* Directory object */
492 command_ret
= CMD_RET_SUCCESS
;
500 return CMD_RET_USAGE
;
501 if (arg
[0] != '-' && arg
[0] != '+')
503 attr
= decode_arg(arg
);
505 return CMD_RET_FAILURE
;
513 if (!path_set(&from
, *argv
)) {
516 char *pattern
= path_split_pattern(&from
);
517 if (*pattern
== '\0')
519 debug_fa("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
520 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
521 debug_fa("==== findfirst %d\n", res
);
522 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
524 err(PSTR("'%s%s': No such file or directory"), from
.p_path
, pattern
);
527 if (set_mask
| clear_mask
) {
528 if ((res
= f_chmod(Finfo
.fname
, set_mask
, set_mask
| clear_mask
)) != FR_OK
) {
530 err(PSTR("'%s%s': %S"), from
.p_path
, Finfo
.fname
, rctostr(res
));
535 print_attrib(from
.p_path
, &Finfo
);
539 res
= f_findnext(&Dir
, &Finfo
);
540 //debug_fa("==== findnext %d\n", res);
541 } while (res
== FR_OK
&& Finfo
.fname
[0]);
549 command_ret_t
do_rm(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
551 DIR Dir
; /* Directory object */
556 command_ret
= CMD_RET_SUCCESS
;
563 while ((opt
= getopt(argc
, argv
, PSTR("nv"))) != -1) {
572 return CMD_RET_USAGE
;
580 err(PSTR("missing operand"));
582 for (int i
= 0; i
< argc
; i
++) {
583 if (!path_set(&from
, argv
[i
])) {
586 char *pattern
= path_split_pattern(&from
);
588 debug_rm("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
590 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
591 debug_rm("==== findfirst %d\n", res
);
593 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
595 err(PSTR("cannot remove '%s%s': No such file or directory"), from
.p_path
, pattern
);
598 if (Finfo
.fattrib
& AM_DIR
) {
600 err(PSTR("cannot remove '%s%s': Is a directory"), from
.p_path
, Finfo
.fname
);
602 if (!(flags
& N_FLAG
)) {
603 if ((res
= f_unlink(Finfo
.fname
)) == FR_OK
) {
606 printf_P(PSTR("removed '%s%s'\n"), from
.p_path
, Finfo
.fname
);
610 err(PSTR("cannot remove '%s%s': %S"), from
.p_path
, Finfo
.fname
, rctostr(res
));
614 printf_P(PSTR("not removed '%s%s'\n"), from
.p_path
, Finfo
.fname
);
618 res
= f_findnext(&Dir
, &Finfo
);
619 //debug_rm("==== findnext %d\n", res);
620 } while (res
== FR_OK
&& Finfo
.fname
[0]);
628 return CMD_RET_FAILURE
;
634 command_ret_t
do_rmdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
637 command_ret
= CMD_RET_SUCCESS
;
642 command_ret_t
do_mkdir(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
645 command_ret
= CMD_RET_SUCCESS
;
651 static void print_dirent(FILINFO
*f
)
653 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
654 (f
->fattrib
& AM_DIR
) ? 'D' : '-',
655 (f
->fattrib
& AM_RDO
) ? 'R' : '-',
656 (f
->fattrib
& AM_HID
) ? 'H' : '-',
657 (f
->fattrib
& AM_SYS
) ? 'S' : '-',
658 (f
->fattrib
& AM_ARC
) ? 'A' : '-',
659 (f
->fdate
>> 9) + 1980, (f
->fdate
>> 5) & 15, f
->fdate
& 31,
660 (f
->ftime
>> 11), (f
->ftime
>> 5) & 63,
665 * ls path - Directory listing
668 command_ret_t
do_ls(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
671 DIR Dir
; /* Directory object */
678 command_ret
= CMD_RET_SUCCESS
;
682 if (!path_set(&from
, argv
[1])) {
687 char *pattern
= path_basename_pattern(&from
);
689 char *pattern
= path_split_pattern(&from
);
690 if (*pattern
== '\0')
693 debug_ls("==== path: '%s', pattern: '%s'\n", from
.p_path
? from
.p_path
: "<NULL>", pattern
? pattern
: "<NULL>");
696 res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
); /* Start to search for files */
697 if (res
!= FR_OK
|| !Finfo
.fname
[0]) {
699 err(PSTR("'%s%s': No such file or directory"), from
.p_path
, pattern
);
702 if (Finfo
.fattrib
& AM_DIR
) {
705 s1
++; p1
+= Finfo
.fsize
;
707 print_dirent(&Finfo
);
710 res
= f_findnext(&Dir
, &Finfo
);
711 } while (res
== FR_OK
&& Finfo
.fname
[0]);
715 if (res
== FR_OK
&& command_ret
== CMD_RET_SUCCESS
) {
716 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1
, p1
, s2
);
717 if (f_getfree(from
.p_path
, (DWORD
*)&p1
, &fs
) == FR_OK
)
718 printf_P(PSTR(", %10luK bytes free\n"), p1
* fs
->csize
/ 2);
721 if (res
&& command_ret
== CMD_RET_SUCCESS
) {
723 return CMD_RET_FAILURE
;
730 * tst path - for debugging: test access with different functions
733 command_ret_t
do_tst(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
735 DIR Dir
; /* Directory object */
741 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO
));
743 char * buf
= (char *) malloc(BUFFER_SIZE
);
745 printf_P(PSTR("tst: Out of Memory!\n"));
746 return CMD_RET_FAILURE
;
748 res
= f_getcwd(buf
, BUFFER_SIZE
); /* Get current directory path */
751 printf_P(PSTR("cwd: '%s'\n"), buf
);
756 return CMD_RET_FAILURE
;
764 printf_P(PSTR("arg: '%s' '%s'\n"), path
, pattern
);
765 printf_P(PSTR("==== f_stat: "));
766 res
= f_stat(path
, &Finfo
);
769 print_dirent(&Finfo
);
772 printf_P(PSTR("==== f_findfirst: "));
773 res
= f_findfirst(&Dir
, &Finfo
, path
, pattern
); /* Start to search for files */
776 print_dirent(&Finfo
);
780 printf_P(PSTR("==== f_opendir: "));
781 res
= f_opendir(&Dir
, path
);
785 return CMD_RET_SUCCESS
;
788 /******************************************************************************/
795 fr
= f_utime(to
.p_path
, fs
);
797 err(PSTR("f_utime: %s: %S"), to
.p_path
, rctostr(fr
));
798 fr
= f_chmod(to
.p_path
, fs
->fattrib
, AM_RDO
|AM_ARC
|AM_SYS
|AM_HID
);
800 err(PSTR("f_chmod: %s: %S"), to
.p_path
, rctostr(fr
));
804 void copy_file(FILINFO
*fs
, uint_fast8_t dne
)
811 if (blockbuf
== NULL
) {
812 blockbuf_size
= get_freemem() / 512 * 512;
813 if (blockbuf_size
!= 0)
814 blockbuf
= (uint8_t *) malloc(blockbuf_size
);
815 if (blockbuf
== NULL
) {
816 err(PSTR("Not enough memory!\n"));
821 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne
, blockbuf_size
, get_freemem());
822 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
825 if ((fr
= f_open(&from_fd
, from
.p_path
, FA_READ
)) != FR_OK
) {
826 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
831 * If the file exists and we're interactive, verify with the user.
834 if (flags
& N_FLAG
) {
836 printf_P(PSTR("%s not overwritten\n"), to
.p_path
);
839 } if (flags
& I_FLAG
) {
840 printf_P(PSTR("overwrite '%s'? "), to
.p_path
);
841 if (!confirm_yes()) {
846 if (flags
& F_FLAG
) {
847 /* Remove existing destination file name create a new file. */
848 f_chmod(to
.p_path
,0, AM_RDO
);
850 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
852 /* Overwrite existing destination file name. */
853 open_mode
= FA_WRITE
|FA_CREATE_ALWAYS
;
856 open_mode
= FA_WRITE
|FA_CREATE_NEW
;
858 fr
= f_open(&to_fd
, to
.p_path
, open_mode
);
861 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
866 while ((fr
= f_read(&from_fd
, blockbuf
, blockbuf_size
, &rcount
)) == FR_OK
&&
868 fr
= f_write(&to_fd
, blockbuf
, rcount
, &wcount
);
869 if (fr
|| wcount
< rcount
) {
870 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
875 err(PSTR("%s: S"), from
.p_path
, rctostr(fr
));
878 if ((fr
= f_close(&to_fd
)) != FR_OK
)
879 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
887 static void copy_dir(void)
889 DIR Dir
; /* Directory object */
891 char *old_from
, *old_to
;
893 char *pattern
= {"*"};
895 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
896 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
900 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
902 command_ret
= CMD_RET_FAILURE
;
906 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
907 res
== FR_OK
&& Finfo
.fname
[0];
908 res
= f_findnext(&Dir
, &Finfo
)) {
910 if (!(Finfo
.fattrib
& AM_DIR
) &&
911 (old_from
= path_append(&from
, Finfo
.fname
))) {
912 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
914 path_restore(&to
, old_to
);
916 path_restore(&from
, old_from
);
920 for (res
= f_findfirst(&Dir
, &Finfo
, from
.p_path
, pattern
);
921 res
== FR_OK
&& Finfo
.fname
[0];
922 res
= f_findnext(&Dir
, &Finfo
)) {
924 if ((Finfo
.fattrib
& AM_DIR
) &&
925 (old_from
= path_append(&from
, Finfo
.fname
))) {
926 if ((old_to
= path_append(&to
, Finfo
.fname
))) {
928 path_restore(&to
, old_to
);
930 path_restore(&from
, old_from
);
937 * copy file or directory at "from" to "to".
941 FILINFO from_stat
, to_stat
;
945 debug_cp("==== copy(); freemem: %u\n", get_freemem());
946 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
948 fr
= f_stat(from
.p_path
, &from_stat
);
950 err(PSTR("%s: %S"), from
.p_path
, rctostr(fr
));
954 /* not an error, but need to remember it happened */
955 if (f_stat(to
.p_path
, &to_stat
) != FR_OK
)
958 if (strcmp(to
.p_path
, from
.p_path
) == 0) {
959 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
960 to
.p_path
, from
.p_path
);
961 command_ret
= CMD_RET_FAILURE
;
967 if(from_stat
.fattrib
& AM_DIR
) {
968 if (!(flags
& R_FLAG
)) {
969 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
971 command_ret
= CMD_RET_FAILURE
;
976 * If the directory doesn't exist, create the new one.
978 if ((fr
= f_mkdir(to
.p_path
)) != FR_OK
) {
979 err(PSTR("%s: %S"), to
.p_path
, rctostr(fr
));
982 } else if (!(to_stat
.fattrib
& AM_DIR
)) {
983 (void)printf_P(PSTR("%s: not a directory.\n"), to
.p_path
);
992 copy_file(&from_stat
, dne
);
995 command_ret_t
do_cp(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
998 FRESULT fr
; /* Return value */
999 //DIR dj; /* Directory search object */
1000 FILINFO to_stat
; /* File information */
1006 command_ret
= CMD_RET_SUCCESS
;
1008 /* reset getopt() */
1012 while ((opt
= getopt(argc
, argv
, PSTR("finprv"))) != -1) {
1015 tflags
&= ~(I_FLAG
| N_FLAG
);
1019 tflags
&= ~(F_FLAG
| N_FLAG
);
1023 tflags
&= ~(F_FLAG
| I_FLAG
);
1036 return CMD_RET_USAGE
;
1045 return CMD_RET_USAGE
;
1049 /* last argument is destination */
1050 if (!path_set(&to
, argv
[--argc
]))
1054 * Cp has two distinct cases:
1056 * % cp [-rip] source target
1057 * % cp [-rip] source1 ... directory
1059 * In both cases, source can be either a file or a directory.
1061 * In (1), the target becomes a copy of the source. That is, if the
1062 * source is a file, the target will be a file, and likewise for
1065 * In (2), the real target is not directory, but "directory/source".
1068 fr
= f_stat(to
.p_path
, &to_stat
);
1069 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
1070 fr
, to_stat
.fattrib
, flags
, get_freemem());
1071 debug_cp(" from:'%s' to:'%s'\n", from
.p_path
, to
.p_path
);
1073 if (fr
!= FR_OK
&& fr
!= FR_NO_FILE
&& fr
!= FR_NO_PATH
) {
1074 err(PSTR("Test1: %s: %S"), to
.p_path
, rctostr(fr
));
1075 command_ret
= CMD_RET_FAILURE
;
1078 if (!(fr
== FR_OK
&& (to_stat
.fattrib
& AM_DIR
))) {
1080 * Case (1). Target is not a directory.
1083 err(PSTR("target '%s' is not a directory"), to
.p_path
);
1084 //command_ret = CMD_RET_USAGE;
1087 if (!path_set(&from
, *argv
)) {
1088 command_ret
= CMD_RET_FAILURE
;
1095 * Case (2). Target is a directory.
1098 if (!path_set(&from
, *argv
))
1100 if (!(old_to
= path_append(&to
, path_basename(&from
))))
1105 path_restore(&to
, old_to
);
1119 printf_P((PSTR("%s %s -> %s\n", badcp
? : "ERR:" : " ", curr
->fts_path
, to
.p_path
)));
1124 /******************************************************************************/
1127 * Work register for stat command
1131 WORD AccFiles
, AccDirs
;
1136 FRESULT
scan_files (
1137 char *path
, /* Pointer to the working buffer with start path */
1138 struct stat_dat_s
*statp
1146 res
= f_opendir(&dirs
, path
);
1150 while (((res
= f_readdir(&dirs
, &statp
->Finfo
)) == FR_OK
) &&
1151 statp
->Finfo
.fname
[0]) {
1152 if (FF_FS_RPATH
&& statp
->Finfo
.fname
[0] == '.')
1154 fn
= statp
->Finfo
.fname
;
1155 if (statp
->Finfo
.fattrib
& AM_DIR
) {
1158 strcpy(path
+i
+1, fn
);
1159 res
= scan_files(path
, statp
);
1164 //printf_P(PSTR("%s/%s\n"), path, fn);
1166 statp
->AccSize
+= statp
->Finfo
.fsize
;
1168 if (check_abort()) {
1180 * fatstat path - Show logical drive status
1183 command_ret_t
do_stat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1190 struct stat_dat_s statp
;
1192 buf
= (char *) malloc(BUFFER_SIZE
);
1194 printf_P(PSTR("fat stat: Out of Memory!\n"));
1195 return CMD_RET_FAILURE
;
1200 res
= f_getfree(path
, &nfreeclst
, &fs
);
1204 "Bytes/Cluster: %lu\n"
1205 "Number of FATs: %u\n"
1206 "Root DIR entries: %u\n"
1207 "Sectors/FAT: %lu\n"
1208 "Number of clusters: %lu\n"
1209 "FAT start (lba): %lu\n"
1210 "DIR start (lba,cluster): %lu\n"
1211 "Data start (lba): %lu\n"),
1212 fs
->fs_type
, (DWORD
)fs
->csize
* 512, fs
->n_fats
,
1213 fs
->n_rootdir
, fs
->fsize
, fs
->n_fatent
- 2,
1214 fs
->fatbase
, fs
->dirbase
, fs
->database
);
1218 res
= f_getlabel(path
, buf
, &serial
);
1222 "Volume S/N: %04X-%04X\n"),
1223 buf
, (WORD
)(serial
>> 16), (WORD
)(serial
& 0xFFFF));
1227 my_puts_P(PSTR("\nCounting... "));
1228 statp
.AccSize
= statp
.AccFiles
= statp
.AccDirs
= 0;
1231 res
= scan_files(buf
, &statp
);
1234 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1235 "%lu KB total disk space.\n%lu KB available.\n"),
1236 statp
.AccFiles
, statp
.AccSize
, statp
.AccDirs
,
1237 (fs
->n_fatent
- 2) * (fs
->csize
/ 2), nfreeclst
* (fs
->csize
/ 2)
1245 return CMD_RET_FAILURE
;
1247 return CMD_RET_SUCCESS
;
1251 * fatread/write - load binary file to/from a dos filesystem
1252 * read <d:/path/filename> <addr> [bytes [pos]]
1253 * write <d:/path/filename> <addr> <bytes>
1255 command_ret_t
do_rw(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc
, char * const argv
[])
1259 unsigned long bytes
;
1261 unsigned long bytes_rw
;
1263 bool dowrite
= (argv
[0][0] == 'w');
1264 FRESULT res
= FR_OK
;
1269 if (argc
< (dowrite
? 4 : 3))
1270 return CMD_RET_USAGE
;
1272 addr
= eval_arg(argv
[2], NULL
);
1273 if (addr
>= MAX_MEMORY
) {
1274 printf_P(PSTR("address too high: 0x%0lx\n"), addr
);
1275 return CMD_RET_FAILURE
;
1278 bytes
= eval_arg(argv
[3], NULL
);
1282 pos
= eval_arg(argv
[4], NULL
);
1286 if (addr
+ bytes
> MAX_MEMORY
)
1287 bytes
= MAX_MEMORY
- addr
;
1289 buffer
= (uint8_t *) malloc(BUFFER_SIZE
);
1290 if (buffer
== NULL
) {
1291 printf_P(PSTR("fatstat: Out of Memory!\n"));
1293 return CMD_RET_FAILURE
;
1297 res
= f_open(&File
, argv
[1], dowrite
? FA_WRITE
| FA_CREATE_ALWAYS
1301 res
= f_lseek(&File
, pos
);
1304 timer
= get_timer(0);
1306 unsigned int cnt
, br
;
1308 if (bytes
>= BUFFER_SIZE
) {
1310 bytes
-= BUFFER_SIZE
;
1312 cnt
= bytes
; bytes
= 0;
1315 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1319 z80_read_block(buffer
, addr
, cnt
);
1320 z80_bus_cmd(Release
);
1321 res
= f_write(&File
, buffer
, cnt
, &br
);
1325 res
= f_read(&File
, buffer
, cnt
, &br
);
1328 if (!(z80_bus_cmd(Request
) & ZST_ACQUIRED
)) {
1332 z80_write_block(buffer
, addr
, br
);
1333 z80_bus_cmd(Release
);
1340 printf_P(PSTR("Disk full?\n"));
1347 FRESULT fr
= f_close(&File
);
1350 timer
= get_timer(timer
);
1351 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1352 bytes_rw
, bytes_rw
, timer
? (bytes_rw
* 1000 / timer
) : 0);
1360 my_puts_P(PSTR("Bus timeout\n"));
1364 return CMD_RET_FAILURE
;
1366 return CMD_RET_SUCCESS
;
1370 * command table for fat subcommands
1373 cmd_tbl_t cmd_tbl_fat
[] = {
1375 stat
, 2, CTBL_RPT
, do_stat
,
1376 "Show logical drive status",
1380 pwd
, 1, CTBL_RPT
, do_pwd
,
1381 "Print name of current/working directory",
1385 attrib
, CONFIG_SYS_MAXARGS
, 0, do_attrib
,
1386 "Display or change attributes on a FAT filesystem",
1387 "[+-ahrs] files...\n"
1389 " - Clear attributes\n"
1390 " + Set attributes\n"
1401 "Change the current/working directory.",
1405 rm
, CONFIG_SYS_MAXARGS
, 0, do_rm
,
1407 "[OPTION]... [FILE]...\n"
1408 //" -i prompt before removal\n"
1409 " -v explain what is being done\n"
1411 "rm does not remove directories."
1414 rmdir
, CONFIG_SYS_MAXARGS
, 0, do_rmdir
,
1415 "Remove the DIRECTORY(ies), if they are empty",
1416 "[OPTION]... DIRECTORY..."
1419 mkdir
, CONFIG_SYS_MAXARGS
, 0, do_mkdir
,
1420 "Create the DIRECTORY(ies), if they do not already exist.",
1421 "[OPTION]... DIRECTORY..."
1424 ls
, 2, CTBL_RPT
, do_ls
,
1425 "Directory listing",
1429 tst
, 3, CTBL_DBG
|CTBL_RPT
, do_tst
,
1430 "FatFS test function",
1435 "load binary file from a dos filesystem",
1436 "<d:/path/filename> <addr> [bytes [pos]]\n"
1437 " - Load binary file 'path/filename' on logical drive 'd'\n"
1438 " to address 'addr' from dos filesystem.\n"
1439 " 'pos' gives the file position to start loading from.\n"
1440 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1441 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1442 " the load stops on end of file."
1446 "write file into a dos filesystem",
1447 "<d:/path/filename> <addr> <bytes>\n"
1448 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1449 " starting at address 'addr'.\n"
1453 cp
, CONFIG_SYS_MAXARGS
, CTBL_DBG
, do_cp
,
1455 "[-f | -i | -n] [-prv] source_file target_file\n"
1456 // "[-f | -i | -n] [-prv] source_file target_file\n"
1457 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1458 " -f overwrite existing file ignoring write protection\n"
1459 " this option is ignored when the -n option is also used\n"
1460 " -i prompt before overwrite (overrides a previous -n option)\n"
1461 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1462 " -p preserve attributes and timestamps\n"
1463 " -r copy directories recursively\n"
1464 " -v explain what is being done\n"
1468 help
, CONFIG_SYS_MAXARGS
, CTBL_RPT
, do_help
,
1469 "Print sub command description/usage",
1471 " - print brief description of all sub commands\n"
1472 "fat help command ...\n"
1473 " - print detailed usage of sub cmd 'command'"
1476 /* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1477 {FSTR("?"), CONFIG_SYS_MAXARGS
, 1, do_help
,
1478 FSTR("Alias for 'help'"),
1479 #ifdef CONFIG_SYS_LONGHELP
1481 #endif /* CONFIG_SYS_LONGHELP */
1483 #ifdef CONFIG_AUTO_COMPLETE
1487 /* Mark end of table */
1488 CMD_TBL_END(cmd_tbl_fat
)
1492 command_ret_t
do_fat(cmd_tbl_t
*cmdtp UNUSED
, uint_fast8_t flag UNUSED
, int argc UNUSED
, char * const argv
[] UNUSED
)
1494 puts_P(PSTR("Huch?"));
1496 return CMD_RET_USAGE
;