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