+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);
+}
+