HelenOS sources
This source file includes following definitions.
- get_type
- strtoint
- get_last_path_component
- merge_paths
- get_user_decision
- do_copy
- copy_file
- help_cmd_cp
- cmd_cp
#include <errno.h>
#include <str_error.h>
#include <stdio.h>
#include <stdlib.h>
#include <io/console.h>
#include <io/keycode.h>
#include <getopt.h>
#include <str.h>
#include <vfs/vfs.h>
#include <dirent.h>
#include "config.h"
#include "util.h"
#include "errors.h"
#include "entry.h"
#include "cp.h"
#include "cmds.h"
#define CP_VERSION "0.0.1"
#define CP_DEFAULT_BUFLEN 1024
static const char *cmdname = "cp";
static console_ctrl_t *con;
static struct option const long_options[] = {
{ "buffer", required_argument, 0, 'b' },
{ "force", no_argument, 0, 'f' },
{ "interactive", no_argument, 0, 'i' },
{ "recursive", no_argument, 0, 'r' },
{ "help", no_argument, 0, 'h' },
{ "version", no_argument, 0, 'v' },
{ "verbose", no_argument, 0, 'V' },
{ 0, 0, 0, 0 }
};
typedef enum {
TYPE_NONE,
TYPE_FILE,
TYPE_DIR
} dentry_type_t;
static int copy_file(const char *src, const char *dest,
size_t blen, int vb);
static dentry_type_t get_type(const char *path)
{
vfs_stat_t s;
errno_t r = vfs_stat_path(path, &s);
if (r != EOK)
return TYPE_NONE;
else if (s.is_directory)
return TYPE_DIR;
else if (s.is_file)
return TYPE_FILE;
return TYPE_NONE;
}
static int strtoint(const char *s1)
{
long t1;
if (-1 == (t1 = strtol(s1, (char **) NULL, 10)))
return -1;
if (t1 <= 0)
return -1;
return (int) t1;
}
static char *get_last_path_component(char *path)
{
char *ptr;
ptr = str_rchr(path, '/');
if (!ptr)
return path;
else
return ptr + 1;
}
static void merge_paths(char *path1, size_t path1_size, char *path2)
{
const char *delim = "/";
str_rtrim(path1, '/');
str_append(path1, path1_size, delim);
str_append(path1, path1_size, path2);
}
static bool get_user_decision(bool bdefault, const char *message, ...)
{
va_list args;
errno_t rc;
va_start(args, message);
vprintf(message, args);
va_end(args);
while (true) {
cons_event_t ev;
console_flush(con);
rc = console_get_event(con, &ev);
if (rc != EOK)
exit(1);
if (ev.type != CEV_KEY || ev.ev.key.type != KEY_PRESS ||
(ev.ev.key.mods & (KM_CTRL | KM_ALT)) != 0) {
continue;
}
switch (ev.ev.key.key) {
case KC_Y:
printf("y\n");
return true;
case KC_N:
printf("n\n");
return false;
case KC_ENTER:
printf("%c\n", bdefault ? 'Y' : 'N');
return bdefault;
default:
break;
}
}
}
static errno_t do_copy(const char *src, const char *dest,
size_t blen, int vb, int recursive, int force, int interactive)
{
errno_t rc = EOK;
char dest_path[PATH_MAX];
char src_path[PATH_MAX];
DIR *dir = NULL;
struct dirent *dp;
dentry_type_t src_type = get_type(src);
dentry_type_t dest_type = get_type(dest);
const size_t src_len = str_size(src);
if (src_type == TYPE_FILE) {
char *src_fname;
str_cpy(src_path, src_len + 1, src);
str_rtrim(src_path, '/');
src_fname = get_last_path_component(src_path);
str_cpy(dest_path, PATH_MAX, dest);
if (dest_type == TYPE_DIR) {
merge_paths(dest_path, PATH_MAX, src_fname);
dest_type = get_type(dest_path);
} else if (dest_type == TYPE_NONE) {
if (dest_path[str_size(dest_path) - 1] == '/') {
printf("The dest directory %s does not exists",
dest_path);
rc = ENOENT;
goto exit;
}
}
if (dest_type == TYPE_DIR) {
printf("Cannot overwrite existing directory %s\n",
dest_path);
rc = EEXIST;
goto exit;
} else if (dest_type == TYPE_FILE) {
if (force && !interactive) {
rc = vfs_unlink_path(dest_path);
if (rc != EOK) {
printf("Unable to remove %s\n",
dest_path);
goto exit;
}
} else if (!force && interactive) {
bool overwrite = get_user_decision(false,
"File already exists: %s. Overwrite? [y/N]: ",
dest_path);
if (overwrite) {
printf("Overwriting file: %s\n", dest_path);
rc = vfs_unlink_path(dest_path);
if (rc != EOK) {
printf("Unable to remove %s\n", dest_path);
goto exit;
}
} else {
printf("Not overwriting file: %s\n", dest_path);
rc = EOK;
goto exit;
}
} else {
printf("File already exists: %s\n", dest_path);
rc = EEXIST;
goto exit;
}
}
if (copy_file(src, dest_path, blen, vb) < 0) {
rc = EIO;
}
} else if (src_type == TYPE_DIR) {
if (!recursive) {
printf("Cannot copy the %s directory without the "
"-r option\n", src);
rc = EINVAL;
goto exit;
} else if (dest_type == TYPE_FILE) {
printf("Cannot overwrite a file with a directory\n");
rc = EEXIST;
goto exit;
}
char *src_dirname;
str_cpy(src_path, src_len + 1, src);
str_rtrim(src_path, '/');
src_dirname = get_last_path_component(src_path);
str_cpy(dest_path, PATH_MAX, dest);
switch (dest_type) {
case TYPE_DIR:
if (str_cmp(src_dirname, "..") &&
str_cmp(src_dirname, ".")) {
merge_paths(dest_path, PATH_MAX, src_dirname);
rc = vfs_link_path(dest_path, KIND_DIRECTORY,
NULL);
if (rc != EOK) {
printf("Unable to create "
"dest directory %s\n", dest_path);
goto exit;
}
}
break;
default:
case TYPE_NONE:
rc = vfs_link_path(dest_path, KIND_DIRECTORY, NULL);
if (rc != EOK) {
printf("Unable to create "
"dest directory %s\n", dest_path);
goto exit;
}
break;
}
dir = opendir(src);
if (!dir) {
printf("Unable to open src %s directory\n", src);
rc = ENOENT;
goto exit;
}
while ((dp = readdir(dir))) {
vfs_stat_t src_s;
vfs_stat_t dest_s;
char src_dent[PATH_MAX];
char dest_dent[PATH_MAX];
str_cpy(src_dent, PATH_MAX, src);
merge_paths(src_dent, PATH_MAX, dp->d_name);
str_cpy(dest_dent, PATH_MAX, dest_path);
merge_paths(dest_dent, PATH_MAX, dp->d_name);
vfs_stat_path(src_dent, &src_s);
vfs_stat_path(dest_path, &dest_s);
if (dest_s.index == src_s.index &&
dest_s.fs_handle == src_s.fs_handle) {
printf("Cannot copy a directory "
"into itself\n");
rc = EEXIST;
goto exit;
}
if (vb)
printf("copy %s %s\n", src_dent, dest_dent);
rc = do_copy(src_dent, dest_dent, blen, vb, recursive,
force, interactive);
if (rc != EOK)
goto exit;
}
} else
printf("Unable to open source file %s\n", src);
exit:
if (dir)
closedir(dir);
return rc;
}
static int copy_file(const char *src, const char *dest,
size_t blen, int vb)
{
int fd1, fd2;
size_t rbytes, wbytes;
errno_t rc;
off64_t total;
char *buff = NULL;
aoff64_t posr = 0, posw = 0;
vfs_stat_t st;
if (vb)
printf("Copying %s to %s\n", src, dest);
rc = vfs_lookup_open(src, WALK_REGULAR, MODE_READ, &fd1);
if (rc != EOK) {
printf("Unable to open source file %s\n", src);
return -1;
}
rc = vfs_lookup_open(dest, WALK_REGULAR | WALK_MAY_CREATE, MODE_WRITE, &fd2);
if (rc != EOK) {
printf("Unable to open destination file %s\n", dest);
vfs_put(fd1);
return -1;
}
if (vfs_stat(fd1, &st) != EOK) {
printf("Unable to fstat %d\n", fd1);
vfs_put(fd1);
vfs_put(fd2);
return -1;
}
total = st.size;
if (vb)
printf("%" PRIu64 " bytes to copy\n", total);
if (NULL == (buff = (char *) malloc(blen))) {
printf("Unable to allocate enough memory to read %s\n",
src);
rc = ENOMEM;
goto out;
}
while ((rc = vfs_read(fd1, &posr, buff, blen, &rbytes)) == EOK &&
rbytes > 0) {
if ((rc = vfs_write(fd2, &posw, buff, rbytes, &wbytes)) != EOK)
break;
}
if (rc != EOK) {
printf("\nError copying %s: %s\n", src, str_error(rc));
return -1;
}
out:
vfs_put(fd1);
vfs_put(fd2);
if (buff)
free(buff);
if (rc != EOK) {
return -1;
} else {
return 0;
}
}
void help_cmd_cp(unsigned int level)
{
static char helpfmt[] =
"Usage: %s [options] <source> <dest>\n"
"Options:\n"
" -h, --help A short option summary\n"
" -v, --version Print version information and exit\n"
" -V, --verbose Be annoyingly noisy about what's being done\n"
" -f, --force Do not complain when <dest> exists (overrides a previous -i)\n"
" -i, --interactive Ask what to do when <dest> exists (overrides a previous -f)\n"
" -r, --recursive Copy entire directories\n"
" -b, --buffer ## Set the read buffer size to ##\n";
if (level == HELP_SHORT) {
printf("`%s' copies files and directories\n", cmdname);
} else {
help_cmd_cp(HELP_SHORT);
printf(helpfmt, cmdname);
}
return;
}
int cmd_cp(char **argv)
{
unsigned int argc, verbose = 0;
int buffer = 0, recursive = 0;
int force = 0, interactive = 0;
int c, opt_ind;
errno_t ret;
con = console_init(stdin, stdout);
argc = cli_count_args(argv);
c = 0;
optreset = 1;
optind = 0;
opt_ind = 0;
while (c != -1) {
c = getopt_long(argc, argv, "hvVfirb:", long_options, &opt_ind);
switch (c) {
case 'h':
help_cmd_cp(1);
return CMD_SUCCESS;
case 'v':
printf("%s\n", CP_VERSION);
return CMD_SUCCESS;
case 'V':
verbose = 1;
break;
case 'f':
interactive = 0;
force = 1;
break;
case 'i':
force = 0;
interactive = 1;
break;
case 'r':
recursive = 1;
break;
case 'b':
if (-1 == (buffer = strtoint(optarg))) {
printf("%s: Invalid buffer specification, "
"(should be a number greater than zero)\n",
cmdname);
console_done(con);
return CMD_FAILURE;
}
if (verbose)
printf("Buffer = %d\n", buffer);
break;
}
}
if (buffer == 0)
buffer = CP_DEFAULT_BUFLEN;
argc -= optind;
if (argc != 2) {
printf("%s: invalid number of arguments. Try %s --help\n",
cmdname, cmdname);
console_done(con);
return CMD_FAILURE;
}
ret = do_copy(argv[optind], argv[optind + 1], buffer, verbose,
recursive, force, interactive);
console_done(con);
if (ret == 0)
return CMD_SUCCESS;
else
return CMD_FAILURE;
}
HelenOS homepage, sources at GitHub