#!/bin/sh
# vim:ts=4:
# Generates a tag file for TCL code. Slow, but gets the job done.
#
# Written by Darren Hiebert <darren@hiebert.com>

program_name=`basename $0`
program_version="0.3"
program_author="Darren Hiebert"
author_email="darren@hiebert.com"
tmp_tagfile=/tmp/${program_name}.$$

usage="\
Usage: $program_name [-au] [-{f|o} tagfile] [--format=n] file(s)
  -a          append to current tag file
  -f tagfile  specify output tag file name (default=tags)
  -o          alternative for -f
  -u          unsorted
  --format=n  specify tag file format (default=2)
  --help      print this help message
"

# defaults
#
append=0
format=2
sorted=1
tagfile=tags
filelist=

# read options
#
getparam()
{
	if [ -n "$1" ]; then
		# set variable to word passed in
		echo "$2='$1'; opt="
	else
		# set variable to next word on command line
		echo "$2="'$1'"; shift"
	fi
}

finished=0
while [ $# -gt 0  -a  $finished -eq 0 ]
do
    case "$1" in
		--*)
			opt=`echo "$1" | cut -c 3-`
			shift
			opt_name=`echo "$opt" | awk -F= '{print $1}'`
			opt_value=`echo "$opt" | awk -F= '{print $2}'`
			case "$opt_name" in
				format) case "$opt_value" in
							1|2) format=$opt_value;;
							*) echo "--$opt: unsupported value" >&2; exit 1;;
						esac
						;;
				help)	echo "$usage"; exit 0;;
				*)		echo "$opt_name: unsupported option" >&2; exit 1;;
			esac
			;;
		-*)
			# chop off leading '-'
			opt=`echo "$1" | cut -c 2-`
			shift
			while [ -n "$opt" ]
			do
				opt_char=`echo "$opt" | cut -c 1`
				opt=`echo "$opt" | cut -c 2-`
				case "$opt_char" in
					a) append=1;;
					u) sorted=0;;
					o|f) eval `getparam "$opt" tagfile`;;
					*) echo "$opt: unsupported option" >&2; exit 1;;
				esac
			done
			;;
		*) filelist="$*"; break;;
    esac
done

if [ -z "$filelist" ] ;then
    echo "$usage" >&2; exit 1
fi

# awk program for generating tags
#
ext_flags=""
ttype=""
if [ $format -eq 2 ] ;then
    ext_flags=';\"	%s'
    ttype=", type"
fi
awkprg='
function trim_comment(string) {
    comment = index(string, "#")
    if (comment != 0)
	string = substr(string, 0, comment-1)
    return string
}
function maketag(tagname, pattern, type, line_end) {
    gsub(/\\/, "\\\\", pattern)
    gsub(/\//, "\\/", pattern)
    if (line_end)
	terminator="$"
    else
	terminator=""
    printf("%s\t%s\t/^%s%s/'"$ext_flags"'\n", \
		tagname, FILENAME, pattern, terminator'"$ttype"')
}
$1 == "proc"  &&  $3 ~ /^{/  {
    pattern = substr($0, 0, index($0, "{"))
    maketag($2, pattern, "f", 0)
}
/^set[ \t]/  &&  $2 !~ /\(/ {
    pattern = substr($0, 0, index($0, $2) + length($2))
    maketag($2, pattern, "v", 0)
}
/^array[ \t]*set[ \t]/  &&  $3 !~ /\(/ {
    pattern = substr($0, 0, index($0, $3) + length($3))
    maketag($3, pattern, "v", 0)
}'

# add or correct the pseudo tags
#
if [ "$tagfile" != "-" ] ;then
    if [ $append -eq 1 ]; then
	# ensure existing sort flag is correct
	sed -e "/^!_TAG_FILE_SORTED/s/	[01]	/	$sorted	/" \
	    -e "/^!_TAG_FILE_FORMAT/s/	1	/	$format	/" \
	    $tagfile > $tmp_tagfile
    else
	echo -ne "\
!_TAG_FILE_FORMAT	$format	/extended format; --format=1 will not append ;\" to lines/
!_TAG_FILE_SORTED	$sorted	/0=unsorted, 1=sorted/
!_TAG_PROGRAM_AUTHOR	$program_author	/$author_email/
!_TAG_PROGRAM_NAME	$program_name	//
!_TAG_PROGRAM_VERSION	$program_version	//
" > $tmp_tagfile
    fi
fi

# generate tags
#
awk "$awkprg" $filelist >> $tmp_tagfile

if [ $sorted -eq 1 ] ;then
    sort -u -o $tmp_tagfile $tmp_tagfile
fi

if [ "$tagfile" = '-' ] ;then
    cat $tmp_tagfile
else
    cp $tmp_tagfile $tagfile
fi
rm $tmp_tagfile

exit 0
