/*
 * comm.
 *
 */

/*)BUILD	$(TKBOPTIONS) = {
			TASK	= ...COM
		}
*/

#ifdef	DOCUMENTATION
title	comm	Compare Files for Equality
index		Compare Files for Equality

synopsis

	comm [- [123]] file_1 file_2 [out_file]

description

	Comm prints lines which are common to two files.  Lines which
	are only in file 1 are printed in the leftmost column; those which
	are only in file 2 are printed indented by one tab; those in both
	files are printed indented by two tabs.
	.s
	Specifying flags 1, 2, or 3 will suppress printing of the
	indicated column:
	.lm +16
	.s.i -12;comm -12	prints only lines common to both files.
	.s.i -12;comm -23	prints lines present in the first file,
	but not in the second.
	.s.i -12;comm -123	does nothing.
	.s.lm -16
	If the output file argument is not present, output is written
	to the standard output.

diagnostics

	.lm +8
	.s.i -8;No argument
	.s.i -8;Unknown flag
	.s.i -8;Two input files needed
	.s.i -8;Cannot open input file
	.lm -8

author
	Martin Minow

bugs

#endif
char	*documentation[] = {
"comm prints lines common to two files",
"        comm [- [123]] file_1 file_2 [out_file]",
"",
"Lines in file 1 only are printed in the leftmost column.",
"Lines in file 2 only are printed in the second column.",
"Lines in both files are printed in the third column.",
"Flags 1, 2, or 3 supress printing of the corresponding column",
"	comm -12  prints only lines common to both files",
"	comm -23  prints lines in the first file, but not the second",
"	comm -123 does nothing",
"",
"If the third file is not present, output goes to the standard output",
"",
0 };

#include <stdio.h>
#ifdef vms
#include		<ssdef.h>
#include		<stsdef.h>
#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
#define	IO_ERROR	SS$_ABORT
#endif
/*
 * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
 */
#ifndef	IO_SUCCESS
#define	IO_SUCCESS	0
#endif
#ifndef	IO_ERROR
#define	IO_ERROR	1
#endif
#define	LINESIZE	512

struct area {
	FILE	*fd;
	char	line[LINESIZE];
} area[2];

FILE	*outfd;

char	*head[3]  = { "", "\t", "\t\t" };	/* Heading vector	*/
int	flag[3];				/* What's set		*/



main(argc, argv)
char *argv[];
{

	register int		temp;
	register int		c;
	register FILE		*fdtemp;	/* Hide a DECUS C bug	*/

#ifdef vms
	argc = getredirection(argc,argv);
#endif

	temp = 0;
	if (argc <= 1)
		usage("No arguments");
	if (argv[1][0] == '?' && argv[1][1] == 0) {
		help();
		exit(IO_ERROR);
	}
	if (argv[1][0] == '-' && argv[1][1] != 0) {
		while ((c = *++argv[1])) {
			switch (c) {
			case '1':
			case '2':
			case '3':
				c -= '1';	/* '1..3' => 0..2	*/
				if (!flag[c]) {
					flag[c]++;
					temp++;
				}
				break;
			default:
				usage("Unknown flag");
			}
		}
		head[2] = head[2 - temp];
		argc--;
		argv++;
	}
/*
 * Open files
 */
	if (argc < 3)
		usage("Two input files needed");
	for (temp = 0; temp < 2; temp++) {
		if ((fdtemp = fopen(argv[1+temp], "r")) == NULL)
			cant(argv[1+temp]);
		area[temp].fd = fdtemp;
	}
	if (argc > 3) {
		if ((outfd = fopen(argv[3], "w")) == NULL)
			cant(argv[3]);
	}
	else	outfd = stdout;

/*
 * Fill both buffers and off we go
 */
	fillboth();
	for (;;) {
		switch(equals(area[0].line, area[1].line)) {

		case 0:				/* File 1 == file 2	*/
			output(area[0].line, 2);
			fillboth();
			break;

		case 1:				/* File 1 < file 2	*/
			output(area[0].line, 0);
			fill(0);
			break;

		case 2:				/* File 1 > file 2	*/
			output(area[1].line, 1);
			fill(1);
			break;
		}
	}
}

fill(into)
int		into;		/* Fill this buffer		*/
/*
 * buffer filler.  Just does one of them.
 */
{
	if (input(into))
		flushout(1 - into);
}

fillboth()
/*
 * Fill both buffers
 */
{
	if (input(0)) {
		if (input(1))
			finis();		/* Both end filed	*/
		else
			flushout(1);		/* Just file 0		*/
	}
	if (input(1))
		flushout(0);
}

input(into)
int		into;		/* Read into this buffer		*/
/*
 * Read a line into this buffer, return 1 on eof
 */
{
	return(fgets(area[into].line, LINESIZE, area[into].fd) == NULL);
}

flushout(other)
int		other;		/* Flush this one			*/
{
	do {
		output(area[other].line, other);
	} while (input(other) == 0);
	finis();
}

output(text, which)
char		*text;		/* Line to output			*/
int		which;		/* Which side 1, 2, or 3		*/
{
	if (!flag[which])
		fprintf(outfd, "%s%s", head[which], text);
}

equals(str1, str2)
char		*str1;		/* Strings				*/
char		*str2;		/* to compare				*/
{
	register char	*p1;
	register char	*p2;

	p1 = str1 - 1;
	p2 = str2 - 1;
	while (*++p1 == *++p2) {
		if (*p1 == 0)
			return(0);
	}
	return((p1 < p2) ? 1 : 2);
}

finis()
/*
 * Exit from comm
 */
{
	fflush(outfd);
	fclose(outfd);
	fclose(area[0].fd);
	fclose(area[1].fd);
	exit(IO_SUCCESS);
}

cant(s)
char *s;
{
	fprintf(stderr, "?COMM-E-input file %s\n", s);
	usage("cannot open input file");
}

help()
/*
 * Give good help
 */
{
	register char	**dp;

	for (dp = documentation; *dp; dp++)
		printf("%s\n", *dp);
}

usage(s)
char	*s;
{
	fprintf(stderr, "?COMM-E-%s\n", s);
	fprintf(stderr,
	  "Usage: comm [-[123]] file1 file2 [out-file].  comm ? for help\n");
	exit(IO_ERROR);
}

