#!/bin/ash
#
#POSIXness v0.22 20000716
#GPL2 -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
#
#Uses the fullest extent of ash, sed, and custom busybox functions
#to provide many of the linux goodies that will not fit on boot
#floppies. Helps give you that 'real machine' feel...
#
#Some of the sed code is stolen from various FAQ's, etc
#
#Some of these functions are LRP oddites, you probably can't use.

# This script is dedicated to Phil Hands and Paul Russell 
# I hope to amaze and inspire people with the power of the bourne
# shell the same way you guys did for me when I was just a lowly 
# scumbag neophyte begging for a clue. 
# Much thanks for your continuing help with LRP.

OIFS=$IFS
TAB='	'
SP=' '
CR='
'

MASTCONF="/etc/lrp.conf"
[ -f $MASTCONF ] && . $MASTCONF	#FIX ME: very wasteful and error prone

qt () { "$@" >/dev/null 2>&1 ; }

source () { . $@; }

basename () {	#usr/bin

	local IFS='/'
	set -- $1
	eval rc="\$$#"
	[ "$rc" = "" ] && eval rc="\$$(($# - 1))"
	echo "$rc"
}

dirname () {	#usr/bin

	echo "$1" | sed '/^\/$/c\
/
s/\/*$//
s/[^/]*$//
/./!c\
.
s/\/$//'

}


free () {	#usr/bin
	cat /proc/meminfo
}

grep () {	#bin
local pat
	pat="$1"
	shift
	sed "\'$pat'P" -n "$@"
}

cut () {	#usr/bin

#default tab
delim='	'

	while getopts b:c:d:f:s opt ; do
		case "$opt" in
			b|c) byte="$OPTARG" ;;
			d) delim="$OPTARG"  ;;
			f) field="$OPTARG"  ;;
			s) delimonly=1  ;;
		esac
	done
#space not working!
	if [ "$delim" = "\s" ]; then
		delim=' ' #space -- IFS prevents specing a space
	fi
#	
	shift $(($OPTIND - 1))

	if [ "$1" = "" ] || [ "$1" = "-" ]; then
		file=''
	else
		file=$1
	fi

	if [ $byte ]; then
	
		#sed 's/./\1/1' $file
		eecho "byte and char not working yet"
	
		exit 
	fi

	local IFS='
'
	if [ $delimonly ]; then
		field=$(($field - 1))
	fi
	if [ "$field" = 0 ]; then
		exit 0
	fi
	for line in `cat $file`; do
		local IFS=$delim
		
		set -- $line
				
		if [ $delimonly ]; then
			shift
			eval necho $"$field"
		else
			[ $# -eq 1 ] && field=1
			eval echo $"$field"
		fi
	done
}

head () {	#usr/bin
local num=10

	if [ "$1" = "-n" ]; then
		num=$2
		shift 2
	fi
	
	sed "$num"q "$@"

}

tail () {	#usr/bin
local num=10

	if [ "$1" = "-n" ]; then
		num=$2
		shift 2
	fi
	
	num=$(($num + 1))
	sed -e :a -e '$q;N;'$num',$D;ba' "$@"
}

nl () {		#usr/bin

	if [ "$1" = "-b" ]; then
		if [ "$2" = "a" ]; then
			sed = $3 | sed 'N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'
		fi
	shift 2
	else
		sed '/^$/b;=' $1 | sed '/./N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'
	fi

}


uniq () {	#usr/bin

	sed '$!N; /^\(.*\)\n\1$/!P; D' "$@"

}

sort () {	#usr/bin

	echo 'sort: fix me!'

}

zgrep () {	#usr/bin
	singlefilearg "$2"
	zcat "$2" | grep "$1" -
}

zmore () {	#usr/bin
	singlefilearg "$1"
	zcat "$1" | more
}

reset () {	#usr/bin
	echo 'c'
	stty sane
}


mail () {

  usage () { 
	echo "Usage: $(basename) options to[,...]"
	echo "Options: [-s subject] [-c cc[,...]] [-b bcc[,...]]"
	echo "         [-a attachment[,...]] [-d domain] [-h smptserver]"
	echo ""
	echo "Mail settings are in /etc/lrp.conf"
	echo "-a attach text file(s)"
	echo "-d specify from FQDN, overriding lrp_MAIL_DOMAIN or local domain"
	echo "-h specify SMTP server, overriding the lrp_MAIL_SERVER setting"
  }

  rfcdate () {
	local IFS=" "
	set -- `date -R`
	case "$#" in
	6)
		echo "$@"
		;;
	1)
		set -- `date`
		[ "$#" -eq 5 ] && echo "$1, $3 $2 $5 $4 +0000"
		;;
	*)
		;;
	esac
  }

  attach () {
	if [ -f "$1" ] ; then
		name=`echo $1 | sed 's/.*\///'`
		echo ""
		echo "--$ContentBoundry"
		echo "Content-Type: application/octet-stream; name=\"$name\""
		echo "Content-Disposition: attatchment; filename=\"$name\""
		echo ""
		cat "$1" | sed 's/^\./\.\./'
	fi
  }

  abort () {
	eecho "Aborting due to $1"
	eecho "  Killing child processes: $pidctrl $pidnc"
	qt kill $pidctrl
	qt kill $pidnc
	eecho "  Removing temporary files"
	qt rm -f $ptx $prx $fdata $fbody
	eecho "  Done"
	exit 1
  }

  ctrl () {

    exit1 () { 
	eecho "Error: Unknown response." 
	eecho "  $lastline"
	eecho "  $state: $nrep $vrep"
	kill -12 $$
	exit 1 
    }

    getresponse () {
	read nrep vrep
	# Skip over multi-line responses
	while : ; do 
		case $nrep in
		???-*)	read nrep vrep	;;
		*)	break		;;
		esac
	done
	x=`echo $nrep | sed 's/^\([0-9]\)..*$/\1/'`
    }

	getresponse < $prx 

	state=0
	line=""
	sentbody=no

	while [ $state -lt 8 ] ; do

		# Send heartbeat signal...prevents timout while active
		kill -10 $$	
		lastline=$line
		IFS="" ; read line ; IFS=$OIFS

		case $x in
		"2")
			echo "$line$CTLM"
			[ $state -eq 4 ] && [ "$line" != "DATA" ] && 
				state=$(($state - 1))
			[ $state -ne 6 ] && state=$(($state + 1))
			;;
		"3")
			[ $state -ne 5 -a $state -ne 6 ] && exit1
			[ $state -eq 5 ] && state=$(($state + 1))
			if [ "$line" = "." ] ; then
				if [ "$sentbody" != "yes" ] ; then
					cat $fbody
					kill -10 $$
					IFS=","
					for file in $att; do
						attach $file
						kill -10 $$
					done
					IFS=$OIFS
					sentbody=yes
				else
					echo "$line$CTLM"
					state=$(($state + 1))
				fi
			else	
				echo "$line$CTLM"
			fi
			;;
		*)
			exit1
			;;
		esac

		[ $state -ne 6 ] && getresponse < $prx
		#eecho "$state: $nrep $line"
	done < $fdata

  }

	CTLM=`echo -e -n "\015"`
	TIMEOUT=60

	ContentBoundry="+++++LRP content boundry $$-`date +%s`+++++"

	ptx="/tmp/smtp.tx.$$"
	prx="/tmp/smtp.rx.$$"
	fdata="/tmp/smtp.data.$$"
	fbody="/tmp/smtp.body.$$"

	[ -e $prx ]   && eecho "Bad in file"   && exit 1
	[ -e $ptx ]   && eecho "Bad out file"  && exit 1
	[ -e $fdata ] && eecho "Bad temp file" && exit 1
	[ -e $fbody ] && eecho "Bad temp file" && exit 1

	mknod $ptx p; mknod $prx p; : > $fdata; : > $fbody
	chmod 600 $ptx $prx $fdata $fbody

	fqdn=${lrp_MAIL_DOMAIN:-`(hostname -f)`}
	user=${USER:-$LOGNAME}
	smtpserv=${lrp_MAIL_SERVER:-mail}

	while getopts a:b:c:d:h:s: opt ; do
		case "$opt" in
			a) att="$OPTARG" ;;
			b) bcc="$OPTARG" ;;
			c) cc="$OPTARG"  ;;
			d) fqdn="$OPTARG"  ;;
			h) smtpserv="$OPTARG"  ;;
			s) subject="$OPTARG"  ;;
		esac
	done

	shift $(($OPTIND - 1))

	[ $# -lt 1 ] && usage && exit 1

	while read line; do
		echo $line | sed 's/^\./\.\./'
	done >$fbody
	
	to="$@"

	IFS=","

	allto="$to,$cc,$bcc"
	envelopes="$(	for str in $allto; do
		echo $str
	done | sed 's/^.*[< ]\(.*\)$/\1/;s/>$//' )"

	IFS=$OIFS

	{	echo "RSET" 
		echo "HELO $fqdn"
		echo "MAIL FROM:<$user@$fqdn>"
			
		for rcpt in $envelopes; do
			echo "RCPT TO:<$rcpt>"
		done	
	
		echo "DATA"

		date=`rfcdate`
		[ -n "$date" ] && echo "Date: $date"
		echo "From: $user@$fqdn"
		echo "Subject: $subject"
		[ -n "$to" ] && echo "To: $to"
		[ -n "$cc" ] && echo "cc: $cc"
		[ -n "$bcc" ] && echo "bcc: "
		[ -n "$att" ] &&
			echo "Mime-Version: 1.0" &&
			echo "Content-Type: multipart/mixed; boundary=\"$ContentBoundry\"" &&
			echo ""
			echo "--$ContentBoundry" &&
			echo 'Content-Type: text/plain; charset="us-ascii"'
		echo ""
		
		echo "."

		[ -n "$att" ] &&
			echo "" &&
			echo -n "--$ContentBoundry"
		
		echo -e "\n.\nQUIT"
  
	} >$fdata

	# Heartbeat signal from ctrl process
	trap "count=0" 10	

	# Abort signal from ctrl process
	trap "abort \"connection error\"" 12

	ctrl > $ptx &
	pidctrl=$!

	nc $smtpserv $(getservbyname smtp) < $ptx > $prx &
	pidnc=$!

	count=0
	while [ -n "`jobs`" ] ; do
		sleep 1
		count=$(($count + 1))
		[ $count -gt $TIMEOUT ] && abort "timeout"
	done

	qt rm -f $ptx $prx $fdata $fbody
}

finger () {	#usr/bin

#[ $# -ne 1 ] && echo "Usage: $(basename) user[@host.domain]" && exit 1

	local IFS='@'
	set -- $1
	
	user="$1"
	if [ "$2" != "" ]; then
		host="$2"
	else
		host="localhost"
	fi

	echo "$user"'
' | nc $host $(getservbyname finger)
	[ $? -ne 0 ] && echo "Connection refused" && exit 1
}

uname () {	#bin
local rc

	case $1 in
		-a|--all)
			rc="`cat /proc/sys/kernel/ostype` `cat /proc/sys/kernel/hostname` `cat /proc/sys/kernel/osrelease` `cat /proc/sys/kernel/version` i386 unknown"  ;;
		-r|--release)
			rc="`cat /proc/sys/kernel/osrelease`"  ;;			
		-s|--sysname|*)
			rc="`cat /proc/sys/kernel/ostype`"  ;;
	esac
	echo $rc
}

uptime () {	#usr/bin

	local IFS='.'
	set -- `cat /proc/uptime` 
	uph=$(($1 / 3600))
	upd=$(($uph / 24))
	
	local IFS=' '
	set -- `date` 
	str="$4"
	set -- `cat /proc/loadavg`
	str="$str up $upd Days ($uph"h"), load average: $1 $2 $3"

	echo " $str"
}

who () {	#usr/bin

local now=$(date +%s)
	echo "USER     TTY      PID      TIMEON   FROM" 
	for f in $(dutmp /var/run/utmp |grep '^7|'); do		#dutmp busybox function
		local IFS='|'
		set -- $f
		minon=$(( $(($now - $10)) / 60 ))
		#echo "$5 	$3	 $2	 $minon	 $6"
		printf "%-8.8s %-8.8s %-8.8s %-8.8s %-8.8s\n" $5 $3 $2 $minon $6
	done

}

last () {	#usr/bin

local now=$(date +%s)
	echo "USER	 TTY	 PID	 TIMEON	 FROM"
	for f in $(dutmp /var/log/wtmp |grep '^\(7\|2\)|'); do		#dutmp busybox function
		local IFS='|'
		set -- $f
		minon=$(( $(($now - $10)) / 60 ))
		echo "$5	 $3	 $2	 $minon	 $6" 
	done

}

getservbyname () {	#usr/sbin
	[ "$1" != "" ] && serv=$1 || \
		{ eecho "Usage: $prog service [type]"; exit 1; }
	proto=${2:-tcp}
	port="$(sed -n "s/^$serv[ |$TAB]*\([^/]*\)\/$proto.*$/\1/p" /etc/services)"
	echo "${port:-0}"
	return ${port:-0}
}


printenv () {	#usr/bin

	if [ $# -gt 0 ]; then
		for v in $@; do
			eval V="\$"$v""
			[ ! "$V" ] && continue 
			echo "$V"
		done
	else
		for v in $(export); do eval echo "$v="\$"$v"; done
	fi
}

id () {	#usr/bin
	IFS=':'
	set -- $(grep "^$USER:" /etc/passwd)
	uid=$3
	gid=$4
	set -- $(grep ":x:$gid:" /etc/group)
	gidn=$1
	
	echo "uid=$uid($USER) gid=$gid($gidn)"
}

whoami () {	#usr/bin

	echo "$USER"
}

w () {		#usr/bin
	uptime
	who
}

expr () {	#usr/bin
	exp "$@"	#ash builtin
}


necho () {	#usr/bin
local E
	if [ "$1" = "-n" ]; then
		shift
		E="echo -n"
	else
		E="echo"
	fi
#Do not echo if line is blank
	if [ ! "$1" = "" ]; then
		$E "$1"
	fi
}

eecho () {	#usr/bin
	echo "$@" >/dev/null >&2
}

pauseme () {	#usr/bin
	echo -n "    Press return...";read n;echo
}

lsmod () {	#sbin
	echo "Module         Pages    Used by"
	cat /proc/modules
}

useradd () {	#usr/sbin

	PW="/etc/passwd"
	SH="/etc/shadow"
	DAY="$(( $(date +%s) / 60 / 60 / 24 ))"
	eecho "FIX ME: $DAY"

}

userdel () {	#usr/sbin

PW="/etc/passwd"
SH="/etc/shadow"

	[ $# -ne 1 ] && eecho "Usage: $prog username" && return 1


	if [ "$(sed -n /^$1:/p $PW)" = "" ]; then
		eecho "$prog: user $1 does not exist"
		return 1	
	fi

	umask 077
	
	cp $PW $PW-; cp $SH $SH-

	sed "/^$1:/d" $PW- > $PW
	sed "/^$1:/d" $SH- > $SH
	
}

usermod () {	#usr/sbin

	eecho "FIX ME"

}

groupadd () {	#usr/sbin

	eecho "FIX ME"

}

groupdel () {	#usr/sbin

GP="/etc/group"
GS="/etc/gshadow"

	[ $# -ne 1 ] && eecho "usage: $prog group" && return 1


	if [ "$(sed -n /^$1:/p $PW)" = "" ]; then
		eecho "$prog: group $1 does not exist"
		return 1	
	fi

	umask 077
	
	cp $GP $GP-; cp $GS $GS-

	sed /^$1:/d $GP- > $GP
	sed /^$1:/d $GS- > $GS
	
}

groupmod () {	#usr/sbin

	eecho "FIX ME"

}

#START LRP stuff

lrpkg () {	#usr/sbin

lrpkgpath="/var/lib/lrpkg/"
CD="$(pwd)"

	usage () {
		eecho "Usage: $(basename $0) [OPTIONS] <packagename>"
		eecho "	-l: list 	List installed packages."
		eecho "	-i: install	Install package. (exclude .lrp)"
	}

	list () {
		echo "Name            Version        Description"
		echo "===============-==============-=============================================="
		
		PACKAGES="$(cat $lrpkgpath/packages)"
		for p in $PACKAGES; do
			ver="$(cat $lrpkgpath/$p.version)"
			desc="$(head -n 1 $lrpkgpath/$p.help)"
			printf "%-15.15s %-14.14s %-46.46s\n" "$p" "$ver" "$desc"
		done 
	}

	install () {
	
		f="$1"
		
		cd /
		
		if [ -f "$CD/$f.lrp" ]; then
			echo -n "Installing $f... "

			qt gunzip -t "$CD/$f.lrp"
			if [ $? -eq 0 ]; then
				gunzip -c $CD/$f.lrp | qt tar xf - 
				echo "$f">>"$lrpkgpath/packages"	#Update installed packages file
				echo "Done."
			else
				echo "Archive appears corrupted! Bailing..."
			fi
		else
			eecho "Archive '$f.lrp' not found."
		fi
		
		cd $CD
	}

	case "$1" in 
		-l )	list "$2"	;;
		-i )	install "$2"	;;
		*  )	usage		;;
	esac
}

update-rc.d () {	#usr/sbin


	usage () {
		eecho "Usage: $(basename $0) [-f] [-a[-w]] <basename>"
		eecho "	-f: force	Force. ALL current links of <basename> are deleted."
		eecho "	-a: all		Process all files in /etc/init.d/. <basename> ignored."
		eecho "	-w: wipe 	Wipe /etc/rc?.d/ directories (implies -f)"
		eecho "	-v: verbose	Be extra verbose."
		eecho ""
		eecho "This program uses 'RCDLINKS=' found in the /etc/init.d/ files."
		eecho "Comment out 'RCDLINKS=' if you want a file to be ignored."
	}

	while getopts fawv opt ; do
		case "$opt" in
			f) rcdforce="yes" ;;
			a) rcdall="yes" ;;
			w) rcdwipe="yes"  ;;
			v) rcdvb="yes"  ;;
		esac
	done

	shift $(($OPTIND - 1))
	
	if [ "$rcdall" = "yes" ]; then
		if [ "$rcdwipe" = "yes" ]; then
			for d in /etc/rc?.d; do
				[ ! -d "$d" ] && continue
				qt rm $d/[KS][0-9][0-9]*
			done
		fi
		F=/etc/init.d/*
	else
		[ "$1" = "" ] && usage &&  exit 1	
		F=/etc/init.d/$1
		
		if [ ! -x "$f" ]; then
			eecho "$f does not exist or is not executable."
			exit 1
		fi
	fi

	errlvl=0
	for f in $F; do
		
		[ ! -x "$f" ] && continue
				
		eval RCDLINKS=$(sed -n '1,/^RCDLINKS=/s/^RCDLINKS=\(.*\)/\1/p' $f 2>/dev/null)
	
		if [ "$RCDLINKS" != "" ]; then
			bf="$(basename $f)"
				
			qt ls /etc/rc?.d/[KS][0-9][0-9]$bf
			if [ $? -eq 0 ]; then
				if [ "$rcdforce" = "yes" ]; then
					qt rm /etc/rc?.d/[KS][0-9][0-9]$bf
				else
					eecho "System startup links for $f already exist."
					errlvl=2
					continue
				fi
			fi
				
			for l in $RCDLINKS; do
				IFS=','; set -- $l; IFS=$OIFS
				[ ! -d "/etc/rc$1.d" ] && continue	# Ignore bad dirs.
				[ "$rcdvb" ] && eecho "RCDLINKS=$RCDLINKS - $f"
				ln -sf "../init.d/$bf" "/etc/rc$1.d/$2$bf" 2>/dev/null
			done
		else
			[ "$rcdvb" ] && eecho "'^RCDLINKS=' not found in $f. File ignored."
			errlvl=3
		fi
	done
	return $errlvl
}

help () {	#usr/bin
HF="$1"
	[ $# -eq 0 ] && HF="root" 
	more "/var/lib/lrpkg/$HF.help"
}

svi () {	#usr/sbin
	singlefilearg "/etc/init.d/$1"
	/etc/init.d/$@
}

mount.boot () {	#usr/sbin
	mount -t `cat /var/lib/lrpkg/boot.fstype` /dev/boot "$1"
}

#END LRP stuff

singlefilearg () {

	if [ "$1" = "" ]; then
		eecho "Usage: $prog file"
		exit 1
	fi
	
	if [ ! -f "$1" ]; then
		eecho "$1: No such file or directory"
		exit 1
	fi
}


##
#main ()
#{
##

prog=$(basename $0)

	[ $prog = "POSIXness" ] && [ $# -gt 0 ] && prog="$1" && shift

case $prog in 

	basename|dirname|uname|grep|cut|free|expr|head|tail|nl|\
	uniq|mail|finger|sort|help|svi|mount.boot|necho|eecho|\
	useradd|userdel|usermod|groupadd|groupdel|groupmod|\
	update-rc.d|printenv|getservbyname|lrpkg|zmore|zgrep)
		$prog "$@"  ;;
	uptime|who|id|whoami|w|pauseme|lsmod|last|reset)
		$prog  ;;
	*)	
		eecho "No function defined for '$prog'"
		exit 1  ;;	
esac
exit $?
	
##
#}
##
