updated to support 6 digits instead of 4.
[feisty_meow.git] / scripts / text / diff_lib.pl
1 #!/usr/bin/perl
2
3 ###############################################################################
4 #                                                                             #
5 #  Name   : diff_lib                                                          #
6 #  Author : Chris Koeritz                                                     #
7 #  Rights : Copyright (C) 1996-$now by Author                                 #
8 #                                                                             #
9 #  Purpose:                                                                   #
10 #                                                                             #
11 #    A collection of services used for creating a report of differences       #
12 #  between two directories.                                                   #
13 #                                                                             #
14 ###############################################################################
15 #  This program is free software; you can redistribute it and/or modify it    #
16 #  under the terms of the GNU General Public License as published by the Free #
17 #  Software Foundation; either version 2 of the License or (at your option)   #
18 #  any later version.  See: "http://www.gruntose.com/Info/GNU/GPL.html" for a #
19 #  version of the License.  Please send any updates to "fred@gruntose.com".   #
20 ###############################################################################
21
22 # note: this script and the scripts that use it require the Text::Diff
23 # support from CPAN.  this can be installed by using cpan and telling it
24 #    install Text::Diff
25
26 require "ctime.pl";
27 require "filename_helper.pl";
28 require "importenv.pl";
29
30 use Text::Diff;
31
32 sub diff_dirs {
33   local(@arguments) = @_;
34
35 #  print "args: @arguments\n";
36
37   ############################################################################
38
39   # constants used for header creation.
40   $long_line = "--------------\n";
41   $break_line = "\n##############\n";
42   $separator = "\n";
43   $differing_header = "Differences seen for %1:\n";
44
45   ############################################################################
46
47   # state machine constants and initialization.
48
49   $STARTING = 2;  # initial state.
50   $SAW_DIFF = 3;  # recently saw a difference.
51   $SAW_SAME = 4;  # recently saw no differences.
52
53   $state = $STARTING;
54
55   ############################################################################
56
57   $header_printed = 0;  # did we print the header for this directory yet?
58
59   ############################################################################
60
61   if ($#arguments < 0) {
62     # complain if they don't pass a directory name.
63     &differ_instructions;
64     return;
65   }
66
67   # get the comparison directory from the arguments.
68   local($compare_directory) = &sanitize_name($arguments[0]);
69   if ($compare_directory eq "") {
70     # it's a bad directory name, only composed of slashes.
71     &differ_instructions;
72     return;
73   }
74   if (-l $compare_directory) {
75     # we will not operate on links, due to recursion issues.
76 ##    &differ_instructions;
77     print "skipping link: $compare_directory\n";
78     return;
79   }
80   if (! -d $compare_directory) {
81     # a missing directory is just not good.
82     print "skipping missing directory: $compare_directory\n";
83     return;
84   }
85
86   # the current directory is usually used as the starting point.
87   local($source_directory) = ".";
88   if ($arguments[1] ne "") {
89     # they specified a current directory.
90     $source_directory = $arguments[1];
91   }
92   $source_directory = &sanitize_name($source_directory);
93   if (-l $source_directory) {
94     # we will not operate on links, due to recursion issues.
95 #    &differ_instructions;
96     print "skipping link: $source_directory\n";
97     return;
98   }
99   if (! -d $source_directory) {
100     # a missing directory is just not good.
101     print "skipping missing directory: $source_directory\n";
102     return;
103   }
104
105 #  print "src=$source_directory, dest=$compare_directory\n";
106
107 # keep a local copy of the names for print_header.
108 #hmmm: fix bad design.
109   local($temp_src) = $source_directory;
110   local($temp_dest) = $compare_directory;
111
112   # iterate over all the files in the source directory.
113   opendir CURDIR, $source_directory
114       || die("couldn't open $source_directory for reading.\n");
115   foreach $filename (readdir CURDIR) {
116     if ( ($filename eq ".") || ($filename eq "..") ) { next; }
117     $filename = $source_directory."/".$filename;
118     # make sure we compare against the base part of the name.
119     @name_components = split(/\//, $filename);
120     $basename = $name_components[$#name_components];
121 #    print "doing diff of $filename against $compare_directory/$basename\n";
122     &do_diff($filename, $compare_directory."/".$basename);
123   }
124   closedir CURDIR;
125
126   if ($state == $SAW_SAME) {
127     print $long_line;
128   }
129
130   # report files that exist in target directory, but not here, or vice-versa.
131   &back_compare($compare_directory, $source_directory);
132   &back_compare($source_directory, $compare_directory);
133
134   # only print the closure line if we printed some other stuff.
135   if ($header_printed) {
136     print "\n";
137 ###    print $break_line;
138   }
139
140   return;  # scram.
141 }
142
143 ############################################################################
144
145 sub print_header {
146   # this function prints out the header prior to printing out any real
147   # data.  if there are no diffs, the header should never get printed.
148   print "$break_line\n";
149   local($printable_date) = &ctime(time);
150   $printable_date =~ s/\n//g;
151   print "[$printable_date]\n";
152   print "Left (<) is \"$temp_src\".\n";
153   print "Right (>) is \"$temp_dest\".\n";
154
155   $header_printed = 1;
156 }
157
158 ############################################################################
159
160 sub differ_instructions {
161   print "
162 differ:
163 This program needs a directory name.  The directory name is used to
164 specify a target directory where there are files which are mostly similar
165 to the files in this directory.  The files in the current directory are
166 compared to those in the specified target directory and the differences
167 are sent to the standard output.  Note that neither directory may be
168 a symbolic link, as that can lead to crazy recursion.
169 ";
170 }
171
172 ############################################################################
173
174 sub cpdiff_instructions {
175   print "
176 cpdiff:
177 This program needs two directory names.  The first is the source and the
178 second is a target directory where the files are mostly similar to the files
179 in the source directory.  The files in source are compared to those in the
180 target directory and if a file differs, then it's copied from the source to
181 the target.  Also, if the file does not exist in the target directory, it
182 gets copied.
183 ";
184 }
185
186 ############################################################################
187
188 sub synchronize_instructions {
189   print "
190 synch_build:
191 This program needs one directory name to be passed on the command line.
192 This directory is where the installation's executable files live.  Any files
193 that are in the installation bin directory will be compared against the files
194 in the build repository (specified by an environment variable FEISTY_MEOW_DIR).
195 If files differ, they will be copied from the repository into the installation
196 directory.
197 ";
198 }
199
200 ############################################################################
201
202 # replaces the variable marks in the text with the two substitutions.
203
204 sub replace_text {
205   local($text, $subst_1, $subst_2) = @_;
206
207   local($text_copy) = $text;
208   $text_copy =~ s/%1/$subst_1/;
209   $text_copy =~ s/%2/$subst_2/;
210
211   return $text_copy;
212 }
213
214 sub change_to_saw_diffs {
215   print $separator, $long_line;
216 }
217
218 sub change_to_saw_same {
219 }
220
221 ############################################################################
222
223 # checks the differences between the two files and creates appropriate output.
224
225 sub do_diff {
226   local($first, $second) = @_; 
227
228   # turn stupid pc slashes into normal ones.
229   $first =~ s/\\/\//g;
230   $second =~ s/\\/\//g;
231
232   @first_components = split(/\//, $first);
233   $base_filename1 = $first_components[$#first_components];
234   @second_components = split(/\//, $second);
235   $base_filename2 = $second_components[$#second_components];
236
237   # skip if it's a directory.
238   if (-d $first) { return; }
239
240   # skip the file if we don't think it's important.
241   if (! &important_filename($base_filename1)) { return; }
242
243   if (! -e $second) {
244     # make sure we don't bother if the file's not in the target directory.
245     return;
246   }
247
248   local($diff_output) = diff $first, $second, { STYLE => "OldStyle" };
249
250   if (!length($diff_output)) {
251     # there are no differences.
252     &change_to_saw_same;
253   } else {
254     # there were differences.
255     if (!$header_printed) { &print_header; }
256
257     &change_to_saw_diffs;
258
259     print &replace_text($differing_header, $base_filename1);
260     print $long_line;
261
262     if (-B $first || -B $second) {
263       print "Binary files $first and $second differ.\n";
264     } else {
265       print $diff_output;
266     }
267   }
268 }
269
270 ############################################################################
271
272 # this function copies files as needed by comparing whether a file has
273 # changed or not.  third parm is a flag added to cp, like -p to preserve
274 # the timestamps.
275
276 sub do_cpdiff {
277   local($first, $second, $third) = @_; 
278
279   @first_components = split(/\//, $first);
280   $base_filename1 = $first_components[$#first_components];
281   @second_components = split(/\//, $second);
282   $base_filename2 = $second_components[$#second_components];
283
284   # turn stupid pc slashes into normal ones.
285   $second =~ s/\\/\//g;
286
287   # skip if it's a directory.
288   if (-d $first) { return; }
289
290   # skip the file if we don't think it's important.
291   if (! &important_filename($base_filename1)) { return; }
292
293   if (! -e $second) {
294     # the file doesn't exist yet, so copy it.
295 #    print "copying $first -> $second\n";
296     system("cp -f -v $third \"$first\" \"$second\"");
297     return;
298   }
299
300   local($diff_output) = diff $first, $second, { STYLE => "OldStyle" };
301   if (length($diff_output)) {
302     # there were differences in the two files, so copy the source.
303 #    print "copying $first -> $second\n";
304     system("cp -f -v $third \"$first\" \"$second\"");
305     return;
306   }
307 #print "did nothing; $first & $second are identical\n";
308 }
309
310 ############################################################################
311
312 # back_compare checks to make sure that all of the files in the target
313 # directory are represented in the source directory.
314
315 sub back_compare {
316   local($source, $target) = @_;
317   local(@missing_list);
318
319 #  print "backcomp: source=$source target=$target\n";
320
321   opendir CURDIR, $target
322       || die("couldn't open $target for reading.\n");
323   foreach $filename (readdir CURDIR) {
324     if ( ($filename eq ".") || ($filename eq "..") ) { next; }
325
326     $filename = $target."/".$filename;
327     # chop up the filename.
328     @dirs = split(/\//, $filename);
329     # get the basename from the last array element.
330     $basename = $dirs[$#dirs];
331
332     # skip the file if it seems like junk.
333     if (! &important_filename($basename)) { next; }
334
335 #    print "backcomp: file a=$source/$basename b=$filename\n";
336     if ( (! -e "$source/$basename") && (! -d $filename) ) {
337       push(@missing_list, $basename);
338     }
339   }
340   closedir CURDIR;
341
342   if ($#missing_list < 0) { return; }
343
344   if (!$header_printed) { &print_header; }
345
346   print "$separator
347 ==============
348 These exist in \"$target\",
349 but not in \"$source\":
350 ==============
351 ";
352
353   foreach $filename (@missing_list) {
354     print "$filename  ";
355   }
356 }
357
358 ############################################################################
359
360 # this function copies files from the source directory to the target directory
361 # if there is a file of the same name whose contents differ, or if there is
362 # no file of that name in the target directory.
363
364 sub copy_diff_dirs {
365   local(@arguments) = @_;
366 #  print "args: @arguments\n";
367
368   if ($#arguments < 1) {
369     # complain if they don't pass two directory names.
370     &cpdiff_instructions;
371     return;
372   }
373
374   # the source directory is the first parameter.
375   local($source_directory) = &sanitize_name($arguments[0]);
376   if ($source_directory eq "") {
377     # it's a bad directory name, only composed of slashes.
378     &cpdiff_instructions;
379     return;
380   }
381   if (-l $source_directory) {
382     # we will not operate on links, due to recursion issues.
383     &cpdiff_instructions;
384     return;
385   }
386
387   # get the comparison directory from the arguments.
388   local($compare_directory) = &sanitize_name($arguments[1]);
389   if ($compare_directory eq "") {
390     # it's a bad directory name here.
391     &cpdiff_instructions;
392     return;
393   }
394   if (-l $compare_directory) {
395     # we will not operate on links, due to recursion issues.
396     &cpdiff_instructions;
397     return;
398   }
399
400 #  print "src=$source_directory, dest=$compare_directory\n";
401
402   # iterate over all the files in the source directory.
403   opendir CURDIR, $source_directory
404       || die("couldn't open $source_directory for reading.\n");
405   foreach $filename (readdir CURDIR) {
406     if ( ($filename eq ".") || ($filename eq "..") ) { next; }
407     $filename = $source_directory."/".$filename;
408     # make sure we compare against the base part of the name.
409     @name_components = split(/\//, $filename);
410     $basename = $name_components[$#name_components];
411 #    print "diffing $filename against $compare_directory/$basename\n";
412     &do_cpdiff($filename, $compare_directory."/".$basename, "-p");
413   }
414   closedir CURDIR;
415 }
416
417 ############################################################################
418
419 # this version of copy_diff_dirs (see above) causes the dates of the copied
420 # files to be changed to "now".
421
422 #hmmm: extract the common bits used in copy_diff_dirs into useful functions.
423
424 sub copy_diff_dirs_using_now {
425   local(@arguments) = @_;
426 #  print "args: @arguments\n";
427
428   if ($#arguments < 1) {
429     # complain if they don't pass two directory names.
430     &cpdiff_instructions;
431     return;
432   }
433
434   # the source directory is the first parameter.
435   local($source_directory) = &sanitize_name($arguments[0]);
436   if ($source_directory eq "") {
437     # it's a bad directory name, only composed of slashes.
438     &cpdiff_instructions;
439     return;
440   }
441
442   # get the comparison directory from the arguments.
443   local($compare_directory) = &sanitize_name($arguments[1]);
444   if ($compare_directory eq "") {
445     # it's a bad directory name here.
446     &cpdiff_instructions;
447     return;
448   }
449
450 #  print "src=$source_directory, dest=$compare_directory\n";
451
452   # iterate over all the files in the source directory.
453   opendir CURDIR, $source_directory
454       || die("couldn't open $source_directory for reading.\n");
455   foreach $filename (readdir CURDIR) {
456     if ( ($filename eq ".") || ($filename eq "..") ) { next; }
457     $filename = $source_directory."/".$filename;
458     # make sure we compare against the base part of the name.
459     @name_components = split(/\//, $filename);
460     $basename = $name_components[$#name_components];
461 #    print "diffing $filename against $compare_directory/$basename\n";
462     &do_cpdiff($filename, $compare_directory."/".$basename);
463   }
464   closedir CURDIR;
465 }
466
467 ############################################################################
468
469 # makes sure all of the exes and dynamic libs are up to date in the
470 # installed executable directory.
471
472 sub synchronize_against_build
473 {
474   local(@arguments) = @_;
475
476 #  print "args: @arguments\n";
477
478   if ($#arguments < 0) {
479     # complain if they don't pass a directory name.
480     &synchronize_instructions;
481     return;
482   }
483
484   # clean up the directory they passed.
485   local($install_directory) = &sanitize_name($arguments[0]);
486   if ($install_directory eq "") {
487     # it's a bad directory name, only composed of slashes.
488     &synchronize_instructions;
489     return;
490   }
491
492 #  print "install=$install_directory\n";
493 #  print "repos=$FEISTY_MEOW_DIR\n";
494
495   # iterate over all the files in the source directory.
496   opendir CURDIR, $install_directory
497       || die("couldn't open $install_directory for reading.\n");
498   $compare_directory = "$FEISTY_MEOW_DIR/dll";
499   foreach $filename (readdir CURDIR) {
500     if ( ($filename eq ".") || ($filename eq "..") ) { next; }
501     if (! ($filename =~ /\.dll$/)) { next; }
502     $filename = $install_directory."/".$filename;
503     # make sure we compare against the base part of the name.
504     @name_components = split(/\//, $filename);
505     $basename = $name_components[$#name_components];
506     if (! -e $compare_directory."/".$basename) {
507       next;
508     }
509 #    print "diffing $filename against $compare_directory/$basename\n";
510     &do_cpdiff($compare_directory."/".$basename, $filename);
511   }
512   closedir CURDIR;
513
514   # repeat for the exe directory.
515   opendir CURDIR, $install_directory
516       || die("couldn't open $install_directory for reading.\n");
517   $compare_directory = "$FEISTY_MEOW_DIR/exe";
518   foreach $filename (readdir CURDIR) {
519     if ( ($filename eq ".") || ($filename eq "..") ) { next; }
520     if (! ($filename =~ /\.exe$/)) { next; }
521     $filename = $install_directory."/".$filename;
522     # make sure we compare against the base part of the name.
523     @name_components = split(/\//, $filename);
524     $basename = $name_components[$#name_components];
525     if (! -e $compare_directory."/".$basename) {
526       next;
527     }
528 #    print "diffing $filename against $compare_directory/$basename\n";
529     &do_cpdiff($compare_directory."/".$basename, $filename);
530   }
531   closedir CURDIR;
532 }
533
534 ############################################################################
535
536 1;
537