#
# Copyright (c) 1990,1991,1992 The Ohio State University.
# All rights reserved.
#
# Redistribution and use in source and binary forms are permitted
# provided that: (1) source distributions retain this entire copyright
# notice and comment, and (2) distributions including binaries display
# the following acknowledgement:  ``This product includes software
# developed by The Ohio State University and its contributors''
# in the documentation or other materials provided with the distribution
# and in all advertising materials mentioning features or use of this
# software. Neither the name of the University nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#

#if ($use_db_file) {
#    require '/usr/lib/perl/linux/fcntl.ph';
#    use Fcntl;
#    use DB_File;
#}

#
# Heck, we almost always need the darned hostname, might as well get it 
# once and for all...
#
if (! defined($hostname)) {
    open(HOST, "$hostname_prog |") || die "error: can't get the hostname";
    chop($hostname=<HOST>);
    close(HOST);

    ($hostname) = split(/\./, $hostname);
}

#
# 'Database' routines.  Be sure to define 'ok_keys' so that it knows what the valid
# keys are for each of your database types.  Format is:
#
#	$ok_keys{"type key"} = 1;
#
# as in 
#
#	$ok_keys{"tape_db uses"} = 1;
#

$DB_READ = 			0;
$DB_CREATE =			1;
$DB_UPDATE =			2;

$DBERR_OK = 			0;
$DBERR_NOTFOUND = 		1;

$DBSEP_1 = ";";			# '\034';
$DBSEP_2 = "=";			# '\035';

sub parse_db_entry {
    local($entry, *result) = @_;
    local($pair, $name, $value);

    print "$hostname,$0 parse_db_entry\n" 
	if $opt_d & $DEBUG_DATABASE;

    %result = ();
    foreach $pair (split(/$DBSEP_1/o, $entry)) {
	($name, $value) = split(/$DBSEP_2/o, $pair);
	$result{$name} = $value;
    }
}

sub pack_em_up {
    local(%stuff) = @_;
    local($key, @pairs);

    print "$hostname,$0 pack_em_up\n" 
	if $opt_d & $DEBUG_DATABASE;

    foreach $key (keys %stuff) {
	push(@pairs, join($DBSEP_2, $key, $stuff{$key}));
    }
    return(join($DBSEP_1, @pairs));
}

#
# update_db updates a database entry in the named database ($db_name), 
# indexed by $key. 
#
# $lock_name is the name of the lock file to be used.  $db_type is the type 
# of the database (used to determine what the valid keys are).
#
# $op is the operation to perform: $DB_READ reads the entry and
# returns the results in %result.  $DB_CREATE creates a new entry that
# supercedes the previous entry - the field values are taken from
# %replace and %add.  $DB_UPDATE updates an existing entry.  %replace
# indicates the fields whose values are to be replaced, %add indicates
# the fields whose values are to be added to the current values.     
#
# In all cases, %result is set to the current value for that entry.
#
# (fixme) should ensure that the field names and values don't include 
# either $DBSEP_1 or $DBSEP_2.
#

sub update_db {
    local($db_name, $lock_name, $key, $db_type, $op, 
	  *replace, *add, *result) = @_;
    local(%DB, $t_key, @pairs, $locked);

    print "$hostname,$0 update_db $db_name $op $key\n" 
	if $opt_d & $DEBUG_DATABASE;

    $locked = 0;

    #
    # Wait to get the lock, if that's required...
    #
    # hack - reading safe?
    if ($lock_name ne "" && $op != $DB_READ) {
	if (&lock($lock_name, 600) != $LOCK_OK) {
	    &sigh("can't lock $lock_name from $hostname in $0, timed out\n");
	}
	$locked = 1;
    }

    #
    # open the database in question...
    #
#    dbmopen(DB, $db_name, $db_mode) ||
    &open_db(*DB, $db_name, $db_mode) ||
      &sigh("can't open the database $db_name on $hostname in $0\n");

    #
    # If we are reading or updating an entry, read the current value 
    # and split it into fields (in result).
    #
    if (($op == $DB_READ || $op == $DB_UPDATE) && ! defined($DB{$key})) {
	&close_db(*DB, $db_name);
	if ($locked) {
	    &unlock($lock_name);
	}
	return($DBERR_NOTFOUND);
    }

    if ($op == $DB_READ || $op == $DB_UPDATE) {
	&parse_db_entry($DB{$key}, *result);
    } else {
	%result = ();
    }

    if ($op == $DB_READ) {
	&close_db(*DB, $db_name);
	if ($locked) {
	    &unlock($lock_name);
	}
	return($DBERR_OK);
    }

    #
    # Otay, now we update/create.  For each entry in replace, replace 
    # the corresponding entry in %result.  For each entry in add, add 
    # to the current value in %result.
    #

    if ($op == $DB_CREATE) {
        print "$hostname,$0 update_db delete $db_name $key\n" 
	    if $opt_d & $DEBUG_DATABASE;

	delete $DB{$key};
    }

    foreach $t_key (keys %replace) {
	if (! defined($ok_keys{"$db_type $t_key"})) {
	    &close_db(*DB, $db_name);
	    &sigh("invalid key $t_key for db type $db_type: db $db_name host $hostname prog $0\n");
        }	
	
	$result{$t_key} = $replace{$t_key};
    }

    foreach $t_key (keys %add) {
	if (! defined($ok_keys{"$db_type $t_key"})) {
	    &close_db(*DB, $db_name);
	    &sigh("invalid key $t_key for db type $db_type: db $db_name host $hostname prog $0\n");
        }	
	
        if (defined($result{$t_key})) {
	    $result{$t_key} .= "," . $add{$t_key};
        } else {
            $result{$t_key} = $add{$t_key};
        }
    }

    #
    # Otay, now %result is updated, we need to reconstruct the value 
    # of the real DB entry and update the real database.
    #

    $DB{$key} = &pack_em_up(%result);

    #
    # Whew.  Close the database and remove the lock.
    #
    &close_db(*DB, $db_name);
    if ($locked) {
	&unlock($lock_name);
    }

    return($DBERR_OK);
}

#
# Get a sequence number...
#

sub get_id {
    local($file) = @_;
    local($sequence_number);

    print "$hostname,$0 get_id\n" 
	if $opt_d & $DEBUG_DATABASE;

    $file = $file . ".$sequence_ext";

    if (defined($opt_n)) {
        return -1;
    }

    if (&lock($file) != $LOCK_OK) {
	&sigh("can't lock $file, timed out\n");
    }

    if ((! open(SEQUENCE, "+<$file")) && (! open(SEQUENCE, ">$file"))) {
	&sigh("Can't open the sequence file '$file'!\n");
    }

    $sequence_number = <SEQUENCE>;

    $sequence_number++;

    if (seek(SEQUENCE, 0, 0) == 0) {
	&sigh("Can't seek to 0 in the sequence file!\n");
    }

    print SEQUENCE $sequence_number;

    close(SEQUENCE);

    &unlock($file);

    print "get_backup_id: using id $sequence_number\n" if $opt_d & $DEBUG_INFO;

    return($sequence_number);
}


sub open_db {
    local(*db, $name, $mode) = @_;
    local($tmp_name);

    print "$hostname,$0 open_db $name\n" 
	if $opt_d & $DEBUG_DATABASE;

#    if ($use_db_file) {
#	$DB_HASH{bsize} = $db_bsize;
#	$DB_HASH{ffactor} = $db_ffactor;
#	$DB_HASH{nelem} = $db_nelem;
#
#	$tmp_name = $name . '.' . $dbext;
#	tie %db, DB_File, $tmp_name, &O_CREAT|&O_RDWR, $mode, $DB_HASH;
#	return(1);
#    } else {
	return(dbmopen(%db, $name, $mode));
#    }
}

sub read_db {
    local(*db, $key, *value, *array) = @_;

    print "$hostname,$0 read_db $key\n" 
	if $opt_d & $DEBUG_DATABASE;

    return(0) if ! defined($db{$key});

    $value = $db{$key};
    &parse_db_entry($value, *array);
    return(1);
}

sub write_db {
    local(*db, $key, *array, $name, $type) = @_;
    local($tmp);

    print "$hostname,$0 write_db $key\n" 
	if $opt_d & $DEBUG_DATABASE;

    foreach $tmp (keys %array) {
	if (! defined($ok_keys{"$type $tmp"})) {
	    &sigh("invalid key $tmp for db type $type: db $name host $hostname prog $0\n");
        }
    }

    $db{$key} = &pack_em_up(%array);
    return(1);
}

sub close_db {
    local(*db, $name) = @_;

    print "$hostname,$0 close_db $name\n" 
	if $opt_d & $DEBUG_DATABASE;

#    if ($use_db_file) {
#	untie %db;
#    } else {
	dbmclose %db;
#    }
    return;
}

sub delete_db {
    local(*db, $key, $name) = @_;

    print "$hostname,$0 delete_db $key\n" 
	if $opt_d & $DEBUG_DATABASE;

    delete $db{$key};
    return(1);
}

sub defined_db {
    local(*db, $key) = @_;

    print "$hostname,$0 defined_db\n" 
	if $opt_d & $DEBUG_DATABASE;

    return(defined($db{$key}));
}

sub keys_db {
    local(*db) = @_;

    print "$hostname,$0 keys_db\n" 
	if $opt_d & $DEBUG_DATABASE;

    return(keys %db);
}

1;
