diff --git a/readme.md b/readme.md index 2c02009..50f9067 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`, `head`, `ed` +`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc` ## How to diff --git a/src/command.h b/src/command.h index fb1acb3..ab1ccea 100644 --- a/src/command.h +++ b/src/command.h @@ -20,3 +20,6 @@ COMMAND(ls); COMMAND(tail); COMMAND(head); COMMAND(ed); +COMMAND(tee); +COMMAND(whoami); +COMMAND(wc); diff --git a/src/commands/id.c b/src/commands/id.c index f1790bc..3990dc8 100644 --- a/src/commands/id.c +++ b/src/commands/id.c @@ -3,6 +3,7 @@ #include #include #include +#include COMMAND_EMPTY(user_id) { diff --git a/src/commands/tee.c b/src/commands/tee.c new file mode 100644 index 0000000..224463c --- /dev/null +++ b/src/commands/tee.c @@ -0,0 +1,75 @@ +#include "../command.h" + +#include +#include +#include + + +static void help() { + printf("Usage: tee [-ai] [FILE]...\n\n"); + printf("Copy stdin to each FILE, and also to stdout\n\n"); + printf("\t-a Append to the given FILEs, don't overwrite\n"); + printf("\t-i Ignore interrupt signals (SIGINT)\n"); + exit(EXIT_SUCCESS); +} + +static void handle(){} + +static void run_tee(int file_count, FILE* files[file_count]) { + char c; + while((c = getchar()) != EOF) { + for (int i = 0; i < file_count; i++) { + fwrite(&c, 1, 1, files[i]); + fflush(files[i]); + } + putchar(c); + } + for (int i = 0; i < file_count; i++) { + fclose(files[i]); + } +} + +COMMAND(tee) { + + bool append = false; + bool handle_sigint = false; + + int start = 0; + for (int i = 0; i < argc; i++) { + if (!prefix("-", argv[i])) break; + if (streql("--help", argv[i])) help(); + + start++; + for (size_t j = 1; j < strlen(argv[i]); j++) { + char o = argv[i][j]; + switch (o) { + case 'a': + append = true; + break; + case 'i': + handle_sigint = true; + break; + default: + error("error: unkown option: %c", o); + } + } + } + + if (handle_sigint) { + signal(SIGINT, handle); + } + + if (argc - start < 1) { + run_tee(0, NULL); + return EXIT_SUCCESS; + } + + FILE* files[argc - start]; + for (int i = start; i < argc; i++) { + FILE* file = get_file(argv[i], append ? "a" : "w"); + files[i - start] = file; + } + + run_tee(argc - start, files); + return EXIT_SUCCESS; +} diff --git a/src/commands/wc.c b/src/commands/wc.c new file mode 100644 index 0000000..7132370 --- /dev/null +++ b/src/commands/wc.c @@ -0,0 +1,161 @@ +#include "../command.h" + +#include +#include +#include + +struct Flags { + bool newlines; + bool words; + bool characters; + bool bytes; + bool longest_line; +}; + +static int lines = 0; +static int words = 0; +static int chars = 0; +static int bytes = 0; +static int logst = 0; + +static void list(int l, int w, int c, int b, int lg, struct Flags* flags) { + if (flags->newlines) { + printf("\t%d", l); + } + if (flags->words) { + printf("\t%d", w); + } + if (flags->characters) { + printf("\t%d", c); + } + if (flags->bytes) { + printf("\t%d", b); + } + if (flags->longest_line) { + printf("\t%d", lg); + } +} + +#define BS 1024 + +static bool is_delimiter(char c) { + return c == ' ' || c == '\t' || c == '\n'; +} + +static void run_wc(FILE* file, struct Flags* flags) { + int l = 0, w = 0, c = 0, b = 0, lg = 0; + + bool in_word = false; + int current_length = 0; + + int read; + char buf[BS]; + while ((read = fread(buf, 1, 1024, file)) > 0) { + for (int i = 0; i < read; i++) { + char ch = buf[i]; + b++; + if (ch == '\n') { + l++; + lg = lg > current_length ? lg : current_length; + current_length = 0; + } else { + current_length++; + } + if (isprint(ch) || is_delimiter(ch)) c++; + if (in_word && is_delimiter(ch)) { + in_word = false; + w++; + } else if (!in_word && !is_delimiter(ch) && isprint(ch)) { + in_word = true; + } + } + } + if (in_word) w++; + lg = lg > current_length ? lg : current_length; + list(l, w, c, b, lg, flags); + lines += l; + words += w; + chars += c; + bytes += b; + logst += lg; + if (file != stdin) + fclose(file); +} + +static void help() { + printf("Usage: wc [-cmlwL] [FILE]...\n\n"); + printf("Count lines, words, and bytes for FILEs (or stdin)\n\n"); + printf("\t-c Count bytes\n"); + printf("\t-m Count characters\n"); + printf("\t-l Count newlines\n"); + printf("\t-w Count words\n"); + printf("\t-L Print longest line length\n"); + exit(EXIT_SUCCESS); +} + +COMMAND(wc) { + struct Flags flags; + flags.newlines = false; + flags.words = false; + flags.characters = false; + flags.bytes = false; + flags.longest_line = false; + + bool has_flags = false; + + int start = 0; + for (int i = 0; i < argc; i++) { + if (!prefix("-", argv[i])) break; + if (streql("--help", argv[i])) help(); + + start++; + for (size_t j = 1; j < strlen(argv[i]); j++) { + char c = argv[i][j]; + switch (c) { + case 'c': + flags.bytes = true; + break; + case 'm': + flags.characters = true; + break; + case 'l': + flags.newlines = true; + break; + case 'w': + flags.words = true; + break; + case 'L': + flags.longest_line = true; + break; + default: + error("error: invald option -%c", c); + } + has_flags = true; + } + } + + if (!has_flags) { + flags.newlines = true; + flags.words = true; + flags.characters = true; + } + + if (argc - start < 1) { + run_wc(stdin, &flags); + printf("\n"); + return EXIT_SUCCESS; + } + + for (int i = start; i < argc; i++) { + FILE* file = get_file(argv[i], "r"); + run_wc(file, &flags); + printf("\t%s\n", argv[i]); + } + + if (argc - start > 1) { + list(lines, words, chars, bytes, logst, &flags); + printf("\ttotal\n"); + } + + return EXIT_SUCCESS; +} diff --git a/src/commands/whoami.c b/src/commands/whoami.c new file mode 100644 index 0000000..efc9bcd --- /dev/null +++ b/src/commands/whoami.c @@ -0,0 +1,24 @@ +#include "../command.h" + +#include +#include +#include + +static void help() { + printf("Usage: whoami\n\n"); + printf("Print the username associated with the current effective user id\n"); + exit(EXIT_SUCCESS); +} + +COMMAND(whoami) { + if (argc > 0 && streql("--help", argv[0])) help(); + + uid_t usr = getuid(); + struct passwd* passwd = getpwuid(usr); + if (passwd == NULL) { + printf("\x1b[1;91myou do not exist.\n"); + } else { + printf("%s\n", passwd->pw_name); + } + return EXIT_SUCCESS; +} diff --git a/src/main.c b/src/main.c index cd9a81f..d8ee1b4 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, head, ed\n"); + printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc\n"); return EXIT_SUCCESS; } argc--; @@ -48,7 +48,7 @@ int main (ARGUMENTS) { return groups(); } else if (streql(cmd, "id")) { return user_id(); - } else if (streql(cmd, "ls")) { + } else if (streql(cmd, "ls") || streql(cmd, "dir")) { return ls(NEXT_ARGS); } else if (streql(cmd, "tail")) { return tail(NEXT_ARGS); @@ -56,6 +56,16 @@ int main (ARGUMENTS) { return head(NEXT_ARGS); } else if (streql(cmd, "ed")) { return ed(NEXT_ARGS); + } else if (streql(cmd, "tee")) { + return tee(NEXT_ARGS); + } else if (streql(cmd, "true")) { + return EXIT_SUCCESS; + } else if (streql(cmd, "false")) { + return EXIT_FAILURE; + } else if (streql(cmd, "whoami")) { + return whoami(NEXT_ARGS); + } else if (streql(cmd, "wc")) { + return wc(NEXT_ARGS); } else { error("error: invalid command %s", cmd); }