diff options
author | Tyler Murphy <tylerm@tylerm.dev> | 2023-04-27 14:38:16 -0400 |
---|---|---|
committer | Tyler Murphy <tylerm@tylerm.dev> | 2023-04-27 14:38:16 -0400 |
commit | 88d3515ae85924b8aea8c0546d33451ee369b7dd (patch) | |
tree | a5b825af7959d79bbd00f0dcfa5987671cb84c1a /src | |
download | lazysphere-88d3515ae85924b8aea8c0546d33451ee369b7dd.tar.gz lazysphere-88d3515ae85924b8aea8c0546d33451ee369b7dd.tar.bz2 lazysphere-88d3515ae85924b8aea8c0546d33451ee369b7dd.zip |
initial
Diffstat (limited to 'src')
-rw-r--r-- | src/command.h | 13 | ||||
-rw-r--r-- | src/commands/cat.c | 152 | ||||
-rw-r--r-- | src/commands/dd.c | 59 | ||||
-rw-r--r-- | src/commands/yes.c | 26 | ||||
-rw-r--r-- | src/main.c | 49 | ||||
-rw-r--r-- | src/shared.c | 46 | ||||
-rw-r--r-- | src/shared.h | 15 |
7 files changed, 360 insertions, 0 deletions
diff --git a/src/command.h b/src/command.h new file mode 100644 index 0000000..4f44ab0 --- /dev/null +++ b/src/command.h @@ -0,0 +1,13 @@ +#include "shared.h" + +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> + +#define ARGUMENTS int argc, char** argv +#define NEXT_ARGS argc - 1, &argv[1] +#define COMMAND(name) int name (ARGUMENTS) + +COMMAND(dd); +COMMAND(cat); +COMMAND(yes); diff --git a/src/commands/cat.c b/src/commands/cat.c new file mode 100644 index 0000000..e2c471b --- /dev/null +++ b/src/commands/cat.c @@ -0,0 +1,152 @@ +#include "../command.h" + +#include <ctype.h> +#include <string.h> + +struct Flags { + bool number_lines; + bool number_non_empty; + bool change_non_print; + bool change_tabs; + bool end_lines_dollar; +}; + +static bool printable(char c) { + switch (c) { + case '\n': return true; + case '\r': return true; + case '\b': return true; + case '\t': return true; + default: return isprint(c); + } +} + +static void help() { + printf("Usage: cat [-nbvteA] [FILE]...\n\n"); + printf("Print FILEs to stdout\n\n"); + printf("-n Number output lines\n"); + printf("-b Number nonempty lines\n"); + printf("-v Show nonprinting characters as ^x or M-x\n"); + printf("-t ...and tabs as ^I\n"); + printf("-e ...and end lines with $\n"); + printf("-A Same as -vte\n"); + exit(EXIT_SUCCESS); +} + +static void cat_file(FILE* file, struct Flags flags) { + char c; + size_t read; + + size_t line = 1; + bool empty = true; + bool newline = true; + + while ((read = fread(&c, 1, 1, file)) != 0) { + if (c == '\n') { + if (empty && flags.number_lines) { + printf("\t%ld ", line); + } + if (flags.end_lines_dollar) { + printf("$"); + } + line++; + newline = true; + empty = true; + goto print; + } else { + empty = false; + } + + if (!newline) { + goto print; + } + + if (!empty && (flags.number_non_empty || flags.number_lines)) { + printf("\t%ld ", line); + newline = false; + } + print: + if (!flags.change_non_print || printable(c)) { + if (flags.change_tabs && c == '\t') { + fwrite("^I", 1, 2, stdout); + } else { + fwrite(&c, 1, 1, stdout); + } + } else { + c |= '@'; + fwrite(&c, 1, 1, stdout); + } + } +} + +COMMAND(cat) { + + struct Flags flags; + flags.number_lines = false; + flags.number_non_empty = false; + flags.change_non_print = false; + flags.change_tabs = false; + flags.end_lines_dollar = false; + + for (int i = 0; i < argc; i++) { + if (!prefix("-", argv[i])) { + continue; + } + + if (streql("--help", argv[i])) { + help(); + } + + size_t len = strlen(argv[i] + 1); + for (size_t j = 0; j < len; j++) { + char c = argv[i][j + 1]; + switch (c) { + case 'n': + flags.number_lines = true; + break; + case 'b': + flags.number_non_empty = true; + break; + case 'v': + flags.change_non_print = true; + break; + case 't': + flags.change_non_print = true; + flags.change_tabs = true; + break; + case 'e': + flags.change_non_print = true; + flags.end_lines_dollar = true; + break; + case 'A': + flags.change_non_print = true; + flags.change_tabs = true; + flags.end_lines_dollar = true; + break; + default: + error("error: unkown flag -%c", c); + } + } + } + + bool files = false; + for (int i = 0; i < argc; i++) { + if (streql("-", argv[i])) { + files = true; + cat_file(stdin, flags); + } else if (prefix("-", argv[i])) { + continue; + } else { + files = true; + FILE* in = get_file(argv[i], "r"); + cat_file(in, flags); + fclose(in); + } + } + + if (!files) { + cat_file(stdin, flags); + } + + return EXIT_SUCCESS; +} diff --git a/src/commands/dd.c b/src/commands/dd.c new file mode 100644 index 0000000..278dee3 --- /dev/null +++ b/src/commands/dd.c @@ -0,0 +1,59 @@ +#include "../command.h" +#include <stdio.h> + +static void help() { + printf("Usage: dd [if=FILE] [of=FILE] [bs=N] [count=N]\n\n"); + printf("Copy a file with converting and formatting\n\n"); + printf("if=FILE\t\tRead from FILE instead of stdin\n"); + printf("of=FILE\t\tWrite to FILE instead of stdout\n"); + printf("bs=N\t\tRead and write N bytes at a time\n"); + printf("count=N\t\tCopy only N input blocks\n"); + exit(EXIT_SUCCESS); +} + +COMMAND(dd) { + + FILE* in_file = stdin; + FILE* out_file = stdout; + int bs = 1024; + int count = -1; + + for (int i = 0; i < argc; i++) { + if (prefix("if=", argv[i])) { + char* path = argv[i] + 3; + in_file = get_file(path, "rb"); + } else if (prefix("of=", argv[i])) { + char* path = argv[i] + 3; + out_file = get_file(path, "wb"); + } else if (prefix("bs=", argv[i])) { + char* str = argv[i] + 3; + bs = get_number(str); + if (bs < 1) { + error("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"); + } + } else if (streql("--help", argv[i])) { + help(); + } else { + error("error: unkown option %s", argv[i]); + } + } + + char* buffer = malloc(bs); + size_t read; + while ((read = fread(buffer, 1, bs, in_file)) != 0) { + fwrite(buffer, 1, read, out_file); + if (--count, count == 0) break; + } + + free(buffer); + fclose(in_file); + fclose(out_file); + + return EXIT_SUCCESS; +} diff --git a/src/commands/yes.c b/src/commands/yes.c new file mode 100644 index 0000000..c954810 --- /dev/null +++ b/src/commands/yes.c @@ -0,0 +1,26 @@ +#include "../command.h" + +static void help() { + printf("Usage: yes [STRING]\n\n"); + printf("Repeatedly output a line with all specified STRING(s), or 'y'.\n"); + exit(EXIT_SUCCESS); +} + +COMMAND(yes) { + const char* repeat; + if (argc == 0) { + repeat = "y"; + } else { + if (streql("--help", argv[0])) { + help(); + } + repeat = argv[0]; + for (int i = 1; i < argc; i++) { + *(argv[i]-1) = ' '; + } + } + + while (true) { + printf("%s\n", repeat); + } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a2582c2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,49 @@ +#include "shared.h" +#include "command.h" + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +int main (ARGUMENTS) { + if (argc < 1) { + error("error: argument 0 missing"); + } + + struct stat buf; + lstat(argv[0], &buf); + + if (!S_ISLNK(buf.st_mode)) { + if (argc < 2) { + printf("usage: lazysphere [function [arguments]...]\n\n"); + printf("currently defined functions:\n"); + printf("\tdd, cat, yes\n"); + return EXIT_SUCCESS; + } + argc--; + argv = &argv[1]; + } + + const char* cmd; + if (strncmp("./", argv[0], 2) == 0) { + cmd = argv[0] + 2; + } else { + cmd = argv[0]; + } + + if (streql(cmd, "dd")) { + return dd(NEXT_ARGS); + } else if (streql(cmd, "cat")) { + return cat(NEXT_ARGS); + } else if (streql(cmd, "yes")) { + return yes(NEXT_ARGS); + } else { + error("error: invalid command %s", cmd); + } + + return EXIT_SUCCESS; +} + diff --git a/src/shared.c b/src/shared.c new file mode 100644 index 0000000..ee3c2f1 --- /dev/null +++ b/src/shared.c @@ -0,0 +1,46 @@ +#include "shared.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +void error(const char* format, ...) { + va_list list; + va_start(list, format); + + vfprintf(stderr, format, list); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); +} + +FILE* get_file(const char* path, const char* type) { + FILE* file = fopen(path, type); + if (file == NULL) { + error("error: failed to open file %s: %s", path, strerror(errno)); + } + return file; +} + +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); + } + return n; +} + +bool streql(const char* a, const char* b) { + if (*a != *b) return false; + int n = 0; + while (true) { + if (*(a+n) != *(b+n)) return false; + if (*(a+n) == '\0') return true; + ++n; + } +} + +bool prefix(const char* pre, const char* str) { + return strncmp(pre, str, strlen(pre)) == 0; +} diff --git a/src/shared.h b/src/shared.h new file mode 100644 index 0000000..430af4b --- /dev/null +++ b/src/shared.h @@ -0,0 +1,15 @@ +#pragma once + +#include <stdio.h> +#include <stdbool.h> + +__attribute__ ((__format__(printf, 1, 2))) +void error(const char* format, ...); + +FILE* get_file(const char* path, const char* type); + +long int get_number(const char* text); + +bool streql(const char* a, const char* b); + +bool prefix(const char* pre, const char* str); |