]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
New command: fat attrib
[z180-stamp.git] / avr / cmd_fat.c
CommitLineData
35edb766 1/*
df607c3a 2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
35edb766 3 *
2d914b45 4 * SPDX-License-Identifier: GPL-2.0
35edb766
L
5 */
6
7/*
8 * FAT filesystem commands
9 */
10
b6c04275 11#include "cmd_fat.h"
97bd904d 12#include <util/delay.h>
2f53dd65 13
2f53dd65
L
14#include "ff.h"
15#include "z80-if.h"
2d914b45 16#include "eval_arg.h"
4565be9a 17#include "con-utils.h"
2f53dd65 18#include "print-utils.h"
19b9a7d8 19#include "time.h"
2f53dd65 20#include "timer.h"
4f881b02 21#include "debug.h"
b6c04275 22#include "env.h"
3d8c1907 23#include "getopt-min.h"
2f53dd65 24
9abe3216 25
b2e23285
L
26#define DEBUG_CP 1 /* set to 1 to debug */
27#define DEBUG_LS 1 /* set to 1 to debug */
0dd441e8 28#define DEBUG_RM 1 /* set to 1 to debug */
aeaab98e 29#define DEBUG_FA 1 /* set to 1 to debug */
9abe3216
L
30
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)
0dd441e8
L
35#define debug_rm(fmt, args...) \
36 debug_cond(DEBUG_RM, fmt, ##args)
aeaab98e
L
37#define debug_fa(fmt, args...) \
38 debug_cond(DEBUG_FA, fmt, ##args)
0dd441e8 39
9abe3216
L
40
41
df607c3a
L
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
19b9a7d8 45#define BUFFER_SIZE 512
3a93185f 46#define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
326d0992 47
19b9a7d8 48
3a93185f
L
49typedef struct {
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 */
52} PATH_T;
53
df607c3a
L
54/*
55 * Multible (fat) partitions per physical drive are not supported,
56 * but we have up to 2 sdcard slots.
57 */
b6c04275
L
58FATFS FatFs0;
59FATFS FatFs1;
60
3a93185f
L
61uint8_t *blockbuf;
62int blockbuf_size;
63PATH_T from;
64PATH_T to;
326d0992 65command_ret_t command_ret;
0dd441e8
L
66char *cmdname;
67
68static uint8_t flags;
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
75
76
326d0992 77
df607c3a
L
78void setup_fatfs(void)
79{
80 f_mount(&FatFs0, "0:", 0);
81 f_mount(&FatFs1, "1:", 0);
82}
b6c04275 83
2f53dd65
L
84DWORD get_fattime (void)
85{
19b9a7d8
L
86 time_t timer;
87 struct tm tm_timer;
88
89 time(&timer);
90 gmtime_r(&timer, &tm_timer);
91
92 return fatfs_time(&tm_timer);
2f53dd65
L
93}
94
95
b15d22a4
L
96static bool check_abort(void)
97{
98 bool ret = ctrlc();
99
100 if (ret)
101 printf_P(PSTR("Abort\n"));
102
103 return ret;
104}
105
106
326d0992
L
107static const FLASH char * const FLASH rc_strings[] = {
108 FSTR("OK"),
109 FSTR("disk error"),
110 FSTR("internal error"),
111 FSTR("not ready"),
112 FSTR("no file"),
113 FSTR("no path"),
114 FSTR("invalid name"),
115 FSTR("denied"),
116 FSTR("exist"),
117 FSTR("invalid object"),
118 FSTR("write protected"),
119 FSTR("invalid drive"),
120 FSTR("not enabled"),
121 FSTR("no file system"),
122 FSTR("mkfs aborted"),
123 FSTR("timeout"),
124 FSTR("locked"),
125 FSTR("not enough core"),
126 FSTR("too many open files"),
127 FSTR("invalid parameter")
128 };
129
2f53dd65
L
130static const FLASH char * const FLASH rc_names[] = {
131 FSTR("OK"),
132 FSTR("DISK_ERR"),
133 FSTR("INT_ERR"),
134 FSTR("NOT_READY"),
135 FSTR("NO_FILE"),
136 FSTR("NO_PATH"),
137 FSTR("INVALID_NAME"),
138 FSTR("DENIED"),
139 FSTR("EXIST"),
140 FSTR("INVALID_OBJECT"),
141 FSTR("WRITE_PROTECTED"),
142 FSTR("INVALID_DRIVE"),
143 FSTR("NOT_ENABLED"),
144 FSTR("NO_FILE_SYSTEM"),
145 FSTR("MKFS_ABORTED"),
146 FSTR("TIMEOUT"),
147 FSTR("LOCKED"),
148 FSTR("NOT_ENOUGH_CORE"),
7af9364e
L
149 FSTR("TOO_MANY_OPEN_FILES"),
150 FSTR("INVALID_PARAMETER")
2f53dd65
L
151 };
152
153static
154void put_rc (FRESULT rc)
155{
156#if GCC_BUG_61443
7af9364e
L
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"));
2f53dd65 160#else
7af9364e
L
161 printf_P(PSTR("rc=%u FR_%S\n"), rc,
162 rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
2f53dd65
L
163#endif
164}
165
326d0992
L
166const FLASH char * rctostr(FRESULT rc)
167{
168 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
169}
170
171void err(const char *fmt, ...)
172{
173 va_list ap;
174 va_start(ap, fmt);
0dd441e8
L
175 printf_P(PSTR("fat %s: "), cmdname);
176 vfprintf_P(stdout, fmt, ap);
326d0992 177 va_end(ap);
0dd441e8 178 printf_P(PSTR("\n"));
97bd904d 179 _delay_ms(20);
326d0992
L
180 command_ret = CMD_RET_FAILURE;
181}
182
b2e23285
L
183/******************************************************************************/
184
185/*
186 * These functions manipulate paths in PATH_T structures.
187 *
188 * They eliminate multiple slashes in paths when they notice them,
189 * and keep the path non-slash terminated.
190 *
191 * Both path_set() and path_append() return 0 if the requested name
192 * would be too long.
193 */
194
195
196static void path_init(void)
197{
6afaef20
L
198 from.p_path[0] = '\0'; from.p_end = from.p_path;
199 to.p_path[0] = '\0'; to.p_end = to.p_path;
b2e23285
L
200}
201
5013b94f
L
202static char *path_skip_heading(char *p)
203{
204 if ((p[0] & 0x38) == '0' && p[1] == ':') {
205 p += 2;
206 }
207 if (*p == '/')
208 ++p;
209
210 return p;
211}
212
b2e23285
L
213static void strip_trailing_slash(PATH_T *p)
214{
5013b94f 215 char *beg = path_skip_heading(p->p_path);
b2e23285 216 char *end = p->p_end;
5013b94f 217
b2e23285
L
218 while (end > beg && end[-1] == '/')
219 *--end = '\0';
220
221 p->p_end =end;
222}
223
224/*
225 * Move specified string into path. Convert "" to "." to handle BSD
226 * semantics for a null path. Strip trailing slashes.
227 */
228int
229path_set(PATH_T *p, char *string)
230{
231 if (strlen(string) > MAX_PATHLEN) {
232 err(PSTR("set: '%s': name too long"), string);
233 return 0;
234 }
235
236 (void)strcpy(p->p_path, string);
237 p->p_end = p->p_path + strlen(p->p_path);
238
239 if (p->p_path == p->p_end) {
240 *p->p_end++ = '.';
6afaef20 241 *p->p_end = '\0';
b2e23285
L
242 }
243
244 strip_trailing_slash(p);
245 return 1;
246}
247
248/*
249 * Append specified string to path, inserting '/' if necessary. Return a
250 * pointer to the old end of path for restoration.
251 */
252char *
253path_append(PATH_T *p, char *name)
254{
255 char *old = p->p_end;
256 int len = strlen(name);
257
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);
779c9785 261 return NULL;
b2e23285
L
262 }
263
264 /*
265 * This code should always be executed, since paths shouldn't
266 * end in '/'.
267 */
268 if (p->p_end[-1] != '/') {
269 *p->p_end++ = '/';
270 *p->p_end = '\0';
271 }
272
779c9785 273 strncat(p->p_end, name, len);
b2e23285
L
274 p->p_end += len;
275 *p->p_end = '\0';
276
277 strip_trailing_slash(p);
278 return old;
279}
280
281/*
282 * Restore path to previous value. (As returned by path_append.)
283 */
284void
285path_restore(PATH_T *p, char *old)
286{
287 p->p_end = old;
288 *p->p_end = '\0';
289}
290
291/*
292 * Return basename of path.
293 */
294char *path_basename(PATH_T *p)
295{
92cdf874 296 char *basename = strrchr(p->p_path, '/');
b2e23285 297
92cdf874
L
298 if (basename) {
299 ++basename;
300 } else {
301 basename = p->p_path;
302 if ((basename[0] & 0x38) == '0' && basename[1] == ':')
303 basename += 2;
304 }
305
306 return basename;
b2e23285
L
307}
308
f20e6902 309#if 0
6afaef20
L
310char *path_basename_pattern(PATH_T *p)
311{
312 char *pattern = path_basename(p);
313 if (strpbrk_P(pattern, PSTR("*?"))) {
314 memmove(pattern+1, pattern, strlen(pattern)+1);
315 *pattern++ = '\0';
316 } else {
317 //p->p_pattern = p->p_end + 1;
318 pattern = p->p_end + 1;
319 pattern[0] = '*';
320 pattern[1] = '\0';
321 }
322 return pattern;
323}
f20e6902 324#endif
b15d22a4 325
5013b94f
L
326/*
327 * Split path
328 * Return basename/pattern of path.
329 */
330
f20e6902 331char *path_split_pattern(PATH_T *p)
5013b94f
L
332{
333 char *pp = path_skip_heading(p->p_path);
334 char *pattern = strrchr(pp, '/');
335
336 if (pattern == NULL) {
337 pattern = pp;
338 p->p_end = pattern;
339 } else {
340 p->p_end = pattern;
341 pattern++;
342 }
343 memmove(pattern+2, pattern, strlen(pattern)+1);
344 pattern += 2;
345 *p->p_end = '\0' ;
346
347 return pattern;
348}
349
f20e6902 350void path_fix(PATH_T *p)
5013b94f
L
351{
352 char *pp = path_skip_heading(p->p_path);
353
354 if (pp != p->p_end) {
355 *p->p_end++ = '/';
356 *p->p_end = '\0' ;
357 }
358}
359
f20e6902 360void path_unfix(PATH_T *p)
5013b94f
L
361{
362 char *pp = path_skip_heading(p->p_path);
363
364 if (pp != p->p_end) {
365 *--p->p_end = '\0' ;
366 }
367}
368
b15d22a4
L
369static void swirl(void)
370{
6204987c 371 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
372 static uint_fast8_t cnt;
373 static uint32_t tstamp;
374
375 if (get_timer(0) > tstamp) {
376 printf_P(PSTR("\b%c"), swirlchar[cnt]);
377 cnt = (cnt+1) % ARRAY_SIZE(swirlchar);
6204987c 378 tstamp = get_timer(0) + 250;
b15d22a4
L
379 }
380}
381
b6c04275
L
382/*
383 * pwd - Print current directory of the current drive.
384 *
385 */
df607c3a 386command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275
L
387{
388 FRESULT res;
b6c04275 389
0dd441e8 390 cmdname = argv[0];
779c9785 391 command_ret = CMD_RET_SUCCESS;
0dd441e8 392
779c9785 393 res = f_getcwd(from.p_path, MAX_PATHLEN); /* Get current directory path */
b6c04275 394
779c9785
L
395 if (res == FR_OK)
396 puts(from.p_path);
397 else
398 err(PSTR("Error: %S"), rctostr(res));
b6c04275 399
779c9785 400 return command_ret;
b6c04275
L
401}
402
403
404/*
405 * cd - Change the current/working directory.
406 *
407 */
df607c3a 408command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
b6c04275
L
409{
410 char *arg;
779c9785 411 FRESULT res = FR_OK;
b6c04275 412
0dd441e8 413 cmdname = argv[0];
779c9785 414 command_ret = CMD_RET_SUCCESS;
0dd441e8 415
b6c04275 416 if (argc < 2) {
ee5c86e9 417 arg = getenv_str(PSTR(ENV_HOME));
b6c04275 418 if (arg == NULL) {
779c9785
L
419 err(PSTR("'%S' is not set"), PSTR(ENV_HOME));
420 return command_ret;
b6c04275
L
421 }
422 } else
423 arg = argv[1];
424
779c9785
L
425 if (arg[1] == ':')
426 res = f_chdrive(arg);
427 if (res == FR_OK)
b6c04275 428 res = f_chdir(arg);
779c9785
L
429 if (res != FR_OK)
430 err(PSTR("'%s': %S"), arg, rctostr(res));
b6c04275 431
779c9785 432 return command_ret;
b6c04275
L
433}
434
3d8c1907 435
aeaab98e
L
436static int decode_arg(const char *arg)
437{
438 BYTE attr = 0;
439 char c;
440
441 while ((c = *++arg) != '\0') {
442 switch (c) {
443 case 'a':
444 attr |= AM_ARC; /* Archive */
445 break;
446 case 'h':
447 attr |= AM_HID; /* Hidden */
448 break;
449 case 'r':
450 attr |= AM_RDO; /* Read only */
451 break;
452 case 's':
453 attr |= AM_SYS; /* System */
454 break;
455 default:
456 err(PSTR("unknown attribute: '%c'"), c);
457 return -1;
458 }
459 }
460 return attr;
461}
462
463static void print_attrib(char *path, FILINFO *f)
464{
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' : '-',
471 path, f->fname);
472}
473
474command_ret_t do_attrib(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, UNUSED char * const argv[])
475{
476 DIR Dir; /* Directory object */
477 FILINFO Finfo;
478 FRESULT res;
479 BYTE set_mask = 0;
480 BYTE clear_mask = 0;
481
482 cmdname = argv[0];
483 command_ret = CMD_RET_SUCCESS;
484
485
486 for (;;) {
487 int attr;
488 char *arg = *++argv;
489
490 if (!arg)
491 return CMD_RET_USAGE;
492 if (arg[0] != '-' && arg[0] != '+')
493 break;
494 attr = decode_arg(arg);
495 if (attr < 0)
496 return CMD_RET_FAILURE;
497 if (arg[0] == '+')
498 set_mask |= attr;
499 else
500 clear_mask |= attr;
501 }
502
503 do {
504 if (!path_set(&from, *argv)) {
505 /* TODO: error out*/
506 }
507 char *pattern = path_split_pattern(&from);
508 if (*pattern == '\0')
509 pattern = "*";
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]) {
514 path_fix(&from);
515 err(PSTR("'%s%s': No such file or directory"), from.p_path, pattern);
516 } else {
517 do {
518 if (set_mask | clear_mask) {
519 if ((res = f_chmod(Finfo.fname, set_mask, set_mask | clear_mask)) != FR_OK) {
520 path_fix(&from);
521 err(PSTR("'%s%s': %S"), from.p_path, Finfo.fname, rctostr(res));
522 path_unfix(&from);
523 }
524 } else {
525 path_fix(&from);
526 print_attrib(from.p_path, &Finfo);
527 path_unfix(&from);
528 }
529
530 res = f_findnext(&Dir, &Finfo);
531 //debug_fa("==== findnext %d\n", res);
532 } while (res == FR_OK && Finfo.fname[0]);
533 }
534 f_closedir(&Dir);
535 } while (*++argv);
536
537 return command_ret;
538}
3d8c1907 539
5be93dcb
L
540command_ret_t do_rm(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
541{
542 DIR Dir; /* Directory object */
543 FILINFO Finfo;
544 FRESULT res;
545
0dd441e8 546 cmdname = argv[0];
5013b94f 547 command_ret = CMD_RET_SUCCESS;
5be93dcb 548
0dd441e8
L
549 /* reset getopt() */
550 optind = 0;
551 flags = 0;
5be93dcb 552
0dd441e8
L
553 int opt;
554 while ((opt = getopt(argc, argv, PSTR("nv"))) != -1) {
555 switch (opt) {
556 case 'n':
557 flags |= N_FLAG;
558 break;
559 case 'v':
560 flags |= V_FLAG;
561 break;
562 default:
563 return CMD_RET_USAGE;
564 break;
565 }
566 }
567 argc -= optind;
568 argv += optind;
5be93dcb 569
0dd441e8
L
570 if (argc < 1) {
571 err(PSTR("missing operand"));
572 } else {
573 for (int i = 0; i < argc; i++) {
574 if (!path_set(&from, argv[i])) {
575 /* TODO: error out*/
576 }
5013b94f 577 char *pattern = path_split_pattern(&from);
0dd441e8
L
578
579 debug_rm("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", pattern ? pattern : "<NULL>");
580
581 res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
582 debug_rm("==== findfirst %d\n", res);
5013b94f
L
583
584 if (res != FR_OK || !Finfo.fname[0]) {
585 path_fix(&from);
586 err(PSTR("cannot remove '%s%s': No such file or directory"), from.p_path, pattern);
587 } else {
588 do {
589 if (Finfo.fattrib & AM_DIR) {
590 path_fix(&from);
591 err(PSTR("cannot remove '%s%s': Is a directory"), from.p_path, Finfo.fname);
592 } else {
593 if (!(flags & N_FLAG)) {
594 if ((res = f_unlink(Finfo.fname)) == FR_OK) {
595 if (flags & V_FLAG)
596 path_fix(&from);
597 printf_P(PSTR("removed '%s%s'\n"), from.p_path, Finfo.fname);
598 path_unfix(&from);
599 } else {
600 path_fix(&from);
601 err(PSTR("cannot remove '%s%s': %S"), from.p_path, Finfo.fname, rctostr(res));
602 }
0dd441e8 603 } else {
5013b94f
L
604 path_fix(&from);
605 printf_P(PSTR("not removed '%s%s'\n"), from.p_path, Finfo.fname);
606 path_unfix(&from);
0dd441e8 607 }
0dd441e8 608 }
5013b94f
L
609 res = f_findnext(&Dir, &Finfo);
610 //debug_rm("==== findnext %d\n", res);
611 } while (res == FR_OK && Finfo.fname[0]);
5be93dcb 612 }
0dd441e8 613 f_closedir(&Dir);
5be93dcb 614 }
5be93dcb 615
0dd441e8
L
616 /* TODO */
617 if (res) {
618 put_rc(res);
619 return CMD_RET_FAILURE;
620 }
5be93dcb 621 }
0dd441e8 622 return command_ret;
5be93dcb
L
623}
624
625command_ret_t do_rmdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
626{
0dd441e8 627 cmdname = argv[0];
5013b94f 628 command_ret = CMD_RET_SUCCESS;
0dd441e8 629
5013b94f 630 return command_ret;
5be93dcb
L
631}
632
633command_ret_t do_mkdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
634{
0dd441e8 635 cmdname = argv[0];
5013b94f 636 command_ret = CMD_RET_SUCCESS;
0dd441e8 637
5013b94f 638 return command_ret;
5be93dcb
L
639}
640
641
f20e6902 642static void print_dirent(FILINFO *f)
3d8c1907 643{
f20e6902 644 printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
3d8c1907
L
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,
652 f->fsize, f->fname);
653}
2f53dd65
L
654
655/*
df607c3a 656 * ls path - Directory listing
2f53dd65
L
657 *
658 */
df607c3a 659command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 660{
bbd45c46 661 FATFS *fs;
2f53dd65
L
662 DIR Dir; /* Directory object */
663 FILINFO Finfo;
664 unsigned long p1;
665 unsigned int s1, s2;
3d8c1907 666 FRESULT res;
b6c04275 667
0dd441e8 668 cmdname = argv[0];
f20e6902 669 command_ret = CMD_RET_SUCCESS;
0dd441e8 670
b2e23285
L
671 path_init();
672 if (argc > 1)
673 if (!path_set(&from, argv[1])) {
674 /* TODO: error out*/
675 }
92cdf874 676
f20e6902 677#if 0
6afaef20 678 char *pattern = path_basename_pattern(&from);
f20e6902
L
679#else
680 char *pattern = path_split_pattern(&from);
681 if (*pattern == '\0')
682 pattern = "*";
683#endif
6afaef20 684 debug_ls("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", pattern ? pattern : "<NULL>");
2f53dd65
L
685
686 p1 = s1 = s2 = 0;
6afaef20 687 res = f_findfirst(&Dir, &Finfo, from.p_path, pattern); /* Start to search for files */
f20e6902
L
688 if (res != FR_OK || !Finfo.fname[0]) {
689 path_fix(&from);
690 err(PSTR("'%s%s': No such file or directory"), from.p_path, pattern);
691 } else {
692 do {
693 if (Finfo.fattrib & AM_DIR) {
694 s2++;
695 } else {
696 s1++; p1 += Finfo.fsize;
697 }
698 print_dirent(&Finfo);
699 if (check_abort())
700 break;
701 res = f_findnext(&Dir, &Finfo);
702 } while (res == FR_OK && Finfo.fname[0]);
2f53dd65 703 }
3d8c1907 704 f_closedir(&Dir);
2f53dd65 705
f20e6902 706 if (res == FR_OK && command_ret == CMD_RET_SUCCESS) {
2f53dd65 707 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
b2e23285 708 if (f_getfree(from.p_path, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
709 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
710 }
711
f20e6902 712 if (res && command_ret == CMD_RET_SUCCESS) {
3d8c1907
L
713 put_rc(res);
714 return CMD_RET_FAILURE;
715 }
716
f20e6902 717 return command_ret;
3d8c1907
L
718}
719
df607c3a
L
720/*
721 * tst path - for debugging: test access with different functions
722 *
723 */
724command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
3d8c1907
L
725{
726 DIR Dir; /* Directory object */
727 FILINFO Finfo;
728 FRESULT res = FR_OK;
0dd441e8
L
729 char *path = "";
730 char *pattern = "*";
3d8c1907 731
3d8c1907
L
732 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO));
733
734 char * buf = (char *) malloc(BUFFER_SIZE);
735 if (buf == NULL) {
9abe3216 736 printf_P(PSTR("tst: Out of Memory!\n"));
3d8c1907
L
737 return CMD_RET_FAILURE;
738 }
739 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
740
741 if (!res) {
9abe3216 742 printf_P(PSTR("cwd: '%s'\n"), buf);
3d8c1907 743 }
b6c04275 744 free(buf);
2f53dd65
L
745 if (res) {
746 put_rc(res);
747 return CMD_RET_FAILURE;
748 }
749
3d8c1907
L
750 if (argc > 1)
751 path = argv[1];
0dd441e8
L
752 if (argc > 2)
753 pattern = argv[2];
3d8c1907 754
0dd441e8 755 printf_P(PSTR("arg: '%s' '%s'\n"), path, pattern);
3d8c1907
L
756 printf_P(PSTR("==== f_stat: "));
757 res = f_stat(path, &Finfo);
758 put_rc(res);
759 if (res == FR_OK) {
760 print_dirent(&Finfo);
761 }
762
763 printf_P(PSTR("==== f_findfirst: "));
0dd441e8 764 res = f_findfirst(&Dir, &Finfo, path, pattern); /* Start to search for files */
3d8c1907
L
765 put_rc(res);
766 if (res == FR_OK) {
767 print_dirent(&Finfo);
768 }
769 f_closedir(&Dir);
770
771 printf_P(PSTR("==== f_opendir: "));
772 res = f_opendir(&Dir, path);
773 put_rc(res);
774 f_closedir(&Dir);
775
2f53dd65
L
776 return CMD_RET_SUCCESS;
777}
778
df607c3a
L
779/******************************************************************************/
780
326d0992 781static void
9abe3216 782setfile(FILINFO *fs)
326d0992 783{
9abe3216 784 FRESULT fr;
326d0992 785
2d23b441 786 fr = f_utime(to.p_path, fs);
9abe3216 787 if (fr != FR_OK)
2d23b441
L
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);
9abe3216 790 if (fr != FR_OK)
2d23b441 791 err(PSTR("f_chmod: %s: %S"), to.p_path, rctostr(fr));
326d0992 792
326d0992
L
793}
794
795void copy_file(FILINFO *fs, uint_fast8_t dne)
796{
326d0992
L
797 FIL from_fd, to_fd;
798 UINT rcount, wcount;
326d0992 799 FRESULT fr;
9abe3216 800 BYTE open_mode;
326d0992 801
2f8b658a
L
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"));
808 return;
809 }
16fb450c
L
810 }
811
6afaef20 812debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne, blockbuf_size, get_freemem());
2d23b441 813debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
326d0992 814
16fb450c 815
2d23b441
L
816 if ((fr = f_open(&from_fd, from.p_path, FA_READ)) != FR_OK) {
817 err(PSTR("%s: %S"), from.p_path, rctostr(fr));
326d0992
L
818 return;
819 }
820
821 /*
822 * If the file exists and we're interactive, verify with the user.
326d0992
L
823 */
824 if (!dne) {
9abe3216
L
825 if (flags & N_FLAG) {
826 if (flags & V_FLAG)
2d23b441 827 printf_P(PSTR("%s not overwritten\n"), to.p_path);
9abe3216
L
828 f_close(&from_fd);
829 return;
830 } if (flags & I_FLAG) {
2d23b441 831 printf_P(PSTR("overwrite '%s'? "), to.p_path);
1cdd02d8 832 if (!confirm_yes()) {
326d0992
L
833 f_close(&from_fd);
834 return;
835 }
836 }
9abe3216
L
837 if (flags & F_FLAG) {
838 /* Remove existing destination file name create a new file. */
2d23b441
L
839 f_chmod(to.p_path,0, AM_RDO);
840 f_unlink(to.p_path);
9abe3216
L
841 open_mode = FA_WRITE|FA_CREATE_NEW;
842 } else {
843 /* Overwrite existing destination file name. */
844 open_mode = FA_WRITE|FA_CREATE_ALWAYS;
845 }
846 } else {
847 open_mode = FA_WRITE|FA_CREATE_NEW;
848 }
2d23b441 849 fr = f_open(&to_fd, to.p_path, open_mode);
326d0992
L
850
851 if (fr != FR_OK) {
2d23b441 852 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
326d0992
L
853 f_close(&from_fd);
854 return;
855 }
856
2f8b658a 857 while ((fr = f_read(&from_fd, blockbuf, blockbuf_size, &rcount)) == FR_OK &&
326d0992 858 rcount > 0) {
2f8b658a 859 fr = f_write(&to_fd, blockbuf, rcount, &wcount);
326d0992 860 if (fr || wcount < rcount) {
2d23b441 861 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
326d0992
L
862 break;
863 }
864 }
865 if (fr != FR_OK)
2d23b441 866 err(PSTR("%s: S"), from.p_path, rctostr(fr));
326d0992 867
326d0992
L
868 f_close(&from_fd);
869 if ((fr = f_close(&to_fd)) != FR_OK)
2d23b441 870 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
2f8b658a 871
9abe3216
L
872 if (flags & P_FLAG)
873 setfile(fs);
326d0992
L
874}
875
2d23b441
L
876static void copy();
877
326d0992 878static void copy_dir(void)
2d23b441
L
879{
880 DIR Dir; /* Directory object */
881 FILINFO Finfo;
882 char *old_from, *old_to;
883 FRESULT res;
884 char *pattern = {"*"};
885
6afaef20 886debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
2d23b441
L
887debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
888
0dd441e8
L
889#if 0
890
891 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
892 from->p_path);
893 command_ret = CMD_RET_FAILURE;
894
895#else
2d23b441
L
896
897 for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
898 res == FR_OK && Finfo.fname[0];
6afaef20 899 res = f_findnext(&Dir, &Finfo)) {
2d23b441
L
900
901 if (!(Finfo.fattrib & AM_DIR) &&
b2e23285
L
902 (old_from = path_append(&from, Finfo.fname))) {
903 if ((old_to = path_append(&to, Finfo.fname))) {
2d23b441
L
904 copy();
905 path_restore(&to, old_to);
906 }
907 path_restore(&from, old_from);
908 }
909 }
910
911 for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
912 res == FR_OK && Finfo.fname[0];
913 res = f_findnext(&Dir, &Finfo)) {
914
915 if ((Finfo.fattrib & AM_DIR) &&
b2e23285
L
916 (old_from = path_append(&from, Finfo.fname))) {
917 if ((old_to = path_append(&to, Finfo.fname))) {
2d23b441
L
918 copy();
919 path_restore(&to, old_to);
920 }
921 path_restore(&from, old_from);
922 }
923 }
924}
925#endif
326d0992
L
926
927/*
928 * copy file or directory at "from" to "to".
929 */
930static void copy()
931{
932 FILINFO from_stat, to_stat;
933 uint_fast8_t dne;
934 FRESULT fr;
935
6afaef20 936debug_cp("==== copy(); freemem: %u\n", get_freemem());
2d23b441 937debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
326d0992 938
2d23b441 939 fr = f_stat(from.p_path, &from_stat);
73cf0c86 940 if (fr != FR_OK) {
2d23b441 941 err(PSTR("%s: %S"), from.p_path, rctostr(fr));
326d0992
L
942 return;
943 }
944
945 /* not an error, but need to remember it happened */
2d23b441 946 if (f_stat(to.p_path, &to_stat) != FR_OK)
326d0992
L
947 dne = 1;
948 else {
2d23b441 949 if (strcmp(to.p_path, from.p_path) == 0) {
326d0992 950 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
2d23b441 951 to.p_path, from.p_path);
326d0992
L
952 command_ret = CMD_RET_FAILURE;
953 return;
954 }
955 dne = 0;
956 }
957
958 if(from_stat.fattrib & AM_DIR) {
959 if (!(flags & R_FLAG)) {
73cf0c86 960 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
2d23b441 961 from.p_path);
326d0992
L
962 command_ret = CMD_RET_FAILURE;
963 return;
964 }
965 if (dne) {
966 /*
967 * If the directory doesn't exist, create the new one.
968 */
2d23b441
L
969 if ((fr = f_mkdir(to.p_path)) != FR_OK) {
970 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
326d0992
L
971 return;
972 }
0dd441e8 973 } else if (!(to_stat.fattrib & AM_DIR)) {
2d23b441 974 (void)printf_P(PSTR("%s: not a directory.\n"), to.p_path);
326d0992
L
975 return;
976 }
977 copy_dir();
326d0992 978 if (flags & P_FLAG)
9abe3216 979 setfile(&from_stat);
31f423d2 980
326d0992
L
981 return;
982 }
983 copy_file(&from_stat, dne);
984}
985
df607c3a
L
986command_ret_t do_cp(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
987{
988
326d0992
L
989 FRESULT fr; /* Return value */
990 //DIR dj; /* Directory search object */
991 FILINFO to_stat; /* File information */
992 char *old_to;
df607c3a 993
df607c3a 994
0dd441e8 995 cmdname = argv[0];
1cdd02d8 996 uint8_t tflags = 0;
326d0992 997 command_ret = CMD_RET_SUCCESS;
df607c3a
L
998
999 /* reset getopt() */
1000 optind = 0;
1001
1002 int opt;
9abe3216 1003 while ((opt = getopt(argc, argv, PSTR("finprv"))) != -1) {
df607c3a
L
1004 switch (opt) {
1005 case 'f':
31f423d2 1006 tflags &= ~(I_FLAG | N_FLAG);
1cdd02d8 1007 tflags |= F_FLAG;
df607c3a
L
1008 break;
1009 case 'i':
31f423d2 1010 tflags &= ~(F_FLAG | N_FLAG);
1cdd02d8 1011 tflags |= I_FLAG;
df607c3a 1012 break;
31f423d2
L
1013 case 'n':
1014 tflags &= ~(F_FLAG | I_FLAG);
1015 tflags |= N_FLAG;
1016 break;
df607c3a 1017 case 'p':
1cdd02d8 1018 tflags |= P_FLAG;
df607c3a 1019 break;
df607c3a 1020 case 'r':
1cdd02d8 1021 tflags |= R_FLAG;
df607c3a
L
1022 break;
1023 case 'v':
1cdd02d8 1024 tflags |= V_FLAG;
df607c3a
L
1025 break;
1026 default:
1027 return CMD_RET_USAGE;
1028 break;
1029 }
1030 }
1cdd02d8 1031 flags = tflags;
df607c3a
L
1032 argc -= optind;
1033 argv += optind;
1034
1035 if (argc < 2)
1036 return CMD_RET_USAGE;
1037
b2e23285 1038 path_init();
326d0992 1039
9abe3216 1040 /* last argument is destination */
2d23b441 1041 if (!path_set(&to, argv[--argc]))
326d0992 1042 goto cleanup;
df607c3a 1043
326d0992
L
1044 /*
1045 * Cp has two distinct cases:
1046 *
1047 * % cp [-rip] source target
1048 * % cp [-rip] source1 ... directory
1049 *
1050 * In both cases, source can be either a file or a directory.
1051 *
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
1054 * directories.
1055 *
1056 * In (2), the real target is not directory, but "directory/source".
1057 */
1058
2d23b441 1059 fr = f_stat(to.p_path, &to_stat);
6afaef20
L
1060debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
1061 fr, to_stat.fattrib, flags, get_freemem());
2d23b441 1062debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
73cf0c86
L
1063
1064 if (fr != FR_OK && fr != FR_NO_FILE && fr != FR_NO_PATH) {
2d23b441 1065 err(PSTR("Test1: %s: %S"), to.p_path, rctostr(fr));
326d0992
L
1066 command_ret = CMD_RET_FAILURE;
1067 goto cleanup;
1068 }
73cf0c86 1069 if (!(fr == FR_OK && (to_stat.fattrib & AM_DIR))) {
326d0992
L
1070 /*
1071 * Case (1). Target is not a directory.
1072 */
1073 if (argc > 1) {
2d23b441 1074 err(PSTR("target '%s' is not a directory"), to.p_path);
97bd904d 1075 //command_ret = CMD_RET_USAGE;
326d0992
L
1076 goto cleanup;
1077 }
2d23b441 1078 if (!path_set(&from, *argv)) {
326d0992
L
1079 command_ret = CMD_RET_FAILURE;
1080 goto cleanup;
73cf0c86 1081 }
326d0992
L
1082 copy();
1083 }
1084 else {
1085 /*
1086 * Case (2). Target is a directory.
1087 */
1088 for (;; ++argv) {
2d23b441 1089 if (!path_set(&from, *argv))
326d0992 1090 continue;
b2e23285 1091 if (!(old_to = path_append(&to, path_basename(&from))))
326d0992
L
1092 continue;
1093 copy();
1094 if (!--argc)
1095 break;
2d23b441 1096 path_restore(&to, old_to);
326d0992
L
1097 }
1098 }
df607c3a 1099
326d0992 1100cleanup:
2f8b658a
L
1101 free(blockbuf);
1102 blockbuf = NULL;
1103 blockbuf_size = 0;
326d0992
L
1104
1105 return command_ret;
1106}
1107
1108#if 0
df607c3a 1109 if (flags & V_FLAG)
2d23b441 1110 printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to.p_path)));
df607c3a
L
1111
1112#endif
1113
df607c3a 1114
df607c3a
L
1115/******************************************************************************/
1116
326d0992
L
1117/*
1118 * Work register for stat command
1119 */
df607c3a
L
1120struct stat_dat_s {
1121 DWORD AccSize;
1122 WORD AccFiles, AccDirs;
1123 FILINFO Finfo;
1124};
1125
1126static
1127FRESULT scan_files (
1128 char *path, /* Pointer to the working buffer with start path */
1129 struct stat_dat_s *statp
1130)
1131{
1132 DIR dirs;
1133 FRESULT res;
1134 int i;
1135 char *fn;
1136
1137 res = f_opendir(&dirs, path);
1138 swirl();
1139 if (res == FR_OK) {
1140 i = strlen(path);
1141 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
1142 statp->Finfo.fname[0]) {
1143 if (_FS_RPATH && statp->Finfo.fname[0] == '.')
1144 continue;
1145 fn = statp->Finfo.fname;
1146 if (statp->Finfo.fattrib & AM_DIR) {
1147 statp->AccDirs++;
1148 path[i] = '/';
1149 strcpy(path+i+1, fn);
1150 res = scan_files(path, statp);
1151 path[i] = '\0';
1152 if (res != FR_OK)
1153 break;
1154 } else {
1155 //printf_P(PSTR("%s/%s\n"), path, fn);
1156 statp->AccFiles++;
1157 statp->AccSize += statp->Finfo.fsize;
1158 }
1159 if (check_abort()) {
1160 res = 255;
1161 break;
1162 }
1163 }
1164 }
1165
1166 return res;
1167}
1168
1169
1170/*
1171 * fatstat path - Show logical drive status
1172 *
1173 */
1174command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
1175{
1176 FATFS *fs;
1177 DWORD nfreeclst;
1178 FRESULT res;
1179 char *buf;
1180 char *path = "";
1181 struct stat_dat_s statp;
1182
1183 buf = (char *) malloc(BUFFER_SIZE);
1184 if (buf == NULL) {
1185 printf_P(PSTR("fat stat: Out of Memory!\n"));
1186 return CMD_RET_FAILURE;
1187 }
1188
1189 if (argc > 1)
1190 path = argv[1];
1191 res = f_getfree(path, &nfreeclst, &fs);
1192 if (!res) {
1193 printf_P(PSTR(
1194 "FAT type: %u\n"
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);
1206
1207#if _USE_LABEL
1208 DWORD serial;
1209 res = f_getlabel(path, buf, &serial);
1210 if (!res) {
1211 printf_P(PSTR(
1212 "Volume name: %s\n"
1213 "Volume S/N: %04X-%04X\n"),
1214 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
1215 }
1216#endif
1217 if (!res) {
1218 my_puts_P(PSTR("\nCounting... "));
1219 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
1220 strcpy(buf, path);
1221
1222 res = scan_files(buf, &statp);
1223 }
1224 if (!res) {
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)
1229 );
1230 }
1231 }
1232
1233 free(buf);
1234 if (res) {
1235 put_rc(res);
1236 return CMD_RET_FAILURE;
1237 }
1238 return CMD_RET_SUCCESS;
1239}
2f53dd65 1240
4565be9a
L
1241/*
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>
1245 */
df607c3a 1246command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 1247{
4565be9a
L
1248 FIL File;
1249 uint32_t addr;
1250 unsigned long bytes;
1251 unsigned long pos;
1252 unsigned long bytes_rw;
1253
3d8c1907 1254 bool dowrite = (argv[0][0] == 'w');
3b841cea 1255 FRESULT res = FR_OK;
4565be9a
L
1256 bool buserr = 0;
1257 uint32_t timer;
19b9a7d8 1258 uint8_t *buffer;
4565be9a 1259
4565be9a
L
1260 if (argc < (dowrite ? 4 : 3))
1261 return CMD_RET_USAGE;
1262
2d914b45 1263 addr = eval_arg(argv[2], NULL);
4565be9a
L
1264 if (addr >= MAX_MEMORY) {
1265 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
1266 return CMD_RET_FAILURE;
1267 }
1268 if (argc > 3)
2d914b45 1269 bytes = eval_arg(argv[3], NULL);
4565be9a
L
1270 else
1271 bytes = MAX_MEMORY;
1272 if (argc > 4)
2d914b45 1273 pos = eval_arg(argv[4], NULL);
4565be9a
L
1274 else
1275 pos = 0;
1276
1277 if (addr + bytes > MAX_MEMORY)
1278 bytes = MAX_MEMORY - addr;
1279
19b9a7d8 1280 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 1281 if (buffer == NULL) {
19b9a7d8 1282 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
1283 free(buffer);
1284 return CMD_RET_FAILURE;
1285 }
1286
4565be9a
L
1287 if (!res) {
1288 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
1289 : FA_READ );
1290
1291 if (!res) {
1292 res = f_lseek(&File, pos);
1293 if (!res) {
1294 bytes_rw = 0;
1295 timer = get_timer(0);
1296 while (bytes) {
1297 unsigned int cnt, br;
1298
19b9a7d8
L
1299 if (bytes >= BUFFER_SIZE) {
1300 cnt = BUFFER_SIZE;
1301 bytes -= BUFFER_SIZE;
4565be9a
L
1302 } else {
1303 cnt = bytes; bytes = 0;
1304 }
1305 if (dowrite) {
1306 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1307 buserr = 1;
1308 break;
1309 }
1310 z80_read_block(buffer, addr, cnt);
1311 z80_bus_cmd(Release);
1312 res = f_write(&File, buffer, cnt, &br);
1313 if (res != FR_OK)
1314 break;
1315 } else {
1316 res = f_read(&File, buffer, cnt, &br);
1317 if (res != FR_OK)
1318 break;
1319 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1320 buserr = 1;
1321 break;
1322 }
1323 z80_write_block(buffer, addr, br);
1324 z80_bus_cmd(Release);
1325 }
1326 addr += br;
1327
1328 bytes_rw += br;
1329 if (cnt != br) {
1330 if (dowrite)
1331 printf_P(PSTR("Disk full?\n"));
1332 break;
1333 }
b15d22a4 1334 if (check_abort())
4565be9a 1335 break;
4565be9a
L
1336 }
1337
1338 FRESULT fr = f_close(&File);
1339 if (!res)
1340 res = fr;
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);
1344 }
1345 }
4565be9a
L
1346 }
1347
19b9a7d8 1348 free(buffer);
19b9a7d8 1349
4565be9a
L
1350 if (buserr)
1351 my_puts_P(PSTR("Bus timeout\n"));
1352 if (res)
1353 put_rc(res);
1354 if (buserr || res)
1355 return CMD_RET_FAILURE;
1356
1357 return CMD_RET_SUCCESS;
1358}
b6c04275 1359
3d8c1907 1360/*
df607c3a 1361 * command table for fat subcommands
3d8c1907 1362 */
b6c04275 1363
6c851813 1364cmd_tbl_t cmd_tbl_fat[] = {
b6c04275 1365CMD_TBL_ITEM(
82c475ad 1366 stat, 2, CTBL_RPT, do_stat,
b6c04275
L
1367 "Show logical drive status",
1368 "dev"
1369),
1370CMD_TBL_ITEM(
779c9785 1371 pwd, 1, CTBL_RPT, do_pwd,
b6c04275
L
1372 "Print name of current/working directory",
1373 ""
1374),
aeaab98e
L
1375CMD_TBL_ITEM(
1376 attrib, CONFIG_SYS_MAXARGS, 0, do_attrib,
1377 "Display or change attributes on a FAT filesystem",
1378 "[+-ahrs] files...\n"
1379 "\n"
1380 " - Clear attributes\n"
1381 " + Set attributes\n"
1382 " a Archive\n"
1383 " h Hidden\n"
1384 " r Read only\n"
1385 " s System\n"
1386 "Display only:\n"
1387 " v Volume label\n"
1388 " d Directory\n"
1389),
b6c04275 1390CMD_TBL_ITEM(
82c475ad 1391 cd, 2, 0, do_cd,
b6c04275
L
1392 "Change the current/working directory.",
1393 "path"
1394),
5be93dcb
L
1395CMD_TBL_ITEM(
1396 rm, CONFIG_SYS_MAXARGS, 0, do_rm,
1397 "Remove FILE(s)",
5013b94f
L
1398 "[OPTION]... [FILE]...\n"
1399 //" -i prompt before removal\n"
1400 " -v explain what is being done\n"
1401 "\n"
1402 "rm does not remove directories."
5be93dcb
L
1403),
1404CMD_TBL_ITEM(
1405 rmdir, CONFIG_SYS_MAXARGS, 0, do_rmdir,
1406 "Remove the DIRECTORY(ies), if they are empty",
1407 "[OPTION]... DIRECTORY..."
1408),
1409CMD_TBL_ITEM(
1410 mkdir, CONFIG_SYS_MAXARGS, 0, do_mkdir,
1411 "Create the DIRECTORY(ies), if they do not already exist.",
1412 "[OPTION]... DIRECTORY..."
1413),
b6c04275 1414CMD_TBL_ITEM(
82c475ad 1415 ls, 2, CTBL_RPT, do_ls,
b6c04275
L
1416 "Directory listing",
1417 "path"
1418),
3d8c1907 1419CMD_TBL_ITEM(
0dd441e8 1420 tst, 3, CTBL_DBG|CTBL_RPT, do_tst,
3d8c1907
L
1421 "FatFS test function",
1422 "path"
1423),
b6c04275 1424CMD_TBL_ITEM(
82c475ad 1425 load, 5, 0, do_rw,
b6c04275
L
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."
1434),
1435CMD_TBL_ITEM(
82c475ad 1436 write, 4, 0, do_rw,
b6c04275
L
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"
1441),
1442
3d8c1907 1443CMD_TBL_ITEM(
82c475ad 1444 cp, CONFIG_SYS_MAXARGS, CTBL_DBG, do_cp,
3d8c1907 1445 "copy files",
1cdd02d8 1446 "[-f | -i | -n] [-prv] source_file target_file\n"
31f423d2
L
1447// "[-f | -i | -n] [-prv] source_file target_file\n"
1448// "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1cdd02d8
L
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"
3d8c1907
L
1456),
1457
ee5c86e9 1458CMD_TBL_ITEM(
82c475ad 1459 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
ee5c86e9
L
1460 "Print sub command description/usage",
1461 "\n"
1462 " - print brief description of all sub commands\n"
1463 "fat help command ...\n"
1464 " - print detailed usage of sub cmd 'command'"
1465),
1466
b6c04275
L
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
1471 FSTR(""),
1472#endif /* CONFIG_SYS_LONGHELP */
6c851813 1473 NULL,
b6c04275 1474#ifdef CONFIG_AUTO_COMPLETE
6c851813 1475 NULL,
b6c04275
L
1476#endif
1477},
6c851813 1478/* Mark end of table */
6c7c9c2d 1479CMD_TBL_END(cmd_tbl_fat)
b6c04275
L
1480};
1481
b6c04275 1482
df607c3a 1483command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275 1484{
6c7c9c2d 1485 puts_P(PSTR("Huch?"));
b6c04275
L
1486
1487 return CMD_RET_USAGE;
1488}