############################################################################
#
#   Copyright 1999-2000 Phone.com, Inc.  All rights reserved.
#
#   Subject to the terms and conditions of the SDK License Agreement, 
#   Phone.com, Inc. hereby grants you the right to use the UP.SDK 
#   software and its related documentation.
#
#   The Phone.com name and logo and the family of terms carrying the "UP."
#   prefix are trademarks of Phone.com, Inc. All other brands and product 
#   names may be trademarks of the respective companies with which they 
#   are associated.
#
############################################################################

######################################################################
#
# FileUtils.pl
#
# Support routines for locked file access
#
# PUBLIC FUNCTIONS:
# OpenWithLock($fh, $fileName, $lockType)
#         - Opens the file with the appropriate READ/WRITE lock 
# CloseWithLock($fh)
#         - Closes the file handle opened with OpenWithLock
#
# EXAMPLE:
# require 'FileUtils.pl';
# ...
# &AppUtils::OpenWithLock(\*INPUT_FILE, "<./input.lst", "READ");
# while (<INPUT_FILE>) { ... }
# &AppUtils::CloseWithLock(\*INPUT_FILE);
# ...
# &AppUtils::OpenWithLock(\*OUTPUT_FILE, ">./output.lst", "WRITE");
# print OUTPUT_FILE $somedata;
# &AppUtils::CloseWithLock(\*OUTPUT_FILE);
# ...
# 
######################################################################

package AppUtils;

######################################################################
# Subroutines
######################################################################
######################################################################
#
# METHOD:
# OpenWithLock
#
# INDEX: (lists the categories this function is indexed under in the docs)
# files:opening;files:locking;locking files
# 
# DESCRIPTION:
# Opens a file handle on the file and locks the file, causing the
# process to block until the file is open.
#
# SYNOPSIS:
# require 'FileUtils.pl';
# ...
# &AppUtils::OpenWithLock($fh, $fname, $lockType);
#
# ARGUMENTS:
# $fh		Reference to the file handle to lock.
# $fname	The name of the file to open.
# $lockType	Type of lock ('READ' or 'WRITE'), default is 'WRITE'
#
# RETURNS:
# TRUE		Success
# undefined	Failure
#
######################################################################
sub OpenWithLock
{
    my($fh, $fname, $lockType) = @_;

    open($fh, $fname) || return(undef);
    seek($fh, 0, 0);

    &LockFile($fh, $lockType);
}

######################################################################
#
# METHOD:
# CloseWithLock
#
# INDEX: (lists the categories this function is indexed under in the docs)
# files:closing;files:locking;locking files
# 
# DESCRIPTION:
# Closes and unlocks a file handle that has been opened with OpenWithLock().
#
# SYNOPSIS:
# require 'FileUtils.pl';
# ...
# &AppUtils::CloseWithLock($fh);
#
# ARGUMENTS:
# $fh		Reference to the file handle to unlock and close.
#
######################################################################
sub CloseWithLock
{
	my($fh) = @_;

    &UnLockFile($fh);

    close($fh);
}

######################################################################
#
# METHOD:
# LockFile
#
# INDEX: (lists the categories this function is indexed under in the docs)
# files:locking;locking files
# 
# DESCRIPTION:
# Locks a specified filehandle, causing the process
# to block until the file is available.
# Note, that to write-lock a file, the file must be opened for writing;
#       to read-lock a file, the file must be opened for reading.
#
# SYNOPSIS:
# require 'FileUtils.pl';
# ...
# &AppUtils::LockFile($fh, $lockType);
#
# ARGUMENTS:
# $fh		Reference to the file handle to lock.
# $lockType	The type of lock ('READ' or 'WRITE'); the default is 'WRITE'
#
# RETURNS:
#	0 but true	Success
#	undefined	Failure.
#
######################################################################
sub LockFile
{
	my($fh, $lockType) = @_;
    my($F_SETLKW) = 7;
    my($F_RDLCK) = 1;
    my($F_WRLCK) = 2;
    my($type);
    my($fcntlData);
	my($status);

    $type = ($lockType eq 'READ') ? $F_RDLCK : $F_WRLCK;

	# Wrap this in an eval. If eval fails (probably because
	# fcntl is not implemented) then try to use flock.
    eval {
		$fcntlData = pack('sslllll4', ($type, 0, 0, 0, 0, 0));

		$status = fcntl($fh, $F_SETLKW, $fcntlData);
	};

	# If the eval failed, try using flock
	if ($@) {
		my($LOCK_EX) = 2;
		eval { $status = flock($fh, $LOCK_EX); };
	}

	$status;
}

######################################################################
#
# METHOD:
# UnLockFile
#
# INDEX: (lists the categories this function is indexed under in the docs)
# files:unlocking;unlocking files;unlocking
# 
# DESCRIPTION:
# Unlocks a file specified by a filehandle.
#
# SYNOPSIS:
# require 'FileUtils.pl';
# ...
# &AppUtils::UnLockFile($fh);
#
# ARGUMENTS:
# $fh		Reference to a file handle to unlock.
#
# RETURNS:
#	0 but true	Success
#	undefined	Failure.
#
######################################################################
sub UnLockFile
{
	my($fh) = @_;
    my($F_SETLK) = 6;
    my($F_UNLCK) = 3;
    my($fcntlData);
	my($status);

	# Wrap this in an eval. If eval fails (probably because
	# fcntl is not implemented) then try to use flock.
    eval {
		$fcntlData = pack('sslllll4', ($F_UNLCK, 0, 0, 0, 0, 0));

		$status = fcntl($fh, $F_SETLK, $fcntlData);
	};

	# If the eval failed, try using flock
	if ($@) {
		my($LOCK_UN) = 8;
		eval { $status = flock($fh, $LOCK_EX); };
	}

	$status;
}

1;
