]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
+ cp command (WIP)
[z180-stamp.git] / avr / cmd_fat.c
CommitLineData
35edb766 1/*
df607c3a 2 * (C) Copyright 2014,2016,2018 Leo C. <erbl259-lmu@yahoo.de>
35edb766 3 *
2d914b45 4 * SPDX-License-Identifier: GPL-2.0
35edb766
L
5 */
6
7/*
8 * FAT filesystem commands
9 */
10
b6c04275 11#include "cmd_fat.h"
2f53dd65 12
2f53dd65
L
13#include "ff.h"
14#include "z80-if.h"
2d914b45 15#include "eval_arg.h"
4565be9a 16#include "con-utils.h"
2f53dd65 17#include "print-utils.h"
19b9a7d8 18#include "time.h"
2f53dd65 19#include "timer.h"
4f881b02 20#include "debug.h"
b6c04275 21#include "env.h"
3d8c1907 22#include "getopt-min.h"
2f53dd65 23
df607c3a
L
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
19b9a7d8 27#define BUFFER_SIZE 512
326d0992
L
28#define MAXBSIZE 512 /* TODO */
29
19b9a7d8 30
df607c3a
L
31/*
32 * Multible (fat) partitions per physical drive are not supported,
33 * but we have up to 2 sdcard slots.
34 */
b6c04275
L
35FATFS FatFs0;
36FATFS FatFs1;
37
326d0992
L
38command_ret_t command_ret;
39
df607c3a
L
40void setup_fatfs(void)
41{
42 f_mount(&FatFs0, "0:", 0);
43 f_mount(&FatFs1, "1:", 0);
44}
b6c04275 45
2f53dd65
L
46DWORD get_fattime (void)
47{
19b9a7d8
L
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);
2f53dd65
L
55}
56
57
b15d22a4
L
58static 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
326d0992
L
69static 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
2f53dd65
L
92static 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"),
7af9364e
L
111 FSTR("TOO_MANY_OPEN_FILES"),
112 FSTR("INVALID_PARAMETER")
2f53dd65
L
113 };
114
115static
116void put_rc (FRESULT rc)
117{
118#if GCC_BUG_61443
7af9364e
L
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"));
2f53dd65 122#else
7af9364e
L
123 printf_P(PSTR("rc=%u FR_%S\n"), rc,
124 rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
2f53dd65
L
125#endif
126}
127
326d0992
L
128const FLASH char * rctostr(FRESULT rc)
129{
130 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
131}
132
133void 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
b15d22a4
L
145
146static void swirl(void)
147{
6204987c 148 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
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);
6204987c 155 tstamp = get_timer(0) + 250;
b15d22a4
L
156 }
157}
158
b6c04275
L
159/*
160 * pwd - Print current directory of the current drive.
161 *
162 */
df607c3a 163command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275
L
164{
165 FRESULT res;
166 char *buf;
167
b6c04275
L
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 */
df607c3a 193command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
b6c04275
L
194{
195 char *arg;
196 FRESULT res = 0;
197
b6c04275 198 if (argc < 2) {
ee5c86e9 199 arg = getenv_str(PSTR(ENV_HOME));
b6c04275 200 if (arg == NULL) {
ee5c86e9 201 printf_P(PSTR("%s: \"%S\" is not set\n"), argv[0], PSTR(ENV_HOME));
b6c04275
L
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
326d0992 225#define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
3d8c1907 226
df607c3a
L
227/*
228 * Remove trailing slashes,
229 * but keep a leading slash (absolute path)
230 */
3d8c1907
L
231void 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
247int 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}
2f53dd65
L
259
260/*
df607c3a 261 * ls path - Directory listing
2f53dd65
L
262 *
263 */
df607c3a 264command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 265{
bbd45c46 266 FATFS *fs;
2f53dd65
L
267 DIR Dir; /* Directory object */
268 FILINFO Finfo;
269 unsigned long p1;
270 unsigned int s1, s2;
3d8c1907 271 FRESULT res;
b6c04275 272
3d8c1907
L
273 char *path = "";
274 if (argc > 1)
275 path = argv[1];
276 strip_trailing_slash_relpath(path);
b6c04275 277
3d8c1907
L
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
326d0992 288 char *pattern;
3d8c1907
L
289 if (strpbrk_P(p, PSTR("*?")) ||
290 (f_stat(path, &Finfo) == FR_OK && !(Finfo.fattrib & AM_DIR))) {
291 pattern = strdup(p);
292 *p = '\0';
326d0992
L
293 } else
294 pattern = strdup("*");
3d8c1907
L
295 strip_trailing_slash_relpath(path);
296
326d0992 297//printf_P(PSTR("*: |%s| |%s|\n"), path ? path : "<NULL>", pattern ? pattern : "<NULL>");
2f53dd65
L
298
299 p1 = s1 = s2 = 0;
326d0992 300 res = f_findfirst(&Dir, &Finfo, path, pattern); /* Start to search for files */
3d8c1907 301 while (res == FR_OK && Finfo.fname[0]) {
2f53dd65
L
302 if (Finfo.fattrib & AM_DIR) {
303 s2++;
304 } else {
305 s1++; p1 += Finfo.fsize;
306 }
3d8c1907 307 print_dirent(&Finfo);
b15d22a4
L
308 if (check_abort())
309 break;
3d8c1907 310 res = f_findnext(&Dir, &Finfo);
2f53dd65 311 }
3d8c1907
L
312 f_closedir(&Dir);
313 free(pattern);
2f53dd65
L
314
315 if (res == FR_OK) {
316 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
3d8c1907 317 if (f_getfree(path, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
318 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
319 }
320
3d8c1907
L
321 if (res) {
322 put_rc(res);
323 return CMD_RET_FAILURE;
324 }
325
326 return CMD_RET_SUCCESS;
327}
328
df607c3a
L
329/*
330 * tst path - for debugging: test access with different functions
331 *
332 */
333command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
3d8c1907
L
334{
335 DIR Dir; /* Directory object */
336 FILINFO Finfo;
337 FRESULT res = FR_OK;
338 char *path;
339
3d8c1907
L
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 }
b6c04275 353 free(buf);
2f53dd65
L
354 if (res) {
355 put_rc(res);
356 return CMD_RET_FAILURE;
357 }
358
3d8c1907
L
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
2f53dd65
L
385 return CMD_RET_SUCCESS;
386}
387
df607c3a 388#if 0
35edb766
L
389static
390FRESULT mkpath(TCHAR *path)
391{
392 /* TODO: */
393 (void) path;
35edb766
L
394 FILINFO fd
395 TCHAR *p, *q;
396 FRESULT ret;
397
35edb766
L
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 }
35edb766
L
412
413 return FR_OK;
414}
df607c3a
L
415#endif
416
417/******************************************************************************/
418
326d0992
L
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
df607c3a
L
429
430typedef 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
326d0992 435static void strip_trailing_slash(PATH_T *p)
df607c3a
L
436{
437 while (p->p_end > p->p_path && p->p_end[-1] == '/')
326d0992 438 *--p->p_end = '\0';
df607c3a
L
439}
440
441/*
442 * Move specified string into path. Convert "" to "." to handle BSD
443 * semantics for a null path. Strip trailing slashes.
444 */
445int
446path_set(PATH_T *p, char *string)
447{
448 if (strlen(string) > MAX_PATHLEN) {
326d0992
L
449 err(PSTR("%s: name too long"), string);
450 return 0;
df607c3a
L
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);
326d0992 462 return 1;
df607c3a
L
463}
464
326d0992
L
465/*
466 * Append specified string to path, inserting '/' if necessary. Return a
467 * pointer to the old end of path for restoration.
468 */
469char *
470path_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 */
504void
505path_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 */
516char *
517path_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
527uint8_t flags = 0;
528PATH_T *from;
529PATH_T *to;
530
df607c3a
L
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
326d0992
L
538static void
539setfile(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
554void 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
563debug("==== copy_file(): dne: %u\n", dne);
564debug("==== 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
622static void copy_dir(void)
623{
624 printf(PSTR("directory copy not supported, ommitting dir '%s'\n"),
625 from->p_path);
626}
627#else
628static void copy_dir(void)
629{
630 FILINFO from_stat;
631 struct dirent *dp, **dir_list;
632 int dir_cnt, i;
633 char *old_from, *old_to;
634
635 dir_cnt = scandir(from->p_path, &dir_list, NULL, NULL);
636 if (dir_cnt == -1) {
637 (void)fprintf(stderr, "%s: can't read directory %s.\n",
638 progname, from->p_path);
639 command_ret = CMD_RET_FAILURE;
640 }
641
642 /*
643 * Instead of handling directory entries in the order they appear
644 * on disk, do non-directory files before directory files.
645 * There are two reasons to do directories last. The first is
646 * efficiency. Files tend to be in the same cylinder group as
647 * their parent, whereas directories tend not to be. Copying files
648 * all at once reduces seeking. Second, deeply nested tree's
649 * could use up all the file descriptors if we didn't close one
650 * directory before recursivly starting on the next.
651 */
652 /* copy files */
653 for (i = 0; i < dir_cnt; ++i) {
654 dp = dir_list[i];
655 if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
656 && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
657 goto done;
658 if (!(old_from =
659 path_append(&from, dp->d_name, (int)dp->d_namlen)))
660 goto done;
661
662 if (statfcn(from->p_path, &from_stat) < 0) {
663 err(PSTR("%s: %s"), dp->d_name, strerror(errno));
664 path_restore(&from, old_from);
665 goto done;
666 }
667 if (S_ISDIR(from_stat.st_mode)) {
668 path_restore(&from, old_from);
669 continue;
670 }
671 if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
672 copy();
673 path_restore(&to, old_to);
674 }
675 path_restore(&from, old_from);
676done: dir_list[i] = NULL;
677 free(dp);
678 }
679
680 /* copy directories */
681 for (i = 0; i < dir_cnt; ++i) {
682 dp = dir_list[i];
683 if (!dp)
684 continue;
685 if (!(old_from =
686 path_append(&from, dp->d_name, (int)dp->d_namlen))) {
687 free(dp);
688 continue;
689 }
690 if (!(old_to =
691 path_append(&to, dp->d_name, (int)dp->d_namlen))) {
692 free(dp);
693 path_restore(&from, old_from);
694 continue;
695 }
696 copy();
697 free(dp);
698 path_restore(&from, old_from);
699 path_restore(&to, old_to);
700 }
701 free(dir_list);
702}
703#endif
704
705/*
706 * copy file or directory at "from" to "to".
707 */
708static void copy()
709{
710 FILINFO from_stat, to_stat;
711 uint_fast8_t dne;
712 FRESULT fr;
713
714debug("==== copy()\n");
715debug("==== from:'%s' to:'%s'\n", from->p_path, to->p_path);
716
717 fr = f_stat(from->p_path, &from_stat);
718 if (fr) {
719 err(PSTR("%s: %S"), from->p_path, rctostr(fr));
720 return;
721 }
722
723 /* not an error, but need to remember it happened */
724 if (f_stat(to->p_path, &to_stat) != FR_OK)
725 dne = 1;
726 else {
727 if (strcmp(to->p_path, from->p_path) !=0) {
728 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
729 to->p_path, from->p_path);
730 command_ret = CMD_RET_FAILURE;
731 return;
732 }
733 dne = 0;
734 }
735
736 if(from_stat.fattrib & AM_DIR) {
737 if (!(flags & R_FLAG)) {
738 (void)printf(PSTR("-r not specified; ommitting dir '%s'\n"),
739 from->p_path);
740 command_ret = CMD_RET_FAILURE;
741 return;
742 }
743 if (dne) {
744 /*
745 * If the directory doesn't exist, create the new one.
746 */
747 if ((fr = f_mkdir(to->p_path)) != FR_OK) {
748 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
749 return;
750 }
751 }
752 else if (!(to_stat.fattrib & AM_DIR)) {
753 (void)printf_P(PSTR("%s: not a directory.\n"), to->p_path);
754 return;
755 }
756 copy_dir();
757 /*
758 * If not -p and directory didn't exist, set it to be the
759 * same as the from directory, umodified by the umask;
760 * arguably wrong, but it's been that way forever.
761 */
762#if 0
763 if (flags & P_FLAG)
764 setfile(&from_stat, 0);
765 else if (dne)
766 (void)chmod(to->p_path, from_stat.st_mode);
767#endif
768 return;
769 }
770 copy_file(&from_stat, dne);
771}
772
df607c3a
L
773command_ret_t do_cp(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
774{
775
326d0992
L
776 FRESULT fr; /* Return value */
777 //DIR dj; /* Directory search object */
778 FILINFO to_stat; /* File information */
779 char *old_to;
df607c3a 780
df607c3a 781
326d0992 782 command_ret = CMD_RET_SUCCESS;
df607c3a
L
783
784 /* reset getopt() */
785 optind = 0;
786
787 int opt;
788 while ((opt = getopt(argc, argv, PSTR("Rrfip"))) != -1) {
789 switch (opt) {
790 case 'f':
791 flags &= I_FLAG;
792 break;
793 case 'i':
794 flags |= I_FLAG;
795 flags &= F_FLAG;
796 break;
797 case 'p':
798 flags |= P_FLAG;
799 break;
800 case 'R':
801 case 'r':
802 flags |= R_FLAG;
803 break;
804 case 'v':
805 flags |= V_FLAG;
806 break;
807 default:
808 return CMD_RET_USAGE;
809 break;
810 }
811 }
812 argc -= optind;
813 argv += optind;
814
815 if (argc < 2)
816 return CMD_RET_USAGE;
817
326d0992
L
818 from = (PATH_T *) malloc(sizeof(PATH_T));
819 to = (PATH_T *) malloc(sizeof(PATH_T));
820 if (from == NULL || to == NULL) {
821 printf_P(PSTR("cp: Out of Memory!\n"));
822 command_ret = CMD_RET_FAILURE;
823 goto cleanup;
824 }
825 from->p_end = from->p_path; *from->p_path = '\0';
826 to->p_end = to->p_path; *to->p_path = '\0';
827
df607c3a 828 /* consume last argument first. */
326d0992
L
829 if (!path_set(to, argv[--argc])) {
830 command_ret = CMD_RET_FAILURE;
831 goto cleanup;
832 }
df607c3a 833
326d0992
L
834 /*
835 * Cp has two distinct cases:
836 *
837 * % cp [-rip] source target
838 * % cp [-rip] source1 ... directory
839 *
840 * In both cases, source can be either a file or a directory.
841 *
842 * In (1), the target becomes a copy of the source. That is, if the
843 * source is a file, the target will be a file, and likewise for
844 * directories.
845 *
846 * In (2), the real target is not directory, but "directory/source".
847 */
848
849 fr = f_stat(to->p_path, &to_stat);
850 if (fr != FR_OK && fr != FR_NO_FILE) {
851 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
852 command_ret = CMD_RET_FAILURE;
853 goto cleanup;
854 }
855 if (fr != FR_OK || !(to_stat.fattrib & AM_DIR)) {
856 /*
857 * Case (1). Target is not a directory.
858 */
859 if (argc > 1) {
860 command_ret = CMD_RET_USAGE;
861 goto cleanup;
862 }
863 if (!path_set(from, *argv))
864 command_ret = CMD_RET_FAILURE;
865 goto cleanup;
866 copy();
867 }
868 else {
869 /*
870 * Case (2). Target is a directory.
871 */
872 for (;; ++argv) {
873 if (!path_set(from, *argv))
874 continue;
875 if (!(old_to = path_append(to, path_basename(from), -1)))
876 continue;
877 copy();
878 if (!--argc)
879 break;
880 path_restore(&to, old_to);
881 }
882 }
df607c3a 883
326d0992
L
884cleanup:
885 free(to);
886 free(from);
887
888 return command_ret;
889}
890
891#if 0
df607c3a 892 if (flags & V_FLAG)
326d0992 893 printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to->p_path)));
df607c3a
L
894
895#endif
896
df607c3a
L
897
898
df607c3a 899
df607c3a 900
df607c3a
L
901
902/******************************************************************************/
903
326d0992
L
904/*
905 * Work register for stat command
906 */
df607c3a
L
907struct stat_dat_s {
908 DWORD AccSize;
909 WORD AccFiles, AccDirs;
910 FILINFO Finfo;
911};
912
913static
914FRESULT scan_files (
915 char *path, /* Pointer to the working buffer with start path */
916 struct stat_dat_s *statp
917)
918{
919 DIR dirs;
920 FRESULT res;
921 int i;
922 char *fn;
923
924 res = f_opendir(&dirs, path);
925 swirl();
926 if (res == FR_OK) {
927 i = strlen(path);
928 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
929 statp->Finfo.fname[0]) {
930 if (_FS_RPATH && statp->Finfo.fname[0] == '.')
931 continue;
932 fn = statp->Finfo.fname;
933 if (statp->Finfo.fattrib & AM_DIR) {
934 statp->AccDirs++;
935 path[i] = '/';
936 strcpy(path+i+1, fn);
937 res = scan_files(path, statp);
938 path[i] = '\0';
939 if (res != FR_OK)
940 break;
941 } else {
942 //printf_P(PSTR("%s/%s\n"), path, fn);
943 statp->AccFiles++;
944 statp->AccSize += statp->Finfo.fsize;
945 }
946 if (check_abort()) {
947 res = 255;
948 break;
949 }
950 }
951 }
952
953 return res;
954}
955
956
957/*
958 * fatstat path - Show logical drive status
959 *
960 */
961command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
962{
963 FATFS *fs;
964 DWORD nfreeclst;
965 FRESULT res;
966 char *buf;
967 char *path = "";
968 struct stat_dat_s statp;
969
970 buf = (char *) malloc(BUFFER_SIZE);
971 if (buf == NULL) {
972 printf_P(PSTR("fat stat: Out of Memory!\n"));
973 return CMD_RET_FAILURE;
974 }
975
976 if (argc > 1)
977 path = argv[1];
978 res = f_getfree(path, &nfreeclst, &fs);
979 if (!res) {
980 printf_P(PSTR(
981 "FAT type: %u\n"
982 "Bytes/Cluster: %lu\n"
983 "Number of FATs: %u\n"
984 "Root DIR entries: %u\n"
985 "Sectors/FAT: %lu\n"
986 "Number of clusters: %lu\n"
987 "FAT start (lba): %lu\n"
988 "DIR start (lba,cluster): %lu\n"
989 "Data start (lba): %lu\n"),
990 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
991 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
992 fs->fatbase, fs->dirbase, fs->database);
993
994#if _USE_LABEL
995 DWORD serial;
996 res = f_getlabel(path, buf, &serial);
997 if (!res) {
998 printf_P(PSTR(
999 "Volume name: %s\n"
1000 "Volume S/N: %04X-%04X\n"),
1001 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
1002 }
1003#endif
1004 if (!res) {
1005 my_puts_P(PSTR("\nCounting... "));
1006 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
1007 strcpy(buf, path);
1008
1009 res = scan_files(buf, &statp);
1010 }
1011 if (!res) {
1012 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1013 "%lu KB total disk space.\n%lu KB available.\n"),
1014 statp.AccFiles, statp.AccSize, statp.AccDirs,
1015 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
1016 );
1017 }
1018 }
1019
1020 free(buf);
1021 if (res) {
1022 put_rc(res);
1023 return CMD_RET_FAILURE;
1024 }
1025 return CMD_RET_SUCCESS;
1026}
2f53dd65 1027
4565be9a
L
1028/*
1029 * fatread/write - load binary file to/from a dos filesystem
1030 * read <d:/path/filename> <addr> [bytes [pos]]
1031 * write <d:/path/filename> <addr> <bytes>
1032 */
df607c3a 1033command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 1034{
4565be9a
L
1035 FIL File;
1036 uint32_t addr;
1037 unsigned long bytes;
1038 unsigned long pos;
1039 unsigned long bytes_rw;
1040
3d8c1907 1041 bool dowrite = (argv[0][0] == 'w');
3b841cea 1042 FRESULT res = FR_OK;
4565be9a
L
1043 bool buserr = 0;
1044 uint32_t timer;
19b9a7d8 1045 uint8_t *buffer;
4565be9a 1046
4565be9a
L
1047 if (argc < (dowrite ? 4 : 3))
1048 return CMD_RET_USAGE;
1049
2d914b45 1050 addr = eval_arg(argv[2], NULL);
4565be9a
L
1051 if (addr >= MAX_MEMORY) {
1052 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
1053 return CMD_RET_FAILURE;
1054 }
1055 if (argc > 3)
2d914b45 1056 bytes = eval_arg(argv[3], NULL);
4565be9a
L
1057 else
1058 bytes = MAX_MEMORY;
1059 if (argc > 4)
2d914b45 1060 pos = eval_arg(argv[4], NULL);
4565be9a
L
1061 else
1062 pos = 0;
1063
1064 if (addr + bytes > MAX_MEMORY)
1065 bytes = MAX_MEMORY - addr;
1066
19b9a7d8 1067 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 1068 if (buffer == NULL) {
19b9a7d8 1069 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
1070 free(buffer);
1071 return CMD_RET_FAILURE;
1072 }
1073
4565be9a
L
1074 if (!res) {
1075 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
1076 : FA_READ );
1077
1078 if (!res) {
1079 res = f_lseek(&File, pos);
1080 if (!res) {
1081 bytes_rw = 0;
1082 timer = get_timer(0);
1083 while (bytes) {
1084 unsigned int cnt, br;
1085
19b9a7d8
L
1086 if (bytes >= BUFFER_SIZE) {
1087 cnt = BUFFER_SIZE;
1088 bytes -= BUFFER_SIZE;
4565be9a
L
1089 } else {
1090 cnt = bytes; bytes = 0;
1091 }
1092 if (dowrite) {
1093 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1094 buserr = 1;
1095 break;
1096 }
1097 z80_read_block(buffer, addr, cnt);
1098 z80_bus_cmd(Release);
1099 res = f_write(&File, buffer, cnt, &br);
1100 if (res != FR_OK)
1101 break;
1102 } else {
1103 res = f_read(&File, buffer, cnt, &br);
1104 if (res != FR_OK)
1105 break;
1106 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1107 buserr = 1;
1108 break;
1109 }
1110 z80_write_block(buffer, addr, br);
1111 z80_bus_cmd(Release);
1112 }
1113 addr += br;
1114
1115 bytes_rw += br;
1116 if (cnt != br) {
1117 if (dowrite)
1118 printf_P(PSTR("Disk full?\n"));
1119 break;
1120 }
b15d22a4 1121 if (check_abort())
4565be9a 1122 break;
4565be9a
L
1123 }
1124
1125 FRESULT fr = f_close(&File);
1126 if (!res)
1127 res = fr;
1128 timer = get_timer(timer);
1129 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1130 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
1131 }
1132 }
4565be9a
L
1133 }
1134
19b9a7d8 1135 free(buffer);
19b9a7d8 1136
4565be9a
L
1137 if (buserr)
1138 my_puts_P(PSTR("Bus timeout\n"));
1139 if (res)
1140 put_rc(res);
1141 if (buserr || res)
1142 return CMD_RET_FAILURE;
1143
1144 return CMD_RET_SUCCESS;
1145}
b6c04275 1146
3d8c1907 1147/*
df607c3a 1148 * command table for fat subcommands
3d8c1907 1149 */
b6c04275 1150
6c851813 1151cmd_tbl_t cmd_tbl_fat[] = {
b6c04275 1152CMD_TBL_ITEM(
82c475ad 1153 stat, 2, CTBL_RPT, do_stat,
b6c04275
L
1154 "Show logical drive status",
1155 "dev"
1156),
1157CMD_TBL_ITEM(
82c475ad 1158 pwd, 2, CTBL_RPT, do_pwd,
b6c04275
L
1159 "Print name of current/working directory",
1160 ""
1161),
1162CMD_TBL_ITEM(
82c475ad 1163 cd, 2, 0, do_cd,
b6c04275
L
1164 "Change the current/working directory.",
1165 "path"
1166),
1167CMD_TBL_ITEM(
82c475ad 1168 ls, 2, CTBL_RPT, do_ls,
b6c04275
L
1169 "Directory listing",
1170 "path"
1171),
3d8c1907 1172CMD_TBL_ITEM(
82c475ad 1173 tst, 2, CTBL_DBG|CTBL_RPT, do_tst,
3d8c1907
L
1174 "FatFS test function",
1175 "path"
1176),
b6c04275 1177CMD_TBL_ITEM(
82c475ad 1178 load, 5, 0, do_rw,
b6c04275
L
1179 "load binary file from a dos filesystem",
1180 "<d:/path/filename> <addr> [bytes [pos]]\n"
1181 " - Load binary file 'path/filename' on logical drive 'd'\n"
1182 " to address 'addr' from dos filesystem.\n"
1183 " 'pos' gives the file position to start loading from.\n"
1184 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1185 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1186 " the load stops on end of file."
1187),
1188CMD_TBL_ITEM(
82c475ad 1189 write, 4, 0, do_rw,
b6c04275
L
1190 "write file into a dos filesystem",
1191 "<d:/path/filename> <addr> <bytes>\n"
1192 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1193 " starting at address 'addr'.\n"
1194),
1195
3d8c1907 1196CMD_TBL_ITEM(
82c475ad 1197 cp, CONFIG_SYS_MAXARGS, CTBL_DBG, do_cp,
3d8c1907 1198 "copy files",
82c475ad 1199 "[-R] [-f | -i | -n] [-aprv] source_file target_file\n"
3d8c1907
L
1200 " - \n"
1201),
1202
ee5c86e9 1203CMD_TBL_ITEM(
82c475ad 1204 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
ee5c86e9
L
1205 "Print sub command description/usage",
1206 "\n"
1207 " - print brief description of all sub commands\n"
1208 "fat help command ...\n"
1209 " - print detailed usage of sub cmd 'command'"
1210),
1211
b6c04275
L
1212/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1213 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
1214 FSTR("Alias for 'help'"),
1215#ifdef CONFIG_SYS_LONGHELP
1216 FSTR(""),
1217#endif /* CONFIG_SYS_LONGHELP */
6c851813 1218 NULL,
b6c04275 1219#ifdef CONFIG_AUTO_COMPLETE
6c851813 1220 NULL,
b6c04275
L
1221#endif
1222},
6c851813 1223/* Mark end of table */
6c7c9c2d 1224CMD_TBL_END(cmd_tbl_fat)
b6c04275
L
1225};
1226
b6c04275 1227
df607c3a 1228command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275 1229{
6c7c9c2d 1230 puts_P(PSTR("Huch?"));
b6c04275
L
1231
1232 return CMD_RET_USAGE;
1233}