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