#!/bin/sh
#*************************************************************************
#
#   $RCSfile: init-static-template-data,v $
#
#   $Revision: 1.2 $
#
#   last change: $Author: pluby $ $Date: 2001/02/24 19:11:37 $
#
#   The Contents of this file are made available subject to the terms of
#   either of the following licenses
#
#	  - GNU Lesser General Public License Version 2.1
#	  - Sun Industry Standards Source License Version 1.1
#
#   Sun Microsystems Inc., October, 2000
#
#   GNU Lesser General Public License Version 2.1
#   =============================================
#   Copyright 2000 by Sun Microsystems, Inc.
#   901 San Antonio Road, Palo Alto, CA 94303, USA
#
#   This library is free software; you can redistribute it and/or
#   modify it under the terms of the GNU Lesser General Public
#   License version 2.1, as published by the Free Software Foundation.
#
#   This library 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
#   Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public
#   License along with this library; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
#   MA  02111-1307  USA
#
#
#   Sun Industry Standards Source License Version 1.1
#   =================================================
#   The contents of this file are subject to the Sun Industry Standards
#   Source License Version 1.1 (the "License"); You may not use this file
#   except in compliance with the License. You may obtain a copy of the
#   License at http://www.openoffice.org/license.html.
#
#   Software provided under this License is provided on an "AS IS" basis,
#   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
#   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
#   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
#   See the License for the specific provisions governing your rights and
#   obligations concerning the Software.
#
#   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
#
#   Copyright: 2000 by Sun Microsystems, Inc.
#
#   All Rights Reserved.
#
#   Contributor(s): _______________________________________
#
#
#
#*************************************************************************

# Documentation
# -------------
#
# The purpose of this script is to search through a all shared libraries,
# archives, and object files with a list of directories and generate a C++
# C++ source file that instantiates all static data members of template classes
# found in those files.
#
# This script has the following usage:
# 	init-static-template-data [-Eexcluded-classes-file] [inputfile1] ... [inputfileN] sourcefile
#
# The excluded-classes-file argument should be a plain text file that contains
# a list of classes that need to be ignored by the setforwarddeclarations
# subroutine to avoid conflicts with the class declarations in #include files
# added in the various process-<classname>-symbols subroutines. The format of
# file is as follows:
#   # Comments are preceded by a "#" character
#   vos::ODynamicLoader # only the class name is needed, no template are needed
#   cppu::.*ImplHelper  # grep style regular expressions are supported
#
# The inputfile arguments should be executables, shared libraries, archives,
# or object files.
#
# The sourcefile argument is the C++ source file that will be generated
# by this script. Note that this script will also create a matching *.hxx
# file in the same directory as the sourcefile.

# Configuration
# -------------

# Supported template classes. These are the template classes with static data
# members for which we have implemented a processing subroutine. You can use
# regular expressions in the classnames assigned to this variable.
supportedclasses="vos::ODynamicLoader cppu::.*ImplHelper[0-9]* com::sun::star::uno::Sequence comphelper::OPropertyArrayUsageHelper comphelper::OIdPropertyArrayUsageHelper salhelper::ODynamicLoader" 

#
# Subroutines
# -----------

# Subroutine to clean up temporary files
cleanup ()
{
	rm -f "$tempfile" "$tempfile.undef" "$tempfile.def" "$tempfile.join" >/dev/null 2>&1
}

# Subroutine to print a general usage statement and an optional error statement
printerror ()
{
	if [ -n "$1" ]; then
		printf "init-static-template-data: error: $1\n"
	fi
	printf "init-static-template-data [-Eexcluded-classes-file] [inputfile1] ... [inputfileN] sourcefile\n" >&2
	cleanup
	exit 1
}

# Subroutine to unmangle template static data members with 10 or more template
# parameters. Since c++filt cannot handle such large numbers of template
# parameters, this subroutine parses a symbol and runs each embedded class
# through c++filt and then reassembles the unmangled pieces. The usage for
# this subroutine is as follows:
#    extended-c++filt symbol
# This subroutine prints the unmangled symbol name to stdout.
# Note that this subroutine cannot handle template classes that are nested
# into the template parameters.
extendedcppfilt ()
{
	local classdelimiter;
	local tokens;
	local paramcount;
	local classtoken;
	local paramtoken;
	local datamembertoken;
	local instance;
	local counter;
	local currentparam;
	local i;

	classdelimiter='ZQ'
	tokens=`printf "$1" | sed 's/'"$classdelimiter"'/ /g'`

	# Use class, first param class, and data member to generate a prototype
	# instance that c++filt can process
	paramcount=`printf "$tokens" | awk '{print NF - 1}'`
	classtoken=`printf "$tokens" | awk '{print $1}' | sed 's/'"$paramcount"'$//'`
	paramtoken=`printf "$tokens" | awk '{print $2}'`
	datamembertoken=`printf "$tokens" | awk '{print $NF}' | sed 's/^.*\.//'`

	# Unmangle name
	instance=`printf "$classtoken""1$classdelimiter$paramtoken.$datamembertoken\n" | c++filt -_`

	# Process remaining params and a them to prototype instance
	counter=0
	currentparam=""
	for i in $tokens; do
		if [ $counter = 0 -o $counter = 1 ]; then
			currentparam=""
		elif [ $counter = $paramcount ]; then
			currentparam=`printf "$i" | sed 's/\.'"$datamembertoken"'$//'`
		else
			currentparam="$i"
		fi
		if [ -n "$currentparam" ]; then
			# Get symbols constructor from c++filt and strip off duplicate
			# class name that is generated by c++filt
			currentparam=`printf "__._Q$currentparam" | c++filt -_ | sed 's/::\~.*$//'`
			instance=`printf "$instance" | sed 's/>::'"$datamembertoken"'$/, '"$currentparam"'&/'`
		fi
		counter=`expr $counter + 1`
	done

	# Print the result for the calling routine
	printf "$instance"

	return 0
}

# Subroutine to turn a header filename into an #include statement and add
# it to the target headerfile. The usage for this subroutine is as follows:
#    set-include-declaration
setincludedeclaration ()
{
	# Declare variables as local so they don't conflict with rest of script
	local includeline;
	local define;

	includeline="#include <$1>"
	define=`printf "INIT_STATIC_TEMPLATE_DATA_HEADER_$1" | sed 's/[\/\.]/_/g'`
	printf "#ifndef $define\n" >> "$headerfile"
	printf "#define $define\n" >> "$headerfile"
	printf "$includeline\n" >> "$headerfile"
	printf "#endif // $define\n" >> "$headerfile"

	return 0
}

# Subroutine to turn a class name into a class forward declaration and add
# it to the target classdecfile. The usage for this subroutine is as follows:
#    setforwarddeclarations classname
# This subroutine will take a fully qualified class name and generate lines of
# source code with the class forward declaration nested within the appropriate
# namespace declarations.
setforwarddeclarations ()
{
	# Declare variables as local so they don't conflict with rest of script
	local instance;
	local classname;
	local paramclasses;
	local paramtokens;
	local tokens;
	local classline;
	local templateline;
	local i;
	local j;
	local exclude;
	local define;
	local fulldefine;

	# Trim any leading and trailing characters
	instance=`printf "$1" | sed 's/^ *//' | sed 's/ *$//' | sed 's/^:://'`
	paramclasses=`printf "$instance" | sed 's/^[a-zA-Z0-9:]*<//' | sed 's/>$//'`
	paramtokens=""

	# Look forward nested template classes
	if printf "$instance" | grep -q '<.*>'; then
		paramtokens=`printf "$paramclasses" | sed 's/<.*>//' | sed 's/,/ /g'`
	fi

	# Recursively invoke this subroutine for each parameter class
	if [ "$paramclasses" != "$instance" ]; then
		# Need to use a "while read" loop as a space is used where a class has
		# a modifier (e.g. const, unsigned, etc.) so we cannot use a for loop
		paramclasses=`printf "$paramclasses" | tr ',' '\n'`
		while read i; do
			setforwarddeclarations "$i" "$2"
		done << !
`printf "$paramclasses"`
!
	fi

	# Check if this is an excluded class
	classname=`printf "$instance" | sed 's/<.*$//'`
	exclude=1
	for i in $supportedclasses $excludedclasses; do
		if printf "$classname" | grep -q -w "$i"; then
			exclude=0
			break
		fi
	done
	if [ $exclude = 0 ]; then
		return 0
	fi

	fulldefine=`printf "INIT_STATIC_TEMPLATE_DATA_CLASS_$classname" | sed 's/::/_/g'`
	classname=`printf "$classname" | sed 's/::/ /g'`
	tokens=`printf "$classname" | awk '{print NF}'`
	j=1;
	for i in $classname ; do
		if [ $j -lt $tokens ]; then
			classline="$classline namespace $i {"
		else
			classline="$classline class $i {};"
			define="$i"
		fi
		j=`expr $j + 1`
	done
	j=1
	while [ $j -lt $tokens ]; do
		classline="$classline }"
		j=`expr $j + 1`
	done

	# If this is a parameter class, we need to set $templateline
	templateline=""
	if [ -n "$paramtokens" ]; then
		templateline="template<"
		j=1
		for i in $paramtokens; do
			if [ $j -gt 1 ]; then
				templateline="$templateline,"
			fi
			templateline="$templateline class INIT_STATIC_TEMPLATE_PARAM_CLASS_$j"
		done
		templateline="$templateline >"
	fi

	# Trim any leading and trailing characters
	classline=`printf "$classline" | sed 's/^ *//' | sed 's/ *$//'`
	printf "#ifndef $fulldefine\n" >> "$classdecfile"
	printf "#define $fulldefine\n" >> "$classdecfile"
	printf "#undef $define\n" >> "$classdecfile"
	if [ -n "$templateline" ]; then
		printf "$templateline\n" >> "$classdecfile"
	fi
	printf "$classline\n" >> "$classdecfile"
	printf "#define $define INIT_STATIC_TEMPLATE_DATA_EXCLUDE_CLASS_$define\n" >> "$classdecfile"
	printf "#endif // $fulldefine\n" >> "$classdecfile"
	printf "#undef $define\n" >> "$sourcefile"
	printf "#undef $fulldefine\n" >> "$sourcefile"
	return 0
}

# Subroutine to turn a construct a static data member initialization statement
# and add it to the target sourcefile. The usage for this subroutine is as
# follows:
#    set-init-statement datatype symbol value
setinitstatement ()
{
	# Declare variables as local so they don't conflict with rest of script
	local initline;
	local define;

	if [ -z "$3" ]; then
		initline="$1 $2;"
	else
		initline="$1 $2 = $3;"
	fi
	define=`printf "INIT_STATIC_TEMPLATE_DATA_INSTANCE_$2" | sed 's/[:<>, ]/_/g'`
	printf "#ifndef $define\n" >> "$sourcefile"
	printf "#define $define\n" >> "$sourcefile"
	printf "$initline\n" >> "$sourcefile"
	printf "#endif // $define\n" >> "$sourcefile"

	return 0
}

# Subroutine to generate #include statements and source lines for instances of
# vos::ODynamicLoader. The usage for this subroutine is as follows:
#    process-vos::ODynamicLoader-symbols unmangled-symbol
# This subroutine will append any #includes, class forward declarations, and
# symbol initializations to the sourcefile that don't already exist.
processvosODynamicLoadersymbols ()
{
	# Declare variables as local so they don't conflict with rest of script
	local instance;

	instance=`printf "$1" | sed 's/>::.*$/>/'`
	setincludedeclaration "vos/dynload.hxx" '_VOS_DYNLOAD_HXX_'
	setforwarddeclarations "$instance"
	setinitstatement "vos::ORealDynamicLoader *" "$symbol" "NULL"
	return 0
}

processsalhelperODynamicLoadersymbols ()
{
	local instance;

	instance=`printf "$1" | sed 's/>::.*$/>/'`
	setincludedeclaration "salhelper/dynload.hxx" '_SALHELPER_DYNLOAD_HXX_'
	setincludedeclaration "registry/registry.hxx" '_REGISTRY_REGISTRY_HXX_'
	setforwarddeclarations "$instance"
	setinitstatement "salhelper::ORealDynamicLoader *" "$symbol" "NULL"
	return 0
}

# Subroutine to generate #include statements and source lines for instances of
# cppu::.*ImplHelper. The usage for this subroutine is as follows:
#    process-vos::ImplHelper-symbols unmangled-symbol
# This subroutine will append any #includes, class forward declarations, and
# symbol initializations to the sourcefile that don't already exist.
processcppuImplHelpersymbols ()
{
	# Declare variables as local so they don't conflict with rest of script
	local classname;
	local num;
	local value;
	local headername;
	local instance;

	classname=`printf "$1" | sed 's/<.*$//'`
	instance=`printf "$1" | sed 's/>::.*$/>/'`
	num=`printf "$classname" | sed 's/^.*ImplHelper//'`
	case "$classname" in
		cppu::ImplHelper[0-9]*)	value=0
			headername="cppuhelper/implbase$num.hxx";;
		cppu::WeakImplHelper[0-9]*)	value=1
			headername="cppuhelper/implbase$num.hxx";;
		cppu::WeakAggImplHelper[0-9]*)	value=2
			headername="cppuhelper/implbase$num.hxx";;
		cppu::WeakComponentImplHelper[0-9]*)	value=4
			headername="cppuhelper/compbase$num.hxx";;
		cppu::WeakAggComponentImplHelper[0-9]*)	value=3
			headername="cppuhelper/compbase$num.hxx";;
	esac
	setincludedeclaration "$headername"
	setforwarddeclarations "$instance"
	setinitstatement "cppu::ClassData$num" "$symbol" "cppu::ClassData$num($value)"
	return 0
}

# Subroutine to generate #include statements and source lines for instances of
# com::sun::star::uno::Sequence. The usage for this subroutine is as follows:
#    process-com::sun::star::uno::Sequence-symbols unmangled-symbol
# This subroutine will append any #includes, class forward declarations, and
# symbol initializations to the sourcefile that don't already exist.
processcomsunstarunoSequencesymbols ()
{
	# Declare variables as local so they don't conflict with rest of script
	local instance;

	instance=`printf "$1" | sed 's/>::.*$/>/'`
	setincludedeclaration "com/sun/star/uno/Sequence.hxx"
	setforwarddeclarations "$instance"
	setinitstatement "typelib_TypeDescriptionReference *" "$symbol" "0"
	return 0
}

# Subroutine to generate #include statements and source lines for instances of
# comphelper::OPropertyArrayUsageHelper. The usage for this subroutine is as
# follows:
#    process-comphelper::OPropertyArrayUsageHelper-symbols unmangled-symbol
# This subroutine will append any #includes, class forward declarations, and
# symbol initializations to the sourcefile that don't already exist.
processcomphelperOPropertyArrayUsageHelpersymbols ()
{
	# Declare variables as local so they don't conflict with rest of script
	local instance;
	local datamember;
	local datatype;
	local value;

	instance=`printf "$1" | sed 's/>::.*$/>/'`
	datamember=`printf "$1" | sed 's/^.*>:://'`
	setincludedeclaration "comphelper/proparrhlp.hxx"
	setforwarddeclarations "$instance"
	case "$datamember" in
		s_nRefCount)	datatype="sal_Int32"
			value="0";;
		s_pProps)		datatype="cppu::IPropertyArrayHelper *"
			value="NULL";;
		s_aMutex)		datatype="osl::Mutex"
			value="";;
	esac
	setinitstatement "$datatype" "$symbol" "$value"
	return 0
}

# Subroutine to generate #include statements and source lines for instances of
# comphelper::OIdPropertyArrayUsageHelper. The usage for this subroutine is as
# follows:
#    process-comphelper::OIdPropertyArrayUsageHelper-symbols unmangled-symbol
# This subroutine will append any #includes, class forward declarations, and
# symbol initializations to the sourcefile that don't already exist.
processcomphelperOIdPropertyArrayUsageHelpersymbols ()
{
	# Declare variables as local so they don't conflict with rest of script
	local instance;
	local datamember;
	local datatype;
	local value;

	instance=`printf "$1" | sed 's/>::.*$/>/'`
	datamember=`printf "$1" | sed 's/^.*>:://'`
	setincludedeclaration "comphelper/IdPropArrayHelper.hxx"
	setforwarddeclarations "$instance"
	case "$datamember" in
		s_nRefCount)	datatype="sal_Int32"
			value="0";;
		s_pMap)			datatype="comphelper::OIdPropertyArrayMap *"
			value="NULL";;
		s_aMutex)		datatype="osl::Mutex"
			value="";;
	esac
	setinitstatement "$datatype" "$symbol" "$value"
	return 0
}

#
# Main Code
# ---------

# Parse command line arguments
if [ $# = 0 ]; then
	printerror "incorrect number of arguments"
fi

# Parse arguments
excludedclasses="signed unsigned char short int long double float bool wchar_t"
inputfiles=""
sourcefile=""
classdecfile=""
headerfile=""
while [ $# -gt 0 ]; do
	if [ $# -gt 1 ]; then
		case "$1" in
			-E*)	excludedclassesfile=`printf -- "$1" | sed 's/^-E//'`
					if [ -f "$excludedclassesfile" ]; then
						# Get contents of file and remove comments
						excludedclasses="$excludedclasses"`cat "$excludedclassesfile" | tr -d "\015" | sed 's/#.*$//'`
					else
						printerror "excluded classes file $excludedclassesfile does not exist"
					fi;;
			*)		inputfiles="$inputfiles $1";;
		esac;
		shift
	else
		sourcefile="$1"
		classdecfile="`dirname $1`/`basename $1 .cxx`.hxx"
		headerfile="`dirname $1`/`basename $1 .cxx`.h"
		if [ ! -d "`dirname $sourcefile`" ]; then
			printerror "directory `dirname $sourcefile` does not exist"
		fi
		if [ ! -f "$sourcefile" ]; then
			touch -f "$sourcefile"
		fi
		if [ ! -f "$classdecfile" ]; then
			touch -f "$classdecfile"
		fi
		if [ ! -f "$headerfile" ]; then
			touch -f "$headerfile"
		fi
		if [ ! -f "$sourcefile" ]; then
			printerror "$sourcefile is not a file"
		fi
		if [ ! -f "$classdecfile" ]; then
			printerror "$classdecfile is not a file"
		fi
		if [ ! -f "$headerfile" ]; then
			printerror "$headerfile is not a file"
		fi
		shift
	fi
done

# Put some warning text at beginning of sourcefile
if [ ! -s "$sourcefile" ]; then
	printf "/* This file is automatically generated. Do not manually edit it. */\n" >> "$sourcefile"
	printf "#include \"`basename $classdecfile`\"\n" >> "$sourcefile"
	printf "#include \"`basename $headerfile`\"\n" >> "$sourcefile"
fi
if [ ! -s "$classdecfile" ]; then
	printf "/* This file is automatically generated. Do not manually edit it. */\n" >> "$classdecfile"
fi
if [ ! -s "$headerfile" ]; then
	printf "/* This file is automatically generated. Do not manually edit it. */\n" >> "$headerfile"
fi

# Get list of static data member symbols
tempfile=/tmp/`basename $0`.$$.`id -u`
rm -f "$tempfile"
/usr/bin/nm $inputfiles 2>/dev/null | grep ' __Q.*\..*$' | sort -u >"$tempfile"

# Get list of undefined static data member symbols
rm -f "$tempfile.undef"
grep ' U ' "$tempfile" | awk '{print $NF}' | sort -u >"$tempfile.undef"

# Get list of defined static data member symbols
rm -f "$tempfile.def"
grep -v ' U ' "$tempfile" | awk '{print $NF}' | sort -u >"$tempfile.def"

# Extract static data members that are not defined anywhere and unmangle their
# symbol names. Note that we grep out anything that is not a template class
# (i.e. has a ">::" sequence of characters.
rm -f "$tempfile"
rm -f "$tempfile.join"
join -v 1 "$tempfile.undef" "$tempfile.def" | c++filt -_ > "$tempfile.join"
cat "$tempfile.join" | grep -F '>::' > "$tempfile"
while read i; do
	if [ -n "$i" ]; then
		symbol=`extendedcppfilt "$i"`
	fi
	if [ -n "$symbol" ]; then
		printf "$symbol\n" >> $tempfile
	fi
done << !
`cat "$tempfile.join" | grep -x '__Q.*\..*'`
!
rm -f "$tempfile.join"

# If there are no uninitialized static data members, we are done
if [ ! -s "$tempfile" ]; then
	cleanup
	exit 0
fi

# Process uninitialized symbols into valid C++ declarations
while read symbol ; do
	classname=`printf "$symbol" | sed 's/<.*$//'`
	validsymbol=1
	for i in $supportedclasses; do
		if printf "$classname" | grep -q -x "$i"; then
			validsymbol=0
			break
		fi
	done
	if [ $validsymbol != 0 ]; then
		printerror "class $classname is not listed as a supported class in this script"
	fi
	case "$classname" in
		vos::ODynamicLoader) processvosODynamicLoadersymbols "$symbol";;
		salhelper::ODynamicLoader) processsalhelperODynamicLoadersymbols "$symbol";;
		cppu::*ImplHelper[0-9]*) processcppuImplHelpersymbols "$symbol";;
		com::sun::star::uno::Sequence) processcomsunstarunoSequencesymbols "$symbol";;
		comphelper::OPropertyArrayUsageHelper) processcomphelperOPropertyArrayUsageHelpersymbols "$symbol";;
		comphelper::OIdPropertyArrayUsageHelper) processcomphelperOIdPropertyArrayUsageHelpersymbols "$symbol";;
		*)	printerror "script does not have code to initialize static data member $symbol"
	esac
done < "$tempfile"

# Final cleanup
cleanup

exit 0
