#!/bin/sh
# sfs_load 28 Jan 2011 by shinobar
# always assume squashfs 4.0
# 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)
# 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
# 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: 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
# 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
#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
#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
#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)
#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  
#20150706 v2.3.0 by ASRI: Increase the size of main GUI # Add info "number of sfs mounted" # For the list of mounted sfs, use a "table" (instead of "list") to sort the list alphabetically # Add button refresh
#20151004 v2.3.3 by ASRI: improve kernel detection KERNVER (Iguleder), add support of pupmode 6 (mavrothal v2.3.2)
#20151028 v2.3.7: Warning SFS in /tmp at pupmod 5 and unloading fdrv sfs (mavrothal), desktops under /usr/local (shinobar) 

for i in $@ ; do
  if [ "$i" = "stop" ] ; then #simple thing to do, do it as fast as possible
	[ "`whoami`" != "root" ] && exec sudo -A ${0} ${@}
	. /etc/rc.d/BOOTCONFIG
	echo "PREVEXTRASFSLIST='$EXTRASFSLIST'"
	echo "PREVEXTRASFSLIST='$EXTRASFSLIST'" > /etc/rc.d/BOOTCONFIG.save
	exit
 fi
done

[ -s /etc/rc.d/BOOTCONFIG.save ] && . /etc/rc.d/BOOTCONFIG.save

MYPATH=$0
MYNAME=$(basename "$0")
VERSION=3.0
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

MYTMPDIR="/tmp/sfs_load"
LOGFILE="$MYTMPDIR/${MYNAME}.log"
TMPFILE="$MYTMPDIR/${MYNAME}_tmp.txt"
ICONS="/usr/share/pixmaps/puppy/clock.svg"
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
DISTRO_SPECS=/etc/DISTRO_SPECS
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"

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
    stop: clean up at shutdown" >&2
}
log() {
  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: $*"
}
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
}

GTKDIALOG=gtkdialog
UNMOUNTME=""
export XPID=""
export DIALOG=""
waitsplash() {
  [ "$1" = "--stop" -a "$GUI" = "" ] && return
  [ "$XPID" != "" ] && [ $XPID -ne 0 ] && kill $XPID && XPID=""
  if [ "$GUI" = "" ]; then
    [ "$*" ] && echo "$MYNAME: $*"
    return
  fi
  case "$1" in
  *stop) return;;
  *start|-*) shift;;
  esac
  /usr/lib/gtkdialog/box_splash -text "$* $(gettext "Wait a moment ...")" &
  XPID=$!
}

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
     busybox 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 ITEM=$2 S="" D="" 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

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 $MNTPNT/usr/local/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" ]
  # 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
  roxpids=`pidof ROX-Filer` || return
  for PID in ${roxpids}; do kill $PID ; done
  rox -p "$HOME/Choices/ROX-Filer/PuppyPin" && log "ROX-Filer restarted." || log "ROX-Filer restart failed." 
}

keyword() { # 13 Nov 2011
  echo "$1"| head -n 1| tr '|@' '  '| sed -e 's/ ([^ ]*)$//'
}

make_combo() {
  NODUP=""
  [ "$1" = '-' ] && NODUP="y" && shift
  LIST="$@"
  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() {
 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)"
  case "$F" in
    $SFSBASE|$ZDRBASE|$ADRBASE|$YDRBASE|$FDRBASE) return 1 ;;
    *.sfs) ok=1 ;;
    *) return 1 ;; #only allow sfs files
  esac
  echo "$LOADEDLIST" | grep -qw "$F" && return 1  # already listed
  if [ "$STARTSCRIPT" = "start" ] ; then
    echo "$LOSETUP_A" | grep "/${F}$" || return 1 # not mounted
  fi
  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 -a | 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 -a)
  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 "$LOADEDLIST" | grep -qw $F && continue
    QUEUELIST="$QUEUELIST
 $F"
  done
  QUEUELIST=$(echo "$QUEUELIST"| grep '.')  # remove blank line
  #echo "$QUEUELIST" #debug
}

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
	[ "$MYTMPDIR" != "" ] && mkdir -p "$MYTMPDIR"
	rm -f "$WRONGVERSION"
    [ "$DISTRO_PUPPYSFS" = "" ] && DISTRO_PUPPYSFS=$SFSBASE
    [ "$DISTRO_VERSION" = "" ] && DISTRO_VERSION=$(basename $DISTRO_PUPPYSFS .sfs| tr '_' '-'| cut -s -d'-' -f2)
    [ "$DISTRO_FILE_PREFIX" = "" ] && DISTRO_FILE_PREFIX=$(echo $DISTRO_PUPPYSFS| tr '_' '-'| cut -d'-' -f1)
    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_YDRVSFS}" = "" ]; then	#v2.0.11
      [ "$YDRBASE" != "" ] && DISTRO_YDRVSFS=$YDRBASE || DISTRO_YDRVSFS="ydrv"
    fi
    if [ "${DISTRO_FDRVSFS}" = "" ]; then
      [ "$FDRBASE" != "" ] && DISTRO_FDRVSFS=$FDRBASE || DISTRO_FDRVSFS="fdrv"
    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 -E '^adrv_|^fdrv_|^ydrv_|^zdrv_|^pup_'`" != "" ] && 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
  [ "${DISTRO_FDRVSFS}" ] && [ "`echo "$BASEONESFS" | grep "^${DISTRO_FDRVSFS}"`" != "" ] && continue #v1.9.2: saluki
  [ "`echo "$BASEONESFS" | grep "^${SFSBASE}"`" != "" ] && continue #100913
  [ "${ZDRBASE}" ] && [ "`echo "$BASEONESFS" | grep "^${ZDRBASE}"`" != "" ] && continue #100913
  [ "${ADRBASE}" ] && [ "`echo "$BASEONESFS" | grep "^${ADRBASE}"`" != "" ] && continue #v1.9.2: saluki
  [ "${YDRBASE}" ] && [ "`echo "$BASEONESFS" | grep "^${YDRBASE}"`" != "" ] && continue #v2.0.11
  [ "${FDRBASE}" ] && [ "`echo "$BASEONESFS" | grep "^${FDRBASE}"`" != "" ] && continue #v1.9.2: saluki
  #v423 eliminate wrong squashfs version...
  if [ "`disktype ${ONESFS} | grep "$SFSSTR"`" = "" ];then
   echo "$BASEONESFS" >> "$WRONGVERSION"
   continue
  fi
  BASEFIXEDSFSLIST="$BASEFIXEDSFSLIST
$BASEONESFS"
 done
    BASELIST=$( echo "$BASEFIXEDSFSLIST" | sort -u) # 1feb11
    #echo "$BASEFIXEDSFSLIST" | sort | uniq
}
main_dialog() {
	waitsplash $(gettext "Probing...")
	BOXHEIGHT1=300
	BOXWIDTH=650
	TEXTWIDTH=580
	TEXTWIDTHCHARS=30
	cntsfsloaded=$(echo $LOADEDLIST|wc -w)
	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='<table tooltip-text="'$(gettext "Select the sfs file you want to unload.")'">
	  <label>'$(gettext 'Total of mounted SFS =')' '$cntsfsloaded'</label>
	  <variable>UNLOADSFS</variable>'$(make_combo $ALREADY_SFS_LIST)'<visible>'$UNLOADVISIBLE'</visible></table>'
	  [ "$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="'$TEXTWIDTHCHARS'" width-request="'$TEXTWIDTH'"><input>echo -en "'$(gettext "You can load or unload extra SFS files on-the-fly.")'"</input></text>
   </hbox>
	<hbox width-request="'$BOXWIDTH'"><frame '$(gettext "Load SFS")'>
	<vbox height-request="'$BOXHEIGHT1'">
   '"$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="'$BOXHEIGHT1'">
   '$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 tooltip-text="'$(gettext "Refresh list of loaded sfs.")'"><input file stock="gtk-refresh"></input><action>sfs_load &</action><action>EXIT:Abort</action></button>
     <button tooltip-text="'$(eval_gettext "Quit $TITLE v$VERSION.")'"><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 'f2fs|ext2|ext3|ext4|reiserfs|btrfs|minix|msdos|vfat|exfat|ntfs')"
  local TOPPART="$1"
  # Offer RAM only if enough
  RAMPART=""
  if [ "$FILEISAT" != "tmpfs" -a "$FILEISAT" != "ata" -a "$PUPMODE" != "5" ]; 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
  
  #### pkg hack ... fix no GUI
  if [ "$GUI" != '' ];then
	RET=$($GTKDIALOG -c -p DIALOG || echo "$DIALOG" >&2)
	eval "$RET"
    PART=$(echo $PARTSPEC| cut -d'|' -f1)
	PART=$(echo $PART| cut -d '/' -f3)
  else
	## could use dialog here to build menu from $TOPPART $PCPARTS ####
	# ..but easier, just dont copy SFS by default, does no harm
    PARTSPEC="NOCOPY"
    PART="NOCOPY"
    EXIT='OK'
  fi
  
  [ "$EXIT" = "OK" ]
}
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
  MNTPNT=$(mount | grep -m1 "^/dev/$PART " | cut -f3 -d ' ') #v2.0.11
  MNTPNT=$(echo $MNTPNT)  # remove blank
  [ "$MNTPNT" != "" ] && return #partition is already mounted
  mkdir -p /mnt/$PART || return
  mount $OPT /dev/$PART /mnt/$PART || return
  UNMOUNTME=/dev/$PART
  MNTPNT=/mnt/$PART
}
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 'Content-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"
  $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
    F="$P"
    if [ "$echo $P| cut -b1)" = "/" ]; then F="$P"
    else
		F="/usr/share/applications/$P"
		[ -f "$F" ] || F="/usr/local/share/applications/$P"
	fi
    [ -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"
  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
  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_[ayzf]/"$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_[ayzf]/"$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_[ayzf]/"$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_[ayzf]/"$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() {
  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 -a | 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() {
 # 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"
 
 # 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=2	# precaution
  [ $N -ge 2 ] || N=2 # pup_ro1 and pup_ro2 reserved
  NEW=$(($N + 1))
  #v1.9.1: search lower
  if [ $NEW -gt 3 ]; then
    for I in $(seq 3 $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 3 $N) # skip 1-2
    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
  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 -a | grep -w "$EXTRASFS" | cut -sd ':' -f1)
  if [ "$LOOPDEVS" ]; then
    for D in $LOOPDEVS; do
      busybox umount "$D"
    done
    LOOPDEV=$(losetup -a | 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
    LOOPDEV=$(losetup -f)
    STATUS=-1
  fi
  if [ $STATUS -eq 0 ]; then
    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=""
  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
  [ "$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
  # remount aufs
  busybox mount -t aufs -o remount,append:$MNTPNT=rr /
  STATUS=$?
  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
  #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_PUPPYSFS|$DISTRO_ZDRVSFS|$DISTRO_ADRVSFS|$DISTRO_YDRVSFS|$DISTRO_FDRVSFS) :;;
  $SFSBASE|$ZDRBASE|$ADRBASE|$YDRBASE|$FDRBASE) :;;
  *) echo $EXTRASFSLIST | grep -qw "$FILENAME" || EXTRASFSLIST="$EXTRASFSLIST $FILENAME";;
  esac
  save_bootconfig
}
save_bootconfig() {
  EXTRASFSLIST=$(echo $EXTRASFSLIST)  # 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")
  CDDEVS=$(probedisk | grep -Ew 'optical|cdrom'| cut -d'|' -f1| cut -s -d'/' -f3)
  [ "$CDDEVS" ] || return
  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))
  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,$INITRDHOME,$MNTHOME,") #20151004
  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."
    save_bootconfig
    # remove from unionfs
    LOSETUPS=$(losetup -a | 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
       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
        # clean up whiteout in save layer
        [ -z "$NO_AFTERWORK" ]  && [ "$WIPEWHITEUNLOAD"  = "true" ] && cleanwhite $MNTPNT
        sleep 0.2  # 13 Nov 2011
        # remount aufs
        debug "busybox mount -t aufs -o remount,del:$MNTPNT unionfs /"
        busybox mount -t aufs -o remount,del:$MNTPNT unionfs /
        STATUS=$?
        if [ $STATUS -eq 0 ]; then
          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
    save_bootconfig
    # delete file
    RMLOG=""
    if [ -f "$EXTRASFS" ]; then
      DIRNAME=$(dirname "$EXTRASFS")
      FILENAME=$(basename "$EXTRASFS")
	  if [ "$(df "$DIRNAME" | tail -n 1 | cut -d ' ' -f1)" = "tmpfs" -a "$PUPMODE" != "5" ]; 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=""
    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

# Temp work around. Do not mount SFS from /tmp when running in pupmode 5
. /etc/rc.d/PUPSTATE 
if [ "$(echo $ORGOPT | cut -f2 -d/)" = "tmp" -a "$PUPMODE" = "5" ]; then
  ### pkg hack, use dialog, not xdialog
  dialog --title "$(gettext 'Error')" --msgbox \
   "$(gettext 'You can not install an SFS saved in the /tmp directory \nwhen running in PUPMODE=5')" 0 0
 exit 0
fi

#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
 
[ "`whoami`" != "root" ] && exec sudo -A ${0} ${@}

date >> "$LOGFILE"
echo $0 $@ >> "$LOGFILE"
echo "$MYNAME-$VERSION" >> "$LOGFILE"

 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";;
*) 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=$(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

if [ "$PUPMODE" = "6" ];then
 INITRDHOME=/
 MNTHOME=/
else
 INITRDHOME=/initrd$PUP_HOME
 MNTHOME=/mnt/home
fi

# 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")
ZDRBASE=${ZDRV##*/}
ADRBASE=${ADRV##*/}
YDRBASE=${YDRV##*/}
FDRBASE=${FDRV##*/}

KERNVER="`uname -r | cut -f 1 -d \-`" #20151004
SUPPORTSIG="green"
SFSVER=4 #supported

[ "$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=$INITRDHOME #20151004
  [ "$PUPHOME" = "$INITRDHOME" ] && PUPHOME=$MNTHOME && 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
      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"
  DESTDIR=$PUPHOME
fi
debug "PUPHOME=$PUPHOME;"
debug "SUPPORTSIG=$SUPPORTSIG, SFSMODE=$SFSMODE, DESTPART=$DESTPART, DESTDIR=$DESTDIR;"

if [ "$SFSMODE" != "" ]; then
  LOSETUP_A="$(losetup -a)"
  loaded_sfs_list
  queued_sfs_list
fi

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

  [ "$SFSMODE" ] || exit 0
  [ "$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=$(busybox 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=$(busybox ps)
    PIDS=$(echo "$PIDS"| grep 'sh.*sfs_load' | grep -vw "$$")
   done
   rm -f "$LOCKFILE"
   log "Finished."
   exit 0
  fi
  MNTPNT=""
  COPIED=""
  NEED_FIXMENUS=""	#v2.0
  CHANGED_DRIVE=""	#v2.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."
      remove_item EXTRASFSLIST "$F"
      save_bootconfig
      continue
    fi
    if ! check_sfs_version; then
      log "'$F' SFS version not match."
      remove_item EXTRASFSLIST "$F"
      save_bootconfig
      continue
    fi
    # copy to ram?
    #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)
      # need another tmpfs
      DESTDIR="/mnt/tmp"
      ERRMSG=""
      ram_free
      REST=$(($RFREE - $FILESIZE))
      [ $REST -gt $RESERVRAM ] && another_tmpfs #|| continue
      DESTISAT="tmp"
      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"
    fi
    PRINTDONE=1
   done
  fi
  # BOOTCONFIG vs BOOTCONFIG.save
  if [ "$EXTRASFSLIST" != "$PREVEXTRASFSLIST" -a $PUPMODE -ne 5 ]; then
   echo -n "layers have changed, fixing menu..." >/dev/console
   echo "layers have changed, fixing menu..."
   [ "$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
    [ "$NEED_FIXMENUS" ] && { jwm -reload &>/dev/null || jwm -restart ; }
    log "jwm restarted."
   fi
   PRINTDONE=1
  fi
  if [ $PRINTDONE ] ; then
    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
  fi
  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
  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 [ "$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.")

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
[ "$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|$FDRBASE) 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
  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
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"
    confirm "$MSG" || finish
  fi
  if [ "$SFSMODE" != "" ]; then
    if [ "$FILENAME" = "$DISTRO_FDRVSFS" ]; then
      confirm --ok-cancel "$(gettext "'$FILENAME' contains additional firmware that maybe needed. Are you sure you want to remove it?")" || finish
    fi
    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 -a | 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 ###
[ "$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";;
  $MNTHOME/$PSUBDIR) FILEISAT="home";; #20151004
  $MNTHOME) 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" = "" ]; 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
  [ "$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"
       ;;
  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
  confirm "$MSG\n$WARNSHORT" || finish
fi
[ "$(readlink $MNTHOME)" = "$DESTDIR" ] && DESTDIR=$MNTHOME #20151004

# 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"
  [ "$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 -a | 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 $MNTHOME)" = "$D" ] && FOUND="$MNTHOME/$FILENAME"
      MSG=$(printf "$(gettext "%s is found. Do you use it?")" $FOUND)
      confirm $MSG || finish
      EXTRASFS="$FOUND"
      [ "$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"
      [ "$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
        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"
    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"
         [ "$DESTISAT" = "tmp" ]  && FILEISAT="tmp" || FILEISAT="home"
         break
  done
  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"
  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"
  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
  # already mounted?
  MNTPNT=""
  LOOPDEV=$(losetup -a | 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 -a | 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
  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 -a | 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
