#!/bin/sh

# Copyright (c) 2012 - 2014 dak180 and contributors. See
# http://opensource.org/licenses/mit-license.php or the included
# COPYING.md for licence terms.
#
# autorevision - extracts metadata about the head version from your
# repository.

# Usage message.

export LANGUAGE=C

arUsage() {
	cat > "/dev/stderr" << EOF
usage: ./autorevision {-t output-type | -s symbol} [-o cache-file [-f] ] [-V]
	Options include:
	-t output-type		= specify output type
	-s symbol		= specify symbol output
	-o cache-file		= specify cache file location
	-f			= force the use of cache data
	-U			= check for untracked files in svn
	-V			= emit version and exit
	-?			= help message

The folowing are valid output types:
	h			= Header for use with c/c++
	xcode			= Header useful for populating info.plist files
	sh			= Bash sytax
	py			= Python file
	pl			= Perl file
	lua			= Lua file
	php			= PHP file
	ini			= INI file
	js			= javascript file
	json			= JSON file
	java			= Java file
	javaprop		= Java properties file
	tex			= (La)TeX file
	m4			= m4 file

The following are valid symbols:
	VCS_TYPE
	VCS_BASENAME
	VCS_UUID
	VCS_NUM
	VCS_DATE
	VCS_BRANCH
	VCS_TAG
	VCS_TICK
	VCS_EXTRA
	VCS_FULL_HASH
	VCS_SHORT_HASH
	VCS_WC_MODIFIED
EOF
	exit 1
}

# Config
ARVERSION="&&ARVERSION&&"
TARGETFILE="/dev/stdout"
while getopts ":t:o:s:VfU" OPTION; do
	case "${OPTION}" in
		t)
			AFILETYPE="${OPTARG}"
		;;
		o)
			CACHEFILE="${OPTARG}"
		;;
		f)
			CACHEFORCE="1"
		;;
		s)
			VAROUT="${OPTARG}"
		;;
		U)
			UNTRACKEDFILES="1"
		;;
		V)
			echo "autorevision ${ARVERSION}"
			exit 0
		;;
		?)
			# If an unknown flag is used (or -?):
			arUsage
		;;
	esac
done

if [ ! -z "${VAROUT}" ] && [ ! -z "${AFILETYPE}" ]; then
	# If both -s and -t are specified:
	echo "error: Improper argument combination." 1>&2
	exit 1
elif [ -z "${VAROUT}" ] && [ -z "${AFILETYPE}" ]; then
	# If neither -s or -t are specified:
	arUsage
elif [ -z "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
	# If -f is specified without -o:
	arUsage
fi

# Make sure that the path we are given is one we can source
# (dash, we are looking at you).
if [ ! -z "${CACHEFILE}" ] && ! echo "${CACHEFILE}" | grep -q '^\.*/'; then
	CACHEFILE="./${CACHEFILE}"
fi


# Functions to extract data from different repo types.
# For git repos
gitRepo() {
	cd "$(git rev-parse --show-toplevel)"

	VCS_TYPE="git"

	VCS_BASENAME="$(basename "${PWD}")"

	VCS_UUID="$(git rev-list --max-parents=0 HEAD |head -n1 2>/dev/null)"
	if [ -z "${VCS_UUID}" ]; then
		VCS_UUID="$(git rev-list --topo-order HEAD | tail -n 1)"
	fi

	 # Is the working copy clean?
	test -z "$(git status --untracked-files=normal --porcelain)"
	VCS_WC_MODIFIED="${?}"

	# Enumeration of changesets
	VCS_NUM="$(git rev-list --count HEAD 2>/dev/null)"
	if [ -z "${VCS_NUM}" ]; then
		echo "warning: Counting the number of revisions may be slower due to an outdated git version less than 1.7.2.3. If something breaks, please update it." 1>&2
		VCS_NUM="$(git rev-list HEAD | wc -l)"
	fi

	# This may be a git-svn remote.  If so, report the Subversion revision.
	if [ -z "$(git config svn-remote.svn.url 2>/dev/null)" ]
	then
		# The full revision hash
		VCS_FULL_HASH="$(git rev-parse HEAD)"

		# The short hash
		VCS_SHORT_HASH="$(echo "${VCS_FULL_HASH}" | cut -b 1-7)"
	else
		# The git-svn revision number
		VCS_FULL_HASH="$(git svn find-rev HEAD)"
		VCS_SHORT_HASH="${VCS_FULL_HASH}"
	fi

	# Current branch
	VCS_BRANCH="$(git rev-parse --symbolic-full-name --verify "$(git name-rev --name-only --no-undefined HEAD 2>/dev/null)" 2>/dev/null | sed -e 's:refs/heads/::' | sed -e 's:refs/::')"

	# Cache the description
	local DESCRIPTION="$(git describe --long --tags 2>/dev/null)"

	# Current or last tag ancestor (empty if no tags)
	VCS_TAG="$(echo "${DESCRIPTION}" | sed -e "s:-g${VCS_SHORT_HASH}\$::" | sed -e 's:-[0-9]*$::')"

	# Distance to last tag or an alias of VCS_NUM if there is no tag
	if [ ! -z "${DESCRIPTION}" ]; then
		VCS_TICK="$(echo "${DESCRIPTION}" | sed -e "s:${VCS_TAG}-::" -e "s:-g${VCS_SHORT_HASH}::")"
	else
		VCS_TICK="${VCS_NUM}"
	fi

	# Date of the current commit
	VCS_DATE="$(git log -1 --pretty=format:%ci | sed -e 's: :T:' | sed -e 's: ::')"
}

# For hg repos
hgRepo() {
        cd "$(hg root)"

        VCS_TYPE="hg"

        VCS_BASENAME="$(basename "${PWD}")"

        VCS_UUID="$(hg log -r "0" -l 1 --template '{node}\n')"

        # Is the working copy clean?
        test -z "$(hg status -duram)"
        VCS_WC_MODIFIED="${?}"

        # Enumeration of changesets
        VCS_NUM="$(hg id -n | tr -d '+')"

        # The full revision hash
        VCS_FULL_HASH="$(hg log -l 1 --template '{node}\n')"

        # The short hash
        VCS_SHORT_HASH="$(hg id -i | tr -d '+')"

        # Current bookmark (bookmarks are roughly equivalent to git's branches)
        # or branch if no bookmark
        VCS_BRANCH="$(hg id -B | cut -d ' ' -f 1)"
        # Fall back to the branch if there are no bookmarks
        if [ -z "${VCS_BRANCH}" ]; then
                VCS_BRANCH="$(hg id -b)"
        fi

        # Current or last tag ancestor (excluding auto tags, empty if no tags)
        VCS_TAG="$(hg log -l 1 --template '{latesttag}\n' 2>/dev/null | sed -e 's:qtip::' -e 's:tip::' -e 's:qbase::' | cut -d ' ' -f 1)"

        # Distance to last tag or an alias of VCS_NUM if there is no tag
        if [ ! -z "${VCS_TAG}" ]; then
                VCS_TICK="$(hg log -l 1 --template '{latesttagdistance}\n' 2>/dev/null)"
        else
                VCS_TICK="${VCS_NUM}"
        fi

        # Date of the current commit
        VCS_DATE="$(hg log -r "${VCS_NUM}" -l 1 --template '{date|isodatesec}\n' 2>/dev/null | sed -e 's: :T:' | sed -e 's: ::')"
}

# For bzr repos
bzrRepo() {
	cd "$(bzr root)"

	VCS_TYPE="bzr"

	VCS_BASENAME="$(basename "${PWD}")"

	# Currently unimplemented because more investigation is needed.
	VCS_UUID=""

	# Is the working copy clean?
	bzr version-info --custom --template='{clean}\n' | grep -q '1'
	VCS_WC_MODIFIED="${?}"

	# Enumeration of changesets
	VCS_NUM="$(bzr revno)"

	# The full revision hash
	VCS_FULL_HASH="$(bzr version-info --custom --template='{revision_id}\n')"

	# The short hash
	VCS_SHORT_HASH="${VCS_NUM}"

	# Nick of the current branch
	VCS_BRANCH="$(bzr nick)"

	# Current or last tag ancestor (excluding auto tags, empty if no tags)
	VCS_TAG="$(bzr tags --sort=time | sed '/?$/d' | tail -n1 | cut -d ' ' -f1)"

	# Distance to last tag or an alias of VCS_NUM if there is no tag
	if [ ! -z "${VCS_TAG}" ]; then
		VCS_TICK="$(bzr log --line -r "tag:${VCS_TAG}.." | tail -n +2 | wc -l | sed -e 's:^ *::')"
	else
		VCS_TICK="${VCS_NUM}"
	fi

	# Date of the current commit
	VCS_DATE="$(bzr version-info --custom --template='{date}\n' | sed -e 's: :T:' | sed -e 's: ::')"
}

# For svn repos
svnRepo() {
	VCS_TYPE="svn"

	case "${PWD}" in
	/*trunk*|/*branches*|/*tags*)
		local fn="${PWD}"
		while [ "$(basename "${fn}")" != 'trunk' ] && [ "$(basename "${fn}")" != 'branches' ] && [ "$(basename "${fn}")" != 'tags' ] && [ "$(basename "${fn}")" != '/' ]; do
			local fn="$(dirname "${fn}")"
		done
		fn="$(dirname "${fn}")"
		if [ "${fn}" = '/' ]; then
			VCS_BASENAME="$(basename "${PWD}")"
		else
			VCS_BASENAME="$(basename "${fn}")"
		fi
		;;
	*) VCS_BASENAME="$(basename "${PWD}")" ;;
	esac

	VCS_UUID="$(svn info | sed -n -e 's:Repository UUID\: ::p')"

	# Cache svnversion output
	local SVNVERSION="$(svnversion)"

	# Is the working copy clean?
	echo "${SVNVERSION}" | grep -q "M"
	case "${?}" in
		0)
			VCS_WC_MODIFIED="1"
		;;
		1)
			if [ ! -z "${UNTRACKEDFILES}"]; then
			# `svnversion` does not detect untracked files and `svn status` is really slow, so only run it if we really have to.
				if [ -z "$(svn status)" ]; then
					VCS_WC_MODIFIED="0"
				else
					VCS_WC_MODIFIED="1"
				fi
			fi
		;;
	esac

	# Enumeration of changesets
	VCS_NUM="$(echo "${SVNVERSION}" | cut -d : -f 1 | sed -e 's:M::' -e 's:S::' -e 's:P::')"

	# The full revision hash
	VCS_FULL_HASH="${SVNVERSION}"

	# The short hash
	VCS_SHORT_HASH="${VCS_NUM}"

	# Current branch
	case "${PWD}" in
	/*trunk*|/*branches*|/*tags*)
		local lastbase=""
		local fn="${PWD}"
		while :
		do
			base="$(basename "${fn}")"
			if [ "${base}" = 'trunk' ]; then
				VCS_BRANCH='trunk'
				break
			elif [ "${base}" = 'branches' ] || [ "${base}" = 'tags' ]; then
				VCS_BRANCH="${lastbase}"
				break
			elif [ "${base}" = '/' ]; then
				VCS_BRANCH=""
				break
			fi
			local lastbase="${base}"
			local fn="$(dirname "${fn}")"
		done
		;;
	*) VCS_BRANCH="" ;;
	esac

	# Current or last tag ancestor (empty if no tags). But "current tag"
	# can't be extracted reliably because Subversion doesn't have tags the
	# way other VCSes do.
	VCS_TAG=""
	VCS_TICK=""

	# Date of the current commit
	VCS_DATE="$(svn info | sed -n -e 's:Last Changed Date\: ::p' | sed 's: (.*)::' | sed -e 's: :T:' | sed -e 's: ::')"
}


# Functions to output data in different formats.
# For header output
hOutput() {
	cat > "${TARGETFILE}" << EOF
/* Generated by autorevision - do not hand-hack! */
#ifndef AUTOREVISION_H
#define AUTOREVISION_H

#define VCS_TYPE		"${VCS_TYPE}"
#define VCS_BASENAME		"${VCS_BASENAME}"
#define VCS_UUID		"${VCS_UUID}"
#define VCS_NUM			${VCS_NUM}
#define VCS_DATE		"${VCS_DATE}"
#define VCS_BRANCH		"${VCS_BRANCH}"
#define VCS_TAG			"${VCS_TAG}"
#define VCS_TICK		${VCS_TICK}
#define VCS_EXTRA		"${VCS_EXTRA}"

#define VCS_FULL_HASH		"${VCS_FULL_HASH}"
#define VCS_SHORT_HASH		"${VCS_SHORT_HASH}"

#define VCS_WC_MODIFIED		${VCS_WC_MODIFIED}

#endif

/* end */
EOF
}

# A header output for use with xcode to populate info.plist strings
xcodeOutput() {
	cat > "${TARGETFILE}" << EOF
/* Generated by autorevision - do not hand-hack! */
#ifndef AUTOREVISION_H
#define AUTOREVISION_H

#define VCS_TYPE		${VCS_TYPE}
#define VCS_BASENAME		${VCS_BASENAME}
#define VCS_UUID		${VCS_UUID}
#define VCS_NUM			${VCS_NUM}
#define VCS_DATE		${VCS_DATE}
#define VCS_BRANCH		${VCS_BRANCH}
#define VCS_TAG			${VCS_TAG}
#define VCS_TICK		${VCS_TICK}
#define VCS_EXTRA		${VCS_EXTRA}

#define VCS_FULL_HASH		${VCS_FULL_HASH}
#define VCS_SHORT_HASH		${VCS_SHORT_HASH}

#define VCS_WC_MODIFIED		${VCS_WC_MODIFIED}

#endif

/* end */
EOF
}

# For bash output
shOutput() {
	cat > "${TARGETFILE}" << EOF
# Generated by autorevision - do not hand-hack!

VCS_TYPE="${VCS_TYPE}"
VCS_BASENAME="${VCS_BASENAME}"
VCS_UUID="${VCS_UUID}"
VCS_NUM=${VCS_NUM}
VCS_DATE="${VCS_DATE}"
VCS_BRANCH="${VCS_BRANCH}"
VCS_TAG="${VCS_TAG}"
VCS_TICK=${VCS_TICK}
VCS_EXTRA="${VCS_EXTRA}"

VCS_FULL_HASH="${VCS_FULL_HASH}"
VCS_SHORT_HASH="${VCS_SHORT_HASH}"

VCS_WC_MODIFIED=${VCS_WC_MODIFIED}

# end
EOF
}

# For Python output
pyOutput() {
	case "${VCS_WC_MODIFIED}" in
		0) VCS_WC_MODIFIED="False" ;;
		1) VCS_WC_MODIFIED="True" ;;
	esac
	cat > "${TARGETFILE}" << EOF
# Generated by autorevision - do not hand-hack!

VCS_TYPE = "${VCS_TYPE}"
VCS_BASENAME = "${VCS_BASENAME}"
VCS_UUID = "${VCS_UUID}"
VCS_NUM = ${VCS_NUM}
VCS_DATE = "${VCS_DATE}"
VCS_BRANCH = "${VCS_BRANCH}"
VCS_TAG = "${VCS_TAG}"
VCS_TICK = ${VCS_TICK}
VCS_EXTRA = "${VCS_EXTRA}"

VCS_FULL_HASH = "${VCS_FULL_HASH}"
VCS_SHORT_HASH = "${VCS_SHORT_HASH}"

VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}

# end
EOF
}

# For Perl output
plOutput() {
	cat << EOF
# Generated by autorevision - do not hand-hack!

\$VCS_TYPE = "${VCS_TYPE}";
\$VCS_BASENAME = "${VCS_BASENAME}"
\$VCS_UUID = "${VCS_UUID}"
\$VCS_NUM = ${VCS_NUM};
\$VCS_DATE = "${VCS_DATE}";
\$VCS_BRANCH = "${VCS_BRANCH}";
\$VCS_TAG = "${VCS_TAG}";
\$VCS_TICK = ${VCS_TICK};
\$VCS_EXTRA = "${VCS_EXTRA}";

\$VCS_FULL_HASH = "${VCS_FULL_HASH}";
\$VCS_SHORT_HASH = "${VCS_SHORT_HASH}";

\$VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};

# end
EOF
}

# For lua output
luaOutput() {
	case "${VCS_WC_MODIFIED}" in
		0) VCS_WC_MODIFIED="false" ;;
		1) VCS_WC_MODIFIED="true" ;;
	esac
	cat > "${TARGETFILE}" << EOF
-- Generated by autorevision - do not hand-hack!

VCS_TYPE = "${VCS_TYPE}"
VCS_BASENAME = "${VCS_BASENAME}"
VCS_UUID = "${VCS_UUID}"
VCS_NUM = ${VCS_NUM}
VCS_DATE = "${VCS_DATE}"
VCS_BRANCH = "${VCS_BRANCH}"
VCS_TAG = "${VCS_TAG}"
VCS_TICK = ${VCS_TICK}
VCS_EXTRA = "${VCS_EXTRA}"

VCS_FULL_HASH = "${VCS_FULL_HASH}"
VCS_SHORT_HASH = "${VCS_SHORT_HASH}"

VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}

-- end
EOF
}

# For php output
phpOutput() {
	case "${VCS_WC_MODIFIED}" in
		0) VCS_WC_MODIFIED="false" ;;
		1) VCS_WC_MODIFIED="true" ;;
	esac
	cat > "${TARGETFILE}" << EOF
<?php
# Generated by autorevision - do not hand-hack!

$GLOBALS["VCS_TYPE"] = "${VCS_TYPE}";
$GLOBALS["VCS_BASENAME"] = "${VCS_BASENAME}";
$GLOBALS["VCS_UUID"] = "${VCS_UUID}";
$GLOBALS["VCS_NUM"] = ${VCS_NUM};
$GLOBALS["VCS_DATE"] = "${VCS_DATE}";
$GLOBALS["VCS_BRANCH"] = "${VCS_BRANCH}";
$GLOBALS["VCS_TAG"] = "${VCS_TAG}";
$GLOBALS["VCS_TICK"] = ${VCS_TICK};
$GLOBALS["VCS_EXTRA"] = "${VCS_EXTRA}";

$GLOBALS["VCS_FULL_HASH"] = "${VCS_FULL_HASH}";
$GLOBALS["VCS_SHORT_HASH"] = "${VCS_SHORT_HASH}";

$GLOBALS["VCS_WC_MODIFIED"] = ${VCS_WC_MODIFIED};

# end
?>
EOF
}

# For ini output
iniOutput() {
	case "${VCS_WC_MODIFIED}" in
		0) VCS_WC_MODIFIED="false" ;;
		1) VCS_WC_MODIFIED="true" ;;
	esac
	cat > "${TARGETFILE}" << EOF
; Generated by autorevision - do not hand-hack!
[VCS]
VCS_TYPE = "${VCS_TYPE}"
VCS_BASENAME = "${VCS_BASENAME}"
VCS_UUID = "${VCS_UUID}"
VCS_NUM = ${VCS_NUM}
VCS_DATE = "${VCS_DATE}"
VCS_BRANCH = "${VCS_BRANCH}"
VCS_TAG = "${VCS_TAG}"
VCS_TICK = ${VCS_TICK}
VCS_EXTRA = "${VCS_EXTRA}"
VCS_FULL_HASH = "${VCS_FULL_HASH}"
VCS_SHORT_HASH = "${VCS_SHORT_HASH}"
VCS_WC_MODIFIED = ${VCS_WC_MODIFIED}
; end
EOF
}

# For javascript output
jsOutput() {
	case "${VCS_WC_MODIFIED}" in
		1) VCS_WC_MODIFIED="true" ;;
		0) VCS_WC_MODIFIED="false" ;;
	esac
	cat > "${TARGETFILE}" << EOF
/** Generated by autorevision - do not hand-hack! */

var autorevision = {
	VCS_TYPE: "${VCS_TYPE}",
	VCS_BASENAME: "${VCS_BASENAME}",
	VCS_UUID: "${VCS_UUID}",
	VCS_NUM: ${VCS_NUM},
	VCS_DATE: "${VCS_DATE}",
	VCS_BRANCH: "${VCS_BRANCH}",
	VCS_TAG: "${VCS_TAG}",
	VCS_TICK: ${VCS_TICK},
	VCS_EXTRA: "${VCS_EXTRA}",

	VCS_FULL_HASH: "${VCS_FULL_HASH}",
	VCS_SHORT_HASH: "${VCS_SHORT_HASH}",

	VCS_WC_MODIFIED: ${VCS_WC_MODIFIED}
};

/** Node.js compatibility */
if (typeof module !== 'undefined') {
	module.exports = autorevision;
}

/** end */
EOF
}

# For JSON output
jsonOutput() {
	case "${VCS_WC_MODIFIED}" in
		1) VCS_WC_MODIFIED="true" ;;
		0) VCS_WC_MODIFIED="false" ;;
	esac
	cat > "${TARGETFILE}" << EOF
{
	"VCS_TYPE": "${VCS_TYPE}",
	"VCS_BASENAME": "${VCS_BASENAME}",
	"VCS_UUID": "${VCS_UUID}",
	"VCS_NUM": ${VCS_NUM},
	"VCS_DATE": "${VCS_DATE}",
	"VCS_BRANCH":"${VCS_BRANCH}",
	"VCS_TAG": "${VCS_TAG}",
	"VCS_TICK": ${VCS_TICK},
	"VCS_EXTRA": "${VCS_EXTRA}",

	"VCS_FULL_HASH": "${VCS_FULL_HASH}",
	"VCS_SHORT_HASH": "${VCS_SHORT_HASH}",

	"VCS_WC_MODIFIED": ${VCS_WC_MODIFIED}
}
EOF
}

# For Java output
javaOutput() {
	case "${VCS_WC_MODIFIED}" in
		1) VCS_WC_MODIFIED="true" ;;
		0) VCS_WC_MODIFIED="false" ;;
	esac
	cat > "${TARGETFILE}" << EOF
/* Generated by autorevision - do not hand-hack! */

public class autorevision {
    public static final String VCS_TYPE = "${VCS_TYPE}";
    public static final String VCS_BASENAME = "${VCS_BASENAME}";
    public static final String VCS_UUID = "${VCS_UUID}";
    public static final long VCS_NUM = ${VCS_NUM};
    public static final String VCS_DATE = "${VCS_DATE}";
    public static final String VCS_BRANCH = "${VCS_BRANCH}";
    public static final String VCS_TAG = "${VCS_TAG}";
    public static final long VCS_TICK = ${VCS_TICK};
    public static final String VCS_EXTRA = "${VCS_EXTRA}";

    public static final String VCS_FULL_HASH = "${VCS_FULL_HASH}";
    public static final String VCS_SHORT_HASH = "${VCS_SHORT_HASH}";

    public static final boolean VCS_WC_MODIFIED = ${VCS_WC_MODIFIED};
}
EOF
}

# For Java properties output
javapropOutput() {
	case "${VCS_WC_MODIFIED}" in
		1) VCS_WC_MODIFIED="true" ;;
		0) VCS_WC_MODIFIED="false" ;;
	esac
	cat > "${TARGETFILE}" << EOF
# Generated by autorevision - do not hand-hack!

VCS_TYPE=${VCS_TYPE}
VCS_BASENAME=${VCS_BASENAME}
VCS_UUID=${VCS_UUID}
VCS_NUM=${VCS_NUM}
VCS_DATE=${VCS_DATE}
VCS_BRANCH=${VCS_BRANCH}
VCS_TAG=${VCS_TAG}
VCS_TICK=${VCS_TICK}
VCS_EXTRA=${VCS_EXTRA}

VCS_FULL_HASH=${VCS_FULL_HASH}
VCS_SHORT_HASH=${VCS_SHORT_HASH}

VCS_WC_MODIFIED=${VCS_WC_MODIFIED}
EOF
}

# For m4 output
m4Output() {
	cat > "${TARGETFILE}" << EOF
define(\`VCS_TYPE', \`${VCS_TYPE}')dnl
define(\`VCS_BASENAME', \`${VCS_BASENAME}')dnl
define(\`VCS_UUID', \`${VCS_UUID}')dnl
define(\`VCS_NUM', \`${VCS_NUM}')dnl
define(\`VCS_DATE', \`${VCS_DATE}')dnl
define(\`VCS_BRANCH', \`${VCS_BRANCH}')dnl
define(\`VCS_TAG', \`${VCS_TAG}')dnl
define(\`VCS_TICK', \`${VCS_TICK}')dnl
define(\`VCS_EXTRA', \`${VCS_EXTRA}')dnl
define(\`VCS_FULLHASH', \`${VCS_FULL_HASH}')dnl
define(\`VCS_SHORTHASH', \`${VCS_SHORT_HASH}')dnl
define(\`VCS_WC_MODIFIED', \`${VCS_WC_MODIFIED}')dnl
EOF
}

# For (La)TeX output
texOutput() {
	case "${VCS_WC_MODIFIED}" in
		0) VCS_WC_MODIFIED="false" ;;
		1) VCS_WC_MODIFIED="true" ;;
	esac
	cat > "${TARGETFILE}" << EOF
% Generated by autorevision - do not hand-hack!
\def \vcsType {${VCS_TYPE}}
\def \vcsBasename {${VCS_BASENAME}}
\def \vcsUUID {${VCS_UUID}}
\def \vcsNum {${VCS_NUM}}
\def \vcsDate {${VCS_DATE}}
\def \vcsBranch {${VCS_BRANCH}}
\def \vcsTag {${VCS_TAG}}
\def \vcsTick {${VCS_TICK}}
\def \vcsExtra {${VCS_EXTRA}}
\def \vcsFullHash {${VCS_FULL_HASH}}
\def \vcsShortHash {${VCS_SHORT_HASH}}
\def \vcsWCModified {${VCS_WC_MODIFIED}}
\endinput
EOF
}



# Helper functions
# Count path segments
pathSegment() {
	local pathz="${1}"
	local depth="0"

	if [ ! -z "${pathz}" ]; then
		while [ ! "${pathz}" = "/" ] && [ ! "${pathz}" = "." ]; do
			pathz="$(dirname "${pathz}")"
			depth="$(expr ${depth} + 1)"
		done
	fi
	echo ${depth}
}

# Largest of four numbers
multiCompare() {
	local larger="${1}"
	local numA="${2}"
	local numB="${3}"
	local numC="${4}"

	[ "${numA}" -gt "${larger}" ] && larger="${numA}"
	[ "${numB}" -gt "${larger}" ] && larger="${numB}"
	[ "${numC}" -gt "${larger}" ] && larger="${numC}"
	echo "${larger}"
}

# Test for repositories
repoTest() {
	REPONUM="0"
	if [ ! -z "$(which git 2>/dev/null)" ] && [ ! -z "$(git rev-parse HEAD 2>/dev/null)" ]; then
		local gitPath="$(git rev-parse --show-toplevel)"
		local gitDepth="$(pathSegment "${gitPath}")"
		REPONUM="$(expr ${REPONUM} + 1)"
	else
		local gitDepth="0"
	fi
	if [ ! -z "$(which hg 2>/dev/null)" ] && [ ! -z "$(hg root 2>/dev/null)" ]; then
		local hgPath="$(hg root 2>/dev/null)"
		local hgDepth="$(pathSegment "${hgPath}")"
		REPONUM="$(expr ${REPONUM} + 1)"
	else
		local hgDepth="0"
	fi
	if [ ! -z "$(which bzr 2>/dev/null)" ] && [ ! -z "$(bzr root 2>/dev/null)" ]; then
		local bzrPath="$(bzr root 2>/dev/null)"
		local bzrDepth="$(pathSegment "${bzrPath}")"
		REPONUM="$(expr ${REPONUM} + 1)"
	else
		local bzrDepth="0"
	fi
        if [ ! -z "$(which svn 2>/dev/null)" ] && [ ! -z "$(svn info 2>/dev/null)" ]; then
                local stringz="Working Copy Root Path: "
                if [ ! -z "`svn info | grep "${stringz}"`" ]; then
                        local svnPath="$(svn info | grep "${stringz}" | tr -d "${stringz}")"
                        local svnDepth="$(pathSegment "${svnPath}")"
                        REPONUM="$(expr ${REPONUM} + 1)"
                else
                        local svnPath="$(svn info | grep "URL: " | tr -d "${stringz}")"
                        local svnDepth="$(pathSegment "${svnPath}")"
                        REPONUM="$(expr ${REPONUM} + 1)"
                fi
        else
                local svnDepth="0"
        fi

	# Do not do more work then we have to.
	if [ "${REPONUM}" = "0" ]; then
		return
	fi

	# Figure out which repo is the deepest and use it.
	local wonRepo="$(multiCompare "${gitDepth}" "${hgDepth}" "${bzrDepth}" "${svnDepth}")"
	if [ "${wonRepo}" = "${gitDepth}" ]; then
		gitRepo
	elif [ "${wonRepo}" = "${hgDepth}" ]; then
		hgRepo
	elif [ "${wonRepo}" = "${bzrDepth}" ]; then
		bzrRepo
	elif [ "${wonRepo}" = "${svnDepth}" ]; then
		svnRepo
	fi
}



# Detect which repos we are in and gather data.
if [ -f "${CACHEFILE}" ] && [ "${CACHEFORCE}" = "1" ]; then
	# When requested only read from the cache to populate our symbols.
	. "${CACHEFILE}"
else
	# If a value is not set through the environment set VCS_EXTRA to nothing.
	if [ -z "${VCS_EXTRA}" ]; then
		VCS_EXTRA=""
	fi
	repoTest

	if [ -f "${CACHEFILE}" ] && [ "${REPONUM}" = "0" ]; then
		# We are not in a repo; try to use a previously generated cache to populate our symbols.
		. "${CACHEFILE}"
		# Do not overwrite the cache if we know we are not going to write anything new.
		CACHEFORCE="1"
	elif [ "${REPONUM}" = "0" ]; then
		echo "No repo or cache detected. Using empty revision file." 1>&2
		exit 1
	fi
fi


# -s output is handled here.
if [ ! -z "${VAROUT}" ]; then
	if [ "${VAROUT}" = "VCS_TYPE" ]; then
		echo "${VCS_TYPE}"
	elif [ "${VAROUT}" = "VCS_BASENAME" ]; then
		echo "${VCS_BASENAME}"
	elif [ "${VAROUT}" = "VCS_NUM" ]; then
		echo "${VCS_NUM}"
	elif [ "${VAROUT}" = "VCS_DATE" ]; then
		echo "${VCS_DATE}"
	elif [ "${VAROUT}" = "VCS_BRANCH" ]; then
		echo "${VCS_BRANCH}"
	elif [ "${VAROUT}" = "VCS_TAG" ]; then
		echo "${VCS_TAG}"
	elif [ "${VAROUT}" = "VCS_TICK" ]; then
		echo "${VCS_TICK}"
	elif [ "${VAROUT}" = "VCS_FULL_HASH" ]; then
		echo "${VCS_FULL_HASH}"
	elif [ "${VAROUT}" = "VCS_SHORT_HASH" ]; then
		echo "${VCS_SHORT_HASH}"
	elif [ "${VAROUT}" = "VCS_WC_MODIFIED" ]; then
		echo "${VCS_WC_MODIFIED}"
	else
		echo "error: Not a valid output symbol." 1>&2
		exit 1
	fi
fi


# Detect requested output type and use it.
if [ ! -z "${AFILETYPE}" ]; then
	if [ "${AFILETYPE}" = "h" ]; then
		hOutput
	elif [ "${AFILETYPE}" = "xcode" ]; then
		xcodeOutput
	elif [ "${AFILETYPE}" = "sh" ]; then
		shOutput
	elif [ "${AFILETYPE}" = "py" ] || [ "${AFILETYPE}" = "python" ]; then
		pyOutput
	elif [ "${AFILETYPE}" = "pl" ] || [ "${AFILETYPE}" = "perl" ]; then
		plOutput
	elif [ "${AFILETYPE}" = "lua" ]; then
		luaOutput
	elif [ "${AFILETYPE}" = "php" ]; then
		phpOutput
	elif [ "${AFILETYPE}" = "ini" ]; then
		iniOutput
	elif [ "${AFILETYPE}" = "js" ]; then
		jsOutput
	elif [ "${AFILETYPE}" = "json" ]; then
		jsonOutput
	elif [ "${AFILETYPE}" = "java" ]; then
		javaOutput
	elif [ "${AFILETYPE}" = "javaprop" ]; then
		javapropOutput
	elif [ "${AFILETYPE}" = "tex" ]; then
		texOutput
	elif [ "${AFILETYPE}" = "m4" ]; then
		m4Output
	else
		echo "error: Not a valid output type." 1>&2
		exit 1
	fi
fi


# If requested, make a cache file.
if [ ! -z "${CACHEFILE}" ] && [ ! "${CACHEFORCE}" = "1" ]; then
	TARGETFILE="${CACHEFILE}"
	shOutput
fi
