/* TODO: detect_ramsize() should be moved to z80-if.c */
#define MAX_MEMORY CONFIG_SYS_RAMSIZE_MAX
#define BUFFER_SIZE 512
+#define MAXBSIZE 512 /* TODO */
+
/*
* Multible (fat) partitions per physical drive are not supported,
FATFS FatFs0;
FATFS FatFs1;
+command_ret_t command_ret;
+
void setup_fatfs(void)
{
f_mount(&FatFs0, "0:", 0);
}
+static const FLASH char * const FLASH rc_strings[] = {
+ FSTR("OK"),
+ FSTR("disk error"),
+ FSTR("internal error"),
+ FSTR("not ready"),
+ FSTR("no file"),
+ FSTR("no path"),
+ FSTR("invalid name"),
+ FSTR("denied"),
+ FSTR("exist"),
+ FSTR("invalid object"),
+ FSTR("write protected"),
+ FSTR("invalid drive"),
+ FSTR("not enabled"),
+ FSTR("no file system"),
+ FSTR("mkfs aborted"),
+ FSTR("timeout"),
+ FSTR("locked"),
+ FSTR("not enough core"),
+ FSTR("too many open files"),
+ FSTR("invalid parameter")
+ };
+
static const FLASH char * const FLASH rc_names[] = {
FSTR("OK"),
FSTR("DISK_ERR"),
#endif
}
+const FLASH char * rctostr(FRESULT rc)
+{
+ return rc < ARRAY_SIZE(rc_strings) ? rc_strings[rc] : PSTR(" Unknown Error");
+}
+
+void err(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+// (void)fprintf(stderr, "%s: ", progname);
+ (void)vfprintf_P(stdout, fmt, ap);
+ va_end(ap);
+ (void)printf_P(PSTR("\n"));
+ command_ret = CMD_RET_FAILURE;
+}
+
+
static void swirl(void)
{
return CMD_RET_SUCCESS;
}
-#define MAX_PATHLEN 255
+#define MAX_PATHLEN CONFIG_SYS_MAX_PATHLEN
/*
* Remove trailing slashes,
p = q;
}
- char *pattern = NULL;
+ char *pattern;
if (strpbrk_P(p, PSTR("*?")) ||
(f_stat(path, &Finfo) == FR_OK && !(Finfo.fattrib & AM_DIR))) {
pattern = strdup(p);
*p = '\0';
- }
+ } else
+ pattern = strdup("*");
strip_trailing_slash_relpath(path);
-printf_P(PSTR("*: |%s| |%s|\n"), path ? path : "<NULL>", pattern ? pattern : "<NULL>");
+//printf_P(PSTR("*: |%s| |%s|\n"), path ? path : "<NULL>", pattern ? pattern : "<NULL>");
p1 = s1 = s2 = 0;
- res = f_findfirst(&Dir, &Finfo, path, pattern ? pattern : "*"); /* Start to search for files */
+ res = f_findfirst(&Dir, &Finfo, path, pattern); /* Start to search for files */
while (res == FR_OK && Finfo.fname[0]) {
if (Finfo.fattrib & AM_DIR) {
s2++;
/******************************************************************************/
+/*
+ * These functions manipulate paths in PATH_T structures.
+ *
+ * They eliminate multiple slashes in paths when they notice them,
+ * and keep the path non-slash terminated.
+ *
+ * Both path_set() and path_append() return 0 if the requested name
+ * would be too long.
+ */
+
typedef struct {
char *p_end; /* pointer to NULL at end of path */
char p_path[MAX_PATHLEN + 1]; /* pointer to the start of a path */
} PATH_T;
-static inline void strip_trailing_slash(PATH_T *p)
+static void strip_trailing_slash(PATH_T *p)
{
while (p->p_end > p->p_path && p->p_end[-1] == '/')
- *--p->p_end = 0;
+ *--p->p_end = '\0';
}
/*
path_set(PATH_T *p, char *string)
{
if (strlen(string) > MAX_PATHLEN) {
- //err("%s: name too long", string);
- return(0);
+ err(PSTR("%s: name too long"), string);
+ return 0;
}
(void)strcpy(p->p_path, string);
}
strip_trailing_slash(p);
- return(1);
+ return 1;
}
+/*
+ * Append specified string to path, inserting '/' if necessary. Return a
+ * pointer to the old end of path for restoration.
+ */
+char *
+path_append(PATH_T *p, char *name, int len)
+{
+ char *old;
+
+ old = p->p_end;
+ if (len == -1)
+ len = strlen(name);
+
+ /* The "+ 1" accounts for the '/' between old path and name. */
+ if ((len + p->p_end - p->p_path + 1) > MAX_PATHLEN) {
+ err(PSTR("%s/%s: name too long"), p->p_path, name);
+ return(0);
+ }
+
+ /*
+ * This code should always be executed, since paths shouldn't
+ * end in '/'.
+ */
+ if (p->p_end[-1] != '/') {
+ *p->p_end++ = '/';
+ *p->p_end = 0;
+ }
+
+ (void)strncat(p->p_end, name, len);
+ p->p_end += len;
+ *p->p_end = 0;
+
+ strip_trailing_slash(p);
+ return(old);
+}
+
+/*
+ * Restore path to previous value. (As returned by path_append.)
+ */
+void
+path_restore(p, old)
+ PATH_T *p;
+ char *old;
+{
+ p->p_end = old;
+ *p->p_end = 0;
+}
+
+/*
+ * Return basename of path.
+ */
+char *
+path_basename(p)
+ PATH_T *p;
+{
+ char *basename;
+
+ basename = strrchr(p->p_path, '/');
+ return(basename ? basename + 1 : p->p_path);
+}
+
+
+uint8_t flags = 0;
+PATH_T *from;
+PATH_T *to;
+
#define R_FLAG (1<<0)
#define I_FLAG (1<<1)
#define N_FLAG (1<<2)
#define P_FLAG (1<<4)
#define V_FLAG (1<<5)
+static void
+setfile(FILINFO *fs, FIL *fd)
+{
+ (void) fs;(void) fd;
+#if 0 /* TODO: */
+ static struct timeval tv[2];
+
+ fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO;
+
+ tv[0].tv_sec = fs->st_atime;
+ tv[1].tv_sec = fs->st_mtime;
+ if (utimes(to->p_path, tv))
+ err(PSTR("utimes: %s: %s"), to->p_path, strerror(errno));
+#endif
+}
+
+void copy_file(FILINFO *fs, uint_fast8_t dne)
+{
+ static char buf[MAXBSIZE];
+ FIL from_fd, to_fd;
+ UINT rcount, wcount;
+ //FILINFO to_stat;
+ //char *p;
+ FRESULT fr;
+
+debug("==== copy_file(): dne: %u\n", dne);
+debug("==== 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 the file DNE, set the mode to be the from file, minus setuid
+ * bits, modified by the umask; arguably wrong, but it makes copying
+ * executables work right and it's been that way forever. (The
+ * other choice is 666 or'ed with the execute bits on the from file
+ * modified by the umask.)
+ */
+ if (!dne) {
+ if (flags & I_FLAG) {
+ int checkch, ch;
+
+ printf_P(PSTR("overwrite %s? "), to->p_path);
+ checkch = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (checkch != 'y') {
+ f_close(&from_fd);
+ return;
+ }
+ }
+ fr = f_open(&to_fd, to->p_path, FA_WRITE|FA_CREATE_ALWAYS);
+ } else
+ fr = f_open(&to_fd, to->p_path, FA_WRITE|FA_CREATE_NEW);
+
+ if (fr != FR_OK) {
+ err(PSTR("%s: %S"), to->p_path, rctostr(fr));
+ f_close(&from_fd);
+ return;
+ }
+
+ while ((fr = f_read(&from_fd, buf, MAXBSIZE, &rcount)) == FR_OK &&
+ rcount > 0) {
+ fr = f_write(&to_fd, buf, 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));
+
+ if (flags & P_FLAG)
+ setfile(fs, &to_fd);
+
+ f_close(&from_fd);
+ if ((fr = f_close(&to_fd)) != FR_OK)
+ err(PSTR("%s: %S"), to->p_path, rctostr(fr));
+}
+
+#if 1
+static void copy_dir(void)
+{
+ printf(PSTR("directory copy not supported, ommitting dir '%s'\n"),
+ from->p_path);
+}
+#else
+static void copy_dir(void)
+{
+ FILINFO from_stat;
+ struct dirent *dp, **dir_list;
+ int dir_cnt, i;
+ char *old_from, *old_to;
+
+ dir_cnt = scandir(from->p_path, &dir_list, NULL, NULL);
+ if (dir_cnt == -1) {
+ (void)fprintf(stderr, "%s: can't read directory %s.\n",
+ progname, from->p_path);
+ command_ret = CMD_RET_FAILURE;
+ }
+
+ /*
+ * Instead of handling directory entries in the order they appear
+ * on disk, do non-directory files before directory files.
+ * There are two reasons to do directories last. The first is
+ * efficiency. Files tend to be in the same cylinder group as
+ * their parent, whereas directories tend not to be. Copying files
+ * all at once reduces seeking. Second, deeply nested tree's
+ * could use up all the file descriptors if we didn't close one
+ * directory before recursivly starting on the next.
+ */
+ /* copy files */
+ for (i = 0; i < dir_cnt; ++i) {
+ dp = dir_list[i];
+ if (dp->d_namlen <= 2 && dp->d_name[0] == '.'
+ && (dp->d_name[1] == NULL || dp->d_name[1] == '.'))
+ goto done;
+ if (!(old_from =
+ path_append(&from, dp->d_name, (int)dp->d_namlen)))
+ goto done;
+
+ if (statfcn(from->p_path, &from_stat) < 0) {
+ err(PSTR("%s: %s"), dp->d_name, strerror(errno));
+ path_restore(&from, old_from);
+ goto done;
+ }
+ if (S_ISDIR(from_stat.st_mode)) {
+ path_restore(&from, old_from);
+ continue;
+ }
+ if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) {
+ copy();
+ path_restore(&to, old_to);
+ }
+ path_restore(&from, old_from);
+done: dir_list[i] = NULL;
+ free(dp);
+ }
+
+ /* copy directories */
+ for (i = 0; i < dir_cnt; ++i) {
+ dp = dir_list[i];
+ if (!dp)
+ continue;
+ if (!(old_from =
+ path_append(&from, dp->d_name, (int)dp->d_namlen))) {
+ free(dp);
+ continue;
+ }
+ if (!(old_to =
+ path_append(&to, dp->d_name, (int)dp->d_namlen))) {
+ free(dp);
+ path_restore(&from, old_from);
+ continue;
+ }
+ copy();
+ free(dp);
+ path_restore(&from, old_from);
+ path_restore(&to, old_to);
+ }
+ free(dir_list);
+}
+#endif
+
+/*
+ * copy file or directory at "from" to "to".
+ */
+static void copy()
+{
+ FILINFO from_stat, to_stat;
+ uint_fast8_t dne;
+ FRESULT fr;
+
+debug("==== copy()\n");
+debug("==== from:'%s' to:'%s'\n", from->p_path, to->p_path);
+
+ fr = f_stat(from->p_path, &from_stat);
+ if (fr) {
+ 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(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 not -p and directory didn't exist, set it to be the
+ * same as the from directory, umodified by the umask;
+ * arguably wrong, but it's been that way forever.
+ */
+#if 0
+ if (flags & P_FLAG)
+ setfile(&from_stat, 0);
+ else if (dne)
+ (void)chmod(to->p_path, from_stat.st_mode);
+#endif
+ 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 fno; /* File information */
+ FRESULT fr; /* Return value */
+ //DIR dj; /* Directory search object */
+ FILINFO to_stat; /* File information */
+ char *old_to;
- uint8_t flags = 0;
+ command_ret = CMD_RET_SUCCESS;
/* reset getopt() */
optind = 0;
if (argc < 2)
return CMD_RET_USAGE;
-#if 0
+ from = (PATH_T *) malloc(sizeof(PATH_T));
+ to = (PATH_T *) malloc(sizeof(PATH_T));
+ if (from == NULL || to == NULL) {
+ printf_P(PSTR("cp: Out of Memory!\n"));
+ command_ret = CMD_RET_FAILURE;
+ goto cleanup;
+ }
+ from->p_end = from->p_path; *from->p_path = '\0';
+ to->p_end = to->p_path; *to->p_path = '\0';
+
/* consume last argument first. */
- if (!path_set(&to, argv[--argc]))
- exit(1);
+ if (!path_set(to, argv[--argc])) {
+ command_ret = CMD_RET_FAILURE;
+ 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);
+ if (fr != FR_OK && fr != FR_NO_FILE) {
+ err(PSTR("%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) {
+ 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), -1)))
+ continue;
+ copy();
+ if (!--argc)
+ break;
+ path_restore(&to, old_to);
+ }
+ }
+cleanup:
+ free(to);
+ free(from);
+ return command_ret;
+}
+
+#if 0
if (flags & V_FLAG)
- printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to.p_path)));
+ printf_P((PSTR("%s %s -> %s\n", badcp ? : "ERR:" : " ", curr->fts_path, to->p_path)));
#endif
-/* Search a directory for objects and display it */
-
- fr = f_findfirst(&dj, &fno, argv[1], argv[2]); /* Start to search for files */
- while (fr == FR_OK && fno.fname[0]) { /* Repeat while an item is found */
- printf_P(PSTR("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu %s\n"),
- (fno.fattrib & AM_DIR) ? 'D' : '-',
- (fno.fattrib & AM_RDO) ? 'R' : '-',
- (fno.fattrib & AM_HID) ? 'H' : '-',
- (fno.fattrib & AM_SYS) ? 'S' : '-',
- (fno.fattrib & AM_ARC) ? 'A' : '-',
- (fno.fdate >> 9) + 1980, (fno.fdate >> 5) & 15, fno.fdate & 31,
- (fno.ftime >> 11), (fno.ftime >> 5) & 63,
- fno.fsize, fno.fname);
- fr = f_findnext(&dj, &fno); /* Search for next item */
- }
- f_closedir(&dj);
- return CMD_RET_SUCCESS;
-}
/******************************************************************************/
-/* Work register for fs command */
+/*
+ * Work register for stat command
+ */
struct stat_dat_s {
DWORD AccSize;
WORD AccFiles, AccDirs;