1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1992-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                    David Korn <dgkorn@gmail.com>                     *
19 *                                                                      *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23  * David Korn
24  * AT&T Bell Laboratories
25  *
26  * comm
27  */
28 
29 static const char usage[] =
30 "[-?\n@(#)$Id: comm (AT&T Research) 1999-04-28 $\n]"
31 USAGE_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 
comm(Sfio_t * in1,Sfio_t * in2,register Sfio_t * out,register int mode)66 static 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 
147 int
b_comm(int argc,char * argv[],Shbltin_t * context)148 b_comm(int argc, char *argv[], Shbltin_t* context)
149 {
150 	register int mode = C_FILE1|C_FILE2|C_COMMON;
151 	register char *cp;
152 	Sfio_t *f1, *f2;
153 
154 	cmdinit(argc, argv, context, ERROR_CATALOG, 0);
155 	for (;;)
156 	{
157 		switch (optget(argv, usage))
158 		{
159  		case '1':
160 			mode &= ~C_FILE1;
161 			continue;
162 		case '2':
163 			mode &= ~C_FILE2;
164 			continue;
165 		case '3':
166 			mode &= ~C_COMMON;
167 			continue;
168 		case ':':
169 			error(2, "%s",opt_info.arg);
170 			break;
171 		case '?':
172 			error(ERROR_usage(2), "%s",opt_info.arg);
173 			break;
174 		}
175 		break;
176 	}
177 	argv += opt_info.index;
178 	argc -= opt_info.index;
179 	if(error_info.errors || argc!=2)
180 		error(ERROR_usage(2),"%s",optusage(NiL));
181 	cp = *argv++;
182 	if(streq(cp,"-"))
183 		f1 = sfstdin;
184 	else if(!(f1 = sfopen(NiL, cp,"r")))
185 		error(ERROR_system(1),"%s: cannot open",cp);
186 	cp = *argv;
187 	if(streq(cp,"-"))
188 		f2 = sfstdin;
189 	else if(!(f2 = sfopen(NiL, cp,"r")))
190 		error(ERROR_system(1),"%s: cannot open",cp);
191 	if(mode)
192 	{
193 		if(comm(f1,f2,sfstdout,mode) < 0)
194 			error(ERROR_system(1)," write error");
195 	}
196 	else if(f1==sfstdin || f2==sfstdin)
197 		sfseek(sfstdin,(Sfoff_t)0,SEEK_END);
198 	if(f1!=sfstdin)
199 		sfclose(f1);
200 	if(f2!=sfstdin)
201 		sfclose(f2);
202 	return error_info.errors;
203 }
204