summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-04-28 20:32:18 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-04-28 20:32:18 -0400
commita94b5bcd943ff91c90d04000d6896fdaf5070392 (patch)
tree531451b0682e1b63a9b45870258e16e55900d8d1
parentls tty fix (diff)
downloadlazysphere-a94b5bcd943ff91c90d04000d6896fdaf5070392.tar.gz
lazysphere-a94b5bcd943ff91c90d04000d6896fdaf5070392.tar.bz2
lazysphere-a94b5bcd943ff91c90d04000d6896fdaf5070392.zip
added tail and head
-rw-r--r--readme.md2
-rw-r--r--src/command.h1
-rw-r--r--src/commands/head.c145
-rw-r--r--src/commands/ls.c50
-rw-r--r--src/commands/tail.c206
-rw-r--r--src/main.c4
-rw-r--r--src/shared.c25
-rw-r--r--src/shared.h1
8 files changed, 384 insertions, 50 deletions
diff --git a/readme.md b/readme.md
index 3440299..8308f19 100644
--- a/readme.md
+++ b/readme.md
@@ -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`
+`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`
## How to
diff --git a/src/command.h b/src/command.h
index 939886d..0b7b8ad 100644
--- a/src/command.h
+++ b/src/command.h
@@ -18,3 +18,4 @@ COMMAND_EMPTY(groups);
COMMAND_EMPTY(user_id);
COMMAND(ls);
COMMAND(tail);
+COMMAND(head);
diff --git a/src/commands/head.c b/src/commands/head.c
new file mode 100644
index 0000000..446a9e6
--- /dev/null
+++ b/src/commands/head.c
@@ -0,0 +1,145 @@
+#include "../command.h"
+
+#include <stdio.h>
+#include <string.h>
+
+struct Flags {
+ int count;
+ bool lines;
+ bool print_headers;
+ bool dont_print_headers;
+};
+
+static void head_file_lines(FILE* file, struct Flags* flags) {
+ size_t len = 0;
+ char* line = NULL;
+
+ int count = flags->count;
+ while(count > 0 && getline(&line, &len, file) != -1) {
+ printf("%s", line);
+ count--;
+ }
+
+ free(line);
+ fclose(file);
+}
+
+static void head_file_chars(FILE* file, struct Flags* flags) {
+ char c;
+ int count = flags->count;
+ while(count > 0 && (c = getc(file)) != EOF) {
+ putchar(c);
+ count--;
+ }
+
+ fclose(file);
+}
+
+static void help() {
+ printf("Usage: head [OPTIONS] [FILE]...\n\n");
+ printf("Print first 10 lines of FILEs (or stdin)\n");
+ printf("With more than one FILE, precede each with a filename header.\n\n");
+ printf("\t-c [+]N[bkm]\tPrint first N bytes\n");
+ printf("\t-n N[bkm]\tPrint first N lines\n");
+ printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n");
+ printf("\t-q\t\tNever print headers\n");
+ printf("\t-v\t\tAlways print headers\n");
+ exit(EXIT_SUCCESS);
+}
+
+static char* next_arg(int argc, char** argv, int index) {
+ if (index >= argc) {
+ error("error: expected another argument after option");
+ }
+ return argv[index];
+}
+
+static void print_header(char* path, struct Flags* flags, bool many) {
+ if (flags->dont_print_headers) return;
+ if (!many && !flags->print_headers) return;
+ if (streql("-", path)) {
+ printf("\n==> standard input <==\n");
+ } else {
+ printf("\n=>> %s <==\n", path);
+ }
+}
+
+static void head_file(char* path, struct Flags* flags, bool many) {
+ FILE* file = get_file(path, "r");
+ print_header(path, flags, many);
+ if (flags->lines) {
+ head_file_lines(file, flags);
+ } else {
+ head_file_chars(file, flags);
+ }
+}
+
+COMMAND(head) {
+ struct Flags flags;
+ flags.count = 10;
+ flags.lines = true;
+ flags.print_headers = false;
+ flags.dont_print_headers = false;
+
+ int start = 0;
+ for (int i = 0; i < argc; i++) {
+ if (!prefix("-", argv[i])) break;
+ if (streql(argv[0], "--help")) help();
+ start++;
+ int i_s = i;
+ for (size_t j = 1; j < strlen(argv[i_s]); j++) {
+ char c = argv[i_s][j];
+ switch(c) {
+ case 'c': {
+ start++;
+ flags.lines = false;
+ char* arg = next_arg(argc, argv, ++i);
+ long int bkm = get_blkm(arg);
+ if (bkm < 1) {
+ error("error: bkm cannot be less than 1");
+ }
+ flags.count = bkm;
+ break;
+ }
+ case 'n': {
+ start++;
+ flags.lines = true;
+ char* arg = next_arg(argc, argv, ++i);
+ long int bkm = get_blkm(arg);
+ if (bkm < 1) {
+ error("error: bkm cannot be less than 1");
+ }
+ flags.count = bkm;
+ break;
+ }
+ case 'q':
+ flags.dont_print_headers = true;
+ break;
+ case 'v':
+ flags.print_headers = true;
+ break;
+ default: {
+ error("error: unknown option -%c", c);
+ }
+ }
+ }
+ }
+
+ int count = argc - start;
+
+ if (count < 1) {
+ head_file_lines(stdin, &flags);
+ return EXIT_SUCCESS;
+ }
+
+ if (count == 1) {
+ head_file(argv[start], &flags, false);
+ return EXIT_SUCCESS;
+ }
+
+ for (int i = 0; i < count; i++) {
+ head_file(argv[start + i], &flags, true);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/commands/ls.c b/src/commands/ls.c
index d31c143..b0b00a5 100644
--- a/src/commands/ls.c
+++ b/src/commands/ls.c
@@ -25,6 +25,7 @@ struct Flags {
bool more_info;
bool one_column;
bool recurse;
+ bool colored;
};
struct FileInfo {
@@ -34,10 +35,11 @@ struct FileInfo {
struct group* grp;
char size[5];
char date[13];
- struct dirent* file;
+ char name[PATH_MAX];
bool set_uid;
bool set_gid;
bool exec;
+ unsigned char type;
};
struct FileListInfo {
@@ -61,14 +63,17 @@ static DIR* get_directory(char* path) {
return d;
}
-static void append_path(char buf[PATH_MAX], const char* dir_path, const struct dirent* file) {
+static void append_path(char buf[PATH_MAX], const char* dir_path, const char* file_path) {
size_t dir_len = strlen(dir_path);
+ size_t file_len = strlen(file_path);
+
memcpy(buf, dir_path, dir_len);
if (buf[dir_len-1] != '/') {
buf[dir_len] = '/';
dir_len++;
}
- memcpy(buf + dir_len, file->d_name, strlen(file->d_name) + 1);
+ memcpy(buf + dir_len, file_path, file_len);
+ buf[dir_len + file_len] = '\0';
}
static bool get_file_info(struct dirent* file, const char* dir_path, struct FileInfo* info) {
@@ -77,17 +82,20 @@ static bool get_file_info(struct dirent* file, const char* dir_path, struct File
gid_t gid = getgid();
char buf[PATH_MAX];
- append_path(buf, dir_path, file);
+ append_path(buf, dir_path, file->d_name);
struct stat s;
+ memset(&s, 0, sizeof(struct stat));
+
if (stat(buf, &s) < 0) {
- printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
+ // printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
return false;
}
info->set_uid = false;
info->set_gid = false;
info->exec = false;
+ info->name[0] = '\0';
if (file->d_type == DT_DIR)
info->mode[0] = 'd';
@@ -137,8 +145,10 @@ static bool get_file_info(struct dirent* file, const char* dir_path, struct File
info->usr = getpwuid(s.st_uid);
info->grp = getgrgid(s.st_gid);
- info->file = file;
info->links = s.st_nlink;
+ info->type = file->d_type;
+
+ strcpy(info->name, file->d_name);
print_file_size(s.st_size, info->size);
print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000, info->date);
@@ -147,9 +157,9 @@ static bool get_file_info(struct dirent* file, const char* dir_path, struct File
static char* get_file_color(struct FileInfo* info) {
char* color;
- if (info->file->d_type == DT_DIR) {
+ if (info->type == DT_DIR) {
color = DIR_COLOR;
- } else if (info->file->d_type == DT_LNK) {
+ } else if (info->type == DT_LNK) {
color = LINK_COLOR;
} else {
if (info->set_uid) {
@@ -171,6 +181,7 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
if (!isatty(1)) {
flags->one_column = true;
+ flags->colored = false;
}
char* color;
@@ -179,7 +190,6 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
for (int i = 0; i < file_len; i++) {
struct FileInfo finfo = files[i];
- if (strlen(finfo.file->d_name) < 1) continue;
color = get_file_color(&finfo);
if (flags->more_info) {
printf("%s %*d %*s %*s %*s %s %s%s\x1b[0m",
@@ -193,12 +203,12 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
info.max_size,
finfo.size,
finfo.date,
- color,
- finfo.file->d_name
+ flags->colored ? color : FILE_COLOR,
+ finfo.name
);
- if (finfo.file->d_type == DT_LNK) {
+ if (finfo.type == DT_LNK) {
char path[PATH_MAX];
- append_path(path, dir_path, finfo.file);
+ append_path(path, dir_path, finfo.name);
char lnk[PATH_MAX];
if (readlink(path, lnk, PATH_MAX) < 0) {
@@ -211,13 +221,13 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
putchar('\n');
}
} else if (flags->one_column) {
- printf("%s%s\x1b[0m\n", color, finfo.file->d_name);
+ printf("%s%s\x1b[0m\n", color, finfo.name);
} else {
if (info.total_len > w.ws_col) {
if (i != 0 && i % row_count == 0) putchar('\n');
- printf("%s%*s\x1b[0m", color, column_width, finfo.file->d_name);
+ printf("%s%*s\x1b[0m", flags->colored ? color : FILE_COLOR, -column_width, finfo.name);
} else {
- printf("%s%s\x1b[0m ", color, finfo.file->d_name);
+ printf("%s%s\x1b[0m ", flags->colored ? color : FILE_COLOR, finfo.name);
}
}
}
@@ -318,7 +328,7 @@ static bool recurse_directory(char* path, struct Flags* flags) {
if (!flags->hidden && prefix(".", file->d_name)) continue;
if (is_dot_dir(file->d_name)) continue;
char buf[PATH_MAX];
- append_path(buf, path, file);
+ append_path(buf, path, file->d_name);
recurse_directory(buf, flags);
}
@@ -374,11 +384,17 @@ COMMAND(ls) {
flags.hide_dot = false;
flags.one_column = false;
flags.recurse = false;
+ flags.colored = false;
+
int start = 0;
for (int i = 0; i < argc; i++) {
if (!prefix("-", argv[i])) break;
if (streql("--help", argv[i])) help();
start++;
+ if (streql("--color=auto", argv[i]) || streql("--color=yes", argv[i])) {
+ flags.colored = true;
+ continue;
+ }
for (size_t j = 1; j < strlen(argv[i]); j++) {
char c = argv[i][j];
switch (c) {
diff --git a/src/commands/tail.c b/src/commands/tail.c
index 04f7f42..24d7b1f 100644
--- a/src/commands/tail.c
+++ b/src/commands/tail.c
@@ -2,19 +2,31 @@
#include <stdio.h>
#include <string.h>
+#include <sys/select.h>
#include <sys/stat.h>
+#include <unistd.h>
-static void tail_file(FILE* file) {
- char* ring[10];
- memset(ring, 0, sizeof(char*) * 10);
+struct Flags {
+ bool lines;
+ int count;
+ bool print_headers;
+ bool dont_print_headers;
+ bool print_as_grow;
+ int grow_wait;
+};
- int ring_len[10];
+static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) {
+ char* ring[count];
+ memset(ring, 0, sizeof(char*) * count);
+
+ int ring_len[count];
int index = 0;
- int size = 0;
+ unsigned int size = 0;
int read;
- size_t len = 0;
+ fseek(file, skip, SEEK_SET);
+ size_t len = skip;
char* line = NULL;
while ((read = getline(&line, &len, file)) != -1) {
@@ -23,57 +35,189 @@ static void tail_file(FILE* file) {
ring_len[index] = read;
index++;
- index %= 10;
- if (size < 10) size++;
+ index %= count;
+ if (size < count) size++;
line = NULL;
}
- index += 10 - size;
- index %= 10;
+ index += count - size;
+ index %= count;
- for (int i = 0; i < size; i++) {
+ for (unsigned int i = 0; i < size; i++) {
fwrite(ring[index], ring_len[index], 1, stdout);
free(ring[index]);
index += 1;
- index %= 10;
+ index %= count;
}
free(line);
fclose(file);
+
+ return len;
+}
+
+static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) {
+ char ring[count];
+ memset(ring, 0, count);
+
+ int index = 0;
+ unsigned int size = 0;
+
+ fseek(file, skip, SEEK_SET);
+ int read = skip;
+ int c;
+ while((c = getc(file)) != EOF) {
+ ring[index] = c;
+ index++;
+ read++;
+ index %= count;
+ if (size < count) size++;
+ }
+
+ index += count - size;
+ index %= count;
+
+ for (unsigned int i = 0; i < size; i++) {
+ putchar(ring[index]);
+ index += 1;
+ index %= count;
+ }
+
+ fclose(file);
+
+ return read;
}
static void help() {
- printf("Usage: tail [FILE]...\n\n");
+ printf("Usage: tail [OPTIONS] [FILE]...\n\n");
printf("Print last 10 lines of FILEs (or stdin) to.\n");
- printf("With more than one FILE, precede each with a filename header.\n");
+ printf("With more than one FILE, precede each with a filename header.\n\n");
+ printf("\t-c [+]N[bkm]\tPrint last N bytes\n");
+ printf("\t-n N[bkm]\tPrint last N lines\n");
+ printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n");
+ printf("\t-q\t\tNever print headers\n");
+ printf("\t-v\t\tAlways print headers\n");
+ printf("\t-f\t\tPrint data as file grows\n");
+ printf("\t-s SECONDS\tWait SECONDS between reads with -f\n");
exit(EXIT_SUCCESS);
}
+static char* next_arg(int argc, char** argv, int index) {
+ if (index >= argc) {
+ error("error: expected another argument after option");
+ }
+ return argv[index];
+}
+
+static void print_header(char* path, struct Flags* flags, bool many) {
+ if (flags->dont_print_headers) return;
+ if (!many && !flags->print_headers) return;
+ if (streql("-", path)) {
+ printf("\n==> standard input <==\n");
+ } else {
+ printf("\n=>> %s <==\n", path);
+ }
+}
+
+static void tail_file(char* path, struct Flags* flags, bool many) {
+ FILE* file = get_file(path, "r");
+ print_header(path, flags, many);
+
+ size_t skip = 0;
+ while (true) {
+ if (flags->lines) {
+ skip = tail_file_lines(file, flags->count, skip);
+ } else {
+ skip = tail_file_chars(file, flags->count, skip);
+ }
+ if (!flags->print_as_grow) break;
+ sleep(flags->grow_wait);
+ get_file(path, "r");
+ };
+}
+
COMMAND(tail) {
- if (argc < 1) {
- tail_file(stdin);
- return EXIT_SUCCESS;
+
+ struct Flags flags;
+ flags.count = 10;
+ flags.dont_print_headers = false;
+ flags.print_headers = false;
+ flags.lines = true;
+ flags.print_as_grow = false;
+ flags.grow_wait = 10;
+
+ int start = 0;
+ for (int i = 0; i < argc; i++) {
+ if (!prefix("-", argv[i])) break;
+ if (streql(argv[0], "--help")) help();
+ start++;
+ int i_s = i;
+ for (size_t j = 1; j < strlen(argv[i_s]); j++) {
+ char c = argv[i_s][j];
+ switch(c) {
+ case 'c': {
+ start++;
+ flags.lines = false;
+ char* arg = next_arg(argc, argv, ++i);
+ long int bkm = get_blkm(arg);
+ if (bkm < 1) {
+ error("error: bkm cannot be less than 1");
+ }
+ flags.count = bkm;
+ break;
+ }
+ case 'n': {
+ start++;
+ flags.lines = true;
+ char* arg = next_arg(argc, argv, ++i);
+ long int bkm = get_blkm(arg);
+ if (bkm < 1) {
+ error("error: bkm cannot be less than 1");
+ }
+ flags.count = bkm;
+ break;
+ }
+ case 'q':
+ flags.dont_print_headers = true;
+ break;
+ case 'v':
+ flags.print_headers = true;
+ break;
+ case 'f':
+ flags.print_as_grow = true;
+ break;
+ case 's': {
+ start++;
+ char* arg = next_arg(argc, argv, ++i);
+ long int sec = get_number(arg);
+ if (sec < 1) {
+ error("error: wait seconds cannot be less than 1");
+ }
+ flags.grow_wait = sec;
+ break;
+ }
+ default: {
+ error("error: unknown option -%c", c);
+ }
+ }
+ }
}
- if (streql(argv[0], "--help")) help();
+ int count = argc - start;
- if (argc == 1) {
- FILE* file = get_file(argv[0], "r");
- tail_file(file);
+ if (count < 1) {
+ tail_file_lines(stdin, 10, 0);
return EXIT_SUCCESS;
}
- for (int i = 0; i < argc; i++) {
- FILE* file;
- if (streql("-", argv[i])) {
- file = stdin;
- } else {
- file = get_file_s(argv[i], "r");
- if (file == NULL) continue;
- }
- printf("\n==> %s <==\n", argv[i]);
- tail_file(file);
+ if (count == 1) {
+ tail_file(argv[start], &flags, false);
+ return EXIT_SUCCESS;
+ }
+
+ for (int i = 0; i < count; i++) {
+ tail_file(argv[start + i], &flags, true);
}
return EXIT_SUCCESS;
diff --git a/src/main.c b/src/main.c
index ce89210..72e1531 100644
--- a/src/main.c
+++ b/src/main.c
@@ -20,7 +20,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\n");
+ printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head\n");
return EXIT_SUCCESS;
}
argc--;
@@ -52,6 +52,8 @@ int main (ARGUMENTS) {
return ls(NEXT_ARGS);
} else if (streql(cmd, "tail")) {
return tail(NEXT_ARGS);
+ } else if (streql(cmd, "head")) {
+ return head(NEXT_ARGS);
} else {
error("error: invalid command %s", cmd);
}
diff --git a/src/shared.c b/src/shared.c
index 8d0dcca..240e364 100644
--- a/src/shared.c
+++ b/src/shared.c
@@ -34,6 +34,7 @@ FILE* get_file_s(const char* path, const char* type) {
}
FILE* get_file(const char* path, const char* type) {
+ if (streql("-", path)) return stdin;
FILE* file = get_file_s(path, type);
if (file == NULL) exit(EXIT_FAILURE);
return file;
@@ -48,6 +49,30 @@ long int get_number(const char* text) {
return n;
}
+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);
+ }
+ if (*end == '\0') return n;
+ switch (*end) {
+ case 'K':
+ case 'k':
+ return n * 1024;
+ case 'B':
+ case 'b':
+ return n * 512;
+ case 'M':
+ case 'm':
+ return n * 1024 * 1204;
+ default:
+ error("error: invalid bkm type %c", *end);
+ }
+ // shouldnt get here anyways
+ return 0;
+}
+
bool streql(const char* a, const char* b) {
if (*a != *b) return false;
int n = 0;
diff --git a/src/shared.h b/src/shared.h
index 251a993..a542ba6 100644
--- a/src/shared.h
+++ b/src/shared.h
@@ -10,6 +10,7 @@ void error(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);
bool streql(const char* a, const char* b);
bool prefix(const char* pre, const char* str);