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