added tail and head

This commit is contained in:
Murphy 2023-04-28 20:32:18 -04:00
parent 4403e7edc2
commit a94b5bcd94
8 changed files with 385 additions and 51 deletions

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` `dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`
## How to ## How to

View file

@ -18,3 +18,4 @@ COMMAND_EMPTY(groups);
COMMAND_EMPTY(user_id); COMMAND_EMPTY(user_id);
COMMAND(ls); COMMAND(ls);
COMMAND(tail); COMMAND(tail);
COMMAND(head);

145
src/commands/head.c Normal file
View file

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

View file

@ -25,6 +25,7 @@ struct Flags {
bool more_info; bool more_info;
bool one_column; bool one_column;
bool recurse; bool recurse;
bool colored;
}; };
struct FileInfo { struct FileInfo {
@ -34,10 +35,11 @@ struct FileInfo {
struct group* grp; struct group* grp;
char size[5]; char size[5];
char date[13]; char date[13];
struct dirent* file; char name[PATH_MAX];
bool set_uid; bool set_uid;
bool set_gid; bool set_gid;
bool exec; bool exec;
unsigned char type;
}; };
struct FileListInfo { struct FileListInfo {
@ -61,14 +63,17 @@ static DIR* get_directory(char* path) {
return d; 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 dir_len = strlen(dir_path);
size_t file_len = strlen(file_path);
memcpy(buf, dir_path, dir_len); memcpy(buf, dir_path, dir_len);
if (buf[dir_len-1] != '/') { if (buf[dir_len-1] != '/') {
buf[dir_len] = '/'; buf[dir_len] = '/';
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) { 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(); gid_t gid = getgid();
char buf[PATH_MAX]; char buf[PATH_MAX];
append_path(buf, dir_path, file); append_path(buf, dir_path, file->d_name);
struct stat s; struct stat s;
memset(&s, 0, sizeof(struct stat));
if (stat(buf, &s) < 0) { 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; return false;
} }
info->set_uid = false; info->set_uid = false;
info->set_gid = false; info->set_gid = false;
info->exec = false; info->exec = false;
info->name[0] = '\0';
if (file->d_type == DT_DIR) if (file->d_type == DT_DIR)
info->mode[0] = 'd'; 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->usr = getpwuid(s.st_uid);
info->grp = getgrgid(s.st_gid); info->grp = getgrgid(s.st_gid);
info->file = file;
info->links = s.st_nlink; 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_file_size(s.st_size, info->size);
print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000, info->date); 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) { static char* get_file_color(struct FileInfo* info) {
char* color; char* color;
if (info->file->d_type == DT_DIR) { if (info->type == DT_DIR) {
color = DIR_COLOR; color = DIR_COLOR;
} else if (info->file->d_type == DT_LNK) { } else if (info->type == DT_LNK) {
color = LINK_COLOR; color = LINK_COLOR;
} else { } else {
if (info->set_uid) { if (info->set_uid) {
@ -171,6 +181,7 @@ static void list_files(struct FileInfo* files, int file_len, struct FileListInfo
if (!isatty(1)) { if (!isatty(1)) {
flags->one_column = true; flags->one_column = true;
flags->colored = false;
} }
char* color; 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++) { for (int i = 0; i < file_len; i++) {
struct FileInfo finfo = files[i]; struct FileInfo finfo = files[i];
if (strlen(finfo.file->d_name) < 1) continue;
color = get_file_color(&finfo); color = get_file_color(&finfo);
if (flags->more_info) { if (flags->more_info) {
printf("%s %*d %*s %*s %*s %s %s%s\x1b[0m", 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, info.max_size,
finfo.size, finfo.size,
finfo.date, finfo.date,
color, flags->colored ? color : FILE_COLOR,
finfo.file->d_name finfo.name
); );
if (finfo.file->d_type == DT_LNK) { if (finfo.type == DT_LNK) {
char path[PATH_MAX]; char path[PATH_MAX];
append_path(path, dir_path, finfo.file); append_path(path, dir_path, finfo.name);
char lnk[PATH_MAX]; char lnk[PATH_MAX];
if (readlink(path, lnk, PATH_MAX) < 0) { 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'); putchar('\n');
} }
} else if (flags->one_column) { } 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 { } else {
if (info.total_len > w.ws_col) { if (info.total_len > w.ws_col) {
if (i != 0 && i % row_count == 0) putchar('\n'); 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 { } 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 (!flags->hidden && prefix(".", file->d_name)) continue;
if (is_dot_dir(file->d_name)) continue; if (is_dot_dir(file->d_name)) continue;
char buf[PATH_MAX]; char buf[PATH_MAX];
append_path(buf, path, file); append_path(buf, path, file->d_name);
recurse_directory(buf, flags); recurse_directory(buf, flags);
} }
@ -374,11 +384,17 @@ 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 = false;
int start = 0; int start = 0;
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
if (!prefix("-", argv[i])) break; if (!prefix("-", argv[i])) break;
if (streql("--help", argv[i])) help(); if (streql("--help", argv[i])) help();
start++; 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++) { for (size_t j = 1; j < strlen(argv[i]); j++) {
char c = argv[i][j]; char c = argv[i][j];
switch (c) { switch (c) {

View file

@ -2,19 +2,31 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/select.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
static void tail_file(FILE* file) { struct Flags {
char* ring[10]; bool lines;
memset(ring, 0, sizeof(char*) * 10); 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 index = 0;
int size = 0; unsigned int size = 0;
int read; int read;
size_t len = 0; fseek(file, skip, SEEK_SET);
size_t len = skip;
char* line = NULL; char* line = NULL;
while ((read = getline(&line, &len, file)) != -1) { while ((read = getline(&line, &len, file)) != -1) {
@ -23,57 +35,189 @@ static void tail_file(FILE* file) {
ring_len[index] = read; ring_len[index] = read;
index++; index++;
index %= 10; index %= count;
if (size < 10) size++; if (size < count) size++;
line = NULL; line = NULL;
} }
index += 10 - size; index += count - size;
index %= 10; 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); fwrite(ring[index], ring_len[index], 1, stdout);
free(ring[index]); free(ring[index]);
index += 1; index += 1;
index %= 10; index %= count;
} }
free(line); free(line);
fclose(file); 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() { 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("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); exit(EXIT_SUCCESS);
} }
COMMAND(tail) { static char* next_arg(int argc, char** argv, int index) {
if (argc < 1) { if (index >= argc) {
tail_file(stdin); error("error: expected another argument after option");
return EXIT_SUCCESS; }
return argv[index];
} }
if (streql(argv[0], "--help")) help(); static void print_header(char* path, struct Flags* flags, bool many) {
if (flags->dont_print_headers) return;
if (argc == 1) { if (!many && !flags->print_headers) return;
FILE* file = get_file(argv[0], "r"); if (streql("-", path)) {
tail_file(file); printf("\n==> standard input <==\n");
return EXIT_SUCCESS;
}
for (int i = 0; i < argc; i++) {
FILE* file;
if (streql("-", argv[i])) {
file = stdin;
} else { } else {
file = get_file_s(argv[i], "r"); printf("\n=>> %s <==\n", path);
if (file == NULL) continue;
} }
printf("\n==> %s <==\n", argv[i]); }
tail_file(file);
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) {
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);
}
}
}
}
int count = argc - start;
if (count < 1) {
tail_file_lines(stdin, 10, 0);
return EXIT_SUCCESS;
}
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; return EXIT_SUCCESS;

View file

@ -20,7 +20,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\n"); printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head\n");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
argc--; argc--;
@ -52,6 +52,8 @@ int main (ARGUMENTS) {
return ls(NEXT_ARGS); return ls(NEXT_ARGS);
} else if (streql(cmd, "tail")) { } else if (streql(cmd, "tail")) {
return tail(NEXT_ARGS); return tail(NEXT_ARGS);
} else if (streql(cmd, "head")) {
return head(NEXT_ARGS);
} else { } else {
error("error: invalid command %s", cmd); error("error: invalid command %s", cmd);
} }

View file

@ -34,6 +34,7 @@ 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) {
if (streql("-", path)) return stdin;
FILE* file = get_file_s(path, type); FILE* file = get_file_s(path, type);
if (file == NULL) exit(EXIT_FAILURE); if (file == NULL) exit(EXIT_FAILURE);
return file; return file;
@ -48,6 +49,30 @@ long int get_number(const char* text) {
return n; 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) { bool streql(const char* a, const char* b) {
if (*a != *b) return false; if (*a != *b) return false;
int n = 0; int n = 0;

View file

@ -10,6 +10,7 @@ void error(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);
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);