Annotation of loncom/build/filecompare.pl, revision 1.3
1.1 harris41 1: #!/usr/bin/perl
2:
3: # Scott Harrison
4: # YEAR=2001
1.2 harris41 5: # 9/27, 10/24, 10/25, 11/4
1.1 harris41 6:
7: my $invocation=<<END;
8: # ------------------------------------------------------------------ Invocation
9: # filecompare.pl FILE1 FILE2
10: # or
11: # filecompare.pl DIR1 DIR2
12: #
13: # A list of space separated values (after the file/dir names)
14: # can restrict the comparison.
1.2 harris41 15: # These values can be: existence, cvstime, age, md5sum, size, lines,
16: # and/or diffs.
1.1 harris41 17: #
18: # Flags (before file/dir names):
19: # -p show all files the same
20: # -n show all files different
21: # -a show all files (with comparisons)
22: # -q only show file names (based on first file/dir)
23: # -v verbose mode (default)
24: END
25: unless (@ARGV) {
26: print $invocation;
27: exit 1;
28: }
29: # ----------------------------------------------------------------------- Notes
30: #
31: # What are all the different ways to compare two files and how to look
32: # at the differences?
33: #
34: # Ways of comparison:
35: # existence similarity
1.2 harris41 36: # cvs time similarity (first argument treated as CVS source)
1.1 harris41 37: # age similarity (modification time)
38: # md5sum similarity
39: # size similarity (bytes)
40: # line count difference
41: # number of different lines
42: #
43: # Quantities of comparison:
44: # existence (no,yes); other values become 'n/a'
1.2 harris41 45: # cvstime in seconds
1.1 harris41 46: # age in seconds
47: # md5sum ("same" or "different")
48: # size similarity (byte difference)
49: # line count difference (integer)
50: # number of different lines (integer)
51: #
52: # Text output of comparison:
53: # existence VALUE
1.2 harris41 54: # cvstime VALUE
1.1 harris41 55: # age VALUE
56: # md5sum VALUE
57: # size VALUE
58: # lines VALUE
59: # diffs VALUE
60: #
61: # Output of comparison:
62: # exist
63: # if md5sum not same, then different
1.2 harris41 64: # if cvstime not 0, then older/newer
1.1 harris41 65: # if age not 0, then older/newer
66: # if size not 0, then bigger/smaller
67: # if lines not 0, then more lines of code/less lines of code
68: # if diffs not 0, then subtracted lines/added lines/changed lines
69:
70: # implementing from unix command line (assuming bash)
71: # md5sum, diff, wc -l
72:
73: # ---------------------------------------------- Process command line arguments
74: # Flags (before file/dir names):
75: # -p show all files the same
76: # -n show all files different
77: # -a show all files (with comparisons)
78: # -q only show file names (based on first file/dir)
79: # -v verbose mode (default)
1.2 harris41 80: # -b build/install mode (returns exitcode)
1.1 harris41 81: my $verbose='1';
82: my $show='all';
1.2 harris41 83: my $buildmode=0;
1.1 harris41 84: while (@ARGV) {
85: my $flag;
86: if ($ARGV[0]=~/^\-(\w)/) {
87: $flag=$1;
88: shift @ARGV;
89: SWITCH: {
90: $verbose=0, last SWITCH if $flag eq 'q';
91: $verbose=1, last SWITCH if $flag eq 'v';
92: $show='same', last SWITCH if $flag eq 'p';
93: $show='different', last SWITCH if $flag eq 'n';
94: $show='all', last SWITCH if $flag eq 'a';
1.2 harris41 95: $buildmode=1, last SWITCH if $flag eq 'b';
96: $buildmode=2, last SWITCH if $flag eq 'B';
97: $buildmode=3, last SWITCH if $flag eq 'g';
98: $buildmode=4, last SWITCH if $flag eq 'G';
1.1 harris41 99: print($invocation), exit(1);
100: }
101: }
102: else {
103: last;
104: }
105: }
1.2 harris41 106: dowarn('Verbose: '.$verbose."\n");
107: dowarn('Show: '.$show."\n");
1.1 harris41 108:
109: # FILE1 FILE2 or DIR1 DIR2
110: my $loc1=shift @ARGV;
111: my $loc2=shift @ARGV;
112: my $dirmode='directories';
113: my @files;
114: unless ($loc1 and $loc2) {
115: print($invocation), exit(1);
116: }
117: if (-f $loc1) {
118: $dirmode='files';
119: @files=($loc1);
120: }
121: else {
122: if (-e $loc1) {
123: @files=`find $loc1 -type f`;
124: }
125: else {
126: @files=($loc1);
127: }
128: map {chomp; s/^$loc1\///; $_} @files;
129: }
1.2 harris41 130: dowarn('Processing for mode: '.$dirmode."\n");
131: dowarn('Location #1: '.$loc1."\n");
132: dowarn('Location #2: '.$loc2."\n");
1.1 harris41 133:
134: # A list of space separated values (after the file/dir names)
135: # can restrict the comparison.
136: my %restrict;
137: while (@ARGV) {
138: my $r=shift @ARGV;
139: if ($r eq 'existence' or
1.2 harris41 140: $r eq 'cvstime' or
1.1 harris41 141: $r eq 'md5sum' or
142: $r eq 'age' or
143: $r eq 'size' or
144: $r eq 'lines' or
145: $r eq 'diffs') {
146: $restrict{$r}=1;
147: }
148: else {
149: print($invocation), exit(1);
150: }
151: }
152: if (%restrict) {
153: warn('Restricting comparison to: '.
154: join(' ',keys %restrict)."\n");
155: }
156:
157: my %OUTPUT=(
158: 'existence'=>(
159: sub {
160: print 'existence: '.@_[0];
161: return;
162: }
163: ),
164: 'md5sum'=>(
165: sub {
166: print 'md5sum: '.@_[0];
167: return;
168: }
169: ),
1.2 harris41 170: 'cvstime'=>(
171: sub {
172: print 'cvstime: '.@_[0];
173: return;
174: }
175: ),
1.1 harris41 176: 'age'=>(
177: sub {
178: print 'age: '.@_[0];
179: return;
180: }
181: ),
182: 'size'=>(
183: sub {
184: print 'size: '.@_[0];
185: return;
186: }
187: ),
188: 'lines'=>(
189: sub {
190: print 'lines: '.@_[0];
191: return;
192: }
193: ),
194: 'diffs'=>(
195: sub {
196: print 'diffs: '.@_[0];
197: return;
198: }
199: ),
200: );
201:
202: my %MEASURE=(
203: 'existence' => (
204: sub {
205: my ($file1,$file2)=@_;
206: my $rv1=(-e $file1)?'yes':'no';
207: my $rv2=(-e $file2)?'yes':'no';
208: return ($rv1,$rv2);
209: }
210: ),
211: 'md5sum'=>(
212: sub {
213: my ($file1,$file2)=@_;
1.3 ! albertel 214: my ($rv1)=split(/ /,`md5sum $file1`); chop $rv1;
! 215: my ($rv2)=split(/ /,`md5sum $file2`); chop $rv2;
1.1 harris41 216: return ($rv1,$rv2);
217: }
218: ),
1.2 harris41 219: 'cvstime'=>(
220: sub {
221: my ($file1,$file2)=@_;
222: my $rv1=&cvstime($file1);
223: my @a=stat($file2); my $gmt=gmtime($a[9]);
224: my $rv2=&utctime($gmt);
225: return ($rv1,$rv2);
226: }
227: ),
1.1 harris41 228: 'age'=>(
229: sub {
230: my ($file1,$file2)=@_;
1.2 harris41 231: my @a=stat($file1); my $rv1=$a[9];
232: @a=stat($file2); my $rv2=$a[9];
1.1 harris41 233: return ($rv1,$rv2);
234: }
235: ),
236: 'size'=>(
237: sub {
238: my ($file1,$file2)=@_;
239: my @a=stat($file1); my $rv1=$a[7];
240: @a=stat($file2); my $rv2=$a[7];
241: return ($rv1,$rv2);
242: }
243: ),
244: 'lines'=>(
245: sub {
246: my ($file1,$file2)=@_;
247: my $rv1=`wc -l $file1`; chop $rv1;
248: my $rv2=`wc -l $file2`; chop $rv2;
249: return ($rv1,$rv2);
250: }
251: ),
252: 'diffs'=>(
253: sub {
254: my ($file1,$file2)=@_;
255: my $rv1=`diff $file1 $file2 | grep '^<' | wc -l`;
256: chop $rv1; $rv1=~s/^\s+//; $rv1=~s/\s+$//;
257: my $rv2=`diff $file1 $file2 | grep '^>' | wc -l`;
258: chop $rv2; $rv2=~s/^\s+//; $rv2=~s/\s+$//;
259: return ($rv1,$rv2);
260: }
261: ),
262: );
263:
264: FLOP: foreach my $file (@files) {
265: my $file1;
266: my $file2;
267: if ($dirmode eq 'directories') {
268: $file1=$loc1.'/'.$file;
269: $file2=$loc2.'/'.$file;
270: }
271: else {
272: $file1=$loc1;
273: $file2=$loc2;
274: }
275: my ($existence1,$existence2)=&{$MEASURE{'existence'}}($file1,$file2);
276: my $existence=$existence1.':'.$existence2;
1.2 harris41 277: my ($cvstime,$md5sum,$age,$size,$lines,$diffs);
1.1 harris41 278: if ($existence1 eq 'no' or $existence2 eq 'no') {
279: $md5sum='n/a';
280: $age='n/a';
1.2 harris41 281: $cvstime='n/a';
1.1 harris41 282: $size='n/a';
283: $lines='n/a';
284: $diffs='n/a';
285: }
286: else {
1.2 harris41 287: my ($cvstime1,$cvstime2)=&{$MEASURE{'cvstime'}}($file1,$file2);
288: $cvstime=$cvstime1-$cvstime2;
1.1 harris41 289: my ($age1,$age2)=&{$MEASURE{'age'}}($file1,$file2);
290: $age=$age1-$age2;
291: my ($md5sum1,$md5sum2)=&{$MEASURE{'md5sum'}}($file1,$file2);
1.3 ! albertel 292: if ($md5sum1 eq $md5sum2) {
1.1 harris41 293: $md5sum='same';
294: $size=0;
295: $lines=0;
296: $diffs=0;
297: }
1.3 ! albertel 298: elsif ($md5sum1 ne $md5sum2) {
1.1 harris41 299: $md5sum='different';
300: my ($size1,$size2)=&{$MEASURE{'size'}}($file1,$file2);
301: $size=$size1-$size2;
302: my ($lines1,$lines2)=&{$MEASURE{'lines'}}($file1,$file2);
303: $lines=$lines1-$lines2;
304: my ($diffs1,$diffs2)=&{$MEASURE{'diffs'}}($file1,$file2);
305: $diffs=$diffs1.':'.$diffs2;
306: }
307: }
308: my $showflag=0;
309: if ($show eq 'all') {
310: $showflag=1;
311: }
312: if ($show eq 'different') {
313: my @ks=(keys %restrict);
314: unless (@ks) {
1.2 harris41 315: @ks=('existence','cvstime','md5sum','age','size','lines','diffs');
1.1 harris41 316: }
317: FLOP2: for my $key (@ks) {
318: if ($key eq 'existence') {
319: if ($existence ne 'yes:yes') {
320: $showflag=1;
321: }
322: }
323: elsif ($key eq 'md5sum') {
324: if ($md5sum ne 'same') {
325: $showflag=1;
326: }
327: }
1.2 harris41 328: elsif ($key eq 'cvstime') {
329: if ($cvstime!=0) {
330: $showflag=1;
331: }
332: }
1.1 harris41 333: elsif ($key eq 'age') {
334: if ($age!=0) {
335: $showflag=1;
336: }
337: }
338: elsif ($key eq 'size') {
339: if ($size!=0) {
340: $showflag=1;
341: }
342: }
343: elsif ($key eq 'lines') {
344: if ($lines!=0) {
345: $showflag=1;
346: }
347: }
348: elsif ($key eq 'diffs') {
349: if ($diffs ne '0:0') {
350: $showflag=1;
351: }
352: }
353: if ($showflag) {
354: last FLOP2;
355: }
356: }
357: }
358: elsif ($show eq 'same') {
359: my @ks=(keys %restrict);
360: unless (@ks) {
1.2 harris41 361: @ks=('existence','md5sum','cvstime','age','size','lines','diffs');
1.1 harris41 362: }
363: my $showcount=length(@ks);
364: FLOP3: for my $key (@ks) {
365: if ($key eq 'existence') {
366: if ($existence ne 'yes:yes') {
367: $showcount--;
368: }
369: }
370: elsif ($key eq 'md5sum') {
371: if ($md5sum ne 'same') {
372: $showcount--;
373: }
374: }
1.2 harris41 375: elsif ($key eq 'cvstime') {
376: if ($cvstime!=0) {
377: $showcount--;
378: }
379: }
1.1 harris41 380: elsif ($key eq 'age') {
381: if ($age!=0) {
382: $showcount--;
383: }
384: }
385: elsif ($key eq 'size') {
386: if ($size!=0) {
387: $showcount--;
388: }
389: }
390: elsif ($key eq 'lines') {
391: if ($lines!=0) {
392: $showcount--;
393: }
394: }
395: elsif ($key eq 'diffs') {
396: if ($diffs ne '0:0') {
397: $showcount--;
398: }
399: }
400: }
401: if ($showcount==0) {
402: $showflag=1;
403: }
404: }
1.2 harris41 405: if ($buildmode==1) {
406: if ($md5sum eq 'same') {
407: exit(1);
408: }
409: elsif ($cvstime<0) {
410: exit(2);
411: }
412: else {
413: exit(0);
414: }
415: }
416: elsif ($buildmode==2) {
417: if ($cvstime<0) {
418: exit(2);
419: }
420: else {
421: exit(0);
422: }
423: }
424: elsif ($buildmode==3) {
425: if ($md5sum eq 'same') {
426: exit(1);
427: }
428: elsif ($age<0) {
429: exit(2);
430: }
431: else {
432: exit(0);
433: }
434: }
435: elsif ($buildmode==4) {
436: if ($cvstime>0) {
437: exit(2);
438: }
439: else {
440: exit(0);
441: }
442: }
1.1 harris41 443: print "$file";
444: if ($verbose==1) {
445: print "\t";
446: print &{$OUTPUT{'existence'}}($existence);
447: print "\t";
1.2 harris41 448: print &{$OUTPUT{'cvstime'}}($cvstime);
449: print "\t";
1.1 harris41 450: print &{$OUTPUT{'age'}}($age);
451: print "\t";
452: print &{$OUTPUT{'md5sum'}}($md5sum);
453: print "\t";
454: print &{$OUTPUT{'size'}}($size);
455: print "\t";
456: print &{$OUTPUT{'lines'}}($lines);
457: print "\t";
458: print &{$OUTPUT{'diffs'}}($diffs);
459: }
460: print "\n";
461: }
462:
1.2 harris41 463: sub cvstime {
464: my ($f)=@_;
465: my $path; my $file;
466: if ($f=~/^(.*\/)(.*?)$/) {
467: $f=~/^(.*\/)(.*?)$/;
468: ($path,$file)=($1,$2);
469: }
470: else {
471: $file=$f; $path='';
472: }
473: my $cvstime;
474: if ($buildmode!=3) {
475: my $entry=`grep '^/$file/' ${path}CVS/Entries` or
476: die('*** ERROR *** cannot grep against '.${path}.
477: 'CVS/Entries for ' .$file . "\n");
478: my @fields=split(/\//,$entry);
479: $cvstime=`date -d '$fields[3] UTC' --utc +"%s"`;
480: chomp $cvstime;
481: }
482: else {
483: $cvstime='n/a';
484: }
485: return $cvstime;
486: }
1.1 harris41 487:
1.2 harris41 488: sub utctime {
489: my ($f)=@_;
490: my $utctime=`date -d '$f UTC' --utc +"%s"`;
491: chomp $utctime;
492: return $utctime;
493: }
1.1 harris41 494:
1.2 harris41 495: sub dowarn {
496: my ($msg)=@_;
497: warn($msg) unless $buildmode;
498: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>