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