#!/bin/sh
# 
# tree-lint.sh - audit a source tree
################################################################
# Copyright (C) 2001, 2002 Tom Lord
# 
# See the file "COPYING" for further information about
# the copyright and warranty status of this work.
# 

set -e 
command_line="$*"

################################################################
# special options
# 
# Some options are special:
# 
#       --version | -V
#       --help | -h
# 
if test $# -ne 0 ; then

  for opt in "$@" ; do
    case $opt in

      --version|-V) exec larch --version
                    ;;


      --help|-h)
                printf "audit a source tree\\n"
                printf "usage: tree-lint [options] [dir] \\n"
                printf "\\n"
                printf " -V --version                  print version info\\n"
                printf " -h --help                     display help\\n"
                printf "\\n"
		printf "    --no-unrecognized          suppress the list of unrecognized files\\n"
		printf "    --no-untagged              suppress the list of untagged files\\n"
                printf "\\n"
                printf "Audit a source tree for missing files, untagged files, duplicate tags,\\n"
		printf "and files not matching recognized naming conventions.\\n"
                printf "\\n"
                exit 0
                ;;

      *)
                ;;
    esac
  done
fi

################################################################
# Ordinary Options
# 
# 

no_untagged=
no_unrecognized=

while test $# -ne 0 ; do

  case "$1" in 

    --no-untagged)	shift
    			no_untagged=yes
			;;

    --no-unrecognized)	shift
    			no_unrecognized=yes
			;;

    --)			shift
    			break
			;;
			
    -*)                 printf "tree-lint: unrecognized option (%s)\\n" "$1" 1>&2
                        printf "try --help\\n" 1>&2
                        exit 1
                        ;;

    *)                  break
                        ;;
  esac

done



################################################################
# Ordinary Arguments
# 

if test $# -gt 1 ; then
  printf "usage: tree-lint [options] [dir]\\n" 1>&2
  printf "try --help\\n" 1>&2
  exit 1
fi

if test $# -gt 0 ; then
  dir="$1"
  shift
else
  dir=.
fi

cd "$dir"
dir="`pwd`"

################################################################
# Sanity Check and Process Defaults
# 

cd "$dir"
wdroot="`larch tree-root`"

status=0

################################################################
# Making an Outline
#

larch heading "tree-lint\\n"
printf "arguments: %s\\n" "$command_line" | fold -w 60 | larch body-indent
larch heading --sub "tree-lint start time: %s\\n" "`date`"
larch heading --sub "directory: %s\\n" "$dir"
larch heading --sub "project tree root: %s\\n" "$wdroot"

page()
{
  title="$1"
  shift

  larch heading --sub "%s\\n" "$title"
  if test $# -gt 0 ; then
    printf "%s\\n" "$@" | larch body-indent --sub
    larch heading --sub --sub "file-list\\n"
  fi
}


################################################################
# Look for Ill-named Files
# 

if test -z "$no_unrecognized" ; then

  cd "$dir"

  larch inventory --names --unrecognized \
  | (   read first_line \
     && page "WARNING: unrecognized files" \
             "These files do not fit any of the \"arch\" naming conventions." \
     && ( printf "%s\\n" "$first_line" ; cat ) \
        | sed "s/^/    /" | larch body-indent --sub --sub ) \
  || true

fi


################################################################
# Look for Explicit Tags With No File 
# 

cd "$dir"

if larch inventory --names --source --all --directories \
   | grep -E -e ".arch-ids\$" \
   | xargs -n 1 need-args find \
   | sed -e "/\\.id\$/!d" -e "s,.arch-ids/\\([^/]*\\)\\.id\$,\\1," \
   | $ARCH_SHELL -c "while read file ; do if test ! -h \"\$file\" -a ! -e \"\$file\" ; then printf %s\\\\n \"\$file\" ; fi ; done" \
   | (   read first_line \
      && page "FATAL: missing files" \
   	 "Explicit tags exist for these files, but the files themselves" \
   	 "are missing."\
      && ( printf "%s\\n" "$first_line" ; cat )\
         | sed "s/^/    /" | larch body-indent --sub --sub ) ; then
  status=1
fi

################################################################
# Look for Symlinks With Nonexistant Targets
# 

cd "$dir"

   larch inventory --names --source --all --directories \
   | grep -E -e ".arch-ids\$" \
   | xargs -n 1 need-args find \
   | sed -e "/\\.id\$/!d" -e "s,.arch-ids/\\([^/]*\\)\\.id\$,\\1," \
   | $ARCH_SHELL -c "while read file ; do if test ! -e \"\$file\" -a -h \"\$file\" ; then printf %s\\\\n \"\$file\" ; fi ; done" \
   | (   read first_line \
      && page "WARNING: symlinks with missing targets" \
   	 "Explicit tags exist for these symlinks, but the targets of the links" \
   	 "are missing."\
      && ( printf "%s\\n" "$first_line" ; cat )\
         | sed "s/^/    /" | larch body-indent --sub --sub ) \
   || true



################################################################
# Look for Duplicate Tags
# 

cd "$dir"

if larch inventory --source --all --tags \
   | awk '{ if (seen[$2]) duplicates[$2] = yes } \
	  { seen[$2] = seen[$2] "\n" $1 } \
	  END { for (tag in duplicates) print seen[tag] "\n" }' \
   | (   read first_line \
      && page "FATAL: duplicate tags" \
   	 "Each group of files in this list have the same tag." \
   	 "This must be corrected before the files can be checked in."\
      && ( printf "%s\\n" "$first_line" ; cat )\
         | sed "s/^/    /" | larch body-indent --sub --sub ) ; then
  status=1
fi
	    

################################################################
# Look for Untagged Files That Pass Naming Conventions
# 


if test -z "$no_untagged" ; then

  cd "$dir"
  
  method=`larch tagging-method`
  
  case $method in 
  
    names)	;;
  
    implicit)	larch inventory --source --files --tags \
  		| grep -E -e "^[^[:space:]]*[[:space:]]+\\?" \
  		| sed -e "s/[[:space:]].*//" \
  		| (   read first_line \
  		   && page "WARNING: untagged files" \
  		 	   "This directory uses the \"implicit\" inventory method, but" \
  		   	   "these files do not have either implicit or explicit tags." \
  		   && ( printf "%s\\n" "$first_line" ; cat )\
  		      | sed "s/^/    /" | larch body-indent --sub --sub ) \
		|| true
  		;;
  
  
    explicit)   larch inventory --implicit --source --files --tags \
  		| grep -E -e "^[^[:space:]]*[[:space:]]+\\?" \
  		| sed -e "s/[[:space:]].*//" \
  		| (   read first_line \
  		   && page "WARNING: untagged files" \
  		   	   "This directory uses the \"explicit\" inventory method. These" \
  		   	   "files pass the naming conventions for source, but do not have" \
  			   "inventory tags." \
  		   && ( printf "%s\\n" "$first_line" ; cat ) \
  		      | sed "s/^/    /" | larch body-indent --sub --sub ) \
		|| true
  		;;
  
     *)		printf "tree-lint: internal error\\n" 1>&2
     		printf "  unrecognized tagging method: %s\\n" "$method" 1>&2
  		printf "\\n" 1>&2
  		exit 1
  		;;
  
  esac
  
fi


################################################################
# Sign Off
# 

larch heading --sub "tree-lint finish time: %s\\n" "`date`"

################################################################
# Reflect Lint Result in Exit Status
# 

exit $status
