From 7208f6fd2d62bef16edbafd327e7e0d4c13b7966 Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sun, 28 Feb 2016 13:03:40 +1300 Subject: First commit of that cat(1) clone I'm making --- Makefile | 9 +++++++++ README.markdown | 18 ++++++++++++++++++ cat.h | 24 ++++++++++++++++++++++++ cfd.c | 30 ++++++++++++++++++++++++++++++ cfn.c | 25 +++++++++++++++++++++++++ main.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 161 insertions(+) create mode 100644 Makefile create mode 100644 README.markdown create mode 100644 cat.h create mode 100644 cfd.c create mode 100644 cfn.c create mode 100644 main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5831686 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +CC = gcc +CFLAGS = -ansi -pedantic-errors + +cat : main.o cfn.o cfd.o + gcc -o cat main.o cfn.o cfd.o + +clean : + rm -f -- cat *.o + diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..b97f4a4 --- /dev/null +++ b/README.markdown @@ -0,0 +1,18 @@ +cat(1) +====== + +Very naive implementation of `cat(1)` with no options, for me to learn a bit +more C and the toolchain around it. Supports reading from stdin if there are no +arguments, but that's as clever as it gets. + +To build, just do: + + $ make + +Author +: Tom Ryder +Copyright +: 2016 +License +: Public domain + diff --git a/cat.h b/cat.h new file mode 100644 index 0000000..c8db240 --- /dev/null +++ b/cat.h @@ -0,0 +1,24 @@ +#ifndef CAT_H +#define CAT_H + +/* Whole bunch of headers I only sort-of understand that are used for the + * various system functions in the source files */ +#include +#include +#include +#include +#include +#include +#include + +/* How big the buffer is to hold the bytes for the read-write cycle from each + * file descriptor */ +#define BUFLEN (2 << 12) + +/* Function prototypes so that I can refer to these functions in main() before + * I actually define them */ +int cfn(const char *fn, void *buf); +int cfd(int fd, void *buf); + +#endif + diff --git a/cfd.c b/cfd.c new file mode 100644 index 0000000..9e8baa6 --- /dev/null +++ b/cfd.c @@ -0,0 +1,30 @@ +#include "cat.h" + +/* Function writes the contents of an opened file descriptor to stdout */ +int cfd(int fd, void *buf) { + int br; + + /* Use the buffer to read the file in blocks, writing each block to stdout + * as we go */ + while ((br = read(fd, buf, BUFLEN)) > 0) { + fwrite(buf, 1, br, stdout); + } + + /* If the last return value for br() was -1, there was an error; 0 is what + * we expect */ + if (br == -1) { + perror(__FUNCTION__); + return -1; + } + + /* Force a write of any still-buffered data to stdout */ + if (fflush(stdout) != 0) { + perror(__FUNCTION__); + return -1; + } + + /* Return success, since apparently nothing went wrong before we got here + * */ + return 0; +} + diff --git a/cfn.c b/cfn.c new file mode 100644 index 0000000..ad6776e --- /dev/null +++ b/cfn.c @@ -0,0 +1,25 @@ +#include "cat.h" + +/* Function opens and writes the contents of a named file to stdout; + * effectively a wrapper around cfd() */ +int cfn(const char *fn, void *buf) { + int fd; + + /* Open the file to get a read-only file descriptor */ + if ((fd = open(fn, O_RDONLY)) == -1) { + perror(__FUNCTION__); + return -1; + } + + /* Pass the opened descriptor to cfd() to read it; we keep going even if + * there are problems, because we need the descriptor closed even if we + * couldn't read it */ + cfd(fd, buf); + + /* Close the descriptor, since we should now be done with it */ + if (close(fd) == -1) { + perror(__FUNCTION__); + return -1; + } +} + diff --git a/main.c b/main.c new file mode 100644 index 0000000..620cc41 --- /dev/null +++ b/main.c @@ -0,0 +1,55 @@ +#include "cat.h" + +/* Main function */ +int main(int argc, const char *argv[]) { + int exv, i; + void *buf; + + /* Exit value begins by assuming success */ + exv = EXIT_SUCCESS; + + /* Allocate a buffer with the prescribed size, or bail out */ + if ((buf = malloc(BUFLEN)) == NULL) { + perror(__FUNCTION__); + exit(EXIT_FAILURE); + } + + /* If there's at least one argument, we'll attempt to read from all the + * files named; if one of them doesn't work, we'll flag an error but will + * proceed */ + if (argc > 1) { + + /* Note we start at i = 1, not i = 0; the first element of argv is the + * command itself, not like in Perl */ + for (i = 1 ; i < argc ; i++) { + + /* If the filename is the special case of -, we emit stdin */ + if (strncmp(argv[i], "-", 1) == 0) { + if (cfd(0, buf) == -1) { + exv = EXIT_FAILURE; + } + + /* Otherwise, we emit the file by name */ + } else { + if (cfn(argv[i], buf) == -1) { + exv = EXIT_FAILURE; + } + } + } + + /* If there were no arguments, we assume the user wants us to read from + * stdin, which should already be opened */ + } else { + if (cfd(0, buf) == -1) { + exv = EXIT_FAILURE; + } + } + + /* Free the allocated buffer */ + free(buf); + buf = NULL; + + /* Done, exit appropriately */ + exit(exv); +} + -- cgit v1.2.3