1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1992-2010 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                                                                      *
20***********************************************************************/
21#pragma prototyped
22/*
23 * David Korn
24 * AT&T Bell Laboratories
25 *
26 * comm
27 */
28
29static const char usage[] =
30"[-?\n@(#)$Id: comm (AT&T Research) 1999-04-28 $\n]"
31USAGE_LICENSE
32"[+NAME?comm - select or reject lines common to two files]"
33"[+DESCRIPTION?\bcomm\b reads two files \afile1\a and \afile2\a "
34	"which should be ordered in the collating sequence of the "
35	"current locale, and produces three text columns as output:]{"
36	"[+1?Lines only in \afile1\a.]"
37	"[+2?Lines only in \afile2\a.]"
38	"[+3?Lines in both files.]"
39	"}"
40"[+?If lines in either file are not ordered according to the collating "
41	"sequence of the current locale, the results are not specified.]"
42"[+?If either \afile1\a or \afile2\a is \b-\b, \bcomm\b "
43        "uses standard input starting at the current location.]"
44
45"[1?Suppress the output column of lines unique to \afile1\a.]"
46"[2?Suppress the output column of lines unique to \afile2\a.]"
47"[3?Suppress the output column of lines duplicate in \afile1\a and \afile2\a.]"
48"\n"
49"\nfile1 file2\n"
50"\n"
51"[+EXIT STATUS?]{"
52	"[+0?Both files processed successfully.]"
53	"[+>0?An error occurred.]"
54"}"
55"[+SEE ALSO?\bcmp\b(1), \bdiff\b(1)]"
56;
57
58
59#include <cmd.h>
60
61#define C_FILE1		1
62#define C_FILE2		2
63#define C_COMMON	4
64#define C_ALL		(C_FILE1|C_FILE2|C_COMMON)
65
66static int comm(Sfio_t *in1, Sfio_t *in2, register Sfio_t *out,register int mode)
67{
68	register char *cp1, *cp2;
69	register int n1, n2, n, comp;
70	if(cp1 = sfgetr(in1,'\n',0))
71		n1 = sfvalue(in1);
72	if(cp2 = sfgetr(in2,'\n',0))
73		n2 = sfvalue(in2);
74	while(cp1 && cp2)
75	{
76		n=(n1<n2?n1:n2);
77		if((comp=memcmp(cp1,cp2,n-1))==0 && (comp=n1-n2)==0)
78		{
79			if(mode&C_COMMON)
80			{
81				if(mode!=C_COMMON)
82				{
83					sfputc(out,'\t');
84					if(mode==C_ALL)
85						sfputc(out,'\t');
86				}
87				if(sfwrite(out,cp1,n) < 0)
88					return(-1);
89			}
90			if(cp1 = sfgetr(in1,'\n',0))
91				n1 = sfvalue(in1);
92			if(cp2 = sfgetr(in2,'\n',0))
93				n2 = sfvalue(in2);
94		}
95		else if(comp > 0)
96		{
97			if(mode&C_FILE2)
98			{
99				if(mode&C_FILE1)
100					sfputc(out,'\t');
101				if(sfwrite(out,cp2,n2) < 0)
102					return(-1);
103			}
104			if(cp2 = sfgetr(in2,'\n',0))
105				n2 = sfvalue(in2);
106		}
107		else
108		{
109			if((mode&C_FILE1) && sfwrite(out,cp1,n1) < 0)
110				return(-1);
111			if(cp1 = sfgetr(in1,'\n',0))
112				n1 = sfvalue(in1);
113		}
114	}
115	n = 0;
116	if(cp2)
117	{
118		cp1 = cp2;
119		in1 = in2;
120		n1 = n2;
121		if(mode&C_FILE1)
122			n = 1;
123		mode &= C_FILE2;
124	}
125	else
126		mode &= C_FILE1;
127	if(!mode || !cp1)
128	{
129		if(cp1 && in1==sfstdin)
130			sfseek(in1,(Sfoff_t)0,SEEK_END);
131		return(0);
132	}
133	/* process the remaining stream */
134	while(1)
135	{
136		if(n)
137			sfputc(out,'\t');
138		if(sfwrite(out,cp1,n1) < 0)
139			return(-1);
140		if(!(cp1 = sfgetr(in1,'\n',0)))
141			return(0);
142		n1 = sfvalue(in1);
143	}
144	/* NOT REACHED */
145}
146
147int
148b_comm(int argc, char *argv[], void* context)
149{
150	register int n;
151	register int mode = C_FILE1|C_FILE2|C_COMMON;
152	register char *cp;
153	Sfio_t *f1, *f2;
154
155	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
156	while (n = optget(argv, usage)) switch (n)
157	{
158 	    case '1':
159		mode &= ~C_FILE1;
160		break;
161	    case '2':
162		mode &= ~C_FILE2;
163		break;
164	    case '3':
165		mode &= ~C_COMMON;
166		break;
167	    case ':':
168		error(2, "%s",opt_info.arg);
169		break;
170	    case '?':
171		error(ERROR_usage(2), "%s",opt_info.arg);
172		break;
173	}
174	argv += opt_info.index;
175	argc -= opt_info.index;
176	if(error_info.errors || argc!=2)
177		error(ERROR_usage(2),"%s",optusage(NiL));
178	cp = *argv++;
179	if(streq(cp,"-"))
180		f1 = sfstdin;
181	else if(!(f1 = sfopen(NiL, cp,"r")))
182		error(ERROR_system(1),"%s: cannot open",cp);
183	cp = *argv;
184	if(streq(cp,"-"))
185		f2 = sfstdin;
186	else if(!(f2 = sfopen(NiL, cp,"r")))
187		error(ERROR_system(1),"%s: cannot open",cp);
188	if(mode)
189	{
190		if(comm(f1,f2,sfstdout,mode) < 0)
191			error(ERROR_system(1)," write error");
192	}
193	else if(f1==sfstdin || f2==sfstdin)
194		sfseek(sfstdin,(Sfoff_t)0,SEEK_END);
195	if(f1!=sfstdin)
196		sfclose(f1);
197	if(f2!=sfstdin)
198		sfclose(f2);
199	return(error_info.errors);
200}
201
202