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