#include "command.h" #include "lslib.h" #include #include #include #include #include #include #include #include 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_main) { 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; }