/contrib/cvs/contrib/pvcs2rcs.in

https://bitbucket.org/freebsd/freebsd-head/ · Autoconf · 1150 lines · 689 code · 162 blank · 299 comment · 102 complexity · acc28f60307d9787e26ef68d35dd6ea5 MD5 · raw file

  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. # FUNCTION:
  14. # To recursively walk through a PVCS archive directory tree (archives
  15. # located in VCS/ or vcs/ subdirectories) and convert them to RCS archives.
  16. # The RCS archive name is the PVCS workfile name with ",v" appended.
  17. #
  18. # SYNTAX:
  19. # pvcs_to_rcs.pl --help
  20. #
  21. # where -l indicates the operation is to be performed only in the current
  22. # directory (no recursion)
  23. #
  24. # EXAMPLE:
  25. # pvcs_to_rcs
  26. # Would walk through every VCS or vcs subdir starting at the current directory,
  27. # and produce corresponding RCS archives one level above the VCS or vcs subdir.
  28. # (VCS/../RCS/)
  29. #
  30. # NOTES:
  31. # * This script performs little error checking and logging
  32. # (i.e. USE AT YOUR OWN RISK)
  33. # * This script was last tested using ActiveState's port of Perl 5.005_02
  34. # (internalcut #507) under Win95, though it does compile under Perl-5.00404
  35. # for Solaris 2.4 run on a Solaris 2.6 system. The script crashed
  36. # occasionally under ActiveState's port of Perl 5.003_07 but this stopped
  37. # happening with the update so if you are having problems, try updating Perl.
  38. # Upgrading to cut #507 also seemed to coincide with a large speed
  39. # improvement, so try and keep up, hey? :) It was executed from MKS's
  40. # UNIX tools version 6.1 for Win32's sh. ALWAYS redirect your output to
  41. # a log!!!
  42. # * PVCS archives are left intact
  43. # * RCS archives are created in VCS/../RCS/ (or ./RCS using '-pflat')
  44. # * Branch labels in this script will be attached to the CVS magic
  45. # revision number. For branch a.b.c of a particular file, this means
  46. # the label will be attached to revision a.b.0.c of the converted
  47. # file. If you use the TrunkTip (1.*) label, be aware that it will convert
  48. # to RCS revision 0.1, which is useless to RCS and CVS. You'll probably
  49. # have to delete these.
  50. # * All revisions are saved with correct "metadata" (i.e. check-in date,
  51. # author, and log message). Any blank log message is replaced with
  52. # "no comment". This is because RCS does not allow non-interactive
  53. # check in of a new revision without a comment string.
  54. # * Revision numbers are incremented by 1 during the conversion (since
  55. # RCS does not allow revision 1.0).
  56. # * All converted branch numbers are even (the CVS paradigm)
  57. # * Version labels are assigned to the appropriate (incremented) revision
  58. # numbers. PVCS allows spaces and periods in version labels while RCS
  59. # does not. A global search and replace converts " " and "." to "_"
  60. # There may be other cases that ought to be added.
  61. # * Any working (checked-out) copies of PVCS archives
  62. # within the VCS/../ or vcs/../ (or possibly ./ with '-pflat')
  63. # will be deleted (or overwritten) depending on your mode of
  64. # operation since the current ./ is used in the checkout of each revision.
  65. # I suppose if development continues these files could be redirected to
  66. # temp space rather than ./ .
  67. # * Locks on PVCS archives should be removed (or the workfiles should be
  68. # checked-in) prior to conversion, although the script will blaze through
  69. # the archive nonetheless (But you would lose any checked out revision(s))
  70. # * The -kb option is added to the RCS archive for workfiles with the following
  71. # extensions: .bin .out .btl .rom .a07 .lib .exe .tco .obj .t8u .c8u .o .lku
  72. # .a and a few others. The %bin_ext variable holds these values in regexp
  73. # form.
  74. # * the --force-binary option can be used to convert binary files which don't
  75. # have proper extensions, but I'd *probably* edit the %bin_ext variable.
  76. # * This script will abort occasionally with the error "invalid revision
  77. # number". This is known to happen when a revision comment has
  78. # /^\s*Rev/ (Perl regexp notation) in it. Fix the comment and start over.
  79. # (The directory locks and existance checking make this a fairly quick
  80. # process.)
  81. # * This script writes lockfiles in the RCS/ directories. It will also not
  82. # convert an archive if it finds the RCS Archive existant in the RCS/
  83. # directory. This enables the conversion to quickly pick up where it left
  84. # off after errors or interrupts occur. If you interrupt the script make
  85. # sure you delete the last RCS Archive File which was being written.
  86. # If you recieve the "Invalid revision number" error, then the RCS archive
  87. # file for that particular PVCS file will not have been created yet.
  88. # * This script will not create lockfiles when processing single
  89. # filenames passed into the script, for hopefully obvious reasons.
  90. # (lockfiles lock directories - DRP)
  91. # * Log the output to a file. That makes it real easy to grep for errors
  92. # later. (grep for "^[ \t]*(rcs|ci):" and be aware I might have missed
  93. # a few cases (get? vcs?) !!!) *** Also note that this script will
  94. # exibit some harmless RCS errors. Namely, it will attempt to lock
  95. # branches which haven't been created yet. ***
  96. # * I tried to keep the error and warning info up to date, but it seems
  97. # to mean very little. This script almost always exits with a warning
  98. # or an error that didn't seem to cause any harm. I didn't trace it
  99. # and our imported source checks out and builds...
  100. # It is probably happening when trying to convert empty directories
  101. # or read files (possibly checked out workfiles ) which are not
  102. # pvcs_archives.
  103. # * You must use the -pflat option when processing single filenames
  104. # passed as arguments to the script. This is probably a bug.
  105. # * questions, comments, additions can be sent to info-cvs@nongnu.org
  106. #########################################################################
  107. #
  108. # USER Configurables
  109. #
  110. # %bin_ext should be editable from the command line.
  111. #
  112. # NOTE: Each possible binary extension is listed as a Perl regexp
  113. #
  114. # The value associated with each regexp key is used to print a log
  115. # message when a binary file is found.
  116. my %bin_ext =
  117. (
  118. '\.(?i)bin$' => "Binary",
  119. '\.(?i)out$' => "Default Compiler Output",
  120. '\.(?i)btl$' => "",
  121. '\.(?i)rom$' => "",
  122. '\.(?i)a07$' => "",
  123. '\.(?i)lib$' => "DOS/Wintel/Netware Compiler Library",
  124. '\.(?i)lif$' => "Netware Binary File",
  125. '\.(?i)exe$' => "DOS/Wintel Executable",
  126. '\.(?i)tco$' => "",
  127. '\.(?i)obj$' => "DOS/Wintel Compiler Object",
  128. '\.(?i)res$' => "DOS/Wintel Resource File",
  129. '\.(?i)ico$' => "DOS/Wintel Icon File",
  130. '\.(?i)nlm$' => "Netware Loadable Module",
  131. '\.(?i)t8u$' => "",
  132. '\.(?i)c8u$' => "",
  133. '\.(?i)lku$' => "",
  134. '\.(?i)(bmp|gif|jpg|jpeg|jfif|tif|tiff|xbm)$' => "Image",
  135. '\.(?i)dll$' => "DOS/Wintel Dynamically Linked Library",
  136. '\.o$' => "UNIX Compiler Object",
  137. '\.a$' => "UNIX Compiler Library",
  138. '\.so(\.\d+\.\d+)?$' => "UNIX Shared Library"
  139. );
  140. # The binaries this script is dependant on:
  141. my @bin_dependancies = ("vcs", "vlog", "rcs", "ci");
  142. # Where we should put temporary files
  143. my $tmpdir = $ENV{TMPDIR} ? $ENV{TMPDIR} : "/var/tmp";
  144. # We use these...
  145. use strict;
  146. use Cwd;
  147. use File::Path;
  148. use IO::File;
  149. use Getopt::Long;
  150. $Getopt::Long::bundling = 1;
  151. # $Getopt::Long::ignorecase = 0;
  152. my $usage = "\
  153. usage: $0 -h
  154. $0 [-lt] [-i vcsid] [-r flat|leaf] [-p flat|leaf] [-x rcs_extension]
  155. [-v none|locks|exists] [options] [path...]
  156. ";
  157. my $help = "\
  158. $usage
  159. ---------------------------- -----------------------------------
  160. -h | --Help Print this text
  161. General Settings
  162. ---------------------------- -----------------------------------
  163. --Recurse Recurse through directories
  164. (default)
  165. -l | --NORecurse Process only .
  166. --Errorfiles Save a count of conversion errors
  167. in the RCS archive directory
  168. (default) (unimplemented)
  169. --NOErrorfiles Don't save a count of conversion
  170. errors (unimplemented)
  171. ( -m | --Mode ) Convert Convert PVCS files to RCS files
  172. (default)
  173. ( -m | --Mode ) Verify Perform verification ONLY (unimplemented)
  174. ( -v | --VERIfy ) None Always replace existing RCS files
  175. ( -v | --VERIfy ) LOCKS Same as exists unless a #conv.done
  176. file exists in the RCS directory.
  177. In that case, only the #conv.done
  178. file's existance is verified for
  179. that directory. (default)
  180. ( -v | --VERIfy ) Exists Don't replace existing RCS files
  181. ( -v | --VERIfy ) LOCKDates Verify that an existing RCS file's
  182. last modification date is older
  183. than that of the lockfile
  184. (unimplemented)
  185. ( -v | --VERIfy ) Revs Verify that the PVCS archive files
  186. and RCS archive file contain the
  187. same number of corresponding
  188. revisions. Add only new revisions
  189. to the RCS file. (unimplemented)
  190. ( -v | --VERIfy ) Full Perform --verify=Revs and confirm
  191. that the text of the revisions is
  192. identical. Add only new revisions
  193. unless an error is found. Then
  194. erase the RCS archive and recreate
  195. it. (unimplemented)
  196. -t | --Test-binaries Use 'which' to check \$PATH for
  197. the binaries required by this
  198. script (default)
  199. --NOTest-binaries Don't check for binaries
  200. --VERBose Enable verbose output
  201. --NOVerbose Disable verbose output (default)
  202. -w | --Warnings Print warning messages (default)
  203. --NOWarnings Don't print warning messages
  204. RCS Settings
  205. ---------------------------- -----------------------------------
  206. ( -r | --RCS-Dirs ) leaf RCS files stored in ./RCS (default)
  207. ( -r | --RCS-Dirs ) flat RCS files stored in .
  208. (unimplemented)
  209. ( -x | --RCS-Extension ) Set RCS file extension
  210. (default = ',v')
  211. --Force-binary Pass '-kb' to 'rcs -i' regardless of
  212. the file extension
  213. --NOForce-binary Only use '-kb' when the file has
  214. a binary extension (default)
  215. --Cvs-branch-labels Use CVS magic branch revision
  216. numbers when attaching branch
  217. labels (default)
  218. --NOCvs-branch-labels Attach branch labels to RCS branch
  219. revision numbers (unimplemented)
  220. PVCS Settings
  221. ---------------------------- -----------------------------------
  222. ( -p | --Pvcs-dirs ) leaf PVCS files expected in ./VCS
  223. (default)
  224. ( -p | --Pvcs-dirs ) flat PVCS files expected in .
  225. ( -i | --VCsid ) vcsid Use vcsid instead of \$VCSID
  226. --------------------------------------------------------------------------
  227. The optional path argument should contain the name of a file or directory
  228. to convert. If not given, it will default to '.'.
  229. --------------------------------------------------------------------------
  230. ";
  231. #
  232. # Initialize globals
  233. #
  234. my ($errors, $warnings) = (0, 0);
  235. my ($curlevel, $maxlevel);
  236. my ($rcs_base_command, $ci_base_command);
  237. my ($donefile_name, $errorfile_name);
  238. # set up the default options
  239. my %options = (
  240. recurse => 1,
  241. mode => "convert",
  242. errorfiles => 1,
  243. 'rcs-dirs' => "leaf",
  244. 'rcs-extension' => ",v",
  245. 'force-binary' => 0,
  246. 'cvs-branch-labels' => 1,
  247. 'pvcs-dirs' => "leaf",
  248. verify => "locks",
  249. 'test-binaries' => 1,
  250. vcsid => $ENV{VCSID} || "",
  251. verbose => 0,
  252. debug => 0,
  253. warnings => 1
  254. );
  255. # This is untested except under Solaris 2.4 or 2.6 and
  256. # may not be portable
  257. #
  258. # I think the readline lib or some such has an interface
  259. # which may enable this now. The perl installer sure looks
  260. # like it's testing this kind of thing, anyhow.
  261. sub hit_any_key
  262. {
  263. STDOUT->autoflush;
  264. system "stty", "-icanon", "min", "1";
  265. print "Hit any key to continue...";
  266. getc;
  267. system "stty", "icanon", "min", "0";
  268. STDOUT->autoflush (0);
  269. print "\nI always wondered where that key was...\n";
  270. }
  271. # print the usage
  272. sub print_usage
  273. {
  274. my $fh = shift;
  275. unless (ref $fh)
  276. {
  277. my $fdn = $fh ? $fh : "STDERR";
  278. $fh = new IO::File;
  279. $fh->fdopen ($fdn, "w");
  280. }
  281. $fh->print ($usage);
  282. }
  283. # print the help
  284. sub print_help
  285. {
  286. my $fh = shift;
  287. unless (ref $fh)
  288. {
  289. my $fdn = $fh ? $fh : "STDOUT";
  290. $fh = new IO::File;
  291. $fh->fdopen ($fdn, "w");
  292. }
  293. $fh->print ($help);
  294. }
  295. # print the help and exit $_[0] || 0
  296. sub exit_help
  297. {
  298. print_help;
  299. exit shift || 0;
  300. }
  301. sub error_count
  302. {
  303. my $type = shift or die "$0: error - error_count usage: error_count type [, ref] [, LIST]\n";
  304. my $error_count_ref;
  305. my $outstring;
  306. if (ref ($_[0]) && ref ($_[0]) == "SCALAR")
  307. {
  308. $error_count_ref = shift;
  309. }
  310. else
  311. {
  312. $error_count_ref = \$errors;
  313. }
  314. $$error_count_ref++;
  315. push @_, "something wrong.\n" unless ( @_ > 0 );
  316. $outstring = sprintf "$0: $type - " . join ("", @_);
  317. $outstring .= sprintf " - $!\n" unless ($outstring =~ /\n$/);
  318. print STDERR $outstring;
  319. if ($options{errorfiles})
  320. {
  321. my $fh = new IO::File ">>$errorfile_name" or new IO::File ">$errorfile_name";
  322. if ($fh)
  323. {
  324. $fh->print ($$error_count_ref . "\n");
  325. $fh->print ($outstring);
  326. $fh->close;
  327. }
  328. else
  329. {
  330. my $cd = cwd;
  331. print STDERR "$0: error - failed to open errorfile $cd/$errorfile_name - $!\n"
  332. if ($options{debug});
  333. }
  334. }
  335. return $$error_count_ref;
  336. }
  337. # the main procedure that is run once in each directory
  338. sub execdir
  339. {
  340. my $dir = shift;
  341. my ($errors, $warnings) = (0, 0); # We return these error counters
  342. my $old_dir = cwd;
  343. local ($_, @_);
  344. my $i; # Generic counter
  345. my ($pvcsarchive, $workfile, $rcsarchive); # .??v, checked out file, and ,v files,
  346. # respectively
  347. my ($rev_count, $first_vl, $last_vl, $description,
  348. $rev_index, @rev_num, %checked_in, %author,
  349. $relative_comment_index, @comment_string,
  350. %comment);
  351. my ($num_version_labels, $label_index, @label_revision, $label,
  352. @new_label, $rcs_rev);
  353. my ($revision, %rcs_rev_num);
  354. my ($get_output, $rcs_output, $ci_output, $mv_output);
  355. my ($ci_command, $rcs_command, $wtr);
  356. my @hits;
  357. my ($num_fields);
  358. my $skipdirlock; # if true, don't write conv.out
  359. # used only for single file operations
  360. # at the moment
  361. my $cd;
  362. my @filenames;
  363. # We may have recieved a single file name to process...
  364. if ( -d $dir )
  365. {
  366. # change into the directory to be processed
  367. # open the current directory for listing
  368. # initialize the list of filenames
  369. # and set filenames equal to directory listing
  370. unless ( ( chdir $dir ) and ( opendir CURDIR, "." ) and ( @filenames = readdir CURDIR ) )
  371. {
  372. $cd = cwd;
  373. error_count 'error', \$errors, "skipping directory $dir from $cd";
  374. chdir $old_dir or die "Failed to restore original directory ($old_dir): ", $!, ", stopped";
  375. return ($errors, $warnings);
  376. }
  377. # clean up by closing the directory
  378. closedir(CURDIR);
  379. }
  380. elsif ( -f $dir ) # we recieved a single file
  381. {
  382. push @filenames, $dir;
  383. $skipdirlock = 1;
  384. }
  385. else
  386. {
  387. $cd = cwd;
  388. error_count 'error', \$errors, "no such directory/file $dir from $cd\n";
  389. # chdir $old_dir or die "Failed to restore original directory ($old_dir): ", $!, ", stopped";
  390. return ($errors, $warnings);
  391. }
  392. # save the current directory
  393. $cd = cwd;
  394. # increment the global $curlevel variable
  395. $curlevel = $curlevel +1;
  396. # initialize a list for any subdirectories and any files
  397. # we need to process
  398. my $vcsdir = "";
  399. my (@subdirs, $fn, $file, @files, @pvcsarchives);
  400. # print "$cd: " . join (", ", @filenames) . "\n";
  401. # hit_any_key;
  402. (@files, @pvcsarchives) = ( (), () );
  403. # begin a for loop to execute on each filename in the list @filename
  404. foreach $fn (@filenames)
  405. {
  406. # if the file is a directory...
  407. if (-d $fn)
  408. {
  409. # then if we are not expecting a flat arrangement of pvcs files
  410. # and we found a vcs directory add its files to @pvcsarchives
  411. if (!$options{'pvcs-dirs-flat'} and $fn =~ /^vcs$/i)
  412. {
  413. if ($options{verify} =~ /^locks$/ ) {
  414. if ( -f $donefile_name ) {
  415. print "Verified existence of lockfile $cd/$donefile_name."
  416. . ( ($options{mode} =~ /^convert$/) ? " Skipping directory." : "" )
  417. . "\n" if ($options{verbose});
  418. next;
  419. } elsif ( $options{mode} =~ /^verify$/ ) {
  420. print "No lockfile found for $cd .\n";
  421. next;
  422. }
  423. }
  424. # else add the files in the vcs dir to our list of files to process
  425. error_count 'warning', \$warnings, "Found two vcs dirs in directory $cd.\n"
  426. if ($vcsdir and $options{warnings});
  427. $vcsdir = $fn;
  428. unless ( ( opendir VCSDIR, $vcsdir ) and ( @files = readdir VCSDIR ) )
  429. {
  430. error_count 'error', \$errors, "skipping directory &cd/$fn";
  431. next;
  432. }
  433. closedir VCSDIR;
  434. # and so we don't need to worry about where these
  435. # files came from later...
  436. foreach $file (@files)
  437. {
  438. push @pvcsarchives, "$vcsdir/$file" if (-f "$vcsdir/$file");
  439. }
  440. # don't want recursion here...
  441. @pvcsarchives = grep !/^\.\.?$/, @pvcsarchives;
  442. }
  443. elsif ($fn !~ /^\.\.?$/)
  444. {
  445. next if (!$options{'rcs-dirs-flat'} and $fn =~ /^rcs$/i);
  446. # include it in @subdir if it's not a parent directory
  447. push(@subdirs,$fn);
  448. }
  449. }
  450. # else if we are processing a flat arrangement of pvcs files...
  451. elsif ($options{'pvcs-dirs-flat'} and -f $fn)
  452. {
  453. if ($options{verify} =~ /^locks$/) {
  454. if ( -f $donefile_name) {
  455. print "Found lockfile $cd/$donefile_name."
  456. . ( ($options{mode} =~ /^convert$/) ? " Skipping directory." : "" )
  457. . "\n" if ($options{verbose});
  458. last;
  459. } elsif ($options{mode} =~ /^verify$/) {
  460. print "No lockfile found for $cd .\n";
  461. last;
  462. }
  463. }
  464. # else add this to the list of files to process
  465. push (@pvcsarchives, $fn);
  466. }
  467. }
  468. # print "pvcsarchives: " . join (", ", @pvcsarchives) . "\n";
  469. # print "subdirs: " . join (", ", @subdirs) . "\n";
  470. # hit_any_key;
  471. # for loop of subdirs
  472. foreach (@subdirs)
  473. {
  474. # run execdir on each sub dir
  475. if ($maxlevel >= $curlevel)
  476. {
  477. my ($e, $w) = execdir ($_);
  478. $errors += $e;
  479. $warnings += $w;
  480. }
  481. }
  482. # Print output header for each directory
  483. print("Directory: $cd\n");
  484. # the @files variable should already contain the list of files
  485. # we should attempt to process
  486. if ( @pvcsarchives && ( $options{mode} =~ /^convert$/ ) )
  487. {
  488. # create an RCS directory in parent to store RCS files in
  489. if ( !( $options{'rcs-dirs-flat'} or (-d "RCS") or mkpath ( "RCS" ) ) )
  490. {
  491. error_count 'error', \$errors, "failed to make directory $cd/RCS - skipping directory $cd";
  492. @pvcsarchives = ();
  493. # after all, we have nowhere to put them...
  494. }
  495. }
  496. # begin a for loop to execute on each filename in the list @files
  497. foreach $pvcsarchive (@pvcsarchives)
  498. {
  499. my $got_workfile = 0;
  500. my $got_version_labels = 0;
  501. my $got_description = 0;
  502. my $got_rev_count = 0;
  503. my $abs_file = $cd . "/" . $pvcsarchive;
  504. print("Verifying $abs_file...\n") if ($options{verbose});
  505. print "vlog $pvcsarchive\n";
  506. my $vlog_output = `vlog $pvcsarchive`;
  507. $_ = $vlog_output;
  508. # Split the vcs status output into individual lines
  509. my @vlog_strings = split /\n/;
  510. my $num_vlog_strings = @vlog_strings;
  511. $_ = $vlog_strings[0];
  512. if ( /^\s*$/ || /^vlog: warning/ )
  513. {
  514. error_count 'warning', \$warnings, "$abs_file is NOT a valid PVCS archive!!!\n";
  515. next;
  516. }
  517. my $num;
  518. # Collect all vlog output into appropriate variables
  519. #
  520. # This will ignore at the very least the /^\s*Archive:\s*/ field
  521. # and maybe more. This should not be a problem.
  522. for ( $num = 0; $num < $num_vlog_strings; $num++ )
  523. {
  524. # print("$vlog_strings[$num]\n");
  525. $_ = $vlog_strings[$num];
  526. if( ( /^Workfile:\s*/ ) && (!$got_workfile ) )
  527. {
  528. my $num_fields;
  529. $got_workfile = 1;
  530. # get the string to the right of the above search (with any path stripped)
  531. $workfile = $';
  532. $_ = $workfile;
  533. $num_fields = split /[\/\\]/;
  534. if ( $num_fields > 1 )
  535. {
  536. $workfile = $_[$num_fields - 1 ];
  537. }
  538. $rcsarchive = $options{'rcs-dirs-flat'} ? "" : "RCS/";
  539. $rcsarchive .= $workfile;
  540. $rcsarchive .= $options{'rcs-extension'} if ($options{'rcs-extension'});
  541. print "Workfile is $workfile\n" if ($options{debug});
  542. }
  543. elsif ( ( /^Rev count:\s*/ ) && (!$got_rev_count ) )
  544. {
  545. $got_rev_count = 1;
  546. # get the string to the right of the above search
  547. $rev_count = $';
  548. print "Revision count is $rev_count\n";
  549. }
  550. elsif ( ( /^Version labels:\s*/ ) && (!$got_version_labels ) )
  551. {
  552. $got_version_labels = 1;
  553. $first_vl = $num+1;
  554. print "Version labels start at $first_vl\n" if ($options{debug});
  555. }
  556. elsif ( ( /^Description:\s*/ ) && (!$got_description ) )
  557. {
  558. $got_description = 1;
  559. $description = "\"" . $vlog_strings[$num+1] . "\"";
  560. print "Description is $description\n" if ($options{debug});
  561. $last_vl = $num - 1;
  562. }
  563. elsif ( /^Rev\s+/ ) # get all the revision information at once
  564. {
  565. $rev_index = 0;
  566. @rev_num = ();
  567. while ( $rev_index < $rev_count )
  568. {
  569. $_ = $vlog_strings[$num];
  570. /^\s*Rev\s+(\d+\.(\d+\.\d+\.)*\d+)$/;
  571. $rev_num[$rev_index] = $1;
  572. print "Found revision: $rev_num[$rev_index]\n" if ($options{debug});
  573. die "Not a valid revision ($rev_num[$rev_index]).\n"
  574. if ($rev_num[$rev_index] !~ /^(\d+\.)(\d+\.\d+\.)*\d+$/);
  575. $_ = $vlog_strings[$num+1];
  576. /^\s*Locked\s*/ and $num++;
  577. $_ = $vlog_strings[$num+1];
  578. /^\s*Checked in:\s*/;
  579. $checked_in{$rev_num[$rev_index]} = "\"" . $' . "\"";
  580. print "Checked in: $checked_in{$rev_num[$rev_index]}\n" if ($options{debug});
  581. $_ = $vlog_strings[$num+3];
  582. /^\s*Author id:\s*/;
  583. split;
  584. $author{$rev_num[$rev_index]} = "\"" . $_[2] . "\"";
  585. print "Author: $author{$rev_num[$rev_index]}\n" if ($options{debug});
  586. my @branches = ();
  587. $_ = $vlog_strings[$num+1];
  588. if (/^\s*Branches:\s*/)
  589. {
  590. $num++;
  591. @branches = split /\s+/, $';
  592. }
  593. $relative_comment_index = 0;
  594. @comment_string = ();
  595. while( ( ( $num + 4 + $relative_comment_index ) < @vlog_strings)
  596. && ( $vlog_strings[$num+4+$relative_comment_index]
  597. !~ /^\s*Rev\s+(\d+\.(\d+\.\d+\.)*\d+)$/ ) )
  598. {
  599. # We need the \n added for multi-line comments. There is no effect for
  600. # single-line comments since RCS inserts the \n if it doesn't exist already
  601. # print "Found commment line: $vlog_strings[$num+4+$relative_comment_index]\n"
  602. # if ($options{debug});
  603. push @comment_string, $vlog_strings[$num+4+$relative_comment_index], "\n";
  604. $relative_comment_index += 1;
  605. }
  606. # print "Popped from comment: " . join ("", splice (@comment_string, -2))
  607. # . "\n"
  608. # if ($options{debug});
  609. # Pop the "-+" or "=+" line from the comment
  610. while ( (pop @comment_string) !~ /^-{35}|={35}$/ )
  611. {}
  612. $comment{$rev_num[$rev_index]} = join "", @comment_string;
  613. $num += ( 4 + $relative_comment_index );
  614. print "Got comment for $rev_num[$rev_index]\n" if ($options{debug});
  615. print "comment string: $comment{$rev_num[$rev_index]}\n" if ($options{debug});
  616. $rev_index += 1;
  617. } # while ( $rev_index < $rev_count )
  618. $num -= 1; #although there should be nothing left for this to matter
  619. } # Get Rev information
  620. } # for ($num = 0; $num < $num_vlog_strings; $num++)
  621. # hit_any_key if ($options{debug});
  622. # Create RCS revision numbers corresponding to PVCS version numbers
  623. foreach $revision (@rev_num)
  624. {
  625. $rcs_rev_num{ $revision } = &pvcs_to_rcs_rev_number( $revision );
  626. print"PVCS revision is $revision; RCS revision is $rcs_rev_num{ $revision }\n"
  627. if ($options{debug});
  628. }
  629. # Sort the revision numbers - PVCS and RCS store them in different orders
  630. # Clear @_ so we don't pass anything in by accident...
  631. @_ = ();
  632. @rev_num = sort revisions @rev_num;
  633. print "Sorted rev_nums:\n" . join ("\n", @rev_num) . "\n" if ($options{debug});
  634. # hit_any_key;
  635. # Loop through each version label, checking for need to relabel ' ' with '_'.
  636. $num_version_labels = $last_vl - $first_vl + 1;
  637. print "Version label count is $num_version_labels\n";
  638. for( $i = $first_vl; $i <= $last_vl; $i += 1 )
  639. {
  640. # print("$vlog_strings[$i]\n");
  641. $label_index = $i - $first_vl;
  642. $_=$vlog_strings[$i];
  643. print "Starting with string '$_'\n" if ($options{debug});
  644. split /\"/;
  645. $label = $_[1];
  646. print "Got label '$label'\n" if ($options{debug});
  647. split /\s+/, $_[2];
  648. $label_revision[$label_index] = $_[2];
  649. print "Original label is $label_revision[$label_index]\n" if ($options{debug});
  650. # Create RCS revision numbers corresponding to PVCS version numbers by
  651. # adding 1 to the revision number (# after last .)
  652. $label_revision[ $label_index ] = pvcs_to_rcs_rev_number( $label_revision [ $label_index ] );
  653. # replace ' ' with '_', if needed
  654. $_=$label;
  655. $new_label[$label_index] = $label;
  656. $new_label[$label_index] =~ s/ /_/g;
  657. $new_label[$label_index] =~ s/\./_/g;
  658. $new_label[$label_index] = "\"" . $new_label[$label_index] . "\"";
  659. print"Label $new_label[$label_index] is for revision $label_revision[$label_index]\n" if ($options{debug});
  660. }
  661. ##########
  662. #
  663. # See if the RCS archive is up to date with the PVCS archive
  664. #
  665. ##########
  666. if ($options{verify} =~ /^locks|exists$/ and -f $rcsarchive)
  667. {
  668. print "Verified existence of $cd/$rcsarchive."
  669. . ( ($options{mode} =~ /^convert$/) ? " Skipping." : "" )
  670. . "\n" if ($options{verbose});
  671. next;
  672. }
  673. # Create RCS archive and check in all revisions, then label.
  674. my $first_time = 1;
  675. foreach $revision (@rev_num)
  676. {
  677. # print "get -p$revision $pvcsarchive >$workfile\n";
  678. print "get -r$revision $pvcsarchive\n";
  679. # $vcs_output = `vcs -u -r$revision $pvcsarchive`;
  680. # $get_output = `get -p$revision $pvcsarchive >$workfile`;
  681. $get_output = `get -r$revision $pvcsarchive`;
  682. # if this is the first time, delete the rcs archive if it exists
  683. # need for $options{verify} == none
  684. unlink $rcsarchive if ($first_time and $options{verify} =~ /^none$/ and -f $rcsarchive);
  685. # Also check here whether this file ought to be "binary"
  686. if ( $first_time )
  687. {
  688. $rcs_command = "$rcs_base_command -i";
  689. if ( ( @hits = grep { $workfile =~ /$_/ } keys %bin_ext ) || $options{'force-binary'} )
  690. {
  691. $rcs_command .= " -kb";
  692. $workfile =~ /$hits[0]/ if (@hits);
  693. print "Binary attribute -kb added ("
  694. . (@hits ? "file type is '$bin_ext{$hits[0]}' for extension '$&'" : "forced")
  695. . ")\n";
  696. }
  697. $first_time and $ci_command .= " -t-$description";
  698. $rcs_command .= " $workfile";
  699. # print and execute the rcs archive initialization command
  700. print "$rcs_command\n";
  701. $wtr = new IO::File "|$rcs_command";
  702. $wtr->print ($description);
  703. $wtr->print ("\n") unless ($description =~ /\n$/s);
  704. $wtr->print (".\n");
  705. $wtr->close;
  706. # $rcs_output = `$rcs_base_command -i -kb $workfile`;
  707. }
  708. # if this isn't the first time, we need to lock the rcs branch
  709. #
  710. # This is a little messy, but it works. Some extra locking is attempted.
  711. # (This happens the first time a branch is used, at the least)
  712. my $branch = "";
  713. my @branch;
  714. @branch = split /\./, $rcs_rev_num{$revision};
  715. pop @branch;
  716. $branch = join ".", @branch;
  717. $rcs_output = `$rcs_base_command -l$branch $workfile` if (!$first_time);
  718. # If an empty comment is specified, RCS will not check in the file;
  719. # check for this case. (but an empty -t- description is fine - go figure!)
  720. # Since RCS will pause and ask for a comment if one is not given,
  721. # substitute a dummy comment "no comment".
  722. $comment{$revision} =~ /^\s*$/ and $comment{$revision} = "no comment\n";
  723. $ci_command = $ci_base_command;
  724. $ci_command .= " -f -r$rcs_rev_num{$revision} -d$checked_in{$revision}"
  725. . " -w$author{$revision}";
  726. $ci_command .= " $workfile";
  727. # print and execute the ci command
  728. print "$ci_command\n";
  729. $wtr = new IO::File "|$ci_command";
  730. $wtr->print ($comment{$revision});
  731. $wtr->print ("\n") unless ($comment{$revision} =~ /\n$/s);
  732. $wtr->print (".\n");
  733. $wtr->close;
  734. # $ci_output = `$ci_command`;
  735. # $ci_output = `cat $tmpdir/ci.out`;
  736. $first_time = 0 if ($first_time);
  737. } # foreach revision
  738. # Attach version labels
  739. for( $i = $num_version_labels - 1; $i >= 0; $i -= 1 )
  740. {
  741. # print "rcs -x,v -n$new_label[$i]:$label_revision[$i] $workfile\n";
  742. $rcs_output = `$rcs_base_command -n$new_label[$i]:$label_revision[$i] $workfile`;
  743. print "Version label $new_label[$i] added to revision $label_revision[$i]\n";
  744. } # foreach label
  745. # hit_any_key;
  746. } # foreach pvcs archive file
  747. # We processed a vcs directory, so if there were any files, lock it.
  748. # We are guaranteed to have made the attempt at
  749. #
  750. # $skipdirlock gets set if a single file name was passed to this function to enable
  751. # a '$0 *' operation...
  752. if ( @pvcsarchives && !$skipdirlock)
  753. {
  754. my $fh = new IO::File ">>$donefile_name" or new IO::File ">$donefile_name";
  755. if ($fh)
  756. {
  757. $fh->close;
  758. }
  759. else
  760. {
  761. error_count 'error', \$errors, "couldn't create lockfile $cd/$donefile_name";
  762. }
  763. }
  764. $curlevel = $curlevel - 1;
  765. chdir $old_dir or die "Failed to restore original directory ($old_dir): ", $!, ", stopped";
  766. return ($errors, $warnings);
  767. }
  768. #
  769. # This function effectively does a cmp between two revision numbers
  770. # It is intended to be passed into Perl's sort routine.
  771. #
  772. # the pvcs_out is not implemented well. It should probably be
  773. # returnning $b[0] <=> $a[0] rather than $a[0] <=> $b[0]
  774. #
  775. # The @_ argument implementation was going to be used for revision
  776. # comparison as an aid to remove the /^\sRev/ in revision comment
  777. # error. The effort was fruitless at the time.
  778. sub revisions
  779. {
  780. my @a = split /\./, (defined $a) ? $a : shift;
  781. my @b = split /\./, (defined $b) ? $b : shift;
  782. my $function = @_ ? shift : 'rcs_in';
  783. my ($i, $ret_val);
  784. die "Not enough arguments to revisions : a = ", join (".", @a),
  785. "; b = ", join (".", @b), ", stopped"
  786. unless (@a and @b);
  787. for ($i = 0; $i < scalar( @a ) && $i < scalar( @b ); $i++)
  788. {
  789. $a[$i] == $b[$i] or return ($a[$i] <=> $b[$i]);
  790. }
  791. return 0 if (scalar (@a) == scalar (@b));
  792. if ($function eq 'rcs_in')
  793. {
  794. return (($i == @b) || -1);
  795. }
  796. elsif ($function eq 'pvcs_out')
  797. {
  798. return (($i == @a) || -1);
  799. }
  800. else
  801. {
  802. die "error - Invalid function type passed to revisions ($function)", ", stopped";
  803. }
  804. }
  805. sub pvcs_to_rcs_rev_number
  806. {
  807. my($input, $num_fields, @rev_string, $return_rev_num, $i);
  808. $input = $_[0];
  809. $_ = $input;
  810. $num_fields = split /\./;
  811. @rev_string = @_;
  812. # @rev_string[$num_fields-1] += 1;
  813. for( $i = 1; $i < $num_fields; $i += 1 )
  814. {
  815. if ( $i % 2 )
  816. {
  817. # DRP: 10/1
  818. # RCS does not allow revision zero
  819. $rev_string[ $i ] += 1;
  820. }
  821. elsif ( $i )
  822. {
  823. # DRP: 10/1
  824. # Branches must have even references for compatibility
  825. # with CVS's magic branch numbers.
  826. # (Indexes 2, 4, 6...)
  827. $rev_string[ $i ] *= 2;
  828. }
  829. }
  830. # If this is a branch revision # (PVCS: a.b.c.*) then we want the CVS
  831. # revision # instead. It's okay to do this conversion here since we
  832. # never commit to branches. We'll only get a PVCS revision # in that
  833. # form when looking through the revision labels.
  834. if ($input =~ /\*$/)
  835. {
  836. pop @rev_string;
  837. push @rev_string, splice (@rev_string, -1, 1, "0");
  838. }
  839. $return_rev_num = join ".", @rev_string;
  840. return $return_rev_num;
  841. }
  842. ###
  843. ###
  844. ###
  845. ###
  846. ###
  847. ### MAIN program: checks to see if there are command line parameters
  848. ###
  849. ###
  850. ###
  851. ###
  852. ###
  853. # and read the options
  854. die $usage unless GetOptions (\%options, "h|help" => \&exit_help,
  855. "recurse!", "mode|m=s", "errorfiles!", "l", "rcs-dirs|rcs-directories|r=s",
  856. "pvcs-dirs|pvcs-directories|p=s", "test-binaries|t!",
  857. "rcs-extension=s", "verify|v=s", "vcsid|i=s", "verbose!", "debug!",
  858. "force-binary!", "cvs-branch-labels!", "warnings|w!");
  859. #
  860. # Special processing for -l !^#%$^@#$%#$
  861. #
  862. # At the moment, -l overrides --recurse, regardless of the order the
  863. # options were passed in
  864. #
  865. $options{recurse} = 0 if defined $options{l};
  866. delete $options{l};
  867. # Make sure we got acceptable values for rcs-dirs and pvcs-dirs
  868. my @hits = grep /^$options{'rcs-dirs'}/i, ("leaf", "flat");
  869. @hits == 1 or die
  870. "$0: $options{'rcs-dirs'} invalid argument to --rcs-dirs or ambiguous\n"
  871. . " abbreviation.\n"
  872. . " Must be one of: 'leaf' or 'flat'.\n"
  873. . $usage;
  874. $options{'rcs-dirs'} = $hits[0];
  875. $options{'rcs-dirs-flat'} = ($options{'rcs-dirs'} =~ /flat/);
  876. delete $options{'rcs-dirs'};
  877. @hits = grep /^$options{'pvcs-dirs'}/i, ("leaf", "flat");
  878. @hits == 1 or die
  879. "$0: $options{'pvcs-dirs'} invalid argument to --pvcs-dirs or ambiguous\n"
  880. . " abbreviation.\n"
  881. . " Must be one of: 'leaf' or 'flat'.\n"
  882. . $usage;
  883. $options{'pvcs-dirs'} = $hits[0];
  884. $options{'pvcs-dirs-flat'} = ($options{'pvcs-dirs'} =~ /flat/);
  885. delete $options{'pvcs-dirs'};
  886. # and for verify
  887. @hits = grep /^$options{verify}/i, ("none", "locks", "exists", "lockdates", "revs", "full");
  888. @hits == 1 or die
  889. "$0: $options{verify} invalid argument to --verify or ambiguous\n"
  890. . " abbreviation.\n"
  891. . " Must be one of: 'none', 'locks', 'exists', 'lockdates', 'revs',\n"
  892. . " or 'full'.\n"
  893. . $usage;
  894. $options{verify} = $hits[0];
  895. $options{verify} =~ /^none|locks|exists$/ or die
  896. "$0: --verify=$options{verify} unimplemented.\n"
  897. . $usage;
  898. # and mode
  899. @hits = grep /^$options{mode}/i, ("convert", "verify");
  900. @hits == 1 or die
  901. "$0: $options{mode} invalid argument to --mode or ambiguous abbreviation.\n"
  902. . " Must be 'convert' or 'verify'.\n"
  903. . $usage;
  904. $options{mode} = $hits[0];
  905. $options{'cvs-branch-labels'} or die
  906. "$0: RCS Branch Labels unimplemented.\n"
  907. . $usage;
  908. # export VCSID into th environment for ourselves and our children
  909. $ENV{VCSID} = $options{vcsid};
  910. #
  911. # Verify we have all the binary executables we need to run this script
  912. #
  913. # Allowed this feature to be disabled in case which is missing or we are
  914. # running on a system which does not return error codes properly (e.g. WIN95)
  915. #
  916. # -- i.e. I don't feel like grepping output yet. --
  917. #
  918. my @missing_binaries = ();
  919. if ($options{'test-binaries'})
  920. {
  921. foreach (@bin_dependancies)
  922. {
  923. if (system "which", $_)
  924. {
  925. push @missing_binaries, $_;
  926. }
  927. }
  928. if (scalar @missing_binaries)
  929. {
  930. print STDERR "The following executables were not found in your PATH: "
  931. . join ( " ", @missing_binaries )
  932. . "\n"
  933. . "You must correct this before continuing.\n";
  934. exit 1;
  935. }
  936. }
  937. delete $options{'test-binaries'};
  938. #
  939. # set up our base archive manipulation commands
  940. #
  941. # set up our rcs_command mods
  942. $rcs_base_command = "rcs";
  943. $rcs_base_command .= " -x$options{'rcs-extension'}" if ($options{'rcs-extension'});
  944. # set up our rcs_command mods
  945. $ci_base_command = "ci";
  946. $ci_base_command .= " -x$options{'rcs-extension'}" if ($options{'rcs-extension'});
  947. #
  948. # So our logs fill in a manner we can monitor with 'tail -f' fairly easily:
  949. #
  950. STDERR->autoflush (1);
  951. STDOUT->autoflush (1);
  952. # Initialize the globals we use to keep track of recursion
  953. if ($options{recurse})
  954. {
  955. $maxlevel = 10000; # Arbitrary recursion limit
  956. }
  957. else
  958. {
  959. $maxlevel = 1;
  960. }
  961. delete $options{recurse};
  962. # So we can lock the directories behind us
  963. $donefile_name = $options{'rcs-dirs-flat'} ? "" : "RCS/";
  964. $errorfile_name = $donefile_name . "#conv.errors";
  965. $donefile_name .= "#conv.done";
  966. #
  967. # start the whole thing and drop the return code on exit
  968. #
  969. push (@ARGV, ".") unless (@ARGV);
  970. while ($_ = shift)
  971. {
  972. # reset the recursion level (corresponds to directory depth)
  973. # level 0 is the first directory we enter...
  974. $curlevel = -1;
  975. my ($e, $w) = execdir($_);
  976. $errors += $e;
  977. $warnings += $w;
  978. }
  979. print STDERR "$0: " . ($errors ? "Aborted" : "Done") . ".\n";
  980. print STDERR "$0: ";
  981. print STDERR ($errors ? $errors : "No") . " error" . (($errors != 1) ? "s" : "");
  982. print STDERR ", " . ($warnings ? $warnings : "no") . " warning" . (($warnings != 1) ? "s" : "")
  983. if ($options{warnings});
  984. print STDERR ".\n";
  985. #
  986. # Woo-hoo! We made it!
  987. #
  988. exit $errors;