aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2016-02-28 13:03:40 +1300
committerTom Ryder <tom@sanctum.geek.nz>2016-02-28 13:03:40 +1300
commit7208f6fd2d62bef16edbafd327e7e0d4c13b7966 (patch)
tree4c87ce5bcdc9cc44022d2cd06fbab57a56507d96
downloadcat-7208f6fd2d62bef16edbafd327e7e0d4c13b7966.tar.gz
cat-7208f6fd2d62bef16edbafd327e7e0d4c13b7966.zip
First commit of that cat(1) clone I'm making
-rw-r--r--Makefile9
-rw-r--r--README.markdown18
-rw-r--r--cat.h24
-rw-r--r--cfd.c30
-rw-r--r--cfn.c25
-rw-r--r--main.c55
6 files changed, 161 insertions, 0 deletions
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 <tom@sanctum.geek.nz>
+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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/* 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);
+}
+