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