add rm, cp, mkdir, mv

This commit is contained in:
Freya 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 += -D_DEFAULT_SOURCE -DMAJOR=$(MAJOR) -DMINOR=$(MINOR) -DPATCH=$(PATCH) -DCHECK_LINK
CCFLAGS += $(INCFLAGS) CCFLAGS += $(INCFLAGS)
LDFLAGS = -s
LDFLAGS += $(INCFLAGS) LDFLAGS += $(INCFLAGS)
BIN = bin BIN = bin

View file

@ -4,7 +4,7 @@
A terrible busybox/gnu coreutils clone. A terrible busybox/gnu coreutils clone.
Currently the only supported commands are: 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 ## How to

View file

@ -33,3 +33,7 @@ COMMAND(whoami);
COMMAND(wc); COMMAND(wc);
COMMAND(xargs); COMMAND(xargs);
COMMAND(tac); 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; flags.end_lines_dollar = true;
break; break;
default: default:
error("error: unkown flag -%c", c); return ARG_INVALID;
} }
return ARG_UNUSED; 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; char* str = argv[i] + 3;
bs = get_number(str); bs = get_number(str);
if (bs < 1) { 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])) { } else if (prefix("count=", argv[i])) {
char* str = argv[i] + 6; char* str = argv[i] + 6;
count = get_number(str); count = get_number(str);
if (count < 1) { if (count < 1) {
error("error: count must be greather than 0"); error("count must be greather than 0");
} }
} else { } 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) { while(true) {
c = *(index++); c = *(index++);
if (c == '\0') { if (c == '\0') {
fprintf(stderr, "error: missing regex after %c\n", dir == BEFORE ? '?' : '/'); error_s("missing regex after %c\n", dir == BEFORE ? '?' : '/');
return false; return false;
} }
if (c == (dir == BEFORE ? '?' : '/')) { if (c == (dir == BEFORE ? '?' : '/')) {
@ -170,7 +170,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
n_pre = -1; n_pre = -1;
} else { } else {
if (n_pre < 0) { if (n_pre < 0) {
fprintf(stderr, "error: input cannot be negative\n"); error_s("input cannot be negative\n");
return false; return false;
} }
index = end_pre; index = end_pre;
@ -192,7 +192,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
char* end; char* end;
long int n = strtol(index, &end, 10) - 1; long int n = strtol(index, &end, 10) - 1;
if (n < 0) { if (n < 0) {
fprintf(stderr, "error: input cannot be negative\n"); error_s("input cannot be negative\n");
return false; return false;
} }
if (index == end) { 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; address.data.index.i = line_current - n;
} }
if (address.data.index.i < 0) { 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; return false;
} }
break; break;
@ -211,7 +211,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
char* end; char* end;
long int n = strtol(index, &end, 10) - 1; long int n = strtol(index, &end, 10) - 1;
if (n < 0) { if (n < 0) {
fprintf(stderr, "error: input cannot be negative\n"); error_s("input cannot be negative\n");
return false; return false;
} }
if (index == end) { 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; address.data.index.i = line_current + n;
} }
if (address.data.index.i >= (long int) line_count) { 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; return false;
} }
break; break;
@ -256,7 +256,7 @@ static bool read_address(char** command, bool whitespace, struct LineAddress* a)
address.data.index.i = n_pre; address.data.index.i = n_pre;
} }
if (address.data.index.i < 0 || address.data.index.i >= (long int) line_count) { 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; return false;
} }
} }
@ -404,7 +404,7 @@ static void delete_lines(unsigned long a, unsigned long b) {
static bool handle_append(struct LineAddress* address) { static bool handle_append(struct LineAddress* address) {
if (address->type != INDEX) { if (address->type != INDEX) {
fprintf(stderr, "error: append command requires index addressing\n"); error_s("append command requires index addressing\n");
return false; return false;
} }
if (line_count == 0) { if (line_count == 0) {
@ -424,20 +424,20 @@ static bool handle_append(struct LineAddress* address) {
static bool handle_delete(struct LineAddress* address) { static bool handle_delete(struct LineAddress* address) {
if (address->empty && address->data.index.i >= (long int) line_count) { 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; return false;
} }
if (address->type == INDEX) { if (address->type == INDEX) {
delete_lines(address->data.index.i, address->data.index.i); 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) { } else if (address->type == RANGE) {
delete_lines(address->data.range.a, address->data.range.b); 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) { } else if (address->type == SET) {
for (unsigned long i = 0; i < address->data.set.s; i++) { for (unsigned long i = 0; i < address->data.set.s; i++) {
delete_lines(address->data.set.b[i], address->data.set.b[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; return true;
} }
@ -446,7 +446,7 @@ static bool get_file_name(char** filename) {
size_t len = strlen(*filename); size_t len = strlen(*filename);
if (len < 1 || (len == 1 && **filename == '\n')) { if (len < 1 || (len == 1 && **filename == '\n')) {
if (default_filename == NULL) { if (default_filename == NULL) {
fprintf(stderr, "error: no default filename specified\n"); error_s("no default filename specified\n");
return false; return false;
} }
*filename = default_filename; *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) { static void write_file(char* filename, struct LineAddress* address, char* type) {
if (line_count < 1) { if (line_count < 1) {
fprintf(stderr, "error: cannot write empty file\n"); error_s("cannot write empty file\n");
return; return;
} }
if (!get_file_name(&filename)) 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; pending_writes = false;
fclose(file); 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) { static void read_file(char* filename) {
@ -512,7 +512,7 @@ static void read_file(char* filename) {
if (size < 1) { if (size < 1) {
free(buf); free(buf);
fprintf(stderr, "error: attempted to read a empty file\n"); error_s("attempted to read a empty file\n");
return; return;
} }
@ -522,7 +522,7 @@ static void read_file(char* filename) {
} }
append_lines(line, buf, size); append_lines(line, buf, size);
free(buf); 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) { 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 (cmd == ',') {
if (address.type != INDEX) { 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); free_address(address);
return; return;
} }
@ -593,7 +593,7 @@ static void prompt(void) {
return; return;
} }
if (address2.type != INDEX) { 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(address);
free_address(address2); free_address(address2);
return; return;
@ -606,7 +606,7 @@ static void prompt(void) {
} }
if (address.type == RANGE && address.data.range.a > address.data.range.b) { 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); free_address(address);
return; return;
} }
@ -618,10 +618,10 @@ test:
case '\n': case '\n':
if (address.empty) { if (address.empty) {
if (line_current == line_count) { 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; break;
} else if (line_current + 1 == line_count) { } 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; break;
} else { } else {
line_current++; line_current++;
@ -634,7 +634,7 @@ test:
} else if (address.type == RANGE) { } else if (address.type == RANGE) {
line_current = address.data.range.b; line_current = address.data.range.b;
} else if (address.type == SET) { } else if (address.type == SET) {
fprintf(stderr, "error: unexpected range addressing\n"); error_s("unexpected range addressing\n");
break; break;
} }
printf("%s", lines[line_current]); printf("%s", lines[line_current]);
@ -656,7 +656,7 @@ test:
__attribute__((fallthrough)); __attribute__((fallthrough));
case 'p': case 'p':
if (address.empty && address.data.index.i >= (long int) line_count) { 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; break;
} }
if (address.type == INDEX) { if (address.type == INDEX) {
@ -686,7 +686,7 @@ test:
skip_whitespace(&index); skip_whitespace(&index);
free_address(address); free_address(address);
if (*(index++) != '/') { if (*(index++) != '/') {
fprintf(stderr, "error: unexpected character at start of regex\n"); error_s("unexpected character at start of regex\n");
break; break;
} }
if (!parse_regex(&index, &address, ALL)) { return; } if (!parse_regex(&index, &address, ALL)) { return; }
@ -701,18 +701,18 @@ test:
case 's': case 's':
skip_whitespace(&index); skip_whitespace(&index);
if (*(index++) != '/') { if (*(index++) != '/') {
fprintf(stderr, "error: unexpected character at start of regex\n"); error_s("unexpected character at start of regex\n");
break; break;
} }
if (!parse_regex_lines(&index, &address)) { return; } if (!parse_regex_lines(&index, &address)) { return; }
char* replace = index; char* replace = index;
while(*index != '\0' && *index != '/') index++; while(*index != '\0' && *index != '/') index++;
if (*index != '/') { if (*index != '/') {
fprintf(stderr, "error: / missing after %c\n", *index); error_s("/ missing after %c\n", *index);
break; break;
} }
if (address.data.set.s < 1) { if (address.data.set.s < 1) {
printf("ed: no matches found\n"); error_s("no matches found\n");
break; break;
} }
*(index++) = '\0'; *(index++) = '\0';
@ -725,11 +725,11 @@ test:
char* end; char* end;
matches = strtol(index, &end, 10); matches = strtol(index, &end, 10);
if (end == index) { if (end == index) {
fprintf(stderr, "error: invalid number: %s\n", index); error_s("invalid number: %s\n", index);
break; break;
} }
if (matches < 1) { if (matches < 1) {
fprintf(stderr, "error: matches cannot be less than 1\n"); error_s("matches cannot be less than 1\n");
break; break;
} }
} }
@ -739,7 +739,7 @@ test:
for (unsigned long i = 0; i < address.data.set.s; i++) { 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); 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; pending_writes = true;
break; break;
case 'w': { case 'w': {
@ -782,7 +782,7 @@ test:
printf("%ld\n", line_current + 1); printf("%ld\n", line_current + 1);
break; break;
default: default:
fprintf(stderr, "error: unimplemented command\n"); error_s("unimplemented command\n");
break; break;
} }

View file

@ -9,7 +9,7 @@ COMMAND_EMPTY(groups) {
struct passwd* pw = getpwuid(uid); struct passwd* pw = getpwuid(uid);
if(pw == NULL){ if(pw == NULL){
perror("error: failed to fetch groups: "); error("failed to fetch groups: %s", strerror(errno));
} }
int ngroups = 0; int ngroups = 0;
@ -21,7 +21,7 @@ COMMAND_EMPTY(groups) {
for (int i = 0; i < ngroups; i++){ for (int i = 0; i < ngroups; i++){
struct group* gr = getgrgid(groups[i]); struct group* gr = getgrgid(groups[i]);
if(gr == NULL){ if(gr == NULL){
perror("error: failed to fetch groups: "); error("failed to fetch groups: %s", strerror(errno));
} }
printf("%s ",gr->gr_name); printf("%s ",gr->gr_name);
} }

View file

@ -70,7 +70,7 @@ static int short_arg(char c, char* next) {
check_arg(next); check_arg(next);
long int bkm = get_blkm(next); long int bkm = get_blkm(next);
if (bkm < 1) { if (bkm < 1) {
error("error: bkm cannot be less than 1"); error("bkm cannot be less than 1");
} }
flags.count = bkm; flags.count = bkm;
return ARG_USED; return ARG_USED;
@ -80,7 +80,7 @@ static int short_arg(char c, char* next) {
check_arg(next); check_arg(next);
long int bkm = get_blkm(next); long int bkm = get_blkm(next);
if (bkm < 1) { if (bkm < 1) {
error("error: bkm cannot be less than 1"); error("bkm cannot be less than 1");
} }
flags.count = bkm; flags.count = bkm;
return ARG_USED; return ARG_USED;
@ -91,9 +91,8 @@ static int short_arg(char c, char* next) {
case 'v': case 'v':
flags.print_headers = true; flags.print_headers = true;
break; break;
default: { default:
error("error: unknown option -%c", c); return ARG_INVALID;
}
} }
return ARG_UNUSED; return ARG_UNUSED;
} }

View file

@ -10,7 +10,7 @@ COMMAND_EMPTY(user_id) {
struct passwd* pw = getpwuid(uid); struct passwd* pw = getpwuid(uid);
if(pw == NULL){ if(pw == NULL){
perror("error: failed to fetch groups: "); error("failed to fetch groups: %s", strerror(errno));
} }
int ngroups = 0; int ngroups = 0;
@ -30,7 +30,7 @@ COMMAND_EMPTY(user_id) {
for (int i = 0; i < ngroups; i++){ for (int i = 0; i < ngroups; i++){
struct group* gr = getgrgid(groups[i]); struct group* gr = getgrgid(groups[i]);
if(gr == NULL){ 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); printf("%d(%s)", gr->gr_gid, gr->gr_name);
if (i + 1 < ngroups) putchar(','); if (i + 1 < ngroups) putchar(',');

View file

@ -55,9 +55,9 @@ static DIR* get_directory(char* path) {
DIR* d = opendir(path); DIR* d = opendir(path);
if (d == NULL) { if (d == NULL) {
if (errno == ENOTDIR) { if (errno == ENOTDIR) {
printf("\x1b[0m%s is a a file\n", path); error_s("`%s` is a a file\n", path);
} else { } 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; 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); int save = push_path_buffer(file_name);
if (lstat(get_path_buffer(), &s) < 0) { 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); pop_path_buffer(save);
return false; return false;
} }
@ -156,14 +156,14 @@ static bool get_file_info(const char* file_name, struct FileInfo* info) {
info->usr = getpwuid(s.st_uid); info->usr = getpwuid(s.st_uid);
if (info->usr == NULL) { 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); pop_path_buffer(save);
return false; return false;
} }
info->grp = getgrgid(s.st_gid); info->grp = getgrgid(s.st_gid);
if (info->grp == NULL) { 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); pop_path_buffer(save);
return false; return false;
} }
@ -288,10 +288,6 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
if (!flags.more_info) printf("\n"); 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) { static int num_places (int n) {
int r = 1; int r = 1;
if (n < 0) n = (n == INT_MIN) ? INT_MAX: -n; 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; flags.more_info = true;
break; break;
default: default:
error("error: unkown option -%c", c); return ARG_INVALID;
} }
return ARG_UNUSED; return ARG_UNUSED;
} }
@ -481,7 +477,7 @@ static int long_arg(char* cur, char* next) {
} else if (streql("no", arg) || streql("never", arg)) { } else if (streql("no", arg) || streql("never", arg)) {
flags.colored = NO; flags.colored = NO;
} else { } else {
error("error: invalid color options: %s", arg); error("invalid color options: %s", arg);
} }
} else { } else {
return ARG_IGNORE; return ARG_IGNORE;
@ -496,7 +492,7 @@ COMMAND(ls) {
flags.hide_dot = false; flags.hide_dot = false;
flags.one_column = false; flags.one_column = false;
flags.recurse = false; flags.recurse = false;
flags.colored = AUTO; flags.colored = NO;
int start = parse_args(argc, argv, help, short_arg, long_arg); 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) { COMMAND(print) {
if (argc < 1) { if (argc < 1) {
error("usage: printf FORMAT [ARG]...\n"); global_help(help);
return EXIT_SUCCESS;
} }
parse_help(argc, argv, help); 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); check_arg(next);
long int bkm = get_blkm(next); long int bkm = get_blkm(next);
if (bkm < 1) { if (bkm < 1) {
error("error: bkm cannot be less than 1"); error("bkm cannot be less than 1");
} }
flags.count = bkm; flags.count = bkm;
return ARG_USED; return ARG_USED;
@ -141,7 +141,7 @@ static int short_arg(char c, char* next) {
check_arg(next); check_arg(next);
long int bkm = get_blkm(next); long int bkm = get_blkm(next);
if (bkm < 1) { if (bkm < 1) {
error("error: bkm cannot be less than 1"); error("bkm cannot be less than 1");
} }
flags.count = bkm; flags.count = bkm;
return ARG_USED; return ARG_USED;
@ -159,14 +159,13 @@ static int short_arg(char c, char* next) {
check_arg(next); check_arg(next);
long int sec = get_number(next); long int sec = get_number(next);
if (sec < 1) { if (sec < 1) {
error("error: wait seconds cannot be less than 1"); error("wait seconds cannot be less than 1");
} }
flags.grow_wait = sec; flags.grow_wait = sec;
return ARG_USED; return ARG_USED;
} }
default: { default:
error("error: unknown option -%c", c); return ARG_INVALID;
}
} }
return ARG_UNUSED; return ARG_UNUSED;
} }

View file

@ -41,7 +41,7 @@ static int short_arg(char c, char* next) {
flags.handle_sigint = true; flags.handle_sigint = true;
break; break;
default: default:
error("error: unkown option: %c", c); return ARG_INVALID;
} }
return ARG_UNUSED; return ARG_UNUSED;
} }

View file

@ -111,7 +111,7 @@ static int short_arg(char c, char* next) {
flags.longest_line = true; flags.longest_line = true;
break; break;
default: default:
error("error: invald option -%c", c); return ARG_INVALID;
} }
flags.has_flags = true; flags.has_flags = true;
return ARG_UNUSED; return ARG_UNUSED;

View file

@ -45,10 +45,12 @@ static int short_arg(char c, char* next) {
check_arg(next); check_arg(next);
long int n = get_number(next); long int n = get_number(next);
if (n < 1) { 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; flags.max_args = n;
return ARG_USED; return ARG_USED;
default:
return ARG_INVALID;
} }
return ARG_UNUSED; return ARG_UNUSED;
} }

View file

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

View file

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