#/bin/bash

# $syslib mustn't contain spaces (shell language limitation)

[ -e /proc/progress ] && echo 110 >/proc/progress

PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin"

: ${syslib:=/etc/rc.d}     # currently hardcoded in facility
: ${facdir:=$syslib/fac}

VERSION=1.1
VENDOR_STRING="lma-init $VERSION"

: ${CONSOLE:=/dev/console} # default console
: ${SUTIMEOUT:=20}         # default sulogin timeout (see fatal)
: ${RCDIR:=/etc}           # where SYSVINIT-style rcX.d files are located
: ${SYSVINIT:=0}           # defaults for SYSV_EXEC and SYSV_FAC
: ${SYSV_EXEC=$SYSVINIT}   # execute sysv-scripts in $RCDIR/rc?.d/[SK][0-9][0-9]*
: ${SYSV_FAC=$SYSVINIT}    # scripts in $RCDIR/init.d/* are used overwrite builtins
: ${COALESCE:=1}
: ${SULOGIN:=sulogin}
: ${SINGLESTEP:=0}

shopt -s extglob

# load additional commands
enable -f $syslib/lmainit.so console setsid winsize renamefunc usleep str2hex

color_r() {
   r="[$1m"
}

tgoto_r() {
   r="[$(($1+1));$(($2+1))H"
}

setscrreg_r() {
   r="[$(($1+1));$(($2+1))r"
}

tgoto() { tgoto_r "$@"; echo -n "$r"; }
color() { color_r "$@"; echo -n "$r"; }

clear="[H[2J"
clreol="[K"
delline="[M"
save_cursor="7"
restore_cursor="8"

reset() {
   echo -n "c"
}

initscreen() {
   eval $(winsize)
}

declare -a fac_args

find_facility() {
   local fac_mode="${1:0:1}"
   local fac="${1:1}"
   local args arg i
   fac_name="${fac%%::*}"
   local ref="fac_${fac_name}_desc"
   if [ -z "${!ref}" ]; then
      # try to load the facility
      if [ -r "$facdir/$fac_name.fac" ] ; then
         . "$facdir/$fac_name.fac"
      fi
   fi
   if [ -n "${!ref}" ]; then
      case "$fac" in
         *::* ) args="${fac#*::}" ;;
         *    ) args=""           ;;
      esac
      IFS=: eval 'fac_args=($args)'
      set -- "${fac_args[@]}"
      eval fac_desc="\"${!ref}\""
      if [ "$fac_mode" == "+" ]; then
         fac_mode="start"
      else
         fac_mode="stop"
      fi
      fac_run="fac_${fac_name}_${fac_mode} \"\${fac_args[@]}\""
      return 0
   else
      fac_desc="$fac_name: no such facility"
      fac_run=false
      return 1
   fi
}

#############################################################################
# Shamelessly stolen from the bash distribution.
# This is ugly code, and I believe my version would have been faster.
# Arrogance is a pity.
# But my version would definitely be much shorter!

# usage: reverse arrayname
reverse()
{
	local -a R
	local -i i
	local rlen temp

	# make r a copy of the array whose name is passed as an arg
	eval R=\( \"\$\{$1\[@\]\}\" \)

	# reverse R
	rlen=${#R[@]}

	for ((i=0; i < rlen/2; i++ ))
	do
		temp=${R[i]}
		R[i]=${R[rlen-i-1]}
		R[rlen-i-1]=$temp
	done

	# and assign R back to array whose name is passed as an arg
	eval $1=\( \"\$\{R\[@\]\}\" \)
}

#############################################################################

declare -i _cnt

# start a facility definition
def() {
   _name="$1"; shift
   _desc="$*"

   end() {
      renamefunc start "fac_${_name}_start"
      renamefunc stop  "fac_${_name}_stop"
      eval fac_${_name}_desc='"$_desc"'

      unset end enddef desc _name _desc
   }
   enddef() { end; }

   desc() {
      _desc="$*"
   }

   start() { :; }
   stop()  { :; }
}

# start a facility sequence
defseq() {
   _cnt=0

   eval '
      fac_'$1'_seq=()

      add() {
         fac_'$1'_seq[_cnt++]="$1"
      }

      fac_'$1'_desc="$2"
      fac_'$1'_start() {
         for fac in "${fac_'$1'_seq[@]}"; do
            if [ "${fac:0:1}" != "-" ]; then
               find_facility "+${fac#+}"
               eval "$fac_run"
            fi
         done
      }
      fac_'$1'_stop() {
         for fac in "${fac_'$1'_seq[@]}"; do
            if [ "${fac:0:1}" != "+" ]; then
               find_facility "-${fac#-}"
               eval "$fac_run"
            fi
         done
      }
   '

   end() {
      unset add end _cnt
   }
}

defrl() {
   _cnt=0

   eval '
      add() {
         _rl_'$1'[_cnt++]="$1"
      }
   '

   end() {
      unset add end _cnt
   }
}

# define an alias (name, description, alias)
defalias() {
   # this was a major speed bottleneck,
   # now it's just a readability bottleneck
   eval '
      fac_'$1'_desc="$2"
      fac_'$1'_alias="$3"
      fac_'$1'_start() {
         find_facility "+$fac_'$1'_alias"
         eval "$fac_run"
      }
      fac_'$1'_stop() {
         find_facility "-$fac_'$1'_alias"
         eval "$fac_run"
      }
   '
}

#############################################################################

fatal() {
   echo "$@"
   echo "you have $SUTIMEOUT seconds to log-in as root, otherwise booting will continue"
   $SULOGIN -t $SUTIMEOUT
}

# evaluate command and, if exit-status is non-zero,
# ask the user wether she wants to log-in (by starting sulogin)
safe_eval() {
   if ! ( "$@" ); then
      fatal "an error occured while running '$@'"
   fi
}

daemonize() {
   {
      cd /
      setsid
      exec "$@"
      exit 127
   } &
}

kill_pidfile() {
   local signal="$1"
   local path="$2"
   local pidfile="$3"
   local pid
   if [ -n "$pidfile" -a -r "$pidfile" ]; then
      read pid <"$pidfile"
   fi
   if ((pid>0)); then
      kill -$signal $pid || echo "$path: pidfile exists, but no process found"
   elif [ -n "$path" ]; then
      [ -n "$pidfile" ] && echo "$path: unable to find pidfile, trying killall instead"
      killall -$signal "$path"
   fi
}

declare -a facilities

_get_facilities() {
   local RL="$1"
   local mode="$2"
   local i=0 j

   local omode="-"; [ $mode == "-" ] && omode="+"

   local -a facs
   eval facs=(\"\${_rl_$RL[@]}\")
   for fac in "${facs[@]}"; do
      if [ "$fac" == "${fac#$omode}" ]; then # facilities not prefixed with OMODE are also o.k.
         fac="${fac#$mode}" # facilities prefixed with the MODE are o.k.
         if [ "$fac" == "${fac#@}" ]; then
            j=${#facilities[@]}
            facilities[j]="$mode$fac"
         else
            _get_facilities "${fac#@}" "$mode"
         fi
      fi
   done
}

new_facilities() {
   facilities=()
}

declare -i _sysvfac_cnt=0

add_facilities() {
   _get_facilities "$1" "$2"

   # optionally check for sysv facilities
   if ((SYSV_EXEC)); then
      local prefix=S fac j
      [[ "$2" == "-" ]] && prefix=K
      local -a sysvfac
      prefix="$RCDIR/rc$1.d/$prefix[0-9][0-9]"
      for scr in $prefix*; do
         if [ -x "$scr" ]; then
            fac="_sysvfac$_sysvfac_cnt"
            eval "fac_${fac}_desc"='"${scr#$prefix}"'
            eval "
               fac_${fac}_start() {
                  \"$scr\" start
               }
               fac_${fac}_stop() {
                  \"$scr\" stop
               }
            "
            sysvfac[_sysvfac_cnt++]="$2$fac"
         fi
      done
      [[ "$2" == "-" ]] && reverse sysvfac
      facilities=("${facilities[@]}" "${sysvfac[@]}")
   fi
}

rev_facilities() {
   reverse facilities
}

# check facilities for dupes ("-mysqld +mysqld" and remove them both)
coalesce_facilities() {
   local -a _facilities

   _facilities=("${facilities[@]}")
   facilities=()

   # pass 1, find dupes
   for fac in "${_facilities[@]}"; do
      if [ "${fac:0:1}" == "-" ]; then
         eval local off_$(str2hex "${fac:1}")=1
      elif eval '(($'off_$(str2hex "${fac:1}")'))'; then
         eval local ignore_$(str2hex "${fac:1}")=1
      fi
   done

   # pass 2, copy all unique facility changes
   local -i cnt=0
   for fac in "${_facilities[@]}"; do
      if ! eval '(($'ignore_$(str2hex "${fac:1}")'))'; then
         facilities[cnt++]="$fac"
      fi
   done
}

# gay penguin
pengo1_r() {
   r="\
[0;1;30;44m        [37m.[40m      [44m.[30m      
[44m        [40m        [44m      
[30;44m        [40m [47m  [40m [47m  [40m  [44m      
[30;44m        [40m [47m [40m   [47m [40m   [44m     
[30;44m        [40m  [43m    [40m   [44m     
[30;44m        [40m [0;30;43m\`----'[40m  [1;44m     
[30;44m        [40m [47m.[37m....[30m.[40m   [44m    
[44m       [40m [47m........[40m    [44m  
[44m       [40m [47m.........[40m    [44m 
[44m       [40m [47m.........[40m    [37;44m 
[44m      [40m [47m..........[40m     
[44m     [40m  [47m..........[40m     
[44m     [43m   [47m........[43m   [40m   
[44m   [43m     [47m........[43m    [40m  
[44m [43m       [47m........[43m      
[44m [43m       [40m [47m......[40m [43m      "
}

# fat penguin
pengo2_r() {
   r="\
[0;1;37;47m#########[0;30;47m#####[1;37m#########[1;37;40m
[0;1;37;47m########[0;30;47m#######[1;37m########[1;37;40m
[0;1;37;47m########[0;30;47m##[1;37mO[0;30;47m#[1;37mO[0;30;47m##[1;37m########[1;37;40m
[0;1;37;47m########[0;30;47m#[1;33m#####[0;30;47m#[1;37m########[1;37;40m
[0;1;37;47m######[0;30;47m##[1;37m##[33m###[37m##[0;30;47m##[1;37m######[1;37;40m
[0;1;37;47m#####[0;30;47m#[1;37m##########[0;30;47m##[1;37m#####[1;37;40m
[0;1;37;47m####[0;30;47m#[1;37m############[0;30;47m##[1;37m####[1;37;40m
[0;1;37;47m####[0;30;47m#[1;37m############[0;30;47m###[1;37m###[1;37;40m
[0;1;37;47m#####[0;30;47m#[1;37m###########[0;30;47m##[1;33m#[37m###[1;37;40m
[0;1;37;47m#[33m######[0;30;47m#[1;37m#######[30m#[33m######[37m#[1;37;40m
[0;1;37;47m#[33m#######[0;30;47m#[1;37m#####[0;30;47m#[1;33m#######[37m#[1;37;40m
[0;1;37;47m###[33m#####[30m#[0;30;47m#####[1m#[33m#####[37m###[1;37;40m"
}

#
# title:
# ---(VENDOR_STRING)------------------(TITLE)--------------------------------
# or
# ----------------------------------(TITLE)----------------------------------

_bars="------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"

show_title () {
   local opt="$1"
   local -i offs=0

   tgoto 0 0
   color 37
   echo -n "${_bars:0:$cols}"

   if [ -n "$VENDOR_STRING" ] ; then
          offs=$(((${#VENDOR_STRING})+6))
          tgoto 0 3
          echo -n "("
          color 33
          echo -n "$VENDOR_STRING"
          color 37
          echo -n ")"
          color 33
   fi		
   tgoto 0 $(((cols-offs-${#opt})/2+offs))
   echo -n "("
   color 33
   echo -n "$opt"
   color 37
   echo -n ")"
   color 36
}
 
#< w1   >< w2                   >
##################################^
# pengo        title             #h1
# pengo                          #
# pengo        x1                #
#              xn                #v
#________________________________#
#                                #
##################################
run_facilities() {
   local title="$1"

   ((COALESCE)) && coalesce_facilities

   ((DRY_RUN)) || console "$CONSOLE"
   initscreen

   local -i nfac=${#facilities[@]}
   local -i h1=rows/3
   local -i w1=PENGUIN?24:0
   local -i w2=cols-w1
   local -i y n wfac
   local pengo

   color "31;44;1"; local C_error="$r"
   color "36;44;1"; local C_fac="$r"
   color "33";      local C_desc="$r"
   color "37";      local C_text="$r"

   tgoto_r 1 0; pengo="$r"

   ((h1>nfac+2)) && h1=nfac+2
   if ((PENGUIN==1)); then
      ((h1<18)) && h1=18
      pengo1_r; pengo="$pengo$r"
   elif ((PENGUIN==2)); then
      ((h1<14)) && h1=14
      pengo2_r; pengo="$pengo$r"
   fi

   pengo="$pengo$C_fac"

   echo -n "$C_fac$fac$clear"
   show_title "$title"
   echo -n "$pengo"

   wfac=h1-1
   if ((wfac>nfac)); then
      wfac=nfac
   fi
   for((y=0; y<wfac; y++)); do
      find_facility "${facilities[y]}"
      tgoto_r $((y+1)) $w1
      echo -n "$r [ ] $fac_desc"
   done

   tgoto $((h1-1)) 0
   color "37"
   echo -n ${_bars:0:$cols}
   color "36"

   y=0 n=0
   setscrreg_r $h1 $((rows-1)); echo -n "$r"
   tgoto $((rows-1)) 0

   setscrreg_r 1 $((h1-2)); local scrollup="$r"
   tgoto_r 1 0; scrollup="$scrollup$r$delline"
   ((SLOW_TERMINAL)) || scrollup="$scrollup$pengo$C_fac"
   setscrreg_r $h1 $((rows-1)); scrollup="$scrollup$r"
   tgoto_r $((rows-1)) 0; scrollup="$scrollup$r"

   for fac in "${facilities[@]}"; do
      find_facility "${facilities[n++]}"
      if ((y<h1-2)); then
         y=y+1
      else
         echo -n "$scrollup"
      fi
      tgoto_r $y $w1
      echo "$save_cursor${r} [*] $fac_desc$restore_cursor[ $C_desc$fac_desc$C_fac ]$clreol$C_text"
      unset ERR
      ((DRY_RUN)) || eval "$fac_run"
      tgoto_r $y $((w1+2))
      if [ -z "$ERR" ]; then
         echo -n "$C_fac$save_cursor${r}X$restore_cursor"
      else
         echo -n "$C_error$save_cursor${r}*$restore_cursor$ERR$C_fac"
      fi
      if ((SINGLESTEP)); then
         stty -echo
         echo -n "${C_error}singlestepping, please press enter to continue... $C_fac"
         read r
         stty echo
         echo -n "$clreol"
      fi
   done

   setscrreg_r 0 $((rows-1)); echo -n "$r"
   if ((RESET)); then
      reset
   else
      tgoto $((rows-1)) 0
   fi
}

# scan sysv-facilities by name in $RCDIR/init.d/* and
# create real facilities from them
scan_sysv_facs() {
   for path in $RCDIR/init.d/*; do
      local fac=${path#$RCDIR/init.d/}
      fac=${fac//[^a-zA-Z0-9_]/_}
      eval "fac_${fac}_desc"='"$path"'
      eval "
         fac_${fac}_start() {
            \"$path\" start
         }
         fac_${fac}_stop() {
            \"$path\" stop
         }
      "
   done
}

. /etc/rc.values

((SYSV_FAC)) && scan_sysv_facs

:


