#@@ luhn - check a number for validity using the Luhn formula #@ USAGE: luhn [-n|-v] NUMBER #@ NOTE: Non-digits are allowed in NUMBER and will be skipped over #@ OUTPUT: none #@ EXIT_CODE: 0 if NUMBER is valid, otherwise 1 #@ OPTIONS: -n Generate checksum digit for number supplied and print #@ -v Print OK or Not valid; with -n, -v has no effect case $1 in --help) printf "%s\n" \ "luhn - check a number for validity using the Luhn formula USAGE: luhn [-n|-v] NUMBER NOTE: Non-digits are allowed in NUMBER and will be skipped over OUTPUT: none unless -v is used EXIT_CODE: 0 if NUMBER is valid, otherwise 1 OPTIONS: -n Generate checksum digit for number supplied and print -v Print OK or Not valid; with -n, -v has no effect " exit esac _ld() #@@ Double a [single-digit] number and, if >10, add both digits { #@ USAGE: _ld N #@ RESULT: stored in $_LD #@ OUTPUT: none #@ RETURN: 0 if $1 is a single digit, otherwise 5 case $1 in 0) _LD=0 ;; 1) _LD=2 ;; 2) _LD=4 ;; 3) _LD=6 ;; 4) _LD=8 ;; 5) _LD=1 ;; 6) _LD=3 ;; 7) _LD=5 ;; 8) _LD=7 ;; 9) _LD=9 ;; *) _LD=; return 5 ;; esac } n=1 gen= verbose=0 while getopts vn opt do case $opt in n) gen=0 ;; v) verbose=$(( verbose + 1 )) ;; esac done shift $(( $OPTIND - 1 )) sum=0 #: If check digit generation is requested, tack on dummy digit, 0 number=$1$gen while [ -n "$number" ] do #: Split $number into last digit and everything else left=${number%?} #~ Everything but the last character digit=${number#"$left"} #~ The last character number=$left #~ Discard last character #: Skip non-digits case $digit in [0-9]) ;; *) continue ;; esac #: Calculate checksum case $n in *[24680]) _ld "${digit:-0}" || exit sum=$(( $sum + $_LD )) ;; *) sum=$(( $sum + ${digit:-0} )) ;; esac n=$(( $n + 1 )) done if [ -z "$gen" ] then case $sum in *0) [ "$verbose" -gt 0 ] && echo OK ; true ;; *) [ "$verbose" -gt 0 ] && echo Not valid ; false;; esac else printf "%s\n" $1$(( 10 - ( $sum % 10 ) )) fi