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