From 9f3a1c9bddeb662db0a8389e2dd44c5f62e9d7b6 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Wed, 17 Apr 2024 21:06:24 +0200 Subject: [PATCH 01/50] Implemented cat base --- src/cat/cat.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 6e6d777..1512f70 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,6 +1,102 @@ +#include #include +#include +#include +#include -int main(void) { - printf("Hello, World!\n"); +#define NAME "cat (canoutils)" +#define VERSION "1.0.0" +#define AUTHOR "SzAkos04" + +#define print_version() \ + do { \ + printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ + } while (0) + +#define BUF_SIZE 1024 + +char **shift(int *argc, char ***argv); +int cat(char *path); + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, "not enough args\n"); + fprintf(stderr, "see cat --help\n"); + exit(1); + } + char *program = *shift(&argc, &argv); + (void)program; + char *flag = *shift(&argc, &argv); + char *path = (char *)malloc(sizeof(char) * BUF_SIZE); + if (!path) { + perror("could not allocate memory"); + exit(1); + } + if (strcmp(flag, "--version") == 0) { + print_version(); + } else { + strcpy(path, flag); + } + + if (cat(path) != 0) { + free(path); + exit(1); + } + + free(path); + return 0; +} + +char **shift(int *argc, char ***argv) { + char **result = *argv; + *argv += 1; + *argc -= 1; + + return result; +} + +int cat(char *path) { + if (access(path, F_OK | R_OK) != 0) { + fprintf(stderr, "file does not exist\n"); + return 1; + } + + FILE *infile = fopen(path, "r"); + if (!infile) { + perror("could not open file"); + return 1; + } + + // get the size of the file + fseek(infile, 0, SEEK_END); + long file_size = ftell(infile); + fseek(infile, 0, SEEK_SET); + + char *buf = (char *)malloc(sizeof(char) * (file_size + 1)); + if (!buf) { + perror("could not allocate memory"); + fclose(infile); + return 1; + } + + // read the file + size_t files_read = fread(buf, sizeof(char), file_size, infile); + if (files_read != (size_t)file_size) { + fprintf(stderr, "could not read file\n"); + fclose(infile); + free(buf); + return 1; + } + + fclose(infile); + + for (int i = 0; i < file_size; ++i) { + if (isprint(buf[i]) || isspace(buf[i])) { + putchar(buf[i]); + } + } + + free(buf); return 0; -} \ No newline at end of file +} + From 1690fe8edcecbbac34cf427e49bfbb6db89b9186 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Wed, 17 Apr 2024 21:53:17 +0200 Subject: [PATCH 02/50] Implemented new arguments --- src/cat/cat.c | 163 +++++++++++++++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 61 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 1512f70..0bee3f1 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #define NAME "cat (canoutils)" #define VERSION "1.0.0" @@ -10,93 +10,134 @@ #define print_version() \ do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ + printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ } while (0) -#define BUF_SIZE 1024 +#define print_help() \ + do { \ + printf("Usage: cat [OPTION]... [FILE]...\n"); \ + printf("Concatenate FILE(s) to standard output.\n"); \ + } while (0) -char **shift(int *argc, char ***argv); -int cat(char *path); +#define print_incorrect_args() \ + do { \ + printf("incorrect args\n"); \ + printf("see `cat --help`\n"); \ + } while (0) + +#define PATH_LEN 256 +#define ARGS_MAX 16 // number of the max arguments +#define ARGS_LEN 32 + +int cat(int filec, char **paths, int argc, char **argv); int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "not enough args\n"); - fprintf(stderr, "see cat --help\n"); - exit(1); - } - char *program = *shift(&argc, &argv); - (void)program; - char *flag = *shift(&argc, &argv); - char *path = (char *)malloc(sizeof(char) * BUF_SIZE); - if (!path) { - perror("could not allocate memory"); + fprintf(stderr, "see `cat --help`\n"); exit(1); } - if (strcmp(flag, "--version") == 0) { + + if (strcmp(argv[1], "--version") == 0) { + if (argc != 2) { + print_incorrect_args(); + exit(1); + } + print_version(); - } else { - strcpy(path, flag); + return 0; } - if (cat(path) != 0) { - free(path); - exit(1); - } + if (strcmp(argv[1], "--help") == 0) { + if (argc != 2) { + print_incorrect_args(); + exit(1); + } - free(path); - return 0; -} + print_help(); + return 0; + } -char **shift(int *argc, char ***argv) { - char **result = *argv; - *argv += 1; - *argc -= 1; + int filec = 0; // file count + char **paths = (char **)malloc(sizeof(char) * PATH_LEN * ARGS_MAX); + if (!paths) { + perror("could not allocate memory"); + exit(1); + } - return result; -} + // arguments for the `cat` function + int cat_argc = 0; + char **cat_argv = (char **)malloc(sizeof(char) * ARGS_LEN * ARGS_MAX); + if (!cat_argv) { + perror("could not allocate memory"); + free(paths); + exit(1); + } -int cat(char *path) { - if (access(path, F_OK | R_OK) != 0) { - fprintf(stderr, "file does not exist\n"); - return 1; + // parse arguments + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0 + || strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { + cat_argv[cat_argc++] = argv[i]; + } else { + if (access(argv[i], F_OK | R_OK) != 0) { + fprintf(stderr, "file `%s` not found\n", argv[i]); + free(paths); + free(cat_argv); + exit(1); + } + + paths[filec++] = argv[i]; + } } - FILE *infile = fopen(path, "r"); - if (!infile) { - perror("could not open file"); - return 1; + if (cat(filec, paths, cat_argc, cat_argv) != 0) { + free(paths); + free(cat_argv); + exit(1); } - // get the size of the file - fseek(infile, 0, SEEK_END); - long file_size = ftell(infile); - fseek(infile, 0, SEEK_SET); + free(paths); + free(cat_argv); - char *buf = (char *)malloc(sizeof(char) * (file_size + 1)); - if (!buf) { - perror("could not allocate memory"); - fclose(infile); - return 1; - } + return 0; +} - // read the file - size_t files_read = fread(buf, sizeof(char), file_size, infile); - if (files_read != (size_t)file_size) { - fprintf(stderr, "could not read file\n"); - fclose(infile); - free(buf); - return 1; +int cat(int filec, char **paths, int argc, char **argv) { + // cat arguments + /* bool A, b, e, E, n, s, t, T, u, v; */ + bool E = false; // show ends + bool T = false; // show tabs + for (int i = 0; i < argc; ++i) { + if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { + E = true; + } + if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { + T = true; + } } - fclose(infile); + for (int i = 0; i < filec; ++i) { + FILE *infile = fopen(paths[i], "r"); + if (!infile) { + perror("could not open file"); + return 1; + } - for (int i = 0; i < file_size; ++i) { - if (isprint(buf[i]) || isspace(buf[i])) { - putchar(buf[i]); + int ch; + while ((ch = fgetc(infile)) != EOF) { + if (E && ch == '\n') { + putchar('$'); + } + if (T && ch == '\t') { + puts("^I"); + continue; + } + putchar(ch); } - } - free(buf); + fclose(infile); + } return 0; } From ba66476ac159b989d403fd302bb9a36d64f1794d Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Wed, 17 Apr 2024 22:27:39 +0200 Subject: [PATCH 03/50] Implemented reading from stdin --- src/cat/cat.c | 83 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 0bee3f1..e76cf6a 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -9,9 +9,9 @@ #define AUTHOR "SzAkos04" #define print_version() \ - do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ - } while (0) + do { \ + printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ + } while (0) #define print_help() \ do { \ @@ -25,11 +25,13 @@ printf("see `cat --help`\n"); \ } while (0) +#define BUF_MAX_LEN 4096 #define PATH_LEN 256 #define ARGS_MAX 16 // number of the max arguments #define ARGS_LEN 32 int cat(int filec, char **paths, int argc, char **argv); +int print_file(char *buf); int main(int argc, char **argv) { if (argc < 2) { @@ -80,7 +82,7 @@ int main(int argc, char **argv) { || strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { cat_argv[cat_argc++] = argv[i]; } else { - if (access(argv[i], F_OK | R_OK) != 0) { + if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { fprintf(stderr, "file `%s` not found\n", argv[i]); free(paths); free(cat_argv); @@ -103,11 +105,12 @@ int main(int argc, char **argv) { return 0; } +bool E = false; // show ends +bool T = false; // show tabs + int cat(int filec, char **paths, int argc, char **argv) { // cat arguments /* bool A, b, e, E, n, s, t, T, u, v; */ - bool E = false; // show ends - bool T = false; // show tabs for (int i = 0; i < argc; ++i) { if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { E = true; @@ -118,26 +121,74 @@ int cat(int filec, char **paths, int argc, char **argv) { } for (int i = 0; i < filec; ++i) { + if (strcmp(paths[i], "-") == 0) { + char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); + scanf("%s", buf); + if (!buf) { + perror("could not allocate memory"); + return 1; + } + + if (print_file(buf) != 0) { + free(buf); + return 1; + } + free(buf); + + continue; + } FILE *infile = fopen(paths[i], "r"); if (!infile) { perror("could not open file"); return 1; } - int ch; - while ((ch = fgetc(infile)) != EOF) { - if (E && ch == '\n') { - putchar('$'); - } - if (T && ch == '\t') { - puts("^I"); - continue; - } - putchar(ch); + // get the size of the file + fseek(infile, 0, SEEK_END); + long file_size = ftell(infile); + fseek(infile, 0, SEEK_SET); + + char *buf = (char *)malloc(sizeof(char) * (file_size + 1)); + if (!buf) { + perror("could not allocate memory"); + fclose(infile); + return 1; + } + + // read the file into the buffer + size_t files_read = fread(buf, sizeof(char), file_size, infile); + if (files_read != (size_t)file_size) { + fprintf(stderr, "could not read file\n"); + fclose(infile); + free(buf); + return 1; } + buf[file_size] = '\0'; fclose(infile); + + if (print_file(buf) != 0) { + return 1; + } + + free(buf); } return 0; } +int print_file(char *buf) { + for (size_t i = 0; i < strlen(buf); ++i) { + if (E && buf[i] == '\n') { + putchar('$'); + } + if (T && buf[i] == '\t') { + puts("^I"); + continue; + } + + putchar(buf[i]); + } + putchar('\n'); + + return 0; +} From 7bccf6a1663f73d712bc6c30e77a59902943a438 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Wed, 17 Apr 2024 22:34:49 +0200 Subject: [PATCH 04/50] Implemented -n --- src/cat/cat.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index e76cf6a..0d65fb6 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -79,7 +79,8 @@ int main(int argc, char **argv) { // parse arguments for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0 - || strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { + || strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0 + || strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { cat_argv[cat_argc++] = argv[i]; } else { if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { @@ -106,6 +107,7 @@ int main(int argc, char **argv) { } bool E = false; // show ends +bool n = false; // number bool T = false; // show tabs int cat(int filec, char **paths, int argc, char **argv) { @@ -115,12 +117,17 @@ int cat(int filec, char **paths, int argc, char **argv) { if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { E = true; } + if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { + n = true; + printf(" 1 "); + } if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { T = true; } } for (int i = 0; i < filec; ++i) { + // read from stdin if (strcmp(paths[i], "-") == 0) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); scanf("%s", buf); @@ -137,6 +144,8 @@ int cat(int filec, char **paths, int argc, char **argv) { continue; } + + // read file FILE *infile = fopen(paths[i], "r"); if (!infile) { perror("could not open file"); @@ -177,10 +186,16 @@ int cat(int filec, char **paths, int argc, char **argv) { } int print_file(char *buf) { + int lines = 1; for (size_t i = 0; i < strlen(buf); ++i) { if (E && buf[i] == '\n') { putchar('$'); } + if (n && buf[i] == '\n') { + // FIXME: this adds one more line on the end of the file + printf("\n %i ", ++lines); + continue; + } if (T && buf[i] == '\t') { puts("^I"); continue; From 4a0848005b68431547caa3b7505ab0a2fe3a4a81 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Wed, 17 Apr 2024 22:40:48 +0200 Subject: [PATCH 05/50] Improved code maintainabilitiy --- src/cat/cat.c | 58 ++++++++++++++++++--------------------------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 0d65fb6..538b4a3 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -30,7 +30,11 @@ #define ARGS_MAX 16 // number of the max arguments #define ARGS_LEN 32 -int cat(int filec, char **paths, int argc, char **argv); +bool E = false; // show ends +bool n = false; // number +bool T = false; // show tabs + +int cat(int filec, char **paths); int print_file(char *buf); int main(int argc, char **argv) { @@ -67,65 +71,43 @@ int main(int argc, char **argv) { exit(1); } - // arguments for the `cat` function - int cat_argc = 0; - char **cat_argv = (char **)malloc(sizeof(char) * ARGS_LEN * ARGS_MAX); - if (!cat_argv) { - perror("could not allocate memory"); - free(paths); - exit(1); - } - // parse arguments for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0 - || strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0 - || strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { - cat_argv[cat_argc++] = argv[i]; + if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { + E = true; + continue; + } + if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { + n = true; + printf(" 1 "); + continue; + } + if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { + T = true; + continue; } else { if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { fprintf(stderr, "file `%s` not found\n", argv[i]); free(paths); - free(cat_argv); exit(1); } paths[filec++] = argv[i]; + continue; } } - if (cat(filec, paths, cat_argc, cat_argv) != 0) { + if (cat(filec, paths) != 0) { free(paths); - free(cat_argv); exit(1); } free(paths); - free(cat_argv); return 0; } -bool E = false; // show ends -bool n = false; // number -bool T = false; // show tabs - -int cat(int filec, char **paths, int argc, char **argv) { - // cat arguments - /* bool A, b, e, E, n, s, t, T, u, v; */ - for (int i = 0; i < argc; ++i) { - if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { - E = true; - } - if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { - n = true; - printf(" 1 "); - } - if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { - T = true; - } - } - +int cat(int filec, char **paths) { for (int i = 0; i < filec; ++i) { // read from stdin if (strcmp(paths[i], "-") == 0) { From 220a375089b4444a0f67e39eb03fe435a1110a34 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 00:02:32 +0200 Subject: [PATCH 06/50] Updated author info --- src/cat/cat.1 | 2 +- src/cat/cat.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cat/cat.1 b/src/cat/cat.1 index fca0bf6..3cf4c6c 100644 --- a/src/cat/cat.1 +++ b/src/cat/cat.1 @@ -1 +1 @@ -\" TODO \ No newline at end of file +\" TODO diff --git a/src/cat/cat.c b/src/cat/cat.c index 538b4a3..6903605 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -6,7 +6,7 @@ #define NAME "cat (canoutils)" #define VERSION "1.0.0" -#define AUTHOR "SzAkos04" +#define AUTHOR "Akos Szijgyarto (SzAkos04)" #define print_version() \ do { \ From b64992c33209cc6c22abf0b1d9587fd55e174901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szijgy=C3=A1rt=C3=B3=20=C3=81kos?= Date: Thu, 18 Apr 2024 02:06:52 +0200 Subject: [PATCH 07/50] Forgot to free memory --- src/cat/cat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cat/cat.c b/src/cat/cat.c index 6903605..0b6a6cb 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -159,6 +159,7 @@ int cat(int filec, char **paths) { fclose(infile); if (print_file(buf) != 0) { + free(buf); return 1; } From 64adb9cbabc3d79fb82a1c612498632676d8ca8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szijgy=C3=A1rt=C3=B3=20=C3=81kos?= Date: Thu, 18 Apr 2024 11:07:02 +0000 Subject: [PATCH 08/50] Improved `--number` logic --- src/cat/cat.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 0b6a6cb..2b6a7b8 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -79,7 +79,6 @@ int main(int argc, char **argv) { } if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { n = true; - printf(" 1 "); continue; } if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { @@ -118,6 +117,7 @@ int cat(int filec, char **paths) { return 1; } + // FIXME: This only can print out one word if (print_file(buf) != 0) { free(buf); return 1; @@ -170,6 +170,9 @@ int cat(int filec, char **paths) { int print_file(char *buf) { int lines = 1; + if (n) { + printf(" %d ", lines); // print number before the first line + } for (size_t i = 0; i < strlen(buf); ++i) { if (E && buf[i] == '\n') { putchar('$'); From 2d17a2bea5e20fdd6d6d342c826bffaffc6ec069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szijgy=C3=A1rt=C3=B3=20=C3=81kos?= Date: Thu, 18 Apr 2024 12:33:54 +0000 Subject: [PATCH 09/50] Fixed stdin bug --- src/cat/cat.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 2b6a7b8..73e5cb6 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -111,7 +111,8 @@ int cat(int filec, char **paths) { // read from stdin if (strcmp(paths[i], "-") == 0) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); - scanf("%s", buf); + // scanf("%s", buf); + fgets(buf, BUF_MAX_LEN, stdin); if (!buf) { perror("could not allocate memory"); return 1; From 042874deaa97aef64558d20740067da73acc92e8 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 19:47:42 +0200 Subject: [PATCH 10/50] Optimized print loop --- src/cat/cat.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 73e5cb6..0c4a66b 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,28 +1,28 @@ +#include #include #include #include #include -#include #define NAME "cat (canoutils)" #define VERSION "1.0.0" #define AUTHOR "Akos Szijgyarto (SzAkos04)" -#define print_version() \ - do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ +#define print_version() \ + do { \ + printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ } while (0) -#define print_help() \ - do { \ - printf("Usage: cat [OPTION]... [FILE]...\n"); \ - printf("Concatenate FILE(s) to standard output.\n"); \ +#define print_help() \ + do { \ + printf("Usage: cat [OPTION]... [FILE]...\n"); \ + printf("Concatenate FILE(s) to standard output.\n"); \ } while (0) -#define print_incorrect_args() \ - do { \ - printf("incorrect args\n"); \ - printf("see `cat --help`\n"); \ +#define print_incorrect_args() \ + do { \ + printf("incorrect args\n"); \ + printf("see `cat --help`\n"); \ } while (0) #define BUF_MAX_LEN 4096 @@ -111,14 +111,13 @@ int cat(int filec, char **paths) { // read from stdin if (strcmp(paths[i], "-") == 0) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); - // scanf("%s", buf); - fgets(buf, BUF_MAX_LEN, stdin); if (!buf) { perror("could not allocate memory"); return 1; } - // FIXME: This only can print out one word + fgets(buf, BUF_MAX_LEN, stdin); + if (print_file(buf) != 0) { free(buf); return 1; @@ -174,12 +173,12 @@ int print_file(char *buf) { if (n) { printf(" %d ", lines); // print number before the first line } - for (size_t i = 0; i < strlen(buf); ++i) { + int len = strlen(buf); + for (int i = 0; i < len; ++i) { if (E && buf[i] == '\n') { putchar('$'); } - if (n && buf[i] == '\n') { - // FIXME: this adds one more line on the end of the file + if (n && buf[i] == '\n' && buf[i + 1] != '\0') { printf("\n %i ", ++lines); continue; } From a88450d12ad4de136ef8fc3c1e5dbf363c1171db Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 19:49:30 +0200 Subject: [PATCH 11/50] Removed unnecessary newline --- src/cat/cat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 0c4a66b..09d2b63 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -189,7 +189,6 @@ int print_file(char *buf) { putchar(buf[i]); } - putchar('\n'); return 0; } From 3c824ba10fa00f221bc1c9f30ba8474005222af7 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 19:58:24 +0200 Subject: [PATCH 12/50] Implemented --- src/cat/cat.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 09d2b63..0b71d97 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -30,6 +30,7 @@ #define ARGS_MAX 16 // number of the max arguments #define ARGS_LEN 32 +bool b = false; // number nonblank bool E = false; // show ends bool n = false; // number bool T = false; // show tabs @@ -73,12 +74,19 @@ int main(int argc, char **argv) { // parse arguments for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-b") == 0 || + strcmp(argv[i], "--number-nonblank") == 0) { + b = true; + n = false; + continue; + } if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { E = true; continue; } if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { n = true; + b = false; continue; } if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { @@ -171,10 +179,18 @@ int cat(int filec, char **paths) { int print_file(char *buf) { int lines = 1; if (n) { - printf(" %d ", lines); // print number before the first line + printf(" %d ", lines); // print number before the first line + } + if (b) { + // TODO: check for the first non-empty line + printf(" %d ", lines); // print number before the first line } int len = strlen(buf); for (int i = 0; i < len; ++i) { + if (b && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { + printf("\n %i ", ++lines); + continue; + } if (E && buf[i] == '\n') { putchar('$'); } From e340d71c47cc0be4a0939b8083a5aaccc6bdc214 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 20:01:11 +0200 Subject: [PATCH 13/50] Improved variable name readability --- src/cat/cat.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 0b71d97..4922f54 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -30,10 +30,10 @@ #define ARGS_MAX 16 // number of the max arguments #define ARGS_LEN 32 -bool b = false; // number nonblank -bool E = false; // show ends -bool n = false; // number -bool T = false; // show tabs +bool number_nonblank = false; // number nonblank +bool show_ends = false; // show ends +bool number = false; // number +bool show_tabs = false; // show tabs int cat(int filec, char **paths); int print_file(char *buf); @@ -76,21 +76,21 @@ int main(int argc, char **argv) { for (int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--number-nonblank") == 0) { - b = true; - n = false; + number_nonblank = true; + number = false; continue; } if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { - E = true; + show_ends = true; continue; } if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { - n = true; - b = false; + number = true; + number_nonblank = false; continue; } if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { - T = true; + show_tabs = true; continue; } else { if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { @@ -162,7 +162,7 @@ int cat(int filec, char **paths) { free(buf); return 1; } - buf[file_size] = '\0'; + buf[file_size] = '\0'; // make sure the string is null terminated fclose(infile); @@ -178,27 +178,28 @@ int cat(int filec, char **paths) { int print_file(char *buf) { int lines = 1; - if (n) { + if (number) { printf(" %d ", lines); // print number before the first line } - if (b) { + if (number_nonblank) { // TODO: check for the first non-empty line printf(" %d ", lines); // print number before the first line } int len = strlen(buf); for (int i = 0; i < len; ++i) { - if (b && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { + if (number_nonblank && buf[i] == '\n' && buf[i + 1] != '\n' && + buf[i + 1] != '\0') { printf("\n %i ", ++lines); continue; } - if (E && buf[i] == '\n') { + if (show_ends && buf[i] == '\n') { putchar('$'); } - if (n && buf[i] == '\n' && buf[i + 1] != '\0') { + if (number && buf[i] == '\n' && buf[i + 1] != '\0') { printf("\n %i ", ++lines); continue; } - if (T && buf[i] == '\t') { + if (show_tabs && buf[i] == '\t') { puts("^I"); continue; } From cc303d8a0459f4ad6cd7dc16364529f4951565a8 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 20:04:33 +0200 Subject: [PATCH 14/50] Improved error handling, fixed whitespace bug --- src/cat/cat.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 4922f54..105c91a 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -124,7 +124,11 @@ int cat(int filec, char **paths) { return 1; } - fgets(buf, BUF_MAX_LEN, stdin); + if (!fgets(buf, BUF_MAX_LEN, stdin)) { + perror("could not read from stdin"); + free(buf); + return 1; + } if (print_file(buf) != 0) { free(buf); @@ -196,7 +200,7 @@ int print_file(char *buf) { putchar('$'); } if (number && buf[i] == '\n' && buf[i + 1] != '\0') { - printf("\n %i ", ++lines); + printf("\n %i ", ++lines); continue; } if (show_tabs && buf[i] == '\t') { From 14084b1d7bf16a753ab693400c973af4a3776743 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Thu, 18 Apr 2024 23:58:08 +0200 Subject: [PATCH 15/50] Fixed line number bug --- commons.mk | 1 + src/cat/cat.c | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/commons.mk b/commons.mk index d9a1baf..2033aab 100644 --- a/commons.mk +++ b/commons.mk @@ -12,6 +12,7 @@ LDFLAGS += -Wl,--gc-sections BUILD_DIR := $/.build LDFLAGS := -fwhole-program -flto +LDFLAGS += -lm OBJ := $(SRC:%.c=$(BUILD_DIR)/%.o) diff --git a/src/cat/cat.c b/src/cat/cat.c index 105c91a..fda61eb 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -180,27 +181,56 @@ int cat(int filec, char **paths) { return 0; } +#define NUMBER_BEFORE 6 + +#define PRINT_N_SPACES(n) \ + do { \ + for (int i = 0; i < n; ++i) { \ + putchar(' '); \ + } \ + } while (0) + int print_file(char *buf) { int lines = 1; + char line_str[11]; if (number) { - printf(" %d ", lines); // print number before the first line + printf(" %d ", lines); // print number before the first line } if (number_nonblank) { - // TODO: check for the first non-empty line - printf(" %d ", lines); // print number before the first line + if (buf[0] != '\n' && buf[1] != '\0') { + printf(" %d ", lines); // print number before the first line + } } int len = strlen(buf); for (int i = 0; i < len; ++i) { if (number_nonblank && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { - printf("\n %i ", ++lines); + snprintf(line_str, sizeof(line_str), "%d", lines); + int len = strlen(line_str); + // check if the line number is a power of 10 + // one off because the whole thing writes numbers one off + if (log10(lines + 1) == (int)log10(lines + 1)) { + len++; + } + putchar('\n'); + PRINT_N_SPACES(NUMBER_BEFORE - len); + printf("%i ", ++lines); continue; } if (show_ends && buf[i] == '\n') { putchar('$'); } if (number && buf[i] == '\n' && buf[i + 1] != '\0') { - printf("\n %i ", ++lines); + snprintf(line_str, sizeof(line_str), "%d", lines); + int len = strlen(line_str); + // check if the line number is a power of 10 + // one off because the whole thing writes numbers one off + if (log10(lines + 1) == (int)log10(lines + 1)) { + len++; + } + putchar('\n'); + PRINT_N_SPACES(NUMBER_BEFORE - len); + printf("%i ", ++lines); continue; } if (show_tabs && buf[i] == '\t') { From 393e0d9edaef988d25a2f13597e5b47a15350dcd Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Fri, 19 Apr 2024 22:55:48 +0200 Subject: [PATCH 16/50] Implemented usage without files --- src/cat/cat.c | 78 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index fda61eb..c735391 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -41,9 +41,10 @@ int print_file(char *buf); int main(int argc, char **argv) { if (argc < 2) { - fprintf(stderr, "not enough args\n"); - fprintf(stderr, "see `cat --help`\n"); - exit(1); + if (cat(0, NULL) != 0) { + exit(1); + } + return 0; } if (strcmp(argv[1], "--version") == 0) { @@ -74,7 +75,36 @@ int main(int argc, char **argv) { } // parse arguments + // parsing arguments like `-nET` (= `-n -E -T`) for (int i = 1; i < argc; ++i) { + int len = strlen(argv[i]); + if (len > 2 && argv[i][0] == '-' && argv[i][1] != '-') { + for (int j = 1; j < len; ++j) { + switch (argv[i][j]) { + case 'b': + number_nonblank = true; + number = false; + continue; + case 'E': + show_ends = true; + continue; + case 'n': + number = true; + number_nonblank = false; + continue; + case 'T': + show_tabs = true; + continue; + default: + fprintf(stderr, "unknown argument `-%c`", argv[i][j]); + free(paths); + return 1; + } + } + goto parsed; + } + + // parsing individual arguments if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--number-nonblank") == 0) { number_nonblank = true; @@ -105,6 +135,7 @@ int main(int argc, char **argv) { } } +parsed: if (cat(filec, paths) != 0) { free(paths); exit(1); @@ -115,7 +146,38 @@ int main(int argc, char **argv) { return 0; } +int print_stdin(char *buf) { + if (!fgets(buf, BUF_MAX_LEN, stdin)) { + perror("could not read from stdin"); + free(buf); + return 1; + } + + if (print_file(buf) != 0) { + free(buf); + return 1; + } + + return 0; +} + int cat(int filec, char **paths) { + if (filec == 0 || !paths) { + char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); + if (!buf) { + perror("could not allocate memory"); + return 1; + } + + if (print_stdin(buf) != 0) { + return 1; + } + + free(buf); + + return 0; + } + for (int i = 0; i < filec; ++i) { // read from stdin if (strcmp(paths[i], "-") == 0) { @@ -125,16 +187,10 @@ int cat(int filec, char **paths) { return 1; } - if (!fgets(buf, BUF_MAX_LEN, stdin)) { - perror("could not read from stdin"); - free(buf); + if (print_stdin(buf) != 0) { return 1; } - if (print_file(buf) != 0) { - free(buf); - return 1; - } free(buf); continue; @@ -234,7 +290,7 @@ int print_file(char *buf) { continue; } if (show_tabs && buf[i] == '\t') { - puts("^I"); + printf("^I"); continue; } From c74d61e0a34ca6f5bb7e28c9c4df3b1e758611ca Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Fri, 19 Apr 2024 23:27:40 +0200 Subject: [PATCH 17/50] Improved print_stdin function --- commons.mk | 1 - src/cat/cat.c | 110 ++++++++++++++++++++++++-------------------------- 2 files changed, 52 insertions(+), 59 deletions(-) diff --git a/commons.mk b/commons.mk index 2033aab..d9a1baf 100644 --- a/commons.mk +++ b/commons.mk @@ -12,7 +12,6 @@ LDFLAGS += -Wl,--gc-sections BUILD_DIR := $/.build LDFLAGS := -fwhole-program -flto -LDFLAGS += -lm OBJ := $(SRC:%.c=$(BUILD_DIR)/%.o) diff --git a/src/cat/cat.c b/src/cat/cat.c index c735391..e3f9cd7 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -26,22 +25,25 @@ printf("see `cat --help`\n"); \ } while (0) -#define BUF_MAX_LEN 4096 -#define PATH_LEN 256 -#define ARGS_MAX 16 // number of the max arguments -#define ARGS_LEN 32 +#define BUF_MAX_LEN 4096 // max length of a buffer in bytes +#define PATH_LEN 256 // max length of a path in bytes +#define ARGS_MAX 16 // number of the max arguments +#define ARGS_LEN 32 // max length of the arguments in bytes -bool number_nonblank = false; // number nonblank -bool show_ends = false; // show ends -bool number = false; // number -bool show_tabs = false; // show tabs +bool number_nonblank = false; // number nonempty output lines, overrides -n +bool show_ends = false; // display $ at end of each line +bool number = false; // number all output lines +bool squeeze_blank = false; // suppress repeated empty output lines +bool show_tabs = false; // display TAB characters as ^I int cat(int filec, char **paths); int print_file(char *buf); +int print_stdin(void); int main(int argc, char **argv) { if (argc < 2) { - if (cat(0, NULL) != 0) { + // print stdin + if (print_stdin() != 0) { exit(1); } return 0; @@ -92,6 +94,9 @@ int main(int argc, char **argv) { number = true; number_nonblank = false; continue; + case 's': + squeeze_blank = true; + continue; case 'T': show_tabs = true; continue; @@ -101,7 +106,7 @@ int main(int argc, char **argv) { return 1; } } - goto parsed; + goto parsing_done; } // parsing individual arguments @@ -120,10 +125,15 @@ int main(int argc, char **argv) { number_nonblank = false; continue; } + if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--squeeze-blank") == 0) { + squeeze_blank = true; + continue; + } if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { show_tabs = true; continue; } else { + // check if the file is accessible if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { fprintf(stderr, "file `%s` not found\n", argv[i]); free(paths); @@ -135,7 +145,7 @@ int main(int argc, char **argv) { } } -parsed: +parsing_done: if (cat(filec, paths) != 0) { free(paths); exit(1); @@ -146,21 +156,6 @@ int main(int argc, char **argv) { return 0; } -int print_stdin(char *buf) { - if (!fgets(buf, BUF_MAX_LEN, stdin)) { - perror("could not read from stdin"); - free(buf); - return 1; - } - - if (print_file(buf) != 0) { - free(buf); - return 1; - } - - return 0; -} - int cat(int filec, char **paths) { if (filec == 0 || !paths) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); @@ -169,7 +164,7 @@ int cat(int filec, char **paths) { return 1; } - if (print_stdin(buf) != 0) { + if (print_stdin() != 0) { return 1; } @@ -187,7 +182,7 @@ int cat(int filec, char **paths) { return 1; } - if (print_stdin(buf) != 0) { + if (print_stdin() != 0) { return 1; } @@ -237,65 +232,64 @@ int cat(int filec, char **paths) { return 0; } -#define NUMBER_BEFORE 6 - -#define PRINT_N_SPACES(n) \ - do { \ - for (int i = 0; i < n; ++i) { \ - putchar(' '); \ - } \ - } while (0) +#define NUMBER_BEFORE 6 // number of spaces before numbers int print_file(char *buf) { int lines = 1; - char line_str[11]; if (number) { - printf(" %d ", lines); // print number before the first line + // print number before the first line + printf("%*d ", NUMBER_BEFORE, lines); } if (number_nonblank) { if (buf[0] != '\n' && buf[1] != '\0') { - printf(" %d ", lines); // print number before the first line + // print number before the first line + printf("%*d ", NUMBER_BEFORE, lines); } } int len = strlen(buf); for (int i = 0; i < len; ++i) { if (number_nonblank && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { - snprintf(line_str, sizeof(line_str), "%d", lines); - int len = strlen(line_str); - // check if the line number is a power of 10 - // one off because the whole thing writes numbers one off - if (log10(lines + 1) == (int)log10(lines + 1)) { - len++; - } putchar('\n'); - PRINT_N_SPACES(NUMBER_BEFORE - len); - printf("%i ", ++lines); + printf("%*d ", NUMBER_BEFORE, ++lines); continue; } if (show_ends && buf[i] == '\n') { putchar('$'); } if (number && buf[i] == '\n' && buf[i + 1] != '\0') { - snprintf(line_str, sizeof(line_str), "%d", lines); - int len = strlen(line_str); - // check if the line number is a power of 10 - // one off because the whole thing writes numbers one off - if (log10(lines + 1) == (int)log10(lines + 1)) { - len++; - } putchar('\n'); - PRINT_N_SPACES(NUMBER_BEFORE - len); - printf("%i ", ++lines); + printf("%6d ", ++lines); continue; } if (show_tabs && buf[i] == '\t') { printf("^I"); continue; } + if (squeeze_blank && buf[i] == '\n') { + // Skip over consecutive '\n' characters + while (i + 1 < len && buf[i + 1] == '\n') { + i++; + } + putchar('\n'); + putchar('\n'); + continue; + } putchar(buf[i]); } return 0; } + +int print_stdin(void) { + char buf[BUF_MAX_LEN]; + while (fgets(buf, BUF_MAX_LEN, stdin)) { + if (print_file(buf) != 0) { + return 1; + } + } + perror("could not read from stdin"); + + __builtin_unreachable(); +} From 56b706126f5cbb332942f48713d5e09b719824d4 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Fri, 19 Apr 2024 23:55:36 +0200 Subject: [PATCH 18/50] Fixed --squeeze-blank unexpected behaviours --- src/cat/cat.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index e3f9cd7..a9feb23 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -232,7 +232,7 @@ int cat(int filec, char **paths) { return 0; } -#define NUMBER_BEFORE 6 // number of spaces before numbers +#define NUMBER_BEFORE 6 // number of spaces before line numbers int print_file(char *buf) { int lines = 1; @@ -248,33 +248,38 @@ int print_file(char *buf) { } int len = strlen(buf); for (int i = 0; i < len; ++i) { + // higher priority than the numbers + if (squeeze_blank && buf[i] == '\n') { + // Skip over consecutive '\n' characters + if (i + 1 < len && buf[i + 1] == '\n') { + // if the consecutive '\n' characters are over + if (i + 2 < len && buf[i + 2] != '\n') { + if (number) { + printf("\n%*d ", NUMBER_BEFORE, ++lines); + } else { + putchar('\n'); + } + } + continue; + } + } + if (number_nonblank && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { - putchar('\n'); - printf("%*d ", NUMBER_BEFORE, ++lines); + printf("\n%*d ", NUMBER_BEFORE, ++lines); continue; } if (show_ends && buf[i] == '\n') { putchar('$'); } if (number && buf[i] == '\n' && buf[i + 1] != '\0') { - putchar('\n'); - printf("%6d ", ++lines); + printf("\n%*d ", NUMBER_BEFORE, ++lines); continue; } if (show_tabs && buf[i] == '\t') { printf("^I"); continue; } - if (squeeze_blank && buf[i] == '\n') { - // Skip over consecutive '\n' characters - while (i + 1 < len && buf[i + 1] == '\n') { - i++; - } - putchar('\n'); - putchar('\n'); - continue; - } putchar(buf[i]); } From 00e00e78d04cc29e71655349b2842494403ea078 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 00:39:14 +0200 Subject: [PATCH 19/50] Added comments --- src/cat/cat.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index a9feb23..3452275 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -76,6 +76,8 @@ int main(int argc, char **argv) { exit(1); } + // TODO: `cat [file] -[args]` works, but `cat -[args] [file]` doesn't + // parse arguments // parsing arguments like `-nET` (= `-n -E -T`) for (int i = 1; i < argc; ++i) { @@ -249,8 +251,9 @@ int print_file(char *buf) { int len = strlen(buf); for (int i = 0; i < len; ++i) { // higher priority than the numbers + // NOTE: Not the prettiest code, but it works, so don't touch it if (squeeze_blank && buf[i] == '\n') { - // Skip over consecutive '\n' characters + // skip over consecutive '\n' characters if (i + 1 < len && buf[i + 1] == '\n') { // if the consecutive '\n' characters are over if (i + 2 < len && buf[i + 2] != '\n') { From 25f22dc1f63021cd92eb264225032c0a798064ba Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 14:33:04 +0200 Subject: [PATCH 20/50] Fixed argument parsing, improved code consistency --- src/cat/cat.c | 86 ++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 3452275..936f7dc 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -21,7 +21,7 @@ #define print_incorrect_args() \ do { \ - printf("incorrect args\n"); \ + printf("incorrect arguments\n"); \ printf("see `cat --help`\n"); \ } while (0) @@ -44,7 +44,7 @@ int main(int argc, char **argv) { if (argc < 2) { // print stdin if (print_stdin() != 0) { - exit(1); + return 1; } return 0; } @@ -52,7 +52,7 @@ int main(int argc, char **argv) { if (strcmp(argv[1], "--version") == 0) { if (argc != 2) { print_incorrect_args(); - exit(1); + return 1; } print_version(); @@ -62,7 +62,7 @@ int main(int argc, char **argv) { if (strcmp(argv[1], "--help") == 0) { if (argc != 2) { print_incorrect_args(); - exit(1); + return 1; } print_help(); @@ -73,7 +73,7 @@ int main(int argc, char **argv) { char **paths = (char **)malloc(sizeof(char) * PATH_LEN * ARGS_MAX); if (!paths) { perror("could not allocate memory"); - exit(1); + return 1; } // TODO: `cat [file] -[args]` works, but `cat -[args] [file]` doesn't @@ -102,55 +102,56 @@ int main(int argc, char **argv) { case 'T': show_tabs = true; continue; + case '-': + // TODO: + if (strcmp(argv[i], "--number-nonblank") == 0) { + number_nonblank = true; + number = false; + continue; + } + if (strcmp(argv[i], "--show-ends") == 0) { + show_ends = true; + continue; + } + if (strcmp(argv[i], "--number") == 0) { + number = true; + number_nonblank = false; + continue; + } + if (strcmp(argv[i], "--squeeze-blank") == 0) { + squeeze_blank = true; + continue; + } + if (strcmp(argv[i], "--show-tabs") == 0) { + show_tabs = true; + continue; + } + break; default: fprintf(stderr, "unknown argument `-%c`", argv[i][j]); free(paths); return 1; } } - goto parsing_done; - } - - // parsing individual arguments - if (strcmp(argv[i], "-b") == 0 || - strcmp(argv[i], "--number-nonblank") == 0) { - number_nonblank = true; - number = false; - continue; - } - if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--show-ends") == 0) { - show_ends = true; - continue; - } - if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--number") == 0) { - number = true; - number_nonblank = false; + // if the argument is parsed, continue continue; } - if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--squeeze-blank") == 0) { - squeeze_blank = true; - continue; - } - if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--show-tabs") == 0) { - show_tabs = true; - continue; - } else { - // check if the file is accessible - if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { - fprintf(stderr, "file `%s` not found\n", argv[i]); - free(paths); - exit(1); - } + // otherwise parse the file path - paths[filec++] = argv[i]; - continue; + // check if the file is accessible + if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { + fprintf(stderr, "file `%s` not found\n", argv[i]); + free(paths); + return 1; } + + paths[filec++] = argv[i]; + continue; } -parsing_done: if (cat(filec, paths) != 0) { free(paths); - exit(1); + return 1; } free(paths); @@ -159,6 +160,7 @@ int main(int argc, char **argv) { } int cat(int filec, char **paths) { + // print stdin if (filec == 0 || !paths) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); if (!buf) { @@ -176,7 +178,7 @@ int cat(int filec, char **paths) { } for (int i = 0; i < filec; ++i) { - // read from stdin + // print stdin if (strcmp(paths[i], "-") == 0) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); if (!buf) { @@ -251,7 +253,7 @@ int print_file(char *buf) { int len = strlen(buf); for (int i = 0; i < len; ++i) { // higher priority than the numbers - // NOTE: Not the prettiest code, but it works, so don't touch it + // NOTE: not the prettiest code, but it works, so don't touch it if (squeeze_blank && buf[i] == '\n') { // skip over consecutive '\n' characters if (i + 1 < len && buf[i + 1] == '\n') { From 6c71cd03e2ef36e7a01b5ae6b38f39e0f99c74e7 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:04:28 +0200 Subject: [PATCH 21/50] Fixed print_stdin function segfault --- src/cat/cat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 936f7dc..014d007 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -299,7 +299,6 @@ int print_stdin(void) { return 1; } } - perror("could not read from stdin"); - __builtin_unreachable(); + return 0; } From 1881f9be5b70d4a24d2a9f3b0ec31d30a64cfd31 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:26:02 +0200 Subject: [PATCH 22/50] Implemented --show-nonprinting, fixed argument parsing --- src/cat/cat.c | 60 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 014d007..35e290d 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -30,11 +31,12 @@ #define ARGS_MAX 16 // number of the max arguments #define ARGS_LEN 32 // max length of the arguments in bytes -bool number_nonblank = false; // number nonempty output lines, overrides -n -bool show_ends = false; // display $ at end of each line -bool number = false; // number all output lines -bool squeeze_blank = false; // suppress repeated empty output lines -bool show_tabs = false; // display TAB characters as ^I +bool number_nonblank = false; // number nonempty output lines, overrides -n +bool show_ends = false; // display $ at end of each line +bool number = false; // number all output lines +bool squeeze_blank = false; // suppress repeated empty output lines +bool show_tabs = false; // display TAB characters as ^I +bool show_nonprinting = false; // use ^ and M- notation, except for LFD and TAB int cat(int filec, char **paths); int print_file(char *buf); @@ -82,7 +84,7 @@ int main(int argc, char **argv) { // parsing arguments like `-nET` (= `-n -E -T`) for (int i = 1; i < argc; ++i) { int len = strlen(argv[i]); - if (len > 2 && argv[i][0] == '-' && argv[i][1] != '-') { + if (len > 1 && argv[i][0] == '-') { for (int j = 1; j < len; ++j) { switch (argv[i][j]) { case 'b': @@ -101,6 +103,11 @@ int main(int argc, char **argv) { continue; case 'T': show_tabs = true; + show_nonprinting = false; + continue; + case 'v': + show_nonprinting = true; + show_tabs = false; continue; case '-': // TODO: @@ -124,6 +131,12 @@ int main(int argc, char **argv) { } if (strcmp(argv[i], "--show-tabs") == 0) { show_tabs = true; + show_nonprinting = false; + continue; + } + if (strcmp(argv[i], "--show-nonprinting") == 0) { + show_nonprinting = true; + show_tabs = false; continue; } break; @@ -133,20 +146,16 @@ int main(int argc, char **argv) { return 1; } } - // if the argument is parsed, continue - continue; - } - // otherwise parse the file path + } else { + // check if the file is accessible + if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { + fprintf(stderr, "file `%s` not found\n", argv[i]); + free(paths); + return 1; + } - // check if the file is accessible - if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { - fprintf(stderr, "file `%s` not found\n", argv[i]); - free(paths); - return 1; + paths[filec++] = argv[i]; } - - paths[filec++] = argv[i]; - continue; } if (cat(filec, paths) != 0) { @@ -275,7 +284,8 @@ int print_file(char *buf) { continue; } if (show_ends && buf[i] == '\n') { - putchar('$'); + printf("$\n"); + continue; } if (number && buf[i] == '\n' && buf[i + 1] != '\0') { printf("\n%*d ", NUMBER_BEFORE, ++lines); @@ -285,6 +295,18 @@ int print_file(char *buf) { printf("^I"); continue; } + if (show_nonprinting && !isprint(buf[i])) { + if (buf[i] & 0x80) { + // meta (M-) notation for characters with the eighth bit set + printf("M-"); + char printable_char = buf[i] & 0x7F; // clear the eighth bit + printf("^%c", '@' + printable_char); + } else { + // regular non-printable character notation + printf("^%c", '@' + buf[i]); + } + continue; + } putchar(buf[i]); } From d0909cd8735e96662c2e5a164d077abc1910327b Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:26:42 +0200 Subject: [PATCH 23/50] Removed done TODO comments --- src/cat/cat.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 35e290d..42a08c4 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -78,10 +78,7 @@ int main(int argc, char **argv) { return 1; } - // TODO: `cat [file] -[args]` works, but `cat -[args] [file]` doesn't - // parse arguments - // parsing arguments like `-nET` (= `-n -E -T`) for (int i = 1; i < argc; ++i) { int len = strlen(argv[i]); if (len > 1 && argv[i][0] == '-') { @@ -110,7 +107,6 @@ int main(int argc, char **argv) { show_tabs = false; continue; case '-': - // TODO: if (strcmp(argv[i], "--number-nonblank") == 0) { number_nonblank = true; number = false; From 068e3abb806d1ab8306f4b7e2cc8d35155c6177a Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:31:53 +0200 Subject: [PATCH 24/50] Fixed bug where if the args are -nE, the numbers don't show up --- src/cat/cat.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 42a08c4..e94fc9c 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -110,30 +110,19 @@ int main(int argc, char **argv) { if (strcmp(argv[i], "--number-nonblank") == 0) { number_nonblank = true; number = false; - continue; - } - if (strcmp(argv[i], "--show-ends") == 0) { + } else if (strcmp(argv[i], "--show-ends") == 0) { show_ends = true; - continue; - } - if (strcmp(argv[i], "--number") == 0) { + } else if (strcmp(argv[i], "--number") == 0) { number = true; number_nonblank = false; - continue; - } - if (strcmp(argv[i], "--squeeze-blank") == 0) { + } else if (strcmp(argv[i], "--squeeze-blank") == 0) { squeeze_blank = true; - continue; - } - if (strcmp(argv[i], "--show-tabs") == 0) { + } else if (strcmp(argv[i], "--show-tabs") == 0) { show_tabs = true; show_nonprinting = false; - continue; - } - if (strcmp(argv[i], "--show-nonprinting") == 0) { + } else if (strcmp(argv[i], "--show-nonprinting") == 0) { show_nonprinting = true; show_tabs = false; - continue; } break; default: @@ -257,7 +246,7 @@ int print_file(char *buf) { } int len = strlen(buf); for (int i = 0; i < len; ++i) { - // higher priority than the numbers + // higher priority // NOTE: not the prettiest code, but it works, so don't touch it if (squeeze_blank && buf[i] == '\n') { // skip over consecutive '\n' characters @@ -280,8 +269,7 @@ int print_file(char *buf) { continue; } if (show_ends && buf[i] == '\n') { - printf("$\n"); - continue; + putchar('$'); } if (number && buf[i] == '\n' && buf[i + 1] != '\0') { printf("\n%*d ", NUMBER_BEFORE, ++lines); From de075b18f8e608a406d98da3e3c14f85ba5a9cbd Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:35:54 +0200 Subject: [PATCH 25/50] Improved show-nonprinting --- src/cat/cat.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index e94fc9c..8e912a1 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -100,11 +100,9 @@ int main(int argc, char **argv) { continue; case 'T': show_tabs = true; - show_nonprinting = false; continue; case 'v': show_nonprinting = true; - show_tabs = false; continue; case '-': if (strcmp(argv[i], "--number-nonblank") == 0) { @@ -279,7 +277,7 @@ int print_file(char *buf) { printf("^I"); continue; } - if (show_nonprinting && !isprint(buf[i])) { + if (show_nonprinting && !isprint(buf[i]) && buf[i] != 9 && buf[i] != 10) { if (buf[i] & 0x80) { // meta (M-) notation for characters with the eighth bit set printf("M-"); From 430e4020ee7d64b1daec67ae7214fcfcd4429398 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:39:29 +0200 Subject: [PATCH 26/50] Implemented new argument options --- src/cat/cat.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 8e912a1..9421102 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -84,10 +84,19 @@ int main(int argc, char **argv) { if (len > 1 && argv[i][0] == '-') { for (int j = 1; j < len; ++j) { switch (argv[i][j]) { + case 'A': + show_nonprinting = true; + show_ends = true; + show_tabs = true; + continue; case 'b': number_nonblank = true; number = false; continue; + case 'e': + show_nonprinting = true; + show_ends = true; + continue; case 'E': show_ends = true; continue; @@ -98,6 +107,10 @@ int main(int argc, char **argv) { case 's': squeeze_blank = true; continue; + case 't': + show_nonprinting = true; + show_tabs = true; + continue; case 'T': show_tabs = true; continue; @@ -105,7 +118,11 @@ int main(int argc, char **argv) { show_nonprinting = true; continue; case '-': - if (strcmp(argv[i], "--number-nonblank") == 0) { + if (strcmp(argv[i], "--show-all") == 0) { + show_nonprinting = true; + show_ends = true; + show_tabs = true; + } else if (strcmp(argv[i], "--number-nonblank") == 0) { number_nonblank = true; number = false; } else if (strcmp(argv[i], "--show-ends") == 0) { From c35d03645c76e37576071f3fdb3277cce3d1b118 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 16:49:21 +0200 Subject: [PATCH 27/50] Improved error handling --- src/cat/cat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 9421102..7fed560 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -141,7 +141,7 @@ int main(int argc, char **argv) { } break; default: - fprintf(stderr, "unknown argument `-%c`", argv[i][j]); + fprintf(stderr, "unknown argument `%s`", argv[i]); free(paths); return 1; } @@ -262,7 +262,7 @@ int print_file(char *buf) { int len = strlen(buf); for (int i = 0; i < len; ++i) { // higher priority - // NOTE: not the prettiest code, but it works, so don't touch it + // NOTE: not the prettiest code, but it works if (squeeze_blank && buf[i] == '\n') { // skip over consecutive '\n' characters if (i + 1 < len && buf[i + 1] == '\n') { From 4d492f4dc017202331f2feef2fa6413dcec7969a Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 23:12:19 +0200 Subject: [PATCH 28/50] Removed global flag variables --- src/cat/cat.c | 161 +++++++++++++++++++++++++------------------------- 1 file changed, 82 insertions(+), 79 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 7fed560..4ea99fc 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -9,10 +9,7 @@ #define VERSION "1.0.0" #define AUTHOR "Akos Szijgyarto (SzAkos04)" -#define print_version() \ - do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ - } while (0) +#include "version_info.h" #define print_help() \ do { \ @@ -26,124 +23,127 @@ printf("see `cat --help`\n"); \ } while (0) +#define assert_argc(argc, n) \ + do { \ + if (argc != n) { \ + print_incorrect_args(); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + #define BUF_MAX_LEN 4096 // max length of a buffer in bytes -#define PATH_LEN 256 // max length of a path in bytes +#define PATH_MAX 256 // max length of a path in bytes #define ARGS_MAX 16 // number of the max arguments #define ARGS_LEN 32 // max length of the arguments in bytes -bool number_nonblank = false; // number nonempty output lines, overrides -n -bool show_ends = false; // display $ at end of each line -bool number = false; // number all output lines -bool squeeze_blank = false; // suppress repeated empty output lines -bool show_tabs = false; // display TAB characters as ^I -bool show_nonprinting = false; // use ^ and M- notation, except for LFD and TAB +typedef enum { + NumberNonblank = (1 << 0), // number nonempty output lines, overrides -n + ShowEnds = (1 << 1), // display $ at end of each line + Number = (1 << 2), // number all output lines + SqueezeBlank = (1 << 3), // suppress repeated empty output lines + ShowTabs = (1 << 4), // display TAB characters as ^I + ShowNonprinting = (1 << 5), // use ^ and M- notation, except for LFD and TAB +} Flag; -int cat(int filec, char **paths); -int print_file(char *buf); -int print_stdin(void); +int cat(int filec, char **paths, unsigned int flags); +int print_file(char *buf, unsigned int flags); +int print_stdin(unsigned int flags); int main(int argc, char **argv) { if (argc < 2) { // print stdin - if (print_stdin() != 0) { - return 1; - } - return 0; + return (print_stdin(0) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } if (strcmp(argv[1], "--version") == 0) { - if (argc != 2) { - print_incorrect_args(); - return 1; - } + assert_argc(argc, 2); print_version(); - return 0; + return EXIT_SUCCESS; } if (strcmp(argv[1], "--help") == 0) { - if (argc != 2) { - print_incorrect_args(); - return 1; - } + assert_argc(argc, 2); print_help(); - return 0; + return EXIT_SUCCESS; } int filec = 0; // file count - char **paths = (char **)malloc(sizeof(char) * PATH_LEN * ARGS_MAX); + char **paths = (char **)malloc(sizeof(char) * PATH_MAX * ARGS_MAX); + if (!paths) { perror("could not allocate memory"); - return 1; + return EXIT_FAILURE; } + unsigned int flags; + // parse arguments for (int i = 1; i < argc; ++i) { + flags = 0; int len = strlen(argv[i]); if (len > 1 && argv[i][0] == '-') { for (int j = 1; j < len; ++j) { switch (argv[i][j]) { case 'A': - show_nonprinting = true; - show_ends = true; - show_tabs = true; + flags |= ShowNonprinting; + flags |= ShowEnds; + flags |= ShowTabs; continue; case 'b': - number_nonblank = true; - number = false; + flags &= ~Number; + flags |= NumberNonblank; continue; case 'e': - show_nonprinting = true; - show_ends = true; + flags |= ShowNonprinting; + flags |= ShowEnds; continue; case 'E': - show_ends = true; + flags |= ShowEnds; continue; case 'n': - number = true; - number_nonblank = false; + flags &= ~NumberNonblank; + flags |= Number; continue; case 's': - squeeze_blank = true; + flags |= SqueezeBlank; continue; case 't': - show_nonprinting = true; - show_tabs = true; + flags |= ShowNonprinting; + flags |= ShowTabs; continue; case 'T': - show_tabs = true; + flags |= ShowTabs; continue; case 'v': - show_nonprinting = true; + flags |= ShowNonprinting; continue; case '-': if (strcmp(argv[i], "--show-all") == 0) { - show_nonprinting = true; - show_ends = true; - show_tabs = true; + flags |= ShowNonprinting; + flags |= ShowEnds; + flags |= ShowTabs; } else if (strcmp(argv[i], "--number-nonblank") == 0) { - number_nonblank = true; - number = false; + flags |= NumberNonblank; + flags &= ~Number; } else if (strcmp(argv[i], "--show-ends") == 0) { - show_ends = true; + flags |= ShowEnds; } else if (strcmp(argv[i], "--number") == 0) { - number = true; - number_nonblank = false; + flags |= Number; + flags &= ~NumberNonblank; } else if (strcmp(argv[i], "--squeeze-blank") == 0) { - squeeze_blank = true; + flags |= SqueezeBlank; } else if (strcmp(argv[i], "--show-tabs") == 0) { - show_tabs = true; - show_nonprinting = false; + flags |= ShowTabs; } else if (strcmp(argv[i], "--show-nonprinting") == 0) { - show_nonprinting = true; - show_tabs = false; + flags |= ShowNonprinting; } break; default: fprintf(stderr, "unknown argument `%s`", argv[i]); free(paths); - return 1; + return EXIT_FAILURE; } } } else { @@ -151,24 +151,24 @@ int main(int argc, char **argv) { if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { fprintf(stderr, "file `%s` not found\n", argv[i]); free(paths); - return 1; + return EXIT_FAILURE; } paths[filec++] = argv[i]; } } - if (cat(filec, paths) != 0) { + if (cat(filec, paths, flags) != 0) { free(paths); - return 1; + return EXIT_FAILURE; } free(paths); - return 0; + return EXIT_SUCCESS; } -int cat(int filec, char **paths) { +int cat(int filec, char **paths, unsigned int flags) { // print stdin if (filec == 0 || !paths) { char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); @@ -177,7 +177,7 @@ int cat(int filec, char **paths) { return 1; } - if (print_stdin() != 0) { + if (print_stdin(flags) != 0) { return 1; } @@ -195,7 +195,7 @@ int cat(int filec, char **paths) { return 1; } - if (print_stdin() != 0) { + if (print_stdin(flags) != 0) { return 1; } @@ -235,7 +235,7 @@ int cat(int filec, char **paths) { fclose(infile); - if (print_file(buf) != 0) { + if (print_file(buf, flags) != 0) { free(buf); return 1; } @@ -247,13 +247,15 @@ int cat(int filec, char **paths) { #define NUMBER_BEFORE 6 // number of spaces before line numbers -int print_file(char *buf) { +int print_file(char *buf, unsigned int flags) { int lines = 1; - if (number) { + /* if (number) { */ + if ((flags & Number)) { // print number before the first line printf("%*d ", NUMBER_BEFORE, lines); } - if (number_nonblank) { + /* if (number_nonblank) { */ + if ((flags & NumberNonblank)) { if (buf[0] != '\n' && buf[1] != '\0') { // print number before the first line printf("%*d ", NUMBER_BEFORE, lines); @@ -263,12 +265,12 @@ int print_file(char *buf) { for (int i = 0; i < len; ++i) { // higher priority // NOTE: not the prettiest code, but it works - if (squeeze_blank && buf[i] == '\n') { + if ((flags & SqueezeBlank) && buf[i] == '\n') { // skip over consecutive '\n' characters if (i + 1 < len && buf[i + 1] == '\n') { // if the consecutive '\n' characters are over if (i + 2 < len && buf[i + 2] != '\n') { - if (number) { + if ((flags & Number)) { printf("\n%*d ", NUMBER_BEFORE, ++lines); } else { putchar('\n'); @@ -278,23 +280,24 @@ int print_file(char *buf) { } } - if (number_nonblank && buf[i] == '\n' && buf[i + 1] != '\n' && + if ((flags & NumberNonblank) && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { printf("\n%*d ", NUMBER_BEFORE, ++lines); continue; } - if (show_ends && buf[i] == '\n') { + if ((flags & ShowEnds) && buf[i] == '\n') { putchar('$'); } - if (number && buf[i] == '\n' && buf[i + 1] != '\0') { + if ((flags & Number) && buf[i] == '\n' && buf[i + 1] != '\0') { printf("\n%*d ", NUMBER_BEFORE, ++lines); continue; } - if (show_tabs && buf[i] == '\t') { + if ((flags & ShowTabs) && buf[i] == '\t') { printf("^I"); continue; } - if (show_nonprinting && !isprint(buf[i]) && buf[i] != 9 && buf[i] != 10) { + if ((flags & ShowNonprinting) && !isprint(buf[i]) && buf[i] != 9 && + buf[i] != 10) { if (buf[i] & 0x80) { // meta (M-) notation for characters with the eighth bit set printf("M-"); @@ -313,10 +316,10 @@ int print_file(char *buf) { return 0; } -int print_stdin(void) { +int print_stdin(unsigned int flags) { char buf[BUF_MAX_LEN]; while (fgets(buf, BUF_MAX_LEN, stdin)) { - if (print_file(buf) != 0) { + if (print_file(buf, flags) != 0) { return 1; } } From a10e25dc0a34051d71a90c9fbb83c3c1526414b3 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 23:30:31 +0200 Subject: [PATCH 29/50] Improved code consistency, removed unnecessary code --- src/cat/cat.c | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 4ea99fc..516b41f 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -31,10 +31,10 @@ } \ } while (0) -#define BUF_MAX_LEN 4096 // max length of a buffer in bytes -#define PATH_MAX 256 // max length of a path in bytes -#define ARGS_MAX 16 // number of the max arguments -#define ARGS_LEN 32 // max length of the arguments in bytes +#define BUF_MAX 4096 // max length of a buffer in bytes +#define PATH_MAX 256 // max length of a path in bytes +#define ARGS_MAX 16 // number of the max arguments +#define ARGS_LEN 32 // max length of the arguments in bytes typedef enum { NumberNonblank = (1 << 0), // number nonempty output lines, overrides -n @@ -55,14 +55,14 @@ int main(int argc, char **argv) { return (print_stdin(0) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } - if (strcmp(argv[1], "--version") == 0) { + if (!strcmp(argv[1], "--version")) { assert_argc(argc, 2); print_version(); return EXIT_SUCCESS; } - if (strcmp(argv[1], "--help") == 0) { + if (!strcmp(argv[1], "--help")) { assert_argc(argc, 2); print_help(); @@ -120,23 +120,23 @@ int main(int argc, char **argv) { flags |= ShowNonprinting; continue; case '-': - if (strcmp(argv[i], "--show-all") == 0) { + if (!strcmp(argv[i], "--show-all")) { flags |= ShowNonprinting; flags |= ShowEnds; flags |= ShowTabs; - } else if (strcmp(argv[i], "--number-nonblank") == 0) { + } else if (!strcmp(argv[i], "--number-nonblank")) { flags |= NumberNonblank; flags &= ~Number; - } else if (strcmp(argv[i], "--show-ends") == 0) { + } else if (!strcmp(argv[i], "--show-ends")) { flags |= ShowEnds; - } else if (strcmp(argv[i], "--number") == 0) { + } else if (!strcmp(argv[i], "--number")) { flags |= Number; flags &= ~NumberNonblank; - } else if (strcmp(argv[i], "--squeeze-blank") == 0) { + } else if (!strcmp(argv[i], "--squeeze-blank")) { flags |= SqueezeBlank; - } else if (strcmp(argv[i], "--show-tabs") == 0) { + } else if (!strcmp(argv[i], "--show-tabs")) { flags |= ShowTabs; - } else if (strcmp(argv[i], "--show-nonprinting") == 0) { + } else if (!strcmp(argv[i], "--show-nonprinting")) { flags |= ShowNonprinting; } break; @@ -171,25 +171,13 @@ int main(int argc, char **argv) { int cat(int filec, char **paths, unsigned int flags) { // print stdin if (filec == 0 || !paths) { - char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); - if (!buf) { - perror("could not allocate memory"); - return 1; - } - - if (print_stdin(flags) != 0) { - return 1; - } - - free(buf); - - return 0; + return (print_stdin(flags) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } for (int i = 0; i < filec; ++i) { // print stdin - if (strcmp(paths[i], "-") == 0) { - char *buf = (char *)malloc(sizeof(char) * BUF_MAX_LEN); + if (!strcmp(paths[i], "-")) { + char *buf = (char *)malloc(sizeof(char) * BUF_MAX); if (!buf) { perror("could not allocate memory"); return 1; @@ -317,8 +305,8 @@ int print_file(char *buf, unsigned int flags) { } int print_stdin(unsigned int flags) { - char buf[BUF_MAX_LEN]; - while (fgets(buf, BUF_MAX_LEN, stdin)) { + char buf[BUF_MAX]; + while (fgets(buf, BUF_MAX, stdin)) { if (print_file(buf, flags) != 0) { return 1; } From cd2a3643cf3d11488f7a0e4737a1841d748e0a62 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sat, 20 Apr 2024 23:47:28 +0200 Subject: [PATCH 30/50] Got rid of most of the manual memory management, fixed flags bug --- src/cat/cat.c | 62 ++++++++++++--------------------------------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 516b41f..d0d77a0 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -46,7 +46,7 @@ typedef enum { } Flag; int cat(int filec, char **paths, unsigned int flags); -int print_file(char *buf, unsigned int flags); +int print_buffer(char *buf, unsigned int flags); int print_stdin(unsigned int flags); int main(int argc, char **argv) { @@ -70,18 +70,12 @@ int main(int argc, char **argv) { } int filec = 0; // file count - char **paths = (char **)malloc(sizeof(char) * PATH_MAX * ARGS_MAX); + char *paths[ARGS_MAX]; - if (!paths) { - perror("could not allocate memory"); - return EXIT_FAILURE; - } - - unsigned int flags; + unsigned int flags = 0; // parse arguments for (int i = 1; i < argc; ++i) { - flags = 0; int len = strlen(argv[i]); if (len > 1 && argv[i][0] == '-') { for (int j = 1; j < len; ++j) { @@ -142,7 +136,6 @@ int main(int argc, char **argv) { break; default: fprintf(stderr, "unknown argument `%s`", argv[i]); - free(paths); return EXIT_FAILURE; } } @@ -150,7 +143,6 @@ int main(int argc, char **argv) { // check if the file is accessible if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { fprintf(stderr, "file `%s` not found\n", argv[i]); - free(paths); return EXIT_FAILURE; } @@ -158,14 +150,7 @@ int main(int argc, char **argv) { } } - if (cat(filec, paths, flags) != 0) { - free(paths); - return EXIT_FAILURE; - } - - free(paths); - - return EXIT_SUCCESS; + return (cat(filec, paths, flags) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } int cat(int filec, char **paths, unsigned int flags) { @@ -177,18 +162,10 @@ int cat(int filec, char **paths, unsigned int flags) { for (int i = 0; i < filec; ++i) { // print stdin if (!strcmp(paths[i], "-")) { - char *buf = (char *)malloc(sizeof(char) * BUF_MAX); - if (!buf) { - perror("could not allocate memory"); - return 1; - } - if (print_stdin(flags) != 0) { return 1; } - free(buf); - continue; } @@ -204,49 +181,38 @@ int cat(int filec, char **paths, unsigned int flags) { long file_size = ftell(infile); fseek(infile, 0, SEEK_SET); - char *buf = (char *)malloc(sizeof(char) * (file_size + 1)); - if (!buf) { - perror("could not allocate memory"); - fclose(infile); - return 1; - } + char buf[file_size + 1]; // read the file into the buffer size_t files_read = fread(buf, sizeof(char), file_size, infile); if (files_read != (size_t)file_size) { fprintf(stderr, "could not read file\n"); fclose(infile); - free(buf); return 1; } buf[file_size] = '\0'; // make sure the string is null terminated fclose(infile); - if (print_file(buf, flags) != 0) { - free(buf); + if (print_buffer(buf, flags) != 0) { return 1; } - - free(buf); } return 0; } -#define NUMBER_BEFORE 6 // number of spaces before line numbers +#define BEFORE_NUMBER 6 // number of spaces before line numbers -int print_file(char *buf, unsigned int flags) { +int print_buffer(char *buf, unsigned int flags) { int lines = 1; - /* if (number) { */ if ((flags & Number)) { // print number before the first line - printf("%*d ", NUMBER_BEFORE, lines); + printf("%*d ", BEFORE_NUMBER, lines); } - /* if (number_nonblank) { */ if ((flags & NumberNonblank)) { if (buf[0] != '\n' && buf[1] != '\0') { // print number before the first line - printf("%*d ", NUMBER_BEFORE, lines); + printf("%*d ", BEFORE_NUMBER, lines); } } int len = strlen(buf); @@ -259,7 +225,7 @@ int print_file(char *buf, unsigned int flags) { // if the consecutive '\n' characters are over if (i + 2 < len && buf[i + 2] != '\n') { if ((flags & Number)) { - printf("\n%*d ", NUMBER_BEFORE, ++lines); + printf("\n%*d ", BEFORE_NUMBER, ++lines); } else { putchar('\n'); } @@ -270,14 +236,14 @@ int print_file(char *buf, unsigned int flags) { if ((flags & NumberNonblank) && buf[i] == '\n' && buf[i + 1] != '\n' && buf[i + 1] != '\0') { - printf("\n%*d ", NUMBER_BEFORE, ++lines); + printf("\n%*d ", BEFORE_NUMBER, ++lines); continue; } if ((flags & ShowEnds) && buf[i] == '\n') { putchar('$'); } if ((flags & Number) && buf[i] == '\n' && buf[i + 1] != '\0') { - printf("\n%*d ", NUMBER_BEFORE, ++lines); + printf("\n%*d ", BEFORE_NUMBER, ++lines); continue; } if ((flags & ShowTabs) && buf[i] == '\t') { @@ -307,7 +273,7 @@ int print_file(char *buf, unsigned int flags) { int print_stdin(unsigned int flags) { char buf[BUF_MAX]; while (fgets(buf, BUF_MAX, stdin)) { - if (print_file(buf, flags) != 0) { + if (print_buffer(buf, flags) != 0) { return 1; } } From 6d24d36a30d3db848bc06fb9e94655fd280092dc Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 01:07:33 +0200 Subject: [PATCH 31/50] Simplified argument parsing --- src/cat/cat.c | 66 ++++++++++++++++----------------------------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index d0d77a0..afc49bb 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -45,10 +45,14 @@ typedef enum { ShowNonprinting = (1 << 5), // use ^ and M- notation, except for LFD and TAB } Flag; +static const char FLAGLIST[] = "bEnsTv"; + int cat(int filec, char **paths, unsigned int flags); int print_buffer(char *buf, unsigned int flags); int print_stdin(unsigned int flags); +inline __attribute__((const)) int stridx(const char *str, char c); + int main(int argc, char **argv) { if (argc < 2) { // print stdin @@ -81,62 +85,25 @@ int main(int argc, char **argv) { for (int j = 1; j < len; ++j) { switch (argv[i][j]) { case 'A': - flags |= ShowNonprinting; - flags |= ShowEnds; - flags |= ShowTabs; - continue; + flags |= ShowNonprinting | ShowEnds | ShowTabs; + break; case 'b': flags &= ~Number; flags |= NumberNonblank; - continue; + break; case 'e': - flags |= ShowNonprinting; - flags |= ShowEnds; - continue; - case 'E': - flags |= ShowEnds; - continue; + flags |= ShowNonprinting | ShowEnds; + break; case 'n': flags &= ~NumberNonblank; flags |= Number; - continue; - case 's': - flags |= SqueezeBlank; - continue; + break; case 't': - flags |= ShowNonprinting; - flags |= ShowTabs; - continue; - case 'T': - flags |= ShowTabs; - continue; - case 'v': - flags |= ShowNonprinting; - continue; - case '-': - if (!strcmp(argv[i], "--show-all")) { - flags |= ShowNonprinting; - flags |= ShowEnds; - flags |= ShowTabs; - } else if (!strcmp(argv[i], "--number-nonblank")) { - flags |= NumberNonblank; - flags &= ~Number; - } else if (!strcmp(argv[i], "--show-ends")) { - flags |= ShowEnds; - } else if (!strcmp(argv[i], "--number")) { - flags |= Number; - flags &= ~NumberNonblank; - } else if (!strcmp(argv[i], "--squeeze-blank")) { - flags |= SqueezeBlank; - } else if (!strcmp(argv[i], "--show-tabs")) { - flags |= ShowTabs; - } else if (!strcmp(argv[i], "--show-nonprinting")) { - flags |= ShowNonprinting; - } + flags |= ShowNonprinting | ShowTabs; break; default: - fprintf(stderr, "unknown argument `%s`", argv[i]); - return EXIT_FAILURE; + flags |= 1 << (stridx(FLAGLIST, argv[i][j])); + break; } } } else { @@ -280,3 +247,10 @@ int print_stdin(unsigned int flags) { return 0; } + +inline __attribute__((const)) int stridx(const char *str, char c) { + for (const char *p = str; *p != '\0'; p++) + if (*p == c) + return (int)(p - str); + return -1; +} From 571430d607e3fa8e7f743c0b9e29a556400f18b6 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 01:23:42 +0200 Subject: [PATCH 32/50] Added back accidentally deleted argument parsing --- src/cat/cat.c | 62 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index afc49bb..3c6646b 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -15,6 +15,31 @@ do { \ printf("Usage: cat [OPTION]... [FILE]...\n"); \ printf("Concatenate FILE(s) to standard output.\n"); \ + printf("\n"); \ + printf("With no FILE, or when FILE is -, read standard input.\n"); \ + printf("\n"); \ + printf(" -A, --show-all equivalent to -vET\n"); \ + printf(" -b, --number-nonblank number nonempty output lines, " \ + "overrides -n\n"); \ + printf(" -e equivalent to -vE\n"); \ + printf(" -E, --show-ends display $ at end of each line\n"); \ + printf(" -n, --number number all output lines\n"); \ + printf( \ + " -s, --squeeze-blank suppress repeated empty output lines\n"); \ + printf(" -t equivalent to -vT\n"); \ + printf(" -T, --show-tabs display TAB characters as ^I\n"); \ + /* TODO: If done, remove the note */ \ + printf(" -u (ignored) --NOTE: not yet " \ + "implemented\n"); \ + printf(" -v, --show-nonprinting use ^ and M- notation, except for LFD " \ + "and TAB\n"); \ + printf(" --help display this help and exit\n"); \ + printf(" --version output version information and exit\n"); \ + printf("\n"); \ + printf("Examples:\n"); \ + printf(" cat f - g Output f's contents, then standard input, then g's " \ + "contents.\n"); \ + printf(" cat Copy standard input to standard output.\n"); \ } while (0) #define print_incorrect_args() \ @@ -59,20 +84,6 @@ int main(int argc, char **argv) { return (print_stdin(0) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } - if (!strcmp(argv[1], "--version")) { - assert_argc(argc, 2); - - print_version(); - return EXIT_SUCCESS; - } - - if (!strcmp(argv[1], "--help")) { - assert_argc(argc, 2); - - print_help(); - return EXIT_SUCCESS; - } - int filec = 0; // file count char *paths[ARGS_MAX]; @@ -101,6 +112,29 @@ int main(int argc, char **argv) { case 't': flags |= ShowNonprinting | ShowTabs; break; + case '-': + if (!strcmp(argv[i], "--show-all")) { + flags |= ShowNonprinting | ShowEnds | ShowTabs; + } else if (!strcmp(argv[i], "--number-nonblank")) { + flags |= 1 << (stridx(FLAGLIST, 'b')); + } else if (!strcmp(argv[i], "--show-ends")) { + flags |= 1 << (stridx(FLAGLIST, 'E')); + } else if (!strcmp(argv[i], "--number")) { + flags |= 1 << (stridx(FLAGLIST, 'n')); + } else if (!strcmp(argv[i], "--squeeze-blank")) { + flags |= 1 << (stridx(FLAGLIST, 's')); + } else if (!strcmp(argv[i], "--show-tabs")) { + flags |= 1 << (stridx(FLAGLIST, 'T')); + } else if (!strcmp(argv[i], "--show-nonprinting")) { + flags |= 1 << (stridx(FLAGLIST, 'v')); + } else if (!strcmp(argv[i], "--help")) { + print_help(); + return EXIT_SUCCESS; + } else if (!strcmp(argv[i], "--version")) { + print_version(); + return EXIT_SUCCESS; + } + break; default: flags |= 1 << (stridx(FLAGLIST, argv[i][j])); break; From 62cbbf31156f99dd7da7a018042b3857babe3868 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 01:28:12 +0200 Subject: [PATCH 33/50] Improved error handling --- src/cat/cat.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 3c6646b..b7c78e4 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -135,15 +135,22 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } break; - default: - flags |= 1 << (stridx(FLAGLIST, argv[i][j])); + default: { + int flag = stridx(FLAGLIST, argv[i][j]); + if (flag == -1) { + fprintf(stderr, "cat: invalid option `-%c`\n", argv[i][j]); + fprintf(stderr, "Try 'cat --help' for more information.\n"); + return EXIT_FAILURE; + } + flags |= 1 << (flag); break; } + } } } else { // check if the file is accessible if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { - fprintf(stderr, "file `%s` not found\n", argv[i]); + fprintf(stderr, "cat: file `%s` not found\n", argv[i]); return EXIT_FAILURE; } @@ -187,7 +194,7 @@ int cat(int filec, char **paths, unsigned int flags) { // read the file into the buffer size_t files_read = fread(buf, sizeof(char), file_size, infile); if (files_read != (size_t)file_size) { - fprintf(stderr, "could not read file\n"); + fprintf(stderr, "cat: could not read file\n"); fclose(infile); return 1; } From 656fb1539ec2b09a71fc2dcdadf70e918a2c74a2 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 01:53:09 +0200 Subject: [PATCH 34/50] Improved stridx() function --- src/cat/cat.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index b7c78e4..ddb1c07 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -95,6 +95,7 @@ int main(int argc, char **argv) { if (len > 1 && argv[i][0] == '-') { for (int j = 1; j < len; ++j) { switch (argv[i][j]) { + // flags that are multiple flags in one case 'A': flags |= ShowNonprinting | ShowEnds | ShowTabs; break; @@ -137,7 +138,7 @@ int main(int argc, char **argv) { break; default: { int flag = stridx(FLAGLIST, argv[i][j]); - if (flag == -1) { + if (flag < 0) { fprintf(stderr, "cat: invalid option `-%c`\n", argv[i][j]); fprintf(stderr, "Try 'cat --help' for more information.\n"); return EXIT_FAILURE; @@ -164,7 +165,7 @@ int main(int argc, char **argv) { int cat(int filec, char **paths, unsigned int flags) { // print stdin if (filec == 0 || !paths) { - return (print_stdin(flags) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return print_stdin(flags); } for (int i = 0; i < filec; ++i) { @@ -290,8 +291,6 @@ int print_stdin(unsigned int flags) { } inline __attribute__((const)) int stridx(const char *str, char c) { - for (const char *p = str; *p != '\0'; p++) - if (*p == c) - return (int)(p - str); - return -1; + const char *p = strchr(str, c); + return (p) ? (int)(p - str) : -1; } From b4e7309bd343f6c0c8a6a7c17eae4e199950c28f Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 02:19:01 +0200 Subject: [PATCH 35/50] Switched from stack to heap memory allocations --- src/cat/cat.c | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index ddb1c07..752ee39 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -56,10 +56,9 @@ } \ } while (0) -#define BUF_MAX 4096 // max length of a buffer in bytes -#define PATH_MAX 256 // max length of a path in bytes -#define ARGS_MAX 16 // number of the max arguments -#define ARGS_LEN 32 // max length of the arguments in bytes +#define BUF_MAX 65535 // max length of a buffer in bytes +#define ARGS_MAX 32767 // number of the max arguments +#define ARGS_LEN 32767 // max length of the arguments in bytes typedef enum { NumberNonblank = (1 << 0), // number nonempty output lines, overrides -n @@ -78,6 +77,8 @@ int print_stdin(unsigned int flags); inline __attribute__((const)) int stridx(const char *str, char c); +void free_paths(int filec, char **paths); + int main(int argc, char **argv) { if (argc < 2) { // print stdin @@ -85,7 +86,12 @@ int main(int argc, char **argv) { } int filec = 0; // file count - char *paths[ARGS_MAX]; + /* char *paths[ARGS_MAX]; */ + char **paths = (char **)malloc(ARGS_MAX * sizeof(char *)); + if (!paths) { + perror("could not allocate memory"); + return EXIT_FAILURE; + } unsigned int flags = 0; @@ -130,9 +136,11 @@ int main(int argc, char **argv) { flags |= 1 << (stridx(FLAGLIST, 'v')); } else if (!strcmp(argv[i], "--help")) { print_help(); + free_paths(filec, paths); return EXIT_SUCCESS; } else if (!strcmp(argv[i], "--version")) { print_version(); + free_paths(filec, paths); return EXIT_SUCCESS; } break; @@ -141,6 +149,7 @@ int main(int argc, char **argv) { if (flag < 0) { fprintf(stderr, "cat: invalid option `-%c`\n", argv[i][j]); fprintf(stderr, "Try 'cat --help' for more information.\n"); + free_paths(filec, paths); return EXIT_FAILURE; } flags |= 1 << (flag); @@ -152,14 +161,28 @@ int main(int argc, char **argv) { // check if the file is accessible if (access(argv[i], F_OK | R_OK) != 0 && strcmp(argv[i], "-") != 0) { fprintf(stderr, "cat: file `%s` not found\n", argv[i]); + free_paths(filec, paths); + return EXIT_FAILURE; + } + + paths[filec] = (char *)malloc(ARGS_LEN * sizeof(char)); + if (!paths[filec]) { + perror("could not allocate memory"); + free_paths(filec, paths); return EXIT_FAILURE; } - paths[filec++] = argv[i]; + strcpy(paths[filec], argv[i]); + filec++; } } - return (cat(filec, paths, flags) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + if (cat(filec, paths, flags) != 0) { + free_paths(filec, paths); + return EXIT_FAILURE; + } + + free_paths(filec, paths); } int cat(int filec, char **paths, unsigned int flags) { @@ -294,3 +317,10 @@ inline __attribute__((const)) int stridx(const char *str, char c) { const char *p = strchr(str, c); return (p) ? (int)(p - str) : -1; } + +void free_paths(int filec, char **paths) { + for (int i = 0; i < filec; i++) { + free(paths[i]); + } + free(paths); +} From 6ab5746151ba378945a32d9c74204126d1bdad20 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 02:25:32 +0200 Subject: [PATCH 36/50] Optimized memory usage --- src/cat/cat.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 752ee39..90dc19f 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -57,7 +57,6 @@ } while (0) #define BUF_MAX 65535 // max length of a buffer in bytes -#define ARGS_MAX 32767 // number of the max arguments #define ARGS_LEN 32767 // max length of the arguments in bytes typedef enum { @@ -86,8 +85,7 @@ int main(int argc, char **argv) { } int filec = 0; // file count - /* char *paths[ARGS_MAX]; */ - char **paths = (char **)malloc(ARGS_MAX * sizeof(char *)); + char **paths = (char **)malloc((argc - 1) * sizeof(char *)); if (!paths) { perror("could not allocate memory"); return EXIT_FAILURE; @@ -177,6 +175,12 @@ int main(int argc, char **argv) { } } + paths = (char **)realloc(paths, filec * sizeof(char *)); + if (!paths) { + perror("could not allocate memory"); + return EXIT_FAILURE; + } + if (cat(filec, paths, flags) != 0) { free_paths(filec, paths); return EXIT_FAILURE; From 0a82d469ec4ba9bd59c3735a9e844b3a9da54a45 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 02:28:57 +0200 Subject: [PATCH 37/50] Added return to main() --- src/cat/cat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cat/cat.c b/src/cat/cat.c index 90dc19f..1580ad5 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -187,6 +187,7 @@ int main(int argc, char **argv) { } free_paths(filec, paths); + return EXIT_SUCCESS; } int cat(int filec, char **paths, unsigned int flags) { From 6ea8832f22c956246e0297cb284649352892a358 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 16:25:40 +0200 Subject: [PATCH 38/50] Implemented -u flag --- src/cat/cat.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 1580ad5..84b9266 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -68,7 +68,7 @@ typedef enum { ShowNonprinting = (1 << 5), // use ^ and M- notation, except for LFD and TAB } Flag; -static const char FLAGLIST[] = "bEnsTv"; +static const char FLAGLIST[] = "bEnsTuv"; int cat(int filec, char **paths, unsigned int flags); int print_buffer(char *buf, unsigned int flags); @@ -117,6 +117,16 @@ int main(int argc, char **argv) { case 't': flags |= ShowNonprinting | ShowTabs; break; + case 'u': + // disable buffering for the output stream + // NOTE: for some reason this makes the program run slower on my + // machine. Running the program with the `-u` flag on this source + // code, the average runtime increases by approximately 10ms + if (setvbuf(stdout, NULL, _IONBF, 0) != 0) { + perror("Error setting output buffer mode"); + return EXIT_FAILURE; + } + break; case '-': if (!strcmp(argv[i], "--show-all")) { flags |= ShowNonprinting | ShowEnds | ShowTabs; @@ -245,8 +255,7 @@ int print_buffer(char *buf, unsigned int flags) { if ((flags & Number)) { // print number before the first line printf("%*d ", BEFORE_NUMBER, lines); - } - if ((flags & NumberNonblank)) { + } else if ((flags & NumberNonblank)) { if (buf[0] != '\n' && buf[1] != '\0') { // print number before the first line printf("%*d ", BEFORE_NUMBER, lines); From d267e9a34beac35977c6b2b96180b52dabca50c8 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 16:35:22 +0200 Subject: [PATCH 39/50] Improved code readability --- src/cat/cat.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 84b9266..50a86d3 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -131,17 +131,19 @@ int main(int argc, char **argv) { if (!strcmp(argv[i], "--show-all")) { flags |= ShowNonprinting | ShowEnds | ShowTabs; } else if (!strcmp(argv[i], "--number-nonblank")) { - flags |= 1 << (stridx(FLAGLIST, 'b')); + flags &= ~Number; + flags |= NumberNonblank; } else if (!strcmp(argv[i], "--show-ends")) { - flags |= 1 << (stridx(FLAGLIST, 'E')); + flags |= ShowEnds; } else if (!strcmp(argv[i], "--number")) { - flags |= 1 << (stridx(FLAGLIST, 'n')); + flags &= ~NumberNonblank; + flags |= Number; } else if (!strcmp(argv[i], "--squeeze-blank")) { - flags |= 1 << (stridx(FLAGLIST, 's')); + flags |= SqueezeBlank; } else if (!strcmp(argv[i], "--show-tabs")) { - flags |= 1 << (stridx(FLAGLIST, 'T')); + flags |= ShowTabs; } else if (!strcmp(argv[i], "--show-nonprinting")) { - flags |= 1 << (stridx(FLAGLIST, 'v')); + flags |= ShowNonprinting; } else if (!strcmp(argv[i], "--help")) { print_help(); free_paths(filec, paths); From c31738c042842ca3ffdaf8b82b852f9a7b41c381 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 16:58:44 +0200 Subject: [PATCH 40/50] Resolved requested changes --- src/cat/cat.c | 71 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 50a86d3..031f9e9 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,8 +1,10 @@ #include +#include #include #include #include #include +#include #include #define NAME "cat (canoutils)" @@ -56,8 +58,9 @@ } \ } while (0) -#define BUF_MAX 65535 // max length of a buffer in bytes -#define ARGS_LEN 32767 // max length of the arguments in bytes +#define BUF_MAX 65535 // max length of a buffer in bytes + +#define CONST_FUNC __attribute__((const)) typedef enum { NumberNonblank = (1 << 0), // number nonempty output lines, overrides -n @@ -175,7 +178,7 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - paths[filec] = (char *)malloc(ARGS_LEN * sizeof(char)); + paths[filec] = (char *)malloc((strlen(argv[i]) + 1) * sizeof(char)); if (!paths[filec]) { perror("could not allocate memory"); free_paths(filec, paths); @@ -218,30 +221,60 @@ int cat(int filec, char **paths, unsigned int flags) { continue; } - // read file - FILE *infile = fopen(paths[i], "r"); - if (!infile) { - perror("could not open file"); + /* // read file */ + /* FILE *infile = fopen(paths[i], "r"); */ + /* if (!infile) { */ + /* perror("could not open file"); */ + /* return 1; */ + /* } */ + + /* // get the size of the file */ + /* fseek(infile, 0, SEEK_END); */ + /* long file_size = ftell(infile); */ + /* fseek(infile, 0, SEEK_SET); */ + + /* char buf[file_size + 1]; */ + + /* // read the file into the buffer */ + /* size_t bytes_read; */ + /* int ch; */ + /* for (bytes_read = 0; (ch = fgetc(infile)) != EOF; ++bytes_read) { */ + /* buf[bytes_read] = (char)ch; */ + /* } */ + /* if (bytes_read != (size_t)file_size) { */ + /* fprintf(stderr, "cat: could not read file\n"); */ + /* fclose(infile); */ + /* return 1; */ + /* } */ + /* buf[file_size] = '\0'; // make sure the string is null terminated */ + + /* fclose(infile); */ + + int fd = open(paths[i], O_RDONLY); + if (fd < 0) { + perror("cat: could not open device"); return 1; } // get the size of the file - fseek(infile, 0, SEEK_END); - long file_size = ftell(infile); - fseek(infile, 0, SEEK_SET); + struct stat st; + if (fstat(fd, &st) < 0) { + perror("cat: could not get file size"); + close(fd); + return 1; + } + off_t file_size = st.st_size; char buf[file_size + 1]; - - // read the file into the buffer - size_t files_read = fread(buf, sizeof(char), file_size, infile); - if (files_read != (size_t)file_size) { - fprintf(stderr, "cat: could not read file\n"); - fclose(infile); + printf("%d\n", (int)file_size); + ssize_t bytes_read = read(fd, buf, sizeof(buf) - 1); + if (bytes_read != file_size) { + perror("cat: could not read file"); + close(fd); return 1; } - buf[file_size] = '\0'; // make sure the string is null terminated - fclose(infile); + buf[bytes_read] = '\0'; // null-terminate the string if (print_buffer(buf, flags) != 0) { return 1; @@ -329,7 +362,7 @@ int print_stdin(unsigned int flags) { return 0; } -inline __attribute__((const)) int stridx(const char *str, char c) { +inline CONST_FUNC int stridx(const char *str, char c) { const char *p = strchr(str, c); return (p) ? (int)(p - str) : -1; } From 0855d3f61687cbf97f8143fb846eb34d15924aa1 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 17:04:36 +0200 Subject: [PATCH 41/50] Resolved code changes with file reading --- src/cat/cat.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 031f9e9..95fe8d4 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -352,11 +352,29 @@ int print_buffer(char *buf, unsigned int flags) { } int print_stdin(unsigned int flags) { + /* char buf[BUF_MAX]; */ + /* while (fgets(buf, BUF_MAX, stdin)) { */ + /* if (print_buffer(buf, flags) != 0) { */ + /* return 1; */ + /* } */ + /* } */ + /* return 0; */ + char buf[BUF_MAX]; - while (fgets(buf, BUF_MAX, stdin)) { + ssize_t bytes_read; + + // Read from stdin using a loop to handle large inputs + while ((bytes_read = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { if (print_buffer(buf, flags) != 0) { return 1; } + // clear the buffer before new prompt + memset(buf, 0, sizeof(buf)); + } + + if (bytes_read == -1) { + perror("cat: read error"); + return 1; } return 0; From de4956150ee1235dacfa152fd1dcc672b832b38f Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 17:06:17 +0200 Subject: [PATCH 42/50] Removed logging message --- src/cat/cat.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 95fe8d4..7bb4e67 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -266,7 +266,6 @@ int cat(int filec, char **paths, unsigned int flags) { off_t file_size = st.st_size; char buf[file_size + 1]; - printf("%d\n", (int)file_size); ssize_t bytes_read = read(fd, buf, sizeof(buf) - 1); if (bytes_read != file_size) { perror("cat: could not read file"); @@ -352,13 +351,6 @@ int print_buffer(char *buf, unsigned int flags) { } int print_stdin(unsigned int flags) { - /* char buf[BUF_MAX]; */ - /* while (fgets(buf, BUF_MAX, stdin)) { */ - /* if (print_buffer(buf, flags) != 0) { */ - /* return 1; */ - /* } */ - /* } */ - /* return 0; */ char buf[BUF_MAX]; ssize_t bytes_read; From 650143525ae159e566b8bde992fbe3d1f1be553e Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 17:07:40 +0200 Subject: [PATCH 43/50] Removed unnecessary include --- src/cat/cat.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 7bb4e67..360b65d 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -221,35 +220,7 @@ int cat(int filec, char **paths, unsigned int flags) { continue; } - /* // read file */ - /* FILE *infile = fopen(paths[i], "r"); */ - /* if (!infile) { */ - /* perror("could not open file"); */ - /* return 1; */ - /* } */ - - /* // get the size of the file */ - /* fseek(infile, 0, SEEK_END); */ - /* long file_size = ftell(infile); */ - /* fseek(infile, 0, SEEK_SET); */ - - /* char buf[file_size + 1]; */ - - /* // read the file into the buffer */ - /* size_t bytes_read; */ - /* int ch; */ - /* for (bytes_read = 0; (ch = fgetc(infile)) != EOF; ++bytes_read) { */ - /* buf[bytes_read] = (char)ch; */ - /* } */ - /* if (bytes_read != (size_t)file_size) { */ - /* fprintf(stderr, "cat: could not read file\n"); */ - /* fclose(infile); */ - /* return 1; */ - /* } */ - /* buf[file_size] = '\0'; // make sure the string is null terminated */ - - /* fclose(infile); */ - + // read file int fd = open(paths[i], O_RDONLY); if (fd < 0) { perror("cat: could not open device"); From 515d7aa252902959264ea7a88e11349df4d77212 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 17:43:22 +0200 Subject: [PATCH 44/50] Removed TODO comment --- src/cat/cat.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 360b65d..8695c08 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -29,9 +29,7 @@ " -s, --squeeze-blank suppress repeated empty output lines\n"); \ printf(" -t equivalent to -vT\n"); \ printf(" -T, --show-tabs display TAB characters as ^I\n"); \ - /* TODO: If done, remove the note */ \ - printf(" -u (ignored) --NOTE: not yet " \ - "implemented\n"); \ + printf(" -u (ignored)\n"); \ printf(" -v, --show-nonprinting use ^ and M- notation, except for LFD " \ "and TAB\n"); \ printf(" --help display this help and exit\n"); \ From 88b6745e90acc2ce58de7b1288cca79a29f9b654 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 18:41:40 +0200 Subject: [PATCH 45/50] Moved help message to variable, fixed FLAGLIST --- src/cat/cat.c | 52 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 8695c08..ca099d7 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -12,33 +12,33 @@ #include "version_info.h" +static const char HELP[] = { + "Usage: cat [OPTION]... [FILE]...\n" + "Concatenate FILE(s) to standard output.\n" + "\n" + "With no FILE, or when FILE is -, read standard input.\n" + "\n" + " -A, --show-all equivalent to -vET\n" + " -b, --number-nonblank number nonempty output lines, overrides -n\n" + " -e equivalent to -vE\n" + " -E, --show-ends display $ at end of each line\n" + " -n, --number number all output lines\n" + " -s, --squeeze-blank suppress repeated empty output lines\n" + " -t equivalent to -vT\n" + " -T, --show-tabs display TAB characters as ^I\n" + " -u (ignored)\n" + " -v, --show-nonprinting use ^ and M- notation, except for LFD and TAB\n" + " --help display this help and exit\n" + " --version output version information and exit\n" + "\n" + "Examples:\n" + " cat f - g Output f's contents, then standard input, then g's " + "contents.\n" + " cat Copy standard input to standard output.\n"}; + #define print_help() \ do { \ - printf("Usage: cat [OPTION]... [FILE]...\n"); \ - printf("Concatenate FILE(s) to standard output.\n"); \ - printf("\n"); \ - printf("With no FILE, or when FILE is -, read standard input.\n"); \ - printf("\n"); \ - printf(" -A, --show-all equivalent to -vET\n"); \ - printf(" -b, --number-nonblank number nonempty output lines, " \ - "overrides -n\n"); \ - printf(" -e equivalent to -vE\n"); \ - printf(" -E, --show-ends display $ at end of each line\n"); \ - printf(" -n, --number number all output lines\n"); \ - printf( \ - " -s, --squeeze-blank suppress repeated empty output lines\n"); \ - printf(" -t equivalent to -vT\n"); \ - printf(" -T, --show-tabs display TAB characters as ^I\n"); \ - printf(" -u (ignored)\n"); \ - printf(" -v, --show-nonprinting use ^ and M- notation, except for LFD " \ - "and TAB\n"); \ - printf(" --help display this help and exit\n"); \ - printf(" --version output version information and exit\n"); \ - printf("\n"); \ - printf("Examples:\n"); \ - printf(" cat f - g Output f's contents, then standard input, then g's " \ - "contents.\n"); \ - printf(" cat Copy standard input to standard output.\n"); \ + printf("%s", HELP); \ } while (0) #define print_incorrect_args() \ @@ -68,7 +68,7 @@ typedef enum { ShowNonprinting = (1 << 5), // use ^ and M- notation, except for LFD and TAB } Flag; -static const char FLAGLIST[] = "bEnsTuv"; +static const char FLAGLIST[] = "bEnsTv"; int cat(int filec, char **paths, unsigned int flags); int print_buffer(char *buf, unsigned int flags); From abeb60d030bccc4e2b98512fc074a5ccef9888ed Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 20:36:57 +0200 Subject: [PATCH 46/50] Removed unnecessary code --- src/cat/cat.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index ca099d7..846da2f 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -55,9 +55,10 @@ static const char HELP[] = { } \ } while (0) +// only used when reading from stdin #define BUF_MAX 65535 // max length of a buffer in bytes -#define CONST_FUNC __attribute__((const)) +#define CONST_ATTR __attribute__((const)) typedef enum { NumberNonblank = (1 << 0), // number nonempty output lines, overrides -n @@ -74,19 +75,15 @@ int cat(int filec, char **paths, unsigned int flags); int print_buffer(char *buf, unsigned int flags); int print_stdin(unsigned int flags); -inline __attribute__((const)) int stridx(const char *str, char c); +inline CONST_ATTR int stridx(const char *str, char c); void free_paths(int filec, char **paths); int main(int argc, char **argv) { - if (argc < 2) { - // print stdin - return (print_stdin(0) == 0) ? EXIT_SUCCESS : EXIT_FAILURE; - } - int filec = 0; // file count + // if argc == 1, then it is allocated 0 bytes char **paths = (char **)malloc((argc - 1) * sizeof(char *)); - if (!paths) { + if (!paths && argc > 1) { perror("could not allocate memory"); return EXIT_FAILURE; } @@ -188,7 +185,7 @@ int main(int argc, char **argv) { } paths = (char **)realloc(paths, filec * sizeof(char *)); - if (!paths) { + if (!paths && argc > 1) { perror("could not allocate memory"); return EXIT_FAILURE; } @@ -341,7 +338,7 @@ int print_stdin(unsigned int flags) { return 0; } -inline CONST_FUNC int stridx(const char *str, char c) { +inline CONST_ATTR int stridx(const char *str, char c) { const char *p = strchr(str, c); return (p) ? (int)(p - str) : -1; } From 2465bfa4acb44d25b2b14f60cc49f347d9a6f975 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Sun, 21 Apr 2024 20:40:18 +0200 Subject: [PATCH 47/50] Improved error messages, removed more unused code --- src/cat/cat.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 846da2f..d4eaef3 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -41,20 +41,6 @@ static const char HELP[] = { printf("%s", HELP); \ } while (0) -#define print_incorrect_args() \ - do { \ - printf("incorrect arguments\n"); \ - printf("see `cat --help`\n"); \ - } while (0) - -#define assert_argc(argc, n) \ - do { \ - if (argc != n) { \ - print_incorrect_args(); \ - exit(EXIT_FAILURE); \ - } \ - } while (0) - // only used when reading from stdin #define BUF_MAX 65535 // max length of a buffer in bytes @@ -84,7 +70,7 @@ int main(int argc, char **argv) { // if argc == 1, then it is allocated 0 bytes char **paths = (char **)malloc((argc - 1) * sizeof(char *)); if (!paths && argc > 1) { - perror("could not allocate memory"); + perror("cat: could not allocate memory"); return EXIT_FAILURE; } @@ -120,7 +106,7 @@ int main(int argc, char **argv) { // machine. Running the program with the `-u` flag on this source // code, the average runtime increases by approximately 10ms if (setvbuf(stdout, NULL, _IONBF, 0) != 0) { - perror("Error setting output buffer mode"); + perror("cat: error setting output buffer mode"); return EXIT_FAILURE; } break; @@ -174,7 +160,7 @@ int main(int argc, char **argv) { paths[filec] = (char *)malloc((strlen(argv[i]) + 1) * sizeof(char)); if (!paths[filec]) { - perror("could not allocate memory"); + perror("cat: could not allocate memory"); free_paths(filec, paths); return EXIT_FAILURE; } @@ -186,7 +172,7 @@ int main(int argc, char **argv) { paths = (char **)realloc(paths, filec * sizeof(char *)); if (!paths && argc > 1) { - perror("could not allocate memory"); + perror("cat: could not allocate memory"); return EXIT_FAILURE; } From a3eb958bbe1e9a3f53615b2cb218d802e0eb6b30 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Mon, 22 Apr 2024 18:54:50 +0200 Subject: [PATCH 48/50] Improved file reading --- src/cat/cat.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index d4eaef3..28f0f35 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -139,7 +139,7 @@ int main(int argc, char **argv) { break; default: { int flag = stridx(FLAGLIST, argv[i][j]); - if (flag < 0) { + if (flag == -1) { fprintf(stderr, "cat: invalid option `-%c`\n", argv[i][j]); fprintf(stderr, "Try 'cat --help' for more information.\n"); free_paths(filec, paths); @@ -203,14 +203,14 @@ int cat(int filec, char **paths, unsigned int flags) { // read file int fd = open(paths[i], O_RDONLY); - if (fd < 0) { + if (fd == -1) { perror("cat: could not open device"); return 1; } // get the size of the file struct stat st; - if (fstat(fd, &st) < 0) { + if (fstat(fd, &st) == -1) { perror("cat: could not get file size"); close(fd); return 1; @@ -218,18 +218,32 @@ int cat(int filec, char **paths, unsigned int flags) { off_t file_size = st.st_size; char buf[file_size + 1]; - ssize_t bytes_read = read(fd, buf, sizeof(buf) - 1); - if (bytes_read != file_size) { + ssize_t total_bytes_read = 0; + while (total_bytes_read < file_size) { + ssize_t bytes_read = + read(fd, buf + total_bytes_read, file_size - total_bytes_read); + if (bytes_read == -1) { + perror("cat: error reading file"); + close(fd); + return 1; + } else if (bytes_read == 0) { + break; // end of file + } + total_bytes_read += bytes_read; + } + if (total_bytes_read != file_size) { perror("cat: could not read file"); close(fd); return 1; } - buf[bytes_read] = '\0'; // null-terminate the string + buf[total_bytes_read] = '\0'; // null-terminate the string if (print_buffer(buf, flags) != 0) { + close(fd); return 1; } + close(fd); } return 0; } @@ -307,7 +321,6 @@ int print_stdin(unsigned int flags) { char buf[BUF_MAX]; ssize_t bytes_read; - // Read from stdin using a loop to handle large inputs while ((bytes_read = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { if (print_buffer(buf, flags) != 0) { return 1; From 3c3441b8f05b51e728cab9a2bbd523b7c373f38a Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Mon, 22 Apr 2024 21:39:23 +0200 Subject: [PATCH 49/50] Optimized print_buffer() function --- src/cat/cat.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 28f0f35..078d59a 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -58,7 +58,7 @@ typedef enum { static const char FLAGLIST[] = "bEnsTv"; int cat(int filec, char **paths, unsigned int flags); -int print_buffer(char *buf, unsigned int flags); +int print_buffer(char *buf, ssize_t buf_size, unsigned int flags); int print_stdin(unsigned int flags); inline CONST_ATTR int stridx(const char *str, char c); @@ -239,7 +239,7 @@ int cat(int filec, char **paths, unsigned int flags) { buf[total_bytes_read] = '\0'; // null-terminate the string - if (print_buffer(buf, flags) != 0) { + if (print_buffer(buf, total_bytes_read, flags) != 0) { close(fd); return 1; } @@ -250,7 +250,7 @@ int cat(int filec, char **paths, unsigned int flags) { #define BEFORE_NUMBER 6 // number of spaces before line numbers -int print_buffer(char *buf, unsigned int flags) { +int print_buffer(char *buf, ssize_t buf_size, unsigned int flags) { int lines = 1; if ((flags & Number)) { // print number before the first line @@ -261,15 +261,14 @@ int print_buffer(char *buf, unsigned int flags) { printf("%*d ", BEFORE_NUMBER, lines); } } - int len = strlen(buf); - for (int i = 0; i < len; ++i) { + for (int i = 0; i < buf_size; ++i) { // higher priority // NOTE: not the prettiest code, but it works if ((flags & SqueezeBlank) && buf[i] == '\n') { // skip over consecutive '\n' characters - if (i + 1 < len && buf[i + 1] == '\n') { + if (i + 1 < buf_size && buf[i + 1] == '\n') { // if the consecutive '\n' characters are over - if (i + 2 < len && buf[i + 2] != '\n') { + if (i + 2 < buf_size && buf[i + 2] != '\n') { if ((flags & Number)) { printf("\n%*d ", BEFORE_NUMBER, ++lines); } else { @@ -317,12 +316,11 @@ int print_buffer(char *buf, unsigned int flags) { } int print_stdin(unsigned int flags) { - char buf[BUF_MAX]; ssize_t bytes_read; while ((bytes_read = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { - if (print_buffer(buf, flags) != 0) { + if (print_buffer(buf, bytes_read, flags) != 0) { return 1; } // clear the buffer before new prompt From 2410bf6e6cf15a7d34ce64038953ff1ab33e93d5 Mon Sep 17 00:00:00 2001 From: SzAkos04 Date: Mon, 22 Apr 2024 22:17:09 +0200 Subject: [PATCH 50/50] Moved buffer clearing --- src/cat/cat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cat/cat.c b/src/cat/cat.c index 078d59a..6fbedc4 100644 --- a/src/cat/cat.c +++ b/src/cat/cat.c @@ -312,6 +312,8 @@ int print_buffer(char *buf, ssize_t buf_size, unsigned int flags) { putchar(buf[i]); } + // clear the buffer after printing it + memset(buf, 0, buf_size); return 0; } @@ -323,8 +325,6 @@ int print_stdin(unsigned int flags) { if (print_buffer(buf, bytes_read, flags) != 0) { return 1; } - // clear the buffer before new prompt - memset(buf, 0, sizeof(buf)); } if (bytes_read == -1) {