]> cloudbase.mooo.com Git - z180-stamp.git/blame - avr/cmd_fat.c
fat ls: works again
[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
9abe3216 25
b2e23285
L
26#define DEBUG_CP 1 /* set to 1 to debug */
27#define DEBUG_LS 1 /* set to 1 to debug */
9abe3216
L
28
29#define debug_cp(fmt, args...) \
30 debug_cond(DEBUG_CP, fmt, ##args)
31#define debug_ls(fmt, args...) \
32 debug_cond(DEBUG_LS, fmt, ##args)
33
34
df607c3a
L
35/* TODO: use memory size test function (detect_ramsize() in cmd_loadihex.c) */
36/* TODO: detect_ramsize() should be moved to z80-if.c */
37#define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
19b9a7d8 38#define BUFFER_SIZE 512
3a93185f 39#define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
326d0992 40
19b9a7d8 41
3a93185f
L
42typedef struct {
43 char *p_end; /* pointer to NULL at end of path */
44 char p_path[MAX_PATHLEN + 1]; /* pointer to the start of a path */
45} PATH_T;
46
df607c3a
L
47/*
48 * Multible (fat) partitions per physical drive are not supported,
49 * but we have up to 2 sdcard slots.
50 */
b6c04275
L
51FATFS FatFs0;
52FATFS FatFs1;
53
3a93185f
L
54uint8_t *blockbuf;
55int blockbuf_size;
56PATH_T from;
57PATH_T to;
326d0992
L
58command_ret_t command_ret;
59
df607c3a
L
60void setup_fatfs(void)
61{
62 f_mount(&FatFs0, "0:", 0);
63 f_mount(&FatFs1, "1:", 0);
64}
b6c04275 65
2f53dd65
L
66DWORD get_fattime (void)
67{
19b9a7d8
L
68 time_t timer;
69 struct tm tm_timer;
70
71 time(&timer);
72 gmtime_r(&timer, &tm_timer);
73
74 return fatfs_time(&tm_timer);
2f53dd65
L
75}
76
77
b15d22a4
L
78static bool check_abort(void)
79{
80 bool ret = ctrlc();
81
82 if (ret)
83 printf_P(PSTR("Abort\n"));
84
85 return ret;
86}
87
88
326d0992
L
89static const FLASH char * const FLASH rc_strings[] = {
90 FSTR("OK"),
91 FSTR("disk error"),
92 FSTR("internal error"),
93 FSTR("not ready"),
94 FSTR("no file"),
95 FSTR("no path"),
96 FSTR("invalid name"),
97 FSTR("denied"),
98 FSTR("exist"),
99 FSTR("invalid object"),
100 FSTR("write protected"),
101 FSTR("invalid drive"),
102 FSTR("not enabled"),
103 FSTR("no file system"),
104 FSTR("mkfs aborted"),
105 FSTR("timeout"),
106 FSTR("locked"),
107 FSTR("not enough core"),
108 FSTR("too many open files"),
109 FSTR("invalid parameter")
110 };
111
2f53dd65
L
112static const FLASH char * const FLASH rc_names[] = {
113 FSTR("OK"),
114 FSTR("DISK_ERR"),
115 FSTR("INT_ERR"),
116 FSTR("NOT_READY"),
117 FSTR("NO_FILE"),
118 FSTR("NO_PATH"),
119 FSTR("INVALID_NAME"),
120 FSTR("DENIED"),
121 FSTR("EXIST"),
122 FSTR("INVALID_OBJECT"),
123 FSTR("WRITE_PROTECTED"),
124 FSTR("INVALID_DRIVE"),
125 FSTR("NOT_ENABLED"),
126 FSTR("NO_FILE_SYSTEM"),
127 FSTR("MKFS_ABORTED"),
128 FSTR("TIMEOUT"),
129 FSTR("LOCKED"),
130 FSTR("NOT_ENOUGH_CORE"),
7af9364e
L
131 FSTR("TOO_MANY_OPEN_FILES"),
132 FSTR("INVALID_PARAMETER")
2f53dd65
L
133 };
134
135static
136void put_rc (FRESULT rc)
137{
138#if GCC_BUG_61443
7af9364e
L
139 printf_P(PSTR("rc=%u FR_"), rc);
140 my_puts_P(rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
141 my_puts_P(PSTR("\n"));
2f53dd65 142#else
7af9364e
L
143 printf_P(PSTR("rc=%u FR_%S\n"), rc,
144 rc < ARRAY_SIZE(rc_names) ? rc_names[rc] : PSTR(" Unknown Error"));
2f53dd65
L
145#endif
146}
147
326d0992
L
148const FLASH char * rctostr(FRESULT rc)
149{
150 return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
151}
152
153void err(const char *fmt, ...)
154{
155 va_list ap;
156 va_start(ap, fmt);
157// (void)fprintf(stderr, "%s: ", progname);
158 (void)vfprintf_P(stdout, fmt, ap);
159 va_end(ap);
160 (void)printf_P(PSTR("\n"));
97bd904d 161 _delay_ms(20);
326d0992
L
162 command_ret = CMD_RET_FAILURE;
163}
164
b2e23285
L
165/******************************************************************************/
166
167/*
168 * These functions manipulate paths in PATH_T structures.
169 *
170 * They eliminate multiple slashes in paths when they notice them,
171 * and keep the path non-slash terminated.
172 *
173 * Both path_set() and path_append() return 0 if the requested name
174 * would be too long.
175 */
176
177
178static void path_init(void)
179{
180 from.p_end = from.p_path; from.p_path[0] = '\0';
181 to.p_end = to.p_path; to.p_path[0] = '\0';
182}
183
184static void strip_trailing_slash(PATH_T *p)
185{
186 char *beg = p->p_path;
187 char *end = p->p_end;
188 if ((beg + 2) < end && (*beg & 0x38) == '0' && *(beg+1) == ':')
189 beg += 2;
190 if (beg < end && *beg == '/')
191 ++beg;
192 while (end > beg && end[-1] == '/')
193 *--end = '\0';
194
195 p->p_end =end;
196}
197
198/*
199 * Move specified string into path. Convert "" to "." to handle BSD
200 * semantics for a null path. Strip trailing slashes.
201 */
202int
203path_set(PATH_T *p, char *string)
204{
205 if (strlen(string) > MAX_PATHLEN) {
206 err(PSTR("set: '%s': name too long"), string);
207 return 0;
208 }
209
210 (void)strcpy(p->p_path, string);
211 p->p_end = p->p_path + strlen(p->p_path);
212
213 if (p->p_path == p->p_end) {
214 *p->p_end++ = '.';
215 *p->p_end = 0;
216 }
217
218 strip_trailing_slash(p);
219 return 1;
220}
221
222/*
223 * Append specified string to path, inserting '/' if necessary. Return a
224 * pointer to the old end of path for restoration.
225 */
226char *
227path_append(PATH_T *p, char *name)
228{
229 char *old = p->p_end;
230 int len = strlen(name);
231
232 /* The "+ 1" accounts for the '/' between old path and name. */
233 if ((len + p->p_end - p->p_path + 1) > MAX_PATHLEN) {
234 err(PSTR("append: '%s/%s': name too long"), p->p_path, name);
235 return(0);
236 }
237
238 /*
239 * This code should always be executed, since paths shouldn't
240 * end in '/'.
241 */
242 if (p->p_end[-1] != '/') {
243 *p->p_end++ = '/';
244 *p->p_end = '\0';
245 }
246
247 (void)strncat(p->p_end, name, len);
248 p->p_end += len;
249 *p->p_end = '\0';
250
251 strip_trailing_slash(p);
252 return old;
253}
254
255/*
256 * Restore path to previous value. (As returned by path_append.)
257 */
258void
259path_restore(PATH_T *p, char *old)
260{
261 p->p_end = old;
262 *p->p_end = '\0';
263}
264
265/*
266 * Return basename of path.
267 */
268char *path_basename(PATH_T *p)
269{
92cdf874 270 char *basename = strrchr(p->p_path, '/');
b2e23285 271
92cdf874
L
272 if (basename) {
273 ++basename;
274 } else {
275 basename = p->p_path;
276 if ((basename[0] & 0x38) == '0' && basename[1] == ':')
277 basename += 2;
278 }
279
280 return basename;
b2e23285
L
281}
282
326d0992 283
b15d22a4
L
284
285static void swirl(void)
286{
6204987c 287 static const FLASH char swirlchar[] = { '-','\\','|','/' };
b15d22a4
L
288 static uint_fast8_t cnt;
289 static uint32_t tstamp;
290
291 if (get_timer(0) > tstamp) {
292 printf_P(PSTR("\b%c"), swirlchar[cnt]);
293 cnt = (cnt+1) % ARRAY_SIZE(swirlchar);
6204987c 294 tstamp = get_timer(0) + 250;
b15d22a4
L
295 }
296}
297
b6c04275
L
298/*
299 * pwd - Print current directory of the current drive.
300 *
301 */
df607c3a 302command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275
L
303{
304 FRESULT res;
305 char *buf;
306
b6c04275
L
307 buf = (char *) malloc(BUFFER_SIZE);
308 if (buf == NULL) {
309 printf_P(PSTR("pwd: Out of Memory!\n"));
310 free(buf);
311 return CMD_RET_FAILURE;
312 }
313
314 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
315
316 if (!res) {
317 puts(buf);
318 }
319 free(buf);
320 if (res) {
321 put_rc(res);
322 return CMD_RET_FAILURE;
323 }
324 return CMD_RET_SUCCESS;
325}
326
327
328/*
329 * cd - Change the current/working directory.
330 *
331 */
df607c3a 332command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
b6c04275
L
333{
334 char *arg;
335 FRESULT res = 0;
336
b6c04275 337 if (argc < 2) {
ee5c86e9 338 arg = getenv_str(PSTR(ENV_HOME));
b6c04275 339 if (arg == NULL) {
ee5c86e9 340 printf_P(PSTR("%s: \"%S\" is not set\n"), argv[0], PSTR(ENV_HOME));
b6c04275
L
341 return CMD_RET_FAILURE;
342 }
343 } else
344 arg = argv[1];
345
346 if (arg[1] == ':') {
347 char drv[3];
348 drv[2] = '\0';
349 drv[1] = ':';
350 drv[0] = arg[0];
351 res = f_chdrive(drv);
352 }
353 if (!res) {
354 res = f_chdir(arg);
355 }
356
357 if (res) {
358 put_rc(res);
359 return CMD_RET_FAILURE;
360 }
361 return CMD_RET_SUCCESS;
362}
363
b2e23285 364#if 0
df607c3a
L
365/*
366 * Remove trailing slashes,
367 * but keep a leading slash (absolute path)
368 */
3d8c1907
L
369void strip_trailing_slash_relpath(char *p)
370{
371 int n = strlen(p);
372
373 if (n >= 2 && (p[0] & 0x38) == '0' && p[1] == ':') {
374 p += 2;
375 n -= 2;
376 }
377 if (n >= 1 && p[0] == '/') {
378 p++;
379 n--;
380 }
381 while (n-- != 0 && p[n] == '/')
382 p[n] = '\0';
383}
b2e23285 384#endif
3d8c1907
L
385
386int print_dirent(FILINFO *f)
387{
388 return printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
389 (f->fattrib & AM_DIR) ? 'D' : '-',
390 (f->fattrib & AM_RDO) ? 'R' : '-',
391 (f->fattrib & AM_HID) ? 'H' : '-',
392 (f->fattrib & AM_SYS) ? 'S' : '-',
393 (f->fattrib & AM_ARC) ? 'A' : '-',
394 (f->fdate >> 9) + 1980, (f->fdate >> 5) & 15, f->fdate & 31,
395 (f->ftime >> 11), (f->ftime >> 5) & 63,
396 f->fsize, f->fname);
397}
2f53dd65
L
398
399/*
df607c3a 400 * ls path - Directory listing
2f53dd65
L
401 *
402 */
df607c3a 403command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
2f53dd65 404{
bbd45c46 405 FATFS *fs;
2f53dd65
L
406 DIR Dir; /* Directory object */
407 FILINFO Finfo;
408 unsigned long p1;
409 unsigned int s1, s2;
3d8c1907 410 FRESULT res;
b6c04275 411
b6c04275 412
b2e23285
L
413 path_init();
414 if (argc > 1)
415 if (!path_set(&from, argv[1])) {
416 /* TODO: error out*/
417 }
92cdf874 418
b2e23285 419 char *p = path_basename(&from);
3d8c1907 420 if (strpbrk_P(p, PSTR("*?")) ||
b2e23285 421 (f_stat(from.p_path, &Finfo) == FR_OK && !(Finfo.fattrib & AM_DIR))) {
92cdf874 422 path_set(&to, p);
3d8c1907 423 *p = '\0';
92cdf874
L
424 } else {
425 to.p_path[0] = '*';
426 to.p_path[1] = '\0';
427 }
3d8c1907 428
92cdf874 429debug_ls("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", to.p_path ? to.p_path : "<NULL>");
2f53dd65
L
430
431 p1 = s1 = s2 = 0;
92cdf874 432 res = f_findfirst(&Dir, &Finfo, from.p_path, to.p_path); /* Start to search for files */
3d8c1907 433 while (res == FR_OK && Finfo.fname[0]) {
2f53dd65
L
434 if (Finfo.fattrib & AM_DIR) {
435 s2++;
436 } else {
437 s1++; p1 += Finfo.fsize;
438 }
3d8c1907 439 print_dirent(&Finfo);
b15d22a4
L
440 if (check_abort())
441 break;
3d8c1907 442 res = f_findnext(&Dir, &Finfo);
2f53dd65 443 }
3d8c1907 444 f_closedir(&Dir);
2f53dd65
L
445
446 if (res == FR_OK) {
447 printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
b2e23285 448 if (f_getfree(from.p_path, (DWORD*)&p1, &fs) == FR_OK)
2f53dd65
L
449 printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
450 }
451
3d8c1907
L
452 if (res) {
453 put_rc(res);
454 return CMD_RET_FAILURE;
455 }
456
457 return CMD_RET_SUCCESS;
458}
459
df607c3a
L
460/*
461 * tst path - for debugging: test access with different functions
462 *
463 */
464command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
3d8c1907
L
465{
466 DIR Dir; /* Directory object */
467 FILINFO Finfo;
468 FRESULT res = FR_OK;
469 char *path;
470
3d8c1907
L
471 printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO));
472
473 char * buf = (char *) malloc(BUFFER_SIZE);
474 if (buf == NULL) {
9abe3216 475 printf_P(PSTR("tst: Out of Memory!\n"));
3d8c1907
L
476 free(buf);
477 return CMD_RET_FAILURE;
478 }
479 res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
480
481 if (!res) {
9abe3216 482 printf_P(PSTR("cwd: '%s'\n"), buf);
3d8c1907 483 }
b6c04275 484 free(buf);
2f53dd65
L
485 if (res) {
486 put_rc(res);
487 return CMD_RET_FAILURE;
488 }
489
3d8c1907
L
490 if (argc > 1)
491 path = argv[1];
492 else
493 path = "";
494
9abe3216 495 printf_P(PSTR("arg: '%s'\n"), path);
3d8c1907
L
496 printf_P(PSTR("==== f_stat: "));
497 res = f_stat(path, &Finfo);
498 put_rc(res);
499 if (res == FR_OK) {
500 print_dirent(&Finfo);
501 }
502
503 printf_P(PSTR("==== f_findfirst: "));
504 res = f_findfirst(&Dir, &Finfo, path, "*"); /* Start to search for files */
505 put_rc(res);
506 if (res == FR_OK) {
507 print_dirent(&Finfo);
508 }
509 f_closedir(&Dir);
510
511 printf_P(PSTR("==== f_opendir: "));
512 res = f_opendir(&Dir, path);
513 put_rc(res);
514 f_closedir(&Dir);
515
2f53dd65
L
516 return CMD_RET_SUCCESS;
517}
518
df607c3a
L
519/******************************************************************************/
520
1cdd02d8 521static uint8_t flags;
31f423d2 522#define F_FLAG (1<<3) // overwrite existing file ignoring write protection
1cdd02d8
L
523#define I_FLAG (1<<1) // prompt before overwrite (overrides a previous -n option)
524#define N_FLAG (1<<2) // do not overwrite an existing file (overrides a previous -i option)
1cdd02d8 525#define P_FLAG (1<<4) // preserve attributes and timestamps
31f423d2 526#define R_FLAG (1<<0) // copy directories recursively
1cdd02d8 527#define V_FLAG (1<<5) // explain what is being done
df607c3a 528
326d0992 529static void
9abe3216 530setfile(FILINFO *fs)
326d0992 531{
9abe3216 532 FRESULT fr;
326d0992 533
2d23b441 534 fr = f_utime(to.p_path, fs);
9abe3216 535 if (fr != FR_OK)
2d23b441
L
536 err(PSTR("f_utime: %s: %S"), to.p_path, rctostr(fr));
537 fr = f_chmod(to.p_path, fs->fattrib, AM_RDO|AM_ARC|AM_SYS|AM_HID);
9abe3216 538 if (fr != FR_OK)
2d23b441 539 err(PSTR("f_chmod: %s: %S"), to.p_path, rctostr(fr));
326d0992 540
326d0992
L
541}
542
543void copy_file(FILINFO *fs, uint_fast8_t dne)
544{
326d0992
L
545 FIL from_fd, to_fd;
546 UINT rcount, wcount;
326d0992 547 FRESULT fr;
9abe3216 548 BYTE open_mode;
326d0992 549
2f8b658a
L
550 if (blockbuf == NULL) {
551 blockbuf_size = get_freemem() / 512 * 512;
552 if (blockbuf_size != 0)
553 blockbuf = (uint8_t *) malloc(blockbuf_size);
554 if (blockbuf == NULL) {
555 err(PSTR("Not enough memory!\n"));
556 return;
557 }
16fb450c
L
558 }
559
9abe3216 560debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d\n", dne, blockbuf_size);
2d23b441 561debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
326d0992 562
16fb450c 563
2d23b441
L
564 if ((fr = f_open(&from_fd, from.p_path, FA_READ)) != FR_OK) {
565 err(PSTR("%s: %S"), from.p_path, rctostr(fr));
326d0992
L
566 return;
567 }
568
569 /*
570 * If the file exists and we're interactive, verify with the user.
326d0992
L
571 */
572 if (!dne) {
9abe3216
L
573 if (flags & N_FLAG) {
574 if (flags & V_FLAG)
2d23b441 575 printf_P(PSTR("%s not overwritten\n"), to.p_path);
9abe3216
L
576 f_close(&from_fd);
577 return;
578 } if (flags & I_FLAG) {
2d23b441 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 }
9abe3216
L
585 if (flags & F_FLAG) {
586 /* Remove existing destination file name create a new file. */
2d23b441
L
587 f_chmod(to.p_path,0, AM_RDO);
588 f_unlink(to.p_path);
9abe3216
L
589 open_mode = FA_WRITE|FA_CREATE_NEW;
590 } else {
591 /* Overwrite existing destination file name. */
592 open_mode = FA_WRITE|FA_CREATE_ALWAYS;
593 }
594 } else {
595 open_mode = FA_WRITE|FA_CREATE_NEW;
596 }
2d23b441 597 fr = f_open(&to_fd, to.p_path, open_mode);
326d0992
L
598
599 if (fr != FR_OK) {
2d23b441 600 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
326d0992
L
601 f_close(&from_fd);
602 return;
603 }
604
2f8b658a 605 while ((fr = f_read(&from_fd, blockbuf, blockbuf_size, &rcount)) == FR_OK &&
326d0992 606 rcount > 0) {
2f8b658a 607 fr = f_write(&to_fd, blockbuf, rcount, &wcount);
326d0992 608 if (fr || wcount < rcount) {
2d23b441 609 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
326d0992
L
610 break;
611 }
612 }
613 if (fr != FR_OK)
2d23b441 614 err(PSTR("%s: S"), from.p_path, rctostr(fr));
326d0992 615
326d0992
L
616 f_close(&from_fd);
617 if ((fr = f_close(&to_fd)) != FR_OK)
2d23b441 618 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
2f8b658a 619
9abe3216
L
620 if (flags & P_FLAG)
621 setfile(fs);
326d0992
L
622}
623
2d23b441
L
624static void copy();
625
626#if 0
326d0992
L
627static void copy_dir(void)
628{
9abe3216 629debug_cp("==== copy_dir()");
2d23b441 630debug_cp(" from:'%s' to:'%s'\n", from->p_path, to.p_path);
73cf0c86
L
631
632 printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
326d0992 633 from->p_path);
73cf0c86 634 command_ret = CMD_RET_FAILURE;
326d0992
L
635}
636#else
637static void copy_dir(void)
2d23b441
L
638{
639 DIR Dir; /* Directory object */
640 FILINFO Finfo;
641 char *old_from, *old_to;
642 FRESULT res;
643 char *pattern = {"*"};
644
645debug_cp("==== copy_dir()");
646debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
647
648
649 for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
650 res == FR_OK && Finfo.fname[0];
651 res = f_findfirst(&Dir, &Finfo, from.p_path, pattern)) {
652
653 if (!(Finfo.fattrib & AM_DIR) &&
b2e23285
L
654 (old_from = path_append(&from, Finfo.fname))) {
655 if ((old_to = path_append(&to, Finfo.fname))) {
2d23b441
L
656 copy();
657 path_restore(&to, old_to);
658 }
659 path_restore(&from, old_from);
660 }
661 }
662
663 for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
664 res == FR_OK && Finfo.fname[0];
665 res = f_findnext(&Dir, &Finfo)) {
666
667 if ((Finfo.fattrib & AM_DIR) &&
b2e23285
L
668 (old_from = path_append(&from, Finfo.fname))) {
669 if ((old_to = path_append(&to, Finfo.fname))) {
2d23b441
L
670 copy();
671 path_restore(&to, old_to);
672 }
673 path_restore(&from, old_from);
674 }
675 }
676}
677#endif
678#if 0
679static void copy_dir(void)
326d0992
L
680{
681 FILINFO from_stat;
682 struct dirent *dp, **dir_list;
683 int dir_cnt, i;
684 char *old_from, *old_to;
685
9abe3216 686debug_cp("==== copy_file(): dne: %u\n", dne);
2d23b441 687debug_cp(" from:'%s' to:'%s'\n", from->p_path, to.p_path);
73cf0c86 688
326d0992
L
689 dir_cnt = scandir(from->p_path, &dir_list, NULL, NULL);
690 if (dir_cnt == -1) {
691 (void)fprintf(stderr, "%s: can't read directory %s.\n",
692 progname, from->p_path);
693 command_ret = CMD_RET_FAILURE;
694 }
695
696 /*
697 * Instead of handling directory entries in the order they appear
698 * on disk, do non-directory files before directory files.
699 * There are two reasons to do directories last. The first is
700 * efficiency. Files tend to be in the same cylinder group as
701 * their parent, whereas directories tend not to be. Copying files
702 * all at once reduces seeking. Second, deeply nested tree's
703 * could use up all the file descriptors if we didn't close one
704 * directory before recursivly starting on the next.
705 */
706 /* copy files */
707 for (i = 0; i < dir_cnt; ++i) {
708 dp = dir_list[i];
709 if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
710 && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
711 goto done;
712 if (!(old_from =
713 path_append(&from, dp->d_name, (int)dp->d_namlen)))
714 goto done;
715
716 if (statfcn(from->p_path, &from_stat) < 0) {
717 err(PSTR("%s: %s"), dp->d_name, strerror(errno));
718 path_restore(&from, old_from);
719 goto done;
720 }
721 if (S_ISDIR(from_stat.st_mode)) {
722 path_restore(&from, old_from);
723 continue;
724 }
725 if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
726 copy();
727 path_restore(&to, old_to);
728 }
729 path_restore(&from, old_from);
730done: dir_list[i] = NULL;
731 free(dp);
732 }
733
734 /* copy directories */
735 for (i = 0; i < dir_cnt; ++i) {
736 dp = dir_list[i];
737 if (!dp)
738 continue;
739 if (!(old_from =
740 path_append(&from, dp->d_name, (int)dp->d_namlen))) {
741 free(dp);
742 continue;
743 }
744 if (!(old_to =
745 path_append(&to, dp->d_name, (int)dp->d_namlen))) {
746 free(dp);
747 path_restore(&from, old_from);
748 continue;
749 }
750 copy();
751 free(dp);
752 path_restore(&from, old_from);
753 path_restore(&to, old_to);
754 }
755 free(dir_list);
756}
757#endif
758
759/*
760 * copy file or directory at "from" to "to".
761 */
762static void copy()
763{
764 FILINFO from_stat, to_stat;
765 uint_fast8_t dne;
766 FRESULT fr;
767
9abe3216 768debug_cp("==== copy()\n");
2d23b441 769debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
326d0992 770
2d23b441 771 fr = f_stat(from.p_path, &from_stat);
73cf0c86 772 if (fr != FR_OK) {
2d23b441 773 err(PSTR("%s: %S"), from.p_path, rctostr(fr));
326d0992
L
774 return;
775 }
776
777 /* not an error, but need to remember it happened */
2d23b441 778 if (f_stat(to.p_path, &to_stat) != FR_OK)
326d0992
L
779 dne = 1;
780 else {
2d23b441 781 if (strcmp(to.p_path, from.p_path) == 0) {
326d0992 782 (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
2d23b441 783 to.p_path, from.p_path);
326d0992
L
784 command_ret = CMD_RET_FAILURE;
785 return;
786 }
787 dne = 0;
788 }
789
790 if(from_stat.fattrib & AM_DIR) {
791 if (!(flags & R_FLAG)) {
73cf0c86 792 (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
2d23b441 793 from.p_path);
326d0992
L
794 command_ret = CMD_RET_FAILURE;
795 return;
796 }
797 if (dne) {
798 /*
799 * If the directory doesn't exist, create the new one.
800 */
2d23b441
L
801 if ((fr = f_mkdir(to.p_path)) != FR_OK) {
802 err(PSTR("%s: %S"), to.p_path, rctostr(fr));
326d0992
L
803 return;
804 }
805 }
806 else if (!(to_stat.fattrib & AM_DIR)) {
2d23b441 807 (void)printf_P(PSTR("%s: not a directory.\n"), to.p_path);
326d0992
L
808 return;
809 }
810 copy_dir();
326d0992 811 if (flags & P_FLAG)
9abe3216 812 setfile(&from_stat);
31f423d2 813
326d0992
L
814 return;
815 }
816 copy_file(&from_stat, dne);
817}
818
df607c3a
L
819command_ret_t do_cp(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
820{
821
326d0992
L
822 FRESULT fr; /* Return value */
823 //DIR dj; /* Directory search object */
824 FILINFO to_stat; /* File information */
825 char *old_to;
df607c3a 826
df607c3a 827
1cdd02d8 828 uint8_t tflags = 0;
326d0992 829 command_ret = CMD_RET_SUCCESS;
df607c3a
L
830
831 /* reset getopt() */
832 optind = 0;
833
834 int opt;
9abe3216 835 while ((opt = getopt(argc, argv, PSTR("finprv"))) != -1) {
df607c3a
L
836 switch (opt) {
837 case 'f':
31f423d2 838 tflags &= ~(I_FLAG | N_FLAG);
1cdd02d8 839 tflags |= F_FLAG;
df607c3a
L
840 break;
841 case 'i':
31f423d2 842 tflags &= ~(F_FLAG | N_FLAG);
1cdd02d8 843 tflags |= I_FLAG;
df607c3a 844 break;
31f423d2
L
845 case 'n':
846 tflags &= ~(F_FLAG | I_FLAG);
847 tflags |= N_FLAG;
848 break;
df607c3a 849 case 'p':
1cdd02d8 850 tflags |= P_FLAG;
df607c3a 851 break;
df607c3a 852 case 'r':
1cdd02d8 853 tflags |= R_FLAG;
df607c3a
L
854 break;
855 case 'v':
1cdd02d8 856 tflags |= V_FLAG;
df607c3a
L
857 break;
858 default:
859 return CMD_RET_USAGE;
860 break;
861 }
862 }
1cdd02d8 863 flags = tflags;
df607c3a
L
864 argc -= optind;
865 argv += optind;
866
867 if (argc < 2)
868 return CMD_RET_USAGE;
869
b2e23285 870 path_init();
326d0992 871
9abe3216 872 /* last argument is destination */
2d23b441 873 if (!path_set(&to, argv[--argc]))
326d0992 874 goto cleanup;
df607c3a 875
326d0992
L
876 /*
877 * Cp has two distinct cases:
878 *
879 * % cp [-rip] source target
880 * % cp [-rip] source1 ... directory
881 *
882 * In both cases, source can be either a file or a directory.
883 *
884 * In (1), the target becomes a copy of the source. That is, if the
885 * source is a file, the target will be a file, and likewise for
886 * directories.
887 *
888 * In (2), the real target is not directory, but "directory/source".
889 */
890
2d23b441 891 fr = f_stat(to.p_path, &to_stat);
9abe3216 892debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x\n", fr, to_stat.fattrib, flags);
2d23b441 893debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
73cf0c86
L
894
895 if (fr != FR_OK && fr != FR_NO_FILE && fr != FR_NO_PATH) {
2d23b441 896 err(PSTR("Test1: %s: %S"), to.p_path, rctostr(fr));
326d0992
L
897 command_ret = CMD_RET_FAILURE;
898 goto cleanup;
899 }
73cf0c86 900 if (!(fr == FR_OK && (to_stat.fattrib & AM_DIR))) {
326d0992
L
901 /*
902 * Case (1). Target is not a directory.
903 */
904 if (argc > 1) {
2d23b441 905 err(PSTR("target '%s' is not a directory"), to.p_path);
97bd904d 906 //command_ret = CMD_RET_USAGE;
326d0992
L
907 goto cleanup;
908 }
2d23b441 909 if (!path_set(&from, *argv)) {
326d0992
L
910 command_ret = CMD_RET_FAILURE;
911 goto cleanup;
73cf0c86 912 }
326d0992
L
913 copy();
914 }
915 else {
916 /*
917 * Case (2). Target is a directory.
918 */
919 for (;; ++argv) {
2d23b441 920 if (!path_set(&from, *argv))
326d0992 921 continue;
b2e23285 922 if (!(old_to = path_append(&to, path_basename(&from))))
326d0992
L
923 continue;
924 copy();
925 if (!--argc)
926 break;
2d23b441 927 path_restore(&to, old_to);
326d0992
L
928 }
929 }
df607c3a 930
326d0992 931cleanup:
2f8b658a
L
932 free(blockbuf);
933 blockbuf = NULL;
934 blockbuf_size = 0;
326d0992
L
935
936 return command_ret;
937}
938
939#if 0
df607c3a 940 if (flags & V_FLAG)
2d23b441 941 printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to.p_path)));
df607c3a
L
942
943#endif
944
df607c3a
L
945
946
df607c3a 947
df607c3a 948
df607c3a
L
949
950/******************************************************************************/
951
326d0992
L
952/*
953 * Work register for stat command
954 */
df607c3a
L
955struct stat_dat_s {
956 DWORD AccSize;
957 WORD AccFiles, AccDirs;
958 FILINFO Finfo;
959};
960
961static
962FRESULT scan_files (
963 char *path, /* Pointer to the working buffer with start path */
964 struct stat_dat_s *statp
965)
966{
967 DIR dirs;
968 FRESULT res;
969 int i;
970 char *fn;
971
972 res = f_opendir(&dirs, path);
973 swirl();
974 if (res == FR_OK) {
975 i = strlen(path);
976 while (((res = f_readdir(&dirs, &statp->Finfo)) == FR_OK) &&
977 statp->Finfo.fname[0]) {
978 if (_FS_RPATH && statp->Finfo.fname[0] == '.')
979 continue;
980 fn = statp->Finfo.fname;
981 if (statp->Finfo.fattrib & AM_DIR) {
982 statp->AccDirs++;
983 path[i] = '/';
984 strcpy(path+i+1, fn);
985 res = scan_files(path, statp);
986 path[i] = '\0';
987 if (res != FR_OK)
988 break;
989 } else {
990 //printf_P(PSTR("%s/%s\n"), path, fn);
991 statp->AccFiles++;
992 statp->AccSize += statp->Finfo.fsize;
993 }
994 if (check_abort()) {
995 res = 255;
996 break;
997 }
998 }
999 }
1000
1001 return res;
1002}
1003
1004
1005/*
1006 * fatstat path - Show logical drive status
1007 *
1008 */
1009command_ret_t do_stat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
1010{
1011 FATFS *fs;
1012 DWORD nfreeclst;
1013 FRESULT res;
1014 char *buf;
1015 char *path = "";
1016 struct stat_dat_s statp;
1017
1018 buf = (char *) malloc(BUFFER_SIZE);
1019 if (buf == NULL) {
1020 printf_P(PSTR("fat stat: Out of Memory!\n"));
1021 return CMD_RET_FAILURE;
1022 }
1023
1024 if (argc > 1)
1025 path = argv[1];
1026 res = f_getfree(path, &nfreeclst, &fs);
1027 if (!res) {
1028 printf_P(PSTR(
1029 "FAT type: %u\n"
1030 "Bytes/Cluster: %lu\n"
1031 "Number of FATs: %u\n"
1032 "Root DIR entries: %u\n"
1033 "Sectors/FAT: %lu\n"
1034 "Number of clusters: %lu\n"
1035 "FAT start (lba): %lu\n"
1036 "DIR start (lba,cluster): %lu\n"
1037 "Data start (lba): %lu\n"),
1038 fs->fs_type, (DWORD)fs->csize * 512, fs->n_fats,
1039 fs->n_rootdir, fs->fsize, fs->n_fatent - 2,
1040 fs->fatbase, fs->dirbase, fs->database);
1041
1042#if _USE_LABEL
1043 DWORD serial;
1044 res = f_getlabel(path, buf, &serial);
1045 if (!res) {
1046 printf_P(PSTR(
1047 "Volume name: %s\n"
1048 "Volume S/N: %04X-%04X\n"),
1049 buf, (WORD)(serial >> 16), (WORD)(serial & 0xFFFF));
1050 }
1051#endif
1052 if (!res) {
1053 my_puts_P(PSTR("\nCounting... "));
1054 statp.AccSize = statp.AccFiles = statp.AccDirs = 0;
1055 strcpy(buf, path);
1056
1057 res = scan_files(buf, &statp);
1058 }
1059 if (!res) {
1060 printf_P(PSTR("\r%u files, %lu bytes.\n%u folders.\n"
1061 "%lu KB total disk space.\n%lu KB available.\n"),
1062 statp.AccFiles, statp.AccSize, statp.AccDirs,
1063 (fs->n_fatent - 2) * (fs->csize / 2), nfreeclst * (fs->csize / 2)
1064 );
1065 }
1066 }
1067
1068 free(buf);
1069 if (res) {
1070 put_rc(res);
1071 return CMD_RET_FAILURE;
1072 }
1073 return CMD_RET_SUCCESS;
1074}
2f53dd65 1075
4565be9a
L
1076/*
1077 * fatread/write - load binary file to/from a dos filesystem
1078 * read <d:/path/filename> <addr> [bytes [pos]]
1079 * write <d:/path/filename> <addr> <bytes>
1080 */
df607c3a 1081command_ret_t do_rw(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
4565be9a 1082{
4565be9a
L
1083 FIL File;
1084 uint32_t addr;
1085 unsigned long bytes;
1086 unsigned long pos;
1087 unsigned long bytes_rw;
1088
3d8c1907 1089 bool dowrite = (argv[0][0] == 'w');
3b841cea 1090 FRESULT res = FR_OK;
4565be9a
L
1091 bool buserr = 0;
1092 uint32_t timer;
19b9a7d8 1093 uint8_t *buffer;
4565be9a 1094
4565be9a
L
1095 if (argc < (dowrite ? 4 : 3))
1096 return CMD_RET_USAGE;
1097
2d914b45 1098 addr = eval_arg(argv[2], NULL);
4565be9a
L
1099 if (addr >= MAX_MEMORY) {
1100 printf_P(PSTR("address too high: 0x%0lx\n"), addr);
1101 return CMD_RET_FAILURE;
1102 }
1103 if (argc > 3)
2d914b45 1104 bytes = eval_arg(argv[3], NULL);
4565be9a
L
1105 else
1106 bytes = MAX_MEMORY;
1107 if (argc > 4)
2d914b45 1108 pos = eval_arg(argv[4], NULL);
4565be9a
L
1109 else
1110 pos = 0;
1111
1112 if (addr + bytes > MAX_MEMORY)
1113 bytes = MAX_MEMORY - addr;
1114
19b9a7d8 1115 buffer = (uint8_t *) malloc(BUFFER_SIZE);
bbd45c46 1116 if (buffer == NULL) {
19b9a7d8 1117 printf_P(PSTR("fatstat: Out of Memory!\n"));
19b9a7d8
L
1118 free(buffer);
1119 return CMD_RET_FAILURE;
1120 }
1121
4565be9a
L
1122 if (!res) {
1123 res = f_open(&File, argv[1], dowrite ? FA_WRITE | FA_CREATE_ALWAYS
1124 : FA_READ );
1125
1126 if (!res) {
1127 res = f_lseek(&File, pos);
1128 if (!res) {
1129 bytes_rw = 0;
1130 timer = get_timer(0);
1131 while (bytes) {
1132 unsigned int cnt, br;
1133
19b9a7d8
L
1134 if (bytes >= BUFFER_SIZE) {
1135 cnt = BUFFER_SIZE;
1136 bytes -= BUFFER_SIZE;
4565be9a
L
1137 } else {
1138 cnt = bytes; bytes = 0;
1139 }
1140 if (dowrite) {
1141 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1142 buserr = 1;
1143 break;
1144 }
1145 z80_read_block(buffer, addr, cnt);
1146 z80_bus_cmd(Release);
1147 res = f_write(&File, buffer, cnt, &br);
1148 if (res != FR_OK)
1149 break;
1150 } else {
1151 res = f_read(&File, buffer, cnt, &br);
1152 if (res != FR_OK)
1153 break;
1154 if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) {
1155 buserr = 1;
1156 break;
1157 }
1158 z80_write_block(buffer, addr, br);
1159 z80_bus_cmd(Release);
1160 }
1161 addr += br;
1162
1163 bytes_rw += br;
1164 if (cnt != br) {
1165 if (dowrite)
1166 printf_P(PSTR("Disk full?\n"));
1167 break;
1168 }
b15d22a4 1169 if (check_abort())
4565be9a 1170 break;
4565be9a
L
1171 }
1172
1173 FRESULT fr = f_close(&File);
1174 if (!res)
1175 res = fr;
1176 timer = get_timer(timer);
1177 printf_P(PSTR("%lu (0x%lx) bytes read/written with %lu bytes/sec.\n"),
1178 bytes_rw, bytes_rw, timer ? (bytes_rw * 1000 / timer) : 0);
1179 }
1180 }
4565be9a
L
1181 }
1182
19b9a7d8 1183 free(buffer);
19b9a7d8 1184
4565be9a
L
1185 if (buserr)
1186 my_puts_P(PSTR("Bus timeout\n"));
1187 if (res)
1188 put_rc(res);
1189 if (buserr || res)
1190 return CMD_RET_FAILURE;
1191
1192 return CMD_RET_SUCCESS;
1193}
b6c04275 1194
3d8c1907 1195/*
df607c3a 1196 * command table for fat subcommands
3d8c1907 1197 */
b6c04275 1198
6c851813 1199cmd_tbl_t cmd_tbl_fat[] = {
b6c04275 1200CMD_TBL_ITEM(
82c475ad 1201 stat, 2, CTBL_RPT, do_stat,
b6c04275
L
1202 "Show logical drive status",
1203 "dev"
1204),
1205CMD_TBL_ITEM(
82c475ad 1206 pwd, 2, CTBL_RPT, do_pwd,
b6c04275
L
1207 "Print name of current/working directory",
1208 ""
1209),
1210CMD_TBL_ITEM(
82c475ad 1211 cd, 2, 0, do_cd,
b6c04275
L
1212 "Change the current/working directory.",
1213 "path"
1214),
1215CMD_TBL_ITEM(
82c475ad 1216 ls, 2, CTBL_RPT, do_ls,
b6c04275
L
1217 "Directory listing",
1218 "path"
1219),
3d8c1907 1220CMD_TBL_ITEM(
82c475ad 1221 tst, 2, CTBL_DBG|CTBL_RPT, do_tst,
3d8c1907
L
1222 "FatFS test function",
1223 "path"
1224),
b6c04275 1225CMD_TBL_ITEM(
82c475ad 1226 load, 5, 0, do_rw,
b6c04275
L
1227 "load binary file from a dos filesystem",
1228 "<d:/path/filename> <addr> [bytes [pos]]\n"
1229 " - Load binary file 'path/filename' on logical drive 'd'\n"
1230 " to address 'addr' from dos filesystem.\n"
1231 " 'pos' gives the file position to start loading from.\n"
1232 " If 'pos' is omitted, 0 is used. 'pos' requires 'bytes'.\n"
1233 " 'bytes' gives the size to load. If 'bytes' is 0 or omitted,\n"
1234 " the load stops on end of file."
1235),
1236CMD_TBL_ITEM(
82c475ad 1237 write, 4, 0, do_rw,
b6c04275
L
1238 "write file into a dos filesystem",
1239 "<d:/path/filename> <addr> <bytes>\n"
1240 " - Write file to 'path/filename' on logical drive 'd' from RAM\n"
1241 " starting at address 'addr'.\n"
1242),
1243
3d8c1907 1244CMD_TBL_ITEM(
82c475ad 1245 cp, CONFIG_SYS_MAXARGS, CTBL_DBG, do_cp,
3d8c1907 1246 "copy files",
1cdd02d8 1247 "[-f | -i | -n] [-prv] source_file target_file\n"
31f423d2
L
1248// "[-f | -i | -n] [-prv] source_file target_file\n"
1249// "cp [-f | -i | -n] [-prv] source_file ... target_dir\n"
1cdd02d8
L
1250 " -f overwrite existing file ignoring write protection\n"
1251 " this option is ignored when the -n option is also used\n"
1252 " -i prompt before overwrite (overrides a previous -n option)\n"
1253 " -n do not overwrite an existing file (overrides a previous -i option)\n"
1254 " -p preserve attributes and timestamps\n"
1255 " -r copy directories recursively\n"
1256 " -v explain what is being done\n"
3d8c1907
L
1257),
1258
ee5c86e9 1259CMD_TBL_ITEM(
82c475ad 1260 help, CONFIG_SYS_MAXARGS, CTBL_RPT, do_help,
ee5c86e9
L
1261 "Print sub command description/usage",
1262 "\n"
1263 " - print brief description of all sub commands\n"
1264 "fat help command ...\n"
1265 " - print detailed usage of sub cmd 'command'"
1266),
1267
b6c04275
L
1268/* This does not use the CMD_TBL_ITEM macro as ? can't be used in symbol names */
1269 {FSTR("?"), CONFIG_SYS_MAXARGS, 1, do_help,
1270 FSTR("Alias for 'help'"),
1271#ifdef CONFIG_SYS_LONGHELP
1272 FSTR(""),
1273#endif /* CONFIG_SYS_LONGHELP */
6c851813 1274 NULL,
b6c04275 1275#ifdef CONFIG_AUTO_COMPLETE
6c851813 1276 NULL,
b6c04275
L
1277#endif
1278},
6c851813 1279/* Mark end of table */
6c7c9c2d 1280CMD_TBL_END(cmd_tbl_fat)
b6c04275
L
1281};
1282
b6c04275 1283
df607c3a 1284command_ret_t do_fat(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
b6c04275 1285{
6c7c9c2d 1286 puts_P(PSTR("Huch?"));
b6c04275
L
1287
1288 return CMD_RET_USAGE;
1289}