add rm, cp, mkdir, mv

This commit is contained in:
Murphy 2023-05-03 12:17:56 -04:00
parent 889b35ebd1
commit f2606d0875
22 changed files with 718 additions and 93 deletions

View file

@ -12,6 +12,7 @@ CCFLAGS = -std=c99 -Wall -Wextra -pedantic -O2
CCFLAGS += -D_DEFAULT_SOURCE -DMAJOR=$(MAJOR) -DMINOR=$(MINOR) -DPATCH=$(PATCH) -DCHECK_LINK
CCFLAGS += $(INCFLAGS)
LDFLAGS = -s
LDFLAGS += $(INCFLAGS)
BIN = bin

View file

@ -4,7 +4,7 @@
A terrible busybox/gnu coreutils clone.
Currently the only supported commands are:
`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs`, `tac`
`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs`, `tac`, `rm`, `cp`, `mkdir`, `mv`
## How to

View file

@ -33,3 +33,7 @@ COMMAND(whoami);
COMMAND(wc);
COMMAND(xargs);
COMMAND(tac);
COMMAND(rm);
COMMAND(cp);
COMMAND(makedir);
COMMAND(mv);

View file

@ -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;
}

232
src/commands/cp.c Normal file
View file

@ -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;
}

View file

@ -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]);
}
}

View file

@ -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;
}

View file

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

View file

@ -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;
}

View file

@ -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(',');

View file

@ -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);

60
src/commands/mkdir.c Normal file
View file

@ -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;
}

108
src/commands/mv.c Normal file
View file

@ -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;
}

View file

@ -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);

132
src/commands/rm.c Normal file
View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -8,9 +8,12 @@
#include <sys/stat.h>
#include <sys/types.h>
char* cmd;
int main (ARGUMENTS) {
if (argc < 1) {
error("error: argument 0 missing");
fprintf(stderr, "fatal: argument 0 missing");
return EXIT_FAILURE;
}
#ifdef CHECK_LINK
@ -21,7 +24,7 @@ int main (ARGUMENTS) {
if (argc < 2) {
printf("usage: lazysphere [function [arguments]...]\n\n");
printf("currently defined functions:\n");
printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc, xargs, tac\n");
printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc, xargs, tac, rm, cp, mkdir, mv\n");
return EXIT_SUCCESS;
}
argc--;
@ -29,7 +32,6 @@ int main (ARGUMENTS) {
}
#endif
const char* cmd;
if (strncmp("./", argv[0], 2) == 0) {
cmd = argv[0] + 2;
} else {
@ -52,6 +54,8 @@ int main (ARGUMENTS) {
return user_id();
} else if (streql(cmd, "ls") || streql(cmd, "dir")) {
return ls(NEXT_ARGS);
} else if (streql(cmd, "lsd")) {
printf("look at all the funny colors\n");
} else if (streql(cmd, "tail")) {
return tail(NEXT_ARGS);
} else if (streql(cmd, "head")) {
@ -72,8 +76,17 @@ int main (ARGUMENTS) {
return xargs(NEXT_ARGS);
} else if (streql(cmd, "tac")) {
return tac(NEXT_ARGS);
} else if (streql(cmd, "rm")) {
return rm(NEXT_ARGS);
} else if (streql(cmd, "cp")) {
return cp(NEXT_ARGS);
} else if (streql(cmd, "mkdir")) {
return makedir(NEXT_ARGS);
} else if (streql(cmd, "mv")) {
return mv(NEXT_ARGS);
} else {
error("error: invalid command %s", cmd);
fprintf(stderr, "lazysphere: invalid command %s", cmd);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;

View file

@ -1,5 +1,6 @@
#include "shared.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
@ -10,15 +11,36 @@
#include <sys/stat.h>
#include <paths.h>
void error(const char* format, ...) {
extern char* cmd;
void error_s(const char *format, ...) {
va_list list;
va_start(list, format);
fprintf(stderr, "%s: ", cmd);
vfprintf(stderr, format, list);
fprintf(stderr, "\n");
}
void error(const char *format, ...) {
va_list list;
va_start(list, format);
fprintf(stderr, "%s: ", cmd);
vfprintf(stderr, format, list);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
void output(const char *format, ...) {
va_list list;
va_start(list, format);
fprintf(stdout, "%s: ", cmd);
vfprintf(stdout, format, list);
fprintf(stdout, "\n");
}
FILE* get_file_s(const char* path, const char* type) {
struct stat s;
if (streql("-", path) && type[0] == 'r') {
@ -28,11 +50,11 @@ FILE* get_file_s(const char* path, const char* type) {
}
if (lstat(path, &s) < 0) {
if (type[0] != 'r') goto read;
fprintf(stderr, "error: failed to read %s: %s\n", path, strerror(errno));
error_s("failed to read %s: %s", path, strerror(errno));
return NULL;
}
if (S_ISDIR(s.st_mode)) {
fprintf(stderr, "error: %s is a directory\n", path);
error_s("%s is a directory", path);
return NULL;
}
@ -40,7 +62,7 @@ FILE* get_file_s(const char* path, const char* type) {
read:
file = fopen(path, type);
if (file == NULL) {
fprintf(stderr, "error: failed to %s file %s: %s\n", type[0] == 'r' ? "read" : "write", path, strerror(errno));
error_s("failed to %s file %s: %s", type[0] == 'r' ? "read" : "write", path, strerror(errno));
}
return file;
}
@ -55,7 +77,7 @@ long int get_number(const char* text) {
char* end;
long int n = strtol(text, &end, 10);
if (text == end) {
error("error: %s is not a valid number", text);
error("%s is not a valid number", text);
}
return n;
}
@ -64,7 +86,7 @@ long int get_blkm(const char* text) {
char* end;
long int n = strtol(text, &end, 10);
if (text == end) {
error("error: %s is not a valid bkm", text);
error("%s is not a valid bkm", text);
}
if (*end == '\0') return n;
switch (*end) {
@ -78,12 +100,23 @@ long int get_blkm(const char* text) {
case 'm':
return n * 1024 * 1204;
default:
error("error: invalid bkm type %c", *end);
error("invalid bkm type %c", *end);
}
// shouldnt get here anyways
return 0;
}
mode_t get_mode(const char* next) {
char* end = NULL;
mode_t mode = (mode_t)strtol(next, &end, 8);
if (!end) return 0;
while(isspace(*end)) end++;
if (*end != '\0' || (unsigned) mode < 010000) {
error("invalid file mode: `%s`", next);
}
return mode;
}
bool streql(const char* a, const char* b) {
if (*a != *b) return false;
int n = 0;
@ -151,11 +184,11 @@ void print_date_time(time_t mills, char buf[13]) {
void check_arg (char* arg) {
if (arg == NULL) {
error("error: expected another argument after option");
error("expected another argument after option");
}
}
static void global_help(void (*help)(void)) {
void global_help(void (*help)(void)) {
printf("LazySphere v%d.%d.%d multi-call binary.\n\n", MAJOR, MINOR, PATCH);
help();
exit(EXIT_SUCCESS);
@ -196,6 +229,8 @@ int parse_args(int argc, char** argv, void (*help)(void), int (*short_arg)(char,
start++;
} else if (r == ARG_IGNORE) {
goto exit;
} else if (r == ARG_INVALID) {
error("invalid argument %s", argv[current]);
}
} else {
if (short_arg == NULL) {
@ -208,6 +243,8 @@ int parse_args(int argc, char** argv, void (*help)(void), int (*short_arg)(char,
start++;
} else if (r == ARG_IGNORE) {
goto exit;
} else if (r == ARG_INVALID) {
error("invalid argument -%c", argv[current][j]);
}
}
}
@ -220,7 +257,7 @@ exit:
int get_tty() {
int fd = open(_PATH_TTY, O_RDONLY);
if (fd < 0) error("error: failed to get tty: %s", strerror(errno));
if (fd < 0) error("failed to get tty: %s", strerror(errno));
return fd;
}
@ -228,11 +265,27 @@ FILE* get_tty_stream(char* type) {
int fd = get_tty();
FILE* file = fdopen(fd, type);
if (file == NULL) {
error("error: failed to open tty stream: %s", strerror(errno));
error("failed to open tty stream: %s", strerror(errno));
}
return file;
}
static int push_path_buffer_b(char* buf, int* index, const char* string) {
int save = *index;
if (*index > 1 || (*index == 1 && buf[0] != '/')) {
buf[(*index)++] = '/';
}
int string_len = strlen(string);
memcpy(buf + *index, string, string_len + 1);
*index += string_len;
return save;
}
static void pop_path_buffer_b(char* buf, int* index, int i) {
*index = i;
buf[*index] = '\0';
}
static char path_buffer[PATH_MAX + 1];
static int path_buffer_index = 0;
@ -241,17 +294,28 @@ char* get_path_buffer() {
}
int push_path_buffer(const char* string) {
int save = path_buffer_index;
if (path_buffer_index > 1 || (path_buffer_index == 1 && path_buffer[0] != '/')) {
path_buffer[path_buffer_index++] = '/';
}
int string_len = strlen(string);
memcpy(path_buffer + path_buffer_index, string, string_len + 1);
path_buffer_index += string_len;
return save;
return push_path_buffer_b(path_buffer, &path_buffer_index, string);
}
void pop_path_buffer(int i) {
path_buffer_index = i;
path_buffer[path_buffer_index] = '\0';
pop_path_buffer_b(path_buffer, &path_buffer_index, i);
}
static char path_buffer_2[PATH_MAX + 1];
static int path_buffer_index_2 = 0;
char* get_path_buffer_2() {
return path_buffer_2;
}
int push_path_buffer_2(const char* string) {
return push_path_buffer_b(path_buffer_2, &path_buffer_index_2, string);
}
void pop_path_buffer_2(int i) {
pop_path_buffer_b(path_buffer_2, &path_buffer_index_2, i);
}
bool is_dot_dir(const char* path) {
return streql(path, ".") || streql(path, "..");
}

View file

@ -32,13 +32,20 @@ enum When {
AUTO
};
__attribute__ ((__format__(printf, 1, 2)))
void error_s(const char* format, ...);
__attribute__ ((__format__(printf, 1, 2)))
void error(const char* format, ...);
__attribute__ ((__format__(printf, 1, 2)))
void output(const char* format, ...);
FILE* get_file_s(const char* path, const char* type);
FILE* get_file(const char* path, const char* type);
long int get_number(const char* text);
long int get_blkm(const char* text);
mode_t get_mode(const char* next);
bool streql(const char* a, const char* b);
bool prefix(const char* pre, const char* str);
@ -50,8 +57,10 @@ void print_date_time(time_t mills, char buf[13]);
#define ARG_UNUSED 0
#define ARG_USED 1
#define ARG_IGNORE 2
#define ARG_INVALID 3
void check_arg (char* arg);
void global_help(void (*help)(void));
void parse_help (int argc, char** argv, void (*help)(void));
int parse_args (int argc, char** argv, void (*help)(void), int (*short_arg)(char, char*), int (*long_arg)(char*, char*));
@ -62,3 +71,8 @@ char* get_path_buffer();
int push_path_buffer(const char* string);
void pop_path_buffer(int i);
char* get_path_buffer_2();
int push_path_buffer_2(const char* string);
void pop_path_buffer_2(int i);
bool is_dot_dir(const char* path);