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