#!/bin/sh
# sfs_load 28 Jan 2011 by shinobar
# some code from the otf_sfs_loader by goingnuts, the sfs_installation.sh by 01micko and scrips by Barry
# 29 Jan 2011 v0.2: fix under RAM mode (PUPSAVE=5), multisession CD support (PUPMODE=77)
# 30 Jan 2011 v0.3: unionfs mount option, fix menu update after unload
#  4 Feb 2011 v0.4: fix was pulldown failed, confirm before move, do not support unionfs other than aufs
#  5 Feb 2011 v0.5: fix was sfs removed from the list even unload failed, search smaller number if pup_roN is not available, fix typo ZDRBASE
#  9 Feb 2011 v0.6: fix was moved remote files, restart X info, launcher (thanks to seaside)
# 11 Feb 2011 v0.7: fix ws not update menu in some case, see aufs even with no module(thanks to jemimah), add new loop device
# 14 Feb 2011 v0.8: warning excessive extra files, restart main dialog, cleanup whiteout at unload(thanks to jpeps and jemimah), mkfontscale, mkfontdir
# 23 Feb 2011 v0.9: improve compatibility under PUPMODE=6 (thanks to mavrothal) and PUPMODE=77
# 23 Feb 2011 v0.9: persistent loading excessive sfs after reboot(idea from jamesbond), main dialog layout(thanks to jamesbond)
# 23 Feb 2011 v0.9: glob pattern file neme, puppypin
# 15 Mar 2011 v1.0: again PUPMODE=5/6/77
# 17 Mar 2011 v1.1: load/unload under PUPMODE=5, launcher
# 22 Sep 2011 v1.1.1: free(busybox) version compatible
# 26 Sep 2011 v1.2: fix was undesired mount (thanks to mories), fix was fleezed input in combobox, fix ramsize
# 28 Oct 2011 v1.2.9: support kernel 3.x and unionfs, special characters in path, profile.d
# 13 Nov 2011 v1.3: fix could not unload excess sfs(>6) with some version of busybox, PUPMODE=7(same as PUPMODE=2), error message at unload failure, wait before removing layer 
# v1.3.9: fix was not perform Startup scripts at load
# v1.3.9: HAS_ICONS, HAS_GLIB_SCHEMA (jemimah)
# v1.3.9: wipe out the masking files in save layers at load
# v1.3.9: kernel versions and SUPPORTSIG (red/yellow/green)
# v1.3.9: another tmpfs (/mnt/tmp) for live CD and etc.
# v1.3.9: PUPHOME without pupsave (PUPMODE=5), look up live CD, PUPMODE=77
# v1.3.9: messages - not to delete loaded sfs, retry unload, experimental, unsupport versions
# 8 Mar 2012 v1.4: extrasfsfind, upadate nls
# 7 Apr 2012 v1.9: clean up whiteout, BOOTCONFIG.save for remastered puppy, accept multiple files, read from standard input, permission check
# 9 Apr 2012 v1.9.1: clean up whiteout once for load, busybox umount and do not 'losetup -d'(jemimah), where to mount(pup_ro?), always register EXTRASFSLIST, no wide search for full path
#13 Apr 2012 v1.9.2: fix was missing mountedpart, skip fixmenus for 'start', on RAM with remastered, saluki stuff
#1 Mar 2012 v1.9.3: fix was clean up whiteout do not see pup_z, incleased RESERVHDD/RAM, version info, jwm -reload without flicker
#6 Mar 2012 v1.9.4: fix excess(>6) load was messing up with some version of busybox(lupu), fix was menu dissappeared for eccess sfs, fix was error but already mounted, avoid 'losetup -d' for some kernel
#6 Mar 2012 v1.9.4: fix 'restart X' warning, never use external download_file, 'Back to sfs_load' button, gtkdialog4
#18 May 2012 for lupu-528JP: SUPPORTSIG=yellow
#19 May 2012 v1.9.5: fix was no move but always copy
#31 May 2012 v1.9.6: fix was everytime rc.update run when the number of additional sfs > 3, also when >6
#            v1.9.6-2: 'losetup -d' is disabled for kernel-3.x #2.0.8 do not automatic
#14 May 2013 v1.9.7: fix was faild more than 7 after reboot (thanks to nonki3476), fix was failed cleanwhite on flash, support util-linux-2.23 (thanks to simargl) , debugging log
#31 May 2013 v1.9.8: again fix was failed cleanwhite on flash with RAM mode (PUPMODE=5)
# 9 Jun 2013 v1.9.9: unmount on permission error (thanks to R-S-H), shrink skip list (PUPMODE=2)
#18 Dec 2013 v2.0: '--info' shows installed sfs list, fix was always copy in cli(thanks to R-S-H), fix launcher was missing some desktop application name, fix menu icons for excess sfs
#v2.0: BOOTCONFIG.save for treating excess sfs, no wide search by default, refresh default applications, fix zdrv was double mounted
#09 Jun 2014 v2.0.8: ydrv, save-to-directory(gyro), DISSOSIATE option to be set by hardcode (thanks to R-S-H), remove find -mount option(Karl Godt) 
#26 Jun 2014 v2.0.9: init script in initramfs may not handle extra sfs
#27 Jun 2014 v2.0.10: afterwork at boot
#3 Jul 2014 v2.0.11: fix save-to-directry(gyro), fix was ydrv on loadable list
#8 Jul 2014 v2.0.12: no extra sfs but layers can be changed, rewrite cleanwhite, WIPEWHITEONINIT, PUPMODE7SUPPORT
#12 Jul 2014 v2.0.13: cleanwhite fix(SFR)
#16 Jul 2014 v2.1: unmount at finish, shutdownconfig ready
#v2.1.8: all extra sfs by sfs_load not by initramfs
#v2.1.9: fix loop device conflict with irregular initramfs
#2014-08-10 v2.2: fix was removed all sfs after abnormal shutdown
#2014-09-06 v2.3: fix again save-to-directry, avoid always mounted puppy sfs partition under PUPMODE=5  
MYPATH=$0
MYNAME=$(basename "$0")
VERSION=2.3
ORGOPT="$@"
#some options the puplet builder can choose
WIPEWHITEONINIT="true"	# true/false
WIPEWHITEONLOAD="true"	# true/false
WIPEWHITEUNLOAD="true"	# recommend 'true'
WIPEMASKONLOAD="false" # perge files in save layer if 'true'. recommend 'false'
DISSOSIATE="true"	# excute'losetup -d', some 3.2.x kernel may hung up
WIDESEARCH="false"   #v2.0: search sfs other than /mnt/home and /mnt/home/PSUBDIR
PUPMODE7SUPPORT="false" # PUPMODE=7 is buggy
RESERVHDD=131072 # kb rest #v1.9.3: increased to 128MB
RESERVRAM=131072 # kB rest #v1.9.3: increased to 96MB, v2.0: 128MB
MAXEXTRANUMSAVE="/etc/MAXEXTRANUM"  # how many extra files initramfs load?, MAXEXTRANUM=0:no depend on the initrd

MYTMPDIR="/tmp/sfs_load"
LOGFILE="$MYTMPDIR/${MYNAME}.log"
TMPFILE="$MYTMPDIR/${MYNAME}_tmp.txt"
ICONS="/usr/local/lib/X11/mini-icons"
MYICON="/usr/share/pixmaps/squashfs-image.png"
MYSMALLICON="/usr/share/pixmaps/sfs.xpm"
PUPSTATE=/etc/rc.d/PUPSTATE
BOOTCONFIG=/etc/rc.d/BOOTCONFIG
PUPSAVECONFIG=$(which pupsaveconfig)
PUPSAVECONF=/etc/rc.d/pupsave.conf
SHUTDOWNCONF=/tmp/shoudownconfig_results
#SHUTDOWNCONFIG=$(which shutdownconfig)
DISTRO_SPECS=/etc/DISTRO_SPECS
PUPPYVERSION=/etc/puppyversion
PKGDIR="/root/.packages"
INSTALLEDLIST="$PKGDIR/user-installed-packages"
WOOFLIST="$PKGDIR/woof-installed-packages"
SFSCONVERT="/usr/local/sfsconvert/sfsconvert"
[ "$HOME" ] || HOME=/root
JWMRC="$HOME/.jwmrc"
[ -x "$SFSCONVERT" ] || SFSCONVERT=""

#fixme: avoid multiple run
# restart log
[ -f  "$LOGFILE" ] && ALREADYRUN="y"
mkdir -p "$MYTMPDIR"
#rm -f "$LOGFILE"
date >> "$LOGFILE"
echo $0 $@ >> "$LOGFILE"
echo "$MYNAME-$VERSION" >> "$LOGFILE"

export DEBUGFLAG=""
[ "$(echo $VERSION|cut -b1)" = "0" ] && DEBUGFLAG="y"
echo $MYNAME | grep -q 'debug' && DEBUGFLAG="y"
tty | grep -q '^/dev/[^c]' && DEBUGFLAG="y"
case "$1" in
  -d|--debug) DEBUGFLAG="y";;
esac

[ "$DISPLAY" ] || LANG=C  # no nls as for console, without X
export TEXTDOMAIN=sfs_load
export OUTPUT_CHARSET=UTF-8
eval_gettext() {
  local myMESSAGE=$(gettext "$1")
  eval echo \"$myMESSAGE\"
}
TITLE="SFS-Load"
DESCRIPTION=$(gettext "on-the-fly")
LONG_TITLE="$TITLE $DESCRIPTION v.$VERSION"

usage() {
	echo "$LONG_TITLE" >&2
	echo $(gettext "Load or unload extra sfs files on-the-fly.") >&2
	echo "usage: $MYNAME [OPTION] [OPTION2 ...] [+|-][FILE_NAME] [+|-][FILE_NAME2] ...
      +FILE_NAME: load, same as FILE_NAME without '+'.
      -FILE_NAME: unload, same as --unload FILE_NAME.
  Options (short, or long):
    -u, --unload: unload the extra sfs files.
    -c, --cli: commandline interface, without dialog.
    -d, --debug: verbose output for debugging information.
    -h, --help: print this help and exit.
    -i, --info: print the base names of installed sfs files.
    -l, --list=FILE_LIST : read file names from the list file FILE_LIST.
    -q, --quiet: skip the 1st confirmation dialog.
    -v, --version: print version and exit.
    - (without file name): read file names from standard input. Imply '--cli'.
  Examples:
        ls -1 DIR_NAME | $MYNAME -
        $MYNAME - < FILE_LIST
        $MYNAME --list=FILE_LIST
      Note that FILE_LIST contains each filename per a line.
  Other options(sfs_load internal use):
    -n, --no-afterwork: suspend fixmenus and etc. after (un)loading.
    -a, --afterwork (without file name): only do suspended afterwork.
    -s, --skip-fixmenus: skip fixmenus (but do startup scripts and etc.).
    start: auto load at boot (called from /etc/initd.d/sfs_load)
    stop: clean up at shutdown (called from /etc/initd.d/sfs_load)" >&2
}
log() {
  #[ "$QUIET" != "" ] && return
  echo -e "$MYNAME: $*"
  echo -e "$MYNAME: $*"  >> "$LOGFILE"
}
info() {
  log "$*"
  [ "$GUI" = "" ] && return
  local OPT="--info"
  case "$1" in
  -*) OPT=$1; shift
  esac
  errmsg $OPT "$@"
}
splash() {
  [ "$1" = "--stop" -a "$GUI" = "" ] && return
  log "$*"
  [ "$GUI" = "" ] && return
  errmsg --splash "$@"
}
confirm() {
  [ "$GUI" = "" ] && debug "$*" && return
  local OPT="--ok-cancel"
  case "$1" in
  -*) OPT=$1; shift
  esac
  errmsg $OPT "$@"
}
debug() {
  [ "$DEBUGFLAG" = "" ] && return 
  log "debug: $*"
  #echo "$MYNAME: $*" >&2
}
error() {
  log "ERROR: $*"
  echo -e "$MYNAME: $*" >&2
  [ "$GUI" = "" ] && return
  errmsg --error "$@"
}
fatal() {
  case "$1" in
  --usage) shift
   [ "$@" ] && error "$@"
   usage; exit 1;;
  esac 
  error "fatal:$*"
 [ "$UNMOUNTME" ] && umount "$UNMOUNTME" && UNMOUNTME=""
 [ "$REENT" ] && [ -x "$MYPATH" ] && exec "$MYPATH" $ORGOPT --quiet
  exit 1
}
finish() {
 splash --stop
 [ "$UNMOUNTME" ] && umount "$UNMOUNTME" && UNMOUNTME=""
 [ "$REENT" = "" ] && exit 0
 [ -x "$MYPATH" ] && exec "$MYPATH" $ORGOPT --quiet
 exit
}
#v1.9.4: which gtkdialog
for P in gtkdialog4 gtkdialog3 gtkdialog; do
  P=$(which $P) && GTKDIALOG=$P && break
done
debug "Using $GTKDIALOG."
UNMOUNTME=""
export XPID=""
export DIALOG=""
waitsplash() {
  [ "$1" = "--stop" -a "$GUI" = "" ] && return
  [ "$XPID" != "" ] && [ $XPID -ne 0 ] && kill $XPID && XPID=""
  #LANG=$LANGORG	# recover lang environment
  if [ "$GUI" = "" ]; then
    [ "$*" ] && echo "$MYNAME: $*"
    return
  fi
  PBAR=""
  case "$1" in
  *progress) shift;
    [ -f "$COUNTFILE" ] && PBAR="<progressbar>
      <input>while [ -s $COUNTFILE ]; do tail -n 1 $COUNTFILE; sleep 1; done; echo 100</input>
     </progressbar>"
    ;;
  *stop) return;;
  *start|-*) shift;;
  esac
  S=$(gettext "Wait a moment ...")
  DIALOG='<window title="'$TITLE'" icon-name="gtk-execute"><vbox>
  <hbox>
  <pixmap><input file>'"$ICONS/mini-clock.xpm"'</input></pixmap>
  <text><input>echo -e -n "'"$* $S"'"</input></text>
  </hbox>
  '$PBAR'
  </vbox></window>'
  $GTKDIALOG -p DIALOG  -c >/dev/null &
  XPID=$!
  #LANG=C	# to be faster
}
processing() {
  [ "$GUI" != "" ] && [ "$XPID" = "" ] && waitsplash --start $(gettext "Processing...")
}
errmsg () {
  #echo $0 $@ >&2
  [ "$XPID" != "" ] && [ $XPID -ne 0 ] && kill $XPID && XPID=""
  TIMEOUT=0
  SPLASH=""
  BUTTONS="<hbox><button ok></button></hbox>"
  while [ "$1" != "" ]; do
   case "$1" in
    -*mark=*) MARK=$(echo $1| cut -d'=' -f2); shift;;
    -*mark) shift: MARK=$1; shift;;
    -*error)  MARK="error";shift;;
    -*warn*) MARK="warning";shift;;
    -*info) MARK="info"; shift;;
    -*yes*no*cancel) M="question"; shift
     BUTTONS="<hbox><button yes></button><button no></button><button cancel></button></hbox>"
     ;;  
    -*yes*no) M="question"; shift
     BUTTONS="<hbox><button yes></button><button no></button></hbox>"
     ;;
    -*ok*cancel) M="question"; shift
     BUTTONS="<hbox><button ok></button><button cancel></button></hbox>"
     ;;
    -*custom) BUTTONS=""; shift;;
    -*button*=*) BUTTONS=$(echo $1| cut -d'=' -f2-); shift;;
    -*button*) shift: BUTTONS=$1; shift;;
    -*splash) M="info"; BUTTONS=""; SPLASH="y"; shift;;
    -*timeout) M="info";shift
            if echo "$1" | grep -q '^[0-9][0-9]*$'; then
              TIMEOUT=$1; shift
            else
              TIMEOUT=10
            fi
            [ $TIMEOUT -lt 5 ] && BUTTONS=""
            ;;
   -*stop) return;;
   *) break;
   esac
  done
  [ "$MARK" != "" ] && M="$MARK"
  case "$M" in
  info|warning|error|question) MARK="dialog-$M";;
  *) MARK="$M";;
  esac
  ERRMSG="$@"
  [ "$ERRMSG" = "" ] && ERRMSG=$(gettext "An error occured")
  if [ "$GUI" = "" ]; then
    if [ "$M" = "error" -o "$M" = "warning" ]; then
      echo "$ERRMSG" >&2
    else
      echo "$ERRMSG"
    fi
    return
  else
    debug "$ERRMSG"
  fi
  [ "$TITLEICON" ] || TITLEICON="icon-name=\"gtk-$MARK\""
  export ERRMSG
  export DIALOG="<window title=\"$TITLE\" $TITLEICON><vbox>
    <hbox>
    <pixmap  icon_size=\"5\"><input file stock=\"gtk-$MARK\"></input></pixmap>
    <text><input>echo -en \"\$ERRMSG\"</input></text>
    </hbox>
    $CUSTOM
	$BUTTONS
	</vbox></window>"
  TITLEICON=""
  MARK=""
  CUSTOM=""
  if [ $TIMEOUT -eq 0 -a "$SPLASH" = "" ]; then
   RET=$($GTKDIALOG -p DIALOG -c || echo "$DIALOG" >&2)
   EXIT=abort
   eval "$RET"
   debug "EXIT=$EXIT"
   case $EXIT in
   Yes|yes|OK|Ok|ok) return 0
   esac
   return 1
  elif [ "$SPLASH" != "" ]; then
    rm -f $TMPFILE
    $GTKDIALOG -p DIALOG -c >$TMPFILE &
    XPID=$!
  else
   rm -f $TMPFILE
   $GTKDIALOG -p DIALOG -c >$TMPFILE &
   XPID=$!
   for I in $(seq 1 $TIMEOUT);do
     # 28feb10 to see exact PID
     ps | grep -qw "^[[:blank:]]*$XPID" || break
     sleep 1
   done
   [ "$XPID" != "" ] && [ $XPID -ne 0 ] && kill $XPID && XPID=""
   RET=$(cat $TMPFILE)
   rm -f $TMPFILE
  fi
}
kbyte2() {
	KB=$(echo $1|tr -dc '0123456789')
	[ "$KB" = "" ] && KB=0 
	if [ $KB -lt 1024 ]
	then echo ${KB}kB; return
	fi
	MB=$(expr $KB / 1024) # mega bytes
	[ $MB -lt 1024 ] && echo "${MB}MB" || echo "$(dc $MB 51.2 + 1024 \/ p|sed -e 's/\(^.*\..\).*/\1/')GB"
}
remove_item() {
  # remove_item VARIABLE ITEM
  local VARIABLE=$1
  local ITEM=$2
  local S=""
  local D=""
  local W=""
  eval S=\$$VARIABLE
  echo $S| grep -q "$ITEM" || return 1
  for W in $S; do
    echo "$W" | grep -q "$ITEM" || D="$D $W"
  done
  D=$(echo $D)
  eval $VARIABLE=\$D
}

HAS_MODULES=""
HAS_FONTS=""
HAS_PINS=""
HAS_DESKTOPS=""
HAS_SCRIPTS=""
HAS_STARTUPS=""
HAS_PROFILE=""
HAS_ICONS=""
HAS_GLIB_SCHEMA=""
SKIP_AFTERWORK=""	#v2.0.9
#v1.9.4
has_afterwork() {
  [ "$SKIP_HAS_AFTERWORK" ] && return	#v2.0.9
  HAS_MODULES=$(ls $MNTPNT/lib/modules 2>/dev/null)
  HAS_FONTS=$(find -L $MNTPNT/usr/share/fonts -mindepth 1 -not -type d -printf '/%P\n' 2>/dev/null)
  HAS_DESKTOPS=$(ls $MNTPNT/usr/share/applications 2>/dev/null)
  HAS_SCRIPTS=$(ls $MNTPNT/etc/init.d 2>/dev/null)
  HAS_STARTUPS=$(ls $MNTPNT/root/Startup 2>/dev/null)
  HAS_PROFILE=$(ls $MNTPNT/etc/profile.d 2>/dev/null) # 28 Oct 2011
  HAS_ICONS=$(find -L $MNTPNT/usr/share/icons/hicolor -not -type d -printf '/%P\n' 2>/dev/null)
  HAS_GLIB_SCHEMA=$(ls $MNTPNT/usr/share/glib-2.0/schemas/ 2>/dev/null)
  if [ -s "$MNTPNT/root/Choices/ROX-Filer/PuppyPin" ]; then
    mkdir -p "$MYTMPDIR"
    cp -f "$MNTPNT/root/Choices/ROX-Filer/PuppyPin" "$MYTMPDIR/PuppyPin-$ROOTNAME"
    [ -s "$MNTPNT/root/Choices/ROX-Filer/globicons" ] && cp -f  "$MNTPNT/root/Choices/ROX-Filer/globicons" "$MYTMPDIR/globicons-$ROOTNAME"
    HAS_PINS="PuppyPin-$ROOTNAME"
  fi
  # save to files
  echo $HAS_MODULES >> "$MYTMPDIR/has_modules"
  echo $HAS_FONTS >> "$MYTMPDIR/has_fonts"
  if [ "$ACTION" = 'load' ]; then
    echo $HAS_PINS >> "$MYTMPDIR/has_pins"
  else
    echo $HAS_PINS >> "$MYTMPDIR/has_pins_removed"
  fi
  echo $HAS_DESKTOPS  >> "$MYTMPDIR/has_desktops"
  echo $HAS_SCRIPTS >> "$MYTMPDIR/has_scripts"
  echo $HAS_STARTUPS >> "$MYTMPDIR/has_startups"
  echo $HAS_PROFILE >> "$MYTMPDIR/has_profiles"
  echo $HAS_ICONS >> "$MYTMPDIR/has_icons"
  echo $HAS_GLIB_SCHEMA >> "$MYTMPDIR/has_glib_schema"
}
afterwork() {
  [ "$NO_AFTERWORK" ] && return
  ###FIXME###[ "$ACTION" = "unload" ]
  #[ -z "$SKIP_FIXMENUS" ] && rm -f $MYTMPDIR/CLEANWHITE	# v1.9.1 #1.9.2: removed
  # recall has_...
  HAS_MODULES=$(cat "$MYTMPDIR/has_modules" 2>/dev/null | grep '.')
  HAS_FONTS=$(cat "$MYTMPDIR/has_fonts" 2>/dev/null | grep '.')
  HAS_PINS=$(cat "$MYTMPDIR/has_pins" 2>/dev/null | grep '.')
  HAS_PINS_REMOVED=$(cat "$MYTMPDIR/has_pins_removed" 2>/dev/null | grep '.')
  HAS_DESKTOPS=$(cat "$MYTMPDIR/has_desktops" 2>/dev/null | grep '.')
  HAS_SCRIPTS=$(cat "$MYTMPDIR/has_scripts" 2>/dev/null | sort -u | grep '.')
  HAS_STARTUPS=$(cat "$MYTMPDIR/has_startups" 2>/dev/null | sort -u | grep '.')
  HAS_PROFILE=$(cat "$MYTMPDIR/has_profiles" 2>/dev/null | sort -u | grep '.') 
  HAS_GLIB_SCHEMA=$(cat "$MYTMPDIR/has_glib_schema" 2>/dev/null | grep '.')
  rm -f "$MYTMPDIR"/has_*	# clean up has_... files
  [ "$HAS_MODULES" ] && waitsplash $(gettext "Depmod...") && depmod -a
  if [ "$HAS_FONTS" ]; then
    waitsplash $(gettext "Updating font cache...")
    HASFONTDIRS=$(echo "$HAS_FONTS"| sed -e 's,/[^/]*$,,'| uniq)
    if which mkfontscale &>/dev/null && which mkfontdir &>/dev/null ; then
      for D in $(find -L /usr/share/fonts -type d -printf '/%P ') ; do
        echo "$HASFONTDIRS" | grep -qw $D || continue
         mkfontscale "/usr/share/fonts$D"
         if [ "$(head -n 1 "/usr/share/fonts$D/fonts.scale")" = "0" ]; then
           rm -f "/usr/share/fonts$D/fonts.scale"
         else
           mkfontdir "/usr/share/fonts$D"
         fi
      done
    fi
    fc-cache -f
  fi
  [ "$HAS_ICONS" ] && gtk-update-icon-cache -f /usr/share/icons/hicolor/
  [ "$HAS_GLIB_SCHEMA" ] && /usr/bin/glib-compile-schemas /usr/share/glib-2.0/schemas/ 
  [ "$HAS_PINS" ] && mergepin
  [ "$HAS_PINS_REMOVED" ] && removepin
  if [ "$HAS_PINS" -o "$HAS_PINS_REMOVED" ] && which restartjwm &>/dev/null; then
    [ "$DISPLAY" ] && restartjwm
  else
    [ "$HAS_PINS" -o "$HAS_PINS_REMOVED" ] && restart_rox
    if [ "$HAS_DESKTOPS" -a -z "$SKIP_FIXMENUS" ]; then
      waitsplash $(gettext "Updating menu...")
      #v2.0: refresh default applications
      if [ "$ACTION" != "load" ] && which defaults-chooser &>/dev/null; then
        for F in $(ls /usr/local/bin/default* | grep -v 'defaults-changer') ; do
          P=$(head "$F" | grep '^[^#]*exec '| grep -vw 'defaults-chooser' | head -n 1 | sed -e 's/^.*exec[ ]//' -e 's/.\$@.*$//')
          [ "$P" ] || continue
          X=$(echo $P| sed -e 's/rxvt [ ]*-e //'| cut -d' ' -f1)
	      [ "$X" ] && [ "$(echo $X| cut -c1)" != "/" ] && X=$(which "$X")
	      [ -x "$X" ] && continue
	      echo '#!/bin/sh
defaults-chooser' > $F
          chmod +x $F
          log "'$(basename $F)' is reset."
        done
      fi
      fixmenus
      if [ "$DISPLAY" ] && pidof jwm &>/dev/null; then
        #v1.9.3: jwm -reload without flicker (01mico)
        jwm -reload &>/dev/null || jwm -restart
      fi
      waitsplash --stop
    fi
  fi
 if [ "$ACTION" = "load" ]; then
  if [ "$HAS_PROFILE" != "" ]; then # 28 Oct 2011
     for S in $HAS_PROFILE; do
       [ -s /etc/profile.d/$S ] && source /etc/profile.d/$S
     done
  fi
  [ "$HAS_SCRIPTS" = "" -a "$HAS_STARTUPS" = "" ] && return
  splash $(gettext "Running script background...")
  if [ "$HAS_SCRIPTS" != "" ]; then
    for S in $HAS_SCRIPTS; do
      [ -x /etc/init.d/$S ] && /etc/init.d/$S start &
      sleep 0.2
    done
  fi
  if [ "$HAS_STARTUPS" != "" -a "$DISPLAY" ]; then	#v1.9.2
    for S in $HAS_STARTUPS; do
      [ -x /root/Startup/$S ] && /root/Startup/$S &
      sleep 0.2
    done
  fi
  [ "$GUI" ] && sleep 2
 fi
}
mergepin() {
  log "mergepin: $HAS_PINS"
  PUPPYPIN="$HOME/Choices/ROX-Filer/PuppyPin"
  MASTERGLOB="$HOME/Choices/ROX-Filer/globicons"
  GLOBICONS="$HOME/.config/rox.sourceforge.net/ROX-Filer/globicons"
  [ -f "$PUPPYPIN" ] || return
  [ "$HAS_PINS" ] || return
  [ -s "$GLOBICONS" ] || cp "$MASTERGLOB" "$GLOBICONS"
     grep -v '</pinboard>' "$PUPPYPIN" > "$MYTMPDIR/PuppyPin"
     grep -v '</special-files>' "$GLOBICONS" > "$MYTMPDIR/globicons"
     PINUP=""
     GLOBUP=""
     for F in $HAS_PINS; do
       # PuppyPin
       P=$(grep '<icon ' "$MYTMPDIR/$F"| head -n 1| cut -s -d'>' -f2 | cut -s -d'<' -f1 | tr -d ' ')
       if ! grep -qw "$P" "$PUPPYPIN" ; then
         grep '<icon ' "$MYTMPDIR/$F" >> "$MYTMPDIR/PuppyPin" && PINUP="y"
       fi
       # globicons
       G="$MYTMPDIR/globicons-"$(echo $F| cut -s -d'-' -f2-)
       P=$(grep '<rule[ ]match=' "$G"| head -n 1| cut -s -d'"' -f2 | tr -d ' ')
       if ! grep -qw "$P" "$GLOBICONS"; then
         grep -E '<([/]*rule|icon)' "$G" >> "$MYTMPDIR/globicons" && GLOBUP="y"
       fi
     done
     echo '</pinboard>' >> "$MYTMPDIR/PuppyPin"
     echo '</special-files>' >> "$MYTMPDIR/globicons"
     [ "$PINUP" != "" -a -s "$MYTMPDIR/PuppyPin" ] && cp -f "$MYTMPDIR/PuppyPin" "$PUPPYPIN"
     [ -f "$GLOBICONS" ] || return
     [ "$GLOBUP" != "" -a -s "$MYTMPDIR/globicons" ] && cp -f "$MYTMPDIR/globicons" "$GLOBICONS"
}
removepin() {
  log "removepin: $HAS_PINS_REMOVED"
  PUPPYPIN="$HOME/Choices/ROX-Filer/PuppyPin"
  [ -f "$PUPPYPIN" ] || return
  [ "$HAS_PINS_REMOVED" ] || return
  PLIST=""
  for F in $HAS_PINS_REMOVED; do
    PLIST="$PLIST
"$(grep '<icon ' "$MYTMPDIR/$F"| cut -s -d'>' -f2 | cut -s -d'<' -f1 | tr -d ' ')
  done
  PLIST=$(echo $PLIST| tr ' ' '|')
  debug "PLIST=$PLIST"
  grep -v -E "$PLIST" "$PUPPYPIN" >  "$MYTMPDIR/PuppyPin"
  [ -s "$MYTMPDIR/PuppyPin" ] && cp -f "$MYTMPDIR/PuppyPin" "$PUPPYPIN"
}
restart_rox() {
 [ "$DISPLAY" ] || return  #v1.9.2
 PIDS=$(pidof ROX-Filer)
 #PIDW=$(pidof "$CURRENTWM")
 ROXRESTART=""
 #if [ "$PIDW" != "" ]; then
  if [ "$PIDS" != "" ]; then
   for PID in $PIDS; do
    kill $PID
   done
  fi
  rox -p "$HOME/Choices/ROX-Filer/PuppyPin" && log "ROX-Filer restarted." || log "ROX-Filer restart failed." 
  ROXRESTART="y"
  #sleep 2
 #fi
}
keyword() {
  #echo "$1"| head -n 1| tr '|@' '  '| cut -d' ' -f1
  # 13 Nov 2011
  echo "$1"| head -n 1| tr '|@' '  '| sed -e 's/ ([^ ]*)$//'
}
#v1.9.3
make_combo() {
  NODUP=""
  [ "$1" = '-' ] && NODUP="y" && shift
  LIST="$@"
#  [ "$ADDNULL" = "" ] || echo "$@" | grep -q "$NULL" || LIST="$@ $NULL"
  if [ -z "$NODUPE" ]; then
     echo "$LIST"| sed -e 's!\([^ ][^ ]*\)!<item>\1</item>!g' |tr '@' ' ' 
     return
  fi
 CHOICE=""
 for ONEITEM in $LIST;do
  echo "$CHOICE" | grep -q ">$(keyword $ONEITEM)[ |]" && continue
  CHOICE="$CHOICE
<item>$(echo $ONEITEM|tr '@' ' ')</item>"
 done
 echo "$CHOICE"
}
check_sfs_version() {
 #local EXTRASFS=$1
EXTRASFSVER=$(LANG=C disktype "$EXTRASFS"| grep version | cut -f 4 -d " " | cut -c 1)
[ $EXTRASFSVER ] || EXTRASFSVER=0
if ! [ $EXTRASFSVER -eq $SFSVER ]; then
  MSG=$(eval_gettext "Required squashfs version \${SFSVER}, but '\$FILENAME' seems squashfs version \${EXTRASFSVER}.")
  [ "$GUI"  = "" ] && info "$MSG" && return 1
  if [ "$SFSCONVERT" ]; then
    MSG="$MSG\n$(gettext "You can convert the version with the 'SFScovert'.")"
  fi
  fatal "$MSG"
 fi
 return 0
}

LOADEDLIST=""
QUEUED=$(gettext "queue"| tr ' ' '%')
add_new_sfs_list() {
  [ $1 ] || return 1
  local F=$(basename "$1")
  local Q=$2
  [ "$Q" ] && Q="@($QUEUED)"
  echo $F | grep -q '\.sfs$' || return  1 # remove pupsave
  [ "$F" = "$SFSBASE" ] && return 1
  [ "$F" = "$ZDRBASE" ] && return 1
  [ "$F" = "$ADRBASE" ] && return 1	#v1.9.2
  [ "$F" = "$YDRBASE" ] && return 1	#v2.0.8
  echo "$LOADEDLIST" | grep -qw "$F" && return 1  # already listed
  if [ "$LOADEDLIST" = "" ]; then
    LOADEDLIST="$F$Q"
  else
    LOADEDLIST="$LOADEDLIST
$F$Q"
  fi
  return 0
}
loaded_sfs_list() {
  [ "$SFSMODE" != "" ] || return
  LOADEDLIST=""
  # looking up BOOTCONFIG
  for F in $LASTUNIONRECORD; do
    add_new_sfs_list $F
  done
  # directly see the unonfs layer (if we can)
  MOUNTINFO=$(mount | grep '^unionfs'| grep 'dirs=')
  [ "$MOUNTINFO" != "" ] && MOUNTINFO=$(echo $MOUNTINFO| sed -e 's/^.*dirs=//' | tr -d '()'| tr ':' ' ')
  if [ "$MOUNTINFO" != "" ]; then
    for ITEM in $MOUNTINFO; do
	 #debug "$ITEM"
	  D=$(echo "$ITEM"| cut -d'=' -f1)
	  [ "$D" != "" ] && LOOPDEV=$(LANG=C df "$D"| tail -n 1 | cut -d' ' -f1) || LOOPDEV=""
	  if [ "$LOOPDEV" != "" ]; then
	    PATHNAME=$(losetup $ALLUSED | grep "^$LOOPDEV:" | cut -s -d' ' -f3)
		if [ "$PATH" != "" ] ;then
		  F=$(basename "$PATHNAME")
		   add_new_sfs_list $F
		fi
	  fi
    done
  fi
  # looking up losetup
  USEDLIST=$(losetup $ALLUSED)
  LOOPDEVS=$(echo "$USEDLIST"| cut -d':' -f1)
  if [ "$LOOPDEVS" ]; then
   PUP_ROS=$(LANG=C df | grep ' /initrd/pup_ro')
   for LOOPDEV in $LOOPDEVS; do
     if echo "$PUP_ROS" | grep -qw "^$LOOPDEV"; then
       F=$(echo "$USEDLIST"| grep -w "$LOOPDEV"| tr -s ' ' | cut -d' ' -f3 | sed -e 's/^(//' -e 's/)$//')
       add_new_sfs_list $F
     fi
   done
  fi
}
QUEUELIST=""
queued_sfs_list() {
  # looking up queue
  for F in $EXTRASFSLIST; do
    echo $LASTUNIONRECORD | grep -qw $F && continue  #v2.0
    echo "$LOADEDLIST" | grep -qw $F && continue
    QUEUELIST="$QUEUELIST
 $F"
  done
  QUEUELIST=$(echo "$QUEUELIST"| grep '.')  # remove blank line
  # report
  #echo "$QUEUELIST"
}
INSTALLEDLIST=""
installed_sfs_lst() {
#PKGDIR="/root/.packages"
#INSTALLEDLIST="$PKGDIR/user-installed-packages"
  [ "$1" != "" ] && QUERY=$(basename "$1" .sfs) || QUERY=""
  [ -f "$INSTALLEDLIST" ] || return 1
   if [ "$QUERY" != "" ]; then
     grep -q "|$QUERY.sfs|" "$INSTALLEDLIST"
	 return
   fi
   grep -v '\.pet|' "$INSTALLEDLIST"| grep '\.sfs|' | cut -d'|' -f8
   return
}
ALLSFSLIST=""
BASELIST=""
loadable_sfs_list() {
	debug "loadable_sfs_list $@"
	# puppy system file names
	#SFSBASE=$DISTRO_PUPPYSFS
	#ZDRBASE=$DISTRO_ZDRVSFS
	#WRONGVERSION=/tmp/$(basename $0)/wrong_sfs_version
	[ "$MYTMPDIR" != "" ] && mkdir -p "$MYTMPDIR"
	rm -f "$WRONGVERSION"
	if [ "$DISTRO_PUPPYSFS" = "" ]; then
      DISTRO_PUPPYSFS=$SFSBASE
    fi
    if [ "$DISTRO_VERSION" = "" ]; then
      DISTRO_VERSION=$(basename $DISTRO_PUPPYSFS .sfs| tr '_' '-'| cut -s -d'-' -f2)
    fi
    if [ "$DISTRO_VERSION" = "" -a -s /etc/puppyversion ]; then
      DISTRO_VERSION=$(cat /etc/puppyversion)
    fi
    if [ "$DISTRO_FILE_PREFIX" = "" ]; then
      DISTRO_FILE_PREFIX=$(echo $DISTRO_PUPPYSFS| tr '_' '-'| cut -d'-' -f1)
    fi
    if [ "${DISTRO_ZDRVSFS}" = "" ]; then
      [ "$ZDRBASE" != "" ] && DISTRO_ZDRVSFS=$ZDRBASE || DISTRO_ZDRVSFS="zdrv"
    fi
    if [ "${DISTRO_ADRVSFS}" = "" ]; then	#v1.9.2
      [ "$ADRBASE" != "" ] && DISTRO_ADRVSFS=$ADRBASE || DISTRO_ADRVSFS="adrv"
    fi
    if [ "${DISTRO_ADRVSFS}" = "" ]; then	#v2.0.11
      [ "$YDRBASE" != "" ] && DISTRO_YDRVSFS=$YDRBASE || DISTRO_A\YDRVSFS="ydrv"
    fi
    SFSSTR="squashfs, version $SFSVER"
	ALLSFSLIST=""
	#if [ "$PUPHOME" != "" -a  "$PUPHOME" != "/" ]; then
	if [ "$PUPHOME" != "" ]; then
	  ALLSFSLIST=$(find -L "$PUPHOME" -mindepth 1 -maxdepth 1 -name '[^.]*.sfs' -type f )
	  if [ "$PSUBDIR" != "" ]; then	# v1.9
	    ALLSFSLIST="$(find -L "$PUPHOME/$PSUBDIR" -maxdepth 1 -name '[^.]*.sfs' -type f)
$ALLSFSLIST"
	  fi
	  if [ "$SFSMODE" = "cd" ]; then	# v1.3.9
		FOUND=""
		if is_on_cd; then
		  ALLSFSLIST="$FOUND
$ALLSFSLIST"
		fi
	  fi
	elif [  "$SFSTYPE" = "iso9660"  ]; then	# v1.3.9
		is_on_cd
		ALLSFSLIST="$FOUND"
	fi
	#debug "$ALLSFSLIST"
	#debug "${DISTRO_FILE_PREFIX};$DISTRO_VERSION;${DISTRO_PUPPYSFS};${DISTRO_ZDRVSFS};"
    [ "$ALLSFSLIST" ] || return	# v1.3.9
	 BASEFIXEDSFSLIST=""
 for ONESFS in $ALLSFSLIST
 do
  BASEONESFS="`basename $ONESFS`" #100711
  [ "`echo "$BASEONESFS" | grep '^z'`" != "" ] && continue #100809
  [ "`echo "$BASEONESFS" | grep '^pup_'`" != "" ] && continue #100809
  [ "`echo "$BASEONESFS" | grep "^${DISTRO_FILE_PREFIX}\\-[0-9][0-9][0-9]\\.sfs$"`" != "" ] && continue #100809
  [ "`echo "$BASEONESFS" | grep "^${DISTRO_FILE_PREFIX}[rz]"`" != "" ] && continue #100809
  [ "`echo "$BASEONESFS" | grep '_[0-9][0-9][0-9]' | grep -v "$DISTRO_VERSION"`" != "" ] && continue
  [ "`echo "$BASEONESFS" | grep "^${DISTRO_PUPPYSFS}"`" != "" ] && continue #100913
  [ "${DISTRO_ZDRVSFS}" ] && [ "`echo "$BASEONESFS" | grep "^${DISTRO_ZDRVSFS}"`" != "" ] && continue #100913
  [ "${DISTRO_ADRVSFS}" ] && [ "`echo "$BASEONESFS" | grep "^${DISTRO_ADRVSFS}"`" != "" ] && continue #v1.9.2: saluki
  [ "${DISTRO_YDRVSFS}" ] && [ "`echo "$BASEONESFS" | grep "^${DISTRO_YDRVSFS}"`" != "" ] && continue #v2.0.11
  #w020 other compat-distro sfs need to be eliminated...
  [ "`echo "$BASEONESFS" | grep '\\-[0-9][0-9][0-9]\\.sfs' | grep -v "$DISTRO_VERSION"`" != "" ] && continue
  #echo $EXTRASFSLIST | grep -q "$BASEONESFS" && continue # remove already loaded
  echo "$LOADEDLIST" | grep -qw "$BASEONESFS" && continue # remove already loaded
  #v423 eliminate wrong squashfs version...
  if [ "`disktype ${ONESFS} | grep "$SFSSTR"`" = "" ];then
   echo "$BASEONESFS" >> "$WRONGVERSION"
   continue
  fi
  #[ "`echo "$BASEFIXEDSFSLIST" | grep "$BASEONESFS"`" != "" ] && continue #100711 avoid duplicates.
  BASEFIXEDSFSLIST="$BASEFIXEDSFSLIST
$BASEONESFS"
 done
    BASELIST=$( echo "$BASEFIXEDSFSLIST" | sort -u) # 1feb11
    #echo "$BASEFIXEDSFSLIST" | sort | uniq
}
main_dialog() {
	waitsplash $(gettext "Probing...")
	BOXHEIGHT=150
	LOADVISIBLE="enabled"
	UNLOADVISIBLE="eabled"
	if [ "$ACTION" = "unload" ]; then
	  LOADVISIBLE="disabled"
	  UNLOADVISIBLE="enabled"
	fi
	[ "$BASELIST" = "" ] && loadable_sfs_list # 1feb11 # 21feb11
    #BASELIST=$(loadable_sfs_list)
    if [ "$BASELIST" != "" ]; then
      LOADCOMBO='<combobox tooltip-text="'$(gettext "Choose from the pulldown or drag a sfs file here. Or, you can copy and paste a weblink here.")'">
	 <variable>FILE1</variable><item>""</item>'$(make_combo $BASELIST)'<visible>'$LOADVISIBLE'</visible></combobox>'
    else
     LOADCOMBO='<hbox><entry tooltip-text="'$(gettext "Type the path or drag a sfs file here. Or, you can copy and paste a weblink here.")'">
     <variable>FILE1</variable><visible>'$LOADVISIBLE'</visible></entry>
     <button tooltip-text="'$(gettext "Browse and select a sfs file.")'">
        <input file stock="gtk-open"></input>
        <variable>FILE_BROWSE</variable>
		<action type="fileselect">FILE1</action>
		<visible>'$LOADVISIBLE'</visible>
      </button></hbox>'
      ENABLEBROWS='<action>enable:FILE_BROWSE</action>'
    fi
    WRONGTEXT=""
    if [ -s "$WRONGVERSION" ]; then
      WRONGVERSION=$(sort -u "$WRONGVERSION")
      WRONGVERSION=$(echo $WRONGVERSION | sed -e 's/ /\\n/g')
      WRONGTEXT='<text><input>echo -en "'$(gettext "The followings are the different version"):'\n'$WRONGVERSION'"</input><visible>disabled</visible></text>'
    fi
	UNLOADCOMBO=""
	if [ "$ALREADY_SFS_LIST" != "" ]; then
	  UNLOADABLE="enabled"
	  UNLOADCOMBO='<list tooltip-text="'$(gettext "Select the sfs file you want to unload.")'">
	  <variable>UNLOADSFS</variable>'$(make_combo $ALREADY_SFS_LIST)'<visible>'$UNLOADVISIBLE'</visible></list>'
	  [ "$QUEUELIST" ] && UNLOADCOMBO="$UNLOADCOMBO
	  "'<text><input>echo -en "'$(gettext "Some are not loaded but in the queue.")'"</input></text>'
	else
	 UNLOADABLE="disabled"
	 UNLOADCOMBO='<text><label>('$(gettext "Nothing loaded.")')</label></text>'
	fi
	DIALOG='<window title="'$LONG_TITLE'"><vbox>
    <hbox>
    <pixmap><input file>'$MYICON'</input></pixmap>
	<text width-chars="40"><input>echo -en "'$(gettext "You can load or unload extra SFS files on-the-fly.")'"</input></text>
   </hbox>
	<hbox><frame '$(gettext "Load SFS")'>
	<vbox height-request="'$BOXHEIGHT'">
   '"$LOADCOMBO
	$WRONGTEXT"'
	</vbox>
	<button tooltip-text="'$(gettext "Which SFS do you want to load?")' '$(gettext "Click here after you choose the extra sfs file.")'">
	<input file stock="gtk-add"></input><label>'$(gettext 'Load')'</label>
	<action>EXIT:Load</action><visible>'$LOADVISIBLE'</visible></button>
	</frame>
	<frame '$(gettext "Unload SFS")'>
	<vbox height-request="'$BOXHEIGHT'">
   '$UNLOADCOMBO'
    </vbox>
     <button tooltip-text="'$(gettext "Which SFS do you want to unload?")' '$(gettext "Click here after you choose the extra sfs file.")'">
	<input file stock="gtk-remove"></input><label>'$(gettext 'Unload')'</label>
	<action>EXIT:Unload</action><visible>'$UNLOADVISIBLE'</visible></button>

    </frame></hbox>
    <hbox>
     <button><label>'$(gettext 'Quit')'</label><input file stock="gtk-quit"></input><action>EXIT:Abort</action></button>
    </hbox>
	</vbox></window>'
	waitsplash --stop
	RET=$($GTKDIALOG -c -p DIALOG || echo "$DIALOG" >&2)
	debug "$RET"
	eval "$RET"
	if [ "$EXIT" = "Unload" ]; then
	  ACTION="unload"
	  EXTRASFS=$(keyword "$UNLOADSFS")
	  debug "EXTRASFS=$EXTRASFS"
	  return
	elif [ "$EXIT" = "Load" ]; then
	  ACTION="load"
	  F=""
	  [ "$FILE1" != "" ] && FILE1=$(keyword "$FILE1")
	  [ "$FILE1" != "" ] && F=$(echo "$ALLSFSLIST" | grep -w "$FILE1"| head -n 1)
	  [ "$F" != "" ] && EXTRASFS="$F" || EXTRASFS="$FILE1"
	  debug "EXTRASFS=$EXTRASFS"
	  return
	fi
	[ "$EXIT" = "OK" ]   #|| finish
}
choosepart() {	# v1.3.9
  case "$FILEISAT" in
  cd)	WHERE=$(gettext "CD");;
  url)	WHERE=$(gettext "internet");;
  remote)	WHERE=$(gettext "network");;
  tmpfs)	WHERE=$(gettext "RAM");;
  unionfs)	WHERE=$(gettext "Puppy space(RAM but need to copy)");;
  loop)	WHERE=$(gettext "loop device");;
  ata)	WHERE=$($gettext "internal strage");;
  mnt)	WHERE="";;
  *) WHERE=$FILEISAT;;
  esac
  [ "$WHERE" ] || WHERE=$(gettext "external storage")
  MSG1=$(gettext "Select the partition to save the SFS file, preferably to be the same place you will save your session as a 'pupsave'.")
  MSG2=$(printf "$(gettext "The SFS file is now at %s.")" "$WHERE")
  _RAM=$(gettext  "You can select 'RAM' if you don't like to touch your Hard Disk, only if you have enough RAM(+swap).")
  _NORAM=$(gettext "Or, select 'NOCOPY' if you do not like to make a copy of the SFS file.")
  _CD=$(gettext "Select 'CD' if you are going to save the session back to the live CD.")
  PCPARTS="$(probepart|grep -E 'ext2|ext3|ext4|reiserfs|msdos|vfat|minix|ntfs')"
  local TOPPART="$1"
  # Offer RAM only if enough
  RAMPART=""
  if [ "$FILEISAT" != "tmpfs" -a "$FILEISAT" != "ata" ]; then
   RAMPART=$(echo "RAM|($(gettext "Available"):$(kbyte2 $RFREE))"|tr ' ' '_')
   REST=$(($RFREE - $FILESIZE))
   if [ $REST -gt $RESERVRAM ]; then
    MSG2="$MSG2 $_RAM"
    if [ "$PCPARTS" = "" ]; then
     PCPARTS=$RAMPART
    else
     PCPARTS="$PCPARTS
$RAMPART"
    fi
   fi
  fi
  if [ "$TOPPART" = "CD" ]; then
    MSG2="$MSG2 $_CD"
    PCPARTS="$PCPARTS
CD|($(gettext "multisession"))"
    [ "$DESTDIR" ] || TOPPART=""   # normally, cd is not the default
  fi
  if [ $FILESIZE -gt 0 -a -z "$NEEDTOMOVE" ]; then  # already somewhere local, not need to download
    MSG2="$MSG2 $_NORAM"
    PCPARTS="$PCPARTS
NOCOPY"
  fi
  PCPARTS=$(echo "$PCPARTS" | grep '[a-zA-Z]')  # remove blank line
  if [ $(echo "$PCPARTS" | wc -l) -le 1 ]; then
    PARTSPEC=$PCPARTS
    PART=$(echo $PARTSPEC| cut -d'|' -f1)
    PART=$(echo $PART| cut -d '/' -f3)
    return 0
  fi
  [ "$TOPPART" ] && TOPPART=$(echo "$PCPARTS" | grep -w "$TOPPART")  # precaution
  [ "$TOPPART" ] && TOPPART=$TOPPART" ($(gettext "Recommended"))"
  DIALOG='<window title="'$MYNAME'"><vbox>
    <hbox>
    <pixmap  icon_size="5"><input file stock="gtk-dialog-question"></input></pixmap>
    <text><input>echo -e -n "'$MSG'"</input></text>
    </hbox>
  <frame '$(gettext "Where to save?")'>
  <text><input>echo -en "'"$MSG1\n\n$MSG2"'"</input></text>
  <combobox><variable>PARTSPEC</variable>'$(make_combo - $TOPPART $PCPARTS)'</combobox>
  </frame>
  <hbox><button ok></button><button cancel></button></hbox>
  </vbox></window>'
  waitsplash --stop
  RET=$($GTKDIALOG -c -p DIALOG || echo "$DIALOG" >&2)
  eval "$RET"
  PART=$(echo $PARTSPEC| cut -d'|' -f1)
  PART=$(echo $PART| cut -d '/' -f3)
  [ "$EXIT" = "OK" ]
}
mountedpart(){
  # see if the partition is mounted
  [ $1 ] || return
  MNTPNT=$(df -a| grep -m1 -w "^/dev/$1"| cut -d'%' -f2| tr -d ' ') #v2.0.11
  MNTPNT=$(echo $MNTPNT)  # remove blank
  echo $MNTPNT
  [ "$MNTPNT" != "" ]     # test if exists
}
mountpart(){
  # except cd(iso9660)
  [ $1 ] || return
  local PART=$1
  case "$1" in
  /dev/*|/mnt/*) PART=$(echo $1 | cut -d'/' -f3);;
  /*) PART=$(echo $1 | cut -d'/' -f2);;
  esac
  mountedpart $PART >/dev/null && return
  local TYPE=$(probepart | grep -w "^/dev/$PART"| cut -s -d'|' -f2)
  local MPART="$PART"
  local OPT=""
  local NLS_PARAM=""
  case "$TYPE" in
  vfat) NLS_PARAM=""
    if [ -f /etc/codepage ]; then #100127...
	  grep -q -i '850' /etc/codepage && [ "$(echo $LANG|cut -d'.' -f1)" != "en_US" ] && NLS_PARAM=",codepage=850"
	  grep -q -i '852' /etc/codepage && NLS_PARAM=",codepage=852,iocharset=iso8859-2"
    fi
    echo $LANG | grep -q -i '\.utf' && NLS_PARAM="$NLS_PARAM,utf8"
    OPT="-t vfat -o shortname=mixed,quiet${NLS_PARAM}"
    ;;
   ntfs)
    OPT="-t ntfs"
    ;;
  esac
  if LANG=C mount | grep -w "/mnt/$MPART" >&2; then
    umount /mnt/$MPART || return
  fi
  mkdir -p /mnt/$MPART || return
  TIMEUP=3
  #UNMOUNTME=""
  mount $OPT /dev/$PART /mnt/$MPART || return
  UNMOUNTME=/dev/$PART
  MNTPNT=/mnt/$MPART
}
download_file() {
  URLSPEC="$1"
  #v1.9.4: never use external
  # has extra program?
  #EXTPROG=$(which download_file)
  #if [ "$EXTPROG" ]; then
  #  $EXTPROG "$@"
  #  return
  #fi
  # internal func
  for P in urxvt rxvt xterm; do
    which $P &>/dev/null && RXVT="$P" && break
  done
  [ "$RXVT" != "" ] || fatal "Terminal program 'rxvt' not found." 
  waitsplash $(gettext "Downloading...")
  local myLOG="/tmp/${MYNAME}_download.log"
  FILENAME=$(basename "${URLSPEC}")
  # check the source size
  wget -t 2 -T 20 --waitretry=20 --spider -S "${URLSPEC}" >"$myLOG" 2>&1
  grep -q '200 OK' "$myLOG" || return 1
  SIZEB_ONLINE=$(grep -m 1 'Length: ' "$myLOG"| cut -d':' -f2| tr -dc '0-9')
  [ $SIZEB_ONLINE ] || SIZEB_ONLINE=0
  SIZEK_ONLINE=$(($SIZEB_ONLINE / 1024))
  debug "SIZEK_ONLINE=$SIZEK_ONLINE;"
  # enough space?
  SSIZE=$SIZEK_ONLINE
  REST=$(($DFREE - $SSIZE))
  [ $REST -gt $RESERVHDD ] || fatal "$(gettext "There is not enough space to download.")\n$(gettext "Source"): $(kbyte2 $SSIZE) --> $(gettext "Free"): $(kbyte2 $DFREE)"
  # now download
  rm -f "$FILENAME"
  #wget -t 3 -T 20 --waitretry=20 "${URLSPEC}"
  $RXVT -geometry 80x10+0+0 -bg orange -fg black -title "$(gettext 'Downloading...')" -e  wget -t 3 -T 20 --waitretry=20 "${URLSPEC}"
  STATUS=$?
  waitsplash --stop
  return $STATUS
}
CONTINUE_BUTTON='<button tooltip-text="'$(gettext "Back to SFS_Load")'"><input file stock="gtk-apply"></input><label>'$(gettext "Continue")'</label><action>EXIT:OK</action></button>'
QUIT_BUTTON='<button><input file stock="gtk-quit"></input><label>'$(gettext "Quit")'</label><action>EXIT:Quit</action></button>'
launcher() {
  #v2.0
	#debug "$@"
  [ $# -gt 0 ] || return
  CONTINUE_BUTTON='<button tooltip-text="'$(gettext "Back to SFS_Load")'"><input file stock="gtk-apply"></input><label>'$(gettext "Skip")'</label><action>EXIT:OK</action></button>'
  # language
  for lng in C $(echo $LANGUAGE|cut -d':' -f1) $LC_ALL $LANG;do :;done   # ex.    ja_JP.UTF-8
  lng1=$(echo $lng|cut -d'.' -f1)      # ex.   ja_JP
  lng2=$(echo $lng|cut -d'_' -f1)   # ex.   ja
  lngs="$lng $lng1 $lng2"
  # listing up
  APPLIST=""
  ID=0
  for P in $@; do
    [ "${P##*.}" = "desktop" ] || continue
    [ "$echo $P| cut -b1)" = "/" ] && F="$P" || F="/usr/share/applications/$P"
    [ -f "$F" ] || continue
    CMD=$(grep '^[[:blank:]]*Exec=' "$F"| head -n1 | cut -s -d'=' -f2)  #v2.0
    [ "$CMD" ] || continue
    LABEL=$(grep '^[[:blank:]]*Name=' "$F"| head -n1 | cut -s -d'=' -f2)  #v2.0
    for L in $lngs; do
      T=$(grep "^[[:blank:]]*Name\[$L\]=" "$F"| head -n1 | cut -s -d'=' -f2)  #v2.0
      [ "$T" ] && LABEL=$T && break
    done
    ID=$(($ID + 1))
    APPLIST="$APPLIST
P$ID|$CMD|$LABEL"
  done
  APPLIST=$(echo "$APPLIST"| grep '^P'| tr ' ' '@') # remove blank 
  [ "$APPLIST" ] || return
  #debug "$APPLIST"
  CHOICES=""
  N=$(echo "$APPLIST"| wc -l)
  if [ $N -eq 1 ]; then
    ICON=$(grep "^[[:blank:]]*Icon=" "$F"| head -n1 | cut -s -d'=' -f2) #v2.0
    #debug "ICON=$ICON"
    if [ "$ICON" ]; then
      if [ ! -s "$ICON" ]; then
        ICONPATH=""
        [ -s "$JWMRC" ] && ICONPATH=$(grep '^[^-]*<IconPath' "$JWMRC" | sed -e 's/<[^<]*>//g')
        #debug "ICONPATH=$ICONPATH"
        if [ "$ICONPATH" ]; then
          ICON=$(find -L $ICONPATH -maxdepth 1 -not -type d -name $ICON* | head -n 1) #v2.0
        fi
      fi
    fi
    #debug "ICON=$ICON"
    CHOICES='<text width-request="240"><label>"'$(echo $APPLIST| cut -d'|' -f3| tr '@' ' ')'"</label></text>'
    [ -s "$ICON" ] && CHOICES='<hbox><pixmap><input file>'$ICON'</input></pixmap>
    '$CHOICES'</hbox>'
    P1="true"
    #FRM=$(gettext "Application")
  else
   DEF="true"
   for ALINE in $APPLIST; do
    CHOICES="$CHOICES
<radiobutton><variable>$(echo $ALINE|cut -d'|' -f1)</variable><label>\"$(echo $ALINE|cut -d'|' -f3|tr '@' ' ')\"</label><default>$DEF</default></radiobutton>"
    DEF="false"
   done
   #FRM=$(gettext "Choose the application")
  fi
  MSGBOX=""
  [ "$MSG" ] && MSGBOX='<text><input>echo -en "'$MSG'"</input></text>'
  M='info'
  [ "$OPT" = "--warning" ] && M='warning'
  MSGBOX='<hbox><pixmap  icon_size="5"><input file stock="gtk-dialog-'$M'"></input></pixmap>
    '$MSGBOX'</hbox>'
  EXTRABUTTON=""
  [ "$REENT" ] && EXTRABUTTON=$CONTINUE_BUTTON
  TITLEICON='icon-name="gtk-dialog-question"'
  export DIALOG='<window title="'$TITLE'" '$TITLEICON'><vbox>
   '$MSGBOX'
   <hbox>
   <pixmap  icon_size="5"><input file stock="gtk-dialog-question"></input></pixmap>
   <text><label>"'$(gettext "Do you want to run the application right now?")'"</label></text>
  </hbox>
  <frame>'$CHOICES'</frame>
  <hbox>
  <button><input file stock="gtk-execute"></input><label>'$(gettext "Run")'</label><action>EXIT:Execute</action></button> 
  '$EXTRABUTTON'
  '$QUIT_BUTTON'
  </hbox>
  </vbox></window>'
  waitsplash --stop
  RET=$($GTKDIALOG -c -p DIALOG || echo "$DIALOG" >&2)
  eval "$RET"
  [ "$EXIT" = "OK" ] ||  REENT=""	#v1.9.4
  [ "$EXIT" = "Execute" ] || return
  for ALINE in $APPLIST;do
     P=$(echo $ALINE| cut -d'|' -f1)
     eval T=\${$P}
     [ "$T" = "true" ] || continue
     CMD=$(echo $ALINE| cut -s -d'|' -f2| tr '@' ' ')
     [ "$CMD" ] || return
     [ -f /etc/profile ] && source /etc/profile
     $CMD &
     finish
  done
}
# v1.9
make_petget_script() {
  #v1.9.8: fix path
  #v2.0.12: ydrv, see whiteout only on pup_rw
  # make a script named 'petget'
  # a trick to suppress snapmergepuppy (pup_event_frontend_d does not issue snapmergepuppyrequest if petget is running.)
  [ -s $MYTMPDIR/petget -a -x $MYTMPDIR/petget ] && return	# created before
  echo '#!/bin/sh
PARAM=$1
LANG=C
SNAP="/initrd/pup_rw/"
BASE="/initrd/pup_ro1/"
# pup_rw
cd $SNAP || exit 0
# .wh..wh..opq or .wh.__dir_opaque(for aufs older version)
find . -mindepth 2 -type f -name ".wh..wh..opq" -o -name ".wh.__dir_opaque" |
while read W; do
  D=$(dirname "$W")
  rm -fr "$BASE$D"
  rm -f "$BASE$W"
  rm -f "$SNAP$W"
  F=$(find /initrd/pup_ro{?,??}/"$D" /initrd/pup_[ayz]/"$D" -maxdepth 1 2>/dev/null| grep -vw "^$PARAM"| head -n 1)
  [ "$F" ] && touch "$BASE$W"
done
# normal whiteout
find . -mindepth 2 -type f -name ".wh.*" -not -name ".wh..wh.*" |
while read W; do
 D=$(dirname "$W")
 B=$(basename "$W"|cut -b5-)
 rm -fr "$BASE$D/$B"
 rm -f "$BASE$W"
 rm -f "$SNAP$W"
 F=$(find /initrd/pup_ro{?,??}/"$D" /initrd/pup_[ayz]/"$D" -maxdepth 1 -name "$B" 2>/dev/null| grep -vw "^$PARAM"| head -n 1)
 [ "$F" ] && touch "$BASE$W"
done
exit 0' > $MYTMPDIR/petget
  chmod +x $MYTMPDIR/petget
}
make_cleanup_script() {
  #v1.9.8: fix path
  #v2.0.12: ydrv, pop_ro1 for init script
  #make ash script for speed up #v2.0.12: no ash cannot interprete the glob expression
  [ -s $MYTMPDIR/cleanup -a -x $MYTMPDIR/cleanup ] && return	# created before
  echo '#!/bin/sh
PARAM=$1
LANG=C
BASE="/initrd/pup_rw/"
if [ "$PARAM" = "/initrd/pup_ro1" ]; then
  BASE="/initrd/pup_ro1/"
  shift
  PARAM=$1
fi
cd $BASE || exit	# precaution
# .wh..wh..opq or .wh.__dir_opaque(for aufs older version)
find . -mindepth 2 -type f -name ".wh..wh..opq" -o -name ".wh.__dir_opaque" |
while read W; do
  D=$(dirname "$W")
  F=$(find /initrd/pup_ro{?,??}/"$D" /initrd/pup_[ayz]/"$D" -maxdepth 1 2>/dev/null | grep -vw "^$PARAM"| head -n 1)
  [ "$F" ] || rm -f "$BASE$W"
done
# normal whiteout
find . -mindepth 2 -type f -name ".wh.*" -not -name ".wh..wh.*" |
while read W; do
 D=$(dirname "$W")
 B=$(basename "$W"|cut -b5-)
 F=$(find /initrd/pup_ro{?,??}/"$D" /initrd/pup_[ayz]/"$D" -maxdepth 1 -name "$B" 2>/dev/null| grep -vw "^$PARAM"| head -n 1)
 [ "$F" ] || rm -f "$BASE$W"
done
exit 0' >$MYTMPDIR/cleanup
  chmod +x $MYTMPDIR/cleanup
}
# 14feb2011: clean up whiteout # 13 Nov 2011: except PUPMODE=7
# v1.9: rewrite, v1.9.7: fix was failed for flash, v1.9.8: fix was skipped PUPMODE=5
cleanwhite() {
  #[ "$SAVE_LAYER" ] || return
  PARAM=$1  # layer path to be removed, example: '/initrd/pup_ro4'
  log cleanwhite $@
  SLAYER=""
  RAMSAVEINTERVAL=0
  case "$PUPMODE" in
    5|12) SLAYER="/pup_rw"
      RAMSAVEINTERVAL=-1;;
    3|13|7)	# ataflash/usbflash, PUPMODE=7 is unsupported
      [ "$PUPMODE" = "7" ] && [ "$PUPMODE7SUPPORT" != 'true' ] && return
      # periodical save may be disabled
      [ -s /etc/eventmanager ] && source /etc/eventmanager
      [ "$RAMSAVEINTERVAL" ] || RAMSAVEINTERVAL=-1 # ignore if not available
      [ $RAMSAVEINTERVAL -ne 0  ] && SLAYER="/pup_ro1"
      ;;
    *) return;;
  esac
  make_cleanup_script
  [ -x $MYTMPDIR/cleanup ] || return	# precaution
  if [ "$STARTSCRIPT" ]; then
    [ $RAMSAVEINTERVAL -ne 0  ] && $MYTMPDIR/cleanup $SLAYER
    return 0
  fi
  if [ "$SLAYER" = "/pup_ro1"  ]; then
        # make a script named 'petget'
        # a trick to suppress snapmergepuppy (pup_event_frontend_d does not issue snapmergepuppyrequest if petget is running.)
        make_petget_script
        [ -x $MYTMPDIR/petget ] || return	# precaution
        # wait snapmergepuppy finishes if it is already running
      if [ "`pidof snapmergepuppy`" != "" ]; then
        while [ "`pidof snapmergepuppy`" != "" ];do sleep 1; done
      else
        $MYTMPDIR/petget $PARAM
      fi
      $MYTMPDIR/cleanup /initrd$SLAYER $PARAM #v2.0.13: SFR fix
      return
  fi
  $MYTMPDIR/cleanup $PARAM
}
another_tmpfs() {
	DESTDIR="/mnt/tmp" #DESTDIR=/initrd/pup_rw/tmp
	ERRMSG=""
	df "$DESTDIR" 2>/dev/null | grep -qw 'tmpfs' && return 0 
	umount "$DESTDIR" &>/dev/null
	ERRMSG=$(gettext "Failed to unmount '/mnt/tmp'.")
	mount| grep -q -w 'on /mnt/tmp' && return 1
	[ $RFREE -gt $RESERVRAM ] || return 1  #v2.0
	mkdir -p "$DESTDIR"
	ALLOCK=$(($RFREE - $RESERVRAM))
	mount -t tmpfs -o size=${ALLOCK}k tmpfs "$DESTDIR"
	ERRMSG=$(gettext "Failed to create tmpfs '/mnt/tmp'.")
	RESULT=$(mount| grep -w 'on /mnt/tmp') || return 1
	ERRMSG=""
	debug "Created $RESULT"
	return 0
}
#v1.9.4
free_loopdev() {
  LD="$1"
  if [ -z "$LD" ]; then
    log "free_loopdev: no loop device specified."
    return 1
  fi
  if mount | grep -qw "^$LD" ; then
    busybox umount  "$LD" #|| log "Failed to unmount $LD."
  fi
  if mount | grep -qw "^$LD"; then
    log "Failed to unmount $LD."
    return 0
  fi
  losetup $ALLUSED | grep -qw "^$LD" || return 0
  [ "$DISSOSIATE" = 'true' ]|| return 0
  debug "losetup -d $LD"
  losetup -d "$LD" ; S=$?
  [ $S -ne 0 ] && log "Failed to dissosiate $LD."
  return $S
}
append_sfs() {
  #[ "$@" ] || return
  #EXTRASFS="$@"
 # filename, etc.
 DIRNAME=$(dirname "$EXTRASFS")
 [ "$DIRNAME" = "." ] && DIRNAME=$PWD
 FILENAME=$(basename "$EXTRASFS")
 ROOTNAME=$(basename "$FILENAME" .sfs)
 EXT=$(echo "$FILENAME" | sed -e 's/^.*\.//')
 [ "$EXT" = "sfs" ] || FILENAME="$FILENAME.sfs"
 EXTRASFS="$DIRNAME/$FILENAME"
 #v1.9.7: fix was faild more than 7 after reboot
 EXTRASFSSAFE=$(echo "$EXTRASFS"| tr ' ,;[]{}' '?')
 [ "$EXTRASFS" = "$EXTRASFSSAFE" ] || log "'$EXTRASFS' has special charactors or blanks in its path name. '$EXTRASFSSAFE' is used for safety."
 #debug "EXTRASFS=$EXTRASFS
 #EXTRASFSSAFE=$EXTRASFSSAFE"
 
 # need to copy to another tmpfs?
 NEWTMPFS=""
 [ "$SUPPORTSIG" = 'yellow' ] && df "$DIRNAME" | grep -qw '^unionfs' && NEWTMPFS="y"
 #[ "$DEBUGFLAG" ] && NEWTMPFS="y"
 if [ "$NEWTMPFS" ]; then	# need another tmpfs
   ram_free
   FILESIZE=$(du -k -L "$EXTRASFS"| cut -f1)
   [ $FILESIZE -gt 0 ] && REST=$(($RFREE - $FILESIZE)) || REST=0
   if [ $REST -le $RESERVRAM ]; then
      log "The '$EXTRASFS' need to copy but the RAM size is not enough."
      return 1
   fi
   DESTDIR="/mnt/tmp"
   ERRMSG=""
   if ! another_tmpfs; then
      log "$ERRMSG"
      return 1
   fi
   cp -f "$EXTRASFS" "$DESTDIR" || return 1
   EXTRASFS="$DESTDIR/$FILENAME"
 fi

 # where to mount
  NUMS=$(LANG=C df | grep '/initrd/pup_ro'| cut -d'%' -f2 | tr -dc '0-9\n'| sort -n)
  N=$(echo "$NUMS" | tail -n 1)
  [ $N ] || N=3	# precaution
  [ $N -ge 3 ] || N=3 # pup_ro1 to 3 is reserved
  NEW=$(($N + 1))
  #v1.9.1: search lower
  if [ $NEW -gt 4 ]; then
    for I in $(seq 4 $N); do
      ! (echo "$NUMS" | grep -qw "$I") && NEW=$I && break
    done
  fi
  MNTPNT=/initrd/pup_ro$NEW
  if [ ! -d "$MNTPNT" ]; then
    # exceeds the number the Puppy supports
    # search free number from the smaller
    S=$(seq 4 $N) # skip 1-3
    [ "$SNAPMERGEBUG" ] && [ $N -gt 9 ] && S="$(seq 4 9) $(seq 20 $N)"  # skip 10-19
    if [ $N -lt 100 ]; then
      for I in $S; do
        [ "$(echo "$NUMS" | grep -w "$I")" = "" ] && NEW=$I && break
      done
    fi
  fi
  MNTPNT=/initrd/pup_ro$NEW
  if [ ! -d "$MNTPNT" -a -z "$EXCESSOK" ]; then #v2.1
    # exceeds the number the Puppy supports
     TEMPORARYUSE="y"
     N=$(echo "$ALREADY_SFS_LIST" | wc -l)
     MSG1=$(gettext "Mount points prepared by Puppy for extra sfs layers seem filled up.")
     MSG2=$(printf "$(gettext "%s extra sfs files are already loaded or in the queue.")" "$N")
     MSG4=$(printf "$(gettext "But you can add new mount point '%s'(not recommended). Do you want to proceed?")" "$MNTPNT")
     confirm --ok-cancel "$MSG1 $MSG2 $MSG3\n\n$MSG4" || finish
     processing
  fi
  mkdir -p "$MNTPNT"
  # loop device number is prefered to be the same number
  #v1.9.1: but can be already assosiated
  #v1.9.4: can be already mounted
  #v1.9.4: not same number for some kernel
  LOOPDEV=""
  STATUS=-1
  LOOPDEVS=$(losetup $ALLUSED| grep -w "$EXTRASFS" | cut -sd ':' -f1)
  if [ "$LOOPDEVS" ]; then
    for D in $LOOPDEVS; do
      busybox umount "$D"
    done
    LOOPDEV=$(losetup $ALLUSED| grep -w "$EXTRASFS" | head -n 1|cut -sd ':' -f1)
  fi
  [ "$LOOPDEV" ] && STATUS=0
  if [ -z "$LOOPDEV" ]; then
    LOOPDEV="/dev/loop$NEW"
    [ -b "$LOOPDEV" ] || mknod -m664 $LOOPDEV b 7 $NEW
    df | grep -qw "^$LOOPDEV" && busybox umount "$LOOPDEV"	#v1.9.1
    sleep 0.1
    if [ "$DISSOSIATE" = 'true' ]; then
      #debug "losetup $LOOPDEV $EXTRASFS"
      losetup "$LOOPDEV" "$EXTRASFS" ; STATUS=$?
    else    #v2.1.9
      STATUS=-1
    fi
    if [ $STATUS -ne 0 ]; then
      LOOPDEV=$(losetup -f)
      log "Do not assosiate $EXTRASFS to /dev/loop$NEW but $LOOPDEV."
      STATUS=-1
    fi
  fi
  if [ $STATUS -eq 0 ]; then
    # 13 Nov 2011: list up on LOOPLIST #v1.9.4: moved down
    #echo "$LOOPDEV: 0 $EXTRASFS" >> "$LOOPLIST"
    mount -r -t squashfs -o noatime "$LOOPDEV" "$MNTPNT" # v1.3.9
    STATUS=$?
  else
    mount -r -t squashfs -o loop,noatime "$EXTRASFSSAFE" "$MNTPNT"
    STATUS=$?
    [ $STATUS -eq 0 ] ||  LOOPDEV=""
     #LOOPDEV=$(losetup $ALLUSED| grep -w "$EXTRASFS" | head -n 1|cut -sd ':' -f1)
  fi
  [ "$LOOPDEV" ] || STATUS=-1
  # v1.9: permission check
  # v1.9.9: unmount on error
  PERMINFO=""
  if [ $STATUS -eq 0 ]; then
    PERM=$(ls -dl "$MNTPNT"| cut -f1 -d' ')
    echo $PERM| grep -qw 'drwxr.xr.x' || STATUS=888
    if [ $STATUS -ne 0 ]; then
      debug "PERMISSION: $PERM"
      PERMINFO=$(eval_gettext "'\$EXTRASFS' has the wrong permissions.")"\n'$PERM'"
      busybox unmount "$MNTPNT"
      free_loopdev "$LOOPDEV"
    fi
  fi
  if [ $STATUS -eq 0 ]; then
    echo "$LOOPDEV: 0 $EXTRASFS" >> "$LOOPLIST"
  else
	[ "$LOOPDEV"  != "" ] && 
    fatal "$(eval_gettext "Failed to mount '\$EXTRASFS' on '\$MNTPNT'.") $PERMINFO\n$MOVEDLOG"
  fi
  has_afterwork
  debug "'$EXTRASFS'($LOOPDEV) is mounted on '$MNTPNT'."
  # v1.9: wipe out masking files in save layers # 1.9.1: do once
  # v1.9.8: fix was skipped PUPMODE=5
  #if [ "$SAVE_LAYER" ]; then
    #[ "$WIPEWHITEONLOAD"  = "true" ] && [ ! -f $MYTMPDIR/CLEANWHITE ] && cleanwhite && touch $MYTMPDIR/CLEANWHITE
    [ "$WIPEWHITEONLOAD"  = "true" ] && [ -z "$SKIP_HAS_AFTERWORK" ] && cleanwhite "$MNTPNT" #v2.0.12
    if [ "$WIPEMASKONLOAD" = "true"  ]; then
     FILES=$(find "$MNTPNT" -not -type d -printf '%P\n' )
     NF=$(echo "$FILES" | wc -l)
     debug "$NF files."
     [ "$SAVE_LAYER" ] && LAYERS=/initrd$SAVE_LAYER
     [ "$SAVE_LAYER" != "/pup_rw" ] && LAYERS="$LAYERS /initrd/pup_rw"
     echo "$FILES" | while read F; do
      for L in $LAYERS; do rm -f "$L/$F"; done
     done
    fi
  #fi
  # remount aufs # 30 Jan 2011 v0.3: unionfs mount option
  if [ "$AUFS" != "" ];then
    busybox mount -t aufs -o remount,append:$MNTPNT=ro unionfs /
    STATUS=$?
  else
    # 28 Oct 2011: unionfs support
    mount -t unionfs -o remount,add=:$MNTPNT=ro unionfs /
    STATUS=$?
  fi
  if [ $STATUS -ne 0 ];then
    umount "$MNTPNT"
	[ "$LOOPDEV"  != "" ] && free_loopdev "$LOOPDEV" 	#v1.9.4
    fatal "$(printf "$(gettext "Failed to append '%s' to unionfs.")" "$MNTPNT")\n$MOVEDLOG"
  fi
  sync
  # add to BOOTCONFIG
  #[ "$TEMPORARYUSE" = "" ] && EXTRASFSLIST="$EXTRASFSLIST $FILENAME"
  #v1.9.1, #v2.0: do not register zdrv #v2.0.11: ditto adrv and ydrv
  #v2.1.8: avoid dup. in EXTRASFSLIST
  case "$FILENAME" in
  $DISTRO_ZDRVSFS|$DISTRO_ADRVSFS|$DISTRO_YDRVSFS) :;;
  *) echo $EXTRASFSLIST | grep -qw "$FILENAME" || EXTRASFSLIST="$EXTRASFSLIST $FILENAME";;
  esac
  LASTUNIONRECORD="$LASTUNIONRECORD $FILENAME"
  save_bootconfig
  # backup for remaster. not removed by unload
  #v2.0: do not save here
  #cp -f $BOOTCONFIG $BOOTCONFIG.save
}
save_bootconfig() {
  EXTRASFSLIST=$(echo $EXTRASFSLIST)  # trim blanks
  LASTUNIONRECORD=$(echo $LASTUNIONRECORD)  # trim blanks
  echo "EXTRASFSLIST='$EXTRASFSLIST'" > $BOOTCONFIG
  echo "PREVUNIONRECORD='$PREVUNIONRECORD'" >> $BOOTCONFIG
  echo "LASTUNIONRECORD='$LASTUNIONRECORD'" >> $BOOTCONFIG
}
is_at_home() {
  FOUND=""
  [ "$1" ] || return
  local F=$(basename "$1")
  # already at the destination?
  SUB=""
  [ "$PSUBDIR" != "" ]  && SUB="$DESTDIR/$PSUBDIR" || SUB=""
  for D in $DESTDIR $SUB; do
    [ -s "$D/$F" ] && FOUND="$D/$F" && break
  done
  echo "$FOUND"
  [ "$FOUND" ]
}
is_at_home_wide() {
  FOUND=""
  [ "$1" ] || return
  local F=$(basename "$1")
  [ "$DESTDIR" = "" ] && [ "$DESTPART" ] && mountpart "$DESTPART" && DESTDIR=$MNTPNT
  FOUND=""
  [ "$DESTDIR" ] && FOUND=$(find -L "$DESTDIR" -xdev -mindepth 1 -maxdepth 2 -name "$F" -type f| head -n 1)
  echo "$FOUND"
  [ "$FOUND" = "" ] && [ "$UNMOUNTME" ] && umount "$UNMOUNTME" && UNMOUNTME="" && DESTDIR=""
  [ "$FOUND" ]
}
# v1.3.9 is_on_cd without parameter
is_on_cd() {
  debug "is_on_cd $@"
  FOUND=""
  local F=$(basename "$1")
  #PROBEDISK=probedisk
  #which probedisk2 &>/dev/null && PROBEDISK=probedisk2
  CDDEVS=$($PROBEDISK|grep -Ew 'optical|cdrom'| cut -d'|' -f1| cut -s -d'/' -f3)
  [ "$CDDEVS" ] || return
  #UNMOUNTME=""
  for D in $CDDEVS; do
    debug "CD drive:$D"
    MNTPNT=$(df | grep -w "^/dev/$D" | cut -d'%' -f2 | tr -d ' ')
    if [ "$MNTPNT" = "" ]; then
      cddetect_quick -d/dev/$D || continue # abort if the madia not inserted
      MNTPNT="/mnt/$D"
      if df | grep -qw "$MNTPNT"; then # seems different device is already mounted
        umount "$MNTPNT" || continue
      fi
      mkdir -p "$MNTPNT"
      mount -t iso9660 -o iocharset=utf8 "/dev/$D" "$MNTPNT" && UNMOUNTME="/dev/$D" || continue #v2.0: UNMOUNTME to be device node
    fi
    if [ "$F" ]; then
      FOUND=$(find -L "$MNTPNT" -mindepth 1 -maxdepth 3 -name "$F" -type f| head -n 1)
    else
      FOUND=$(find -L "$MNTPNT" -mindepth 1 -maxdepth 3 -name '[^.]*.sfs' -type f)	# v1.3.9
    fi
    FOUND=$(echo "$FOUND"| grep -v "$MNTPNT"'/[0-9][0-9-]*/')	# v1.3.9
    [ "$FOUND" ] && break
    [ "$UNMOUNTME" ] && umount "$UNMOUNTME" && UNMOUNTME=""
  done
  debug "$FOUND"
  [ "$FOUND" ]
}
# how much free RAM?  - 22Sep2011 free version compatible
ram_free() {
  ALINE=$(LANG=C free | grep -w 'Mem:')
  RAMSIZE=$(echo $ALINE | tr -s ' ' | cut -f2 -d' ')
  #debug "RAMSIZE=$RAMSIZE"
  [ $RAMSIZE ] || RAMSIZE=0
  TMPFUSED=$(LANG=C df -k | grep -E '^(tmpfs|shms)'| tr -s ' ' | cut -d' ' -f3)
  RFREE=$RAMSIZE
  if [ "$TMPFUSED" ]; then
    for S in $TMPFUSED; do
      #debug "USED=$S"
      RFREE=$(($RFREE - $S))
    done
  fi
  # swap space
  ALINE=$(LANG=C free | grep -w 'Swap:')
  SFREE=$(echo $ALINE | tr -s ' '| cut -f4  -d' ')
  [ $SFREE ] ||  SFREE=0
  #debug "SFREE=$SFREE" 
  # tmpfs
  TFREE=0
  DF=$(LANG=C df -k /tmp | tail -n 1)
  if echo $DF | grep -qw '^tmpfs' ; then
    TFREE=$(echo $DF| tr -s ' ' | cut -d' ' -f4)
  elif echo $DF | grep -qw '^unionfs' ; then
    DF=$(LANG=C df -k /initrd/pup_rw/tmp | tail -n 1)
    echo $DF | grep -qw '^tmpfs' && TFREE=$(echo $DF| tr -s ' ' | cut -d' ' -f4)
  fi
  RFREE=$(($RFREE + $SFREE))
  #[ $TFREE -gt 0 -a $TFREE -lt $RFREE ] && RFREE=$TFREE
  debug "TFREE=$TFREE"
  debug "RFREE=$RFREE"
}
# free space in the destination
dest_free() {
  DFREE=0
  [ "$DESTDIR" ] || return
  DF=$(LANG=C df -k "$DESTDIR" 2>/dev/null) || DF=""
  DFREE=$(echo "$DF" | tail -n 1 | tr -s ' ' |cut -d ' ' -f4)
  TORAM=""
  if echo "$DF" | grep -qw '^tmpfs'; then
    [ "$RFREE" ] || ram_free
    TORAM="y"
    [ $DFREE -gt $RFREE ] && DFREE=$RFREE
  fi
  [ $DFREE ] || DFREE=0
}
reform_filename() {
  EXTRASFS=$1
  DIRNAME=$(dirname "$EXTRASFS")
  [ "$DIRNAME" = "." ] && DIRNAME=$PWD
  FILENAME=$(basename "$EXTRASFS")
  ROOTNAME=$(basename "$FILENAME" .sfs)
  EXT=$(echo "$FILENAME" | sed -e 's/^.*\.//')
  [ "$EXT" = "sfs" ] || FILENAME="$FILENAME.sfs"
  # /mnt/home?
  if [ "$PUP_HOME" != "" -a "$DIRNAME" != "" ]; then
    DIRNAME=$(echo $DIRNAME| sed -e "s,/initrd$PUP_HOME,/mnt/home,")
  fi
  EXTRASFS="$DIRNAME/$FILENAME"
}

unload() {
	FILENAME=$1
    #mkdir -p $MYTMPDIR
    #TMPFILE=$MYTMPDIR/${MYNAME}_tmp.txt
    # remove from EXTRASFSLIST (in BOOTCONFIG)
    cp -pf $BOOTCONFIG $MYTMPDIR/BOOTCONFIG.bak
    REGISTERED=""
    remove_item EXTRASFSLIST "$FILENAME" && REGISTERED="y" || log "'$FILENAME' seems not registered."
    #remove_item LASTUNIONRECORD "$FILENAME" && REGISTERED="y" || log "'$FILENAME' seems not registered."
    save_bootconfig
    # remove from unionfs
    #LOSETUPS=$(losetup $ALLUSED| grep -v -E '^/dev/loop[012]:'| sort -r) #0-2 is reserved
    LOSETUPS=$(losetup $ALLUSED| sort -r)
    LOOP=$(echo "$LOSETUPS"| grep -w "$FILENAME" | head -n 1)
    if [ "$LOOP" = "" ] ;then
       # 13 Nov 2011: cn be on LOOPLIST
       LOOP=$(grep -w "$FILENAME" "$LOOPLIST" | head -n 1)
    fi   
   debug "LOOP=$LOOP"
    if [ "$LOOP" = "" ] ;then
      [ "$REGISTERED" ] || debug $(printf "'%s' seems not registered as loaded." "$FILENAME")
        # losetup does not show full file name but must be somewhere
        rm -f "$TMPFILE"
        echo "$LOSETUPS"| while read L ; do
          F=$(echo "$L"| cut -d' ' -f3 | tr -d '()')
           [ "$F" ] || continue
          F=$(basename "$F")
          echo "$FILENAME" | grep -q "$F" && echo $L >> "$TMPFILE" && break
        done
        [ -s "$TMPFILE" ] && LOOP=$(cat "$TMPFILE")
    fi
      LOOPDEV=$(echo "$LOOP"| cut -d':' -f1)
     if [ "$LOOPDEV" = "" ]; then
       # 13 Nov 2011: remove from registrations
       remove_item LASTUNIONRECORD "$FILENAME" && REGISTERED="y" || log "'$FILENAME' seems not registered."
       save_bootconfig
       error --info "$(gettext "Could not find the loaded point.")$(gettext "Maybe already unloaded.")"
       return 1 #finish
     fi
      F=$(echo "$LOOP"| cut -d' ' -f3| tr -d '()')
      [ -s "$F" ] && EXTRASFS="$F"
      [ -s "/initrd$F" ] && EXTRASFS="/initrd$F"
      [ ! -s "$EXTRASFS" ] && F=$(is_at_home "$FILENAME") && EXTRASFS="$F"
      debug "$LOOPDEV;$EXTRASFS;"
      MNTPNT=""
      [ "$LOOPDEV" != "" ] && MNTPNT=$(LANG=C df | grep -w "^$LOOPDEV"| tail -n 1 | cut -d'%' -f2| tr -d ' ')
      debug "mount point is $MNTPNT;"
      if [ -d "$MNTPNT" ]; then
        has_afterwork
        #debug "HAS_FONTS:HAS_STARTUPS=$HAS_FONTS:$HAS_STARTUPS:"
       if [ "$HAS_SCRIPTS" != "" ]; then
          log "Stopping script..."
          for S in $HAS_SCRIPTS; do
            [ -x /etc/init.d/$S ] && /etc/init.d/$S stop
          done
          #HAS_SCRIPTS=""
        fi
        #[ "$HAS_STARTUPS" != "" ] && echo"$HAS_STARTUPS" > $MYTMPDIR/afterwork_has_startups
        # clean up whiteout in save layer
        [ -z "$NO_AFTERWORK" ]  && [ "$WIPEWHITEUNLOAD"  = "true" ] && cleanwhite $MNTPNT
        sleep 0.2  # 13 Nov 2011
        # remount aufs
        if [ "$AUFS" != "" ];then
          debug "busybox mount -t aufs -o remount,del:$MNTPNT unionfs /"
          busybox mount -t aufs -o remount,del:$MNTPNT unionfs /
          STATUS=$?
        else
          debug "mount -t unionfs -o remount,del=$MNTPNT unionfs /"
          mount -t unionfs -o remount,del=$MNTPNT unionfs /
          STATUS=$?
          [ $STATUS -eq 0 ] && sleep 1   # 13 Nov 2011
          if [ $STATUS -ne 0 ];then
			# can be already removed
			ALINE=$(LANG=C mount -t unionfs| grep  'on / '| head -n 1)
			if echo $ALINE| grep -qw "/initrd$PUP_LAYER"; then
			  echo $ALINE| grep -qw "$MNTPNT" || STATUS=0
			fi 
          fi
        fi
        if [ $STATUS -eq 0 ]; then
          # v1.9: move up
          busybox umount "$LOOPDEV"	#v1.9.1
          if [ $? -ne 0 ]; then
            log "Failed to unmount $LOOPDEV."
            STATUS=2
          fi
        fi
        if [ $STATUS -ne 0 ];then
          MSG1=$(printf "$(gettext "Failed to unload '%s'.")" "$FILENAME")
          MSG2=$(gettext "Some files may be in use. You can try this again after you restart X (From the main menu> Shutdown> Restart X). Click 'No' here.")
          MSG3=$(gettext "Or, do you want to remove it from the queue list right now?")
          MSG4=$(gettext "Then it will be unloaded at next boot.")
          if ! confirm --yes-no "$MSG1 $MSG2\n\n$MSG3 $MSG4"; then
            # recover BOOTCONFIG
            [ -s "$MYTMPDIR/BOOTCONFIG.bak" ] && mv -f "$MYTMPDIR/BOOTCONFIG.bak" "$BOOTCONFIG"
          fi
          rm -f "$MYTMPDIR"/has_*	# clean up has_... files
          return 1 #finish
        fi
      fi
	  [ "$LOOPDEV"  != "" ] && free_loopdev "$LOOPDEV"	#v1.9.4
	  # 13 Nov 2011: remove from LOOPLIST
	  if [ "$LOOPDEV"  != "" ] ; then
	    grep -v "^$LOOPDEV:" "$LOOPLIST" > "$LOOPLIST.new"
	    mv -f "$LOOPLIST.new" "$LOOPLIST"
	  fi
	  #true  # unless fatal error
    #done || finish 1
    # remove from LASTUNIONRECORD # 5 Feb 2011
    remove_item LASTUNIONRECORD "$FILENAME" && REGISTERED="y" || log "'$FILENAME' seems not registered."
    save_bootconfig
    # delete file
    RMLOG=""
    if [ -f "$EXTRASFS" ]; then
      DIRNAME=$(dirname "$EXTRASFS")
      FILENAME=$(basename "$EXTRASFS")
      #if echo "$DIRNAME" | grep -qw '/tmp$' || [ "$(df "$DIRNAME" | tail -n 1 | cut -d ' ' -f1)" = "tmpfs" ]; then
	  if [ "$(df "$DIRNAME" | tail -n 1 | cut -d ' ' -f1)" = "tmpfs" ]; then
        rm -f "$EXTRASFS"
        RMLOG=$(printf "$(gettext "The temporary file '%s' is deleted.")" "$EXTRASFS")
        FOUND=""
        for D in / /root/; do
          [ -e "$D$FILENAME" ] && FOUND="$D$FILENAME" && break
        done
        if [ "$FOUND" ]; then
          RMLOG="$RMLOG $(printf "$(gettext "You can also delete '%s' if you want to.")" "$FOUND")"
        fi
      fi
    fi
    ADDINFO=""
    [ "$HAS_FONTS" -o "$HAS_STARTUPS" ] && ADDINFO="\\n$_Restart_X"
    [ "$RMLOG" ] && ADDINFO="$ADDINFO\\n$RMLOG"
	HAS_STARTUPS=""
	#rm -fr  $MYTMPDIR  #rm -f $MYTMPDIR/afterwork*
    afterwork
    return 0
}

# how i called?
EXTRASFS=""
ACTION="load"
GUI=""
QUIET=""
READLISTFILE=""
READSTDIN=""
REENT=""
STARTSCRIPT=""
NO_AFTERWORK=""
DO_AFTERWORK=""
SKIP_FIXMENUS=""
GLOBAL_PARMS=""
basename "$0" | grep -q '^un' && ACTION="unload"
[ "$DISPLAY" != "" ] && GUI="y"
basename "$0" | grep -q 'debug' && DEBUGFLAG="y"	# v1.3.9
#v1.9.2: allow --skip-fixmenus with multiple files
 while [ "$1" ] ; do
  case "$1" in
  -u|--un*) ACTION="unload"; GLOBAL_PARMS="$GLOBAL_PARMS $1";;
  -c|--cli) GUI=""; GLOBAL_PARMS="$GLOBAL_PARMS $1";;
  -q|--quiet) QUIET="y"; GLOBAL_PARMS="$GLOBAL_PARMS $1";;
  -d|--debug) DEBUGFLAG="y"; GLOBAL_PARMS="$GLOBAL_PARMS $1";;
  -h|-help|--help) usage; exit 0;;
  -i|--info) SHOW_INFO="y";;
  -n|--no-afterwork) NO_AFTERWORK="y";;
  -a|--do-afterwork|--afterwork) DO_AFTERWORK="y";;
  -s|--skip-*menu*|--no-*menu*) SKIP_FIXMENUS="y"; GLOBAL_PARMS="$GLOBAL_PARMS $1";;
  -l|-l=|--list*)  READLISTFILE=$(echo $1| cut -s -f2- -d'=')
     [ -z "$READLISTFILE" ] && shift && READLISTFILE="$1"
     ;;
  -v|-version|--version) echo "$MYNAME $VERSION"; exit 0;;
  --*) usage; exit 0;;
  -) if [ "$2" = "" ]; then
       READSTDIN="y"; GUI=""; shift; break
     fi
     ;;
  *=*) GLOBAL_PARMS="$GLOBAL_PARMS $1"; [ "$DEBUGFLAG" ] && eval $1 || break;;
  *) break;;
  esac
  shift
 done
 GLOBAL_PARMS=$(echo $GLOBAL_PARMS)	# remove blanks
 MULTIPLE=""
 if [ "$READSTDIN" ]; then
   # read from standard input
   GLOBAL_PARMS="$GLOBAL_PARMS --cli"
   MULTIPLE=""
   while true; do
     read ONEEXTRASFS
     [ "$ONEEXTRASFS" ] || break
     [ "$(echo $ONEEXTRASFS| cut -b1)" = '#' ] && continue	# skip comment
     MULTIPLE="$MULTIPLE
$ONEEXTRASFS"
   done
   # note that the first line of $MULTIPLE is blank
   #debug "$MULTIPLE"
 fi
 if [ "$READLISTFILE" ]; then
   # multiple files from list
   [ -s "$READLISTFILE" ] || fatal $(printf "List file '%s' not found." "$READLISTFILE")
   MULTIPLE=$(cat "$READLISTFILE")
 fi
 if [ "$2" ]; then
   IFSSAVE=$IFS
   IFS='
 '	# set line-feed (and space) as the separater
   MULTIPLE="$*"
   IFS=$IFSSAVE
 fi
 if [ "$MULTIPLE" ]; then
   # multiple files
   echo "$MULTIPLE" | while read ONEEXTRASFS; do
     [ "$ONEEXTRASFS" ] || continue
     [ "$(echo $ONEEXTRASFS| cut -b1)" = '#' ] && continue	# skip comment
     "$MYPATH" $GLOBAL_PARMS --no-afterwork "$ONEEXTRASFS"
   done
   "$MYPATH" $GLOBAL_PARMS --afterwork
   finish
   exit
fi

if [ "$DO_AFTERWORK" ]; then
   NO_AFTERWORK=""
   if  [ "$1" = "" ]; then
     # do afterwork only
     [ "$WIPEWHITEONLOAD" = "true" -o "$WIPEWHITEUNLOAD" = "true" ] && cleanwhite
     afterwork
     finish
     exit
   fi
fi

[ "$1" = "" -a "$DISPLAY" != "" ] && GUI="y"
case "$1" in
-*) EXTRASFS=$(echo $1| cut -b2-); ACTION="unload";;
+*) EXTRASFS=$(echo $1| cut -b2-);;
start) STARTSCRIPT="start";;
stop) STARTSCRIPT="stop";;
*) EXTRASFS=$1;;
esac

[ -d "$MYTMPDIR" ] || mkdir -p "$MYTMPDIR"
LOOPLIST="$MYTMPDIR/loop_device_list"
WRONGVERSION="$MYTMPDIR/wrong_sfs_version"
PUPMODE_dummy=$PUPMODE                          ############ for debugging
#savefile_dummy=savefile
DISTRO_NAME="Puppy"
for F in $PUPSTATE $BOOTCONFIG $DISTRO_SPECS; do
  [ -s $F ] && source $F
done
[ "$DISTRO_VERSION" ] || DISTRO_VERSION=$(cat $PUPPYVERSION)
[ "$DISTRO_VERSION" = "" ] && DISTRO_VERSION=$(cat /initrd/PUPPYVERSION 2>/dev/null)
DISTRO_VERSION=$(echo $DISTRO_VERSION | tr -dc '0-9')
[ "$DISTRO_VERSION" ] || DISTRO_VERSION=0

if [ "$PUPMODE_dummy" ] ; then ############ for debugging
  [ "$PUPMODE_dummy" = "CD" ] && PUPMODE_dummy=5 && PUPSFS="sr0,iso9660,$SFSBASE"
  [ "$PUPMODE_dummy" ] && PUPMODE=$PUPMODE_dummy
  case "$PUPMODE_dummy" in
   2) SAVE_LAYER=''; PUP_HOME='/'; PUPSAVE="";;
   5) SAVE_LAYER=''; PUP_HOME=''; PUPSAVE="";;
   6) SAVE_LAYER='/pup_rw'; PUP_HOME='/pup_rw'; PUPSAVE='sda1,ext2,/';;
   12) SAVE_LAYER='/pup_rw'; PUP_HOME='/mnt/dev_save';;
   13) SAVE_LAYER='/pup_ro1'; PUP_HOME='/mnt/dev_save';;
   77) SAVE_LAYER='/pup_ro1'; PUP_HOME=''; PUPSAVE='sr0,iso9660,/2011-01-27-20-26';;
  esac
fi ############ for debugging end

# see what pup_*.sfs file is used
SFSPART=$(echo $PUPSFS|cut -d',' -f1)
SFSTYPE=$(echo $PUPSFS|cut -s -d',' -f2)
SFSFILE=$(echo $PUPSFS|cut -s -d',' -f3)
SFSBASE=$(basename "$SFSFILE")
#ZDRPART=$(echo $ZDRV|cut -d',' -f1)
ZDRFILE=$(echo $ZDRV|cut -d',' -f3)
ZDRBASE=$(basename "$ZDRFILE")
#v1.9.2: saluki stuff
ADRFILE=$(echo $ADRV|cut -d',' -f3)
ADRBASE=$(basename "$ADRFILE")
#v2.0.8, v2.0.9: ydrv
YDRFILE=$(echo $YDRV|cut -d',' -f3)
YDRBASE=$(basename "$YDRFILE")
# aufs or unionfs?
#AUFS=$(lsmod | grep -w '^aufs')
# 11 Feb 2011 v0.7: see aufs even with no module(thanks to jemimah)
[ "$(mount -t aufs)" != "" ] && AUFS=aufs || AUFS=""
# sfs version indirect via kernel version
# 28 Oct 2011 v1.2.9: support kernel 3.x
KERNVER="`uname -r`"
KERNELMAJORVER=`echo -n "$KERNVER" | cut -f 1-2 -d '.'|tr -d '.'`
KERNELSUBVER=`echo -n "$KERNVER" | cut -f 3 -d '.' | cut -f 1 -d '-' | cut -f 1 -d '_'` #v423 100831
SUB_SUB_KERNELVER=`echo -n "$KERNVER" | cut -f 1 -d '-' | cut -f 4 -d '.'` #100607
SFSVER=3 #'squashfs, version 3' #v423
[ $KERNELSUBVER -gt 28 ] && SFSVER=4 #'squashfs, version 4' #v423
[ $KERNELSUBVER -eq 27 ] && [ $SUB_SUB_KERNELVER -gt 46 ] && SFSVER=4 #'squashfs, version 4' #100607
[ $KERNELMAJORVER -gt 26 ] && SFSVER=4
# v1.3.9: sfs on-the-fly support
SUPPORTSIG="red" # unsupported
[ $KERNELSUBVER -gt 20 ] && SUPPORTSIG="yellow"	# limited support
[ "$AUFS" ] || SUPPORTSIG="red"
#[ $KERNELSUBVER -gt 30 ] && SUPPORTSIG="green"	# full support #v1.9.5: yellow for all 2.6.x kernel
[ $KERNELMAJORVER -gt 26 ] && SUPPORTSIG="green"
#v1.9.4, v1.9.6-2: 'losetup -d' is disabled for kernel-3.x
#v2.0.8: DISSOSIATE option to be set at the top of this script
#[ $KERNELMAJORVER -ge 32 ] &&  DISSOSIATE="false" && debug "'losetup -d' is disabled. for kernel-3.x"

# probedisk2 available?
PROBEDISK=probedisk
which probedisk2 &>/dev/null && PROBEDISK=probedisk2
# losetup accepts without option?
ALLUSED="-a"
losetup $ALLUSED &>/dev/null || ALLUSED=""
# snapmergepuppy on wary-5.1(110212)  has a bug treating 'pup_ro1x'
SNAPMERGEBUG=""
case "$PUPMODE" in
7|13) grep -q 'grep[ ]*"$BASE"' /usr/sbin/snapmergepuppy && SNAPMERGEBUG="y"
  ;;
esac
[ "$HOME" = "/" ] && HOME=/root
[ "$HOME" ] || HOME=/root

# home path
PUPHOME=""
PSUBDIR=""
SAVEFILE=""
SAVEPART=""
DESTPART=""
DESTDIR=""
if [ "$PUPSAVE" != "" ]; then
  SAVEPART=$(echo $PUPSAVE| cut -d',' -f1)
  DESTPART=$SAVEPART
  #v2.0.8: save-to-directory (gyro)
  case $PUPMODE in
  12|13) SAVEFILE=$(echo $PUPSAVE| cut -sd',' -f3);;
  *) SAVEFILE="";;
  esac
  [ "$PUP_HOME" != "" ] && PUPHOME=/initrd$PUP_HOME
  [ "$PUPHOME" = "/initrd$PUP_HOME" ] && PUPHOME=/mnt/home && DESTDIR=$PUPHOME
else
  # PUPMODE=2 or 5
  if [ "$PUPMODE" = "2" ]; then
    DESTDIR="/root"; PUPHOME="/"
  else
    # PUPMODE=5
    [  -s "$PUPSAVECONF" ] && source "$PUPSAVECONF" && PUPSAVE="$SAVEPART,,$SAVEFILE"
    [ -s "$SHUTDOWNCONF" ] && PUPMODE_SAVE=$PUPMODE && source "$SHUTDOWNCONF" && PUPMODE=$PUPMODE_SAVE
    if [ "$PUPSAVE" != "" ]; then
		SAVEPART=$(echo $PUPSAVE| cut -d',' -f1)
		DESTPART=$SAVEPART
    fi
    if [ -z "$SAVEPART" ]; then
      # nowhere to save the session
      [ "$SFSFILE" != "" ] && PSUBDIR=$(dirname "$SFSFILE"| cut -b2-)
      if [ "$SFSPART" != "" -a "$SFSTYPE" != "iso9660" ]; then	# v1.3.9
        # internal or external HDD or FLASH
        DESTPART=$SFSPART
        #mountpart "$DESTPART" && PUPHOME=$MNTPNT && DESTDIR=$PUPHOME	# v1.3.9, #v2.3: will be done later
      fi
    fi
  fi
fi
[ "$SAVEFILE" != "" ] && PSUBDIR=$(dirname "$SAVEFILE"| cut -b2-)  # remove '/' at head
[ "$DESTDIR" -a "$PSUBDIR" -a ! -d "$DESTDIR/$PSUBDIR" ] && PSUBDIR=""  # may not yet be created
# 13 Nov 2011: except PUPMODE=7 #2.0.12: PUPMODE=7 optional
case "$PUPMODE" in
5) if [ "$SAVEFILE" != "" ]; then SFSMODE="y"
   elif [ "$SAVEPART" != "" ]; then SFSMODE="cd"
   else SFSMODE="tmp"
   fi
  ;;
6|12|13) SFSMODE="y";;
77) SFSMODE="cd";;
*) SFSMODE="";;
esac
[ "$PUPMODE7SUPPORT" = 'true' ] && [ "$PUPMODE" = "7" ] && SFSMODE="y"
if [ "$SFSMODE" = "cd" ]; then
  PUPHOME="/"	#; DESTDIR="/initrd/pup_rw"
  [ "$SUPPORTSIG" = 'yellow' ] && PUPHOME="/root"
  DESTDIR=$PUPHOME
fi
#debug "PUPHOME=$PUPHOME;"
debug "SUPPORTSIG=$SUPPORTSIG, SFSMODE=$SFSMODE, DESTPART=$DESTPART, DESTDIR=$DESTDIR;"

EXCESSOK=""
if [ "$SFSMODE" != "" ]; then
  [ "$MYNAME" ] && [ -x "/etc/init.d/$MYNAME" ] && EXCESSOK="y"
  # how many extra files Puppy supports?
 #DEFAULTMAXEXTRANUM=0  # wil be read from /etc/MAXEXTRANUM, 0:no depend on the initrd
 grep -q '^[^#]*sfs_load' /etc/rc.d/rc.sysinit &&  DEFAULTMAXEXTRANUM=0 && rm -f "$MAXEXTRANUMSAVE"
 if [ "$DEFAULTMAXEXTRANUM" ]; then
    MAXEXTRANUM=$DEFAULTMAXEXTRANUM
  elif [ -s "$MAXEXTRANUMSAVE" ]; then
    MAXEXTRANUM=$(grep '^[^#]*MAXEXTRANUM=' "$MAXEXTRANUMSAVE" | cut -f2 -d'=' | tr -dc '0-9')
  else
    MAXEXTRANUM=$(ls -1 -d /initrd/pup_ro*| grep -vw 'pup_ro[123]'|wc -l)  # not exact. new directories may be createded after.
    echo "#number of extra SFS loaded by the initramfs (except zdrv/adrv/ydrv)
MAXEXTRANUM=$MAXEXTRANUM" > "$MAXEXTRANUMSAVE"
  fi
  [ "$MAXEXTRANUM" ] || MAXEXTRANUM=0 # precaution
  debug MAXEXTRANUM=$MAXEXTRANUM
  loaded_sfs_list
  queued_sfs_list
fi

# startup script called with a parameter 'start/stop'
if [ "$STARTSCRIPT" ]; then

  [ "$SFSMODE" ] || exit 0

  if [ "$STARTSCRIPT" = "stop" ]; then
    #v1.9.6: fix was everytime rc.update run when the number of additional sfs > 3
    #v2.1.8: MAXEXTRANUM=0
    cp -f $BOOTCONFIG $BOOTCONFIG.save #v2.1.8: always save backup
    N=$(echo $LOADEDLIST|wc -w)
    debug "$N: $LOADEDLIST"
    [ $N -le $MAXEXTRANUM ] && exit	#v2.0.9, v2.1.8
    if [ $MAXEXTRANUM -eq 0 ]; then
      EXTRASFSLIST=""
      LASTUNIONRECORD=$(echo $LASTUNIONRECORD | tr ' ' "\n" | grep -E "^(${DISTRO_FILE_PREFIX}save|$DISTRO_PUPPYSFS|zdrv|adrv|ydrv)"| tr "\n" ' ')
      save_bootconfig
      exit
    fi
    # trim the excessives not to run rc.update
    NEWUNIONRECORD=""
    NEWEXTRASFSLIST=""
    COUNT=0
    for F in $LASTUNIONRECORD; do
      NEWUNIONRECORD="$NEWUNIONRECORD $F"
      echo "$LOADEDLIST"| grep -qw $F || continue
      NEWEXTRASFSLIST="$NEWEXTRASFSLIST $F"
      COUNT=$(($COUNT + 1))
      [ $COUNT -lt $MAXEXTRANUM ] && break
    done
    LASTUNIONRECORD=$NEWUNIONRECORD
    EXTRASFSLIST=$NEWEXTRASFSLIST
    save_bootconfig
    exit
  fi
  ###### else 'start' ######
  [ "$STARTSCRIPT" != "start" ] && echo "usage: $0 start/stop" && exit
  #v2.0.9: initramfs may not hadle any extra SFS
  #v2.2: fix was removed all sfs after abnormal shutdown
  if [ "$ALREADYRUN" ]; then
    #skip because already called from rc.sysinit
    log "sfs_load already run."
    LOCKFILE=$MYTMPDIR/$APPNAME.lock
    PIDS=$(ps)
    PIDS=$(echo "$PIDS"| grep 'sh.*sfs_load' | grep -vw "$$")
   [ -z "$PIDS" -o -f "$LOCKFILE" ] && exit 0
   touch "$LOCKFILLE"
   T=0
   TLIMIT=10
   while [ "$PIDS" ]; do
     T=$(($T + 1))
     [ $T -gt $TLIMIT ] && log "Timeout." && break
     sleep 1
    PIDS=$(ps)
    PIDS=$(echo "$PIDS"| grep 'sh.*sfs_load' | grep -vw "$$")
   done
   rm -f "$LOCKFILE"
   log "Finished."
   exit 0
 fi
  #[ "$QUEUELIST" ] || exit 0
  #v2.0: recover backup is done by the init script, but need LASTUNIONRECORD saved
  #LASTNOW="$LASTUNIONRECORD"
  #[ -s $BOOTCONFIG.save ] && . $BOOTCONFIG.save
  rm -f $BOOTCONFIG.save
  #LASTSAVE="$LASTUNIONRECORD"
  #LASTUNIONRECORD=$LASTNOW
  #EXTRASFSLIST=$LOADEDLIST
  MNTPNT=""
  COPIED=""
  NEED_FIXMENUS=""	#v2.0
  CHANGED_DRIVE=""	#v2.0
  #SKIP_HAS_AFTERWORK=""
  #[ $MAXEXTRANUM -eq 0 ] && 
  SKIP_HAS_AFTERWORK="y" #v2.1.8
  if [ "$QUEUELIST" ]; then
   [ "$DESTPART" ] && mountpart "$DESTPART" && PUPHOME=$MNTPNT && DESTDIR=$PUPHOME
   DESTDIRSAVE=$DESTDIR
   echo -n "Loading extra SFS..." >/dev/console
   for F in $QUEUELIST; do
    EXTRASFS="$F"
    DESTDIR=$DESTDIRSAVE
    [ ! -s "$EXTRASFS" ] && EXTRASFS=$(is_at_home "$F")
    [ "$WIDESEARCH" = "true" ] && [ ! -s "$EXTRASFS" ] && EXTRASFS=$(is_at_home_wide "$F")   #v2.0
    FILEISAT=""
    if [ ! -s "$EXTRASFS" ] && is_on_cd "$F" ; then
      EXTRASFS="$FOUND"
      FILEISAT="cd"
    fi
    if [ ! -s "$EXTRASFS" ] ; then
      log "'$F' not found."
      continue
    fi
    if ! check_sfs_version; then
      log "'$F' SFS version not match."
      continue
    fi
    # copy to ram?
    #v1.9.2: read BOOTCONFIG.save on remastered
    #2.0: see if the main sfs in RAM?
    [ "$PUPMODE" = "5" ] && ! df /initrd/mnt/dev_ro2 | grep -q  '^/dev/' && SFSINRAM="y" || SFSINRAM=""
    if [ "$SFSINRAM" != "" -o "$FILISAT" = "cd" ] ; then
      FILESIZE=$(du -k -L "$EXTRASFS"| cut -f1)
	  #DESTDIR="/tmp"
      #if [ "$SUPPORTSIG" = 'yellow' ] && df "$DESTDIR" | grep -qw '^unionfs'; then
        # need another tmpfs
        DESTDIR="/mnt/tmp"
        ERRMSG=""
        ram_free
        REST=$(($RFREE - $FILESIZE))
        [ $REST -gt $RESERVRAM ] && another_tmpfs #|| continue
      #fi
      DESTISAT="tmp"
      #[ "$DESTDIR" = "" ] && [ "$DESTPART" ] && mountpart "$DESTPART" && DESTDIR=$MNTPNT
      dest_free
      if [ $DFREE -gt $FILESIZE ] && cp "$EXTRASFS" "$DESTDIR"; then
          info "$EXTRASFS is copied to $DESTDIR."
          EXTRASFS="$DESTDIR/"$(basename "$EXTRASFS")
          COPIED="yes"
      fi
    fi
    log "Loading '$EXTRASFS'..."
    append_sfs #"$EXTRASFS" #|| log "Loading faild."
    if [ $? -eq 0 -a -n "$HAS_DESKTOPS" ]; then
     echo "$PREVUNIONRECORD" | grep -qw "$F" || NEED_FIXMENUS="y"	#v2.0: see the LASTUNIONRECORD backup
    fi
   done
 fi
  #v1.9.2: skip fixmenus, hope done before
  #v2.0: see the LASTUNIONRECORD backup
  if [ "$LASTUNIONRECORD" != "$PREVUNIONRECORD" ]; then
   echo -n "layers have changed, fixing menu..." >/dev/console
   [ "$WIPEWHITEONINIT"  = "true" ] && cleanwhite
   [ "$NEED_FIXMENUS" ] || SKIP_FIXMENUS="y"
   if [ "SKIP_HAS_FIXMENUS" ]; then 
     #not checked, force all on
     SKIP_FIXMENUS=""
     HAS_DESKTOPS="y"
     echo "y" > "$MYTMPDIR/has_modules"
     echo "y" > "$MYTMPDIR/has_desktops"
     echo "y" > "$MYTMPDIR/has_icons"
     echo "y" > "$MYTMPDIR/has_glib_schema"
     echo "/usr/sharefonts" > "$MYTMPDIR/has_fonts"	###FIXME subdirs ###
    #puppy pins
    if [ -s "/root/Choices/ROX-Filer/PuppyPin" ]; then
      cp -f "/root/Choices/ROX-Filer/globicons" "$MYTMPDIR/globicons-sfs_load"
      cp -f "/root/Choices/ROX-Filer/PuppyPin" "$MYTMPDIR/PuppyPin-sfs_load"
      echo "PuppyPin-sfs_load" >  "$MYTMPDIR/has_pins"
    fi
   fi
   afterwork
   #v2.0: fix menu icons
   if [ "$HAS_DESKTOPS" ] && pidof jwm &>/dev/null; then
    [ "$DISPLAY" ] || export DISPLAY=':0'
    if [ "$NEED_FIXMENUS" ]; then
      jwm -restart
    else
      jwm -reload &>/dev/null || jwm -restart
    fi
    log "jwm restarted."
   fi
  fi
  #v2.1.8: modify rc.local
  MYINIT="/etc/init.d/sfs_load"
  NEEDMODIFY="y"
  grep -q  "$MYINIT" /etc/rc.d/rc.sysinit && NEEDMODIFY=""
  [ "$NEEDMODIFY" ] && [ -s /etc/rc.d/rc.local ] &&  grep -q  "$MYINIT" /etc/rc.d/rc.local && NEEDMODIFY=""
  if [ "$NEEDMODIFY" ]; then
   echo "[ -x $MYINIT ] && $MYINIT start" >> /etc/rc.d/rc.local
  fi
  echo -n -e "\\033[74G\\033[1;32m" >/dev/console #green [done] msg. 110426: change 72 to 74.
  echo -n "done" >/dev/console #done
  echo -e "\\033[0;39m" >/dev/console
  finish
  exit
fi

# list up sfs already loaded or installed
if [ "$SFSMODE" != "" ]; then
  if [ "$QUEUELIST" ]; then
    for F in $QUEUELIST; do
      add_new_sfs_list $F queue
    done
  fi
  ALREADY_SFS_LIST=$LOADEDLIST
else
  MAXEXTRANUM=0
  ALREADY_SFS_LIST=$(installed_sfs_lst)
fi

#v2.0: show installed sfs
if [ "$SHOW_INFO" ];then
  echo "$ALREADY_SFS_LIST"
  exit 0
fi

# ensure mounted
[ "$DESTPART" ] && mountpart "$DESTPART" && PUPHOME=$MNTPNT && DESTDIR=$PUPHOME

# waring
WARNMSG=""
WARNSHORT=""
WARNMODE=$PUPMODE
[ "$SFSMODE" = "cd" ] && WARNMODE=77
WARNEXPERIMENTAL=$(printf "$(gettext "WARNING: Using this under 'PUPMODE=%s' is EXPERIMENTAL.")" "$WARNMODE")
if [ "$SFSMODE" = "" ]; then
  WARNSHORT=$WARNEXPERIMENTAL
  WARNMSG=$(gettext "Your Puppy is running under the mode without extra SFS support. But this program can install them using pseudo-PET. It is not exactly the same as loading SFS but may work well in most of case.")'\n'$WARNEXPERIMENTAL
else
 if [ "$SUPPORTSIG" = "red" ]; then	# v1.3.9
  #WARNMSG=$(gettext "Your Puppy is using 'unionfs', not 'aufs'. Currently 'unionfs' support is EXPERIMENTAL.")
  WARNMSG=$(printf "$(gettext "Sorry, but this %s with the kernel version %s is not supported.")" "$DISTRO_NAME-$DISTRO_VERSION" "$KERNVER")
  [ "$DEBUGFLAG" != "" ] || fatal "$WARNMSG"
  WARNMSG="$WARNMSG\n$(gettext "DANGER: Your PC may freeze if you proceed.")"
 elif [ "$SFSMODE" = "tmp" ]; then
      WARNMSG=$(gettext "You seem running Puppy without 'pupsave'. Recommended to click 'Cancel' here and reboot PC after making 'pupsave'.")
      [ "$PUPMODE" = "5" ] && [ "$PUPSAVECONFIG" != "" ] && \
      WARNMSG="$WARNMSG $(gettext "Or, you need not reboot PC if you use this program after setting it up using the 'pupsaveconfig'. Follow MENU >> Utility >> PupSaveConfig")"
      WARNMSG="$WARNMSG\n\n$(gettext "But you can load extra SFS files for temporary use(may disappear at the next boot). Click 'OK' if you want to continue.")"
      WARNSHORT=$(gettext "You can load extra SFS files for temporary use.")
 elif [ "$SFSMODE" = "cd" ]; then
  WARNSHORT=$WARNEXPERIMENTAL
  WARNMSG=$(printf "$(gettext "You seem going to save the session back to the live CD. You can place the extra SFS at '%s' if you want the SFS to persist even after the next boot. Note that the RAM should be large enough.")"  "$PUPHOME")'\n'$WARNEXPERIMENTAL
 elif [ "$PUPMODE" = "6" -o "$PUPMODE" = "7"  ]; then
  WARNSHORT=$WARNEXPERIMENTAL
  WARNMSG=$(gettext "Your Puppy is running without a 'pupsave' file but with saving the session to the entire partition.")' '$(gettext "The extra SFS support under this mode may be buggy.")'\n'$WARNEXPERIMENTAL
 fi
fi
[ "$WARNSHORT" ] || WARNSHORT=$WARNMSG
_Restart_X=$(gettext "You may need to restart X to reflect changes.")

#EXTRASFS=$1
if [ "$WARNMSG" != "" -a "$EXTRASFS" = "" -a "$GUI" != "" -a "$QUIET" = "" ]; then
  log "$WARNMSG"
  errmsg --warning --ok-cancel "$WARNMSG" || exit 1
fi
while [ "$EXTRASFS" = "" -a "$GUI" != "" ] ; do
  REENT="y"
  main_dialog || REENT=""	# v1.3.9
  [ "$REENT" ] || finish
done
 #[ "$WARN0" != "" ] && WARNMSG="\\n$WARN0\\n$WARN1"
[ "$EXTRASFS" != "" ] || fatal --usage

#v1.9.1: full path?
D=$(dirname "$EXTRASFS")
case "$D" in
*://*) FULLPATH='url';;
 /*) FULLPATH="y";;
 *) FULLPATH="";;
 esac
reform_filename "$EXTRASFS"

# system file? #v2.0
[ "$FILENAME" != "$SFSBASE" ] || fatal $(printf "$(gettext "'%s' is the system file and cannot be removed.")" "$FILENAME")
# new?
# convert glob pattern to grep
PATTERN=$(echo "$FILENAME" | sed -e 's/[.]/\\./g' -e 's/*/.*/g' -e 's/?/./g')
#debug "Seach pattern:$PATTERN;"
#v2.0
ALINE=$(echo $LASTUNIONRECORD | grep -o -w "$PATTERN")
[ "$ALINE" ] || ALINE=$(echo "$ALREADY_SFS_LIST" | grep -w "$PATTERN"| tail -n 1)
#debug "Found:$ALINE"
if [ "$ALINE" ]; then
  #v2.0: fix zdrv, adrv #2.0.8 ydrv
  case "$FILENAME" in
   $SFSBASE|$ZDRBASE|$ADRBASE|$YDRBASE) fatal $(printf "$(gettext "'%s' is the system file and cannot be removed.")" "$FILENAME");;
  esac
  FILENAME=$(keyword $ALINE)
  EXTRASFS="$DIRNAME/$FILENAME"
  if [ "$SFSMODE" != "" ]; then
    if echo "$QUEUELIST"| grep -qw "$FILENAME"; then
     ALREADY=$(printf "$(gettext "'%s' seems not currently loaded but in the queue.")" "$FILENAME")
     [ "$GUI" ] && ALREADY=$ALREADY'\n'$(gettext "Do you want to remove it from the queue list?")
    else 
     ALREADY=$(printf "$(gettext "'%s' seems already loaded.")" "$FILENAME")
     [ "$GUI" ] && ALREADY=$ALREADY'\n'$(gettext "Do you want to unload?")
    fi
  else
    # already installed as a pseudo-PET
    ALREADY=$(printf "$(gettext "'%s' seems already installed.")" "$FILENAME")
    [ "$GUI" ] && ALREADY=$ALREADY'\n'$(gettext "Do you want to uninstall?")
  fi
  [ "$ACTION" = "unload" -o "$GUI" != "" ] || fatal $ALREADY
  #confirm $ALREADY || exit 1
  # call myself with unload option
  #exec $0 --unload --yes "$FILENAME"
  ACTION="unload"
elif [ ! -s "$EXTRASFS" ]; then
  # 21feb11
  [ "$BASELIST" = "" ] && loadable_sfs_list
  FOUND=$(echo "$BASELIST" | grep -w "$PATTERN"| tail -n 1)
  if [ "$FOUND" ]; then EXTRASFS=$(echo "$ALLSFSLIST" | grep -w "$FOUND" | head -n 1)
  elif [ -z "$FULLPATH" ]; then	#v1.9.1: no wide search for full path
     FOUND=$(is_at_home_wide "$EXTRASFS") && EXTRASFS=$FOUND
  else
     [ "$FULLPATH" = 'url' ] || fatal $(printf "$(gettext "'%s' not found.")" "$EXTRASFS")
  fi
fi
reform_filename "$EXTRASFS"
# 28 Oct 2011, 13 Nov 2011: special characters
DIRNAMESAFE=$(echo "$DIRNAME"| tr ' ,;[]{}' '?')
EXTRASFSSAFE=$(echo "$EXTRASFS"| tr ' ,;[]{}' '?')
if [ "$EXTRASFS" != "$EXTRASFSSAFE" ] ; then
  WARNSHORT="$WARNSHORT\n"$(gettext "'$EXTRASFS' has special charactors or blanks in its path name. It may FAIL loading.")
fi
# unload # 30 Jan 2011 v0.3: fix menu update after unload # 4 Feb 2011 v0.4: fatal check
# 13 Nov 2011: remove from LASTUNIONRECORD
if [ "$ACTION" = "unload" ]; then
  # confirmation
  [ "$ALREADY" = "" ] && ALREADY=$(printf "$(gettext "'%s' does not seem loaded or installed.")" "$FILENAME")
  [ "$SFSMODE" = "tmp" ] && WARNSHORT=""
  if [ "$QUIET" = "" ]; then
    MSG=$ALREADY
    [ "$WARNMSG" != "" ] && MSG="$MSG\\n$WARNSHORT"
    #TITLEICON='icon-name="gtk-remove"'
    confirm "$MSG" || finish
  fi
  if [ "$SFSMODE" != "" ]; then
    #TITLEICON='icon-name="gtk-remove"' confirm $(printf "$(gettext "Do you want to unload '%s'?")" "$FILENAME") || exit
    processing
    unload "$FILENAME" || finish
    MSG="$(printf "$(gettext "'%s' is unloaded.")" "$FILENAME")$ADDINFO"
    BUTTONS=$QUIT_BUTTON
    [ "$REENT" ] && BUTTONS="$CONTINUE_BUTTON $BUTTONS"
    CUSTOM="<hbox>$BUTTONS</hbox>"
    info $OPT --custom $MSG || REENT=""
    finish
    exit
  fi
  # SFSMODE = "" 
    LOOPDEV=$(LANG=C df| grep -w "$FILENAME" | cut -d' ' -f1)
    [ "$LOOPDEV" ] && busybox umount $LOOPDEV	#v1.9.1
    LOOPDEV=$(losetup $ALLUSED | grep -w "$FILENAME" | cut -d':' -f1)
    [ "$LOOPDEV" ] && free_loopdev  "$LOOPDEV"	#v1.9.4
   petget -"$ROOTNAME" && [ "$HAS_FONTS" -o "$HAS_STARTUPS" ] && info $_Restart_X
   finish
   exit
fi

### load ###
#[ ! -s "$EXTRASFS" ] && FOUND=$(is_at_home_wide "$EXTRASFS") && EXTRASFS=$FOUND
[ "$EXT" = "sfs" ] || fatal $(printf "$(gettext "'%s' does not seem sfs files.")" "$FILENAME")
TEMPORARYUSE=""
[ "$DESTDIR" = "" ] && [ "$DESTPART" ] && mountpart "$DESTPART" && DESTDIR="$MNTPNT"
DESTISAT=$(df "$DESTDIR"| tail -n 1| cut -d' ' -f1| cut -d'/' -f3| tr -d '0-9')
echo $ATADRIVES | grep -qw "$DESTISAT" && DESTISAT="ata"
KEEPMOUNT=""
# where is the sfs file?
FILEISAT=""
if [ "$PUPHOME" != "" ]; then
  case "$DIRNAME" in
  $PUPHOME/$PSUBDIR) FILEISAT="home";;
  $PUPHOME) FILEISAT="home";;
  esac
fi
if [ "$FILEISAT" = "" ];then
  case "$DIRNAME" in
  http://*|ftp://*) FILEISAT="url" ;;
  /mnt/cdrom*|/mnt/dvd*|/mnt/sr*) FILEISAT="cd";;
  /mnt/home/$PSUBDIR) FILEISAT="home";;
  /mnt/home) FILEISAT="home";;
  /tmp*) FILEISAT="tmpfs";;
  $DESTDIR|$DESTDIR/$PSUBDIR) FILEISAT="home";;
  /mnt/network/*) FILEISAT="remote";;
  /mnt/+*) FILEISAT="loop";;
  /mnt/*|/initrd/mnt/*) FILEISAT="mnt"
    DEVPART=$(df "$DIRNAMESAFE"| tail -n 1| cut -d ' ' -f1)
    DRV=$(echo $DEVPART| cut -s -d'/' -f3|tr -d '0-9')
    [ "$DRV" ] && echo $ATADRIVES| grep -qw $DRV &&  FILEISAT="ata"
    [ "$DEVPART" != "" ] && mount -t iso9660| grep -q "^$DEVPART " && FILEISAT="cd"
    ;; 
  esac
fi
if [ "$FILEISAT" = "" -o "$SUPPORTSIG" = "yellow" ]; then	# v1.3.9
  DFRESULT=$(df "$DIRNAME" 2>/dev/null)
  echo "$DFRESULT" | grep -qw '^unionfs' && FILEISAT="unionfs"
fi
[ "$FILESAT" = "" ] && echo "$DFRESULT" | grep -qw '^tmpfs' && FILEISAT="tmpfs"
FILEWASAT="$FILEISAT"
EXTRASFSORG="$EXTRASFS"
# file size
FILESIZE=0
if [ "$FILEISAT" != "url" ]; then
  [ -s "$EXTRASFS" ] || fatal $(printf "$(gettext "'%s' not found.")" "$EXTRASFS")
  check_sfs_version
  FILESIZE=$(du -k -L "$EXTRASFS"| cut -f1)
fi
[ $FILESIZE ] || FILESIZE=0  # precaution

# how much free RAM?
ram_free
#debug "RFREE=$RFREE;"

PART=""
MNTPNT=""
#[ "$PUPMODE_dummy" ] && FILEISAT="cd"  ############ for debugging
debug "FILEISAT=$FILEISAT;"
# confirmation
[ $FILESIZE -ne 0 ] && SIZE="$(gettext "filesize"): $(kbyte2 "$FILESIZE")" || SIZE=$(gettext "needs download")
MSG=$(printf "$(gettext "Do you want to load '%s'?")" "$FILENAME($SIZE)")
NEEDTOMOVE=""
[ "$SUPPORTSIG" = "yellow" -a "$FILEISAT" = 'unionfs' ] && NEEDTOMOVE="y"
if [ "$SFSMODE" = "tmp" -a "$FILEISAT" != "home" -a  "$FILEISAT" != "ata" ]; then
  WARNMSG=$WARNSHORT
  MSG="$MSG\n$WARNSHORT"
  TOPART=$DESTPART
  if [ "$TOPART" = "" ]; then
    TOPART=$SFSPART
    [ "$SFSTYPE" = "iso9660"  -a "$FILEISAT" != 'unionfs' ] && TOPART="CD"
  fi
  choosepart $TOPART || finish
  #[ "$PART" != "" ] || exit 1
  [ "$UNMOUNTME" ] && [ "$FILEISAT" != "cd" -a "$UNMOUNTME" != "/dev/$PART" ] && umount "$UNMOUNTME" && UNMOUNTME=""  
  case "$PART" in
  NOCOPY) DESTDIR=
      DESTISAT=$FILEISAT
      ;;
  RAM) DESTDIR="/tmp"
       if [ "$SUPPORTSIG" = 'yellow' ] && df "$DESTDIR" | grep -qw '^unionfs'; then
         # need another tmpfs
         DESTDIR="/mnt/tmp"
         ERRMSG=""
         another_tmpfs || fatal "$ERRMSG"
       fi
       DESTISAT="tmp"
       #TEMPORARYUSE="y"
       ;;
  CD)  DESTDIR=/	#DESTDIR="/initrd/pup_rw"
       [ "$SUPPORTSIG" = 'yellow' ] && DESTDIR="/root"
       DESTISAT="tmpfs"
       SFSMODE="cd"
       ;;
  *)
    mountpart $PART || fatal $(printf "$(gettext "Failed to mount '/dev/%s'.")" "$PART")
    DESTDIR="$MNTPNT";
    DESTISAT=$(df "$DESTDIR"| tail -n 1| cut -d' ' -f1| cut -d'/' -f3| tr -d '0-9')
    echo $ATADRIVES | grep -qw "$DESTISAT" && DESTISAT="ata"
    ;;
  esac
elif [ "$QUIET" = "" ]; then
  #TITLEICON='icon-name="gtk-add"'
  confirm "$MSG\n$WARNSHORT" || finish
fi
[ "$(readlink /mnt/home)" = "$DESTDIR" ] && DESTDIR=/mnt/home

# free space in the destination
dest_free
#debug "DFREE=$DFREE"

# needs to download
if [ "$FILEISAT" = "url" ]; then
  _Failed=$(eval_gettext "Failed to download '\$EXTRASFS' to '\$DESTDIR'.")
  (cd "$DESTDIR"
  download_file "$EXTRASFS") || fatal "$_Failed"
 [ -s "$DESTDIR/$FILENAME" ] || fatal "$_Failed"
  log "'$FILENAME' is downloaded at '$DESTDIR'"
  DIRNAME="$DESTDIR"
  EXTRASFS="$DIRNAME/$FILENAME"
  #EXTRASFSSAFE=""
  [ "$DIRNAME" = "$PUPHOME" ] && FILEISAT="home"
fi
[ -s "$EXTRASFS" ] || fatal $(printf "$(gettext "'%s' not found.")" "$EXTRASFS")
check_sfs_version

processing
FILESIZE=$(du -k -L "$EXTRASFS"| cut -f1)
COPIED=""
MOVEDLOG=""
TEMPORARYUSE=""
if [ "$SFSMODE" != "" ]; then
  # unmount if already mounted
  LOOPDEVS=$(losetup $ALLUSED | grep -w "$EXTRASFS" | cut -sd ':' -f1)
  if [ "$LOOPDEVS" != "" ]; then
    for LOOPDEV in $LOOPDEVS; do
      free_loopdev "$LOOPDEV" 	#v1.9.4
    done
  fi
  # needs to move?
  NEEDTOMOVE=""
  CANMOVE=""
  while [ "$DESTDIR" ] && [ "$FILEISAT" != "home" ]; do
    # already at the destination?
    SUB=""
    [ "$PSUBDIR" != "" ]  && SUB="$DESTDIR/$PSUBDIR" || SUB=""
    FOUND=""
    for D in $DESTDIR $SUB; do
      [ -s "$D/$FILENAME" ] && FOUND="$D/$FILENAME" && break
    done
    if [ "$FOUND" != "" ]; then
      debug "FOUND=$FOUND;"
      [ "$(readlink /mnt/home)" = "$D" ] && FOUND="/mnt/home/$FILENAME"
      MSG=$(printf "$(gettext "%s is found. Do you use it?")" $FOUND)
      confirm $MSG || finish
      EXTRASFS="$FOUND"
      #EXTRASFSSAFE=""
      [ "$DESTISAT" = "tmp" ]  && FILEISAT="tmp" || FILEISAT="home"
      break
    fi
	NEEDTOMOVE=""; CANMOVE=""; NEEDNOTCOPY=""
	[ "$SUPPORTSIG" = "yellow" -a "$FILEISAT" = 'unionfs' ] && NEEDTOMOVE="y"
	[ "$FILEISAT" = 'ata' -o "$FILEISAT" = 'unionfs' ] && [ "$DESTISAT" = 'ata' ] && CANMOVE="y"
	[ "$FILEISAT" = 'cd' -a "$SFSMODE" = 'cd' ] && COPYTORAM="y"
     # enough space?
    REST=$(($DFREE - $FILESIZE))
    [ $REST -gt $RESERVHDD ] && SPACEOK="y" || SPACEOK=""
    if [ -z "$SPACEOK" ]; then	# not enough space
      [ "$NEEDTOMOVE" ] && fatal "$(printf "$(gettext "Could not copy or move. Maybe there is not enough space at '%s'.")" "$DESTDIR")\n$(gettext "Source"): $(kbyte2 $FILESIZE) --> $(gettext "Availble"): $(kbyte2 $DFREE)"
      break
    fi
    if [ "$NEEDTOMOVE" ]; then
       cp "$EXTRASFS" "$DESTDIR" || fatal $(eval_gettext "Failed to copy '\$EXTRASFS' to '\$DESTDIR'.")
       COPIED="y"
       MOVEDLOG=$(eval_gettext "'\$EXTRASFS' is copied to '\$DESTDIR'.")
       EXTRASFS="$DESTDIR/$FILENAME"
       #EXTRASFSSAFE=""
      [ "$DESTISAT" = "tmp" ]  && FILEISAT="tmp" || FILEISAT="home"
       break
    fi
    BUTTONS="<hbox><button yes></button><button no></button><button cancel></button></hbox>"
    MSG=$(gettext "The SFS file is preferably to be at the same place you will save your session as a 'pupsave'.")
    EXIT="Copy" #v2.0
    #19 May 2012 v1.9.5: fix was no move but always copy
    if [ "$CANMOVE" ]; then
      MSG="$MSG\n$(eval_gettext "Do you want to move '\$EXTRASFS' to '\$DESTDIR'(recommended)?")"
      BUTTONS='<button><input file stock="gtk-yes"></input><label>'$(gettext "Move")'</label><action>EXIT:Move</action></button>
        <button><input file stock="gtk-copy"></input><label>'$(gettext "Copy")'</label><action>EXIT:Copy</action></button>
        <button><input file stock="gtk-no"></input><label>'$(gettext "No Copy")'</label><action>EXIT:No</action></button>' 
      EXIT="No" #v2.0
    else
      if [ "$COPYTORAM" ]; then
  #_ANOTHERCD=$(gettext "Select 'CD' if the SFS file is not on the live CD(from which you booted off your Puppy), but you want to copy it in the live CD at saving the session back to the live CD.")
  #_SAMECD=$(gettext "Select 'RAM' if the SFS file is on the live CD and you have enough RAM(+swap).")
        MSG="$(eval_gettext "Do you want to copy '\$EXTRASFS' to '\$DESTDIR'?")\n\n$(gettext "You need not copy if the SFS file is on the live CD(from which you booted off your Puppy)")"
      else
        MSG="$MSG\n$(eval_gettext "Do you want to copy '\$EXTRASFS' to '\$DESTDIR'(recommended)?")"
      fi
      BUTTONS='<button><input file stock="gtk-copy"></input><label>'$(gettext "Copy")'</label><action>EXIT:Copy</action></button>
        <button><input file stock="gtk-no"></input><label>'$(gettext "No")'</label><action>EXIT:No</action></button>'
    fi
    BUTTONS='<hbox>'$BUTTONS'<button cancel></button></hbox>'
    #debug "$BUTTONS"
    #EXIT="Copy" #v2.0
    CUSTOM=$BUTTONS
    confirm --custom "$MSG"  
       case "$EXIT" in
       Yes|yes|OK|ok) :;;
       Move)	COPYTORAM=""; MOVE="y";;
       Copy)	COPYTORAM=""; MOVE="";;
       No|no) [ "$COPYTORAM" ] || break;;
       *) finish;;
       esac
       processing
       if [ "$MOVE" ]; then
         mv "$EXTRASFS" "$DESTDIR" || fatal $(eval_gettext "Failed to move '\$EXTRASFS' to '\$DESTDIR'.")
         MOVEDLOG=$(eval_gettext "'\$EXTRASFS' is moved to '\$DESTDIR'.")
       else
         if [ "$COPYTORAM" ]; then
           # another tmpfs
           DESTDIR="/mnt/tmp"
           ERRMSG=""
           ram_free
           another_tmpfs
           DESTISAT="livecd"
         fi
         cp "$EXTRASFS" "$DESTDIR" || fatal $(eval_gettext "Failed to copy '\$EXTRASFS' to '\$DESTDIR'.")
         COPIED="y"
         MOVEDLOG=$(eval_gettext "'\$EXTRASFS' is copied to '\$DESTDIR'.")
       fi
         log "$MOVELOG"
         EXTRASFS="$DESTDIR/$FILENAME"
         #EXTRASFSSAFE=""
         [ "$DESTISAT" = "tmp" ]  && FILEISAT="tmp" || FILEISAT="home"
         break
  done
  #[ "$SPACEOK" ] || fatal 
  processing
  _FAILED="$(printf "$(gettext "Failed to load '%s'.")" "$EXTRASFS")"
  if [ "$FILEISAT" = "unionfs" ]; then
   # which layer?
   TEMPORARYUSE="y"
   F=$(ls /initrd/*/"$EXTRASFS" 2>/dev/null | head -n 1)
   if [ ! -s "$F" ]; then
     [ "$DESTDIR" ] && fatal "$(printf "$(gettext "Could not copy or move. Maybe there is not enough space at '%s'.")" "$DESTDIR")\n$(gettext "Source"): $(kbyte2 $FILESIZE) --> $(gettext "Availble"): $(kbyte2 $DFREE)"
     fatal "$_FAILED"
   fi
   EXTRASFS="$F"
   #EXTRASFSSAFE=""
  fi
  # append
  [ "$FILEISAT" = "home" -o "$FILEISAT" = "livecd" ] || TEMPORARYUSE="y"  # do not regist
  append_sfs || fatal "$_FAILED"
  [ "$UNMOUNTME" ] && umount "$UNMOUNTME" && UNMOUNTME="" && DESTDIR=""
  save_bootconfig
  afterwork
  # final report
  [ "$MOVEDLOG" != "" ] && MOVEDLOG="\\n$MOVEDLOG"
  OPT=""
  [ "$PUPHOME" ] || TEMPORARYUSE="y"  # for message
  if [ "$TEMPORARYUSE" != "" ]; then
    OPT="--warning"
    MOVEDLOG="$MOVEDLOG\n$(gettext "NOTE: Loading the SFS is temporary. It may be unloaded at the next boot.")"
  fi
   ADDINFO=$(gettext "Do not remove or move the file unless you unload it.")	# v1.3.9
   [ "$COPIED" ] && ADDINFO="$ADDINFO $(gettext "But you can remove or move the original if you want to.")"
   [ "$HAS_FONTS" -o "$HAS_STARTUPS" ] && ADDINFO="$ADDINFO\\n$_Restart_X"
  MSG="$(printf "$(gettext "'%s' is successfully loaded.")" "$FILENAME")$MOVEDLOG\\n$ADDINFO"
  #[ "$UNMOUNTME" ] && umount "$UNMOUNTME" && UNMOUNTME=""
  if [ "$HAS_DESKTOPS" -a "$GUI" ]; then
    launcher $HAS_DESKTOPS
  else
    BUTTONS=$QUIT_BUTTON
    [ "$REENT" ] && BUTTONS="$CONTINUE_BUTTON $BUTTONS"
    CUSTOM="<hbox>$BUTTONS</hbox>"
    info $OPT --custom $MSG || REENT=""
  fi
 finish
 exit
fi ### SFSMODE != "" ### end

  ### SFSMODE = "" ### start
  #fatal "Currently 'PUPMODE=$PUPMODE' is not suppurted."
  # already mounted?
  MNTPNT=""
  LOOPDEV=$(losetup $ALLUSED | grep -w "$EXTRASFS" | cut -sd ':' -f1)
  if [ "$LOOPDEV" != "" ]; then
    MNTPNT=$(df | grep -w "$LOOPDEV" | cut -sd'%' -f2)
    MNTPNT=$(echo $MNTPNT) # remove blank
  fi
  if [ "$MNTPNT" = "" ]; then
    MNTPNT=/mnt/$(echo "$EXTRASFS"| tr '/ ' '+_')
    mkdir -p "$MNTPNT"
    STATUS=0
    if [ "$LOOPDEV" != "" ]; then
      mount -r -t squashfs "$LOOPDEV" "$MNTPNT" # v1.3.9
      STATUS=$?
    else
      mount -r -t squashfs -o loop "$EXTRASFS" "$MNTPNT" # v1.3.9
      STATUS=$?
    fi
    if [ $STATUS -ne 0 ]; then
      fatal "Failed to mount '$EXTRASFS' on '$MNTPNT'."
    fi
  fi
  # enough space at destination?
  SSIZE=$(du -k -s "$MNTPNT"| cut -f1)
  [ $SSIZE ] || SSIZE=0
  REST=$(($DFREE - $SSIZE))
  if [ $REST -lt $RESERVHDD ]; then  # 20MB rest
    umount "$MNTPNT" && rmdir "$MNTPNT"
    # ensure loop device be free
    LOOPDEV=$(losetup $ALLUSED | grep -w "$EXTRASFS" | cut -sd ':' -f1)
    [ "$LOOPDEV" != "" ] && free_loopdev "$LOOPDEV" 	#v1.9.4
     fatal "$(gettext "There is not enough space to install.")\n$(gettext "Source"): $(kbyte2 $SSIZE) --> $(gettext "Free"): $(kbyte2 $DFREE)"
  fi
  # check contents 
  has_afterwork
  # make file list and copy the files
  #cp -af "$MNTPNT"/* / ; STATUS=$?
  STATUS=0
  FILELIST="$PKGDIR/$ROOTNAME.files"
  SKIPLIST="$MYTMPDIR/${ROOTNAME}_skipped.files"
  rm -f "$FILELIST" "$SKIPLIST"
  STATUS=0
  find "$MNTPNT" -mindepth 1 -printf '/%P\n' | while read ALINE; do
    if [ "$WIPEMASKONLOAD" = "true" -o ! -e "$ALINE" ]; then
      echo $ALINE >> "$FILELIST"
    else
      [ -d "$ALINE" ] || echo $ALINE >> "$SKIPLIST"  #v1.9.9: shrink SKIPLIST
    fi
  done
  (while read ALINE; do
    cp -a --remove-destination "$MNTPNT$ALINE" "$ALINE" || STATUS=$1
  done) < "$FILELIST"
  umount "$MNTPNT" && rmdir "$MNTPNT"
  # ensure loop device be free
  LOOPDEV=$(losetup $ALLUSED | grep -w "$EXTRASFS" | cut -sd ':' -f1)
  [ "$LOOPDEV" != "" ] && free_loopdev "$LOOPDEV" 	#v1.9.4
  if [ $STATUS -ne 0 ]; then
     error $(gettext "Failed to copy some files to root file system.")
  fi
  # register in the .package
  THESFSMNT=$ROOTNAME
  INSTSIZEK=${SSIZE}K
  DESCRIPTION="extra SFS installed by $MYNAME"
     # code from sfs_installation.sh by 01micko
	 #quickpet_lupu-4beta2|quickpet_lupu|4beta2||Setup|484K||quickpet_lupu-4beta2.pet||Quickpet Install Popular Programs||||
	 #format #packagename-ver1|packagename|ver1|||||packagename-ver1.pet|||||| 
	 THESFSVERSION=`echo $THESFSMNT|cut -d '-' -f2-`
	 THESFSNAME=`echo $THESFSMNT | cut -d '-' -f1`
	 if [[ `echo $THESFSMNT | grep "devx"` != "" ]];then
		THESFSNAME=`echo $THESFSMNT | cut -d '_' -f 1,2`
		THESFSVERSION=`echo $THESFSMNT|cut -d '_' -f3`
	 fi
	 SFSASPETNAME=$FILENAME  #`echo $THESFS | sed 's/sfs$/pet/'`
	 echo "${THESFSMNT}|${THESFSNAME}|$THESFSVERSION|||$INSTSIZEK||${SFSASPETNAME}||$DESCRIPTION||||" >> "$INSTALLEDLIST"
  # remove copied file
  if [ "$EXTRASFS" != "$EXTRASFSORG" ]; then
    rm -f "$EXTRASFS"
    log $(printf "$(gettext "'%s' is deleted.")" "$EXTRASFS") 
  fi
  afterwork
  ADDINFO=""
  [ "$HAS_FONTS" -o "$HAS_STARTUPS" ] && ADDINFO="\\n$_Restart_X"
  [ -s "$SKIPLIST" ] && ADDINFO="$ADDINFO\\n$(printf "$(gettext "Some files may not be copied. See '%s'.")" "$SKIPLIST")"
  MSG="$(printf "$(gettext "'%s' is installed.")" "$FILENAME")$ADDINFO"
  OPT="--info"
  if [ "$HAS_DESKTOPS" ]; then
    launcher $HAS_DESKTOPS
  else
    BUTTONS=$QUIT_BUTTON
    [ "$REENT" ] && BUTTONS="$CONTINUE_BUTTON $BUTTONS"
    CUSTOM="<hbox>$BUTTONS</hbox>"
    info $OPT --custom $MSG || REENT=""
  fi
  finish
  exit
