From 85145ba1e2de1f7e4819048ff4f6aa27340fe935 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Fri, 6 Aug 2021 20:48:07 -0400 Subject: [PATCH] finally release cat knowledgepage --- homepage/index.html | 1 + homepage/knowledge/cat/cat.c | 106 ++++++++++++++++++++++++++++++ homepage/knowledge/cat/index.html | 69 ++++++++++++------- 3 files changed, 154 insertions(+), 22 deletions(-) create mode 100644 homepage/knowledge/cat/cat.c diff --git a/homepage/index.html b/homepage/index.html index 40b9738..6953e6c 100644 --- a/homepage/index.html +++ b/homepage/index.html @@ -71,6 +71,7 @@ I'm vaccinated against COVID-19. Are you? #privacy, /thegame; knowledge: +cat(1); NetBSD; true(1); shilling: diff --git a/homepage/knowledge/cat/cat.c b/homepage/knowledge/cat/cat.c new file mode 100644 index 0000000..bd09008 --- /dev/null +++ b/homepage/knowledge/cat/cat.c @@ -0,0 +1,106 @@ +#include +#include +#include + +#define STDIN_NAME "" +#define STDOUT_NAME "" + +/* these are the predicted errors that could occur */ +enum error_type{ + FILE_ACCESS, + FILE_CLOSE, + FILE_WRITE +}; + +/* this is an error function that will print to standard error the error that + * occurred in the program and exit */ +void +error(enum error_type type, char *argv0, char *file_name){ + switch(type){ + case FILE_ACCESS: + fprintf(stderr, "%s: %s: cannot open file\n", argv0, file_name); + break; + case FILE_CLOSE: + fprintf(stderr, "%s: %s: cannot close file\n", argv0, file_name); + break; + case FILE_WRITE: + fprintf(stderr, "%s: %s: cannot write to file\n", argv0, file_name); + break; + } + exit(1); +} + +/* print input to output, returns 0 if successful and 1 if unsuccessful */ +int +file_copy(FILE *input, FILE *output){ + char c; + while((c = getc(input)) != EOF) + if(putc(c, output) == EOF) + return 1; + return 0; +} + +int +main(int argc, char *argv[]){ + /* the name of the file being printed (for diagnostics) */ + char *file_name; + + /* allocate this ahead of time */ + char *stdin_file_name = STDIN_NAME; + + /* the file pointer of the file being printed */ + FILE *input; + + /* this will always be stdout */ + FILE *output = stdout; + + int i; + + /* whether or not options are being parsed */ + int parsing_opts = 1; + + /* usage with 0 arguments - print standard input to standard output */ + if(argc == 1 && file_copy(stdin, stdout)) + error(FILE_WRITE, argv[0], STDOUT_NAME); + else if(argc == 1) + return 0; + + for(i = 1; i < argc; ++i){ + /* parsing options */ + + /* after `--`, interpret `--`, `-`, and `-u` as literal + * filenames */ + if(parsing_opts && !strcmp(argv[i], "--")){ + parsing_opts = 0; + continue; + + /* ignore `-u` if still parsing options */ + }else if(parsing_opts && !strcmp(argv[i], "-u")) + continue; + + /* take `-` to mean standard input if still parsing options */ + else if(parsing_opts && !strcmp(argv[i], "-")){ + file_name = stdin_file_name; + input = stdin; + + /* non-option; open the file and make sure file_name points to + * the right string */ + }else{ + file_name = argv[i]; + input = fopen(file_name, "r"); + if(input == NULL) + error(FILE_ACCESS, argv[0], file_name); + } + + /* print input to output */ + if(file_copy(input, output)) + error(FILE_WRITE, argv[0], STDOUT_NAME); + + /* close input file if it's not stdin */ + if(input != stdin && fclose(input)) + error(FILE_CLOSE, argv[0], file_name); + } + + /* exit successfully */ + return 0; +} diff --git a/homepage/knowledge/cat/index.html b/homepage/knowledge/cat/index.html index e98f4e4..066d5cc 100644 --- a/homepage/knowledge/cat/index.html +++ b/homepage/knowledge/cat/index.html @@ -60,8 +60,8 @@ window.load_highlighting = function(language){ } -

POSIX cat(1) WIP ARTICLE

-

updated 2021-06-21

+

POSIX cat(1)

+

updated 2021-08-06


cat on a POSIX or otherwise UNIX-like system is a program that exists to concatenate files; to “join” one file at its end to another at its start, and output that resulting file to standard output.

cat was introduced in UNIX v1 to supercede the program pr which printed the contents of a single file to the screen (McIlroy); its first-edition manual page described cat as “about the easiest way to print a file” (“cat(1)”). cat’s modern, typical use is more or less the same; it’s often introduced to UNIX beginners as a method to print the contents of a file to the screen, which is why many implementations of cat include options that are technically redundant - see the often-included cat -e, -t, and -v that replace the ends of lines, tabs, and invisible characters respectively with printing portrayals (“cat(1p)”).

@@ -73,20 +73,31 @@ window.load_highlighting = function(language){ #include <stdlib.h> #include <string.h> -#define STDIN_NAME "<stdin>" -#define STDOUT_NAME "<stdout>" +#define STDIN_NAME "<stdin>" +#define STDOUT_NAME "<stdout>" -/* these two errors will exit out of the program with an unsuccessful status, - * and print a diagnostic message to standard error */ -void -file_access_error(char *argv0, char *file_name){ - fprintf(stderr, "%s: %s: cannot open file\n", argv0, file_name); - exit(1); -} +/* these are the predicted errors that could occur */ +enum error_type{ + FILE_ACCESS, + FILE_CLOSE, + FILE_WRITE +}; +/* this is an error function that will print to standard error the error that + * occurred in the program and exit */ void -file_write_error(char *argv0, char *file_name){ - fprintf(stderr, "%s: %s: error writing to file\n", argv0, file_name); +error(enum error_type type, char *argv0, char *file_name){ + switch(type){ + case FILE_ACCESS: + fprintf(stderr, "%s: %s: cannot open file\n", argv0, file_name); + break; + case FILE_CLOSE: + fprintf(stderr, "%s: %s: cannot close file\n", argv0, file_name); + break; + case FILE_WRITE: + fprintf(stderr, "%s: %s: cannot write to file\n", argv0, file_name); + break; + } exit(1); } @@ -121,7 +132,7 @@ main(int argc, char *argv[]){ /* usage with 0 arguments - print standard input to standard output */ if(argc == 1 && file_copy(stdin, stdout)) - file_write_error(argv[0], STDOUT_NAME); + error(FILE_WRITE, argv[0], STDOUT_NAME); else if(argc == 1) return 0; @@ -130,16 +141,16 @@ main(int argc, char *argv[]){ /* after `--`, interpret `--`, `-`, and `-u` as literal * filenames */ - if(parsing_opts && !strcmp(argv[i], "--")){ + if(parsing_opts && !strcmp(argv[i], "--")){ parsing_opts = 0; continue; /* ignore `-u` if still parsing options */ - }else if(parsing_opts && !strcmp(argv[i], "-u")) + }else if(parsing_opts && !strcmp(argv[i], "-u")) continue; /* take `-` to mean standard input if still parsing options */ - else if(parsing_opts && !strcmp(argv[i], "-")){ + else if(parsing_opts && !strcmp(argv[i], "-")){ file_name = stdin_file_name; input = stdin; @@ -147,14 +158,18 @@ main(int argc, char *argv[]){ * the right string */ }else{ file_name = argv[i]; - input = fopen(file_name, "r"); + input = fopen(file_name, "r"); if(input == NULL) - file_access_error(argv[0], file_name); + error(FILE_ACCESS, argv[0], file_name); } /* print input to output */ if(file_copy(input, output)) - file_write_error(argv[0], STDOUT_NAME); + error(FILE_WRITE, argv[0], STDOUT_NAME); + + /* close input file if it's not stdin */ + if(input != stdin && fclose(input)) + error(FILE_CLOSE, argv[0], file_name); } /* exit successfully */ @@ -162,6 +177,9 @@ main(int argc, char *argv[]){ } +

+This is also available at /knowledge/cat/cat.c on this website as a plain .c file with which you can toy. +

It’s worth noting that this concept of cat as a utility that sequentially prints given files to standard output means cat can be replaced by a simple shell script that does the same using dd and printf; cat as defined by POSIX is actually totally redundant to other POSIX utilities. Here’s the shell script:


@@ -208,9 +226,16 @@ done
 exit 0
 
-

It's worth noting that the dd_ shell function in the above sample that allows for re-aliasing of dd to dd bs=1 could be replaced with a shell variable $DD with the initial value dd and a changed value according to -u of dd bs=1. However, alias dd="dd bs=1" would not work due to how shell aliases are parsed (ShellCheck).

+

+It's worth noting that the dd_ shell function in the above sample that allows for re-aliasing of dd to dd bs=1 could be replaced with a shell variable $DD with the initial value dd and a changed value according to -u of dd bs=1. +However, alias dd="dd bs=1" would not work due to how shell aliases are parsed (ShellCheck). +

-

cat doesn't work well as a shell script though. The script is relatively slow for short files and very slow for very large files (though dd itself should probably be used to copy large files from one medium to another anyway). This is provided for educational purposes (though I personally use this shell script in my system PATH; the C implementation provided compiles to a much larger binary using gcc 11.1.0, so this saves a couple kilobytes).

+

+cat doesn't work well as a shell script though. +The script is relatively slow for short files and very slow for very large files (though dd itself should probably be used to copy large files from one medium to another anyway). +This is provided for educational purposes. +

Cited media and further reading

  • Articles