]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/cmd_fat.c
some cleanup, more debugging
[z180-stamp.git] / avr / cmd_fat.c
1 /*
2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
3 *
4 * SPDX-License-Identifier: GPL-2.0
5 */
6
7 /*
8 * FAT filesystem commands
9 */
10
11 #include "cmd_fat.h"
12 #include <util/delay.h>
13
14 #include "ff.h"
15 #include "z80-if.h"
16 #include "eval_arg.h"
17 #include "con-utils.h"
18 #include "print-utils.h"
19 #include "time.h"
20 #include "timer.h"
21 #include "debug.h"
22 #include "env.h"
23 #include "getopt-min.h"
24
25
26 #define DEBUG_CP 1 /* set to 1 to debug */
27 #define DEBUG_LS 1 /* set to 1 to debug */
28 #define DEBUG_RM 1 /* set to 1 to debug */
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)
34 #define debug_rm(fmt, args...) \
35 debug_cond(DEBUG_RM, fmt, ##args)
36
37
38
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
42 #define BUFFER_SIZE 512
43 #define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
44
45
46 typedef 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
51 /*
52 * Multible (fat) partitions per physical drive are not supported,
53 * but we have up to 2 sdcard slots.
54 */
55 FATFS FatFs0;
56 FATFS FatFs1;
57
58 uint8_t *blockbuf;
59 int blockbuf_size;
60 PATH_T from;
61 PATH_T to;
62 command_ret_t command_ret;
63 char *cmdname;
64
65 static 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
74
75 void setup_fatfs(void)
76 {
77 f_mount(&FatFs0, "0:", 0);
78 f_mount(&FatFs1, "1:", 0);
79 }
80
81 DWORD get_fattime (void)
82 {
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);
90 }
91
92
93 static 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
104 static 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
127 static 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"),
146 FSTR("TOO_MANY_OPEN_FILES"),
147 FSTR("INVALID_PARAMETER")
148 };
149
150 static
151 void put_rc (FRESULT rc)
152 {
153 #if GCC_BUG_61443
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"));
157 #else
158 printf_P(PSTR("rc=%u FR_%S\n"), rc,
159 rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
160 #endif
161 }
162
163 const FLASH char * rctostr(FRESULT rc)
164 {
165 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
166 }
167
168 void err(const char *fmt, ...)
169 {
170 va_list ap;
171 va_start(ap, fmt);
172 printf_P(PSTR("fat %s: "), cmdname);
173 vfprintf_P(stdout, fmt, ap);
174 va_end(ap);
175 printf_P(PSTR("\n"));
176 _delay_ms(20);
177 command_ret = CMD_RET_FAILURE;
178 }
179
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
193 static void path_init(void)
194 {
195 from.p_path[0] = '\0'; from.p_end = from.p_path;
196 to.p_path[0] = '\0'; to.p_end = to.p_path;
197 }
198
199 static 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 */
217 int
218 path_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++ = '.';
230 *p->p_end = '\0';
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 */
241 char *
242 path_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 */
273 void
274 path_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 */
283 char *path_basename(PATH_T *p)
284 {
285 char *basename = strrchr(p->p_path, '/');
286
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;
296 }
297
298 char *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 }
312
313 static void swirl(void)
314 {
315 static const FLASH char swirlchar[] = { '-','\\','|','/' };
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);
322 tstamp = get_timer(0) + 250;
323 }
324 }
325
326 /*
327 * pwd - Print current directory of the current drive.
328 *
329 */
330 command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
331 {
332 FRESULT res;
333 char *buf;
334
335 cmdname = argv[0];
336
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 */
362 command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
363 {
364 char *arg;
365 FRESULT res = 0;
366
367 cmdname = argv[0];
368
369 if (argc < 2) {
370 arg = getenv_str(PSTR(ENV_HOME));
371 if (arg == NULL) {
372 printf_P(PSTR("%s: \"%S\" is not set\n"), argv[0], PSTR(ENV_HOME));
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
396 #if 0
397 /*
398 * Remove trailing slashes,
399 * but keep a leading slash (absolute path)
400 */
401 void 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 }
416 #endif
417
418 command_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
424 cmdname = argv[0];
425
426 /* reset getopt() */
427 optind = 0;
428 flags = 0;
429
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;
446
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 }
476 }
477 res = f_findnext(&Dir, &Finfo);
478 debug_rm("==== findnext %d\n", res);
479 }
480 f_closedir(&Dir);
481 }
482
483 /* TODO */
484 if (res) {
485 put_rc(res);
486 return CMD_RET_FAILURE;
487 }
488 }
489 return command_ret;
490 }
491
492 command_ret_t do_rmdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
493 {
494 cmdname = argv[0];
495
496 return CMD_RET_SUCCESS;
497 }
498
499 command_ret_t do_mkdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
500 {
501 cmdname = argv[0];
502
503 return CMD_RET_SUCCESS;
504 }
505
506
507 int 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 }
519
520 /*
521 * ls path - Directory listing
522 *
523 */
524 command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
525 {
526 FATFS *fs;
527 DIR Dir; /* Directory object */
528 FILINFO Finfo;
529 unsigned long p1;
530 unsigned int s1, s2;
531 FRESULT res;
532
533 cmdname = argv[0];
534
535
536 path_init();
537 if (argc > 1)
538 if (!path_set(&from, argv[1])) {
539 /* TODO: error out*/
540 }
541
542 char *pattern = path_basename_pattern(&from);
543
544 debug_ls("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", pattern ? pattern : "<NULL>");
545
546 p1 = s1 = s2 = 0;
547 res = f_findfirst(&Dir, &Finfo, from.p_path, pattern); /* Start to search for files */
548 while (res == FR_OK && Finfo.fname[0]) {
549 if (Finfo.fattrib & AM_DIR) {
550 s2++;
551 } else {
552 s1++; p1 += Finfo.fsize;
553 }
554 print_dirent(&Finfo);
555 if (check_abort())
556 break;
557 res = f_findnext(&Dir, &Finfo);
558 }
559 f_closedir(&Dir);
560
561 if (res == FR_OK) {
562 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
563 if (f_getfree(from.p_path, (DWORD*)&p1, &fs) == FR_OK)
564 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
565 }
566
567 if (res) {
568 put_rc(res);
569 return CMD_RET_FAILURE;
570 }
571
572 return CMD_RET_SUCCESS;
573 }
574
575 /*
576 * tst path - for debugging: test access with different functions
577 *
578 */
579 command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
580 {
581 DIR Dir; /* Directory object */
582 FILINFO Finfo;
583 FRESULT res = FR_OK;
584 char *path = "";
585 char *pattern = "*";
586
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) {
591 printf_P(PSTR("tst: Out of Memory!\n"));
592 return CMD_RET_FAILURE;
593 }
594 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
595
596 if (!res) {
597 printf_P(PSTR("cwd: '%s'\n"), buf);
598 }
599 free(buf);
600 if (res) {
601 put_rc(res);
602 return CMD_RET_FAILURE;
603 }
604
605 if (argc > 1)
606 path = argv[1];
607 if (argc > 2)
608 pattern = argv[2];
609
610 printf_P(PSTR("arg: '%s' '%s'\n"), path, pattern);
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: "));
619 res = f_findfirst(&Dir, &Finfo, path, pattern); /* Start to search for files */
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
631 return CMD_RET_SUCCESS;
632 }
633
634 /******************************************************************************/
635
636 static void
637 setfile(FILINFO *fs)
638 {
639 FRESULT fr;
640
641 fr = f_utime(to.p_path, fs);
642 if (fr != FR_OK)
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);
645 if (fr != FR_OK)
646 err(PSTR("f_chmod: %s: %S"), to.p_path, rctostr(fr));
647
648 }
649
650 void copy_file(FILINFO *fs, uint_fast8_t dne)
651 {
652 FIL from_fd, to_fd;
653 UINT rcount, wcount;
654 FRESULT fr;
655 BYTE open_mode;
656
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 }
665 }
666
667 debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne, blockbuf_size, get_freemem());
668 debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
669
670
671 if ((fr = f_open(&from_fd, from.p_path, FA_READ)) != FR_OK) {
672 err(PSTR("%s: %S"), from.p_path, rctostr(fr));
673 return;
674 }
675
676 /*
677 * If the file exists and we're interactive, verify with the user.
678 */
679 if (!dne) {
680 if (flags & N_FLAG) {
681 if (flags & V_FLAG)
682 printf_P(PSTR("%s not overwritten\n"), to.p_path);
683 f_close(&from_fd);
684 return;
685 } if (flags & I_FLAG) {
686 printf_P(PSTR("overwrite '%s'? "), to.p_path);
687 if (!confirm_yes()) {
688 f_close(&from_fd);
689 return;
690 }
691 }
692 if (flags & F_FLAG) {
693 /* Remove existing destination file name create a new file. */
694 f_chmod(to.p_path,0, AM_RDO);
695 f_unlink(to.p_path);
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 }
704 fr = f_open(&to_fd, to.p_path, open_mode);
705
706 if (fr != FR_OK) {
707 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
708 f_close(&from_fd);
709 return;
710 }
711
712 while ((fr = f_read(&from_fd, blockbuf, blockbuf_size, &rcount)) == FR_OK &&
713 rcount > 0) {
714 fr = f_write(&to_fd, blockbuf, rcount, &wcount);
715 if (fr || wcount < rcount) {
716 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
717 break;
718 }
719 }
720 if (fr != FR_OK)
721 err(PSTR("%s: S"), from.p_path, rctostr(fr));
722
723 f_close(&from_fd);
724 if ((fr = f_close(&to_fd)) != FR_OK)
725 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
726
727 if (flags & P_FLAG)
728 setfile(fs);
729 }
730
731 static void copy();
732
733 static void copy_dir(void)
734 {
735 DIR Dir; /* Directory object */
736 FILINFO Finfo;
737 char *old_from, *old_to;
738 FRESULT res;
739 char *pattern = {"*"};
740
741 debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
742 debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
743
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
751
752 for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
753 res == FR_OK && Finfo.fname[0];
754 res = f_findnext(&Dir, &Finfo)) {
755
756 if (!(Finfo.fattrib & AM_DIR) &&
757 (old_from = path_append(&from, Finfo.fname))) {
758 if ((old_to = path_append(&to, Finfo.fname))) {
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) &&
771 (old_from = path_append(&from, Finfo.fname))) {
772 if ((old_to = path_append(&to, Finfo.fname))) {
773 copy();
774 path_restore(&to, old_to);
775 }
776 path_restore(&from, old_from);
777 }
778 }
779 }
780 #endif
781
782 /*
783 * copy file or directory at "from" to "to".
784 */
785 static void copy()
786 {
787 FILINFO from_stat, to_stat;
788 uint_fast8_t dne;
789 FRESULT fr;
790
791 debug_cp("==== copy(); freemem: %u\n", get_freemem());
792 debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
793
794 fr = f_stat(from.p_path, &from_stat);
795 if (fr != FR_OK) {
796 err(PSTR("%s: %S"), from.p_path, rctostr(fr));
797 return;
798 }
799
800 /* not an error, but need to remember it happened */
801 if (f_stat(to.p_path, &to_stat) != FR_OK)
802 dne = 1;
803 else {
804 if (strcmp(to.p_path, from.p_path) == 0) {
805 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
806 to.p_path, from.p_path);
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)) {
815 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
816 from.p_path);
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 */
824 if ((fr = f_mkdir(to.p_path)) != FR_OK) {
825 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
826 return;
827 }
828 } else if (!(to_stat.fattrib & AM_DIR)) {
829 (void)printf_P(PSTR("%s: not a directory.\n"), to.p_path);
830 return;
831 }
832 copy_dir();
833 if (flags & P_FLAG)
834 setfile(&from_stat);
835
836 return;
837 }
838 copy_file(&from_stat, dne);
839 }
840
841 command_ret_t do_cp(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
842 {
843
844 FRESULT fr; /* Return value */
845 //DIR dj; /* Directory search object */
846 FILINFO to_stat; /* File information */
847 char *old_to;
848
849
850 cmdname = argv[0];
851 uint8_t tflags = 0;
852 command_ret = CMD_RET_SUCCESS;
853
854 /* reset getopt() */
855 optind = 0;
856
857 int opt;
858 while ((opt = getopt(argc, argv, PSTR("finprv"))) != -1) {
859 switch (opt) {
860 case 'f':
861 tflags &= ~(I_FLAG | N_FLAG);
862 tflags |= F_FLAG;
863 break;
864 case 'i':
865 tflags &= ~(F_FLAG | N_FLAG);
866 tflags |= I_FLAG;
867 break;
868 case 'n':
869 tflags &= ~(F_FLAG | I_FLAG);
870 tflags |= N_FLAG;
871 break;
872 case 'p':
873 tflags |= P_FLAG;
874 break;
875 case 'r':
876 tflags |= R_FLAG;
877 break;
878 case 'v':
879 tflags |= V_FLAG;
880 break;
881 default:
882 return CMD_RET_USAGE;
883 break;
884 }
885 }
886 flags = tflags;
887 argc -= optind;
888 argv += optind;
889
890 if (argc < 2)
891 return CMD_RET_USAGE;
892
893 path_init();
894
895 /* last argument is destination */
896 if (!path_set(&to, argv[--argc]))
897 goto cleanup;
898
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
914 fr = f_stat(to.p_path, &to_stat);
915 debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
916 fr, to_stat.fattrib, flags, get_freemem());
917 debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
918
919 if (fr != FR_OK && fr != FR_NO_FILE && fr != FR_NO_PATH) {
920 err(PSTR("Test1: %s: %S"), to.p_path, rctostr(fr));
921 command_ret = CMD_RET_FAILURE;
922 goto cleanup;
923 }
924 if (!(fr == FR_OK && (to_stat.fattrib & AM_DIR))) {
925 /*
926 * Case (1). Target is not a directory.
927 */
928 if (argc > 1) {
929 err(PSTR("target '%s' is not a directory"), to.p_path);
930 //command_ret = CMD_RET_USAGE;
931 goto cleanup;
932 }
933 if (!path_set(&from, *argv)) {
934 command_ret = CMD_RET_FAILURE;
935 goto cleanup;
936 }
937 copy();
938 }
939 else {
940 /*
941 * Case (2). Target is a directory.
942 */
943 for (;; ++argv) {
944 if (!path_set(&from, *argv))
945 continue;
946 if (!(old_to = path_append(&to, path_basename(&from))))
947 continue;
948 copy();
949 if (!--argc)
950 break;
951 path_restore(&to, old_to);
952 }
953 }
954
955 cleanup:
956 free(blockbuf);
957 blockbuf = NULL;
958 blockbuf_size = 0;
959
960 return command_ret;
961 }
962
963 #if 0
964 if (flags & V_FLAG)
965 printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to.p_path)));
966
967 #endif
968
969
970 /******************************************************************************/
971
972 /*
973 * Work register for stat command
974 */
975 struct stat_dat_s {
976 DWORD AccSize;
977 WORD AccFiles, AccDirs;
978 FILINFO Finfo;
979 };
980
981 static
982 FRESULT 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 */
1029 command_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 }
1095
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 */
1101 command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
1102 {
1103 FIL File;
1104 uint32_t addr;
1105 unsigned long bytes;
1106 unsigned long pos;
1107 unsigned long bytes_rw;
1108
1109 bool dowrite = (argv[0][0] == 'w');
1110 FRESULT res = FR_OK;
1111 bool buserr = 0;
1112 uint32_t timer;
1113 uint8_t *buffer;
1114
1115 if (argc < (dowrite ? 4 : 3))
1116 return CMD_RET_USAGE;
1117
1118 addr = eval_arg(argv[2], NULL);
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)
1124 bytes = eval_arg(argv[3], NULL);
1125 else
1126 bytes = MAX_MEMORY;
1127 if (argc > 4)
1128 pos = eval_arg(argv[4], NULL);
1129 else
1130 pos = 0;
1131
1132 if (addr + bytes > MAX_MEMORY)
1133 bytes = MAX_MEMORY - addr;
1134
1135 buffer = (uint8_t *) malloc(BUFFER_SIZE);
1136 if (buffer == NULL) {
1137 printf_P(PSTR("fatstat: Out of Memory!\n"));
1138 free(buffer);
1139 return CMD_RET_FAILURE;
1140 }
1141
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
1154 if (bytes >= BUFFER_SIZE) {
1155 cnt = BUFFER_SIZE;
1156 bytes -= BUFFER_SIZE;
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 }
1189 if (check_abort())
1190 break;
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 }
1201 }
1202
1203 free(buffer);
1204
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 }
1214
1215 /*
1216 * command table for fat subcommands
1217 */
1218
1219 cmd_tbl_t cmd_tbl_fat[] = {
1220 CMD_TBL_ITEM(
1221 stat, 2, CTBL_RPT, do_stat,
1222 "Show logical drive status",
1223 "dev"
1224 ),
1225 CMD_TBL_ITEM(
1226 pwd, 2, CTBL_RPT, do_pwd,
1227 "Print name of current/working directory",
1228 ""
1229 ),
1230 CMD_TBL_ITEM(
1231 cd, 2, 0, do_cd,
1232 "Change the current/working directory.",
1233 "path"
1234 ),
1235 CMD_TBL_ITEM(
1236 rm, CONFIG_SYS_MAXARGS, 0, do_rm,
1237 "Remove FILE(s)",
1238 "[OPTION]... [FILE]..."
1239 ),
1240 CMD_TBL_ITEM(
1241 rmdir, CONFIG_SYS_MAXARGS, 0, do_rmdir,
1242 "Remove the DIRECTORY(ies), if they are empty",
1243 "[OPTION]... DIRECTORY..."
1244 ),
1245 CMD_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 ),
1250 CMD_TBL_ITEM(
1251 ls, 2, CTBL_RPT, do_ls,
1252 "Directory listing",
1253 "path"
1254 ),
1255 CMD_TBL_ITEM(
1256 tst, 3, CTBL_DBG|CTBL_RPT, do_tst,
1257 "FatFS test function",
1258 "path"
1259 ),
1260 CMD_TBL_ITEM(
1261 load, 5, 0, do_rw,
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 ),
1271 CMD_TBL_ITEM(
1272 write, 4, 0, do_rw,
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
1279 CMD_TBL_ITEM(
1280 cp, CONFIG_SYS_MAXARGS, CTBL_DBG, do_cp,
1281 "copy files",
1282 "[-f | -i | -n] [-prv] source_file target_file\n"
1283 // "[-f | -i | -n] [-prv] source_file target_file\n"
1284 // "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
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"
1292 ),
1293
1294 CMD_TBL_ITEM(
1295 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
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
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 */
1309 NULL,
1310 #ifdef CONFIG_AUTO_COMPLETE
1311 NULL,
1312 #endif
1313 },
1314 /* Mark end of table */
1315 CMD_TBL_END(cmd_tbl_fat)
1316 };
1317
1318
1319 command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
1320 {
1321 puts_P(PSTR("Huch?"));
1322
1323 return CMD_RET_USAGE;
1324 }