/*
* comm.c - select or reject lines common to two files
*
* Version: 2008-1.01
* Build: c89 -o comm comm.c
* Source:
* Spec:
*
* 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 for further details.
*/
#define _POSIX_SOURCE
#include
#include
#include
#include
#include
#include
#include
#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);
}