#!/bin/sh

# Shell script with utilities for the modules of scc.
# Copyright (C) 2001-2004 Open Challenge B.V.
# Copyright (C) 2004 OpenEyeT Professional Services.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING.
# If not, write to the Free Software Foundation,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Contact information: www.OpenEyeT.nl/scc/index.html 

# This is a system module of scc, to call it separately in the
# proper environment, use: scc-collect -i -e <module_name>

# SCC-release:	1.6.26
# file-version:	$Revision: 1.28 $

export TMP_FILE=${SCC_TMP}/scc_utils_$$

# Use the following utilities for SunOS.
AWK=awk
if [ -x /usr/xpg4/bin/awk ]
then
	alias awk=/usr/xpg4/bin/awk
	AWK=/usr/xpg4/bin/awk
fi
if [ -x /usr/xpg4/bin/grep ]
then
	alias grep=/usr/xpg4/bin/grep
fi
if [ -x /usr/xpg4/bin/id ]
then
	alias id=/usr/xpg4/bin/id
fi
if [ -x /usr/xpg4/bin/df ]
then
	alias df=/usr/xpg4/bin/df
fi
if [ -x /usr/xpg4/bin/tr ]
then
	alias tr=/usr/xpg4/bin/tr
fi
export AWK

# Use a binary character '\001' as separator for sed substitute statements to avoid clashes.
if [ -z "${sed_sep}" ]
then
	sed_sep=$(awk 'END { printf( "%c", 1 ) }' /dev/null)
	export sed_sep
fi

# On a Mandrake MNF the command "file" is absent.
# In that case we ignore the check for binary contents of files.
bin_file_check="yes"
file_exe="$(which file 2>/dev/null)"
if [ ! -x "${file_exe}" ]
then
	bin_file_check=""
fi

# Add help-info to the snapshot.
# Syntax: scc_help_info <class>
# <class> can start with "fix:", "var:", "hlp:" or no prefix at all.
# Help-info is on stdin. Typical a here-document.
scc_help_info()
{
	case "${1}" in
	fix:*|var:*)	utils_h_class="hlp:${1#*:}"
			;;
	hlp:*)		utils_h_class="${1}"
			;;
	*)		utils_h_class="hlp:${1}"
			;;
	esac

	sed -e "s@^@${utils_h_class}@"

	return 0;
}

# Add the contents of the file to the snapshot.
# Syntax: scc_check_file <file> <prefix> [ <comment-char> ]
# Return:	0	file is present
# 		1	file is absent
scc_check_file()
{
	if [ $# -lt 2 -o $# -gt 3 ]
	then
		echo "Syntax error, use: scc_check_file <file> <prefix> [ <comment-char> ]" >&2
		echo "arg1: ${1}, arg2: ${2}" >&2
		return 1
	fi

	check=${2%%::*}
	if [ "${check}" = "${2}" ]
	then
		echo "improper prefix: ${2}, missing '::'" >&2
	fi

	if [ -f "${1}" ]
	then
		# Binary files do not look nice in the snapshots. Use file
		# to avoid files with binary data.
		# List with exceptions:
		# - file thinks that some files in /proc are empty
		#   so, do process empty files
		# - on HP-UX: /etc/default/fs: nettl binary Log file -version 17217
		# - RH uses: empty
		# - SunOS uses: empty file
		utils_action=1;
		if [ -n "${bin_file_check}" ]
		then
			f_type="$(file ${1} 2>/dev/null | head -1)"
			case "${f_type}" in
			*International?Language*)	utils_action=-1;;	# Ignore multibyte characterset files
			*cannot?open*)			utils_action=-1;;	# Ignore NFS-errors.
			*ASCII?text,*)			;;
			*text)				;;
			*empty)				;;
			*empty?file)			;;
			*executable?*?script)		;;
			*shell?script*)			;;
			*symbolic?link?to*)		;;
			/etc/default/fs*)		;;
			*xml*)				;;
			*XML*)				;;
			*)				utils_action=0;;
			esac
		fi

		if [ ${utils_action} -gt 0 ]
		then
			utils_class="${2}"
			if [ "${1}" != "${1%%:*}" ]		# Filename contains ":"
			then
				# The filename is probably part of the classification.
				# Replace ":" with "_" to avoid confusion with the class-separator.
				utils_new_name="$(echo "${1##*/}" | sed -e 's/:/_/g')"
				utils_class="$(echo "${2}" | sed -e "s@${1##*/}@${utils_new_name}@")"
			fi

			# Ignore comment and blank lines.
			${AWK} 'BEGIN {
					xml = 0;
					squelch = 0;
				}

				# Check if this is a XML file
				/^<\?xml version/ {
						if ( NR == 1 )
						{
							xml = 1
						}
					}

				# Ignore lines in non-XML files starting with comment char
				/^[ 	]*'"${3}"'/ {
						if ( xml == 0 && c != "" )
						{
							next;
						}
					}

				# General processing.
					{
						# Print non-empty non-XML lines
						if ( xml == 0 )
						{
							# This will also remove trailing blanks
							sub(/[ 	]*$/, "");
							if ( $0 != "" )
							{
								print p $0;
							}
							next
						}

						# every line that passes this point, is an xml file,
						# no further checks on xml var needed
						line = $0;
						pline = "";
						while ( line != "" )
						{
							if ( squelch == 0 )
							{
								# it seems we need to print something,
								# check for beginning of comment
								x = index (line, "<!--")
								if ( x > 0 )
								{
									pline = pline substr( line, 1, x - 1 );
									line = substr( line, x + 4 );
									squelch = 1;
								}
								else
								{
									pline = pline line;
									line = "";
								}
							}
							if ( squelch == 1 )
							{
								# it seems we are squelching comment,
								# check for end of that
								x = index( line, "-->" );
								if ( x > 0 )
								{
									line = substr( line, x + 3 );
									squelch = 0;
								}
								else
								{
									line = "";
								}
							}
						}
						sub(/[ 	]*$/, "", pline );
						if ( pline != "" )
						{
							print p pline;
						}
						next
					}'	c="${3}"		\
						p="${utils_class}"	\
						"${1}"

			scc_help_info "${utils_class}" <<-_X_
				Contents of file: ${1}
				Ignore blank lines and lines starting with character: ${3}
			_X_

			return 0;	# indicate logging of data in file.
		elif [ ${utils_action} -eq 0 ]
		then
			# Sometimes the file command tries to interpret too much.
			# Add a comment-line to get the result: data
			echo "fix:general::scc_check_file ignored non-ASCII file ${f_type}"
		fi
	fi

	return 1;			# indicate skip of (absent) file.
} 

# Encrypt stdin
# Syntax: scc_crypt
scc_crypt()
{
	# Encrypt stdin.
	if [ -x /usr/bin/md5sum ]
	then
		/usr/bin/md5sum	|
		sed -e 's/ .*//'
	elif [ -x /sbin/md5 ]
	then
		/sbin/md5
	elif [ -x /usr/bin/crypt ]
	then
		/usr/bin/crypt "${crypt_key}"	|
		cksum				|
		sed -e 's/ .*//'
	else
		# no encryption available, ignore data
		cat >/dev/null
	fi

	return 0;
}

# Add profiling data
# Syntax: scc_timing <message>
scc_timing()
{
	utils_now=$(date '+%H.%M.%S')
	utils_min=${utils_now%.*}
	utils_min=${utils_min#*.}
	utils_sec=${utils_now##*.}

	utils_tick_cur=$(( ${utils_sec#0} + ( 60 * ${utils_min#0} ) ))

	if [ ${utils_tick_cur} -lt ${tick_prev} ]
	then
		utils_runtime=$(( ${utils_tick_cur} + 3600 - ${tick_prev} ))
	else
		utils_runtime=$(( ${utils_tick_cur} - ${tick_prev} ))
	fi
	total_time=$(( ${total_time} + ${utils_runtime} ))
	printf "var:profiling::%s: %4d : %4d :%s\n" ${utils_now} ${utils_runtime} ${total_time} "${1}"

	tick_prev=${utils_tick_cur};

	return;
}

# Reformat the output of ls -l, replace the time by the year.
# The first 6 months after modification, the time is shown.
# After 6 months, the year is shown. Avoid unnessary differences between snapshots.
month_now=$(date '+%m')
year_now=$(date '+%Y')
scc_ls()
{
	awk '{ if ( NF > 8 ) print }'		|
	sed	-e 's/ Jan / 01 /'	\
		-e 's/ Feb / 02 /'	\
		-e 's/ Mar / 03 /'	\
		-e 's/ Apr / 04 /'	\
		-e 's/ May / 05 /'	\
		-e 's/ Jun / 06 /'	\
		-e 's/ Jul / 07 /'	\
		-e 's/ Aug / 08 /'	\
		-e 's/ Sep / 09 /'	\
		-e 's/ Oct / 10 /'	\
		-e 's/ Nov / 11 /'	\
		-e 's/ Dec / 12 /'		|
	${AWK} '{
		if ( $8 ~ "^[0-9][0-9][0-9][0-9]$" )
		{
			year=$8
		}
		else
		{
			year=j
			if ( $6 > m )
			{
				year--;
			}
		}
		printf( "%s:%s:%s:%s:%s:%d-%02d-%02d:%s %s %s\n",
			$1, $2, $3, $4, $5, year, $6, $7, $9, $10, $11 );
	}' j=${year_now} m=${month_now}

	return;
}

# Perform oracle query
# Requires: ${ORACLE_HOME} and ${ORACLE_USER}
# Syntax:
# - scc_oracle_query <sql-statement>
# - scc_oracle_query <query-file>
scc_oracle_query ()
{
	# This routine only works for Oracle 7.3 and later!
	ORA_USE_PROFILE=${ORA_USE_PROFILE:-0}

	# We create a shellscript that will be interpreted.
	# The structure of the shellscript contains a nested here-document:
	# su ${ORACLE_USER} <<EOF_SU
	# svrmgrl or sqlplus <<EOF_QRY
	# query statements
	# EOF_QRY
	# EOF_SU
	{
		# Oracle users with /bin/csh als default shell, use a different syntax to set the library path.
		utils_ora_shell="$(awk -F: '/^'"${ORACLE_USER}"':/	{ print $NF }' /etc/passwd)"
		case "${utils_ora_shell}" in
		*csh)	utils_lib_cmd="setenv LD_LIBRARY_PATH ${ORACLE_HOME}/lib;"
			;;
		*)	utils_lib_cmd="LD_LIBRARY_PATH=${ORACLE_HOME}/lib; export LD_LIBRARY_PATH;"
			;;
		esac

		if [ ${ORA_USE_PROFILE} -eq 1 ]
		then
			echo "su - ${ORACLE_USER} 2>/dev/null <<EOF_SU"
		else
			echo "su ${ORACLE_USER} 2>/dev/null <<EOF_SU"
		fi

		# Supply the query by means of a nested here-document.
		if [ -x ${ORACLE_HOME}/bin/svrmgrl -a -z "${force_sqlplus_query}" ]
		then
			echo "${ORACLE_HOME}/bin/svrmgrl <<EOF_QRY"
			echo "connect internal"
		else
			echo "${utils_lib_cmd}"
			echo "${ORACLE_HOME}/bin/sqlplus /nolog <<EOF_QRY"
			echo "connect / as sysdba"
			echo "set linesize 32000;"
			echo "set pagesize 32000;"
		fi

		if [ $# -eq 1 -a -f "${1}" ]
		then
			sed -e 's/\$/\\\\\\$/g' "${1}"	# query is supplied by means of a file
		else
			echo "$*"			# query is supplied by means of the command-line
		fi			|
		tr -d "\012"
		# Reduce multiline queries to single line to avoid continuation prompts (2> ...) in the output.
		echo ""				# add one CR

		echo "exit"
		echo "EOF_QRY"
		echo "EOF_SU"

		# the output of the script above, executed by sh below MUST be redirected
		# to the TMP file, because of strange behavior of Oracle 7 svrmgrl
	} | sh >${TMP_FILE} 2>/dev/null

	if [ -z "${force_sqlplus_query}" ]
	then
		grep -l "^no rows selected" ${TMP_FILE} >/dev/null 2>/dev/null
		if [ $? -ne 0 ]
		then
			sed	-e 's/SVRMGR> //'			\
				-e 's/SQL> //g'				\
				-e '1,/^Connected.$/d'			\
				-e '/^Server Manager complete/d'	\
				-e '/^Disconnected from Oracle/,$d'	\
				-e '/^[ 	]*$/d'			\
				-e 's/[ 	]*$//'			\
				-e '/^1 row selected/d'			\
				-e '/^no rows selected/d'		\
				-e '/^[0123456789]* rows selected/d' ${TMP_FILE}
		fi
	else
		cat ${TMP_FILE}
	fi

	rm ${TMP_FILE}

	return 0;
}

# Record Linux distribution
# Syntax:
# - no arguments: set variable SCC_LINUX_DISTRO
# - <class>: show contents of release file of Linux distribution
scc_linux_distro()
{
	SCC_LINUX_DISTRO=""

	for utils_file in	/etc/debian_version		\
				/etc/gentoo-release		\
				/etc/mandrake-release		\
				/etc/redhat-release		\
				/etc/slackware-version		\
				/etc/SLOX-release		\
				/etc/SuSE-release		\
				/etc/turbolinux-release		\
				/etc/UnitedLinux-release
	do
		if [ -s "${utils_file}" ]
		then
			# Remove "-release", "_version" or "-version" from basename of file.
			SCC_LINUX_DISTRO="${utils_file%-*}"
			SCC_LINUX_DISTRO="${SCC_LINUX_DISTRO%_*}"
			SCC_LINUX_DISTRO="${SCC_LINUX_DISTRO##*/}"
			if [ $# -eq 1 ]
			then
				sed -e "s/^/${1}/" "${utils_file}"
			fi
		fi
	done

	return 0;
}

# On a RH73 Workstation, there is no "strings", replace the functionality by a function.
strings_exe="$(which strings 2>/dev/null)"
if [ ! -x "${strings_exe}" ]
then
	strings()
	{
		# Replace all binary nuls by a linefeed to avoid lines that are too long.
		if [ $# -eq 0 ]
		then
			tr -s "\000" "\012"
		else
			tr -s "\000" "\012" <$1
		fi							|
		tr -d -c "[_a-zA-Z \011\-/=\.\"\012]"

		return 0;
	}
fi

# Use the following utilities for SunOS.
if [ -x /usr/ccs/bin/what ]
then
	alias what=/usr/ccs/bin/what
else
	# On a RH73 Workstation, there is no "what", replace the functionality by a function.
	utils_what_exe="$(which what 2>/dev/null)"
	if [ ! -x "${utils_what_exe}" ]
	then
		what()
		{
			if [ -r "${1}" ]
			then
				echo "${1}:"
				strings "${1}"		|
				sed -n -e 's/^@(#)/	/p'

				return 0;
			else
				echo "can't open ${1}"

				return 1
			fi
		}
	fi
fi

# Do not exit
