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