#!/bin/ash
#
#POSIXness v0.21 20000129
#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
}

smtp-out () {

fin=$1
fdata=$2

stage=0

exit1 () { eecho "$@" && exit 1 ; }

	read nrep < $fin
	
	[ "$nrep" != "220" ] && exit1 "error: Could not connect."
	nrep=250

	IFS=''
	while [ $stage -lt 10 ]; do
		read -r line 		
	
		case $nrep in
			"250"|"354")
				echo "$line$CR"
				
				[ $stage -eq 4 ] && [ "$line" != "DATA" ] && stage=$(($stage - 1))
				[ $stage -ne 6 ] && stage=$(($stage + 1))
				[ "$line" = '.' ] && stage=$(($stage + 1))
				
				#if [ $stage -ne 6 ] || [ "$line" = '.' ] ; then
				#	stage=$(($stage + 1))
				#fi
				;;
			"221")	
				[ $stage -lt 7 ] && exit1 "error: Remote quit."
				exit 0
				;; 
			*)	
				exit1 "error: Unknown response." 
				;;
		esac
		
		[ $stage -ne 6 ] && read nrep < $fin
		
		#echo "$nrep, $stage: $line" >/dev/null >&2
	
	done < $fdata
	IFS=$OIFS
	
}

mail () {	#usr/sbin

data=$1
fout="/tmp/smtpout.$$"
fin="/tmp/smtpin.$$"
fdata="/tmp/smtpdata.$$"
fbody="/tmp/mailbody.$$"

usage () { echo "Usage: $(basename) [-s subject] [-c cc[,...]] [-b bcc[,...]] to[,...]"; }

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

mknod $fout p; mknod $fin p; : > $fdata; : > $fbody
chmod 600 $fout $fin $fdata $fbody

	while getopts b:c:s: opt ; do
		case "$opt" in
			b) bcc="$OPTARG" ;;
			c) cc="$OPTARG"  ;;
			s) subject="$OPTARG"  ;;
		esac
	done

	shift $(($OPTIND - 1))
	
	[ $# -lt 1 ] && usage && exit 1

	cat >$fbody
	
to="$@"

IFS=","

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

fqdn="$(hostname -f)"	


	{	echo "RSET$CR" 
		echo "HELO $fqdn$CR"
		echo "MAIL FROM:<$USER@$fqdn>$CR"
			
		for rcpt in $envelopes; do
			echo "RCPT TO:<$rcpt>$CR"		
		done	
	
		echo "DATA$CR"
		
		echo "From: $USER@$fqdn$CR"
		echo "Subject: $subject$CR"
		[ "$to" ] && echo "To: $to$CR"
		[ "$cc" ] && echo "cc: $cc$CR"
		[ ! "$to" ] && echo "Bcc: $CR"
		echo "$CR"					
		
		cat $fbody
		
		echo "$CR";echo ".$CR"
		echo "QUIT$CR"
  
	} >$fdata

	smtp-out $fin $fdata > $fout &

	( mnc ${lrp_MAIL_SERVER:-mail} $(getservbyname smtp) <$fout ) | 
	while read -r nrep vrep; do
		echo "$nrep" >$fin
	done 

	qt rm -f $fout; qt rm -f $fin; qt rm -f $fdata; qt rm -f $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"'
' | mnc $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 < "$CD/$f.lrp"
			if [ $? -eq 0 ]; then
				gunzip < "$CD/$f.lrp" | qt untar 
				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 $?
	
##
#}
##
