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