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