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