#!/usr/local/bin/expect --
# mkpasswd - make a password, if username given, set it.
# Author: Don Libes, NIST

# defaults
set length 9
set minnum 2
set minlower 2
set minupper 2
set verbose 0

if [file executable /bin/yppasswd] {
	set prog /bin/yppasswd
} else {
	set prog /bin/passwd
}

while {[llength $argv]>0} {
	set flag [lindex $argv 0]
	switch -- $flag \
	"-l" {
		set length [lindex $argv 1]
		set argv [lrange $argv 2 end]
	} "-d" {
		set minnum [lindex $argv 1]
		set argv [lrange $argv 2 end]
	} "-c" {
		set minlower [lindex $argv 1]
		set argv [lrange $argv 2 end]
	} "-C" {
		set minupper [lindex $argv 1]
		set argv [lrange $argv 2 end]
	} "-v" {
		set verbose 1
		set argv [lrange $argv 1 end]
	} "-p" {
		set prog [lindex $argv 1]
		set argv [lrange $argv 2 end]
	} default {
		set user [lindex $argv 0]
		set argv [lrange $argv 1 end]
		break
	}
}

if {[llength $argv]} {
	puts "usage: mkpasswd \[args] \[user]"
	puts "  where arguments are:"
	puts "    -l #      (length of password, default = $length)"
	puts "    -d #      (min # of digits, default = $minnum)"
	puts "    -c #      (min # of lowercase chars, default = $minlower)"
	puts "    -C #      (min # of uppercase chars, default = $minupper)"
	puts "    -v        (verbose, show passwd interaction)"
	puts "    -p prog   (program to set password, default = $prog)"
	exit 1
}

if {$minnum + $minlower + $minupper > $length} {
	puts "impossible to generate $length-character password\
		with $minnum numbers, $minlower lowercase letters,\
		and $minupper uppercase letters"
	exit 1
}

# if there is any underspecification, use additional lowercase letters
set minlower [expr $length - ($minnum + $minupper)]

set password ""

proc appendpass {c} {
	global password

	set password [linsert $password [rand [expr 1+[llength $password]]] $c]
}

set _ran [pid]

proc rand {m} {
	global _ran

	set period 233280
	set _ran [expr ($_ran*9301 + 49297) % $period]
	expr int($m*($_ran/double($period)))
}

for {set i 0} {$i<$minnum} {incr i} {
	appendpass [rand 10]
}

for {set i 0} {$i<$minlower} {incr i} {
	appendpass [format "%c" [expr 0x61 + [rand 26]]]
}

for {set i 0} {$i<$minupper} {incr i} {
	appendpass [format "%c" [expr 0x41 + [rand 26]]]
}

set password [join $password ""]

if {[info exists user]} {
	if {!$verbose} {
		log_user 0
	}

	spawn $prog $user
	expect {
		"assword:" {
			send "$password\r"
			exp_continue
		}
	}

	# if user isn't watching, check status
	if {!$verbose} {
		if {[lindex [wait] 3]} {
			puts -nonewline "$expect_out(buffer)"
			exit 1
		}
	}
}

if {$verbose} {
	puts -nonewline "password for $user is "
}
puts "$password"
