#! /bin/sh

# verifycron - contrle un fichier crontab pour assurer qu'il est bien
#   format. Attend la notation cron standard de
#     min hr dom mon dow CMD
# o min est 0-59, hr est 0-23, dom est 1-31, mon est 1-12 (ou des noms 
#   de mois en anglais) et dow is 0-7 (ou des noms de jours de la
#   semaine en anglais).
# Les champs pourront tre des champs (a-e), des listes spares par des
#   virgules (a,c,z), ou un astrisque.
# Vous remarquerez que la notation d'incrments du cron Vixie (ex:
#   2-6/2) n'est pas reconnue par ce script.


validNum()
{
  # Renvoie 0 si c'est une valeur valide, 1 sinon. 
  # Spcifier un nombre et une valeur maximale en arguments. 
  num=$1   max=$2

  if [ "$num" = "X" ] ; then  
    return 0 
  elif [ ! -s $(echo $num | sed 's/[[:digit:]]//g') ] ; then  
    return 1 
  elif [ $num -lt 0 -o $num -gt $max ] ; then  
    return 1 
  else  
    return 0 
  fi 
}


validDay()
{
  # Renvoie 0 si c'est un nom de jour reconnu, 1 sinon

  case $(echo $1 | tr '[:upper:]' '[:lower:]') in
    sun*|mon*|tue*|wed*|thu*|fri*|sat*) return 0 ;;
    X) return 0 ;;   # cas particulier - c'est un *
    *) return 1
  esac 
}


validMon() 
{
  # Renvoie 0 si c'est un nom de mois reconnu, 1 sinon

  case $(echo $1 | tr '[:upper:]' '[:lower:]') in 
    jan*|feb*|mar*|apr*|may|jun*)  return 0  ;;
    jul*|aug*|sep*|oct*|nov*|dec*) return 0  ;;
    X) return 0 ;;   # cas particulier - c'est un *
    *) return 1 ;;
  esac
}


fixvars()
{
  # Transforme tous les * en X pour couper court aux problmes
  #   d'expansion du shell.
  # Sauvegarde l'entre originale sous le nom sourceline pour
  #   utilisation dans les messages d'erreur.

  sourceline="$min $hour $dom $mon $dow $command"
   min=$(echo "$min"  | tr '*' 'X')
  hour=$(echo "$hour" | tr '*' 'X')
   dom=$(echo "$dom"  | tr '*' 'X')
   mon=$(echo "$mon"  | tr '*' 'X')
   dow=$(echo "$dow"  | tr '*' 'X')
}


if [ $# -ne 1 ] || [ ! -r $1 ] ; then 
  echo "Syntaxe: $0 fichier_crontab" >&2; exit 1 
fi


lines=0  entries=0  totalerrors=0


while read min hour dom mon dow command
do 
  lines="$(( $lines + 1 ))" 
  errors=0

  if [ -z "$min" -o "${min%${min#?}}" = "#" ] ; then  
    continue    # rien  vrifier
  elif [ ! -z $(echo ${min%${min#?}} | sed 's/[[:digit:]]//') ] ; then  
    continue    # le premier caractre n'est pas un chiffre: on passe!
  fi


  entries="$(($entries + 1))"

  fixvars


  #### Tout est ventil en champs, les * sont remplacs par des X
  # Vrification minutieuse

  for minslice in $(echo "$min" | sed 's/[,-]/ /g') ; do
    if ! validNum $minslice 60 ; then
     echo "Ligne ${lines}: valeur de minute incorrecte $minslice"
     errors=1
    fi
  done


  # Contrle des heures
  for hrslice in $(echo "$hour" | sed 's/[,-]/ /g') ; do
    if ! validNum $hrslice 24 ; then
      echo "Ligne ${lines}: valeur d'heure incorrecte $hrslice"
      errors=1
    fi
  done


  # Contrle du jour du mois

  for domslice in $(echo $dom | sed 's/[,-]/ /g') ; do
    if ! validNum $domslice 31 ; then 
      echo "Ligne ${lines}: valeur de jour dans le mois incorrecte $domslice" 
      errors=1
    fi
  done


  # Contrle du mois

  for monslice in $(echo "$mon" | sed 's/[,-]/ /g') ; do
    if ! validNum $monslice 12 ; then
      if ! validMon "$monslice" ; then 
        echo "Ligne ${lines}: valeur de mois incorrecte $monslice" 
        errors=1
      fi
    fi
  done


  # Contrle du jour de la semaine

  for dowslice in $(echo "$dow" | sed 's/[,-]/ /g') ; do
    if ! validNum $dowslice 7 ; then
      if ! validDay $dowslice ; then 
        echo "Ligne ${lines}: valeur de jour dans la semaine incorrecte $dowslice" 
        errors=1
      fi
    fi
  done

  if [ $errors -gt 0 ] ; then
    echo ">>>> ${lines}: $sourceline"
    echo ""
    totalerrors="$(( $totalerrors + 1 ))"
  fi 
done < $1

echo "Termin. $totalerrors erreurs trouves dans $entries entres de crontab."

exit 0
