#!/usr/bin/ksh93

########################################################################
#                                                                      #
#               This software is part of the ast package               #
#                 Copyright (c) 2008-2012 Roland Mainz                 #
#                      and is licensed under the                       #
#                 Eclipse Public License, Version 1.0                  #
#                    by AT&T Intellectual Property                     #
#                                                                      #
#                A copy of the License is available at                 #
#          http://www.eclipse.org/org/documents/epl-v10.html           #
#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
#                                                                      #
#                                                                      #
#                 Roland Mainz <roland.mainz@nrubsig.org>              #
#                                                                      #
########################################################################

#
# Copyright (c) 2008, 2012, Roland Mainz. All rights reserved.
#

# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
export PATH='/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin'

# Make sure all math stuff runs in the "C" locale to avoid problems
# with alternative # radix point representations (e.g. ',' instead of
# '.' in de_DE.*-locales). This needs to be set _before_ any
# floating-point constants are defined in this script).
if [[ "${LC_ALL-}" != '' ]] ; then
	export \
		LC_MONETARY="${LC_ALL}" \
		LC_MESSAGES="${LC_ALL}" \
		LC_COLLATE="${LC_ALL}" \
		LC_CTYPE="${LC_ALL}"
		unset LC_ALL
fi
export LC_NUMERIC='C'


function usage
{
	OPTIND=0
	getopts -a "${progname}" "${multifollow_usage}" OPT '-?'
	exit 2
}

function main
{
	integer i
	compound -a files
	typeset pipebasedir
	typeset do_debug='false'
	typeset line
	integer running
	
	while getopts -a "${progname}" "${multifollow_usage}" OPT ; do 
		case "${OPT}" in
			'd')	do_debug='true' ;;
			'+d')	do_debug='false' ;;
			*)	usage ;;
		esac
	done
	shift $(( OPTIND-1 ))
	
	# expecting at least one more arguments
	(( $# >= 1 )) || usage
	
	if ! builtin -f 'libshell.so.1' poll ; then
		print -u2 -f $"poll builtin not found.\n"
		return 1
	fi

	${do_debug} && print -u2 -f 'parent PID=%d\n' $$
			
	# setup "tail -f" childs, FIFOs and information for the "poll" builtin
	pipebasedir="$(mktemp -d -t "multifollow_pipe_${PPID}_$$.XXXXXXX")"
	for (( i=0 ; $# > 0 ; i++ )) ; do
		nameref fn=files[i]
		fn=(
			typeset name="$1"
			typeset pipename="${pipebasedir}/${i}"
			integer childpid=-1
			
			# poll(1) information
			integer fd=-1
			typeset events='POLLIN'
			typeset revents=''
		)
		
		mkfifo "${fn.pipename}"
		
		/usr/bin/tail -f "${fn.name}" >"${fn.pipename}" &
		(( fn.childpid=$! ))
		
		redirect {fn.fd}< "${fn.pipename}"
		
		rm "${fn.pipename}"
		
		shift
	done
	rmdir "${pipebasedir}"

	# run event loop
	(( running=1))
	trap '(( running=0 ))' INT HUP

	for (( ; ${#files[@]} > 0 && running ; )) ; do
		for i in "${!files[@]}" ; do
			files[i].revents=''
		done

		# fixme: The "poll" builtin has a bug: the shell uses SIGARLM for it's
		# own purpose and sometimes this causes an interupted system call error
		# fixme: "poll" does not work with nameref'ed arrays
		poll files || continue
	
		${do_debug} && print -u2 -v files
	
		for i in "${!files[@]}" ; do
			nameref fn=files[i]

			if [[ "${fn.revents}" != '' ]] ; then
				# file gone ? If yes remove it's record...
				if [[ "${fn.revents}" == *POLLHUP* ]] ; then
					redirect {fn.fd}<&-
					unset fn
					continue
				fi

				# make sure we drain the outstanding data to
				# avoid that there are any data in the SFIO
				# buffers which "poll" can't see since it
				# only looks at the raw fd and _not_
				# into the SFIO buffers
				while read -t0 -u${fn.fd} line ; do
					printf $"#%d: %s\n" i "${line}"
				done
			fi
		done
	done

	# fixme: Should be TERM, not KILL
	for i in "${!files[@]}" ; do
		nameref fn=files[i]
		kill -s KILL ${fn.childpid}
		redirect {fn.fd}<&-
	done

	return 0
}

# program start
builtin basename
builtin cat
builtin mkfifo
builtin mktemp
builtin rm
builtin rmdir

set -o noglob
set -o nounset

typeset progname="$(basename "${0}")"

typeset -r multifollow_usage=$'+
[-?\n@(#)\$Id: multifollow (Roland Mainz) 2011-03-30 \$\n]
[-author?Roland Mainz <roland.mainz@nrubsig.org>]
[+NAME?multifollow - use tail -f on multiple files]
[+DESCRIPTION?\bmultifollow\b is a small utility which can "follow" multiple
	files similar to tail -f.]
[d:debug?Enable some debug code.]

file

[+SEE ALSO?\bksh93\b(1), \btail\b(1)]
'

main "$@"
exit $?
# EOF.
