245 lines
5.2 KiB
C
245 lines
5.2 KiB
C
#include "command.h"
|
|
#include "lslib.h"
|
|
|
|
#include <dirent.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
static struct {
|
|
bool recurse;
|
|
bool preserve;
|
|
bool sym_link;
|
|
bool hard_link;
|
|
bool verbose;
|
|
} flags;
|
|
|
|
static void help(void) {
|
|
printf("Usage: cp [-rplsv] SOURCE... DEST\n\n");
|
|
printf("Copy SOURCEs to DEST\n\n");
|
|
printf("\t-R,-r\tRecurse\n");
|
|
printf("\t-p\tPreserve file attributes if possible\n");
|
|
printf("\t-l,-s\tCreate (sym)links\n");
|
|
printf("\t-v\tVerbose\n");
|
|
}
|
|
|
|
static int short_arg(char c, char* next) {
|
|
UNUSED(next);
|
|
switch (c) {
|
|
case 'R':
|
|
case 'r':
|
|
flags.recurse = true;
|
|
break;
|
|
case 'p':
|
|
flags.preserve = true;
|
|
break;
|
|
case 'l':
|
|
flags.hard_link = true;
|
|
flags.sym_link = false;
|
|
break;
|
|
case 's':
|
|
flags.sym_link = true;
|
|
flags.hard_link = false;
|
|
break;
|
|
case 'v':
|
|
flags.verbose = true;
|
|
break;
|
|
default:
|
|
return ARG_INVALID;
|
|
}
|
|
return ARG_UNUSED;
|
|
}
|
|
|
|
static bool copy_file(char* from, char* to) {
|
|
#define BS 1024
|
|
|
|
FILE *from_f, *to_f;
|
|
char buf[BS];
|
|
int read;
|
|
|
|
from_f = get_file_s(from, "r");
|
|
if (from_f == NULL) { return false; }
|
|
|
|
to_f = get_file_s(to, "w");
|
|
if (to_f == NULL) { fclose(from_f); return false; }
|
|
|
|
|
|
while ((read = fread(buf, 1, BS, from_f)) > 0) {
|
|
fwrite(buf, 1, read, to_f);
|
|
}
|
|
|
|
if (flags.verbose) {
|
|
output("copied '%s'", from);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool symlink_file(char* from, char* to) {
|
|
if (symlink(from, to) < 0) {
|
|
error_s("failed to symlink '%s'", from);
|
|
return false;
|
|
} else if (flags.verbose) {
|
|
output("symlinked '%s'", from);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool hardlink_file(char* from, char* to) {
|
|
if (link(from, to) < 0) {
|
|
error_s("failed to hardlink '%s'", from);
|
|
return false;
|
|
} else if (flags.verbose) {
|
|
output("hardlinked '%s'", from);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void run_copy(struct stat* s) {
|
|
|
|
char* from = get_path_buffer();
|
|
char* to = get_path_buffer_2();
|
|
|
|
bool result;
|
|
if (flags.sym_link) {
|
|
result = symlink_file(from, to);
|
|
} else if (flags.hard_link) {
|
|
result = hardlink_file(from, to);
|
|
} else {
|
|
result = copy_file(from, to);
|
|
}
|
|
|
|
if (!result) return;
|
|
if (!flags.preserve) return;
|
|
|
|
if (chmod(to, s->st_mode) < 0) {
|
|
error_s("cannot chmod '%s'", to);
|
|
return;
|
|
}
|
|
|
|
if (chown(to, s->st_uid, s->st_gid) < 0) {
|
|
error_s("cannot chown '%s'", to);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void cp_file(char* path);
|
|
|
|
static void cp_directory(struct stat* s) {
|
|
|
|
DIR* d;
|
|
struct dirent* file;
|
|
|
|
if (!flags.recurse) {
|
|
error_s("-r not specified; omitting directory '%s'", get_path_buffer());
|
|
return;
|
|
}
|
|
|
|
if (mkdir(get_path_buffer_2(), s->st_mode) < 0 && errno != EEXIST) {
|
|
error_s("cannot create directory '%s'", get_path_buffer_2());
|
|
return;
|
|
}
|
|
|
|
d = opendir(get_path_buffer());
|
|
|
|
if (d == NULL) {
|
|
error_s("cannot open directory '%s'", get_path_buffer());
|
|
return;
|
|
}
|
|
|
|
while ((file = readdir(d)) != NULL) {
|
|
if (is_dot_dir(file->d_name)) continue;
|
|
cp_file(file->d_name);
|
|
}
|
|
}
|
|
|
|
static char* get_file_name(char* path) {
|
|
int last = 0;
|
|
int i = 0;
|
|
|
|
if (path[0] == '\0') return path;
|
|
|
|
while (true) {
|
|
if (path[i+1] == '\0') break;
|
|
if (path[i] == '/') {
|
|
last = i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (last == 0) return path;
|
|
else return path + last + 1;
|
|
}
|
|
|
|
static void cp_file(char* path) {
|
|
|
|
int save = push_path_buffer(path);
|
|
int save2 = push_path_buffer_2(get_file_name(path));
|
|
|
|
struct stat s;
|
|
if (lstat(get_path_buffer(), &s) < 0) {
|
|
pop_path_buffer(save);
|
|
error_s("cannot stat '%s'", get_path_buffer());
|
|
return;
|
|
}
|
|
|
|
if (S_ISDIR(s.st_mode)) {
|
|
cp_directory(&s);
|
|
} else {
|
|
run_copy(&s);
|
|
}
|
|
|
|
pop_path_buffer(save);
|
|
pop_path_buffer_2(save2);
|
|
}
|
|
|
|
COMMAND(cp) {
|
|
|
|
int start, i;
|
|
struct stat s;
|
|
|
|
flags.hard_link = false;
|
|
flags.sym_link = false;
|
|
flags.preserve = false;
|
|
flags.recurse = false;
|
|
flags.verbose = false;
|
|
|
|
start = parse_args(argc, argv, help, short_arg, NULL);
|
|
|
|
if (argc - start < 2) {
|
|
global_help(help);
|
|
}
|
|
|
|
/* only when 2 args and first is a file, the 2nd will be a file */
|
|
if (argc - start == 2) {
|
|
struct stat s;
|
|
if (lstat(argv[start], &s) < 0) {
|
|
error("cannot stat '%s'", argv[start]);
|
|
}
|
|
push_path_buffer(argv[argc-2]);
|
|
push_path_buffer_2(argv[argc-1]);
|
|
if (!S_ISDIR(s.st_mode)) {
|
|
run_copy(&s);
|
|
} else {
|
|
cp_directory(&s);
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
/* push directory */
|
|
push_path_buffer_2(argv[argc-1]);
|
|
|
|
if (lstat(get_path_buffer_2(), &s) < 0) {
|
|
error("target: '%s'", get_path_buffer_2());
|
|
}
|
|
|
|
for (i = start; i < argc - 1; i++) {
|
|
cp_file(argv[i]);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|