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