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