# 
# 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 "    --strict                   warnings cause non-0 exit\\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=
strict=0

while test $# -ne 0 ; do

  case "$1" in 

    --silent)           shift   # ignored
                        ;;
    --quiet)            shift   # ignored
                        ;;
    --report)           shift   # ignored
                        ;;
    --verbose)          shift   # ignored
                        ;;


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

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

    --strict)		shift
                        strict=1
                        ;;

    --)                 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"

  if 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 ) ; then

    status=$(($status || $strict))
  fi

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"

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 ! -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 ) ; then

  status=$(($status || $strict))
fi



################################################################
# 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)   if 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 ) ; then

                   status=$(($status || $strict))
                 fi
                ;;
  
  
    explicit)   if 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 ) ; then

                   status=$(($status || $strict))
                 fi
                ;;
  
     *)         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
