# $NetBSD: shell-lib,v 1.4 2006/10/22 00:35:26 rillig Exp $
#
# Copyright (c) 2004 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to The NetBSD Foundation
# by Johnny C. Lam.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#        This product includes software developed by the NetBSD
#        Foundation, Inc. and its contributors.
# 4. Neither the name of The NetBSD Foundation 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 BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

######################################################################
# msg_log logfile msg
#	Output msg to logfile.  If logfile is "stdout" or "stderr"
#	then output to there instead.
######################################################################
msg_log()
{
	: ${echo=echo}

	_msg_log="$1"; shift
	case $_msg_log in
	stdout)	$echo "$@" ;;
	stderr)	$echo "$@" 1>&2 ;;
	*)	$echo "$@" >> $_msg_log ;;
	esac
}

######################################################################
# die msg
#	Output $msg to stderr, and exit with a positive error code.
######################################################################
die()
{
	msg_log stderr "$@"
	exit 1
}

######################################################################
# check_prog var prog ...
#	If $var is empty or unset, then set it to the path of one of
#	the program names in the list.
######################################################################
check_prog()
{
	: ${test=test}

	_ckp_var="$1"; shift

	eval _ckp_tmp=\"\$$_ckp_var\"
	if $test "x$_ckp_tmp" != "x"; then
		return 0
	fi

	for _ckp_prog do
		_ckp_IFS="${IFS}"; IFS=":"
		for _ckp_dir in ${PATH}; do
			if $test -x "$_ckp_dir/$_ckp_prog"; then
				eval $_ckp_var=\""$_ckp_dir/$_ckp_prog"\"
				return 1
			fi
		done
		IFS="${_ckp_IFS}"
	done

	die "$_ckp_var could not be set."
}

######################################################################
# shquote arg
#	Returns a backslashed and quoted version of arg in $shquoted.
######################################################################
shquote()
{
	: ${echo=echo}
	: ${sed=sed}

	_shq_arg=$1
	_shq_sed="$sed -e 1s/^X//"
	_shq_sed_quote_subst='s/\([\`\"$\\]\)/\\\1/g'
	case $_shq_arg in
	*[\`\"\$\\]*)
		shquoted=`$echo "X$_shq_arg" | $_shq_sed -e "$_shq_sed_quote_subst"`
		;;
	*)
		shquoted="$_shq_arg"
		;;
	esac
	case $shquoted in
	*[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
		shquoted="\"$shquoted\""
		;;
	esac
}

######################################################################
# lock_file -f path [-n token]
#	Attempt to create a lockfile at $path.  Any directories in the
#	path should already exist.  If $token is specified, then assume
#	that it is unique between machines sharing an NFS mount.
#
# (1) Create globally-unique filename in the same filesystem as the
#     lockfile.
# (2) Try to create a hard-link from this file to the lockfile, but
#     ignoring any errors.
# (3) If the two files are the same file, then the lock was successfully
#     obtained; otherwise, the lock attempt wasn't successful.
######################################################################
lock_file()
{
	: ${dirname=dirname}
	: ${echo=echo}
	: ${link=link}
	: ${mkdir=mkdir}
	: ${mktemp=mktemp}
	: ${rm=rm}
	: ${test=test}
	: ${touch=touch}

	_lf_lockfile=
	_lf_nfs=
	while $test $# -gt 0; do
		case $1 in
		-f)	_lf_lockfile="$2"; shift ;;
		-n)	_lf_nfs="$2"; shift ;;
		esac
		shift
	done
	if $test -z "$_lf_lockfile"; then
		$echo 1>&2 "$0: no lock file specified."
		exit
	fi
	_lf_pid=$$
	_lf_lockdir=`$dirname $_lf_lockfile`
	_lf_uniqfile=`$mktemp "$_lf_lockdir/.lock.$_lf_nfs.$_lf_pid.XXXXXX" 2>/dev/null` || return 1
	if $test -n "$_lf_nfs"; then
		{ $echo $_lf_pid; $echo $_lf_nfs; } > $_lf_uniqfile
	else
		$echo $_lf_pid > $_lf_uniqfile
	fi
	$link $_lf_uniqfile $_lf_lockfile 2>/dev/null
	if $test $_lf_uniqfile -ef $_lf_lockfile; then
		_lf_result=0
	else
		_lf_result=1
	fi
	$rm -f $_lf_uniqfile
	return $_lf_result
}

######################################################################
######################################################################
###
### Queue routines.  The queue is implemented as a set of variables
### that is unique to each queue name, thus the use of multiple queues
### is allowed.
###
######################################################################
######################################################################

######################################################################
# init_queue name
#	Initialize the named queue.
######################################################################
init_queue()
{
	_qname="$1"
	eval "_q${_qname}head=1111111111"
	eval "_q${_qname}tail=1111111111"
}

######################################################################
# append_queue name item ...
#	Append items onto the end of the named queue in FIFO order.
######################################################################
append_queue()
{
	: ${test=test}

	_qname="$1"; shift
	while $test $# -gt 0; do
		eval "_qtail=\"\$_q${_qname}tail\""
		eval "_q${_qname}${_qtail}=\"\${1}\""
		case $_qtail in
		*000000000)	_qtail=${_qtail%000000000}1 ;;
		*)		_qtail=${_qtail}0 ;;
		esac
		eval "_q${_qname}tail=\"\${_qtail}\""
		shift
	done
}

######################################################################
# prepend_queue name item ...
#	Prepend items to the head of the named queue in LIFO order.
######################################################################
prepend_queue()
{
	: ${test=test}

	_qname="$1"; shift
	while $test $# -gt 0; do
		eval "_qhead=\"\$_q${_qname}head\""
		case $_qhead in
		*1)	_qhead=${_qhead%1}000000000 ;;
		*)	_qhead=${_qhead%0} ;;
		esac
		eval "_q${_qname}${_qhead}=\"\${1}\""
		eval "_q${_qname}head=\"\${_qhead}\""
		shift
	done
}

######################################################################
# head_queue name var
#	Return the head of the named queue in $var.
######################################################################
head_queue()
{
	_qname="$1"; shift
	eval "_qhead=\"\$_q${_qname}head\""
	eval "${1}=\"\$_q${_qname}${_qhead}\""
}

######################################################################
# pop_queue name var
#	Pop off the head of the named queue and return it in $var.
######################################################################
pop_queue()
{
	_qname="$1"; shift
	head_queue $_qname $1
	case $_qhead in
	*000000000)	_qhead=${_qhead%000000000}1 ;;
	*)		_qhead=${_qhead}0 ;;
	esac
	eval "_q${_qname}head=\"\${_qhead}\""
}

######################################################################
# queue_is_empty name
#	Return 0 if the named queue is empty and 1 otherwise.
######################################################################
queue_is_empty()
{
	: ${test=test}

	_qname="$1"
	eval "_qhead=\"\$_q${_qname}head\""
	eval "_qtail=\"\$_q${_qname}tail\""
	$test "$_qhead" = "$_qtail"
	return $?
}

######################################################################
######################################################################
###
### File queue routines.  The file queue is implemented as a file
### whose lines represent the queue elements.  The file queue name
### is simply the file used for the queue, thus the use of multiple
### queues is allowed.
###
######################################################################
######################################################################

######################################################################
# init_fqueue name
#	Initialize the named file queue.
######################################################################
init_fqueue()
{
	_fqname="$1"
	: > "$_fqname"
}

######################################################################
# append_fqueue name item ...
#	Append items onto the end of the named file queue in FIFO order.
######################################################################
append_fqueue()
{
	: ${echo=echo}
	: ${test=test}

	_fqname="$1"; shift
	while $test $# -gt 0; do
		$echo "$1" >> "$_fqname"
		shift
	done
}

######################################################################
# prepend_fqueue name item ...
#	Prepend items to the head of the named file queue in LIFO order.
######################################################################
prepend_fqueue()
{
	: ${cat=cat}
	: ${echo=echo}
	: ${mv=mv}

	_fqname="$1"; shift
	_fqtmpfile="$_fqname.$$"
	init_queue _fqtmpqueue
	prepend_queue _fqtmpqueue "$@"
	while ! queue_is_empty _fqtmpqueue; do
		pop_queue _fqtmpqueue _fqelt
		$echo "$_fqelt" >> "$_fqtmpfile"
	done
	$cat "$_fqname" >> "$_fqtmpfile"
	$mv -f "$_fqtmpfile" "$_fqname"
}

######################################################################
# head_fqueue name var
#	Return the head of the named file queue in $var.
######################################################################
head_fqueue()
{
	: ${head=head}

	_fqname="$1"; shift
	_tmp=`$head -n 1 "$_fqname"`
	eval "${1}=\"\$_tmp\""
}

######################################################################
# pop_fqueue name var
#	Pop off the head of the named file queue and return it in $var.
######################################################################
pop_fqueue()
{
	: ${mv=mv}
	: ${sed=sed}

	_fqname="$1"; shift
	_fqtmpfile="$_fqname.$$"
	head_fqueue "$_fqname" $1
	$sed "1,1d" "$_fqname" >> "$_fqtmpfile"
	$mv -f "$_fqtmpfile" "$_fqname"
}

######################################################################
# fqueue_is_empty name
#	Return 0 if the named file queue is empty and >0 otherwise.
######################################################################
fqueue_is_empty()
{
	: ${test=test}
	: ${wc=wc}

	_fqname="$1"
	if $test -f "$_fqname"; then
		_fqlines=`$wc -l < "$_fqname"`
		return $_fqlines
	else
		return 1
	fi
}