#!/usr/bin/perl
# -*-Fundamental-*-

#eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
#  & eval 'exec perl -S $0 $argv:q'
#  if 0;
##  THE PRECEEDING STUFF EXECS perl via $PATH
## -*-Fundamental-*-

require 5.000;

#use bytes;

my $s = "\001";

# Schema stuff
#
my $Act_add	= 0;
my $Act_edit	= 1;
my $Act_delete	= 2;
my $Act_branch	= 3;
my $Act_integ	= 4;
my $Act_import	= 5;

#  $Id: //guest/daniel_sherwood/cvs2p4/bin/dochanges#2 $
#
#  Richard Geiger
#

sub dirname
{
  local($dir) = @_;
  $dir =~ s%^$%.%; $dir = "$dir/";
  if ($dir =~ m%^/[^/]*//*$%) { return "/"; }
  if ($dir =~ m%^.*[^/]//*[^/][^/]*//*$%)
  { $dir =~ s%^(.*[^/])//*[^/][^/]*//*$%$1%; { return $dir; } }
  return ".";
}

use Carp; # ...or flounder. (This will fail unless 'perl' is a perl5!)
$| = 1;

($Myname = $0) =~ s%^.*/%%;
$Mydir = &dirname($0);
$Here = `/bin/pwd`; chop $Here;
if ($Mydir ne ".") { chdir "$Mydir" || die "$Myname: can't chdir \"$Mydir\": $!"; }
chdir ".." || die "$Myname: can't chdir \"..\": $!";
$Mydir = `/bin/pwd`; chop $Mydir;
chdir $Here || die "$Myname: can't chdir \"$Here\": $!";

#obs#use IPC::Open2;
require "$Mydir/lib/util.pl";

$Usage = <<LIT;
$Myname: usage: $Myname [-v <n>] [-s <change>] [-c] <conversiondir>
LIT


sub usage
{
  print STDERR $Usage;
  exit 1;
}


sub help
{
  print STDERR <<LIT;
$Usage

  -v <n>       set verbosity level [0]
  -s <change>  start numbering generated changes at <change>
  -c           copy (rather than link) the file archive

$Myname uses the outputs of the genmetadata and genchanges stages of
cvs2p4 and creates a Perforce metadatabase describing the converted
changes. It also links (or, optionally, copies) the RCS archives being
converted into the file archive tree under $P4ROOT.

LIT
  exit 1;
}


sub path
{
  my($prefix, $file_dir, $file_name) = @_;
  my($path);
  $path = "$prefix";
  if ($file_dir) { $path .= "$file_dir/"; }
  $path .= "$file_name";
  return $path;
}


sub is_binary_ext
{
  my ($path) = @_;

  my ($chkpath) = ($path =~ /^$CVS_MODULE\/(.*)$/);

  my ($filename) = $path;
  $filename =~ s/^.*\///;
  my $fileext;
  $filename =~ m/\.([^\.]+)$/;
  $fileext = $1;
  $fileext =~ tr/A-Z/a-z/;
  if (defined($EXT{$fileext})) { return 1; }
  return 0;
}


#  Determine whether the depot already has this file on this code line.
#  (Used to determine when we need to branch).
#

sub depot_has
{
  my($file) = @_;
  if (defined($DEPOTMAP{$file}))
    {
      my ($have, $rev) = split(/$S/, $DEPOTMAP{$file});
      return $have;
    }    
  return 0;
}


sub depot_newrev
{
  my($file, $action) = @_;
  my ($have, $rev);

  if ($action =~ /^(add|branch)$/)
    {
      if (defined($DEPOTMAP{$file}))
        {
          ($have, $rev) = split(/$S/, $DEPOTMAP{$file});
	  if ($have)
	    { die "already have $file on add"; }
          $have = 1;
          $rev++;
        }
      else
        {
          $have = 1;
          $rev = 1;
        }
    }
  else
    {
      if (! defined($DEPOTMAP{$file}))
        {
          print "$Myname: unmapped \"$file\" on edit/delete.\n";
          exit 1;
        }
      ($have, $rev) = split(/$S/, $DEPOTMAP{$file});

      if (! $have)
        {
          print "$Myname: don't already have \"$file\" on edit/delete.\n";
          exit 1;
        }
      if ($action eq "delete")
        { $have = 0; }
      $rev++;
    }
    
  $DEPOTMAP{$file} = "$have$S$rev";

  return $rev;
}


sub genrev
{
  my ($depotfile, $p4rev, $type, $action, $modtime, $lbrfile, $lbrrev, $lbrtype) = @_;
  return "$depotfile$s$p4rev$s$type$s$action$s$modtime$s//$lbrfile$s$lbrrev$s$lbrtype";
}


sub mkdepot
{
  my ($depot, $spec) = @_;

  my $depot_type;
  my $depot_extra;

  if ($spec)
    {
      $depot_type = "2";
      $depot_extra = ".p4s";
    }
  else
    {
      $depot_type = "0";
      $depot_extra = "subdir";
    }

  print DBMETA "\@pv\@ 0 \@db.depot\@ \@$depot\@ $depot_type \@$depot_extra\@ \@DEPOTS/$depot/...\@\n";
  $now = time;
  print DBMETA "\@pv\@ 2 \@db.domain\@ \@$depot\@ 100 \@\@ \@\@ \@cvs2p4\@ $now $now".
               " 0 0 \@Created by cvs2p4\n@\n";

  $Made_depot{$depot} = 1;
}


sub genchange
{
  my ($revs, $ch_time, $msg, $who, $integs) = @_;

  if ($LOGNOTE)
    {
      my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
      my($ts) = sprintf("%04d/%02d/%02d %02d:%02d:%02d",
                         $year+1900, $mon+1, $mday, $hour, $min, $sec);
      $msg .= "\n[imported from CVS by cvs2p4 at $ts]\n";
    }

  my $smsg = $msg;
  $smsg = substr($smsg, 0, 31);
  $smsg = &atq($smsg);

  print DBMETA "\@pv\@ 0 \@db.change\@ $Change $Change cvs2p4 $who $ch_time 1 $smsg\n";
  
  $msg = &atq($msg);
  print DBMETA "\@pv\@ 0 \@db.desc\@ $Change $msg\n";

  foreach my $rev (@{$revs})
    {
      my ($depotfile, $p4rev, $type, $action, $modtime, $lbrfile, $lbrrev, $lbrtype) = split(/$s/, $rev);

      my ($depot) = ($depotfile =~ /^\/\/([^\/]+)\//);
      if (! defined($Made_depot{$depot})) { &mkdepot($depot); }

      $depotfile = &atq($depotfile);
      $lbrfile   = &atq($lbrfile);
      $lbrrev    = &atq($lbrrev);
      $zdig      = "00000000000000000000000000000000";

      $depotfile =~ s/\/Attic(\/[^\/]+)$/$1/;

      my $p4depotfile = &p4_esc($depotfile);

      print DBMETA "\@pv\@ 3 \@db.rev\@ $p4depotfile $p4rev $type $action ".
                      "$Change $ch_time $time $zdig $lbrfile $lbrrev $lbrtype\n";
      print DBMETA "\@pv\@ 0 \@db.revcx\@ $Change $p4depotfile $p4rev $action\n";
    }

  foreach my $integ (@{$integs})
    {
      my($tofile, $fromfile, $fromrev) = split(/$s/, $integ);

      my ($to_depot, $to_branch, $to_module, $to_path) =
        ($tofile =~ /^\/\/([^\/]+)\/([^\/]+)\/([^\/]+)\/(.*)$/);

      if (defined($Depotmap{$to_module}))
        { $tofile = "$Depotmap{$to_module}/$to_branch/$to_module/$to_path"; }

      my ($from_depot, $from_branch, $from_module, $from_path) =
        ($fromfile =~ /^\/\/([^\/]+)\/([^\/]+)\/([^\/]+)\/(.*)$/);

      if (defined($Depotmap{$from_module}))
        { $fromfile = "$Depotmap{$from_module}/$from_branch/$from_module/$from_path"; }

      $tofile = &atq($tofile);
      $fromfile = &atq($fromfile);

      $tofile =~ s/\/Attic(\/[^\/]+)$/$1/;
      $fromfile =~ s/\/Attic(\/[^\/]+)$/$1/;

      my $p4tofile = &p4_esc($tofile);
      my $p4fromfile = &p4_esc($fromfile);

      print DBMETA "\@pv\@ 0 \@db.integed\@ $tofile $fromfile 0 $fromrev 0 1 2 $Change\n";
      print DBMETA "\@pv\@ 0 \@db.integed\@ $fromfile $tofile 0 1 0 $fromrev 3 $Change\n";
    }

  $Change++;

}

  
#  Perforce filetype bit constants
#
#    0x0020             to "ktext"
#    0x0000             to "text"
#
my $TEXTTYPE_K     = 0x0020;
my $TEXTTYPE	   = 0x0000;

sub type_of
{
   my ($rcsfile, $rev, $options) = @_;

   my $binary = 0;

   if ($options =~ /[ob]/ || &is_binary_ext($rcsfile))
     { $binary = 1; }
   elsif ($CHECKBIN)
     {
       if (&s("$CO -q -p$rev '$file' >'$Convdir/rev.tmp'"))
         {
           print "$Myname: \"$CO -p$rev '$file' >'$Convdir/rev.tmp'\" failed.\n";
           exit 1;
         }
       if (-B "$Convdir/rev.tmp") { $binary = 1; }
       unlink "$Convdir/rev.tmp";
     }
       
   my $type = 0;

   #  NOTE: CVS/RCS just doesn't have the notion of Perforce's "text"
   #  vs "ktext" types; in RCS, a file is either "text" (in which
   #  keywords get expanded in one way or another), or "binary", in
   #  which they are left untouched (RCS's concept of "binary"). How's
   #  a poor conversion program to cope?! For now, we treat all text
   #  files as "ktext", with an exclusion list for files which must be
   #  plain text.
   #
   if ($binary)
     { $type |= 0x0100; }
   else
     {

       my $chkpath = $rcsfile;
       $chkpath =~ s/^$CVS_ROOT\///;

       if (defined($NOKEYEXP_PATHNAME{$chkpath}))
        { $type |= $TEXTTYPE; }
       else
        { $type |= $TEXTTYPE_K; }
     }

   if ($options =~ /x/)
     { $type |= 0x0200; }

   return $type;
}


sub set_depot_paths
{
  my ($file_dir, $file_name, $line, $import_branch) = @_;
  my ($Depotfile, $Importfile);

  my ($module, $remainder) = ($file_dir =~ /^([^\/]+)\/?(.*)?$/);


  # If we are doing $USE_IMPORT_DEPOT, and $line names the branch for
  # 1.1.1, use "$USE_IMPORT_DEPOT" as the depot; otherwise, use that
  # selected by $Depotmap{}; otherwise, the default $P4_DEPOT.
  #
  if ($USE_IMPORT_DEPOT && $line eq $import_branch)
    {
      $Depotfile = &path("$USE_IMPORT_DEPOT/$line/", $remainder, $file_name);
      $Importfile = &path("$IMPORT/", $file_dir, $file_name);
    }
  elsif (defined($Depotmap{$module}))
    {
      $Depotfile = &path("$Depotmap{$module}/$line/$module/", $remainder, $file_name);
      $Importfile = &path("$IMPORT/", $file_dir, $file_name);
    }
  else
    {
      $Depotfile  = &path("$P4_DEPOT/$line/", $file_dir, $file_name);
      $Importfile = &path("$IMPORT/", $file_dir, $file_name);
    }

  return ($Depotfile, $Importfile);
}


sub rmap
{
  my ($depotrev, $cvsrev, $revinfo) = @_;

  $depotrev =~ s/\/Attic(\/[^\/]+)$/$1/;

  $REVMAP{$depotrev} = $revinfo;
  if ($RREVMAP{$cvsrev}) { $RREVMAP{$cvsrev} .= "\001"; }
  $RREVMAP{$cvsrev} .= "$depotrev";
}

my %map;

#  Process one "change" from CVS.
#  Called with the set of CVS files/revs to process in @change
#
sub dochange
{
  print "========== change group $do_change_num\n";

  if ($V > 0)
    {
      print "NCHGS = <$#change>\n";
      foreach $c (@change)
        {
          ($filerev, $time, $who, $state, $line, $import_branch, $branches, $prevrev, $options) = split(/$S/, $c);
          @filerev = split(/\//, $filerev);
          $rev = pop(@filerev);
          $file = join("/", @filerev);
          print "DUMP $who $time $filerev $state $line $import_branch $branches\n";
        }
    }

  # Do the revisions...
  #
#  print "=== deletes\n";

  undef @revs;

  my $ch_time = 0;
  foreach $c (@change)
    {
      chomp $c;
      ($filerev, $time, $who, $state, $line, $import_branch, $branches, $prevrev, $options) = split(/$S/, $c);
      if ($line eq $TRUNKLINE) { $line = $MAINNAME; }
      @filerev = split(/\//, $filerev);
      $rev = pop(@filerev);
      $file = join("/", @filerev);

      ($file_dir = &dirname($file)) =~ s/^$CVS_MODULE//;
      $file_dir =~ s/^\///;
      ($file_name = $file) =~ s%^.*/%%;

      ($Depotfile, $Importfile) = &set_depot_paths($file_dir, $file_name, $line, $import_branch);

      if ($state eq $DEADSTATE)
        {
          $Exists_in_depot = &depot_has($Depotfile);
          if ($Exists_in_depot)
            {
	      $type = &type_of($file, $rev, $options);
	      if ($time > $ch_time) { $ch_time = $time; }
	      my $grev = &depot_newrev($Depotfile, "delete");
	      &rmap("$Depotfile#$grev", $filerev, $c);
              push(@revs, (&genrev($Depotfile,
                                   $grev,
                                   $type,
                                   $Act_delete,
                                   $time, 
                                   $Importfile,
                                   $rev,
                                   $type)));
            }
        }
    }

  if ($#revs >= 0) { &genchange(\@revs, $ch_time, $MSGS{$filerev}, $who); }

#  print "===adds/edits===\n";
  undef @revs;
  $ch_time = 0;
  foreach $c (@change)
    {
      chomp $c;
      ($filerev, $time, $who, $state, $line, $import_branch, $branches, $prevrev, $options) = split(/$S/, $c);

      if ($line eq "$TRUNKLINE") { $line = $MAINNAME; }
      @filerev = split(/\//, $filerev);
      $rev = pop(@filerev);
      $file = join("/", @filerev);

      #  The was already done by the main/import handling when the 1.1
      #  revision was processed (Since 1.1.1.1 is, logically, on a
      #  sub-branch of 1.1).  Or so I claim. Note that we still *do*
      #  note the creation of new sub-branches of 1.1.1.1 implied by a
      #  non-empty $branches.
      #
      if ($rev eq "1.1.1.1" && $line eq $import_branch) { next; }

      ($file_dir = &dirname($file)) =~ s/^$CVS_MODULE//;
      $file_dir =~ s/^\///;
      ($file_name = $file) =~ s%^.*/%%;

      ($Depotfile, $Importfile) = &set_depot_paths($file_dir, $file_name, $line, $import_branch);

      # Note: we assume binary files never become text files or vice versa
      #
      if ($state ne $DEADSTATE)
        {
          $Exists_in_depot = &depot_has($Depotfile);

          if ($Exists_in_depot)
            {
	      $type = &type_of($file, $rev, $options);
	      if ($time > $ch_time) { $ch_time = $time; }
	      my $grev = &depot_newrev($Depotfile, "edit");
	      &rmap("$Depotfile#$grev", $filerev, $c);
 	      push(@revs, (&genrev($Depotfile,
                                   $grev,
                                   $type,
                                   $Act_edit,
                                   $time,
                                   $Importfile,
                                   $rev,
                                   $type)));
	      $map{"$Depotfile$s$rev"} = $grev;
            }
          else
            {
	      $type = &type_of($file, $rev, $options);
	      if ($time > $ch_time) { $ch_time = $time; }
	      my $grev = &depot_newrev($Depotfile, "add");
	      &rmap("$Depotfile#$grev", $filerev, $c);
 	      push(@revs, (&genrev($Depotfile,
                                   $grev,
                                   $type,
                                   $Act_add,
                                   $time,
                                   $Importfile,
                                   $rev,
                                   $type)));
	      $map{"$Depotfile$s$rev"} = $grev;
            }
        }
    }

  if ($#revs >= 0) { &genchange(\@revs, $ch_time, $MSGS{$filerev}, $who); }

#  print "===branches===\n";
  undef @revs; undef @integs;
  $ch_time = 0;
  foreach $c (@change)
    {
      chomp $c;
      ($filerev, $time, $who, $state, $line, $import_branch, $branches, $prevrev, $options) = split(/$S/, $c);
      if ($line eq "$TRUNKLINE") { $line = $MAINNAME; }

      if ($state ne $DEADSTATE && $branches ne "-")
        {
          @filerev = split(/\//, $filerev);
          $rev = pop(@filerev);
          $file = join("/", @filerev);

          ($file_dir = &dirname($file)) =~ s/^$CVS_MODULE//;
          $file_dir =~ s/^\///;

          ($file_name = $file) =~ s%^.*/%%;

          ($Depotfile, $Importfile) = &set_depot_paths($file_dir, $file_name, $line, $import_branch);

          foreach $branch (split(/:/, $branches))
            {
	      $type = &type_of($file, $rev, $options);

              my ($Depot_branchfile) = &set_depot_paths($file_dir, $file_name, $branch, $import_branch);

	      if ($time > $ch_time) { $ch_time = $time; }

              my $grev = &depot_newrev($Depot_branchfile, "add");

              # if we are adding the initial import_branch revision as 1.1,
              # make it 1.1.1.1
              #
              if ($branch eq $import_branch && $grev eq "1")
                {
                  my ($f, $r) = ($filerev =~ m/^(.*)\/([^\/]+)$/);
                  if ($r !~ /^1\.1(\.1\.1)?$/)
                    { die "import_branch $import_branch assert rev '$r' !~ /^1\.1(\.1\.1)?\$/"; }
                  $filerev = "$f/1.1.1.1";
                }

	      &rmap("$Depot_branchfile#$grev", $filerev, $c);

 	      push(@revs, (&genrev($Depot_branchfile,
                                   $grev,
                                   $type,
                                   $Act_branch,
                                   $time,
                                   $Importfile,
                                   $rev,
                                   $type)));
	      $map{"$Depot_branchfile$s$rev"} = $grev;

	      my $bprev;

              if (! ($bprev = $map{"$Depotfile$s$rev"}))
                { 
                  if ($rev eq "1.1.1.1")
                    {
                      if (! ($bprev = $map{"$Depotfile${s}1.1"}))
                        { die "unmapped rev <$Depotfile> <1.1[.1.1]>"; }
                    }
                  else
                    { die "unmapped rev <$Depotfile> <$rev>"; }
                }

	      push(@integs, "$Depot_branchfile$s$Depotfile$s$bprev");
            }
        } 
    }

  if ($#revs >= 0) { &genchange(\@revs, $ch_time, "Branching\n", $who, \@integs); }

}


###### main starts here
#  

# option switch variables get defaults here...

$Metadata = "metadata";

# option switch variables get defaults here...

$V = 0;
$Change = 1;
$Copy = 0;

while ($#ARGV >= 0)
  {
    if ($ARGV[0] eq "-c")    { $Copy = 1; shift; next; }
    elsif ($ARGV[0] eq "-v")
      {
        shift; if ($ARGV[0] < 0) { &usage; }
        $V = $ARGV[0]; shift; next;
      }
    elsif ($ARGV[0] eq "-s")
      {
        shift; if ($ARGV[0] < 0) { &usage; }
        $Change = $ARGV[0]; shift; next;
      }
    elsif ($ARGV[0] eq "-help")
      { &help; }
    elsif ($ARGV[0] =~ /^-/) { &usage; }
    push(@Args, $ARGV[0]);
    shift;
  }

if ($#Args ne 0) { &usage; }

$Convdir = $Args[0];
#chdir $Convdir || die "$Myname: can't chdir \"$Convdir\": $!";
#$Convdir = `/bin/pwd`; chop $Convdir;
#chdir $Here || die "$Myname: can't chdir \"$Here\": $!";

require "$Convdir/config";

if ($PureRCS) { $MAINNAME = "1"; }


my ($p4d_y, $p4d_r) = &p4d_vers($P4D);

if ($p4d_y < 2002)
  { print "$Myname: this version requires p4d 2002.1 or later <$p4d>.\n"; exit 1; }

if ($CHECKBIN)
  {
    #  Path the the RCS co command
    #
    if (! defined($CO)) { $CO = "/usr/local/bin/co"; }
    if (! -x ($CO))
      { print "$Myname: No executable \"co\" command at \"$CO\".\n"; exit 1; }
  }

$Metadata = "$Convdir/metadata";
$Logmsgs  = "$Convdir/logmsgs";
$Changes  = "$Convdir/changes";
$Revmap   = "$Convdir/revmap";
$Rrevmap  = "$Convdir/rrevmap";
$Depotmap = "$Convdir/depotmap";
$Client   = "$Convdir/p4";
$DBmeta   = "$P4ROOT/dbmeta";
$Checkpoint = "checkpoint";

$LOGNOTE = 1;

if (! defined($P4PORT))
  { print "$Myname: no P4PORT in \"$Convdir/config\".\n"; exit 1; }

($P4HOST = $P4PORT) =~ s/:.*//;
($P4PORTNUM = $P4PORT) =~ s/^.*://;

$P4ADMINUSER = "p4";

# These defaults can be overriden in the config file
#

if ($Change == 1)
  {
    # Remove any revmap, rrevmap files...
    #  
    if (&s("/bin/rm -f $Revmap $Revmap.db $Revmap.dir $Revmap.pag"))
      { die "/bin/rm -f $Revmap.db ..."; }
    if (&s("/bin/rm -f $Rrevmap $Rrevmap.db $Rrevmap.dir $Rrevmap.pag"))
      { die "/bin/rm -f $Rrevmap.db ..."; }
    if (&s("/bin/rm -f $Depotmap $Depotmap.db $Depotmap.dir $Depotmap.pag"))
      { die "/bin/rm -f $Depotmap.db ..."; }
    if (&s("/bin/rm -rf $P4ROOT && mkdir -p $P4ROOT"))
      { die "/bin/rm -rf $P4ROOT && mkdir -p $P4ROOT"; }
  }

$CVS_MODPATH = $CVS_MODULE;

chdir $CVS_MODPATH || die "$Myname: can't chdir \"$CVS_MODPATH\": $!";
$CVS_MODPATH = `/bin/pwd`; chop $CVS_MODPATH;
chdir $Here || die "$Myname: can't chdir \"$Here\": $!";

$CVS_MODULE_NAME = $CVS_MODULE;
$CVS_MODULE_NAME =~ s@/.*/@@g;
$IMPORT = "$DEPOT/$CVS_MODULE_NAME";
$ndIMPORT = $IMPORT;
$ndIMPORT =~ s@^[^/]+/@@;

chdir $P4ROOT || die "$Myname: can't chdir \"$P4ROOT\": $!";
$P4ROOT = `/bin/pwd`; chop $P4ROOT;
if (&s("/bin/mkdir -p $P4ROOT/DEPOTS/$DEPOT"))
  { die "/bin/mkdir -p $P4ROOT/DEPOTS/$DEPOT"; }

if ($Change == 1)
  {
    my $destdir = dirname("$P4ROOT/DEPOTS/$IMPORT");
    if (&s("/bin/mkdir -p $destdir"))
      { die "/bin/mkdir -p $destdir"; }

    if ($Copy || $COPYIMPORT)
      {
        my $cmd = "/bin/cp -rp \"$CVS_MODPATH\" \"$P4ROOT/DEPOTS/$IMPORT\"";

        if (&s($cmd))
          {
            print "$Myname <$cmd> failed.\n";
            exit 1;
          }

        $cmd = "/usr/bin/find \"$P4ROOT/DEPOTS/$IMPORT\" -type f -print0 | /usr/bin/xargs -0 /bin/chmod a-w";

        if (&s($cmd))
          {
            print "$Myname <$cmd> failed.\n";
            exit 1;
          }
      }
    else
      {
        my $cmd = "/bin/ln -s \"$CVS_MODPATH\" \"$P4ROOT/DEPOTS/$IMPORT\"";

        if (&s($cmd))
          {
            print "$Myname <$cmd> failed.\n";
            exit 1;
          }
      }
  }

chdir $Here || die "$Myname: can't chdir \"$Here\": $!";

#  Open the changes file (this is the genchanges output file)
#
if (! open(CHGS, "<$Changes"))
  { print "$Myname: can't open \"$Changes\": $!\n"; exit 1; }

#  Open the db metadata (journal format) file we will write
#
if (! open(DBMETA, ">$DBmeta"))
  { print "$Myname: can't open \"$DBmeta\": $!\n"; exit 1; }

use DB_File;
$DBMCLASS="DB_File";

#$myhashinfo = new DB_File::HASHINFO;
#$myhashinfo->{bsize}=4096;

$myhashinfo = new DB_File::BTREEINFO;

#  Open the REVMAP database...
#
if (! tie(%REVMAP, $DBMCLASS, $Revmap, O_CREAT|O_RDWR, 0666, $myhashinfo))
  { print "$Myname: can't tie \"$Revmap\": $!\n"; exit 1; }

#  Open the RREVMAP database...
#
if (! tie(%RREVMAP, $DBMCLASS, $Rrevmap, O_CREAT|O_RDWR, 0666, $myhashinfo))
  { print "$Myname: can't tie \"$Rrevmap\": $!\n"; exit 1; }

#  Open the DEPOTMAP database...
#
if (! tie(%DEPOTMAP, $DBMCLASS, $Depotmap, O_CREAT|O_RDWR, 0666, $myhashinfo))
  { print "$Myname: can't tie \"$Depotmap\": $!\n"; exit 1; }

#  Open the log messages database
#
if (! tie(%MSGS, $DBMCLASS, $Logmsgs, O_RDONLY, 0444, $myhashinfo))
  { print "$Myname: can't tie \"$Logmsgs\": $!\n"; exit 1; }

#
#
($depotname) = ($P4_DEPOT =~ m/^\/\/([^\/]+)\/?/);

my %Made_depot;

my $import_depot = $DEPOT;
$import_depot =~ s/\/.*$//;
&mkdepot($import_depot);

if ($USE_IMPORT_DEPOT)
  {
    my $ndUSE_IMPORT_DEPOT = $USE_IMPORT_DEPOT;
    $ndUSE_IMPORT_DEPOT =~ s/^\/\///;
    &mkdepot($ndUSE_IMPORT_DEPOT);
  }


$gather_change_num = $do_change_num = 0;
while (<CHGS>)
  {
    if (/^# ([0-9]+)$/)
      {
        $gather_change_num = $1;
        if ($do_change_num)
          {
            &dochange(@change);
            $do_change_num = 0;
          }

        #  Clear the change
        #
        undef @change;
      }
    elsif ($gather_change_num)
      { push(@change, $_); $do_change_num = $gather_change_num; }
  }
if ($do_change_num) { &dochange(@change); }

if ($SPEC_DEPOT) { &mkdepot($SPEC_DEPOT, 1); }


$Change--;
print DBMETA "\@pv\@ 0 \@db.counters\@ \@change\@ $Change\n";

#  We've written an upgrade-level 3 compliant database.
#
print DBMETA "\@pv\@ 0 \@db.counters\@ \@upgrade\@ 3\n";

close DBMETA;
untie %MSGS;
close CHGS;
untie %REVMAP;
untie %RREVMAP;
untie %DEPOTMAP;

$DBmeta =~ s%^.*/%%;

$P4D = "$P4D -r .";

if (&s("cd $P4ROOT && $P4D -jr $DBmeta"))
  { print "$Myname: \"$P4D -jr $DBmeta\" failed.\n"; exit 1; }

if ($p4d_y > 2002 || $p4d_r > 1)
  {
    # Then we could need a database upgrade. Let's go for it...
    #
    if (&s("cd $P4ROOT && $P4D -xu"))
      { print "$Myname: \"$P4D -xu\" failed.\n"; exit 1; }
  }

if (&s("cd $P4ROOT && rm -f $Checkpoint && $P4D -jd $Checkpoint"))
  { print "$Myname: \"$P4D -jd $Checkpoint\" failed.\n"; exit 1; }

exit 0;