diff options
Diffstat (limited to 'src/commands')
-rw-r--r-- | src/commands/cat.c | 2 | ||||
-rw-r--r-- | src/commands/cp.c | 232 | ||||
-rw-r--r-- | src/commands/dd.c | 6 | ||||
-rw-r--r-- | src/commands/ed.c | 64 | ||||
-rw-r--r-- | src/commands/groups.c | 4 | ||||
-rw-r--r-- | src/commands/head.c | 9 | ||||
-rw-r--r-- | src/commands/id.c | 4 | ||||
-rw-r--r-- | src/commands/ls.c | 20 | ||||
-rw-r--r-- | src/commands/mkdir.c | 60 | ||||
-rw-r--r-- | src/commands/mv.c | 108 | ||||
-rw-r--r-- | src/commands/printf.c | 3 | ||||
-rw-r--r-- | src/commands/rm.c | 132 | ||||
-rw-r--r-- | src/commands/tail.c | 11 | ||||
-rw-r--r-- | src/commands/tee.c | 2 | ||||
-rw-r--r-- | src/commands/wc.c | 2 | ||||
-rw-r--r-- | src/commands/xargs.c | 4 |
16 files changed, 596 insertions, 67 deletions
diff --git a/src/commands/cat.c b/src/commands/cat.c index 6ed77e8..8c15522 100644 --- a/src/commands/cat.c +++ b/src/commands/cat.c @@ -103,7 +103,7 @@ static int short_arg(char c, char* next) { flags.end_lines_dollar = true; break; default: - error("error: unkown flag -%c", c); + return ARG_INVALID; } return ARG_UNUSED; } diff --git a/src/commands/cp.c b/src/commands/cp.c new file mode 100644 index 0000000..37e3354 --- /dev/null +++ b/src/commands/cp.c @@ -0,0 +1,232 @@ +#include "../command.h" +#include <dirent.h> +#include <limits.h> +#include <stdio.h> +#include <sys/stat.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) { + FILE* from_f = get_file_s(from, "r"); + if (from_f == NULL) { return false; } + + FILE* to_f = get_file_s(to, "w"); + if (to_f == NULL) { fclose(from_f); return false; } + + #define BS 1024 + + char buf[BS]; + int read; + + 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': %s", from, strerror(errno)); + 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': %s", from, strerror(errno)); + 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': %s", to, strerror(errno)); + return; + } + + if (chown(to, s->st_uid, s->st_gid) < 0) { + error_s("cannot chown '%s': %s", to, strerror(errno)); + return; + } +} + +static void cp_file(char* path); + +static void cp_directory(struct stat* s) { + 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': %s", get_path_buffer_2(), strerror(errno)); + return; + } + + DIR* d = opendir(get_path_buffer()); + + if (d == NULL) { + error_s("cannot open directory '%s': %s", get_path_buffer(), strerror(errno)); + return; + } + + struct dirent* file; + 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) { + if (path[0] == '\0') return path; + + int last = 0; + int i = 0; + 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': %s", get_path_buffer(), strerror(errno)); + 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) { + + flags.hard_link = false; + flags.sym_link = false; + flags.preserve = false; + flags.recurse = false; + flags.verbose = false; + + int 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': %s", argv[start], strerror(errno)); + } + 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]); + + struct stat s; + if (lstat(get_path_buffer_2(), &s) < 0) { + error("target: '%s': %s", get_path_buffer_2(), strerror(errno)); + } + + for (int i = start; i < argc - 1; i++) { + cp_file(argv[i]); + } + + return EXIT_SUCCESS; +} diff --git a/src/commands/dd.c b/src/commands/dd.c index 6b973e2..78f6041 100644 --- a/src/commands/dd.c +++ b/src/commands/dd.c @@ -29,16 +29,16 @@ COMMAND(dd) { char* str = argv[i] + 3; bs = get_number(str); if (bs < 1) { - error("error: block size must be greater than 0"); + error("block size must be greater than 0"); } } else if (prefix("count=", argv[i])) { char* str = argv[i] + 6; count = get_number(str); if (count < 1) { - error("error: count must be greather than 0"); + error("count must be greather than 0"); } } else { - error("error: unkown option %s", argv[i]); + error("unkown option %s", argv[i]); } } diff --git a/src/commands/ed.c b/src/commands/ed.c index 44f1a2c..379da3c 100644 --- a/src/commands/ed.c +++ b/src/commands/ed.c @@ -50,7 +50,7 @@ static bool read_regex(char** end, re_t* regex, enum RegexDirection dir) { while(true) { c = *(index++); if (c == '\0') { - fprintf(stderr, "error: missing regex after %c\n", dir == BEFORE ? '?' : '/'); + error_s("missing regex after %c\n", dir == BEFORE ? '?' : '/'); return false; } if (c == (dir == BEFORE ? '?' : '/')) { @@ -170,7 +170,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a) n_pre = -1; } else { if (n_pre < 0) { - fprintf(stderr, "error: input cannot be negative\n"); + error_s("input cannot be negative\n"); return false; } index = end_pre; @@ -192,7 +192,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a) char* end; long int n = strtol(index, &end, 10) - 1; if (n < 0) { - fprintf(stderr, "error: input cannot be negative\n"); + error_s("input cannot be negative\n"); return false; } if (index == end) { @@ -201,7 +201,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a) address.data.index.i = line_current - n; } if (address.data.index.i < 0) { - fprintf(stderr, "error: line number %ld does not exist\n", address.data.index.i + 1); + error_s("line number %ld does not exist\n", address.data.index.i + 1); return false; } break; @@ -211,7 +211,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a) char* end; long int n = strtol(index, &end, 10) - 1; if (n < 0) { - fprintf(stderr, "error: input cannot be negative\n"); + error_s("input cannot be negative\n"); return false; } if (index == end) { @@ -220,7 +220,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a) address.data.index.i = line_current + n; } if (address.data.index.i >= (long int) line_count) { - fprintf(stderr, "error: line number %ld does not exist\n", address.data.index.i + 1); + error_s("line number %ld does not exist\n", address.data.index.i + 1); return false; } break; @@ -256,7 +256,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a) address.data.index.i = n_pre; } if (address.data.index.i < 0 || address.data.index.i >= (long int) line_count) { - fprintf(stderr, "error: line number %ld does not exist\n", address.data.index.i + 1); + error_s("line number %ld does not exist\n", address.data.index.i + 1); return false; } } @@ -404,7 +404,7 @@ static void delete_lines(unsigned long a, unsigned long b) { static bool handle_append(struct LineAddress* address) { if (address->type != INDEX) { - fprintf(stderr, "error: append command requires index addressing\n"); + error_s("append command requires index addressing\n"); return false; } if (line_count == 0) { @@ -424,20 +424,20 @@ static bool handle_append(struct LineAddress* address) { static bool handle_delete(struct LineAddress* address) { if (address->empty && address->data.index.i >= (long int) line_count) { - fprintf(stderr, "error: line number %ld does not exist\n", address->data.index.i + 1); + error_s("line number %ld does not exist\n", address->data.index.i + 1); return false; } if (address->type == INDEX) { delete_lines(address->data.index.i, address->data.index.i); - printf("ed: deleted line %ld\n", address->data.index.i+1); + output("deleted line %ld\n", address->data.index.i+1); } else if (address->type == RANGE) { delete_lines(address->data.range.a, address->data.range.b); - printf("ed: deleted lines %ld-%ld\n", address->data.range.a+1, address->data.range.b+1); + output("deleted lines %ld-%ld\n", address->data.range.a+1, address->data.range.b+1); } else if (address->type == SET) { for (unsigned long i = 0; i < address->data.set.s; i++) { delete_lines(address->data.set.b[i], address->data.set.b[i]); } - printf("ed: deleted %lu lines\n", address->data.set.s); + output("deleted %lu lines\n", address->data.set.s); } return true; } @@ -446,7 +446,7 @@ static bool get_file_name(char** filename) { size_t len = strlen(*filename); if (len < 1 || (len == 1 && **filename == '\n')) { if (default_filename == NULL) { - fprintf(stderr, "error: no default filename specified\n"); + error_s("no default filename specified\n"); return false; } *filename = default_filename; @@ -468,7 +468,7 @@ static bool get_file_name(char** filename) { static void write_file(char* filename, struct LineAddress* address, char* type) { if (line_count < 1) { - fprintf(stderr, "error: cannot write empty file\n"); + error_s("cannot write empty file\n"); return; } if (!get_file_name(&filename)) return; @@ -497,7 +497,7 @@ static void write_file(char* filename, struct LineAddress* address, char* type) } pending_writes = false; fclose(file); - printf("ed: wrote %d lines from %s\n", wrote, filename); + output("wrote %d lines from %s\n", wrote, filename); } static void read_file(char* filename) { @@ -512,7 +512,7 @@ static void read_file(char* filename) { if (size < 1) { free(buf); - fprintf(stderr, "error: attempted to read a empty file\n"); + error_s("attempted to read a empty file\n"); return; } @@ -522,7 +522,7 @@ static void read_file(char* filename) { } append_lines(line, buf, size); free(buf); - printf("ed: read and appended %lu lines from %s\n", size, filename); + output("read and appended %lu lines from %s\n", size, filename); } static void expand_string(char** buf, int* capacity, int* size, char* text, int len) { @@ -582,7 +582,7 @@ static void prompt(void) { if (cmd == ',') { if (address.type != INDEX) { - fprintf(stderr, "error: comma range addressing requires two index addresses\n"); + error_s("comma range addressing requires two index addresses\n"); free_address(address); return; } @@ -593,7 +593,7 @@ static void prompt(void) { return; } if (address2.type != INDEX) { - fprintf(stderr, "error: comma range addressing requires two index addresses\n"); + error_s("comma range addressing requires two index addresses\n"); free_address(address); free_address(address2); return; @@ -606,7 +606,7 @@ static void prompt(void) { } if (address.type == RANGE && address.data.range.a > address.data.range.b) { - fprintf(stderr, "error: range addressing must be in ascending order\n"); + error_s("range addressing must be in ascending order\n"); free_address(address); return; } @@ -618,10 +618,10 @@ test: case '\n': if (address.empty) { if (line_current == line_count) { - fprintf(stderr, "error: line number %ld does not exist\n", line_current + 1); + error_s("line number %ld does not exist\n", line_current + 1); break; } else if (line_current + 1 == line_count) { - fprintf(stderr, "error: line number %ld does not exist\n", line_current + 2); + error_s("line number %ld does not exist\n", line_current + 2); break; } else { line_current++; @@ -634,7 +634,7 @@ test: } else if (address.type == RANGE) { line_current = address.data.range.b; } else if (address.type == SET) { - fprintf(stderr, "error: unexpected range addressing\n"); + error_s("unexpected range addressing\n"); break; } printf("%s", lines[line_current]); @@ -656,7 +656,7 @@ test: __attribute__((fallthrough)); case 'p': if (address.empty && address.data.index.i >= (long int) line_count) { - fprintf(stderr, "error: line number %ld does not exist\n", address.data.index.i + 1); + error_s("line number %ld does not exist\n", address.data.index.i + 1); break; } if (address.type == INDEX) { @@ -686,7 +686,7 @@ test: skip_whitespace(&index); free_address(address); if (*(index++) != '/') { - fprintf(stderr, "error: unexpected character at start of regex\n"); + error_s("unexpected character at start of regex\n"); break; } if (!parse_regex(&index, &address, ALL)) { return; } @@ -701,18 +701,18 @@ test: case 's': skip_whitespace(&index); if (*(index++) != '/') { - fprintf(stderr, "error: unexpected character at start of regex\n"); + error_s("unexpected character at start of regex\n"); break; } if (!parse_regex_lines(&index, &address)) { return; } char* replace = index; while(*index != '\0' && *index != '/') index++; if (*index != '/') { - fprintf(stderr, "error: / missing after %c\n", *index); + error_s("/ missing after %c\n", *index); break; } if (address.data.set.s < 1) { - printf("ed: no matches found\n"); + error_s("no matches found\n"); break; } *(index++) = '\0'; @@ -725,11 +725,11 @@ test: char* end; matches = strtol(index, &end, 10); if (end == index) { - fprintf(stderr, "error: invalid number: %s\n", index); + error_s("invalid number: %s\n", index); break; } if (matches < 1) { - fprintf(stderr, "error: matches cannot be less than 1\n"); + error_s("matches cannot be less than 1\n"); break; } } @@ -739,7 +739,7 @@ test: for (unsigned long i = 0; i < address.data.set.s; i++) { matches_found += substute_string(address.data.set.b[i], matches, last_regex, replace, sub_len); } - printf("ed: replaced %ld matches over %ld lines\n", matches_found, address.data.set.s); + output("replaced %ld matches over %ld lines\n", matches_found, address.data.set.s); pending_writes = true; break; case 'w': { @@ -782,7 +782,7 @@ test: printf("%ld\n", line_current + 1); break; default: - fprintf(stderr, "error: unimplemented command\n"); + error_s("unimplemented command\n"); break; } diff --git a/src/commands/groups.c b/src/commands/groups.c index aea92af..763f294 100644 --- a/src/commands/groups.c +++ b/src/commands/groups.c @@ -9,7 +9,7 @@ COMMAND_EMPTY(groups) { struct passwd* pw = getpwuid(uid); if(pw == NULL){ - perror("error: failed to fetch groups: "); + error("failed to fetch groups: %s", strerror(errno)); } int ngroups = 0; @@ -21,7 +21,7 @@ COMMAND_EMPTY(groups) { for (int i = 0; i < ngroups; i++){ struct group* gr = getgrgid(groups[i]); if(gr == NULL){ - perror("error: failed to fetch groups: "); + error("failed to fetch groups: %s", strerror(errno)); } printf("%s ",gr->gr_name); } diff --git a/src/commands/head.c b/src/commands/head.c index 44bc45b..c09dc9a 100644 --- a/src/commands/head.c +++ b/src/commands/head.c @@ -70,7 +70,7 @@ static int short_arg(char c, char* next) { check_arg(next); long int bkm = get_blkm(next); if (bkm < 1) { - error("error: bkm cannot be less than 1"); + error("bkm cannot be less than 1"); } flags.count = bkm; return ARG_USED; @@ -80,7 +80,7 @@ static int short_arg(char c, char* next) { check_arg(next); long int bkm = get_blkm(next); if (bkm < 1) { - error("error: bkm cannot be less than 1"); + error("bkm cannot be less than 1"); } flags.count = bkm; return ARG_USED; @@ -91,9 +91,8 @@ static int short_arg(char c, char* next) { case 'v': flags.print_headers = true; break; - default: { - error("error: unknown option -%c", c); - } + default: + return ARG_INVALID; } return ARG_UNUSED; } diff --git a/src/commands/id.c b/src/commands/id.c index 578c1e3..4bd7bca 100644 --- a/src/commands/id.c +++ b/src/commands/id.c @@ -10,7 +10,7 @@ COMMAND_EMPTY(user_id) { struct passwd* pw = getpwuid(uid); if(pw == NULL){ - perror("error: failed to fetch groups: "); + error("failed to fetch groups: %s", strerror(errno)); } int ngroups = 0; @@ -30,7 +30,7 @@ COMMAND_EMPTY(user_id) { for (int i = 0; i < ngroups; i++){ struct group* gr = getgrgid(groups[i]); if(gr == NULL){ - perror("error: failed to fetch groups: "); + error("failed to fetch groups: %s", strerror(errno)); } printf("%d(%s)", gr->gr_gid, gr->gr_name); if (i + 1 < ngroups) putchar(','); diff --git a/src/commands/ls.c b/src/commands/ls.c index fec8cdd..a5c60d2 100644 --- a/src/commands/ls.c +++ b/src/commands/ls.c @@ -55,9 +55,9 @@ static DIR* get_directory(char* path) { DIR* d = opendir(path); if (d == NULL) { if (errno == ENOTDIR) { - printf("\x1b[0m%s is a a file\n", path); + error_s("`%s` is a a file\n", path); } else { - printf("\x1b[0merror: failed to open directory '%s': %s\n", path, strerror(errno)); + error_s("failed to open directory '%s': %s\n", path, strerror(errno)); } } return d; @@ -74,7 +74,7 @@ static bool get_file_info(const char* file_name, struct FileInfo* info) { int save = push_path_buffer(file_name); if (lstat(get_path_buffer(), &s) < 0) { - printf("\x1b[0merror: failed to read file '%s': %s\n", get_path_buffer(), strerror(errno)); + error_s("failed to read file '%s': %s\n", get_path_buffer(), strerror(errno)); pop_path_buffer(save); return false; } @@ -156,14 +156,14 @@ static bool get_file_info(const char* file_name, struct FileInfo* info) { info->usr = getpwuid(s.st_uid); if (info->usr == NULL) { - fprintf(stderr, "error: failed to get user from %s\n", get_path_buffer()); + error_s("failed to get user from `%s`\n", get_path_buffer()); pop_path_buffer(save); return false; } info->grp = getgrgid(s.st_gid); if (info->grp == NULL) { - fprintf(stderr, "error: failed to get user from %s\n", get_path_buffer()); + error_s("failed to get user from `%s`\n", get_path_buffer()); pop_path_buffer(save); return false; } @@ -288,10 +288,6 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo if (!flags.more_info) printf("\n"); } -static bool is_dot_dir(const char* path) { - return streql(path, ".") || streql(path, ".."); -} - static int num_places (int n) { int r = 1; if (n < 0) n = (n == INT_MIN) ? INT_MAX: -n; @@ -465,7 +461,7 @@ static int short_arg(char c, char* next) { flags.more_info = true; break; default: - error("error: unkown option -%c", c); + return ARG_INVALID; } return ARG_UNUSED; } @@ -481,7 +477,7 @@ static int long_arg(char* cur, char* next) { } else if (streql("no", arg) || streql("never", arg)) { flags.colored = NO; } else { - error("error: invalid color options: %s", arg); + error("invalid color options: %s", arg); } } else { return ARG_IGNORE; @@ -496,7 +492,7 @@ COMMAND(ls) { flags.hide_dot = false; flags.one_column = false; flags.recurse = false; - flags.colored = AUTO; + flags.colored = NO; int start = parse_args(argc, argv, help, short_arg, long_arg); diff --git a/src/commands/mkdir.c b/src/commands/mkdir.c new file mode 100644 index 0000000..3ff1afd --- /dev/null +++ b/src/commands/mkdir.c @@ -0,0 +1,60 @@ +#include "../command.h" + +static struct { + bool make_parent; + mode_t mode; +} flags; + +static int short_arg(char c, char* next) { + switch (c) { + case 'p': + flags.make_parent = true; + break; + case 'm': + check_arg(next); + flags.mode = get_mode(next); + return ARG_USED; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static void help(void) { + printf("Usage: mkdir [-m MODE] [-p] DIRECTORY...\n\n"); + printf("Create DIRECTORY\n\n"); + printf("\t-m\tMODE\n"); + printf("\t-p\tNo error if exists; make parent directories as needed\n"); +} + +static bool mkdir_parents(char* path) { + for (size_t i = 1; i < strlen(path); i++) { + if (path[i] != '/') continue; + path[i] = '\0'; + if (mkdir(path, flags.mode) < 0 && errno != EEXIST) { + error_s("failed to create directory '%s': %s", path, strerror(errno)); + return false; + }; + path[i] = '/'; + } + return true; +} + +COMMAND(makedir) { + if (argc < 1) global_help(help); + + flags.make_parent = false; + flags.mode = 0755; + int start = parse_args(argc, argv, help, short_arg, NULL); + + for (int i = start; i < argc; i++) { + if (flags.make_parent && !mkdir_parents(argv[i])) { + continue; + } + if (mkdir(argv[i], flags.mode) < 0) { + error_s("failed to create directory '%s': %s", argv[i], strerror(errno)); + } + } + + return EXIT_SUCCESS; +} diff --git a/src/commands/mv.c b/src/commands/mv.c new file mode 100644 index 0000000..f3e759a --- /dev/null +++ b/src/commands/mv.c @@ -0,0 +1,108 @@ +#include "../command.h" + +static struct { + bool prompt; + bool dont_overwrite; + bool refuse_if_dir; + bool verbose; +} flags; + +static void help(void) { + printf("Usage: mv [-inT] SOURCE... DIRECTORY\n\n"); + printf("Rename SOURCE to DEST, or move SOURCEs to DIRECTORY\n"); + printf("\t-i\tInteractive, prompt before overwriting\n"); + printf("\t-n\tDon't overwrite an existing file\n"); + printf("\t-T\tRefuse to move if DEST is a directory\n"); + printf("\t-v\tVerbose\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 't': + flags.prompt = true; + break; + case 'n': + flags.dont_overwrite = true; + break; + case 'T': + flags.refuse_if_dir = true; + break; + case 'v': + flags.verbose = true; + break; + default: + return ARG_UNUSED; + } + return ARG_USED; +} + +static void mv_dir(bool exists) { + if (exists && flags.dont_overwrite) { + if (flags.verbose) output("skipping '%s'; overwrise is false", get_path_buffer_2()); + return; + } + if (exists && flags.prompt) { + fprintf(stderr, "overwrite '%s'? ", get_path_buffer_2()); + fflush(stderr); + char c = getchar(); + if (c != 'y' && c != 'Y') { + if (flags.verbose) output("skipping..."); + return; + } + } + + if (rename(get_path_buffer(), get_path_buffer_2()) < 0) { + error_s("cannot move '%s': %s", get_path_buffer(), strerror(errno)); + } else if (flags.verbose) { + output("moved '%s'", get_path_buffer()); + } +} + +COMMAND(mv) { + + flags.refuse_if_dir = false; + flags.dont_overwrite = false; + flags.prompt = false; + flags.verbose = false; + + int start = parse_args(argc, argv, help, short_arg, NULL); + + if (argc - start < 2) { + global_help(help); + } + + push_path_buffer_2(argv[argc-1]); + + bool dest = true; + struct stat s; + if (lstat(get_path_buffer_2(), &s) < 0 && argc - start > 2) { + dest = false; + error("cannot stat '%s': %s", get_path_buffer_2(), strerror(errno)); + } + + if (dest && flags.refuse_if_dir) { + if (S_ISDIR(s.st_mode)) { + error("target '%s': Is A Directory", get_path_buffer_2()); + } + } + + if (argc - start == 2) { + push_path_buffer(argv[argc-2]); + mv_dir(dest); + return EXIT_SUCCESS; + } + + if (dest && !S_ISDIR(s.st_mode)) { + error("target '%s': Is Not A Directory", get_path_buffer_2()); + } + + for (int i = start; i < argc - 1; i++) { + int save = push_path_buffer(argv[i]); + bool exists = lstat(get_path_buffer(), &s) >= 0; + mv_dir(exists); + pop_path_buffer(save); + } + + return EXIT_SUCCESS; +} diff --git a/src/commands/printf.c b/src/commands/printf.c index f9f3931..df6ce32 100644 --- a/src/commands/printf.c +++ b/src/commands/printf.c @@ -96,7 +96,8 @@ static void help(void) { COMMAND(print) { if (argc < 1) { - error("usage: printf FORMAT [ARG]...\n"); + global_help(help); + return EXIT_SUCCESS; } parse_help(argc, argv, help); diff --git a/src/commands/rm.c b/src/commands/rm.c new file mode 100644 index 0000000..b85f91a --- /dev/null +++ b/src/commands/rm.c @@ -0,0 +1,132 @@ +#include "../command.h" +#include <dirent.h> +#include <string.h> + +static struct { + bool force; + bool prompt; + bool verbose; + bool recurse; +} flags; + +#ifdef FRENCH + static bool get_frenched; +#endif + +static void help(void) { + printf("Usage: rm [-irfv] FILE...\n\n"); + printf("Remove (unlink) FILESs\n\n"); + printf("\t-i\tAlways prompt before removing\n"); + printf("\t-f\tForce, never prompt\n"); + printf("\t-v\tVerbose\n"); + printf("\t-R,-r\tRecurse\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case 'i': + flags.prompt = true; + break; + case 'f': + flags.force = true; + break; + case 'v': + flags.verbose = true; + break; + case 'R': + case 'r': + flags.recurse = true; + break; + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +static void rm_file(char* path); + +static bool rm_dir() { + DIR* d = opendir(get_path_buffer()); + if (d == NULL) { + error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno)); + return false; + } + + struct dirent* file; + while ((file = readdir(d)) != NULL) { + if (is_dot_dir(file->d_name)) continue; + rm_file(file->d_name); + } + + closedir(d); + return true; +} + +static void rm_file(char* path) { + int save = push_path_buffer(path); + + struct stat s; + if (lstat(get_path_buffer(), &s) < 0) { + pop_path_buffer(save); + error_s("failed to stat '%s': %s\n", get_path_buffer(), strerror(errno)); + return; + } + + if (S_ISDIR(s.st_mode)) { + if (!flags.force) { + error_s("cannot delete '%s': Is a directory\n", get_path_buffer()); + pop_path_buffer(save); + return; + } + if (flags.recurse && !rm_dir()) { + pop_path_buffer(save); + return; + } + } + + if (flags.prompt) { + fprintf(stderr, "delete '%s'? ", get_path_buffer()); + fflush(stderr); + char c = getchar(); + if (c != 'y' && c != 'Y') { + fprintf(stderr, "Skipping...\n"); + pop_path_buffer(save); + return; + } + } + + if (remove(get_path_buffer()) < 0) { + error_s("failed to delete '%s': %s\n", get_path_buffer(), strerror(errno)); + } else if (flags.verbose) { + output("deleted '%s'\n", get_path_buffer()); + } + + pop_path_buffer(save); +} + +COMMAND(rm) { + if (argc < 1) { + global_help(help); + return EXIT_SUCCESS; + } + + flags.prompt = false; + flags.force = false; + flags.verbose = false; + flags.recurse = false; + + int start = parse_args(argc, argv, help, short_arg, NULL); + +#ifdef FRENCH + if (streql(argv[0], "-rf")) { + printf("\x1b[94mremoving \x1b[97mthe \x1b[91mfrench \x1b[93m(beguette noises)\x1b[0m\n"); + } +#endif + + for (int i = start; i < argc; i++) { + rm_file(argv[i]); + } + + return EXIT_SUCCESS; +} diff --git a/src/commands/tail.c b/src/commands/tail.c index a611842..4856bc0 100644 --- a/src/commands/tail.c +++ b/src/commands/tail.c @@ -131,7 +131,7 @@ static int short_arg(char c, char* next) { check_arg(next); long int bkm = get_blkm(next); if (bkm < 1) { - error("error: bkm cannot be less than 1"); + error("bkm cannot be less than 1"); } flags.count = bkm; return ARG_USED; @@ -141,7 +141,7 @@ static int short_arg(char c, char* next) { check_arg(next); long int bkm = get_blkm(next); if (bkm < 1) { - error("error: bkm cannot be less than 1"); + error("bkm cannot be less than 1"); } flags.count = bkm; return ARG_USED; @@ -159,14 +159,13 @@ static int short_arg(char c, char* next) { check_arg(next); long int sec = get_number(next); if (sec < 1) { - error("error: wait seconds cannot be less than 1"); + error("wait seconds cannot be less than 1"); } flags.grow_wait = sec; return ARG_USED; } - default: { - error("error: unknown option -%c", c); - } + default: + return ARG_INVALID; } return ARG_UNUSED; } diff --git a/src/commands/tee.c b/src/commands/tee.c index ced5b06..652e369 100644 --- a/src/commands/tee.c +++ b/src/commands/tee.c @@ -41,7 +41,7 @@ static int short_arg(char c, char* next) { flags.handle_sigint = true; break; default: - error("error: unkown option: %c", c); + return ARG_INVALID; } return ARG_UNUSED; } diff --git a/src/commands/wc.c b/src/commands/wc.c index 9acbf5c..4012b6c 100644 --- a/src/commands/wc.c +++ b/src/commands/wc.c @@ -111,7 +111,7 @@ static int short_arg(char c, char* next) { flags.longest_line = true; break; default: - error("error: invald option -%c", c); + return ARG_INVALID; } flags.has_flags = true; return ARG_UNUSED; diff --git a/src/commands/xargs.c b/src/commands/xargs.c index 01fb6ce..8f24441 100644 --- a/src/commands/xargs.c +++ b/src/commands/xargs.c @@ -45,10 +45,12 @@ static int short_arg(char c, char* next) { check_arg(next); long int n = get_number(next); if (n < 1) { - error("error: max arg count must be at least 1"); + error("max arg count must be at least 1"); } flags.max_args = n; return ARG_USED; + default: + return ARG_INVALID; } return ARG_UNUSED; } |