+/*
+ * pwd - Print current directory of the current drive.
+ *
+ */
+command_ret_t do_pwd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc UNUSED, char * const argv[] UNUSED)
+{
+ FRESULT res;
+
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+ res = f_getcwd(from.p_path, MAX_PATHLEN); /* Get current directory path */
+
+ if (res == FR_OK)
+ puts(from.p_path);
+ else
+ err(PSTR("Error: %S"), rctostr(res));
+
+ return command_ret;
+}
+
+
+/*
+ * cd - Change the current/working directory.
+ *
+ */
+command_ret_t do_cd(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ char *arg;
+ FRESULT res = FR_OK;
+
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+ if (argc < 2) {
+ arg = getenv_str(PSTR(ENV_HOME));
+ if (arg == NULL) {
+ err(PSTR("'%S' is not set"), PSTR(ENV_HOME));
+ return command_ret;
+ }
+ } else
+ arg = argv[1];
+
+ if (arg[1] == ':')
+ res = f_chdrive(arg);
+ if (res == FR_OK)
+ res = f_chdir(arg);
+ if (res != FR_OK)
+ err(PSTR("'%s': %S"), arg, rctostr(res));
+
+ return command_ret;
+}
+
+
+static int decode_arg(const char *arg)
+{
+ BYTE attr = 0;
+ char c;
+
+ while ((c = *++arg) != '\0') {
+ switch (c) {
+ case 'a':
+ attr |= AM_ARC; /* Archive */
+ break;
+ case 'h':
+ attr |= AM_HID; /* Hidden */
+ break;
+ case 'r':
+ attr |= AM_RDO; /* Read only */
+ break;
+ case 's':
+ attr |= AM_SYS; /* System */
+ break;
+ default:
+ err(PSTR("unknown attribute: '%c'"), c);
+ return -1;
+ }
+ }
+ return attr;
+}
+
+static void print_attrib(char *path, FILINFO *f)
+{
+ printf_P(PSTR("%c%c%c%c%c %s%s\n"),
+ (f->fattrib & AM_DIR) ? 'D' : '-',
+ (f->fattrib & AM_RDO) ? 'R' : '-',
+ (f->fattrib & AM_HID) ? 'H' : '-',
+ (f->fattrib & AM_SYS) ? 'S' : '-',
+ (f->fattrib & AM_ARC) ? 'A' : '-',
+ path, f->fname);
+}
+
+command_ret_t do_attrib(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, UNUSED char * const argv[])
+{
+ DIR Dir; /* Directory object */
+ FILINFO Finfo;
+ FRESULT res;
+ BYTE set_mask = 0;
+ BYTE clear_mask = 0;
+
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+
+ for (;;) {
+ int attr;
+ char *arg = *++argv;
+
+ if (!arg)
+ return CMD_RET_USAGE;
+ if (arg[0] != '-' && arg[0] != '+')
+ break;
+ attr = decode_arg(arg);
+ if (attr < 0)
+ return CMD_RET_FAILURE;
+ if (arg[0] == '+')
+ set_mask |= attr;
+ else
+ clear_mask |= attr;
+ }
+
+ do {
+ if (!path_set(&from, *argv)) {
+ /* TODO: error out*/
+ }
+ char *pattern = path_split_pattern(&from);
+ if (*pattern == '\0')
+ pattern = "*";
+ debug_fa("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", pattern ? pattern : "<NULL>");
+ res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
+ debug_fa("==== findfirst %d\n", res);
+ if (res != FR_OK || !Finfo.fname[0]) {
+ path_fix(&from);
+ err(PSTR("'%s%s': No such file or directory"), from.p_path, pattern);
+ } else {
+ do {
+ if (set_mask | clear_mask) {
+ if ((res = f_chmod(Finfo.fname, set_mask, set_mask | clear_mask)) != FR_OK) {
+ path_fix(&from);
+ err(PSTR("'%s%s': %S"), from.p_path, Finfo.fname, rctostr(res));
+ path_unfix(&from);
+ }
+ } else {
+ path_fix(&from);
+ print_attrib(from.p_path, &Finfo);
+ path_unfix(&from);
+ }
+
+ res = f_findnext(&Dir, &Finfo);
+ //debug_fa("==== findnext %d\n", res);
+ } while (res == FR_OK && Finfo.fname[0]);
+ }
+ f_closedir(&Dir);
+ } while (*++argv);
+
+ return command_ret;
+}
+
+command_ret_t do_rm(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ DIR Dir; /* Directory object */
+ FILINFO Finfo;
+ FRESULT res;
+
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+ /* reset getopt() */
+ optind = 0;
+ flags = 0;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("nv"))) != -1) {
+ switch (opt) {
+ case 'n':
+ flags |= N_FLAG;
+ break;
+ case 'v':
+ flags |= V_FLAG;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ err(PSTR("missing operand"));
+ } else {
+ for (int i = 0; i < argc; i++) {
+ if (!path_set(&from, argv[i])) {
+ /* TODO: error out*/
+ }
+ char *pattern = path_split_pattern(&from);
+
+ debug_rm("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", pattern ? pattern : "<NULL>");
+
+ res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
+ debug_rm("==== findfirst %d\n", res);
+
+ if (res != FR_OK || !Finfo.fname[0]) {
+ path_fix(&from);
+ err(PSTR("cannot remove '%s%s': No such file or directory"), from.p_path, pattern);
+ } else {
+ do {
+ if (Finfo.fattrib & AM_DIR) {
+ path_fix(&from);
+ err(PSTR("cannot remove '%s%s': Is a directory"), from.p_path, Finfo.fname);
+ } else {
+ if (!(flags & N_FLAG)) {
+ if ((res = f_unlink(Finfo.fname)) == FR_OK) {
+ if (flags & V_FLAG)
+ path_fix(&from);
+ printf_P(PSTR("removed '%s%s'\n"), from.p_path, Finfo.fname);
+ path_unfix(&from);
+ } else {
+ path_fix(&from);
+ err(PSTR("cannot remove '%s%s': %S"), from.p_path, Finfo.fname, rctostr(res));
+ }
+ } else {
+ path_fix(&from);
+ printf_P(PSTR("not removed '%s%s'\n"), from.p_path, Finfo.fname);
+ path_unfix(&from);
+ }
+ }
+ res = f_findnext(&Dir, &Finfo);
+ //debug_rm("==== findnext %d\n", res);
+ } while (res == FR_OK && Finfo.fname[0]);
+ }
+ f_closedir(&Dir);
+ }
+
+ /* TODO */
+ if (res) {
+ put_rc(res);
+ return CMD_RET_FAILURE;
+ }
+ }
+ return command_ret;
+}
+
+command_ret_t do_rmdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+ return command_ret;
+}
+
+command_ret_t do_mkdir(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+ return command_ret;
+}
+
+
+static void print_dirent(FILINFO *f)
+{
+ printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
+ (f->fattrib & AM_DIR) ? 'D' : '-',
+ (f->fattrib & AM_RDO) ? 'R' : '-',
+ (f->fattrib & AM_HID) ? 'H' : '-',
+ (f->fattrib & AM_SYS) ? 'S' : '-',
+ (f->fattrib & AM_ARC) ? 'A' : '-',
+ (f->fdate >> 9) + 1980, (f->fdate >> 5) & 15, f->fdate & 31,
+ (f->ftime >> 11), (f->ftime >> 5) & 63,
+ f->fsize, f->fname);
+}
+
+/*
+ * ls path - Directory listing
+ *
+ */
+command_ret_t do_ls(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ FATFS *fs;
+ DIR Dir; /* Directory object */
+ FILINFO Finfo;
+ unsigned long p1;
+ unsigned int s1, s2;
+ FRESULT res;
+
+ cmdname = argv[0];
+ command_ret = CMD_RET_SUCCESS;
+
+ path_init();
+ if (argc > 1)
+ if (!path_set(&from, argv[1])) {
+ /* TODO: error out*/
+ }
+
+#if 0
+ char *pattern = path_basename_pattern(&from);
+#else
+ char *pattern = path_split_pattern(&from);
+ if (*pattern == '\0')
+ pattern = "*";
+#endif
+ debug_ls("==== path: '%s', pattern: '%s'\n", from.p_path ? from.p_path : "<NULL>", pattern ? pattern : "<NULL>");
+
+ p1 = s1 = s2 = 0;
+ res = f_findfirst(&Dir, &Finfo, from.p_path, pattern); /* Start to search for files */
+ if (res != FR_OK || !Finfo.fname[0]) {
+ path_fix(&from);
+ err(PSTR("'%s%s': No such file or directory"), from.p_path, pattern);
+ } else {
+ do {
+ if (Finfo.fattrib & AM_DIR) {
+ s2++;
+ } else {
+ s1++; p1 += Finfo.fsize;
+ }
+ print_dirent(&Finfo);
+ if (check_abort())
+ break;
+ res = f_findnext(&Dir, &Finfo);
+ } while (res == FR_OK && Finfo.fname[0]);
+ }
+ f_closedir(&Dir);
+
+ if (res == FR_OK && command_ret == CMD_RET_SUCCESS) {
+ printf_P(PSTR("%4u File(s),%10lu bytes total\n%4u Dir(s)"), s1, p1, s2);
+ if (f_getfree(from.p_path, (DWORD*)&p1, &fs) == FR_OK)
+ printf_P(PSTR(", %10luK bytes free\n"), p1 * fs->csize / 2);
+ }
+
+ if (res && command_ret == CMD_RET_SUCCESS) {
+ put_rc(res);
+ return CMD_RET_FAILURE;
+ }
+
+ return command_ret;
+}
+
+/*
+ * tst path - for debugging: test access with different functions
+ *
+ */
+command_ret_t do_tst(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+ DIR Dir; /* Directory object */
+ FILINFO Finfo;
+ FRESULT res = FR_OK;
+ char *path = "";
+ char *pattern = "*";
+
+ printf_P(PSTR("sizeof DIR: %u, sizeof FIL: %u\n"), sizeof (DIR), sizeof (FILINFO));
+
+ char * buf = (char *) malloc(BUFFER_SIZE);
+ if (buf == NULL) {
+ printf_P(PSTR("tst: Out of Memory!\n"));
+ return CMD_RET_FAILURE;
+ }
+ res = f_getcwd(buf, BUFFER_SIZE); /* Get current directory path */
+
+ if (!res) {
+ printf_P(PSTR("cwd: '%s'\n"), buf);
+ }
+ free(buf);
+ if (res) {
+ put_rc(res);
+ return CMD_RET_FAILURE;
+ }
+
+ if (argc > 1)
+ path = argv[1];
+ if (argc > 2)
+ pattern = argv[2];
+
+ printf_P(PSTR("arg: '%s' '%s'\n"), path, pattern);
+ printf_P(PSTR("==== f_stat: "));
+ res = f_stat(path, &Finfo);
+ put_rc(res);
+ if (res == FR_OK) {
+ print_dirent(&Finfo);
+ }
+
+ printf_P(PSTR("==== f_findfirst: "));
+ res = f_findfirst(&Dir, &Finfo, path, pattern); /* Start to search for files */
+ put_rc(res);
+ if (res == FR_OK) {
+ print_dirent(&Finfo);
+ }
+ f_closedir(&Dir);
+
+ printf_P(PSTR("==== f_opendir: "));
+ res = f_opendir(&Dir, path);
+ put_rc(res);
+ f_closedir(&Dir);
+
+ return CMD_RET_SUCCESS;
+}
+
+/******************************************************************************/
+
+static void
+setfile(FILINFO *fs)
+{
+ FRESULT fr;
+
+ fr = f_utime(to.p_path, fs);
+ if (fr != FR_OK)
+ err(PSTR("f_utime: %s: %S"), to.p_path, rctostr(fr));
+ fr = f_chmod(to.p_path, fs->fattrib, AM_RDO|AM_ARC|AM_SYS|AM_HID);
+ if (fr != FR_OK)
+ err(PSTR("f_chmod: %s: %S"), to.p_path, rctostr(fr));
+
+}
+
+void copy_file(FILINFO *fs, uint_fast8_t dne)
+{
+ FIL from_fd, to_fd;
+ UINT rcount, wcount;
+ FRESULT fr;
+ BYTE open_mode;
+
+ if (blockbuf == NULL) {
+ blockbuf_size = get_freemem() / 512 * 512;
+ if (blockbuf_size != 0)
+ blockbuf = (uint8_t *) malloc(blockbuf_size);
+ if (blockbuf == NULL) {
+ err(PSTR("Not enough memory!\n"));
+ return;
+ }
+ }
+
+debug_cp("==== copy_file(): dne: %u, blockbuf_size: %d, freemem: %u\n", dne, blockbuf_size, get_freemem());
+debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
+
+
+ if ((fr = f_open(&from_fd, from.p_path, FA_READ)) != FR_OK) {
+ err(PSTR("%s: %S"), from.p_path, rctostr(fr));
+ return;
+ }
+
+ /*
+ * If the file exists and we're interactive, verify with the user.
+ */
+ if (!dne) {
+ if (flags & N_FLAG) {
+ if (flags & V_FLAG)
+ printf_P(PSTR("%s not overwritten\n"), to.p_path);
+ f_close(&from_fd);
+ return;
+ } if (flags & I_FLAG) {
+ printf_P(PSTR("overwrite '%s'? "), to.p_path);
+ if (!confirm_yes()) {
+ f_close(&from_fd);
+ return;
+ }
+ }
+ if (flags & F_FLAG) {
+ /* Remove existing destination file name create a new file. */
+ f_chmod(to.p_path,0, AM_RDO);
+ f_unlink(to.p_path);
+ open_mode = FA_WRITE|FA_CREATE_NEW;
+ } else {
+ /* Overwrite existing destination file name. */
+ open_mode = FA_WRITE|FA_CREATE_ALWAYS;
+ }
+ } else {
+ open_mode = FA_WRITE|FA_CREATE_NEW;
+ }
+ fr = f_open(&to_fd, to.p_path, open_mode);
+
+ if (fr != FR_OK) {
+ err(PSTR("%s: %S"), to.p_path, rctostr(fr));
+ f_close(&from_fd);
+ return;
+ }
+
+ while ((fr = f_read(&from_fd, blockbuf, blockbuf_size, &rcount)) == FR_OK &&
+ rcount > 0) {
+ fr = f_write(&to_fd, blockbuf, rcount, &wcount);
+ if (fr || wcount < rcount) {
+ err(PSTR("%s: %S"), to.p_path, rctostr(fr));
+ break;
+ }
+ }
+ if (fr != FR_OK)
+ err(PSTR("%s: S"), from.p_path, rctostr(fr));
+
+ f_close(&from_fd);
+ if ((fr = f_close(&to_fd)) != FR_OK)
+ err(PSTR("%s: %S"), to.p_path, rctostr(fr));
+
+ if (flags & P_FLAG)
+ setfile(fs);
+}
+
+static void copy();
+
+static void copy_dir(void)
+{
+ DIR Dir; /* Directory object */
+ FILINFO Finfo;
+ char *old_from, *old_to;
+ FRESULT res;
+ char *pattern = {"*"};
+
+debug_cp("==== copy_dir(): freemem: %u\n", get_freemem());
+debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
+
+#if 0
+
+ printf_P(PSTR("directory copy not supported, ommitting dir '%s'\n"),
+ from->p_path);
+ command_ret = CMD_RET_FAILURE;
+
+#else
+
+ for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
+ res == FR_OK && Finfo.fname[0];
+ res = f_findnext(&Dir, &Finfo)) {
+
+ if (!(Finfo.fattrib & AM_DIR) &&
+ (old_from = path_append(&from, Finfo.fname))) {
+ if ((old_to = path_append(&to, Finfo.fname))) {
+ copy();
+ path_restore(&to, old_to);
+ }
+ path_restore(&from, old_from);
+ }
+ }
+
+ for (res = f_findfirst(&Dir, &Finfo, from.p_path, pattern);
+ res == FR_OK && Finfo.fname[0];
+ res = f_findnext(&Dir, &Finfo)) {
+
+ if ((Finfo.fattrib & AM_DIR) &&
+ (old_from = path_append(&from, Finfo.fname))) {
+ if ((old_to = path_append(&to, Finfo.fname))) {
+ copy();
+ path_restore(&to, old_to);
+ }
+ path_restore(&from, old_from);
+ }
+ }
+}
+#endif
+
+/*
+ * copy file or directory at "from" to "to".
+ */
+static void copy()
+{
+ FILINFO from_stat, to_stat;
+ uint_fast8_t dne;
+ FRESULT fr;
+
+debug_cp("==== copy(); freemem: %u\n", get_freemem());
+debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
+
+ fr = f_stat(from.p_path, &from_stat);
+ if (fr != FR_OK) {
+ err(PSTR("%s: %S"), from.p_path, rctostr(fr));
+ return;
+ }
+
+ /* not an error, but need to remember it happened */
+ if (f_stat(to.p_path, &to_stat) != FR_OK)
+ dne = 1;
+ else {
+ if (strcmp(to.p_path, from.p_path) == 0) {
+ (void)printf_P(PSTR("%s and %s are identical (not copied).\n"),
+ to.p_path, from.p_path);
+ command_ret = CMD_RET_FAILURE;
+ return;
+ }
+ dne = 0;
+ }
+
+ if(from_stat.fattrib & AM_DIR) {
+ if (!(flags & R_FLAG)) {
+ (void)printf_P(PSTR("-r not specified; ommitting dir '%s'\n"),
+ from.p_path);
+ command_ret = CMD_RET_FAILURE;
+ return;
+ }
+ if (dne) {
+ /*
+ * If the directory doesn't exist, create the new one.
+ */
+ if ((fr = f_mkdir(to.p_path)) != FR_OK) {
+ err(PSTR("%s: %S"), to.p_path, rctostr(fr));
+ return;
+ }
+ } else if (!(to_stat.fattrib & AM_DIR)) {
+ (void)printf_P(PSTR("%s: not a directory.\n"), to.p_path);
+ return;
+ }
+ copy_dir();
+ if (flags & P_FLAG)
+ setfile(&from_stat);
+
+ return;
+ }
+ copy_file(&from_stat, dne);
+}
+
+command_ret_t do_cp(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[])
+{
+
+ FRESULT fr; /* Return value */
+ //DIR dj; /* Directory search object */
+ FILINFO to_stat; /* File information */
+ char *old_to;
+
+
+ cmdname = argv[0];
+ uint8_t tflags = 0;
+ command_ret = CMD_RET_SUCCESS;
+
+ /* reset getopt() */
+ optind = 0;
+
+ int opt;
+ while ((opt = getopt(argc, argv, PSTR("finprv"))) != -1) {
+ switch (opt) {
+ case 'f':
+ tflags &= ~(I_FLAG | N_FLAG);
+ tflags |= F_FLAG;
+ break;
+ case 'i':
+ tflags &= ~(F_FLAG | N_FLAG);
+ tflags |= I_FLAG;
+ break;
+ case 'n':
+ tflags &= ~(F_FLAG | I_FLAG);
+ tflags |= N_FLAG;
+ break;
+ case 'p':
+ tflags |= P_FLAG;
+ break;
+ case 'r':
+ tflags |= R_FLAG;
+ break;
+ case 'v':
+ tflags |= V_FLAG;
+ break;
+ default:
+ return CMD_RET_USAGE;
+ break;
+ }
+ }
+ flags = tflags;
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ path_init();
+
+ /* last argument is destination */
+ if (!path_set(&to, argv[--argc]))
+ goto cleanup;
+
+ /*
+ * Cp has two distinct cases:
+ *
+ * % cp [-rip] source target
+ * % cp [-rip] source1 ... directory
+ *
+ * In both cases, source can be either a file or a directory.
+ *
+ * In (1), the target becomes a copy of the source. That is, if the
+ * source is a file, the target will be a file, and likewise for
+ * directories.
+ *
+ * In (2), the real target is not directory, but "directory/source".
+ */
+
+ fr = f_stat(to.p_path, &to_stat);
+debug_cp("==== main, stat to: fr: %d, attr: %02x, flags:%02x, freemem: %u\n",
+ fr, to_stat.fattrib, flags, get_freemem());
+debug_cp(" from:'%s' to:'%s'\n", from.p_path, to.p_path);
+
+ if (fr != FR_OK && fr != FR_NO_FILE && fr != FR_NO_PATH) {
+ err(PSTR("Test1: %s: %S"), to.p_path, rctostr(fr));
+ command_ret = CMD_RET_FAILURE;
+ goto cleanup;
+ }
+ if (!(fr == FR_OK && (to_stat.fattrib & AM_DIR))) {
+ /*
+ * Case (1). Target is not a directory.
+ */
+ if (argc > 1) {
+ err(PSTR("target '%s' is not a directory"), to.p_path);
+ //command_ret = CMD_RET_USAGE;
+ goto cleanup;
+ }
+ if (!path_set(&from, *argv)) {
+ command_ret = CMD_RET_FAILURE;
+ goto cleanup;
+ }
+ copy();
+ }
+ else {
+ /*
+ * Case (2). Target is a directory.
+ */
+ for (;; ++argv) {
+ if (!path_set(&from, *argv))
+ continue;
+ if (!(old_to = path_append(&to, path_basename(&from))))
+ continue;
+ copy();
+ if (!--argc)
+ break;
+ path_restore(&to, old_to);
+ }
+ }
+
+cleanup:
+ free(blockbuf);
+ blockbuf = NULL;
+ blockbuf_size = 0;
+
+ return command_ret;
+}
+
+#if 0
+ if (flags & V_FLAG)
+ printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to.p_path)));
+
+#endif
+
+
+/******************************************************************************/
+
+/*
+ * Work register for stat command
+ */