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