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