compare-dir-trees.pl 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/perl -w
  2. #
  3. # ./compare-dir-trees.pl /first/path /second/path
  4. #
  5. # returns 0 if both paths have the same elements.
  6. # an element is not the same as another element if it is a different kind of
  7. # object (link, directory, file).
  8. # a directory is not the same as another directory if it has different children.
  9. # a link is not the same as another link if the link data is different.
  10. # a normal file is not the same as another file if their contents differ.
  11. #
  12. # returns a non zero value if any of the above do not hold.
  13. my ($left, $right) = @ARGV;
  14. my $verbose = 0;
  15. sub debug {
  16. return unless $verbose;
  17. my ($debug) = @_;
  18. print STDERR $debug . "\n";
  19. }
  20. sub ensure_d {
  21. my ($dir) = @_;
  22. return 0 if -d $dir;
  23. debug "$dir does not exist!";
  24. exit -2;
  25. }
  26. sub compare_items {
  27. my ($l, $lf, $r, $rf) = @_;
  28. if ($lf eq $rf) {
  29. return ("$l/$lf", "$r/$rf");
  30. }
  31. debug "directory contents mismatch for $l - $r: $lf - $rf";
  32. exit 1;
  33. }
  34. sub compare_dirs {
  35. my ($l, $r) = @_;
  36. my ($l_fail, $r_fail) = (0, 0);
  37. $l_fail = 1 unless opendir(LEFT, $l);
  38. $r_fail = 1 unless opendir(RIGHT, $r);
  39. unless ($l_fail == $r_fail) {
  40. debug "$l-$l_fail did not match $r-$r_fail!";
  41. exit 1;
  42. }
  43. return if $l_fail;
  44. my (@llinks, @rlinks, @ldirs, @rdirs, @lfiles, @rfiles);
  45. {
  46. my @names = sort readdir(LEFT);
  47. foreach my $i (@names) {
  48. next if $i eq '.';
  49. next if $i eq '..';
  50. if (-l "$l/$i") {
  51. push @llinks, $i;
  52. } elsif (-d "$l/$i") {
  53. push @ldirs, $i;
  54. } else {
  55. push @lfiles, $i
  56. }
  57. }
  58. closedir LEFT;
  59. }
  60. {
  61. my @names = sort readdir(RIGHT);
  62. foreach my $i (@names) {
  63. next if $i eq '.';
  64. next if $i eq '..';
  65. if (-l "$r/$i") {
  66. push @rlinks, $i;
  67. } elsif (-d "$r/$i") {
  68. push @rdirs, $i;
  69. } else {
  70. push @rfiles, $i
  71. }
  72. }
  73. closedir RIGHT;
  74. }
  75. my ($lc, $rc) = (scalar @llinks, scalar @rlinks);
  76. unless ($lc == $rc) {
  77. debug "link count mismatch $l / $r";
  78. exit 1;
  79. }
  80. {
  81. for (my $i = 0; $i < $lc; ++$i) {
  82. my ($lfile, $rfile) = compare_items($l, $llinks[$i], $r, $rlinks[$i]);
  83. $llink = readlink $lfile;
  84. $rlink = readlink $rfile;
  85. if ($llink ne $rlink) {
  86. debug "$lfile($llink) does not match $rfile($rlink)";
  87. exit 1;
  88. }
  89. }
  90. }
  91. ($lc, $rc) = (scalar @lfiles, scalar @rfiles);
  92. unless ($lc == $rc) {
  93. debug "file count mismatch $l / $r";
  94. exit 1;
  95. }
  96. {
  97. for (my $i = 0; $i < $lc; ++$i) {
  98. my ($lfile, $rfile) = compare_items($l, $lfiles[$i], $r, $rfiles[$i]);
  99. system('cmp', '-s', $lfile, $rfile);
  100. if ($? == -1) {
  101. debug "failed to execute: $!";
  102. exit 1;
  103. }
  104. if ($? & 127) {
  105. debug "cmp died!";
  106. exit 1;
  107. }
  108. if ($? >> 8) {
  109. debug "$lfile does not match $rfile";
  110. exit 1;
  111. }
  112. }
  113. }
  114. ($lc, $rc) = (scalar @ldirs, scalar @rdirs);
  115. unless ($lc == $rc) {
  116. debug "dir count mismatch $l / $r";
  117. exit 1;
  118. }
  119. {
  120. for (my $i = 0; $i < $lc; ++$i) {
  121. my ($lfile, $rfile) = compare_items($l, $ldirs[$i], $r, $rdirs[$i]);
  122. compare_dirs($lfile, $rfile);
  123. }
  124. }
  125. }
  126. sub main {
  127. debug qq!Comparing: "$left" "$right"
  128. !;
  129. if ($left eq $right) {
  130. debug "paths are actually the same!";
  131. exit 0;
  132. }
  133. ensure_d($left);
  134. ensure_d($right);
  135. compare_dirs($left, $right);
  136. exit 0;
  137. }
  138. main();