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