]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
fat cp: more on 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
31f423d2 529#define F_FLAG (1<<3) // overwrite existing file ignoring write protection
1cdd02d8
L
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)
1cdd02d8 532#define P_FLAG (1<<4) // preserve attributes and timestamps
31f423d2 533#define R_FLAG (1<<0) // copy directories recursively
1cdd02d8 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{
326d0992
L
554 FIL from_fd, to_fd;
555 UINT rcount, wcount;
556 //FILINFO to_stat;
557 //char *p;
558 FRESULT fr;
559
16fb450c
L
560 int buf_size = get_freemem() / 512 * 512;
561 uint8_t * buf = (uint8_t *) malloc(buf_size);
562 if (buf == NULL) {
563 free(buf);
564 err(PSTR("cp: Out of Memory!\n"));
565 return;
566 }
567
568debug("==== copy_file(): dne: %u, buf_zize: %d\n", dne, buf_size);
73cf0c86 569debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
326d0992 570
16fb450c 571
326d0992
L
572 if ((fr = f_open(&from_fd, from->p_path, FA_READ)) != FR_OK) {
573 err(PSTR("%s: %S"), from->p_path, rctostr(fr));
574 return;
575 }
576
577 /*
578 * If the file exists and we're interactive, verify with the user.
579 * If the file DNE, set the mode to be the from file, minus setuid
580 * bits, modified by the umask; arguably wrong, but it makes copying
581 * executables work right and it's been that way forever. (The
582 * other choice is 666 or'ed with the execute bits on the from file
583 * modified by the umask.)
584 */
585 if (!dne) {
586 if (flags & I_FLAG) {
16fb450c 587 printf_P(PSTR("overwrite '%s'? "), to->p_path);
1cdd02d8 588 if (!confirm_yes()) {
326d0992
L
589 f_close(&from_fd);
590 return;
591 }
592 }
593 fr = f_open(&to_fd, to->p_path, FA_WRITE|FA_CREATE_ALWAYS);
594 } else
595 fr = f_open(&to_fd, to->p_path, FA_WRITE|FA_CREATE_NEW);
596
597 if (fr != FR_OK) {
598 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
599 f_close(&from_fd);
600 return;
601 }
602
16fb450c 603 while ((fr = f_read(&from_fd, buf, buf_size, &rcount)) == FR_OK &&
326d0992
L
604 rcount > 0) {
605 fr = f_write(&to_fd, buf, rcount, &wcount);
606 if (fr || wcount < rcount) {
607 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
608 break;
609 }
610 }
611 if (fr != FR_OK)
612 err(PSTR("%s: S"), from->p_path, rctostr(fr));
613
614 if (flags & P_FLAG)
615 setfile(fs, &to_fd);
616
617 f_close(&from_fd);
618 if ((fr = f_close(&to_fd)) != FR_OK)
619 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
620}
621
622#if 1
623static void copy_dir(void)
624{
73cf0c86
L
625debug("==== copy_dir()");
626debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
627
628 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
326d0992 629 from->p_path);
73cf0c86 630 command_ret = CMD_RET_FAILURE;
326d0992
L
631}
632#else
633static void copy_dir(void)
634{
635 FILINFO from_stat;
636 struct dirent *dp, **dir_list;
637 int dir_cnt, i;
638 char *old_from, *old_to;
639
73cf0c86
L
640debug("==== copy_file(): dne: %u\n", dne);
641debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
642
326d0992
L
643 dir_cnt = scandir(from->p_path, &dir_list, NULL, NULL);
644 if (dir_cnt == -1) {
645 (void)fprintf(stderr, "%s: can't read directory %s.\n",
646 progname, from->p_path);
647 command_ret = CMD_RET_FAILURE;
648 }
649
650 /*
651 * Instead of handling directory entries in the order they appear
652 * on disk, do non-directory files before directory files.
653 * There are two reasons to do directories last. The first is
654 * efficiency. Files tend to be in the same cylinder group as
655 * their parent, whereas directories tend not to be. Copying files
656 * all at once reduces seeking. Second, deeply nested tree's
657 * could use up all the file descriptors if we didn't close one
658 * directory before recursivly starting on the next.
659 */
660 /* copy files */
661 for (i = 0; i < dir_cnt; ++i) {
662 dp = dir_list[i];
663 if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
664 && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
665 goto done;
666 if (!(old_from =
667 path_append(&from, dp->d_name, (int)dp->d_namlen)))
668 goto done;
669
670 if (statfcn(from->p_path, &from_stat) < 0) {
671 err(PSTR("%s: %s"), dp->d_name, strerror(errno));
672 path_restore(&from, old_from);
673 goto done;
674 }
675 if (S_ISDIR(from_stat.st_mode)) {
676 path_restore(&from, old_from);
677 continue;
678 }
679 if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
680 copy();
681 path_restore(&to, old_to);
682 }
683 path_restore(&from, old_from);
684done: dir_list[i] = NULL;
685 free(dp);
686 }
687
688 /* copy directories */
689 for (i = 0; i < dir_cnt; ++i) {
690 dp = dir_list[i];
691 if (!dp)
692 continue;
693 if (!(old_from =
694 path_append(&from, dp->d_name, (int)dp->d_namlen))) {
695 free(dp);
696 continue;
697 }
698 if (!(old_to =
699 path_append(&to, dp->d_name, (int)dp->d_namlen))) {
700 free(dp);
701 path_restore(&from, old_from);
702 continue;
703 }
704 copy();
705 free(dp);
706 path_restore(&from, old_from);
707 path_restore(&to, old_to);
708 }
709 free(dir_list);
710}
711#endif
712
713/*
714 * copy file or directory at "from" to "to".
715 */
716static void copy()
717{
718 FILINFO from_stat, to_stat;
719 uint_fast8_t dne;
720 FRESULT fr;
721
722debug("==== copy()\n");
73cf0c86 723debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
326d0992
L
724
725 fr = f_stat(from->p_path, &from_stat);
73cf0c86 726 if (fr != FR_OK) {
326d0992
L
727 err(PSTR("%s: %S"), from->p_path, rctostr(fr));
728 return;
729 }
730
731 /* not an error, but need to remember it happened */
732 if (f_stat(to->p_path, &to_stat) != FR_OK)
733 dne = 1;
734 else {
73cf0c86 735 if (strcmp(to->p_path, from->p_path) == 0) {
326d0992
L
736 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
737 to->p_path, from->p_path);
738 command_ret = CMD_RET_FAILURE;
739 return;
740 }
741 dne = 0;
742 }
743
744 if(from_stat.fattrib & AM_DIR) {
745 if (!(flags & R_FLAG)) {
73cf0c86 746 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
326d0992
L
747 from->p_path);
748 command_ret = CMD_RET_FAILURE;
749 return;
750 }
751 if (dne) {
752 /*
753 * If the directory doesn't exist, create the new one.
754 */
755 if ((fr = f_mkdir(to->p_path)) != FR_OK) {
756 err(PSTR("%s: %S"), to->p_path, rctostr(fr));
757 return;
758 }
759 }
760 else if (!(to_stat.fattrib & AM_DIR)) {
761 (void)printf_P(PSTR("%s: not a directory.\n"), to->p_path);
762 return;
763 }
764 copy_dir();
326d0992
L
765 if (flags & P_FLAG)
766 setfile(&from_stat, 0);
31f423d2 767
326d0992
L
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;
31f423d2
L
789// while ((opt = getopt(argc, argv, PSTR("fiprv"))) != -1) {
790 while ((opt = getopt(argc, argv, PSTR("fiprv"))) != -1) {
df607c3a
L
791 switch (opt) {
792 case 'f':
31f423d2 793 tflags &= ~(I_FLAG | N_FLAG);
1cdd02d8 794 tflags |= F_FLAG;
df607c3a
L
795 break;
796 case 'i':
31f423d2 797 tflags &= ~(F_FLAG | N_FLAG);
1cdd02d8 798 tflags |= I_FLAG;
df607c3a 799 break;
31f423d2
L
800 case 'n':
801 tflags &= ~(F_FLAG | I_FLAG);
802 tflags |= N_FLAG;
803 break;
df607c3a 804 case 'p':
1cdd02d8 805 tflags |= P_FLAG;
df607c3a 806 break;
df607c3a 807 case 'r':
1cdd02d8 808 tflags |= R_FLAG;
df607c3a
L
809 break;
810 case 'v':
1cdd02d8 811 tflags |= V_FLAG;
df607c3a
L
812 break;
813 default:
814 return CMD_RET_USAGE;
815 break;
816 }
817 }
1cdd02d8 818 flags = tflags;
df607c3a
L
819 argc -= optind;
820 argv += optind;
821
822 if (argc < 2)
823 return CMD_RET_USAGE;
824
326d0992
L
825 from = (PATH_T *) malloc(sizeof(PATH_T));
826 to = (PATH_T *) malloc(sizeof(PATH_T));
827 if (from == NULL || to == NULL) {
828 printf_P(PSTR("cp: Out of Memory!\n"));
829 command_ret = CMD_RET_FAILURE;
830 goto cleanup;
831 }
832 from->p_end = from->p_path; *from->p_path = '\0';
833 to->p_end = to->p_path; *to->p_path = '\0';
834
df607c3a 835 /* consume last argument first. */
326d0992
L
836 if (!path_set(to, argv[--argc])) {
837 command_ret = CMD_RET_FAILURE;
838 goto cleanup;
839 }
df607c3a 840
326d0992
L
841 /*
842 * Cp has two distinct cases:
843 *
844 * % cp [-rip] source target
845 * % cp [-rip] source1 ... directory
846 *
847 * In both cases, source can be either a file or a directory.
848 *
849 * In (1), the target becomes a copy of the source. That is, if the
850 * source is a file, the target will be a file, and likewise for
851 * directories.
852 *
853 * In (2), the real target is not directory, but "directory/source".
854 */
855
856 fr = f_stat(to->p_path, &to_stat);
1cdd02d8 857debug("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr, to_stat.fattrib, flags);
73cf0c86
L
858debug(" from:'%s' to:'%s'\n", from->p_path, to->p_path);
859
860 if (fr != FR_OK && fr != FR_NO_FILE && fr != FR_NO_PATH) {
861 err(PSTR("Test1: %s: %S"), to->p_path, rctostr(fr));
326d0992
L
862 command_ret = CMD_RET_FAILURE;
863 goto cleanup;
864 }
73cf0c86 865 if (!(fr == FR_OK && (to_stat.fattrib & AM_DIR))) {
326d0992
L
866 /*
867 * Case (1). Target is not a directory.
868 */
869 if (argc > 1) {
97bd904d
L
870 err(PSTR("target '%s' is not a directory"), to->p_path);
871 //command_ret = CMD_RET_USAGE;
326d0992
L
872 goto cleanup;
873 }
73cf0c86 874 if (!path_set(from, *argv)) {
326d0992
L
875 command_ret = CMD_RET_FAILURE;
876 goto cleanup;
73cf0c86 877 }
326d0992
L
878 copy();
879 }
880 else {
881 /*
882 * Case (2). Target is a directory.
883 */
884 for (;; ++argv) {
885 if (!path_set(from, *argv))
886 continue;
887 if (!(old_to = path_append(to, path_basename(from), -1)))
888 continue;
889 copy();
890 if (!--argc)
891 break;
97bd904d 892 path_restore(to, old_to);
326d0992
L
893 }
894 }
df607c3a 895
326d0992
L
896cleanup:
897 free(to);
898 free(from);
899
900 return command_ret;
901}
902
903#if 0
df607c3a 904 if (flags & V_FLAG)
326d0992 905 printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to->p_path)));
df607c3a
L
906
907#endif
908
df607c3a
L
909
910
df607c3a 911
df607c3a 912
df607c3a
L
913
914/******************************************************************************/
915
326d0992
L
916/*
917 * Work register for stat command
918 */
df607c3a
L
919struct stat_dat_s {
920 DWORD AccSize;
921 WORD AccFiles, AccDirs;
922 FILINFO Finfo;
923};
924
925static
926FRESULT scan_files (
927 char *path, /* Pointer to the working buffer with start path */
928 struct stat_dat_s *statp
929)
930{
931 DIR dirs;
932 FRESULT res;
933 int i;
934 char *fn;
935
936 res = f_opendir(&dirs, path);
937 swirl();
938 if (res == FR_OK) {
939 i = strlen(path);
940 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
941 statp->Finfo.fname[0]) {
942 if (_FS_RPATH && statp->Finfo.fname[0] == '.')
943 continue;
944 fn = statp->Finfo.fname;
945 if (statp->Finfo.fattrib & AM_DIR) {
946 statp->AccDirs++;
947 path[i] = '/';
948 strcpy(path+i+1, fn);
949 res = scan_files(path, statp);
950 path[i] = '\0';
951 if (res != FR_OK)
952 break;
953 } else {
954 //printf_P(PSTR("%s/%s\n"), path, fn);
955 statp->AccFiles++;
956 statp->AccSize += statp->Finfo.fsize;
957 }
958 if (check_abort()) {
959 res = 255;
960 break;
961 }
962 }
963 }
964
965 return res;
966}
967
968
969/*
970 * fatstat path - Show logical drive status
971 *
972 */
973command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
974{
975 FATFS *fs;
976 DWORD nfreeclst;
977 FRESULT res;
978 char *buf;
979 char *path = "";
980 struct stat_dat_s statp;
981
982 buf = (char *) malloc(BUFFER_SIZE);
983 if (buf == NULL) {
984 printf_P(PSTR("fat stat: Out of Memory!\n"));
985 return CMD_RET_FAILURE;
986 }
987
988 if (argc > 1)
989 path = argv[1];
990 res = f_getfree(path, &nfreeclst, &fs);
991 if (!res) {
992 printf_P(PSTR(
993 "FAT type: %u\n"
994 "Bytes/Cluster: %lu\n"
995 "Number of FATs: %u\n"
996 "Root DIR entries: %u\n"
997 "Sectors/FAT: %lu\n"
998 "Number of clusters: %lu\n"
999 "FAT start (lba): %lu\n"
1000 "DIR start (lba,cluster): %lu\n"
1001 "Data start (lba): %lu\n"),
1002 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
1003 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
1004 fs->fatbase, fs->dirbase, fs->database);
1005
1006#if _USE_LABEL
1007 DWORD serial;
1008 res = f_getlabel(path, buf, &serial);
1009 if (!res) {
1010 printf_P(PSTR(
1011 "Volume name: %s\n"
1012 "Volume S/N: %04X-%04X\n"),
1013 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
1014 }
1015#endif
1016 if (!res) {
1017 my_puts_P(PSTR("\nCounting... "));
1018 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
1019 strcpy(buf, path);
1020
1021 res = scan_files(buf, &statp);
1022 }
1023 if (!res) {
1024 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1025 "%lu KB total disk space.\n%lu KB available.\n"),
1026 statp.AccFiles, statp.AccSize, statp.AccDirs,
1027 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
1028 );
1029 }
1030 }
1031
1032 free(buf);
1033 if (res) {
1034 put_rc(res);
1035 return CMD_RET_FAILURE;
1036 }
1037 return CMD_RET_SUCCESS;
1038}
2f53dd65 1039
4565be9a
L
1040/*
1041 * fatread/write - load binary file to/from a dos filesystem
1042 * read <d:/path/filename> <addr> [bytes [pos]]
1043 * write <d:/path/filename> <addr> <bytes>
1044 */
df607c3a 1045command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 1046{
4565be9a
L
1047 FIL File;
1048 uint32_t addr;
1049 unsigned long bytes;
1050 unsigned long pos;
1051 unsigned long bytes_rw;
1052
3d8c1907 1053 bool dowrite = (argv[0][0] == 'w');
3b841cea 1054 FRESULT res = FR_OK;
4565be9a
L
1055 bool buserr = 0;
1056 uint32_t timer;
19b9a7d8 1057 uint8_t *buffer;
4565be9a 1058
4565be9a
L
1059 if (argc < (dowrite ? 4 : 3))
1060 return CMD_RET_USAGE;
1061
2d914b45 1062 addr = eval_arg(argv[2], NULL);
4565be9a
L
1063 if (addr >= MAX_MEMORY) {
1064 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
1065 return CMD_RET_FAILURE;
1066 }
1067 if (argc > 3)
2d914b45 1068 bytes = eval_arg(argv[3], NULL);
4565be9a
L
1069 else
1070 bytes = MAX_MEMORY;
1071 if (argc > 4)
2d914b45 1072 pos = eval_arg(argv[4], NULL);
4565be9a
L
1073 else
1074 pos = 0;
1075
1076 if (addr + bytes > MAX_MEMORY)
1077 bytes = MAX_MEMORY - addr;
1078
19b9a7d8 1079 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 1080 if (buffer == NULL) {
19b9a7d8 1081 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
1082 free(buffer);
1083 return CMD_RET_FAILURE;
1084 }
1085
4565be9a
L
1086 if (!res) {
1087 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
1088 : FA_READ );
1089
1090 if (!res) {
1091 res = f_lseek(&File, pos);
1092 if (!res) {
1093 bytes_rw = 0;
1094 timer = get_timer(0);
1095 while (bytes) {
1096 unsigned int cnt, br;
1097
19b9a7d8
L
1098 if (bytes >= BUFFER_SIZE) {
1099 cnt = BUFFER_SIZE;
1100 bytes -= BUFFER_SIZE;
4565be9a
L
1101 } else {
1102 cnt = bytes; bytes = 0;
1103 }
1104 if (dowrite) {
1105 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1106 buserr = 1;
1107 break;
1108 }
1109 z80_read_block(buffer, addr, cnt);
1110 z80_bus_cmd(Release);
1111 res = f_write(&File, buffer, cnt, &br);
1112 if (res != FR_OK)
1113 break;
1114 } else {
1115 res = f_read(&File, buffer, cnt, &br);
1116 if (res != FR_OK)
1117 break;
1118 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1119 buserr = 1;
1120 break;
1121 }
1122 z80_write_block(buffer, addr, br);
1123 z80_bus_cmd(Release);
1124 }
1125 addr += br;
1126
1127 bytes_rw += br;
1128 if (cnt != br) {
1129 if (dowrite)
1130 printf_P(PSTR("Disk full?\n"));
1131 break;
1132 }
b15d22a4 1133 if (check_abort())
4565be9a 1134 break;
4565be9a
L
1135 }
1136
1137 FRESULT fr = f_close(&File);
1138 if (!res)
1139 res = fr;
1140 timer = get_timer(timer);
1141 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1142 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
1143 }
1144 }
4565be9a
L
1145 }
1146
19b9a7d8 1147 free(buffer);
19b9a7d8 1148
4565be9a
L
1149 if (buserr)
1150 my_puts_P(PSTR("Bus timeout\n"));
1151 if (res)
1152 put_rc(res);
1153 if (buserr || res)
1154 return CMD_RET_FAILURE;
1155
1156 return CMD_RET_SUCCESS;
1157}
b6c04275 1158
3d8c1907 1159/*
df607c3a 1160 * command table for fat subcommands
3d8c1907 1161 */
b6c04275 1162
6c851813 1163cmd_tbl_t cmd_tbl_fat[] = {
b6c04275 1164CMD_TBL_ITEM(
82c475ad 1165 stat, 2, CTBL_RPT, do_stat,
b6c04275
L
1166 "Show logical drive status",
1167 "dev"
1168),
1169CMD_TBL_ITEM(
82c475ad 1170 pwd, 2, CTBL_RPT, do_pwd,
b6c04275
L
1171 "Print name of current/working directory",
1172 ""
1173),
1174CMD_TBL_ITEM(
82c475ad 1175 cd, 2, 0, do_cd,
b6c04275
L
1176 "Change the current/working directory.",
1177 "path"
1178),
1179CMD_TBL_ITEM(
82c475ad 1180 ls, 2, CTBL_RPT, do_ls,
b6c04275
L
1181 "Directory listing",
1182 "path"
1183),
3d8c1907 1184CMD_TBL_ITEM(
82c475ad 1185 tst, 2, CTBL_DBG|CTBL_RPT, do_tst,
3d8c1907
L
1186 "FatFS test function",
1187 "path"
1188),
b6c04275 1189CMD_TBL_ITEM(
82c475ad 1190 load, 5, 0, do_rw,
b6c04275
L
1191 "load binary file from a dos filesystem",
1192 "<d:/path/filename> <addr> [bytes [pos]]\n"
1193 " - Load binary file 'path/filename' on logical drive 'd'\n"
1194 " to address 'addr' from dos filesystem.\n"
1195 " 'pos' gives the file position to start loading from.\n"
1196 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1197 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1198 " the load stops on end of file."
1199),
1200CMD_TBL_ITEM(
82c475ad 1201 write, 4, 0, do_rw,
b6c04275
L
1202 "write file into a dos filesystem",
1203 "<d:/path/filename> <addr> <bytes>\n"
1204 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1205 " starting at address 'addr'.\n"
1206),
1207
3d8c1907 1208CMD_TBL_ITEM(
82c475ad 1209 cp, CONFIG_SYS_MAXARGS, CTBL_DBG, do_cp,
3d8c1907 1210 "copy files",
1cdd02d8 1211 "[-f | -i | -n] [-prv] source_file target_file\n"
31f423d2
L
1212// "[-f | -i | -n] [-prv] source_file target_file\n"
1213// "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1cdd02d8
L
1214 " -f overwrite existing file ignoring write protection\n"
1215 " this option is ignored when the -n option is also used\n"
1216 " -i prompt before overwrite (overrides a previous -n option)\n"
1217 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1218 " -p preserve attributes and timestamps\n"
1219 " -r copy directories recursively\n"
1220 " -v explain what is being done\n"
3d8c1907
L
1221),
1222
ee5c86e9 1223CMD_TBL_ITEM(
82c475ad 1224 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
ee5c86e9
L
1225 "Print sub command description/usage",
1226 "\n"
1227 " - print brief description of all sub commands\n"
1228 "fat help command ...\n"
1229 " - print detailed usage of sub cmd 'command'"
1230),
1231
b6c04275
L
1232/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1233 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
1234 FSTR("Alias for 'help'"),
1235#ifdef CONFIG_SYS_LONGHELP
1236 FSTR(""),
1237#endif /* CONFIG_SYS_LONGHELP */
6c851813 1238 NULL,
b6c04275 1239#ifdef CONFIG_AUTO_COMPLETE
6c851813 1240 NULL,
b6c04275
L
1241#endif
1242},
6c851813 1243/* Mark end of table */
6c7c9c2d 1244CMD_TBL_END(cmd_tbl_fat)
b6c04275
L
1245};
1246
b6c04275 1247
df607c3a 1248command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275 1249{
6c7c9c2d 1250 puts_P(PSTR("Huch?"));
b6c04275
L
1251
1252 return CMD_RET_USAGE;
1253}