--- /dev/null
+/*
+ * comm.c - select or reject lines common to two files
+ *
+ * Version: 2008-1.01
+ * Build: c89 -o comm comm.c
+ * Source: <http://pdcore.sourceforge.net/>
+ * Spec: <http://www.opengroup.org/onlinepubs/9699919799/utilities/comm.html>
+ *
+ * This is free and unencumbered software released into the public domain,
+ * provided "as is", without warranty of any kind, express or implied. See the
+ * file UNLICENSE and the website <http://unlicense.org> for further details.
+ */
+
+
+#define _POSIX_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define USAGE "usage: comm [-123] file1 file2\n"
+#define ENOMALLOC "Unable to allocate memory for read buffer"
+#define BUFSIZE 4096
+#define LINESIZE 64
+#define SYSERR 1
+#define APPERR 0
+
+typedef struct { int fd;
+ char *fn;
+ int eof;
+ char buf[BUFSIZE];
+ size_t bufpos;
+ size_t eobuf;
+ char *line;
+ size_t linesz;
+ } FINFO;
+
+static void initfile(FINFO *file, char *fn);
+static void cleanup(FINFO *file);
+static void commfiles(FINFO *file1, FINFO *file2);
+static int getline(FINFO *file);
+static void fillbuf(FINFO *file);
+static void fatal(int errtype, char *s);
+
+static int opt1, opt2, opt3;
+
+
+int main(int argc, char **argv)
+{
+ extern int optind, opterr;
+ int c;
+ FINFO file1, file2;
+
+ setlocale(LC_ALL, "");
+ opterr = 0;
+
+ while ((c = getopt(argc, argv, "123")) != -1)
+ switch (c)
+ {
+ case '1':
+ opt1 = 1;
+ break;
+ case '2':
+ opt2 = 1;
+ break;
+ case '3':
+ opt3 = 1;
+ break;
+ default:
+ fprintf(stderr, USAGE);
+ exit(1);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ fatal(APPERR, USAGE);
+
+ initfile(&file1, argv[0]);
+ initfile(&file2, argv[1]);
+
+ commfiles(&file1, &file2);
+
+ cleanup(&file1);
+ cleanup(&file2);
+
+ return(0);
+}
+
+
+void initfile(FINFO *file, char *fn)
+{
+ int fd;
+
+ if (strcmp(fn, "-") == 0)
+ fd = STDIN_FILENO;
+ else
+ if ((fd = open(fn, O_RDONLY)) == -1)
+ fatal(SYSERR, fn);
+
+ file->fd = fd;
+ file->fn = fn;
+ file->eof = 0;
+ file->bufpos = 0;
+ file->eobuf = 0;
+ file->linesz = LINESIZE;
+
+ if ((file->line = malloc(file->linesz)) == NULL)
+ fatal(APPERR, ENOMALLOC);
+}
+
+
+void cleanup(FINFO *file)
+{
+ free(file->line);
+
+ if (strcmp(file->fn, "-") != 0 && close(file->fd) == -1)
+ fatal(SYSERR, file->fn);
+}
+
+
+void commfiles(FINFO *file1, FINFO *file2)
+{
+ int cmp;
+
+ if (getline(file1) && getline(file2))
+ while(1)
+ {
+ cmp = strcoll(file1->line, file2->line);
+
+ if (cmp == 0)
+ {
+ if (! opt3)
+ printf("%s%s%s\n", (opt1 ? "" : "\t"), (opt2 ? "" : "\t"), file1->line);
+
+ if ((getline(file1) + getline(file2)) < 2)
+ break;
+ }
+
+ else if (cmp > 0)
+ {
+ if (! opt2)
+ printf("%s%s\n", (opt1 ? "" : "\t"), file2->line);
+
+ if (! getline(file2))
+ break;
+ }
+
+ else /* cmp < 0 */
+ {
+ if (! opt1)
+ printf("%s\n", file1->line);
+
+ if (! getline(file1))
+ break;
+ }
+ }
+
+ while(! file1->eof && ! opt1)
+ {
+ printf("%s\n", file1->line);
+ getline(file1);
+ }
+
+ while(! file2->eof && ! opt2)
+ {
+ printf("%s%s\n", (opt1 ? "" : "\t"), file2->line);
+ getline(file2);
+ }
+}
+
+
+int getline(FINFO *file)
+{
+ int success = 0;
+ size_t i = 0;
+
+ if (! file->eof)
+ {
+ while(1)
+ {
+ if (i == file->linesz)
+ {
+ file->linesz = file->linesz + LINESIZE;
+ if ((file->line = realloc(file->line, file->linesz)) == NULL)
+ fatal(APPERR, ENOMALLOC);
+ }
+
+ if (file->bufpos >= file->eobuf)
+ {
+ fillbuf(file);
+ if (file->eof)
+ {
+ if (i > 0)
+ {
+ file->line[i] = '\0';
+ success = 1;
+ }
+ break;
+ }
+ }
+
+ if (file->buf[file->bufpos] == '\n')
+ {
+ file->bufpos++;
+ file->line[i] = '\0';
+ success = 1;
+ break;
+ }
+ else
+ file->line[i++] = file->buf[file->bufpos++];
+ }
+ }
+
+ return(success);
+}
+
+
+void fillbuf(FINFO *file)
+{
+ ssize_t n;
+
+ if (! file->eof)
+ {
+ if ((n = read(file->fd, file->buf, BUFSIZE)) < 0)
+ fatal(SYSERR, file->fn);
+
+ if (n == 0)
+ file->eof = 1;
+ else
+ {
+ file->bufpos = 0;
+ file->eobuf = n;
+ }
+ }
+}
+
+
+void fatal(int errtype, char *s)
+{
+ if (errtype == SYSERR)
+ fprintf(stderr, "comm: %s: %s\n", s, strerror(errno));
+ else
+ fprintf(stderr, "comm: %s\n", s);
+
+ exit(1);
+}