1#! @PERL@
2# ---------------------------------
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2, or (at your option)
6# any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12
13###########################################################################
14# FUNCTION:
15# To recursively walk through a PVCS archive directory tree (archives
16# located in VCS/ or vcs/ subdirectories) and convert them to RCS archives.
17# The RCS archive name is the PVCS workfile name with ",v" appended.
18#
19# SYNTAX:
20# pvcs_to_rcs.pl --help
21#
22# where -l indicates the operation is to be performed only in the current
23# directory (no recursion)
24#
25# EXAMPLE:
26# pvcs_to_rcs
27# Would walk through every VCS or vcs subdir starting at the current directory,
28# and produce corresponding RCS archives one level above the VCS or vcs subdir.
29# (VCS/../RCS/)
30#
31# NOTES:
32# * This script performs little error checking and logging
33#   (i.e. USE AT YOUR OWN RISK)
34# * This script was last tested using ActiveState's port of Perl 5.005_02
35#   (internalcut #507) under Win95, though it does compile under Perl-5.00404
36#   for Solaris 2.4 run on a Solaris 2.6 system.  The script crashed
37#   occasionally under ActiveState's port of Perl 5.003_07 but this stopped
38#   happening with the update so if you are having problems, try updating Perl.
39#   Upgrading to cut #507 also seemed to coincide with a large speed
40#   improvement, so try and keep up, hey?  :)  It was executed from MKS's
41#   UNIX tools version 6.1 for Win32's sh.  ALWAYS redirect your output to
42#   a log!!!
43# * PVCS archives are left intact
44# * RCS archives are created in VCS/../RCS/ (or ./RCS using '-pflat')
45# * Branch labels in this script will be attached to the CVS magic
46#   revision number.  For branch a.b.c of a particular file, this means
47#   the label will be attached to revision a.b.0.c of the converted
48#   file.  If you use the TrunkTip (1.*) label, be aware that it will convert
49#   to RCS revision 0.1, which is useless to RCS and CVS.  You'll probably
50#   have to delete these.
51# * All revisions are saved with correct "metadata" (i.e. check-in date,
52#   author, and log message).  Any blank log message is replaced with
53#   "no comment".  This is because RCS does not allow non-interactive
54#   check in of a new revision without a comment string.
55# * Revision numbers are incremented by 1 during the conversion (since
56#   RCS does not allow revision 1.0).
57# * All converted branch numbers are even (the CVS paradigm)
58# * Version labels are assigned to the appropriate (incremented) revision
59#   numbers.  PVCS allows spaces and periods in version labels while RCS
60#   does not.  A global search and replace converts " " and "." to "_"
61#   There may be other cases that ought to be added.
62# * Any working (checked-out) copies of PVCS archives
63#   within the VCS/../ or vcs/../ (or possibly ./ with '-pflat')
64#   will be deleted (or overwritten) depending on your mode of
65#   operation since the current ./ is used in the checkout of each revision.
66#   I suppose if development continues these files could be redirected to
67#   temp space rather than ./ .
68# * Locks on PVCS archives should be removed (or the workfiles should be
69#   checked-in) prior to conversion, although the script will blaze through
70#   the archive nonetheless (But you would lose any checked out revision(s))
71# * The -kb option is added to the RCS archive for workfiles with the following
72#   extensions: .bin .out .btl .rom .a07 .lib .exe .tco .obj .t8u .c8u .o .lku
73#   .a and a few others.  The %bin_ext variable holds these values in regexp
74#   form.
75# * the --force-binary option can be used to convert binary files which don't
76#   have proper extensions, but I'd *probably* edit the %bin_ext variable.
77# * This script will abort occasionally with the error "invalid revision
78#   number".  This is known to happen when a revision comment has
79#   /^\s*Rev/ (Perl regexp notation) in it.  Fix the comment and start over.
80#   (The directory locks and existance checking make this a fairly quick
81#   process.)
82#   Binary files which do not have their mode set properly are likely to look
83#   corrupted on initial checkout and use, but using
84#   `cvs admin -kb <workfilename>' to retroactively change the RCS keyword
85#   substitution mode of the file to binary (and refreshing the files in any
86#   local workspaces they are checked out in: `rm <workfilename>; update'
87#   should do the trick) should end any problems with the original import.
88#   If anyone has checked in changes since the import, those revisions may
89#   be corrupted in the imported archive and therefore those changes (commits
90#   of corrupted data) may need to be backed out.
91# * This script writes lockfiles in the RCS/ directories.  It will also not
92#   convert an archive if it finds the RCS Archive existant in the RCS/
93#   directory.  This enables the conversion to quickly pick up where it left
94#   off after errors or interrupts occur.  If you interrupt the script make
95#   sure you delete the last RCS Archive File which was being written.
96#   If you recieve the "Invalid revision number" error, then the RCS archive
97#   file for that particular PVCS file will not have been created yet.
98# * This script will not create lockfiles when processing single
99#   filenames passed into the script, for hopefully obvious reasons.
100#   (lockfiles lock directories - DRP)
101# * Log the output to a file.  That makes it real easy to grep for errors
102#   later.  (grep for "^[ \t]*(rcs|ci):" and be aware I might have missed
103#   a few cases (get?  vcs?) !!!) *** Also note that this script will
104#   exibit some harmless RCS errors.  Namely, it will attempt to lock
105#   branches which haven't been created yet. ***
106# * I tried to keep the error and warning info up to date, but it seems
107#   to mean very little.  This script almost always exits with a warning
108#   or an error that didn't seem to cause any harm.  I didn't trace it
109#   and our imported source checks out and builds...
110#   It is probably happening when trying to convert empty directories
111#   or read files (possibly checked out workfiles ) which are not
112#   pvcs_archives.
113# * You must use the -pflat option when processing single filenames
114#   passed as arguments to the script.  This is probably a bug.
115# * questions, comments, additions can be sent to info-cvs@nongnu.org
116#########################################################################
117
118
119
120#
121# USER Configurables
122#
123
124# %bin_ext should be editable from the command line.
125#
126# NOTE:  Each possible binary extension is listed as a Perl regexp
127#
128# The value associated with each regexp key is used to print a log
129# message when a binary file is found.
130my %bin_ext =
131          (
132          '\.(?i)abs$' => "Absolute File",
133          '\.(?i)bin$' => "Binary",
134          '\.(?i)bit$' => "Bit File",
135          '\.(?i)ol$' => "Compiler Output",
136          '\.(?i)out$' => "Default Compiler Output",
137          '\.(?i)ln$' => "Linker Output",
138          '\.(?i)lob$' => "Lint Output",
139          '\.(?i)zob$' => "DBCO Object",
140          '\.(?i)mim$' => "MIME File",
141          '\.(?i)dwi$' => "DWI File",
142          '\.(?i)iop$' => "IOP File",
143          '\.(?i)btl$' => "",
144          '\.(?i)rom$' => "ROM File",
145          '\.(?i)a07$' => "",
146          '\.(?i)lib$' => "DOS/Wintel/Netware Compiler Library",
147          '\.(?i)lif$' => "Netware Binary File",
148          '\.(?i)(com|exe)$' => "DOS/Wintel Executable",
149          '\.(?i)tco$' => "",
150          '\.(?i)obj$' => "DOS/Wintel Compiler Object",
151          '\.(?i)res$' => "DOS/Wintel Resource File",
152          '\.(?i)ico$' => "DOS/Wintel Icon File",
153          '\.(?i)nlm$' => "Netware Loadable Module",
154          '\.(?i)t8u$' => "",
155          '\.(?i)c8u$' => "",
156          '\.(?i)lku$' => "",
157          '\.(?i)pdf$' => "Adobe Acrobat Portable Document Format",
158          '\.(?i)doc$' => "MS Word Document",
159          '\.(?i)dot$' => "MS Word Document Template",
160          '\.(?i)pps$' => "MS PowerPoint Presentation",
161          '\.(?i)xls$' => "MS Excel Spreadsheet",
162          '\.(?i)(bmp|gif|jfif|jpeg|jpg|png|tif|tiff|xbm)$' => "Image",
163          '\.(?i)(bz2|gz|tgz|zip)$' => "Compressed File",
164          '\.(?i)dll$' => "DOS/Wintel Dynamically Linked Library",
165          '\.(?i)class$' => "Compliled Java Class File",
166          '\.(?i)jar$' => "Java Archive File",
167          '\.(?i)war$' => "Java Web Archive File",
168          '\.o$' => "UNIX Compiler Object",
169          '\.a$' => "UNIX Compiler Library",
170          '\.so(\.\d+\.\d+)?$' => "UNIX Shared Library"
171          );
172
173# The binaries this script is dependant on:
174my @bin_dependancies = ("vcs", "vlog", "rcs", "ci");
175
176# Where we should put temporary files
177my $tmpdir = $ENV{TMPDIR} ? $ENV{TMPDIR} : "/var/tmp";
178
179# We use these...
180use strict;
181
182use Cwd;
183use File::Basename;                     # For the usage message.
184use File::Copy;
185use File::Path;
186use IO::File;
187use Getopt::Long;
188          $Getopt::Long::bundling = 1;
189
190my $program = basename $0;
191my $usage = "\
192usage:  $program -h
193        $program [-lt] [-i vcsid] [-r flat|leaf] [-p flat|leaf]
194                 [-x rcs_extension] [-v none|locks|exists] [options] [path...]
195";
196
197my $help = "\
198$usage
199     ----------------------------           -----------------------------------
200     -h | --Help                            Print this text
201
202     General Settings
203     ----------------------------           -----------------------------------
204     --Recurse                              Recurse through directories
205                                            (default)
206     -l | --NORecurse                       Process only .
207     --Errorfiles                           Save a count of conversion errors
208                                            in the RCS archive directory
209                                            (default) (unimplemented)
210     --NOErrorfiles                         Don't save a count of conversion
211                                            errors (unimplemented)
212     ( -m | --Mode ) Convert                Convert PVCS files to RCS files
213                                            (default)
214     ( -m | --Mode ) Verify                 Perform verification ONLY
215                                            (unimplemented)
216     ( -v | --VERIfy ) None                 Always replace existing RCS files
217     ( -v | --VERIfy ) LOCKS                Same as exists unless a #conv.done
218                                            file exists in the RCS directory.
219                                            In that case, only the #conv.done
220                                            file's existance is verified for
221                                            that directory.  (default)
222     ( -v | --VERIfy ) Exists               Don't replace existing RCS files
223     ( -v | --VERIfy ) LOCKDates            Verify that an existing RCS file's
224                                            last modification date is older
225                                            than that of the lockfile
226                                            (unimplemented)
227     ( -v | --VERIfy ) Revs                 Verify that the PVCS archive files
228                                            and RCS archive file contain the
229                                            same number of corresponding
230                                            revisions.  Add only new revisions
231                                            to the RCS file.  (unimplemented)
232     ( -v | --VERIfy ) Full                 Perform --verify=Revs and confirm
233                                            that the text of the revisions is
234                                            identical.  Add only new revisions
235                                            unless an error is found.  Then
236                                            erase the RCS archive and recreate
237                                            it.  (unimplemented)
238     -t | --Test-binaries                   Use 'which' to check \$PATH for
239                                            the binaries required by this
240                                            script (default)
241     --NOTest-binaries                      Don't check for binaries
242     --VERBose                              Enable verbose output
243     --NOVerbose                            Disable verbose output (default)
244     -w | --Warnings                        Print warning messages (default)
245     --NOWarnings                           Don't print warning messages
246
247     RCS Settings
248     ----------------------------           -----------------------------------
249     ( -r | --RCS-Dirs ) leaf               RCS files stored in ./RCS (default)
250     ( -r | --RCS-Dirs ) flat               RCS files stored in .
251                                            (unimplemented)
252     ( -x | --RCS-Extension )               Set RCS file extension
253                                            (default = ',v')
254     --Force-binary                         Pass '-kb' to 'rcs -i' regardless
255                                            of the file extension
256     --NOForce-binary                       Only use '-kb' when the file has
257                                            a binary extension (default)
258     --CVS-Branch-labels                    Use CVS magic branch revision
259                                            numbers when attaching branch
260                                            labels (default)
261     --NOCvs-branch-labels                  Attach branch labels to RCS branch
262                                            revision numbers (unimplemented)
263
264     CVS Settings
265     ----------------------------           -----------------------------------
266     ( -d | --CVS-Module-path)              Import RCS files directly into this
267                                            destination directory rather than
268                                            converting in place
269
270     PVCS Settings
271     ----------------------------           -----------------------------------
272     ( -p | --Pvcs-dirs ) leaf              PVCS files expected in ./VCS
273                                            (default)
274     ( -p | --Pvcs-dirs ) flat              PVCS files expected in .
275     ( -i | --VCsid ) vcsid                 Use vcsid instead of \$VCSID
276
277     --------------------------------------------------------------------------
278     The optional path argument should contain the name of a file or directory
279     to convert.  If not given, it will default to '.'.
280     --------------------------------------------------------------------------
281";
282
283
284
285#
286# Initialize globals
287#
288
289my ($errors, $warnings) = (0, 0);
290my ($curlevel, $maxlevel);
291my ($rcs_base_command, $ci_base_command);
292my ($donefile_name, $errorfile_name);
293my @rel_dirs = ();  # list of relative directory names up to current dir
294
295
296# set up the default options
297my %options = (
298          'recurse' => 1,
299          'mode' => "convert",
300          'errorfiles' => 1,
301          'rcs-dirs' => "leaf",
302          'rcs-extension' => ",v",
303          'force-binary' => 0,
304          'cvs-branch-labels' => 1,
305          'cvs-module-path' => undef,
306          'pvcs-dirs' => "leaf",
307          'verify' => "locks",
308          'test-binaries' => 1,
309          'vcsid' => $ENV{VCSID} || "",
310          'verbose' => 0,
311          'debug' => 0,
312          'warnings' => 1
313          );
314
315
316
317# This is untested except under Solaris 2.4 or 2.6 and
318# may not be portable
319#
320# I think the readline lib or some such has an interface
321# which may enable this now.  The perl installer sure looks
322# like it's testing this kind of thing, anyhow.
323sub hit_any_key
324          {
325          STDOUT->autoflush;
326          system "stty", "-icanon", "min", "1";
327
328          print "Hit any key to continue...";
329          getc;
330
331          system "stty", "icanon", "min", "0";
332          STDOUT->autoflush (0);
333
334          print "\nI always wondered where that key was...\n";
335          }
336
337
338
339# print the usage
340sub print_usage
341          {
342          my $fh = shift;
343          unless (ref $fh)
344                    {
345                    my $fdn = $fh ? $fh : "STDERR";
346                    $fh = new IO::File;
347                    $fh->fdopen ($fdn, "w");
348                    }
349
350          $fh->print ($usage);
351          }
352
353# print the help
354sub print_help
355          {
356          my $fh = shift;
357          unless (ref $fh)
358                    {
359                    my $fdn = $fh ? $fh : "STDOUT";
360                    $fh = new IO::File;
361                    $fh->fdopen ($fdn, "w");
362                    }
363
364          $fh->print ($help);
365          }
366
367# print the help and exit $_[0] || 0
368sub exit_help
369          {
370          print_help;
371          exit shift || 0;
372          }
373
374sub error_count
375          {
376          my $type = shift
377                    or die "$0:  error - error_count usage:  error_count type [, ref] [, LIST]\n";
378          my $error_count_ref;
379          my $outstring;
380
381          if (ref ($_[0]) && ref ($_[0]) == "SCALAR")
382                    {
383                    $error_count_ref = shift;
384                    }
385          else
386                    {
387                    $error_count_ref = \$errors;
388                    }
389          $$error_count_ref++;
390
391          push @_, "something wrong.\n" unless ( @_ > 0 );
392
393          $outstring = sprintf "$0:  $type - " . join ("", @_);
394          $outstring .= sprintf " - $!\n" unless ($outstring =~ /\n$/);
395
396          print STDERR $outstring;
397
398          if ($options{errorfiles})
399                    {
400                    my $fh = new IO::File ">>$errorfile_name" or new IO::File ">$errorfile_name";
401                    if ($fh)
402                              {
403                              $fh->print ($$error_count_ref . "\n");
404                              $fh->print ($outstring);
405                              $fh->close;
406                              }
407                    else
408                              {
409                              my $cd = cwd;
410                              print STDERR "$0: error - failed to open errorfile $cd/$errorfile_name - $!\n"
411                                                  if ($options{debug});
412                              }
413                    }
414
415          return $$error_count_ref;
416          }
417
418
419
420# the main procedure that is run once in each directory
421sub execdir
422          {
423          my $dir = shift;
424          my ($errors, $warnings) = (0, 0);                                               # We return these error counters
425          my $old_dir = cwd;
426
427          local ($_, @_);
428
429          my $i;                                                                                    # Generic counter
430          my ($pvcsarchive, $workfile, $rcsarchive);                                      # .??v, checked out file, and ,v files,
431                                                                                                    # respectively
432          my ($rev_count, $first_vl, $last_vl, $description,
433                              $rev_index, @rev_num, %checked_in, %author,
434                              $relative_comment_index, @comment_string,
435                              %comment);
436          my ($num_version_labels, $label_index, @label_revision, $label,
437                              @new_label, $rcs_rev);
438          my ($revision, %rcs_rev_num);
439          my @remainder;
440          my ($get_output, $rcs_output, $ci_output, $mv_output);
441          my ($ci_command, $rcs_command, $wtr);
442          my @hits;
443          my ($num_fields);
444          my $skipdirlock;                                                                # if true, don't write conv.out
445                                                                                                    # used only for single file operations
446                                                                                                    # at the moment
447          my $cd;
448          my $cvs_dir;
449
450          my @filenames;
451          # We may have recieved a single file name to process...
452          if ( -d $dir )
453                    {
454                    # change into the directory to be processed
455                    # open the current directory for listing
456                    # initialize the list of filenames
457                    # and set filenames equal to directory listing
458                    unless ( ( chdir $dir ) and ( opendir CURDIR, "." ) and ( @filenames = readdir CURDIR ) )
459                              {
460                              $cd = cwd;
461                              error_count 'error', \$errors, "skipping directory $dir from $cd";
462                              chdir $old_dir or die "Failed to restore original directory ($old_dir): ", $!, ", stopped";
463                              return ($errors, $warnings);
464                              }
465
466                    # clean up by closing the directory
467                    closedir(CURDIR);
468
469                    if ($options{'rcs-dirs-flat'} && $options{'cvs-module-path'})
470                              {
471                              my @cur_dir_names = split qr{[/\\]}, cwd;
472                              my $rel_cd = $cur_dir_names[-1];
473                              push @rel_dirs, $rel_cd;
474                              $cvs_dir = "$options{'cvs-module-path'}/"
475                                         . join "/", @rel_dirs;
476                              if (!-d $cvs_dir)
477                                        {
478                                        print "Creating directory \`$cvs_dir'\n";
479                                        if (!mkpath ($cvs_dir))
480                                                  {
481                                                  pop @rel_dirs;
482                                                  error_count 'error', \$errors,
483"failed to make directory \`$cvs_dir' - skipping directory \`$cd'";
484                                                  chdir $old_dir or die
485"Failed to restore original directory (\`$old_dir'): ", $!, ", stopped";
486                                                  return ($errors, $warnings);
487                                                  # after all, we have nowhere to put
488                                                  # them...
489                                                  }
490                                        }
491                              }
492
493                    }
494          elsif ( -f $dir ) # we recieved a single file
495                    {
496                    push @filenames, $dir;
497                    $skipdirlock = 1;
498                    }
499          else
500                    {
501                    $cd = cwd;
502                    error_count 'error', \$errors, "no such directory/file $dir from $cd\n";
503                    chdir $old_dir or die
504"Failed to restore original directory ($old_dir): ", $!, ", stopped";
505                    return ($errors, $warnings);
506                    }
507
508          # save the current directory
509          $cd = cwd;
510
511          # increment the global $curlevel variable
512          $curlevel = $curlevel +1;
513
514          # initialize a list for any subdirectories and any files
515          # we need to process
516          my $vcsdir = "";
517          my (@subdirs, $fn, $file, @files, @pvcsarchives);
518
519          # print "$cd:  " . join (", ", @filenames) . "\n";
520          # hit_any_key;
521
522          (@files, @pvcsarchives) = ( (), () );
523          # begin a for loop to execute on each filename in the list @filename
524          foreach $fn (@filenames)
525                    {
526                    # if the file is a directory...
527                    if (-d $fn)
528                              {
529                              # then if we are not expecting a flat arrangement of pvcs files
530                              # and we found a vcs directory add its files to @pvcsarchives
531                              if (!$options{'pvcs-dirs-flat'} and $fn =~ /^vcs$/i)
532                                        {
533                                        if ($options{verify} =~ /^locks$/ ) {
534                                        if ( -f $donefile_name ) {
535                                                  print "Verified existence of lockfile $cd/$donefile_name."
536                                                                      . ( ($options{mode} =~ /^convert$/) ? "  Skipping directory." : "" )
537                                                                      . "\n" if ($options{verbose});
538                                                  next;
539                                        } elsif ( $options{mode} =~ /^verify$/ ) {
540                                                  print "No lockfile found for $cd .\n";
541                                                  next;
542                                        }
543                                        }
544
545                                        # else add the files in the vcs dir to our list of files to process
546                                        error_count 'warning', \$warnings, "Found two vcs dirs in directory $cd.\n"
547                                                            if ($vcsdir and $options{warnings});
548
549                                        $vcsdir = $fn;
550
551                                        unless ( ( opendir VCSDIR, $vcsdir ) and ( @files = readdir VCSDIR ) )
552                                                  {
553                                                  error_count 'error', \$errors, "skipping directory &cd/$fn";
554                                                  next;
555                                                  }
556                                        closedir VCSDIR;
557
558                                        # and so we don't need to worry about where these
559                                        # files came from later...
560                                        foreach $file (@files)
561                                                  {
562                                                  push @pvcsarchives, "$vcsdir/$file" if (-f "$vcsdir/$file");
563                                                  }
564
565                                        # don't want recursion here...
566                                        @pvcsarchives = grep !/^\.\.?$/, @pvcsarchives;
567                                        }
568                              elsif ($fn !~ /^\.\.?$/)
569                                        {
570                                        next if (!$options{'rcs-dirs-flat'} and $fn =~ /^rcs$/i);
571                                        # include it in @subdir if it's not a parent directory
572                                        push(@subdirs,$fn);
573                                        }
574                              }
575                    # else if we are processing a flat arrangement of pvcs files...
576                    elsif ($options{'pvcs-dirs-flat'} and -f $fn)
577                              {
578                              if ($options{verify} =~ /^locks$/) {
579                                        if ( -f $donefile_name) {
580                                                  print "Found lockfile $cd/$donefile_name."
581                                                            . ( ($options{mode} =~ /^convert$/) ? "  Skipping directory." : "" )
582                                                            . "\n" if ($options{verbose});
583                                                  last;
584                                        } elsif ($options{mode} =~ /^verify$/) {
585                                                  print "No lockfile found for $cd .\n";
586                                                  last;
587                                        }
588                              }
589                              # else add this to the list of files to process
590                              push (@pvcsarchives, $fn);
591                              }
592                    }
593
594          # print "pvcsarchives:  " . join (", ", @pvcsarchives) . "\n";
595          # print "subdirs:  " . join (", ", @subdirs) . "\n";
596          # hit_any_key;
597
598          # for loop of subdirs
599          foreach (@subdirs)
600                    {
601                    # run execdir on each sub dir
602                    if ($maxlevel >= $curlevel)
603                              {
604                              my ($e, $w) = execdir ($_);
605                              $errors += $e;
606                              $warnings += $w;
607                              }
608                    }
609
610          # Print output header for each directory
611          print("Directory: $cd\n");
612
613          # the @files variable should already contain the list of files
614          # we should attempt to process
615          if ( @pvcsarchives && ( $options{mode} =~ /^convert$/ ) )
616                    {
617                    # create an RCS directory in parent to store RCS files in
618                    if ( !( $options{'rcs-dirs-flat'} or (-d "RCS") or mkpath ( "RCS" ) ) )
619                              {
620                              error_count 'error', \$errors, "failed to make directory $cd/RCS - skipping directory $cd";
621                              @pvcsarchives = ();
622                              # after all, we have nowhere to put them...
623                              }
624                    }
625
626          # begin a for loop to execute on each filename in the list @files
627          foreach $pvcsarchive (@pvcsarchives)
628                    {
629                    my $got_workfile = 0;
630                    my $got_version_labels = 0;
631                    my $got_description = 0;
632                    my $got_rev_count = 0;
633
634                    my $abs_file = $cd . "/" . $pvcsarchive;
635
636                    print("Verifying $abs_file...\n") if ($options{verbose});
637
638                    print "vlog $pvcsarchive\n";
639                    # FIXME: Quoting this is better than no quotes, but quotes in
640                    #        filenames remain unquoted.
641                    my $vlog_output = `vlog \"$pvcsarchive\"`;
642
643                    # Split the vcs status output into individual lines
644                    my @vlog_strings = split /\n/, $vlog_output;
645                    my $num_vlog_strings = @vlog_strings;
646                    $_ = $vlog_strings[0];
647                    if ( /^\s*$/ || /^vlog: warning/ )
648                              {
649                              error_count 'warning', \$warnings, "$abs_file is NOT a valid PVCS archive!!!\n";
650                              next;
651                              }
652
653                    my $num;
654                    # Collect all vlog output into appropriate variables
655                    #
656                    # This will ignore at the very least the /^\s*Archive:\s*/ field
657                    # and maybe more.  This should not be a problem.
658                    for ( $num = 0; $num < $num_vlog_strings; $num++ )
659                              {
660                              # print("$vlog_strings[$num]\n");
661                              $_ = $vlog_strings[$num];
662
663                              if( ( /^Workfile:\s*/ ) && (!$got_workfile ) )
664                                        {
665                                        my $num_fields;
666
667                                        $got_workfile = 1;
668                                        # get the string to the right of the above search (with any path stripped)
669                                        $workfile = $';
670                                        $num_fields = split /[\/\\]/, $workfile;
671                                        if ( $num_fields > 1 )
672                                                  {
673                                                  $workfile = $_[$num_fields - 1 ];
674                                                  }
675
676                                        $rcsarchive = $options{'rcs-dirs-flat'} ? "" : "RCS/";
677                                        $rcsarchive .= $workfile;
678                                        $rcsarchive .= $options{'rcs-extension'} if ($options{'rcs-extension'});
679                                        print "Workfile is $workfile\n" if ($options{debug});
680                                        }
681
682                              elsif ( ( /^Rev count:\s*/ ) && (!$got_rev_count ) )
683                                        {
684                                        $got_rev_count = 1;
685                                        # get the string to the right of the above search
686                                        $rev_count = $';
687                                        print "Revision count is $rev_count\n";
688                                        }
689
690                              elsif ( ( /^Version labels:\s*/ ) && (!$got_version_labels ) )
691                                        {
692                                        $got_version_labels = 1;
693                                        $first_vl = $num+1;
694                                        print "Version labels start at $first_vl\n" if ($options{debug});
695                                        }
696
697                              elsif ( ( /^Description:\s*/ ) && (!$got_description ) )
698                                        {
699                                        $got_description = 1;
700                                        $description = $vlog_strings[$num+1];
701                                        print "Description is `$description'\n" if ($options{debug});
702                                        $last_vl = $num++ - 1;
703                                        }
704
705                              elsif ( /^Rev\s+/ ) # get all the revision information at once
706                                        {
707                                        $rev_index = 0;
708                                        @rev_num = ();
709                                        while ( $rev_index < $rev_count )
710                                                  {
711                                                  $_ = $vlog_strings[$num];
712                                                  /^\s*Rev\s+(\d+\.(\d+\.\d+\.)*\d+)$/;
713                                                  $rev_num[$rev_index] = $1;
714                                                  print "Found revision: $rev_num[$rev_index]\n" if ($options{debug});
715                                                  die "Not a valid revision ($rev_num[$rev_index]).\n"
716                                                            if ($rev_num[$rev_index] !~ /^(\d+\.)(\d+\.\d+\.)*\d+$/);
717
718                                                  $_ = $vlog_strings[$num+1];
719                                                  /^\s*Locked\s*/ and $num++;
720
721                                                  $_ = $vlog_strings[$num+1];
722                                                  /^\s*Checked in:\s*/;
723                                                  $checked_in{$rev_num[$rev_index]} = "\"" . $' . "\"";
724                                                  print "Checked in: $checked_in{$rev_num[$rev_index]}\n" if ($options{debug});
725
726                                                  $_ = $vlog_strings[$num+3];
727                                                  /^\s*Author id:\s*/;
728                                                  my @fields = split;
729                                                  $author{$rev_num[$rev_index]} = "\"" . $fields[2] . "\"";
730                                                  print "Author: $author{$rev_num[$rev_index]}\n" if ($options{debug});
731
732                                                  my @branches = ();
733                                                  $_ = $vlog_strings[$num+1];
734                                                  if (/^\s*Branches:\s*/)
735                                                            {
736                                                            $num++;
737                                                            @branches = split /\s+/, $';
738                                                            }
739
740                                                  $relative_comment_index = 0;
741                                                  @comment_string = ();
742                                                  while (($num + 4 + $relative_comment_index) < @vlog_strings)
743                                                            {
744                                                            last if $vlog_strings[$num+4+$relative_comment_index]
745                                                                      =~ /^\s*Rev\s+(\d+\.(\d+\.\d+\.)*\d+)$/
746                                                                    && $vlog_strings[$num+3+$relative_comment_index]
747                                                                      =~ /^-{35}$/;
748
749                                                            # We need the \n added for multi-line comments.  There is no effect for
750                                                            # single-line comments since RCS inserts the \n if it doesn't exist already
751                                                            # print "Found commment line: $vlog_strings[$num+4+$relative_comment_index]\n"
752                                                            #         if ($options{debug});
753                                                            push @comment_string, $vlog_strings[$num+4+$relative_comment_index], "\n";
754                                                            $relative_comment_index += 1;
755                                                            }
756                                                  # print "Popped from comment: " . join ("", splice (@comment_string, -2))
757                                                  #                   . "\n"
758                                                  #         if ($options{debug});
759                                                  # Pop the "-+" or "=+" line from the comment
760                                                  while ( (pop @comment_string) !~ /^-{35}|={35}$/ )
761                                                            {}
762                                                  $comment{$rev_num[$rev_index]} = join "", @comment_string;
763
764                                                  $num += ( 4 + $relative_comment_index );
765                                                  print "Got comment for $rev_num[$rev_index]\n" if ($options{debug});
766                                                  print "comment string: $comment{$rev_num[$rev_index]}\n" if ($options{debug});
767                                                  $rev_index += 1;
768                                                  } # while ( $rev_index < $rev_count )
769                                        $num -= 1; #although there should be nothing left for this to matter
770                                        } # Get Rev information
771                              } # for ($num = 0; $num < $num_vlog_strings; $num++)
772                    # hit_any_key if ($options{debug});
773                    # Create RCS revision numbers corresponding to PVCS version numbers
774                    my @rcs_rev_nums;
775                    foreach $revision (@rev_num)
776                              {
777                              $rcs_rev_num{ $revision } = &pvcs_to_rcs_rev_number( $revision );
778                              push @rcs_rev_nums, $rcs_rev_num{$revision};
779                              print"PVCS revision is $revision; RCS revision is $rcs_rev_num{ $revision }\n"
780                                                  if ($options{debug});
781                              }
782
783                    # Sort the revision numbers - PVCS and RCS store them in different orders
784                    # Clear @_ so we don't pass anything in by accident...
785                    @_ = ();
786                    @rev_num = sort revisions @rev_num;
787                    print "Sorted rev_nums:\n" . join ("\n", @rev_num) . "\n" if ($options{debug});
788                    # hit_any_key;
789
790                    # Loop through each version label, checking for need to relabel ' ' with '_'.
791                    $num_version_labels = $last_vl - $first_vl + 1;
792                    print "Version label count is $num_version_labels\n";
793                    for( $i = $first_vl; $i <= $last_vl; $i += 1 )
794                              {
795                              # print("$vlog_strings[$i]\n");
796                              $label_index = $i - $first_vl;
797                              $_=$vlog_strings[$i];
798                              print "Starting with string '$_'\n" if ($options{debug});
799                              my @fields = split /\"/;
800                              $label = $fields[1];
801                              print "Got label '$label'\n" if ($options{debug});
802                              @fields = split /\s+/, $fields[2];
803                              $label_revision[$label_index] = $fields[2];
804                              print "Original label is $label_revision[$label_index]\n" if ($options{debug});
805
806                              # Create RCS revision numbers corresponding to PVCS version numbers by
807                              # adding 1 to the revision number (# after last .)
808                              $label_revision[ $label_index ] = pvcs_to_rcs_rev_number( $label_revision [ $label_index ] );
809                              # replace ' ' with '_', if needed
810                              $_=$label;
811                              $new_label[$label_index] = $label;
812                              $new_label[$label_index] =~ s/ /_/g;
813                              $new_label[$label_index] =~ s/\./_/g;
814                              $new_label[$label_index] = "\"" . $new_label[$label_index] . "\"";
815                              print"Label $new_label[$label_index] is for revision $label_revision[$label_index]\n" if ($options{debug});
816                              }
817
818                    ##########
819                    #
820                    # See if the RCS archive is up to date with the PVCS archive
821                    #
822                    ##########
823                    my $cvsarchive;
824                    $cvsarchive = "$cvs_dir/$rcsarchive" if $options{'cvs-module-path'};
825                    $cvsarchive .= $rcsarchive;
826                    if ($options{verify} =~ /^locks|exists$/ and -f $cvsarchive)
827                              {
828                              print "Verified existence of "
829                                  . ($options{'cvs-module-path'} ? $cvsarchive : "$cd/$rcsarchive")
830                                  . "."
831                                                  . ( ($options{mode} =~ /^convert$/) ? "  Skipping." : "" )
832                                                  . "\n" if ($options{verbose});
833                              next;
834                              }
835
836                    # Create RCS archive and check in all revisions, then label.
837                    my $first_time = 1;
838                    foreach $revision (@rev_num)
839                              {
840                              # print "get -p$revision $pvcsarchive >$workfile\n";
841                              print "get -r$revision $pvcsarchive\n";
842                              # $vcs_output = `vcs -u -r$revision $pvcsarchive`;
843                              # $get_output = `get -p$revision $pvcsarchive >$workfile`;
844                              # FIXME: Doesn't handle quotes in filenames as FIXME above.
845                              $get_output = `get -r$revision \"$pvcsarchive\"`;
846
847                              # if this is the first time, delete the rcs archive if it exists
848                              # need for $options{verify} == none
849                              unlink $rcsarchive if ($first_time and $options{verify} =~ /^none$/ and -f $rcsarchive);
850
851                              # Also check here whether this file ought to be "binary"
852                              if ( $first_time )
853                                        {
854                                        $rcs_command = "$rcs_base_command -i";
855                                        if ( ( @hits = grep { $workfile =~ /$_/ } keys %bin_ext ) || $options{'force-binary'} )
856                                                  {
857                                                  $rcs_command .= " -kb";
858                                                  $workfile =~ /$hits[0]/ if (@hits);
859                                                  print "Binary attribute -kb added ("
860                                                            . (@hits ? "file type is '$bin_ext{$hits[0]}' for extension '$&'" : "forced")
861                                                            . ")\n";
862                                                  }
863
864                                        # FIXME: Doesn't handle quotes and other special characters in
865                                        #        filenames as two FIXMEs above.
866                                        $rcs_command .= " \"$workfile\"";
867
868                                        # print and execute the rcs archive initialization command
869                                        print "$rcs_command\n";
870                                        $wtr = new IO::File "|$rcs_command";
871                                        $wtr->print ($description);
872                                        $wtr->print ("\n") unless ($description =~ /\n$/s);
873                                        $wtr->print (".\n");
874                                        $wtr->close;
875
876                                        # $rcs_output = `$rcs_base_command -i -kb $workfile`;
877                                        }
878
879                              # if this isn't the first time, we need to lock the rcs branch
880                              #
881                              # This is a little messy, but it works.  Some extra locking is attempted.
882                              # (This happens the first time a branch is used, at the least)
883                              my $branch = "";
884                              my @branch;
885                              @branch = split /\./, $rcs_rev_num{$revision};
886                              pop @branch;
887                              $branch = join ".", @branch if @branch != 1;
888
889                              # FIXME: Quotes around file names handles spaces but not shell
890                              #        metacharacters in file names.
891                              unless ($first_time)
892                              {
893                                        print "$rcs_base_command -l$branch \"$workfile\"\n"
894                                                  if $options{'debug'};
895                                        $rcs_output = `$rcs_base_command -l$branch \"$workfile\"`;
896                              }
897
898                              # If an empty comment is specified, RCS will not check in the file;
899                              # check for this case.        (but an empty -t- description is fine - go figure!)
900                              # Since RCS will pause and ask for a comment if one is not given,
901                              # substitute a dummy comment "no comment".
902                              $comment{$revision} =~ /^\s*$/ and $comment{$revision} = "no comment\n";
903
904                              $ci_command = $ci_base_command;
905                              $ci_command .= " -f -r$rcs_rev_num{$revision} -d$checked_in{$revision}"
906                                                  . " -w$author{$revision}";
907
908                              $ci_command .= " \"$workfile\"";
909
910                              # print and execute the ci command
911                              print "$ci_command\n";
912                              $wtr = new IO::File "|$ci_command";
913                              $wtr->print ($comment{$revision});
914                              $wtr->print ("\n") unless ($comment{$revision} =~ /\n$/s);
915                              $wtr->print (".\n");
916                              $wtr->close;
917                              # $ci_output = `$ci_command`;
918                              # $ci_output = `cat $tmpdir/ci.out`;
919
920                              $first_time = 0 if ($first_time);
921                              } # foreach revision
922
923                    # Keep track of 1.*, 2.*, etc. branches as they are created.
924                    my %trunk_branches;
925
926                    # Attach version labels
927                    for( $i = $num_version_labels - 1; $i >= 0; $i -= 1 )
928                              {
929                              print "$rcs_base_command -n$new_label[$i]:$label_revision[$i] \"$workfile\"\n"
930                                        if $options{'debug'};
931                              $rcs_output = `$rcs_base_command -n$new_label[$i]:$label_revision[$i] \"$workfile\"`;
932                              print "Version label $new_label[$i] added to revision $label_revision[$i]\n";
933
934                              # If the label revision is attached to a 1.* revision on the trunk
935                              # when a 2.* revision exists, then 1.MAX needs to be branched to
936                              # allow commits to this label.  This applies to 2.* when 3.*
937                              # exists, as well.
938                              if ($label_revision[$i] !~ /\./)
939                              {
940                                        # This revision is attached to the trunk.
941                                        # $rcs_rev_nums[0] will always be the max revision.
942                                        print "Label `$new_label[$i]' moved from $label_revision[$i] to ";
943                                        if (exists $trunk_branches{$label_revision[$i]})
944                                        {
945                                                  $label_revision[$i] = $trunk_branches{$label_revision[$i]};
946                                        }
947                                        else
948                                        {
949                                                  # Attached to X.* with X < M
950                                                  my @X_revs = grep /^$label_revision[$i]\./, @rcs_rev_nums;
951                                                  # Need a _NEW_ branch from $X_revs[0] to attach
952                                                  # to.  CVS could do this easily, but our archive
953                                                  # isn't in a CVS repository yet.
954                                                  my @tmp_lbl = @label_revision;
955                                                  my @branch_nums = grep s/^\Q$X_revs[0]\E\.0\.(\d+)$/$1/, @tmp_lbl;
956                                                  @tmp_lbl = @rcs_rev_nums;
957                                                  push @branch_nums,
958                                                  grep (s/^\Q$X_revs[0]\E\.(\d+)\.\d+$/$1/, @tmp_lbl);
959                                                  my $max = 0;
960                                                  foreach my $num (@branch_nums)
961                                                  {
962                                                            $max = $num if $num > $max;
963                                                  }
964                                                  $max += 2;
965                                                  $trunk_branches{$label_revision[$i]} = "$X_revs[0].0.$max";
966                                                  $label_revision[$i] = "$X_revs[0].0.$max";
967                                        }
968                                        print "$label_revision[$i].\n";
969                              }
970
971                              $rcs_output = `$rcs_base_command -n$new_label[$i]:$label_revision[$i] \"$workfile\"`;
972                              print "Version label $new_label[$i] added to revision $label_revision[$i]\n";
973
974                              if ($label_revision[$i] =~ /^(.*)\.0\./)
975                              {
976                                        my $base = $1;
977                                        my $rootlbl = $new_label[$i];
978                                        $rootlbl =~ s/.$/_broot$&/;
979                                        $rcs_output = `$rcs_base_command -n$rootlbl:$base \"$workfile\"`;
980                                        print "Version label $rootlbl added to revision $base\n";
981                              }
982
983                              } # foreach label
984
985                    if ($options{'cvs-module-path'})
986                    {
987                                        print "Moving $rcsarchive to $cvsarchive\n";
988                                        move $rcsarchive, $cvsarchive or warn "Move failed: $!";
989                    }
990
991                    # hit_any_key;
992                    } # foreach pvcs archive file
993
994          # We processed a vcs directory, so if there were any files, lock it.
995          # We are guaranteed to have made the attempt at
996          #
997          # $skipdirlock gets set if a single file name was passed to this function to enable
998          # a '$0 *' operation...
999          if ( @pvcsarchives && !$skipdirlock)
1000                    {
1001                    my $fh = new IO::File ">>$donefile_name" or new IO::File ">$donefile_name";
1002                    if ($fh)
1003                              {
1004                              $fh->close;
1005                              }
1006                    else
1007                              {
1008                              error_count 'error', \$errors, "couldn't create lockfile $cd/$donefile_name";
1009                              }
1010                    }
1011
1012          $curlevel = $curlevel - 1;
1013
1014          chdir $old_dir
1015                    or die "Failed to restore original directory ($old_dir): ", $!, ", stopped";
1016
1017          # Update the relative directory path.
1018          pop @rel_dirs if -d $dir;
1019
1020          return ($errors, $warnings);
1021          }
1022
1023
1024
1025#
1026# This function effectively does a cmp between two revision numbers
1027# It is intended to be passed into Perl's sort routine.
1028#
1029# the pvcs_out is not implemented well.  It should probably be
1030# returnning $b[0] <=> $a[0] rather than $a[0] <=> $b[0]
1031#
1032# The @_ argument implementation was going to be used for revision
1033# comparison as an aid to remove the /^\sRev/ in revision comment
1034# error.  The effort was fruitless at the time.
1035sub revisions
1036          {
1037          my @a = split /\./, (defined $a) ? $a : shift;
1038          my @b = split /\./, (defined $b) ? $b : shift;
1039          my $function = @_ ? shift : 'rcs_in';
1040          my ($i, $ret_val);
1041
1042          die "Not enough arguments to revisions : a = ", join (".", @a),
1043                              "; b = ", join (".", @b), ", stopped"
1044                    unless (@a and @b);
1045
1046          for ($i = 0; $i < scalar( @a ) && $i < scalar( @b ); $i++)
1047                    {
1048                    $a[$i] == $b[$i] or return ($a[$i] <=> $b[$i]);
1049                    }
1050
1051          return 0 if (scalar (@a) == scalar (@b));
1052
1053          if ($function eq 'rcs_in')
1054                    {
1055                    return (($i == @b) || -1);
1056                    }
1057          elsif ($function eq 'pvcs_out')
1058                    {
1059                    return (($i == @a) || -1);
1060                    }
1061          else
1062                    {
1063                    die "error - Invalid function type passed to revisions ($function)", ", stopped";
1064                    }
1065          }
1066
1067
1068
1069sub pvcs_to_rcs_rev_number
1070          {
1071          my($input, $num_fields, @rev_string, $return_rev_num, $i);
1072
1073          $input = $_[0];
1074          $num_fields = split /\./, $input;
1075          @rev_string = @_;
1076          # @rev_string[$num_fields-1] += 1;
1077
1078          for( $i = 1; $i < $num_fields; $i += 1 )
1079                    {
1080                    if ( $i % 2 )
1081                              {
1082                              # DRP: 10/1
1083                              # RCS does not allow revision zero
1084                              $rev_string[ $i ] += 1;
1085                              }
1086                    elsif ( $i )
1087                              {
1088                              # DRP: 10/1
1089                              # Branches must have even references for compatibility
1090                              # with CVS's magic branch numbers.
1091                              # (Indexes 2, 4, 6...)
1092                              $rev_string[ $i ] *= 2;
1093                              }
1094                    }
1095
1096          # If this is a branch revision # (PVCS: a.b.c.*) then we want the CVS
1097          # revision # instead.  It's okay to do this conversion here since we
1098          # never commit to branches.  We'll only get a PVCS revision # in that
1099          # form when looking through the revision labels.
1100          if ($input =~ /\*$/)
1101                    {
1102                    pop @rev_string;
1103                    # If there is only one entry in @rev_string, this is a
1104                    # revision that needs to be attached to the trunk.  Let it be
1105                    # for now.  It might require a new branch, but we can't decide
1106                    # which branches are valid to create before we know what
1107                    # branches already exist.
1108                    push @rev_string, splice (@rev_string, -1, 1, "0")
1109                              unless @rev_string == 1;
1110                    }
1111
1112          $return_rev_num = join ".", @rev_string;
1113          return $return_rev_num;
1114          }
1115
1116
1117
1118
1119
1120###
1121###
1122###
1123###
1124###
1125###   MAIN program: checks to see if there are command line parameters
1126###
1127###
1128###
1129###
1130###
1131
1132
1133
1134
1135
1136# and read the options
1137die $usage
1138          unless GetOptions (\%options, "h|help" => \&exit_help,
1139                             "recurse!", "mode|m=s", "errorfiles!", "l",
1140                             "rcs-dirs|rcs-directories|r=s",
1141                             "pvcs-dirs|pvcs-directories|p=s", "test-binaries|t!",
1142                             "rcs-extension=s", "verify|v=s", "vcsid|i=s", "verbose!",
1143                             "debug!", "force-binary!", "cvs-branch-labels!",
1144                             "warnings|w!", "cvs-module-path|d=s");
1145
1146
1147
1148#
1149# Special processing for -l !^#%$^@#$%#$
1150#
1151# At the moment, -l overrides --recurse, regardless of the order the
1152# options were passed in
1153#
1154$options{recurse} = 0 if defined $options{l};
1155delete $options{l};
1156
1157
1158
1159# Make sure we got acceptable values for rcs-dirs and pvcs-dirs
1160my @hits = grep /^$options{'rcs-dirs'}/i, ("leaf", "flat");
1161@hits == 1 or die
1162                      "$0: $options{'rcs-dirs'} invalid argument to --rcs-dirs or ambiguous\n"
1163                    . "    abbreviation.\n"
1164                    . "    Must be one of: 'leaf' or 'flat'.\n"
1165                    . $usage;
1166$options{'rcs-dirs'} = $hits[0];
1167$options{'rcs-dirs-flat'} = ($options{'rcs-dirs'} =~ /flat/);
1168delete $options{'rcs-dirs'};
1169
1170@hits = grep /^$options{'pvcs-dirs'}/i, ("leaf", "flat");
1171@hits == 1 or die
1172                      "$0: $options{'pvcs-dirs'} invalid argument to --pvcs-dirs or ambiguous\n"
1173                    . "    abbreviation.\n"
1174                    . "    Must be one of: 'leaf' or 'flat'.\n"
1175                    . $usage;
1176$options{'pvcs-dirs'} = $hits[0];
1177$options{'pvcs-dirs-flat'} = ($options{'pvcs-dirs'} =~ /flat/);
1178delete $options{'pvcs-dirs'};
1179
1180# and for verify
1181@hits = grep /^$options{verify}/i, ("none", "locks", "exists", "lockdates", "revs", "full");
1182@hits == 1 or die
1183                      "$0: $options{verify} invalid argument to --verify or ambiguous\n"
1184                    . "    abbreviation.\n"
1185                    . "    Must be one of: 'none', 'locks', 'exists', 'lockdates', 'revs',\n"
1186                    . "    or 'full'.\n"
1187                    . $usage;
1188$options{verify} = $hits[0];
1189$options{verify} =~ /^none|locks|exists$/ or die
1190                      "$0: --verify=$options{verify} unimplemented.\n"
1191                    . $usage;
1192
1193# and mode
1194@hits = grep /^$options{mode}/i, ("convert", "verify");
1195@hits == 1 or die
1196                      "$0: $options{mode} invalid argument to --mode or ambiguous abbreviation.\n"
1197                    . "    Must be 'convert' or 'verify'.\n"
1198                    . $usage;
1199$options{mode} = $hits[0];
1200
1201$options{'cvs-branch-labels'} or die
1202                      "$0: RCS Branch Labels unimplemented.\n"
1203                    . $usage;
1204
1205# export VCSID into th environment for ourselves and our children
1206$ENV{VCSID} = $options{vcsid};
1207
1208
1209
1210#
1211# Verify we have all the binary executables we need to run this script
1212#
1213# Allowed this feature to be disabled in case which is missing or we are
1214# running on a system which does not return error codes properly (e.g. WIN95)
1215#
1216#      -- i.e. I don't feel like grepping output yet. --
1217#
1218my @missing_binaries = ();
1219if ($options{'test-binaries'})
1220          {
1221          foreach (@bin_dependancies)
1222                    {
1223                    my $output = qx/which $_ 2>&1/;
1224                    print $output if $options{verbose} && $output;
1225                    if ($? || $output =~ /^no/)
1226                              {
1227                              push @missing_binaries, $_;
1228                              }
1229                    }
1230
1231          if (scalar @missing_binaries)
1232                    {
1233                    print STDERR "The following executables were not found in your PATH: "
1234                              . join ( " ", @missing_binaries )
1235                              . "\n"
1236                              . "You must correct this before continuing.\n";
1237                    exit 1;
1238                    }
1239          }
1240delete $options{'test-binaries'};
1241
1242
1243
1244#
1245# set up our base archive manipulation commands
1246#
1247
1248# set up our rcs_command mods
1249$rcs_base_command = "rcs";
1250$rcs_base_command .= " -x$options{'rcs-extension'}"
1251          if $options{'rcs-extension'};
1252
1253# set up our rcs_command mods
1254$ci_base_command = "ci";
1255$ci_base_command .= " -x$options{'rcs-extension'}"
1256          if $options{'rcs-extension'};
1257
1258
1259
1260#
1261# So our logs fill in a manner we can monitor with 'tail -f' fairly easily:
1262#
1263STDERR->autoflush (1);
1264STDOUT->autoflush (1);
1265
1266
1267
1268# Initialize the globals we use to keep track of recursion
1269if ($options{recurse})
1270          {
1271          $maxlevel = 10000;            # Arbitrary recursion limit
1272          }
1273else
1274          {
1275          $maxlevel = 1;
1276          }
1277delete $options{recurse};
1278
1279# So we can lock the directories behind us
1280$donefile_name = $options{'rcs-dirs-flat'} ? "" : "RCS/";
1281$errorfile_name = $donefile_name . "#conv.errors";
1282$donefile_name .= "#conv.done";
1283
1284
1285
1286#
1287# start the whole thing and drop the return code on exit
1288#
1289push @ARGV, "." unless (@ARGV);
1290while ($_ = shift)
1291          {
1292          # reset the recursion level (corresponds to directory depth)
1293          # level 0 is the first directory we enter...
1294          $curlevel = -1;
1295          my ($e, $w) = execdir($_);
1296          $errors += $e;
1297          $warnings += $w;
1298          }
1299
1300
1301
1302print STDERR "$0:  " . ($errors ? "Aborted" : "Done") . ".\n";
1303print STDERR "$0:  ";
1304print STDERR ($errors ? $errors : "No") . " error" . (($errors != 1) ? "s" : "");
1305print STDERR ", " . ($warnings ? $warnings : "no") . " warning" . (($warnings != 1) ? "s" : "")
1306                    if ($options{warnings});
1307print STDERR ".\n";
1308
1309
1310
1311#
1312# Woo-hoo!  We made it!
1313#
1314exit $errors;
1315