]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
fat cp: flags
[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
1cdd02d8 525static uint8_t flags;
326d0992
L
526PATH_T *from;
527PATH_T *to;
528
1cdd02d8
L
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
df607c3a 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) {
326d0992 579 printf_P(PSTR("overwrite %s? "), to->p_path);
1cdd02d8 580 if (!confirm_yes()) {
326d0992
L
581 f_close(&from_fd);
582 return;
583 }
584 }
585 fr = f_open(&to_fd, to->p_path, FA_WRITE|FA_CREATE_ALWAYS);
586 } else
587 fr = f_open(&to_fd, to->p_path, FA_WRITE|FA_CREATE_NEW);
588
589 if (fr != FR_OK) {
590 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
591 f_close(&from_fd);
592 return;
593 }
594
595 while ((fr = f_read(&from_fd, buf, MAXBSIZE, &rcount)) == FR_OK &&
596 rcount > 0) {
597 fr = f_write(&to_fd, buf, rcount, &wcount);
598 if (fr || wcount < rcount) {
599 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
600 break;
601 }
602 }
603 if (fr != FR_OK)
604 err(PSTR("%s: S"), from->p_path, rctostr(fr));
605
606 if (flags & P_FLAG)
607 setfile(fs, &to_fd);
608
609 f_close(&from_fd);
610 if ((fr = f_close(&to_fd)) != FR_OK)
611 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
612}
613
614#if 1
615static void copy_dir(void)
616{
73cf0c86
L
617debug("==== copy_dir()");
618debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
619
620 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
326d0992 621 from->p_path);
73cf0c86 622 command_ret = CMD_RET_FAILURE;
326d0992
L
623}
624#else
625static void copy_dir(void)
626{
627 FILINFO from_stat;
628 struct dirent *dp, **dir_list;
629 int dir_cnt, i;
630 char *old_from, *old_to;
631
73cf0c86
L
632debug("==== copy_file(): dne: %u\n", dne);
633debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
634
326d0992
L
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");
73cf0c86 715debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
326d0992
L
716
717 fr = f_stat(from->p_path, &from_stat);
73cf0c86 718 if (fr != FR_OK) {
326d0992
L
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 {
73cf0c86 727 if (strcmp(to->p_path, from->p_path) == 0) {
326d0992
L
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)) {
73cf0c86 738 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
326d0992
L
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
1cdd02d8 782 uint8_t tflags = 0;
326d0992 783 command_ret = CMD_RET_SUCCESS;
df607c3a
L
784
785 /* reset getopt() */
786 optind = 0;
787
788 int opt;
1cdd02d8 789 while ((opt = getopt(argc, argv, PSTR("rfipv"))) != -1) {
df607c3a
L
790 switch (opt) {
791 case 'f':
1cdd02d8
L
792 tflags &= ~I_FLAG;
793 tflags |= F_FLAG;
df607c3a
L
794 break;
795 case 'i':
1cdd02d8
L
796 tflags &= ~F_FLAG;
797 tflags |= I_FLAG;
df607c3a
L
798 break;
799 case 'p':
1cdd02d8 800 tflags |= P_FLAG;
df607c3a 801 break;
df607c3a 802 case 'r':
1cdd02d8 803 tflags |= R_FLAG;
df607c3a
L
804 break;
805 case 'v':
1cdd02d8 806 tflags |= V_FLAG;
df607c3a
L
807 break;
808 default:
809 return CMD_RET_USAGE;
810 break;
811 }
812 }
1cdd02d8 813 flags = tflags;
df607c3a
L
814 argc -= optind;
815 argv += optind;
816
817 if (argc < 2)
818 return CMD_RET_USAGE;
819
326d0992
L
820 from = (PATH_T *) malloc(sizeof(PATH_T));
821 to = (PATH_T *) malloc(sizeof(PATH_T));
822 if (from == NULL || to == NULL) {
823 printf_P(PSTR("cp: Out of Memory!\n"));
824 command_ret = CMD_RET_FAILURE;
825 goto cleanup;
826 }
827 from->p_end = from->p_path; *from->p_path = '\0';
828 to->p_end = to->p_path; *to->p_path = '\0';
829
df607c3a 830 /* consume last argument first. */
326d0992
L
831 if (!path_set(to, argv[--argc])) {
832 command_ret = CMD_RET_FAILURE;
833 goto cleanup;
834 }
df607c3a 835
326d0992
L
836 /*
837 * Cp has two distinct cases:
838 *
839 * % cp [-rip] source target
840 * % cp [-rip] source1 ... directory
841 *
842 * In both cases, source can be either a file or a directory.
843 *
844 * In (1), the target becomes a copy of the source. That is, if the
845 * source is a file, the target will be a file, and likewise for
846 * directories.
847 *
848 * In (2), the real target is not directory, but "directory/source".
849 */
850
851 fr = f_stat(to->p_path, &to_stat);
1cdd02d8 852debug("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr, to_stat.fattrib, flags);
73cf0c86
L
853debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
854
855 if (fr != FR_OK && fr != FR_NO_FILE && fr != FR_NO_PATH) {
856 err(PSTR("Test1: %s: %S"), to->p_path, rctostr(fr));
326d0992
L
857 command_ret = CMD_RET_FAILURE;
858 goto cleanup;
859 }
73cf0c86 860 if (!(fr == FR_OK && (to_stat.fattrib & AM_DIR))) {
326d0992
L
861 /*
862 * Case (1). Target is not a directory.
863 */
864 if (argc > 1) {
97bd904d
L
865 err(PSTR("target '%s' is not a directory"), to->p_path);
866 //command_ret = CMD_RET_USAGE;
326d0992
L
867 goto cleanup;
868 }
73cf0c86 869 if (!path_set(from, *argv)) {
326d0992
L
870 command_ret = CMD_RET_FAILURE;
871 goto cleanup;
73cf0c86 872 }
326d0992
L
873 copy();
874 }
875 else {
876 /*
877 * Case (2). Target is a directory.
878 */
879 for (;; ++argv) {
880 if (!path_set(from, *argv))
881 continue;
882 if (!(old_to = path_append(to, path_basename(from), -1)))
883 continue;
884 copy();
885 if (!--argc)
886 break;
97bd904d 887 path_restore(to, old_to);
326d0992
L
888 }
889 }
df607c3a 890
326d0992
L
891cleanup:
892 free(to);
893 free(from);
894
895 return command_ret;
896}
897
898#if 0
df607c3a 899 if (flags & V_FLAG)
326d0992 900 printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to->p_path)));
df607c3a
L
901
902#endif
903
df607c3a
L
904
905
df607c3a 906
df607c3a 907
df607c3a
L
908
909/******************************************************************************/
910
326d0992
L
911/*
912 * Work register for stat command
913 */
df607c3a
L
914struct stat_dat_s {
915 DWORD AccSize;
916 WORD AccFiles, AccDirs;
917 FILINFO Finfo;
918};
919
920static
921FRESULT scan_files (
922 char *path, /* Pointer to the working buffer with start path */
923 struct stat_dat_s *statp
924)
925{
926 DIR dirs;
927 FRESULT res;
928 int i;
929 char *fn;
930
931 res = f_opendir(&dirs, path);
932 swirl();
933 if (res == FR_OK) {
934 i = strlen(path);
935 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
936 statp->Finfo.fname[0]) {
937 if (_FS_RPATH && statp->Finfo.fname[0] == '.')
938 continue;
939 fn = statp->Finfo.fname;
940 if (statp->Finfo.fattrib & AM_DIR) {
941 statp->AccDirs++;
942 path[i] = '/';
943 strcpy(path+i+1, fn);
944 res = scan_files(path, statp);
945 path[i] = '\0';
946 if (res != FR_OK)
947 break;
948 } else {
949 //printf_P(PSTR("%s/%s\n"), path, fn);
950 statp->AccFiles++;
951 statp->AccSize += statp->Finfo.fsize;
952 }
953 if (check_abort()) {
954 res = 255;
955 break;
956 }
957 }
958 }
959
960 return res;
961}
962
963
964/*
965 * fatstat path - Show logical drive status
966 *
967 */
968command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
969{
970 FATFS *fs;
971 DWORD nfreeclst;
972 FRESULT res;
973 char *buf;
974 char *path = "";
975 struct stat_dat_s statp;
976
977 buf = (char *) malloc(BUFFER_SIZE);
978 if (buf == NULL) {
979 printf_P(PSTR("fat stat: Out of Memory!\n"));
980 return CMD_RET_FAILURE;
981 }
982
983 if (argc > 1)
984 path = argv[1];
985 res = f_getfree(path, &nfreeclst, &fs);
986 if (!res) {
987 printf_P(PSTR(
988 "FAT type: %u\n"
989 "Bytes/Cluster: %lu\n"
990 "Number of FATs: %u\n"
991 "Root DIR entries: %u\n"
992 "Sectors/FAT: %lu\n"
993 "Number of clusters: %lu\n"
994 "FAT start (lba): %lu\n"
995 "DIR start (lba,cluster): %lu\n"
996 "Data start (lba): %lu\n"),
997 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
998 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
999 fs->fatbase, fs->dirbase, fs->database);
1000
1001#if _USE_LABEL
1002 DWORD serial;
1003 res = f_getlabel(path, buf, &serial);
1004 if (!res) {
1005 printf_P(PSTR(
1006 "Volume name: %s\n"
1007 "Volume S/N: %04X-%04X\n"),
1008 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
1009 }
1010#endif
1011 if (!res) {
1012 my_puts_P(PSTR("\nCounting... "));
1013 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
1014 strcpy(buf, path);
1015
1016 res = scan_files(buf, &statp);
1017 }
1018 if (!res) {
1019 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1020 "%lu KB total disk space.\n%lu KB available.\n"),
1021 statp.AccFiles, statp.AccSize, statp.AccDirs,
1022 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
1023 );
1024 }
1025 }
1026
1027 free(buf);
1028 if (res) {
1029 put_rc(res);
1030 return CMD_RET_FAILURE;
1031 }
1032 return CMD_RET_SUCCESS;
1033}
2f53dd65 1034
4565be9a
L
1035/*
1036 * fatread/write - load binary file to/from a dos filesystem
1037 * read <d:/path/filename> <addr> [bytes [pos]]
1038 * write <d:/path/filename> <addr> <bytes>
1039 */
df607c3a 1040command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 1041{
4565be9a
L
1042 FIL File;
1043 uint32_t addr;
1044 unsigned long bytes;
1045 unsigned long pos;
1046 unsigned long bytes_rw;
1047
3d8c1907 1048 bool dowrite = (argv[0][0] == 'w');
3b841cea 1049 FRESULT res = FR_OK;
4565be9a
L
1050 bool buserr = 0;
1051 uint32_t timer;
19b9a7d8 1052 uint8_t *buffer;
4565be9a 1053
4565be9a
L
1054 if (argc < (dowrite ? 4 : 3))
1055 return CMD_RET_USAGE;
1056
2d914b45 1057 addr = eval_arg(argv[2], NULL);
4565be9a
L
1058 if (addr >= MAX_MEMORY) {
1059 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
1060 return CMD_RET_FAILURE;
1061 }
1062 if (argc > 3)
2d914b45 1063 bytes = eval_arg(argv[3], NULL);
4565be9a
L
1064 else
1065 bytes = MAX_MEMORY;
1066 if (argc > 4)
2d914b45 1067 pos = eval_arg(argv[4], NULL);
4565be9a
L
1068 else
1069 pos = 0;
1070
1071 if (addr + bytes > MAX_MEMORY)
1072 bytes = MAX_MEMORY - addr;
1073
19b9a7d8 1074 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 1075 if (buffer == NULL) {
19b9a7d8 1076 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
1077 free(buffer);
1078 return CMD_RET_FAILURE;
1079 }
1080
4565be9a
L
1081 if (!res) {
1082 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
1083 : FA_READ );
1084
1085 if (!res) {
1086 res = f_lseek(&File, pos);
1087 if (!res) {
1088 bytes_rw = 0;
1089 timer = get_timer(0);
1090 while (bytes) {
1091 unsigned int cnt, br;
1092
19b9a7d8
L
1093 if (bytes >= BUFFER_SIZE) {
1094 cnt = BUFFER_SIZE;
1095 bytes -= BUFFER_SIZE;
4565be9a
L
1096 } else {
1097 cnt = bytes; bytes = 0;
1098 }
1099 if (dowrite) {
1100 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1101 buserr = 1;
1102 break;
1103 }
1104 z80_read_block(buffer, addr, cnt);
1105 z80_bus_cmd(Release);
1106 res = f_write(&File, buffer, cnt, &br);
1107 if (res != FR_OK)
1108 break;
1109 } else {
1110 res = f_read(&File, buffer, cnt, &br);
1111 if (res != FR_OK)
1112 break;
1113 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1114 buserr = 1;
1115 break;
1116 }
1117 z80_write_block(buffer, addr, br);
1118 z80_bus_cmd(Release);
1119 }
1120 addr += br;
1121
1122 bytes_rw += br;
1123 if (cnt != br) {
1124 if (dowrite)
1125 printf_P(PSTR("Disk full?\n"));
1126 break;
1127 }
b15d22a4 1128 if (check_abort())
4565be9a 1129 break;
4565be9a
L
1130 }
1131
1132 FRESULT fr = f_close(&File);
1133 if (!res)
1134 res = fr;
1135 timer = get_timer(timer);
1136 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1137 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
1138 }
1139 }
4565be9a
L
1140 }
1141
19b9a7d8 1142 free(buffer);
19b9a7d8 1143
4565be9a
L
1144 if (buserr)
1145 my_puts_P(PSTR("Bus timeout\n"));
1146 if (res)
1147 put_rc(res);
1148 if (buserr || res)
1149 return CMD_RET_FAILURE;
1150
1151 return CMD_RET_SUCCESS;
1152}
b6c04275 1153
3d8c1907 1154/*
df607c3a 1155 * command table for fat subcommands
3d8c1907 1156 */
b6c04275 1157
6c851813 1158cmd_tbl_t cmd_tbl_fat[] = {
b6c04275 1159CMD_TBL_ITEM(
82c475ad 1160 stat, 2, CTBL_RPT, do_stat,
b6c04275
L
1161 "Show logical drive status",
1162 "dev"
1163),
1164CMD_TBL_ITEM(
82c475ad 1165 pwd, 2, CTBL_RPT, do_pwd,
b6c04275
L
1166 "Print name of current/working directory",
1167 ""
1168),
1169CMD_TBL_ITEM(
82c475ad 1170 cd, 2, 0, do_cd,
b6c04275
L
1171 "Change the current/working directory.",
1172 "path"
1173),
1174CMD_TBL_ITEM(
82c475ad 1175 ls, 2, CTBL_RPT, do_ls,
b6c04275
L
1176 "Directory listing",
1177 "path"
1178),
3d8c1907 1179CMD_TBL_ITEM(
82c475ad 1180 tst, 2, CTBL_DBG|CTBL_RPT, do_tst,
3d8c1907
L
1181 "FatFS test function",
1182 "path"
1183),
b6c04275 1184CMD_TBL_ITEM(
82c475ad 1185 load, 5, 0, do_rw,
b6c04275
L
1186 "load binary file from a dos filesystem",
1187 "<d:/path/filename> <addr> [bytes [pos]]\n"
1188 " - Load binary file 'path/filename' on logical drive 'd'\n"
1189 " to address 'addr' from dos filesystem.\n"
1190 " 'pos' gives the file position to start loading from.\n"
1191 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1192 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1193 " the load stops on end of file."
1194),
1195CMD_TBL_ITEM(
82c475ad 1196 write, 4, 0, do_rw,
b6c04275
L
1197 "write file into a dos filesystem",
1198 "<d:/path/filename> <addr> <bytes>\n"
1199 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1200 " starting at address 'addr'.\n"
1201),
1202
3d8c1907 1203CMD_TBL_ITEM(
82c475ad 1204 cp, CONFIG_SYS_MAXARGS, CTBL_DBG, do_cp,
3d8c1907 1205 "copy files",
1cdd02d8
L
1206 "[-f | -i | -n] [-prv] source_file target_file\n"
1207 "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1208 " -f overwrite existing file ignoring write protection\n"
1209 " this option is ignored when the -n option is also used\n"
1210 " -i prompt before overwrite (overrides a previous -n option)\n"
1211 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1212 " -p preserve attributes and timestamps\n"
1213 " -r copy directories recursively\n"
1214 " -v explain what is being done\n"
3d8c1907
L
1215),
1216
ee5c86e9 1217CMD_TBL_ITEM(
82c475ad 1218 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
ee5c86e9
L
1219 "Print sub command description/usage",
1220 "\n"
1221 " - print brief description of all sub commands\n"
1222 "fat help command ...\n"
1223 " - print detailed usage of sub cmd 'command'"
1224),
1225
b6c04275
L
1226/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1227 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
1228 FSTR("Alias for 'help'"),
1229#ifdef CONFIG_SYS_LONGHELP
1230 FSTR(""),
1231#endif /* CONFIG_SYS_LONGHELP */
6c851813 1232 NULL,
b6c04275 1233#ifdef CONFIG_AUTO_COMPLETE
6c851813 1234 NULL,
b6c04275
L
1235#endif
1236},
6c851813 1237/* Mark end of table */
6c7c9c2d 1238CMD_TBL_END(cmd_tbl_fat)
b6c04275
L
1239};
1240
b6c04275 1241
df607c3a 1242command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275 1243{
6c7c9c2d 1244 puts_P(PSTR("Huch?"));
b6c04275
L
1245
1246 return CMD_RET_USAGE;
1247}