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