#!/bin/sh

#==============================================================================
# LiveCD/USB/HD init script
# (C) 2009-2016 BitJam for antiX <antiX@operamail.com>
# Inspired by the work of Klaus Knopper
#
# License: GPLv3 or later
#==============================================================================

# SUFFIX NOTE: _MP     absolute mountpoint
#              _NAME   filename with no path
#              _PATH   relative path but no filename
#              _FILE   relative path with filename
#              _FULL   absolute mountpoint and path and filename
#              _DIR    varies
#
# CUSTOMIZATION:
# Customize to your distro using /etc/initrd-release.  Add further
# customization if needed with scripts at /live/custom/$DISTRO/[1-9].sh.
# These scripts are run at the respective breakpoints.  So 1.sh is run
# at breakpoint 1 and so on.  The script 0.sh is run before we read the
# boot parameters.  You can use this to set defaults that can be
# over-ridden by boot parameters.  Use 1.sh to override boot parameters
# that you don't want users to be able to adjust.

                     ME="initrd init"
                VERSION="7.60.04"
           VERSION_DATE="Sun Oct 30 16:21:26 MDT 2016"
              DEVELOPER="BitJam"
         BUG_REPORT_URL="liveboot@mxlinux.org"

# This are all times in seconds, not attempts
             BOOT_RETRY=15
           FRUGAL_RETRY=10
          PERSIST_RETRY=10
    FAILSAFE_BOOT_RETRY=30

    DISABLE_SERVICES_OPTS="(c[0-9]+(\.[0-9]*)?|c\.[0-9]+|[DLMPSXacdgklmnNrsvwx])+"

           RW_MODE="ro"
       #DISTRO_NAME="Linux"
            PRINTK=0

          LIVE_DIR="/live"
         FINAL_DIR="/live"
          LIVE_BIN="/bin"

          AUFS_DIR="aufs"

       EXTRA_FILES="deb xtra xtra.tgz state"
       MIN_SYS_RAM=80
      MIN_AUFS_RAM=80

           LOG_DIR="/var/log/live"
          LOG_FILE="$LOG_DIR/initrd.log"

            MY_LOG=/init.log
           VID_DIR="/etc/live/version"
          VID_FILE="$VID_DIR/linuxfs.ver"
          MAKE_OLD="rootfs xtra deb xtra.tgz linuxfs.md5"
           MOD_DIR="/lib/modules/$(uname -r)"

         FROM_TYPE="usb,cd"
     FROM_TYPE_ALL="usb,cd,hd"

      FRUGAL_FILES="linuxfs vmlinuz vmlinuz1 initrd.gz xtra xtra.tgz"
      FRUGAL_FILES="$FRUGAL_FILES ../boot ../efi ../EFI ../cdrom.ico ../version"

     MAX_MOUNT_CNT=30
       RETRY_DELAY=50               # in hundredths of a second
           VERBOSE=5

       DO_COLDPLUG=true
    COLDPLUG_DELAY=
          USER_UID=1000
          USER_GID=1000

            DO_DEB=true

   PLUG_LOOP_DELAY=50   # In 100ths of a second

        # Note: adding "crc32c-generic" works around a bug in the kernel:
        # https://github.com/manjaro/packages-core/issues/8
        LOAD_FIRST="usb-common usbcore ehci-hcd fusbh200-hcd fotg210-hcd battery aufs overlay crc32c-generic mmc-block"

          VID_NAME=VID
      LINUXFS_NAME=linuxfs

        BLACK_LIST=
   BLACK_LIST_FILE=/etc/modprobe.d/initrd.conf
 KMS_VIDEO_MODULES="ast,bochs-drm,cirrus,emgd,gma500_gfx,i915,mgag200,nouveau,qxl,radeon,udl,vmwgfx"
    DEFAULT_SQFILE=/antiX/linuxfs
  DEFAULT_ISO_FILE=/antiX/antiX.iso

  LIVE_X64_LD_PATH="/lib:/lib/x86_64-linux-gnu:/usr/lib"
  LIVE_386_LD_PATH="/lib:/lib/i386-linux-gnu:/usr/lib"

MKFS_FREE_MARGIN=10
  rootfs_SIZE_PARAM="min_size=250 mid_size=100 max_size=2000 factor=50"
  homefs_SIZE_PARAM="min_size=10 mid_size=100 max_size=4000 factor=25"
         MKFS_SIZES="250 375 500 750 1000 1500 2000 2500 3000 4000"
       BIGGER_SIZES="5000 6000"
      SMALLER_SIZES="10 100 175"

      LIVE_SCRIPTS="live-L10n live-init"
      LAST_BP_TIME=0
          NO_PLINK=""
   DEFAULT_UNIONFS="overlay"
   OPTIONS_ENTRIES="toram|from=usb|password|nousb2"

          LAZYTIME=""

set_live_dirs() {
    local dir=$1

         FRUGAL_MP="$dir/frugal"
           BOOT_MP="$dir/boot-dev"
        ISO_DEV_MP="$dir/iso-dev"
       ISO_FILE_MP="$dir/iso-file"
          TORAM_MP="$dir/to-ram"
       AUFS_RAM_MP="$dir/aufs-ram"
           AUFS_MP="$dir/$AUFS_DIR"
         ROOTFS_MP="$dir/persist-root"
        PERSIST_MP="$dir/persist-dev"
           SQFS_MP="$dir/linux"         # where the linuxfs file gets *mounted*
      WRAP_FILE_MP="$dir/wrapper"
        OUTPUT_DIR="$dir/config"
        LOCALE_DIR="$dir/locale"
        CUSTOM_DIR="$dir/custom"
 DISABLED_USB_FILE="$OUTPUT_DIR/usb-disabled"
}


hbar="======================================================================"
tbar="----------------------------------------------------------------------"

main_wrapper() {

    local black blue green cyan red purple brown lt_gray dk_gray lt_blue
    local lt_green lt_cyan lt_red magenta yellow white rev_red
    local cheat_co cmd_co dev_co err_co from_co to_co head_co
    local hi_co mp_co m_co nc_co num_co ok_co bold_co

    PATH=$LIVE_BIN
    HOME=/
    TERM=linux
    PWD=/

    set_live_dirs $LIVE_DIR
    mkdir -p $OUTPUT_DIR

    # Don't allow interrupts to ruin our day
    #       HUP INT QUIT SEGV TERM
    trap "" 1   2   3    11   15

    umask 022

    system_mount
    local time_0=$(cut -d" " -f22 /proc/$$/stat)

    make_nodes /dev

    read_distro_release /etc/initrd-release
    : ${DISTRO_BUG_REPORT_URL:=$BUG_REPORT_URL}
    : ${DISTRO_NAME:=antiX}
    : ${DISTRO_PRETTY_NAME:=antiX-16 (generic)}

    custom_code 0 'before reading boot codes'

    clear_env
    read_cmdline_params
    select_breakpoints

    FREE_MEM=$(mem_info MemFree)

    set_colors "$NO_COLOR" "$LOW_COLOR"

    read_xlat init $LANG

    # disable console screen blanking
    printf "\e[9;0]\e[14;0]"

    echo "$PRINTK" > /proc/sys/kernel/printk

    SCREEN_WIDTH=$(stty size 2>/dev/null | cut -d" " -f2)

    [ -z "${FINAL_DIR##/*}" ] || FINAL_DIR="/$FINAL_DIR"
    NEW_ROOT=$AUFS_MP

    if [ "$NO_ERR_LOG" ]; then
        main_core
    else
        # Evaluate redirects RIGHT TO LEFT:
        #
        #  stdout  1 -------.        .---> while, sed, tee ---> screen (stdout)
        #                    \      /
        #  stderr  2 ---.     `--- / -------------------------> screen (stderr)
        #                \        /
        #         17      `------'

        main_core  17>&1  1>&2  2>&17  | while read line; do echo "$line" \
            | sed -e "s/^/${yellow}Error: $red/" -e "s/$/$nc_co/" | tee -a $MY_LOG; done
    fi

    read LAST_BP_TIME < /LAST_BP_TIME

    start_hotplug

    # Nota Bene: when error log is enabled, main_core() runs in a subshell
    # so it cannot affect our variables.

    # Need to run persist-password outside of our error catching

    breakpoint 8      "before running live init.d scripts"
    breakpoint b8     "Bash shell before running live init.d scripts"

    # Try running live-usb-save first so it can restore an xorg.conf machine-state file
    confile remasterable && LIVE_SCRIPTS="live-usb-save $LIVE_SCRIPTS"
    confile hwclock      && LIVE_SCRIPTS="$LIVE_SCRIPTS live-hwclock"
    confile persist-root force-passwd && LIVE_SCRIPTS="$LIVE_SCRIPTS persist-password"
    confile save-persist && LIVE_SCRIPTS="$LIVE_SCRIPTS persist-autosave"
    # Do this after the above so the changes aren't saved until after a "real" persist-save
    confile systemd      || LIVE_SCRIPTS="$LIVE_SCRIPTS live-disable-services"
    confile toram-eject  && LIVE_SCRIPTS="$LIVE_SCRIPTS live-toram-eject"
    confile bootsave     && LIVE_SCRIPTS="$LIVE_SCRIPTS live-bootsave"
    confile deb-install  && LIVE_SCRIPTS="$LIVE_SCRIPTS live-deb-install"

    unset CMDLINE
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
    run_init_scripts $NEW_ROOT $LIVE_SCRIPTS
    PATH=$LIVE_BIN

    test -r /late-umount.sh && . /late-umount.sh
    sync
    late_umount $PERSIST_DEVICE
    late_umount $SQFILE_DEV

    if [ -z "$DISABLE_STORE" ]; then
        if detect_old_persist_scripts $NEW_ROOT; then
            warn  "$_X_disabled_due_to_outdated_persistence_scripts_and_libs_" "$(pqw LiveUSB-Storage)"
        else
            confile remasterable && run_init_scripts $NEW_ROOT live-usb-storage

            local dir new_dir
            for dir in config bin; do
                local new_dir=$NEW_ROOT/etc/live/$dir
                mkdir -p $new_dir
                mount --bind /live/aufs/live/$dir $new_dir
            done

            chmod 700 $NEW_ROOT/live
        fi
    fi

    local f rm_list
    for f in $NEW_ROOT/live/menus/*; do
        case $f in *tz.*) continue;; esac
        rm_list="$rm_list $f"
    done
    rm -f $rm_list

    dmesg > $NEW_ROOT/live/config/dmesg.out

    breakpoint bash  "Bash shell right before starting init"
    breakpoint b9    "Bash shell right before starting init"
    breakpoint 9     "right before starting init"

    stop_hotplug

    local time_1=$(get_time)
    local dt=$((time_1 - time_0))
    vmsg 6
    vmsg 5  "$_The_X_program_took_Y_seconds_" "$ME" $(nq $(get_seconds $dt))
    vmsg 6 $hbar

    write_log_files $MY_LOG $NEW_ROOT/$LOG_FILE
    MY_LOG=$NEW_ROOT/$LOG_FILE.color

    #free -m >> $MY_LOG
    echo 3 > /proc/sys/vm/drop_caches
    vmsg 6 'drop caches'
    #free -m >> $MY_LOG

    if find_init_prog INIT_PROG $NEW_ROOT; then

        vmsg 4 'Run %s instead of %s' $white$INIT_PROG$m_co $white/sbin/init$m_co
        vmsg 5 "${hi_co}%s" 'Please exit shells normally to allow for a clean unmount'

        CHROOT_BIN=$FINAL_DIR/bin
        chroot $NEW_ROOT $CHROOT_BIN/setsid $CHROOT_BIN/cttyhack $INIT_PROG
        breakpoint i "after init= chroot"

        MY_LOG=/dev/null
        safe_shutdown poweroff ask

    else
        : ${INIT_COMMAND:=/sbin/init}
        test -x $NEW_ROOT$INIT_COMMAND || INIT_COMMAND=/sbin/init
        vmsg 6  "Start %s process" $(pq $INIT_COMMAND)
        exec $LIVE_BIN/switch_root -c /dev/console $NEW_ROOT $INIT_COMMAND "$@"
    fi
}

main_core() {
    [ "$NO_CLEAR" ] || printf "\e[1J"       # Clear and reset screen
    vmsg 6 "=== initrd bootstrap =================================================="

    vmsg 6 '%s started at %s seconds' "$ME" $(nq $(get_seconds $time_0))

    mkdir -p $MOD_DIR
    depmod &>/dev/null
    blacklist_modules $BLACK_LIST

    breakpoint 1 "before welcome"

    # These ones must be loaded before the others
    # Add a cheap black-lister
    if [ ${#BLACK_LIST} -gt 0 ]; then
        local bl_regex=${BLACK_LIST#,}
        bl_regex=${bl_regex%,}
        bl_regex=${bl_regex//,/|}
        echo $LOAD_FIRST | tr " " "\n" | egrep -v "^($bl_regex)$" | xargs modprobe -q -a 2>/dev/null
    else
        echo $LOAD_FIRST | xargs modprobe -q -a 2>/dev/null
    fi

    start_hotplug

    do_welcome "$DISTRO_PRETTY_NAME" "$VERSION" "$VERSION_DATE"
    show_bootcodes "$(live_param_filter $UNKNOWN_BOOTCODES)"
    show_integer_errors "$INTEGER_ERRORS"
    show_disable_errors "$DISABLE_SERVICES" "$DS_PARAM"
    breakpoint 2 "before coldplug"

    check_unionfs

    coldplug_delay

    coldplug_modules

    # local orig_fb_name
    # read orig_fb_name 2>/dev/null </sys/class/graphics/fb0/name
    load_kernel_modules "$MODULE_LIST"
    # set_fbcondecor "$SPLASH_PARAM" "$orig_fb_name"

    initialize_cmdline2

    read PROC_CMDLINE 2>/dev/null </proc/cmdline

    [ "$DO_MENUS" ]; do_early_menus

    read_late_cmdline_params "$PROC_CMDLINE $(cat /live/config/cmdline)"
    echo "$PROC_CMDLINE" > /live/config/proc-cmdline

    clean_xlat

    : ${SQFILE_FILE:=$DEFAULT_SQFILE}
    : ${SQFILE_NAME:=${SQFILE_FILE##*/}}

    [ -z "${SQFILE_FILE##*/*}" ] && : ${BOOT_DIR:=${SQFILE_FILE%/*}}

    [ "$SQFILE_EXT" ] && SQFILE_NAME="$SQFILE_NAME.$SQFILE_EXT"
    SQFILE_FILE="$BOOT_DIR/$SQFILE_NAME"
    SQFILE_FILE=${SQFILE_FILE#/}

    # Wait and only do this in the find_files() loop so the usb report
    # will always work
    #[ "$NO_EHCI" ] && disable_hcd ehci

    breakpoint 3 "before looking for linuxfs file"

    RW_MODE=ro

    if [ "$FRUGAL_ID" ]; then
        find_frugal_file "$FRUGAL_ID" || frugal_error $?
    else
        find_linuxfs_file
    fi

    mount -o remount,rw $BOOT_MP &>/dev/null
    dir_has_param "$BOOT_MP" rw && REMASTERABLE=true
    [ "$DID_ISO" ] && REMASTERABLE=

    case $BOOT_FSTYPE in
        iso9660|udf) REMASTERABLE= ;;
    esac

    BOOT_UUID=$(device_uuid $SQFILE_DEV)
    [ "$BOOT_UUID" ] && vmsg 6 "boot device uuid: $BOOT_UUID"
    breakpoint 4 "after mounting boot device"

    mount -o remount,rw $SQFILE_DEV 2>/dev/null
    dir_has_param "$SQFILE_MP" rw || REMASTERABLE=
    [ "$REMASTERABLE" ] && do_remaster "$SQFILE_FULL"

    mount_linuxfs "$SQFS_MP" "$SQFILE_FULL" "$WRAP_FILE_MP"

    if [ "$LOAD_ALL_MODULES" ]; then
        debug_cmd find /sys/devices -name uevent -exec sed -n 's/^MODALIAS=//p' '{}' + 2>/dev/null | sort -u
        debug_cmd lsmod
    fi

    prep_ld_path

    RW_MODE=rw
    # FIXME: this makes debugging persist_makefs simpler
    #[ "$PERSIST_FILES" ] && mount -o remount,rw $SQFILE_DEV
    mount -o remount,rw $SQFILE_DEV

    #breakpoint 4 "after mounting linuxfs file"

    prepare_persistence

    mount_persist_device "$PERSIST_FILES" "$FROM_PERSIST"

    list_modules

    # Check for ehci warning
    dmesg | grep 'Warning.*ehci' >&2

    breakpoint 5 "after mounting persistence device"

    fsck_boot_dev "$SQFILE_DEV" "$SQFILE_MP"

    ORIG_SQFILE_MP=$SQFILE_MP
    ORIG_SQFILE_DIR=$SQFILE_PATH
    ORIG_SQFILE_FULL=$ORIG_SQFILE_DIR/$SQFILE_NAME

    test -d $SQFILE_MP/$SQFILE_PATH/deb || DO_DEB=

    # Check md5 of files in directory containing linuxfs file
    # 'fromiso' causes us to check md5sums in two places
    [ "$CHECK_MD5" ] && check_md5 $SQFILE_DIR

    [ "$TO_RAM" ] && copy_to_ram "$SQFILE_FULL" $TORAM_MP

    check_kernel_version $SQFS_MP

    mount -o remount,rw $SQFILE_DEV 2>/dev/null
    fsck_persist_dev "$PERSIST_FILES" "$PERSIST_DEVICE" "$PERSIST_MP"

    [ "$WANT_ROOTFS" ] && remaster_rootfs "$PERSIST_MP" "$PERSIST_FULL_PATH"

    mount_aufs_ram "$AUFS_RAM_MP" "$MIN_AUFS_RAM" "$FREE_MEM"

    mount_and_copy_rootfs

    [ "$STATIC_ROOT" ] && log_cmd umount $AUFS_RAM_MP

    mount_unionfs "$AUFS_MP" "$SQFS_MP" "$AUFS_RAM_MP" "$ROOTFS_MP"

    SYSTEM_D=
    local sbin_init=$(readlink $NEW_ROOT/sbin/init)
    if [ -n "$sbin_init" -a -z "${sbin_init##*systemd}" ]; then
        SYSTEM_D=true
        touch /live/config/systemd
        msg  "$_Detected_X_init_system_" $(cq systemd)
    fi

    breakpoint 6 "after mounting aufs"

    rm -f $NEW_ROOT$BLACK_LIST_FILE
    [ -e $BLACK_LIST_FILE ] && cp $BLACK_LIST_FILE $NEW_ROOT$BLACK_LIST_FILE

    [ "$WANT_HOMEFS" ] \
        && mount_homefs $PERSIST_FULL_PATH/homefs $NEW_ROOT/home "$WANT_HOMEFS" "$NEED_HOMEFS"

    [ "$DB_PLUS"       ] && local plus_t1=$(get_time)
    [ "$DO_XTRA"       ] && copy_xtra    $DEFAULT_DIR
    [ "$DO_XTRA"       ] && delete_files $DEFAULT_DIR
    [ "$DO_AUTO_LOGIN" ] && auto_login   "$AUTO_LOGIN_PROG" "$AUTO_LOGIN_TERMS" "$NEW_ROOT"
    [ "$DO_FANCY"      ] && fancy_prompt "$FANCY_PROMPT" "$NEW_ROOT"
    if [ "$DB_PLUS" ]; then
        local plus_t2=$(get_time)
        local delta=$(get_seconds $((plus_t2 - plus_t1)))
        vmsg 8 "$green@${cyan} Extras took$hi_co $delta${cyan} seconds"
    fi

    [ ! -e /$NEW_ROOT/etc/issue -o -n "$FORCE_ISSUE" ] && cp /issue $NEW_ROOT/etc/issue

    # FIXME: do this after copy_xtra for easy testing (for now) (????)
    PROC_CMDLINE=$(cat /live/config/proc-cmdline)
    [ "$DO_MENUS" ] && do_late_menus
    echo "$PROC_CMDLINE" > /live/config/proc-cmdline

    [ "$NO_NEW_OUTPUT" ] || write_output_files     $OUTPUT_DIR
    [ "$NO_OLD_OUTPUT" ] || old_write_output_files $OUTPUT_DIR

    update_store_file "$NO_STORE" "$DO_STORE"

    mkdir -p $NEW_ROOT/etc/live

    echo $FINAL_DIR > $NEW_ROOT/etc/live/live-dir

    # FIXME: since we are using the real persist-save we can do these either
    # before or after we run the init scripts!  But we lose some of the variables
    # when not run inside of main_core().

    #sync
    #late_umount $PERSIST_DEVICE
    #late_umount $SQFILE_DEV

    stop_hotplug

    breakpoint 7 "before prepare switch_root"

    prepare_switch_root $NEW_ROOT $LIVE_DIR $FINAL_DIR

    write_ntfs_pids $FINAL_DIR/aufs/run/sendsigs.omit.d/initrd-ntfs-3g

    echo $LAST_BP_TIME > /LAST_BP_TIME

    cat<<Late_Umount > late-umount.sh
PERSIST_DEVICE="$PERSIST_DEVICE"
SQFILE_DEV="$SQFILE_DEV"
HOMEFS_DEV="$HOMEFS_DEV"
ROOTFS_DEV="$ROOTFS_DEV"
LINUXFS_DEV="$LINUXFS_DEV"
Late_Umount
}

#------------------------------------------------------------------------------
# Getting started
#------------------------------------------------------------------------------

system_mount() {
    dir=$1

    mkdir -p $dir/proc $dir/sys $dir/dev

    mount -t proc   proc  $dir/proc
    mount -t sysfs  sys   $dir/sys

    mount -t devtmpfs devtmpfs $dir/dev
    mkdir -p $dir/dev/pts
    mount -t devpts devpts $dir/dev/pts
}


make_nodes() {
    local dir=$1
    mkdir -p $dir

    [ -e $dir/console ] || mknod $dir/console c 5 1
    [ -e $dir/null    ] || mknod $dir/null    c 1 3
    [ -e $dir/tty0    ] || mknod $dir/tty0    c 4 0
}

read_distro_release() {
    local file=$1
    #sed -r -n 's/^\s*([A-Z0-9_]+=)/DISTRO_\1/p' $file >> $MY_LOG
    eval $(sed -r -n 's/^\s*([A-Z0-9_]+=)/DISTRO_\1/p' $file)
}

clear_env() {
    local ev
    # unset almost all env variables
    for ev in $(printenv | sed 's/=.*//'); do
        case $ev in
            HOME|PATH|PWD|TERM) continue;;
        esac
        unset $ev
    done
}

read_late_cmdline_params() {
    #vmsg 6 "read_late_cmdline_params: $*"
    local param value cmdline=${1:-$(cat /proc/cmdline)}

    for param in $cmdline; do
        value=${param#*=}

        case $param in
                         frugal) set_frugal                           ;;
                       frugal=*) set_frugal $value                    ;;
                hwclock=*|hwc=*) DO_HWCLOCK=true                      ;;
                         from=*) FROM_TYPE=$value ; ALWAYS_SCAN=true  ;;
                  persist=*|p=*) PERSIST=$PERSIST,$value              ;;
                        persist) PERSIST=$PERSIST,root,home           ;;
                         lang=*) LANG=$value                          ;;
                   md5|checkmd5) CHECK_MD5=true                       ;;
                        checkfs) FORCE_FSCK=true                      ;;

                  nousb2|noehci) NO_EHCI=true                         ;;
              private|private=*) FORCE_PASSWD=true                    ;;
                          toram) TO_RAM=true                          ;;

        # Convenience short cuts from our menus
                    persist_all) PERSIST='root!,home'                 ;;
                   persist_root) PERSIST='root!'                      ;;
                persist_statict) PERSIST='root!,home,static'          ;;
                   persist_home) PERSIST='home!'                      ;;

                 frugal_persist) set_frugal 'root!,home'              ;;
                    frugal_root) set_frugal 'root!'                   ;;
                  frugal_static) set_frugal 'root!,home,static'       ;;
                    frugal_home) set_frugal 'home!'                   ;;
                    frugal_only) set_frugal                           ;;

                        nostore) NO_STORE=true                        ;;
                        dostore) DO_STORE=true                        ;;
        esac
    done
}

set_frugal() {
    : ${FRUGAL_ID:=label=$DISTRO_NAME-Frugal}
    [ "$NO_LABEL_FRUGAL" ] || LABEL_FRUGAL=true
    PERSIST=$1
}

#------------------------------------------------------------------------------
# Function read_cmdline_params
#
# Gather all boot codes
#------------------------------------------------------------------------------
read_cmdline_params()
{
    local param value cmdline=${1:-$(cat /proc/cmdline)}

    for param in $cmdline; do
        value=${param#*=}

        case $param in
                           aufs) WANT_UNIONFS=aufs                               ;;
                      overlayfs) WANT_UNIONFS=overlay                            ;;
                          menus) DO_MENUS=${MENUS_LIST:-ltopfs}                  ;;
                        menus=*) DO_MENUS=$DO_MENUS$value                        ;;
            rootdelay=*|delay=*) small_int COLDPLUG_DELAY $param                 ;;
                       old-conf) NO_NEW_OUTPUT=true                              ;;
                       new-conf) NO_OLD_OUTPUT=true                              ;;
                         splash) SPLASH_PARAM=v                                  ;;
                       splash=*) SPLASH_PARAM=$value                             ;;
                       poweroff) poweroff -f -n                                  ;;
                         reboot) reboot -f -n                                    ;;
               bootdir=*|bdir=*) BOOT_DIR=$value                                 ;;
    bootlabel=*|blabel=*|blab=*) BOOT_ID=label=$value                            ;;
             bootuuid=*|buuid=*) BOOT_ID=uuid=$value                             ;;
               bootdev=*|bdev=*) BOOT_ID=name=$value                             ;;

                  try=*|retry=*) small_int CMD_RETRY $param                      ;;

            persistdir=*|pdir=*) PERSIST_PATH=${value#/}                         ;;

 persistlabel=*|plabel=*|plab=*) PERSIST_ID=label=$value  ;  SET_PERSIST=true    ;;
          persistuuid=*|puuid=*) PERSIST_ID=uuid=$value   ;  SET_PERSIST=true    ;;
            persistdev=*|pdev=*) PERSIST_ID=name=$value   ;  SET_PERSIST=true    ;;

                         btry=*) small_int CMD_BOOT_RETRY $param                 ;;
                         ftry=*) small_int CMD_FRUGAL_RETRY $param               ;;
                         ptry=*) small_int CMD_PERSIST_RETRY $param              ;;

                        fneed=*) FRUGAL_NEEDED=$value                            ;;
                         fforce) FORCE_FRUGAL=true                               ;;
                         flab=*) FRUGAL_ID=label=$value ; NO_LABEL_FRUGAL=true   ;;
                         fdev=*) FRUGAL_ID=name=$value  ; NO_LABEL_FRUGAL=true   ;;
                        fuuid=*) FRUGAL_ID=uuid=$value  ; NO_LABEL_FRUGAL=true   ;;

                iso=*|fromiso=*) ISO_FILE=${value/#}                             ;;
                    iso|fromiso) FROM_ISO=true                                   ;;

                        sqext=*) SQFILE_EXT=$value                               ;;
                       sqname=*) SQFILE_NAME=${value#/}                          ;;
                           sq=*) SQFILE_FILE=$value                              ;;

           verbose=*|verb=*|v=*) small_int VERBOSE $param                        ;;
                           bp=*) BREAK_POINTS=$BREAK_POINTS,$value               ;;
                           pk=*) PRINTK=$value                                   ;;

                 hico|highcolor) LOW_COLOR=                                      ;;
                  loco|lowcolor) LOW_COLOR=true                                  ;;
                   noco|nocolor) NO_COLOR=true                                   ;;

                         noxtra) DO_XTRA=                                        ;;
                         doxtra) DO_XTRA=true                                    ;;

                       fdb+|db+) DO_AUTO_LOGIN=true; DO_FANCY=true; DB_PLUS=true ;;
                           db++)                                                 ;;

            fancyprompt|fprompt) DO_FANCY=true                                   ;;
               autologin|alogin) DO_AUTO_LOGIN=true                              ;;

                          nodeb) DO_DEB=                                         ;;
                    #toram-eject) TO_RAM=true ; TO_RAM_EJECT=true                 ;;

                     noremaster) NO_REMASTER=true                                ;;
                       rollback) ROLLBACK=true                                   ;;
                         lang=*) LANG=$value                                     ;;

                        noclear) NO_CLEAR=true                                   ;;

                      gfxsave=*) GFX_SAVE=$value ; BOOT_SAVE=true                ;;
                   save|gfxsave) GFX_SAVE=both   ; BOOT_SAVE=true                ;;
                       bootsave) BOOT_SAVE=true                                  ;;

                      nocheckfs) DO_FSCK=                                        ;;

                       failsafe) MODULE_LIST=all
                                 BLACK_LIST=$BLACK_LIST,VIDEO
                                 BOOT_RETRY=$FAILSAFE_BOOT_RETRY
                                 FROM_TYPE=all                                   ;;

                         load=*) MODULE_LIST=$MODULE_LIST,$value                 ;;
               bl=*|blacklist=*) BLACK_LIST=$BLACK_LIST,$value                   ;;

                     nocoldplug) DO_COLDPLUG=                                    ;;
                      nohotplug) FORCE_COLDPLUG_LOOP=true                        ;;
                      traceload) TRACE_LOAD=true                                 ;;
                 autoload|aload) FULL_AUTOLOAD=true                              ;;

                         init=*) INIT_PROG=$value                                ;;

                       noerrlog) NO_ERR_LOG=true                                 ;;
                        noerr=*) NO_ERROR=$value                                 ;;

                      # Not used here see live-init
                      vtblank=*) small_int VT_BLANK $param                       ;;

               livedir=*|ldir=*) FINAL_DIR=$value                                ;;
        pw|passwd|pw=*|passwd=*) FORCE_PASSWD=true                               ;;
                      bootchart) INIT_COMMAND=/sbin/bootchartd ; BOOT_CHART=true ;;
                        noplink) NO_PLINK=,noplink                               ;;
                          plink) NO_PLINK=                                       ;;

                       fatuid=*) any_int USER_UID $param                         ;;
                       fatgid=*) any_int USER_GID $param                         ;;

             disable=*|nosysv=*) DISABLE_SERVICES="$DISABLE_SERVICES$value" ; DS_PARAM=$param  ;;

                   disablestore) DISABLE_STORE=true                              ;;
                         nolazy) LAZYTIME=                                       ;;
                       lazytime) LAZYTIME=,lazytime                              ;;

                          issue) FORCE_ISSUE=true                                ;;

        #----- These get interpreted late but we don't want them to be unknown
                hwclock=*|hwc=*)  ;;
                         frugal)  ;;
                       frugal=*)  ;;
                         from=*)  ;;
                  persist=*|p=*)  ;;
                        persist)  ;;
                         lang=*)  ;;
                   md5|checkmd5)  ;;
                        checkfs)  ;;
                  nousb2|noehci)  ;;
              private|private=*)  ;;
                          toram)  ;;

        # Convenience short cuts from our menus
                    persist_all)  ;;
                   persist_root)  ;;
                 persist_static)  ;;
                   persist_home)  ;;
                 frugal_persist)  ;;
                    frugal_root)  ;;
                  frugal_static)  ;;
                    frugal_home)  ;;
                    frugal_only)  ;;
                        nostore)  ;;
                        dostore)  ;;

        #----- Some known codes -------------------------

        [sS1-6]|BOOT_IMAGE=*);;
        video.*);;

        # Most kernel codes from version 3.8 (plus additions)
        acpi_force_table_verification|acpi_no_auto_serialize|acpica_no_return_repair);;
        forcepae|systemd.*);;
        acpi=*|acpi_rsdp=*|acpi_apic_instance=*|acpi_backlight=*);;
        acpi.debug_layer=*|acpi.debug_level=*|acpi_irq_balance);;
        acpi_irq_nobalance|acpi_irq_isa=*|acpi_irq_pci=*|acpi_no_auto_ssdt);;
        acpi_os_name=*|acpi_osi=*|acpi_pm_good|acpi_sci=*|acpi_serialize);;
        acpi_skip_timer_override|acpi_sleep=*|acpi_use_timer_override);;
        acpi_enforce_resources=*|add_efi_memmap|agp=*|ALSA|alignment=*);;
        align_va_addr=*|amd_iommu=*|amd_iommu_dump=*|amijoy.map=*);;
        analog.map=*|apc=*|apic=*|autoconf=*|show_lapic=*|apm=*|arcrimi=*);;
        ataflop=*|atarimouse=*|atkbd.extra=*|atkbd.reset=*|atkbd.set=*);;
        atkbd.scroll=*|atkbd.softraw=*|atkbd.softrepeat=*|baycom_epp=*);;
        baycom_par=*|baycom_ser_fdx=*|baycom_ser_hdx=*|boot_delay=*);;
        bootmem_debug|bttv.card=*|bttv.radio=*|bttv.pll=*|bttv.tuner=*);;
        bulk_remove=*|c101=*|cachesize=*|ccw_timeout_log|cgroup_disable=*);;
        checkreqprot|cio_ignore=*|clock=*|clocksource=*|clearcpuid=*|cma=*);;
        cmo_free_hint=*|coherent_pool=*|code_bytes|com20020=*|com90io=*);;
        com90xx=*|condev=*|conmode=*|console=*|consoleblank=*);;
        coredump_filter=*|cpuidle.off=*|cpcihp_generic=*|crashkernel=*);;
        cs89x0_dma=*|cs89x0_media=*|dasd=*|db9.dev[23]=*|ddebug_query=*|debug);;
        debug_locks_verbose=*|debug_objects|no_debug_objects);;
        debug_guardpage_minorder=*|debugpat|decnet.addr=*);;
        default_hugepagesz=*|dhash_entries=*|digi=*|digiepca=*|disable=*);;
        disable_ddw|disable_ipv6=*|disable_mtrr_cleanup|disable_mtrr_trim);;
        disable_timer_pin_1|dma_debug=*|dma_debug_entries=*);;
        dma_debug_driver=*|drm_kms_helper.edid_firmware=*|dscc4.setup=*);;
        dyndbg|dyndbg=*|module.dyndbg|module.dyndbg=*|earlycon=*);;
        earlyprintk=*|ekgdboc=*|edd=*|eisa_irq_edge=*|elanfreq=*|elevator=*);;
        elfcorehdr=*|enable_mtrr_cleanup|enable_timer_pin_1|enforcing);;
        erst_disable|ether=*|evm=*|failslab=*|fail_page_alloc=*);;
        fail_make_request=*|floppy=*|force_pal_cache_flush|ftrace=*);;
        ftrace_dump_on_oops|ftrace_dump_on_oops=*|ftrace_filter=*);;
        ftrace_notrace=*|ftrace_graph_filter=*|gamecon.map[23]=*|gamma=*);;
        gart_fix_e820=*|gcov_persist=*|gpt|grcan.enable0=*|grcan.enable1=*);;
        grcan.select=*|grcan.txsize=*|grcan.rxsize=*|hashdist=*|hcl=*|hd=*);;
        hest_disable|highmem=*|highres=*|hisax=*|hlt|hpet=*|hugepages=*);;
        hugepagesz=*|hvc_iucv=*|hvc_iucv_allow=*|keep_bootcon|i2c_bus=*);;
        i8042.debug|i8042.direct|i8042.dumbkbd|i8042.noaux|i8042.nokbd);;
        i8042.noloop|i8042.nomux|i8042.nopnp|i8042.notimeout|i8042.reset);;
        i8042.unlock|i810=*|i8k.ignore_dmi|i8k.force|i8k.power_status);;
        i8k.restricted|i915.invert_brightness=*|icn=*|ide-core.nodma=*);;
        ide-pci-generic.all-generic-ide|idle=*|ignore_loglevel);;
        ihash_entries=*|ima_appraise=*|ima_appraise_tcb|ima_audit=*);;
        ima_hash=*|ima_tcb|init=*|initcall_debug|initrd=*|inport.irq=*);;
        intel_iommu=*|intel_idle.max_cstate=*|intremap=*|iomem=*|iommu=*);;
        io7=*|io_delay=*|ip=*|ip2=*|irqfixup|irqpoll|isapnp=*|isolcpus=*);;
        iucv=*|js=*|keepinitrd|kernelcore=*|kgdbdbgp=*|kgdboc=*|kgdbwait);;
        kmac=*|kmemleak=*|kstack=*|kvm.ignore_msrs=*|kvm.mmu_audit=*);;
        kvm-amd.nested=*|kvm-amd.npt=*|kvm-intel.ept=*);;
        kvm-intel.emulate_invalid_guest_state=*|kvm-intel.flexpriority=*);;
        kvm-intel.nested=*|kvm-intel.unrestricted_guest=*|kvm-intel.vpid=*);;
        l2cr=*|l3cr=*|lapic|lapic=*|lapic_timer_c2_ok|libata.dma=*);;
        libata.ignore_hpa=*|libata.noacpi|libata.force=*|memblock=*);;
        load_ramdisk=*|lockd.nlm_grace_period=*|lockd.nlm_tcpport=*);;
        lockd.nlm_timeout=*|lockd.nlm_udpport=*|logibm.irq=*|loglevel=*);;
        log_buf_len=*|logo.nologo|lp=*|lpj=*|ltpc=*|machvec=*|machtype=*);;
        max_addr=*|maxcpus=*|max_loop=*|mce|mce=*|md=*);;
        mdacon=*|mem=*|memchunk=*|memmap=*|memory_corruption_check=*);;
        memory_corruption_check_size=*|memory_corruption_check_period=*);;
        memtest=*|meye.*=*|mfgpt_irq=*|mfgptfix|mga=*|min_addr=*|mini2440=*);;
        mminit_loglevel=*|module.sig_enforce|mousedev.tap_time=*);;
        mousedev.xres=*|mousedev.yres=*|movablecore=*|MTD_Partition=*);;
        MTD_Region=*|mtdparts=*|multitce=*|onenand.bdry=*|mtdset=*);;
        mtouchusb.raw_coordinates=*|mtrr_chunk_size=*|mtrr_gran_size=*);;
        mtrr_spare_reg_nr=*|n2=*|netdev=*|nf_conntrack.acct=*|nfsaddrs=*);;
        nfsroot=*|nfsrootdebug|nfs.callback_tcpport=*|nfs.cache_getent=*);;
        nfs.cache_getent_timeout=*|nfs.idmap_cache_timeout=*);;
        nfs.enable_ino64=*|nfs.max_session_slots=*);;
        nfs.nfs4_disable_idmapping=*|nfs.nfs4_unique_id=*);;
        nfs.send_implementation_id|nfsd.nfs4_disable_idmapping=*);;
        objlayoutdriver.osd_login_prog=*|nmi_debug=*|nmi_watchdog=*);;
        netpoll.carrier_timeout=*|no387|no_console_suspend|noaliencache);;
        noalign|noapic|noautogroup|nobats|nocache|noclflush|nodelayacct);;
        nodisconnect|nodsp|noefi|noexec|nosmap|nosmep|noexec32|nofpu|nofxsr);;
        noxsave|eagerfpu=*|nohlt|no-hlt|no_file_caps|nohalt|nohz=*|noiotrap);;
        noirqdebug|no_timer_check|noisapnp|noinitrd|nointremap|nointroute);;
        nojitter|no-kvmclock|no-kvmapf|no-steal-acc|nolapic|nolapic_timer);;
        noltlbs|nomca|nomce|nomfgpt|nonmi_ipi|nomodule|nopat|norandmaps);;
        noreplace-paravirt|noreplace-smp|noresidual|nordrand|noresume);;
        no-scroll|nosbagart|nosep|nosmp|nosoftlockup|nosync|notsc|nousb);;
        nowatchdog|nowb|nox2apic|cpu0_hotplug|nptcg=*|nr_cpus=*|nr_uarts=*);;
        numa_balancing=*|numa_zonelist_order=*|ohci1394_dma=*);;
        olpc_ec_timeout=*|omap_mux=*|oprofile.timer=*|oprofile.cpu_type=*);;
        oops=*|OSS|panic=*|parkbd.port=*|parkbd.mode=*|parport=*);;
        parport_init_mode=*|pause_on_oops=*|pcbit=*|pcd.|pci=*|pcie_aspm=*);;
        pcie_hp=*|pcie_ports=*|pcie_pme=*|pcmv=*|pd.|pdcchassis=*);;
        percpu_alloc=*|pf.|pg.|pirq=*|plip=*|pmtmr=*|pnp.debug=*|pnpacpi=*);;
        pnpbios=*|pnp_reserve_irq=*|pnp_reserve_dma=*|pnp_reserve_io=*);;
        pnp_reserve_mem=*|ports=*|print-fatal-signals=*);;
        printk.always_kmsg_dump=*|printk.time=*|processor.max_cstate=*);;
        processor.nocst|profile=*|prompt_ramdisk=*|psmouse.proto=*);;
        psmouse.rate=*|psmouse.resetafter=*|psmouse.resolution=*);;
        psmouse.smartscroll=*|pstore.backend=*|pt.|pty.legacy_count=*|quiet);;
        r128=*|raid=*|ramdisk_blocksize=*|ramdisk_size=*|rcu_nocbs=*);;
        rcu_nocb_poll|rcutree.blimit=*|rcutree.fanout_leaf=*);;
        rcutree.qhimark=*|rcutree.qlowmark=*|rcutree.rcu_cpu_stall_suppress=*);;
        rcutree.rcu_cpu_stall_timeout=*|rcutree.jiffies_till_first_fqs=*);;
        rcutree.jiffies_till_next_fqs=*|rcutorture.fqs_duration=*);;
        rcutorture.fqs_holdoff=*|rcutorture.fqs_stutter=*);;
        rcutorture.irqreader=*|rcutorture.n_barrier_cbs=*);;
        rcutorture.nfakewriters=*|rcutorture.nreaders=*);;
        rcutorture.onoff_holdoff=*|rcutorture.onoff_interval=*);;
        rcutorture.shuffle_interval=*|rcutorture.shutdown_secs=*);;
        rcutorture.stall_cpu=*|rcutorture.stall_cpu_holdoff=*);;
        rcutorture.stat_interval=*|rcutorture.stutter=*);;
        rcutorture.test_boost=*|rcutorture.test_boost_duration=*);;
        rcutorture.test_boost_interval=*|rcutorture.test_no_idle_hz=*);;
        rcutorture.torture_type=*|rcutorture.verbose=*|rdinit=*|reboot=*);;
        relax_domain_level=*|reserve=*|reservetop=*|reservelow=*);;
        reset_devices|resume=*|resume_offset=*|resumedelay=*|resumewait);;
        hibernate=*|retain_initrd|rhash_entries=*|riscom8=*|ro|root=*);;
        rootdelay=*|rootflags=*|rootfstype=*|rootwait|rw|S|sa1100ir|sbni=*);;
        sched_debug|skew_tick=*|security=*|selinux=*|apparmor=*|serialnumber);;
        shapers=*|show_msr=*|simeth=*|simscsi=*|slram=*|slab_max_order=*);;
        slub_debug|slub_debug=*|slub_max_order=*|slub_min_objects=*);;
        slub_min_order=*|slub_nomerge|smart2=*|smsc-ircc2.nopnp);;
        smsc-ircc2.ircc_cfg=*|smsc-ircc2.ircc_sir=*|smsc-ircc2.ircc_fir=*);;
        smsc-ircc2.ircc_irq=*|smsc-ircc2.ircc_dma=*);;
        smsc-ircc2.ircc_transceiver=*|softlockup_panic=*|sonypi.*=*);;
        specialix=*|spia_io_base=*|spia_fio_base=*|spia_pedr=*|spia_peddr=*);;
        stacktrace|stacktrace_filter=*|sti=*|sti_font=*|stifb=*);;
        sunrpc.min_resvport=*|sunrpc.max_resvport=*|sunrpc.pool_mode=*);;
        sunrpc.tcp_slot_table_entries=*|sunrpc.udp_slot_table_entries=*);;
        swapaccount|swapaccount=*|swiotlb=*|switches=*|sysfs.deprecated=*);;
        sysrq_always_enabled|tdfx=*|test_suspend=*|thash_entries=*);;
        thermal.act=*|thermal.crt=*|thermal.nocrt=*|thermal.off=*);;
        thermal.psv=*|thermal.tzp=*|threadirqs|topology=*|tp720=*);;
        tpm_suspend_pcr=*|trace_buf_size=*|trace_event=*|trace_options=*);;
        transparent_hugepage=*|tsc=*|turbografx.map[23]=*|udbg-immortal);;
        uhash_entries=*|uhci-hcd.ignore_oc=*|unknown_nmi_panic);;
        usbcore.authorized_default=*|usbcore.autosuspend=*);;
        usbcore.usbfs_snoop=*|usbcore.blinkenlights=*);;
        usbcore.old_scheme_first=*|usbcore.usbfs_memory_mb=*);;
        usbcore.use_both_schemes=*|usbcore.initial_descriptor_timeout=*);;
        usbhid.mousepoll=*|usb-storage.delay_use=*|usb-storage.quirks=*);;
        user_debug=*|userpte=*|vdso=*|vdso32=*|vector=*|video=*);;
        virtio_mmio.device=*|vga=*|vmalloc=*|vmhalt=*|vmpanic=*|vmpoff=*);;
        vsyscall=*|vt.cur_default=*|vt.default_blu=*|vt.default_grn=*);;
        vt.default_red=*|vt.default_utf8=*|vt.global_cursor_default=*);;
        watchdog|x2apic_phys|x86_mrst_timer=*|xd=*|xd_geo=*|xen_emul_unplug=*);;
        xirc2ps_cs=*);;

        drm.*|i915.*) ;;
        nomodeset|*.modeset=*);;
        nosplash|splash=*|fbcon=*);;

        #------------------------------------------------------------------------
        # NOTE: see /live/custom/$DISTRO_NAME/0.sh for distro specific boot codes
        #------------------------------------------------------------------------

        *) UNKNOWN_BOOTCODES="$UNKNOWN_BOOTCODES $param"
        esac
    done

       BOOT_RETRY=${CMD_RETRY:-$BOOT_RETRY}
     FRUGAL_RETRY=${CMD_RETRY:-$FRUGAL_RETRY}
    PERSIST_RETRY=${CMD_RETRY:-$PERSIST_RETRY}

       BOOT_RETRY=${CMD_BOOT_RETRY:-$BOOT_RETRY}
     FRUGAL_RETRY=${CMD_FRUGAL_RETRY:-$FRUGAL_RETRY}
    PERSIST_RETRY=${CMD_PERSIST_RETRY:-$PERSIST_RETRY}

    if [ "$DISABLE_STORE" ]; then
        USER_UID=0
        USER_GID=0
    fi
}


small_int() {
    local var=$1 val=${2#*=} name=${2%%=*} param=$2

    case $val in
                      "")                   return ;;
        [0-9]|[0-9][0-9]) eval $var=\$val;  return ;;

    esac
    INTEGER_ERRORS="$INTEGER_ERRORS $param"
}

any_int() {
    local var=$1 val=${2#*=} name=${2%%=*} param=$2

    if echo $val | egrep -q "^[0-9]+$"; then
        eval $var=\$val
        return
    fi
    INTEGER_ERRORS="$INTEGER_ERRORS $param"
}

coldplug_delay() {
    [ "$COLDPLUG_DELAY" ] || return
    msg  "$_Waiting_X_seconds_for_buses_to_settle_"  $(nq $COLDPLUG_DELAY)
    sleep $COLDPLUG_DELAY
}

start_coldplug_loop() {
    coldplug-loop $PLUG_LOOP_DELAY &>/dev/null 17>/dev/null &
    COLDPLUG_LOOP_PID=$!
    vmsg 6 'Coldplug loop pid: %s' $(nq $COLDPLUG_LOOP_PID)
    echo $COLDPLUG_LOOP_PID > /COLDPLUG_LOOP_PID
}

start_hotplug() {
    local trace_load=$1  targ=/proc/sys/kernel/hotplug

    # Set up file for recording sequence of kernel events sent to mdev
    echo > /dev/mdev.seq

    if [ ! -e $targ -o -n "$FORCE_COLDPLUG_LOOP" ]; then
        start_coldplug_loop
        return
    fi

    if [ "$trace_load" ]; then
        echo $LIVE_BIN/mdev-trace   | tee $targ &>/dev/null
    else
        echo $LIVE_BIN/mdev-hotplug | tee $targ &>/dev/null
    fi
}

stop_hotplug() {
    local targ=/proc/sys/kernel/hotplug
    test -e $targ && echo | tee $targ &>/dev/null

    test -r /COLDPLUG_LOOP_PID || return
    read COLDPLUG_LOOP_PID < /COLDPLUG_LOOP_PID
    vmsg 6 'kill coldplug loop pid: %s' $(nq $COLDPLUG_LOOP_PID)
    rm /COLDPLUG_LOOP_PID
    kill -9 $COLDPLUG_LOOP_PID
}

old_stop_hotplug() {
    local targ=/proc/sys/kernel/hotplug
    test -e $targ && echo | tee $targ &>/dev/null
}

list_modules() {
    local mod list cnt=0
    list=$( echo $(lsmod | grep "^[a-z]" | cut -d" " -f1) | sort)
    cnt=$(echo $list | wc -w)

    # loaded <count> modules(s)
    vmsg 6 'Loaded %s module(s)' $(nq $cnt)

    [ "$list" ] && vmsg 7 "$white$list"

    [ "$ERR_MODULES" ] || return
    local ecnt=$(echo "$ERR_MODULES" | wc -w)

    vmsg 6 '%s module(s) failed to load' $(nq $ecnt)

    vmsg 7 "$white$ERR_MODULES"

}

select_breakpoints() {
    case $BREAK_POINTS in
        *\?*|*ask*)
            echo
            echo "${m_co}Remaining ${hi_co}$ME$m_co breakpoints:$nc_co"
            echo
            sed -rn 's/^\s*breakpoint ([1-9a-z])\> */  \1) /p' $0 | sed "s/['\"]//g" | sort
            echo
            echo "${m_co}Use \"a\" to set most breakpoints$nc_co"
            echo "${m_co}Use \"A\" to set all breakpoints$nc_co"
            echo "${m_co}Use \"bash\" to enter a Bash shell before system starts$nc_co"
            echo
            printf "${ok_co}%s$m_co: $nc_co"  "$_Enter_breakpoint_s_separated_by_commas_"
            setsid cttyhack > /dev/null
            read BREAK_POINTS;;
    esac
}

mem_info() {
    local info amt kb
    while read info amt kb; do
        [ ! "$info" = "$1:" ] && continue
        echo $((amt / 1024))
        return
    done </proc/meminfo
}

clean_xlat() {
    find $LOCALE_DIR/xlat -name init.xlat -delete
    #rm -rf $LOCALE_DIR/fonts
}

read_xlat() {
    local prog=$1  lang=${2%%_*}
    local xdir=$LOCALE_DIR/xlat
    local fdir=$LOCALE_DIR/fonts

    local xlat=$xdir/en/$prog.xlat
    [ -r $xlat ] && . $xlat

    # Always load a font
    local font=$fdir/${lang:-default}
    [ -e "$font" ] && log_cmd setfont $font -C $(tty)

    local alt=$DISTRO_ASCII_PRETTY_NAME
    [ -n "$lang" -a -e $fdir/$lang -a -n "$alt" ] && DISTRO_PRETTY_NAME=$alt

    [ "$lang" ] || return

    xlat=$xdir/$lang/$prog.xlat
    [ -r "$xlat" ] || return
    . $xlat
    vmsg 7 'Translate to %s' $lang
}

do_welcome() {

    [ "$NO_CLEAR" ] || clear   # Clear and reset screen

    local pretty_name=$1 version=$2  v_date=$3  arch
    case $(uname -m) in
          i686) arch=" 32-bit" ;;
        x86_64) arch=" 64-bit" ;;
    esac

    case $(uname -r) in
        *[pP][aA][eE]) arch="$arch pae" ;;
    esac

    vmsg 2  "$_Welcome_to_X_" "$(pq $pretty_name$arch)!"

    (   imsg  "$_Welcome_to_X_Powered_by_Y_" "$(gq $pretty_name$arch)" "$(gq Debian)."
        if [ -n "$CLI_INSTALLER" ]; then
            echo
            imsg  "$_For_command_line_install_login_as_X_password_Y_" "$(gq root)" "$(gq root)"
            imsg  "$_Enter_the_command_X_Follow_the_instructions_" "$(gq $CLI_INSTALLER)"
        fi
    ) > /issue

    if lspci -n | grep -iq " 80ee:beef"; then
        vmsg 7 'VirtualBox detected'
        touch /live/config/virtualbox
    fi

    local bat_stat=$(battery-status | sed "s/:/:$hi_co/" )
    [ -n "$DB_PLUS" -a -n "$bat_stat" ] && msg %s "$bat_stat"

    # Get total ramsize, and available real ram in MB. We need this later.

    local total_mem=$(mem_info MemTotal)
    local used_mem=$((total_mem - FREE_MEM))

    vmsg 8 "$white  $(busybox | head -n 1)"

    [ -x /bin/ntfs-3g ] && vmsg 8 "$white  $(ntfs-3g --version 2>&1)"

    vmsg 6 "%25s: $white%s"  'initrd version' "$version"
    vmsg 6 "%25s: $white%s"  'initrd built'   "$v_date"

    # Print meminfo.
    local mem_format="${m_co}%s: $num_co%5d${m_co} M"
    vmsg 8 "$mem_format"  '             Total Memory' $total_mem
    # vmsg 6 "$mem_format"  '              Free Memory'  $FREE_MEM
    vmsg 8 "$mem_format"  '              Used Memory'  $used_mem

    local cpu_format="%s:$white %s"
    vmsg 5 "$cpu_format"  '             Linux kernel' "$(uname -r)"
    vmsg 8 "$cpu_format"  '             Screen width' "$SCREEN_WIDTH"
    vmsg 8 "$cpu_format"  '              Kernel arch' "$(uname -m)"

    local product_name board_vendor id_dir=/sys/class/dmi/id
    read product_name 2>/dev/null  < $id_dir/product_name
    read board_vendor 2>/dev/null  < $id_dir/board_vendor
    vmsg 8 "$cpu_format"  '                 Hardware' "$board_vendor $product_name"
    # vmsg 6  "$cpu_format" '                      CPU' "$(cpu_param 'model name' unknown)"
    # vmsg 6  "$cpu_format" '                    Cores' "$num_co$(cpu_param 'cpu cores'  1)"
    # vmsg 6  "$cpu_format" '                    Cache' "$(cpu_param 'cache size' unknown)"
}

show_bootcodes() {
    local unknown=$1

    vmsg 5 "%s:"  "$_Current_boot_codes_"
    vmsg 5 "    $white $(cat /proc/cmdline)"

    [ -n "$unknown"  -a -n "$CHECK_BOOTCODES" ] || return

    vmsg 4
    vmsg 4 "$warn_co%s:"  "$_Possibly_unknown_or_misspelled_boot_codes_"
    vmsg 4 "(%s)"  "$_dont_take_this_too_seriously_"
    local code
    for code in $unknown; do
        vmsg 4 "$hi_co    $code"
    done
    vmsg 4
}

show_integer_errors() {
    local param name value params=$1

    # Only small integer values are allowed for <name>.  Will ignore <name=value>
    for param in $params; do
        name=${param%%=*}
        value=${param#*=}
        warn 'Only small integer values are allowed for %s.  Will ignore %s' \
            $(pqw $name) $(pqw $param)
    done
}

show_disable_errors() {
    local codes=$1  ds_param=$2
    [ -n "$codes" ] || return
    local bad=$(echo "$codes" | sed -r "s/$DISABLE_SERVICES_OPTS//g")
    [ -n "$bad" ] || return
    #. Unknown options in <disable=abcRxyz>.  Unknown options(s): <R>
    warn  "$_Unknown_option_s_in_X_Unknown_option_s_Y_" "$(pqw $ds_param)" "$(pqw $bad)"
}

cpu_param() {
    local result=$(grep "^$1" /proc/cpuinfo | head -n 1 | sed 's/.*\t: //')
    [ "$result" ] || result=$2
    echo "$result"
}

disable_hcd() {
    local type=$1
    local dir=/sys/bus/pci/drivers/${type}_hcd
    local symlink kmsg=/dev/kmsg
    test -d $dir || return

    for symlink in $(find $dir -name "0*" | sed 's=.*/=='); do
        echo $symlink >> $DISABLED_USB_FILE.$type
        msg 'disable %s bus: %s' $(nq $type) $(pq $symlink)
        [ -e $kmsg ] && echo "initrd disabled $type: $symlink" >> $kmsg
        echo $symlink > $dir/unbind
    done
}

set_fbcondecor() {
    local boot_param=$1 orig_name=$2 param verbose theme=default
    #vmsg 5 "set_fbcondecor('$1' '$2')"
    [ "$orig_name"  ] || return
    [ "$boot_param" ] || return
    #vmsg 5 "Got boot param: $param"

    local prog=/bin/fbcondecor_ctl.static
    test -x $prog || return
    #vmsg 5 "found $prog"
    local new_name
    read new_name 2>/dev/null </sys/class/graphics/fb0/name
    [ "$orig_name" = "$new_name" ] && return

    vmsg 6 "Found new fb driver:$white $new_name"

    for param in ${boot_param//,/ }; do
        case $param in
            v|verbose) verbose=true ;;
          t=*|theme=*) theme=${param#*=} ;;
        esac
    done

    [ "$verbose" ] || return
    vmsg 6 "set fbcondecor to: $theme"

    $prog -t $theme -c setcfg 2>/dev/null
    $prog -t $theme -c setpic 2>/dev/null
    $prog -c on 2>/dev/null
}

blacklist_modules() {
    local black_list=$1
    [ "$black_list" ] || return

    black_list=$(echo $black_list | sed -r "s/(VIDEO|KMS)/$KMS_VIDEO_MODULES/");
    mkdir -p /etc/modprobe.d/
    echo $black_list | sed 's/,/\n/g' | sed -r 's/^([a-z])/blacklist \1/' >> $BLACK_LIST_FILE
}

load_kernel_modules() {
    local list=$1 file=$2

    # if [ -r $file ]; then
    #     vmsg 7 'Load modules from file %s' "$(fq $file)"
    #     grep -v "^\s*#" $file | sed 's/\s*#.*//' | xargs modprobe -a -q -b
    # fi

    coldplug_modules 5

    for module in ${list//,/ }; do
        case $module in
            all) load_all_modules ;;
              *) modprobe -q $module || warn   "Could not load module %s" $(pqw $module);;
        esac
    done
}

load_all_modules() {
    local subdir=$1
    msg  "$_Loading_all_modules_"
    debug_cmd lsmod
    debug_cmd lspci
    LOAD_ALL_MODULES=true
    find $MOD_DIR/$subdir -name "*.ko" | sed -e 's=.*/==' -e 's/\.ko$//' \
        | xargs modprobe -q -a -b 2>/dev/null
}

debug_cmd() {
    echo "==== $*"                        >> $MY_LOG
    "$@"                                  >> $MY_LOG
    echo "==============================" >> $MY_LOG
}

coldplug_modules() {
    local verb=$1

    [ "$verb" ] && vmsg $verb  "$_Loading_hardware_specific_modules_"

    #find /sys/devices -name uevent -exec sed -n 's/^MODALIAS=//p' '{}' + 2>/dev/null \
    #    | sort -u | xargs modprobe -a -q -b
    #return

    #find /sys/devices -name modalias -exec cat '{}' + 2>/dev/null | sort -u \
    #    | xargs modprobe -a -q -b 2>/dev/null

    find /sys/devices -name modalias -print0 | xargs -0 sort -u \
        | xargs modprobe -a -q -b 2>/dev/null
    return

    # local alias_file alias
    # find /sys/devices -name modalias | while read alias_file; do
    #     alias=$(cat $alias_file 2>/dev/null)
    #     modprobe -q -b $alias 2>/dev/null
    # done
}

#==============================================================================
# Find the squashfs file
#==============================================================================

#------------------------------------------------------------------------------
# Function: is_usb_or_removable <device>
#------------------------------------------------------------------------------

is_usb_or_removable() {
    local drive=$(get_drive $1)
    local dir=/sys/block/$drive flag
    read flag 2>/dev/null < $dir/removable
    [ "$flag" = 1 ] && return 0
    local devpath=$(readlink -f $dir/device)
    [ "$devpath" ] || return 1
    echo $devpath | grep -q /usb
    return $?
}

is_removable() {
    local drive=$(get_drive $1)
    local dir=/sys/block/$drive flag
    read flag 2>/dev/null < $dir/removable
    [ "$flag" = 1 ]
    return $?
}


#------------------------------------------------------------------------------
# Function: get_drive <partition>
#------------------------------------------------------------------------------
get_drive() {
    local drive part=${1##*/}
    case $part in
        mmcblk*) echo ${part%p[0-9]}                        ;;
              *) drive=${part%[0-9]}  ; echo ${drive%[0-9]} ;;
    esac
}

#------------------------------------------------------------------------------
# Function: from_filter <device-list>
#
# Filter and order devices in list according to types listed in $FROM_TYPE.
# Output goes into FILTERED_LIST so we can give error messages from within.
# Return true if there is at least one item in the FILTERED_LIST otherwise
# return false.  Error out if there is an invalid from= type.
#
# See LINUX ALLOCATED DEVICES for device numbers
# https://www.kernel.org/doc/Documentation/devices.txt
#
#   3 block First MFM, RLL and IDE hard disk/CD-ROM interface   hda, hdb
#   8 block SCSI disk devices (0-15)                            sda, sdb, ... sdp
#  22 block Second IDE hard disk/CD-ROM interface               hdc, hdd
# 179 block MMC block devices                                   mmcblk0, mmcblk1, ... mmcblk7
#------------------------------------------------------------------------------
from_filter() {

    FILTERED_LIST=

    local from_type invalid_scan
    case ,$FROM_TYPE, in
        *,all,*) from_type=$FROM_TYPE_ALL;;
              *) from_type=$FROM_TYPE;;
    esac

    # First segregate devices by type
    local dev cd_devs hd_devs usb_devs mmc_devs
    for dev; do
        [ -b "$dev" ] || continue
        case $(stat -c %t $dev) in

            b) cd_devs="$cd_devs $dev" ;;
           b3) mmc_devs="$mmc_devs $dev"
               if is_removable $dev; then
                    usb_devs="$usb_devs $dev"
                else
                    hd_devs="$hd_devs $dev"
                fi ;;

   3|8|22|103) if is_usb_or_removable $dev; then
                    usb_devs="$usb_devs $dev"
                else
                    hd_devs="$hd_devs $dev"
                fi ;;
        esac
    done

    local type
    for type in ${from_type//,/ }; do
        case $type in

            cd) FILTERED_LIST="$FILTERED_LIST$cd_devs"
                cd_devs= ;;

           usb) FILTERED_LIST="$FILTERED_LIST$usb_devs"
                usb_devs= ;;

            hd) FILTERED_LIST="$FILTERED_LIST$hd_devs"
                hd_devs= ;;

           mmc) FILTERED_LIST="$FILTERED_LIST$mmc_devs"
                mmc_devs= ;;

           "") ;;

            *) invalid_scan="$invalid_scan $type";;
        esac
    done

    # Invalid <parameter-name> value(s) <bad values>
    [ "$invalid_scan" ] \
        && _fatal "$(printf 'Invalid %s values(s) %s' "$(pqh from=)" "$(pqh $invalid_scan)")" \
        "$(printf 'Valid values are %s' "$(cq cd hd mmc usb all)")"

    [ "$FILTERED_LIST" ]

    return $?
}

find_boot_file() {
    local ret
    find_files boot "$@"
    ret=$?
    local file=$1

    case $ret in
        10) fatal  "$_No_X_devices_found_" block             ;;
        20) fatal  "$_No_X_devices_found_" $(pqh $FROM_TYPE) ;;
        30) _fatal "" \
            "$(printf  "$_The_X_parameter_is_blocking_device_Y_" \
            $(pqh from=$FROM_TYPE) "$(pqh $DEVICE_LIST)")"     \
            "$(printf  "$_Even_though_device_X_has_Y_" "$(pqh $DEVICE_LIST)" "$(pqh $device_id)")" \
            "$(printf  "$_Remove_the_X_boot_parameter_to_allow_that_device_to_be_scanned_" \
            $(cq $FROM_TYPE))" ;;
        40) fatal  "$_Device_found_but_could_not_find_X_file_on_device_"  $(pqh $file) ;;
    esac

    return $ret
}

#------------------------------------------------------------------------------
# Function: find_files <files> <mntpnt> <dev> <label> <uuid> <retry>
#
# The outer wrapper lets us show the loop times regardless of how we left the
# main code in _find_files().
#------------------------------------------------------------------------------

find_files() {
    local ret loop_times start_t=$(get_time) t2 elapsed  find_type=$1 files=$2 device_id=$4
    _find_files "$@"
    ret=$?
    t2=$(get_time)
    elapsed=$(get_seconds $((t2 - start_t)))
    # Spent <15> seconds looking for <boot> file(s) <linuxfs>
    vmsg 6  "Spent %s seconds looking for %s file(s) %s" $(nq $elapsed) $(fq $find_type) "$(fq $files)"
    [ "$loop_times" ] && vmsg 6 "loop times:$num_co$loop_times"

    [ "$find_type" != boot ] && return $ret

    return $ret
}

_find_files() {
    local find_type=$1 files=$2  mp_orig=$3  device_id=$4  retry_time=$5

    local short_files f
    if [ ${#files} -gt 20 ]; then
        for f in $files; do
            short_files="$short_files$(basename $f) "
        done
    else
        short_files=$files
    fi

    local last_list  # disk_device partition partition_error

    # Find disk device if we are looking for a named partition.
    # This lets us bail early if the disk is found but the partition is not.

    # The disk device can appear before the partition appears so this is not reliable
    # if [ -n "$device_id" -a -z "${device_id##name=*}" ]; then
    #     partition=${device_id##name=}
    #     partition=${partition##*/}
    #     disk_device=/dev/$(get_drive $partition)
    #     partition=/dev/$partition
    # fi

    unset FOUND_DEV FOUND_MP

    local be_verbose vthresh=8
    [ "$VERBOSE" -ge $vthresh ] && be_verbose=true

    mkdir -p "$mp_orig"

    local end_t  loop_t1  usleep  dt
    local start_t=$(get_time)  current_t=0
    local try=-1  have_devices  final_try  dot=.  dt_secs

    # Stay in loop until success or time goal_t is reached
    local goal_t=$((start_t + retry_time * 100))

    local do_delay said_retry mounted_device

    while [ -z "$final_try" ]; do

        try=$((try + 1))
         [ "$be_verbose" ] && cnt_up=$(nq "$(printf "%2d" $try)")

        #-- delay every time except inside first second
        if [ "$do_delay" ]; then
            if [ -z "$said_retry" ]; then
                #. Retry for <N> seconds <...>
                vmsgN 4  "$_Retry_for_X_seconds_Y_" $(nq $retry_time) ""
                vmsg_if $vthresh " ..."
                said_retry=true
            fi

            [ "$DO_COLDPLUG" ] && coldplug_modules

            # Normally just print out a dot for each iteration
            [ "$be_verbose" ] || vmsgN 3 $dot

            # Adjust sleep time dynamically to account for previous time
            # through the loop

            current_t=$(get_time)
            dt=$((current_t - loop_t1))
            usleep=$(( ($RETRY_DELAY - dt ) * 10000))
            dt_secs=$(get_seconds $dt)
            loop_times="$loop_times $dt_secs"
            vmsg_if 9 "loop time:$num_co $dt_secs"

            # Once we have exceeded the time limit we go through the
            # loop one final time.
            [ $current_t -gt $goal_t ] && final_try=true

            # Only sleep long enough so total time through loop is constant
            [ $usleep -gt 0 ] && usleep $usleep
        else
            [ $(($(get_time) - start_t)) -gt 99 ] && do_delay=true
        fi

        loop_t1=$(get_time)

        # This disables usb-2
        [ "$NO_EHCI" ] && disable_hcd ehci

        unset DEVICE_LIST FILTERED_LIST

        #----------------------------------------------------------------------
        # Either scan all available block devices because no uuid, label, or
        # device was specified ...
        #----------------------------------------------------------------------
        if [ -z "$device_id" ]; then

            #. Scan <type> devices.  Look for <type> file(s) <file-names>
            [ "$try" = "0" ] \
                && vmsg 4  "$_Scan_X_devices_Look_for_Y_file_s_Z_"  $(dq $FROM_TYPE) $find_type "$(fq $short_files)"

            DEVICE_LIST=$(most_block_devices)

            if [ -z "$DEVICE_LIST" ]; then
                [ "$final_try" ] && return 10

                vmsg_if $vthresh "%s No %s devices found" "$cnt_up" block
                continue
            fi

            # Filter and order devices according to from= parameter
            if ! from_filter $DEVICE_LIST; then
                [ "$final_try" ] && return 20

                vmsg_if $vthresh "%s No %s devices found" "$cnt_up" $(pq $FROM_TYPE)
                continue
            fi

            DEVICE_LIST=$FILTERED_LIST

            # pure window dressing
            if [ -z "$have_devices" ]; then
                [ $try -gt 0 ] && vmsg 4
                vmsg 5  "$_Filtered_devices_X_" "$white$DEVICE_LIST"
            else
                vmsg_if $vthresh "%s Filtered devices %s" "$cnt_up" "$(pq $DEVICE_LIST)"
            fi

        #----------------------------------------------------------------------
        # ... Or look for specific devices because a uuid, label, or device was
        # specified
        #----------------------------------------------------------------------
        else
            if [ "$mounted_device" ]; then
                FOUND_ID_DEVICE=$mounted_device
                return 40
            fi

            # Give up early if the root device is found but the partition is not
            # But wait a second in case there is a delay for the partitions to show
            # if [ "$partition_error" ]; then
            #     warn "Found root device %s but not partition %s" "$(pqw $disk_device)" "$(pqw $partition)"
            #     sleep 1
            #     final_try=true
            # fi

            # [ "$disk_device" -a -e "$disk_device" -a ! -e $partition ] && partition_error=true

            find_device "$find_type" "$try" "$final_try" "$device_id" || return $?
            if [ -z "$DEVICE_LIST" ]; then
                vmsg_if $vthresh "%s No %s devices yet found" "$cnt_up" $(pq $FROM_TYPE)
                continue
            fi

            #------------------------------------------------------------------
            # When a device has been specified via label, uuid, or /dev/node
            # then only filter and order if from= parameter is given explicitly
            #------------------------------------------------------------------
            if [ "$ALWAYS_SCAN" ]; then
                if ! from_filter $DEVICE_LIST; then
                    [ "$final_try" ] && return 30
                    continue
                fi

                DEVICE_LIST=$FILTERED_LIST
            fi

            # pure window dressing
            if [ -z "$have_devices" ]; then
                [ "$try" != "0" ] && msg
                #. Look for files(s) <file-list> on device(s) <device-list>
                vmsg 4  "$_Look_for_file_s_X_on_device_s_Y_" "$(pq $short_files)" "$(dq $DEVICE_LIST)"
            else
                #vmsg_if $vthresh
                vmsg_if $vthresh "%s Look for %s on %s" "$cnt_up" "$(pq $short_files)" "$(dq $DEVICE_LIST)"
            fi
        fi

        #----------------------------------------------------------------------
        # Now we have some device or devices to mount and search
        #----------------------------------------------------------------------

        # more window dressing
        if [ -n "$have_devices" -a "$last_list" != "$DEVICE_LIST" ]; then
            msg
            msg  "$_Found_new_device_s_X_" "$(hq $DEVICE_LIST)"
        fi
        last_list=$DEVICE_LIST

        have_devices=true

        #-- now try to mount each device and find at least one of the $files ...
        local dev fname mntpnt mounted already
        for dev in $DEVICE_LIST; do

            # use existing mountpoint if device is already mounted
            mntpnt="$(get_mountpoint $dev)"
            if [ "$mntpnt" ]; then
                mounted=
                mounted_device=$dev
                [ "$already" ] || vmsg 5  "$_Device_X_is_already_mounted_at_Y_"  "$(dq $dev)" "$(mpq $mntpnt)"
                already=true
            else
                try_mount $dev $mp_orig || continue
                mounted_device=$dev
                mounted=true
                mntpnt=$mp_orig

            fi
            if [ -n "$device_id" -a "$find_type" = persist ]; then
                if [ "$DID_FRUGAL" ]; then
                    create_persist_files "$mntpnt" "$PERSIST_PATH" "$WANT_ROOTFS" "$WANT_HOMEFS"
                else
                    create_persist_files "$mntpnt" "$PERSIST_PATH" "$NEED_ROOTFS" "$NEED_HOMEFS"
                fi
            fi

            for fname in $files; do
                [ -f "$mntpnt/$fname" ] || continue
                #[ "$said_retry" ] && echo
                if [ "$mntpnt" = "$mp_orig" ]; then
                    #. Mounted <type> device at <mount point>
                    vmsg 4  "$_Mounted_X_device_Mounted_device_Y_at_Z_" \
                        $find_type "$(fq $dev)" "${to_co}$mntpnt${m_co}"

                    local fstype=$(mntpnt_fstype $mntpnt)
                    #. <type> device filesystem: <filesystem-name>
                    vmsg 6  "$_X_device_filesystem_Y_" $find_type \
                        "$(cq $fstype)"

                    case $find_type in
                        boot) BOOT_FSTYPE=$fstype ;;
                    esac

                    local drive=$(get_drive $dev)
                    local model
                    read model 2>/dev/null </sys/block/$drive/device/model

                    # <type> device model: <model-name>
                    [ "$model" ] && vmsg 6 '%s device model: %s' $find_type "$(cq $model)"

                else
                    rmdir $mp_orig
                fi
                FOUND_DEV=$dev
                FOUND_MP=$mntpnt
                return 0
            done
            [ "$mounted" ] && mountpoint -q $mp_orig && umount $mp_orig
        done
    done
    [ "$try" != "0" ] && msg
    return 90
}

#------------------------------------------------------------------------------
# Function find_device: <type> <try> <final_try> <device_id>
#
# Find the device(s) associated with a device name, a label, or a uuid.  Puts
# the result in $DEVICE_LIST so are free to echo error messages.  Returns true
# on success or if we need to retry.  Returns false if there was a final error.
#------------------------------------------------------------------------------

find_device() {
    local err_ret find_type=$1  try=$2  final_try=$3  type=${4%%=*}  value=${4#*=}

    DEVICE_LIST=

    #. Look for <type> devices with <attribute X>
    [ "$try" = 0 ] && msg  "$_Look_for_X_device_with_Y_" "$(fq $find_type)" "$type $(cq $value)"

    case $type in
        name) DEVICE_LIST=$(cleanse_dev  $value)  ; err_ret=1 ;;
       label) DEVICE_LIST=$(label_to_dev $value)  ; err_ret=2 ;;
        uuid) DEVICE_LIST=$(uuid_to_dev  $value)  ; err_ret=3 ;;
           *) fatal 'Internal error: find_device() type=%s value=%s' "$type" "$value" ;;
    esac

    [ "$DEVICE_LIST" ] && return 0
    [ "$final_try" ]   || return 0

    [ "$try" != 0 ] && msg

    if [ "$find_type" != boot ]; then
        return 100
    fi

    [ "$AUTO_PERSIST"       ] && return 100
    [ "$find_type" = frugal ] && return 100

    general_device_error "$find_type" "$type" "$value"
    return $err_ret
}

device_uuid() {
    local device=$1
    blkid $device | sed -n -r 's/.* UUID="([^"]*)"( |$).*/\1/p'
}

device_label() {
    local device=$1
    blkid $device | sed -n -r -e 's/" (UUID=|TYPE=)".*//' -e 's/[^:]*: LABEL="//p'
}

device_fstype() {
    local device=$1
    blkid $device | sed -n -r 's/.* TYPE="([^"]*)"$/\1/p' | tail -n 1
}

mntpnt_fstype() {
    local dev=$(df $1 | tail -n1 | cut -d" " -f1)
    device_fstype $dev
}

frugal_error() {

    # Disable persistence if frugal fails or is aborted
    PERSIST=

    local mp
    for mp in $FRUGAL_MP $SQFS_MP; do
        mountpoint -q $mp && umount $mp
        [ -d $mp ] && rmdir $mp
    done

    non_fatal  "Frugal install failed (or was teminated)"

}

find_frugal_file() {
    local frugal_id=$1  bdir=$DISTRO_NAME-Frugal-$(uname -r)
    local sqfile=$bdir/$SQFILE_NAME need_to_ask
    local device name=${frugal_id%%=*}  value=${frugal_id#*=}

    find_files frugal "$sqfile" "$BOOT_MP" "$frugal_id" "$FRUGAL_RETRY"
    local ret=$?

    if [ $ret -eq 0  -a -z "$FORCE_FRUGAL" ]; then
        #. Found existing <frugal> install
        msg  "$_Found_existing_X_install_" frugal
        SQFILE_MP=$BOOT_MP
        SQFILE_DEV=$FOUND_DEV

        SQFILE_FULL=$BOOT_MP/$sqfile
        SQFILE_PATH=${sqfile%/*}
        DEFAULT_PERSIST_PATH=$SQFILE_PATH
        SQFILE_DIR=$(dirname $SQFILE_FULL)
        DEFAULT_DIR=$SQFILE_DIR

        return 0

    elif [ $ret -eq 40 ]; then
        device=$FOUND_ID_DEVICE
        need_to_ask=true

        warn
        # Found <frugal> device with <label=xxxx>
        warn  "Found %s device with %s" $(cqw frugal) "$name=$(cqw $value)"
        warn  "But did not find file %s on the device" "$(cqw $sqfile)"
        warn  "Will start normal boot to do frugal install"
        warn

    elif [ "$FORCE_FRUGAL" ]; then
        msg 'Force frugal install'
        if [ $ret -eq 0  ]; then
            device=$FOUND_DEV
            umount $FOUND_DEV
        fi

    else
        warn
        warn  "Could not find %s device with %s" $(cqw frugal) "$name=$(cqw $value)"
        warn  "Will start normal boot to do frugal install"
        warn
    fi

    find_boot_file "$SQFILE_FILE" "$BOOT_MP" "$BOOT_ID" "$BOOT_RETRY" \
        || linuxfs_error  "$_Could_not_find_X_file_Y_" "$LINUXFS_NAME" "$(fqe \"$SQFILE_FILE\")"

    SQFILE_MP=$BOOT_MP
    SQFILE_DEV=$FOUND_DEV

    SQFILE_FULL=$BOOT_MP/$SQFILE_FILE
    SQFILE_PATH=${SQFILE_FILE%/*}
    DEFAULT_PERSIST_PATH=$SQFILE_PATH

    mount_linuxfs "$SQFS_MP" "$SQFILE_FULL" "$WRAP_FILE_MP"

    check_kernel_version $SQFS_MP

    prep_ld_path

    local old_dir=$(dirname $SQFILE_FULL)
    local actual=$(file_usage_m $old_dir $FRUGAL_FILES)

    # Safety first!
    local needed=${FRUGAL_NEEDED:=$((actual + 10))}

    if  [ -z "$device" ]; then

        msg  "$_Looking_for_partitions_with_at_least_X_free_Please_wait_" "$(nq $(size_label_m $needed))"

        select_device 'frugal' device $needed not_mounted || return 2

        local ret=$?

        [ -z "$device" -o "$device" =  "$_quit_" ] && return 1

        [ "$LABEL_FRUGAL" ] && label_device $device "$DISTRO_NAME-Frugal"
    fi

    if ! mkdir -p $FRUGAL_MP; then
        err  "Could not make frugal directory"
        return 10
    fi

    if ! try_mount $device $FRUGAL_MP rw; then
        err  "Could not mount frugal device %s" $(pqe $device)
        return 20
    fi

    local sqfile_full=$FRUGAL_MP/$sqfile
    local sqfile_dir=$(dirname $sqfile_full)

    [ "$FORCE_FRUGAL" ] && rm -rf $sqfile_dir

    breakpoint f "in frugal install"

    if [ -e $sqfile_full ]; then
        msg  "$_Found_existing_X_install_" frugal
    else

        local free=$(free_space $FRUGAL_MP)

        vmsg 5 'Device %s has %s free' $(pq $device) "$(nq $(size_label_m $free))"

        if [ $free -lt $needed ]; then
            err  "Not enough free space on device %s to do install" $(pqe $device)
            return 20
        fi

        if [ "$need_to_ask" ]; then
            #. Do you want to do a <frugal> install on the <name> device?
            YES_no  "$_Do_you_want_to_do_a_X_install_on_the_Y_device_" frugal $(pq $device) || return 2
        fi

        #. Doing <frugal> install, please be patient ...
        heading  "$_Doing_X_install_please_be_patient_" frugal
        log_cmd mkdir -p $sqfile_dir || return 30

        msg  "$_copy_X_to_Y_" $(pq $(size_label_m $actual)) $(pq $sqfile_dir/)

        if ! time_cmd copy_files_md5 $old_dir $sqfile_dir $FRUGAL_FILES; then
            warn  "Copy failed.  Erasing partial copy"
            log_cmd rm -f $sqfile_full
            return 40
        fi
        #. The <frugal> install succeeded
        warn  "$_The_X_install_succeeded_" frugal
        DID_FRUGAL=true
    fi

    log_cmd umount $SQFS_MP
    log_cmd umount $BOOT_MP

    mount --move $FRUGAL_MP $BOOT_MP
    rmdir $FRUGAL_MP

    SQFILE_MP=$BOOT_MP
    SQFILE_DEV=$device

    SQFILE_FULL=$BOOT_MP/$sqfile
    SQFILE_PATH=${sqfile%/*}
    DEFAULT_PERSIST_PATH=$SQFILE_PATH
    SQFILE_DIR=$(dirname $SQFILE_FULL)
    DEFAULT_DIR=$SQFILE_DIR

    return 0
}

find_linuxfs_file() {

    if [ -n "$ISO_FILE" -o -n "$FROM_ISO" ]; then

        : ${ISO_FILE:=$DEFAULT_ISO_FILE}
        ISO_FILE=${ISO_FILE#/}

        heading "${cheat_co}fromiso"

        #. Could not find <type> file <file-name>
        find_boot_file "$ISO_FILE" "$ISO_DEV_MP" "$BOOT_ID" "$BOOT_RETRY" \
            || fatal  "$_Could_not_find_X_file_Y_" iso "$(pqh $ISO_FILE)"

        local iso_full=$ISO_DEV_MP/$ISO_FILE
        DEFAULT_PERSIST_PATH=${ISO_FILE%/*}

        # Check md5 of files in directory containing iso file
        [ "$CHECK_MD5" ] && check_md5 $iso_full

        # Mount the iso file
        # Could not mount <file-name> as a <iso> file
        mkdir -p $ISO_FILE_MP
        mount -t iso9660 -o loop,ro $iso_full $ISO_FILE_MP \
            || mount -t udf -o loop,ro $iso_full $ISO_FILE_MP \
            || fatal  "$_Could_not_mount_X_as_a_Y_file_" "$(pqh $iso_full)" 'iso'

        # Find the linuxfs file
        SQFILE_FULL="$ISO_FILE_MP/$SQFILE_FILE"
        [ -f "$SQFILE_FULL" ] \
            || linuxfs_error  "$_File_X_not_found_on_device_Y_" "$LINUXFS_NAME" "$SQFILE_FULL" "$FOUND_DEV"

        SQFILE_MP=$ISO_FILE_MP
        SQFILE_DEV=$FOUND_DEV   # Used for toram-eject
        SQFILE_PATH=${SQFILE_FILE%/*}

        DID_ISO=true
    else
        find_boot_file "$SQFILE_FILE" "$BOOT_MP" "$BOOT_ID" "$BOOT_RETRY" \
            || linuxfs_error  "$_Could_not_find_X_file_Y_" "$LINUXFS_NAME" "$(fqe \"$SQFILE_FILE\")"

        SQFILE_MP=$BOOT_MP

        SQFILE_DEV=$FOUND_DEV

        SQFILE_FULL=$BOOT_MP/$SQFILE_FILE
        SQFILE_PATH=${SQFILE_FILE%/*}
        DEFAULT_PERSIST_PATH=$SQFILE_PATH

    fi

    SQFILE_DIR=$(dirname $SQFILE_FULL)
    DEFAULT_DIR=$SQFILE_DIR
}

# FIXME: mention failsafe option or load=all.

linuxfs_error() {
    local msg=$(printf "$@")  device_list=$(echo $DEVICE_LIST)
    local all_devices=$(echo $(most_block_devices));

    _fatal "$msg" \
        "$(printf  "$_Searched_devices_X_" "${hi_co}$device_list")" \
        "$(printf  "$_Searched_types_X_" "${hi_co}$FROM_TYPE")"   \
        "$(printf  "$_All_block_devices_X_" "${hi_co}$all_devices")" \
        "" \
        "$(printf  "$_Please_contact_X_at_Y_" "$DEVELOPER" "$DISTRO_BUG_REPORT_URL")"
}

check_kernel_version() {
    local mp=${1%/}  fatal=$2
    local kversion=$(uname -r)
    local mod_dir=/lib/modules/$kversion

    [ -d $mp$mod_dir ] && return

    if [ "$fatal" ]; then
        _fatal  "$_Kernel_version_mismatch_" \
            "$(printf  "$_Kernel_version_X_" $(pqh $kversion))" \
            "$(printf  "$_The_module_directory_X_is_missing_" $(pqh $mp$mod_dir))"
    else
        _non_fatal  "$_Kernel_version_mismatch_" \
            "$(printf  "$_Kernel_version_X_" $(pqh $kversion))" \
            "$(printf  "$_The_module_directory_X_is_missing_" $(pqh $mp$mod_dir))"
    fi
}

#==============================================================================
# Mount things
#==============================================================================

mount_linuxfs() {

    local sqfs_mp=${1:-$SQFS_MP}  sqfile_full=${2:-$SQFILE_FULL}
    local wrap_mp=${3:-$WRAP_FILE_MP}  vid_file=$1$VID_FILE

    mkdir -p $sqfs_mp
    vmsg 6  "Mount file %s at %s" "$(fq $sqfile_full)" "$(fq $sqfs_mp)"
    #. Could not mount <file name> as a <type> filesystem
    log_cmd mount -t squashfs -o loop,ro $sqfile_full $sqfs_mp \
        || fatal  "$_Could_not_mount_X_as_a_Y_filesystem_" $sqfile_full squashfs

    if [ -n "$wrap_mp" -a -e "$sqfs_mp/$SQFILE_NAME" -a ! -d "$sqfs_mp/usr" ]; then
        msg 'Found wrapped filesystem. Move wrapper ...'
        mkdir -p $wrap_mp
        mount --move $sqfs_mp $wrap_mp

        try_file_mount  $wrap_mp/$SQFILE_NAME $sqfs_mp ro \
            || fatal  "$_Could_not_mount_X_as_a_Y_file_" $sqfile_full 'filesystem'
    fi

    LINUXFS_DEV=$(df $sqfile_full | tail -n1 | cut -d" " -f1)

    SQFS_VID=$(get_vid $vid_file)

    if [ "$SQFS_VID" ]; then
        vmsg 7 'sqfs_vid: %s' "$nc_co$SQFS_VID"
    else
        #. No <VID> found in <file-name>
        vmsg 7  "$_No_X_found_in_Y_" "$VID_NAME" "$white$vid_file$m_co"
    fi
    SQFS_VID_FILE=$vid_file
}

mount_aufs_ram() {
    local aufs_ram_mp=$1  min_aufs_ram=$2  free_mem=$3
    # Default tempfs size for tempfs = 80% of FREE_MEM
    AUFS_RAM_SIZE=$((8 * free_mem / 10))

    [ "$AUFS_RAM_SIZE" -lt "$MIN_AUFS_RAM" ] && AUFS_RAM_SIZE=$MIN_AUFS_RAM
    mkdir -p $aufs_ram_mp

    mount_tmpfs $aufs_ram_mp $AUFS_RAM_SIZE 'aufs ram' || fatal  "$_Create_X_failed_" 'aufs tmpfs'
}

check_unionfs() {
    test -d /sys/module/aufs    && AUFS_OKAY=true
    test -d /sys/module/overlay && OVERLAY_OKAY=true
    [ "$AUFS_OKAY" ]    && vmsg 6  "$_Found_X_" aufs
    [ "$OVERLAY_OKAY" ] && vmsg 6  "$_Found_X_" overlay

    case $AUFS_OKAY:$OVERLAY_OKAY in
            :)  check_kernel_version / fatal
                fatal  "$_Neither_X_nor_Y_is_available_" aufs overlayfs ;;
    esac

    case $WANT_UNIONFS in
           aufs) want_unionfs "$AUFS_OKAY"     aufs    overlay ;;
        overlay) want_unionfs "$OVERLAY_OKAY"  overlay aufs    ;;
    esac

    [ "$USE_UNIONFS" ] && return

    case  $AUFS_OKAY:$OVERLAY_OKAY in
        true:) USE_UNIONFS=aufs              ;;
        :true) USE_UNIONFS=overlay           ;;
    true:true) USE_UNIONFS=$DEFAULT_UNIONFS  ;;
    esac
}

want_unionfs() {
    local okay=$1  this=$2  other=$3
    if [ "$okay" ]; then
        USE_UNIONFS=$this
    else
        USE_UNIONFS=$other
        non_fatal  "$_The_X_filesystem_is_not_available_Using_Y_" "$this" "$other"
    fi
}

need_unionfs() {
    local okay=$1  this=$2
    if [ "$okay" ]; then
        [ "$USE_UNIONFS" != "$this" ] && warn  "$_Forcing_use_of_X_to_match_existing_rootfs_file_" $(pqw $this)
        USE_UNIONFS=$this
    else
        fatal  "$_The_X_filesystem_is_not_available_Cant_use_this_Y_file_" "$this" 'rootfs'
    fi
}

check_existing_unionfs() {
    local mp=$1  type
    if test -d $mp/etc; then
        need_unionfs "$AUFS_OKAY" aufs
    elif test -d $mp/upper; then
        need_unionfs "$OVERLAY_OKAY" overlay
    fi

    case $USE_UNIONFS in
           aufs) ROOTFS_BASE=$ROOTFS_MP                        ;;
        overlay) ROOTFS_BASE=$ROOTFS_MP/upper                  ;;
              *) fatal "Unknown unionfs type: $USE_UNIONFS"    ;;
    esac
}

mount_unionfs() {
    local upper_mp aufs_mp=$1  aufs_ram_mp=$3  rootfs_mp=$4

    if [ -n "$PERSIST_ROOT" -a -n "$STATIC_ROOT" ]; then
        #. Enable <type> persistence
        msg  "$_Enable_X_persistence_" "$(pq static rootfs)"
        upper_mp=$rootfs_mp
    else
        STATIC_ROOT=
        upper_mp=$aufs_ram_mp
    fi

    case $USE_UNIONFS in
        overlay) mount_overlayfs "$1" "$2" $upper_mp  ;;
           aufs) mount_aufs      "$1" "$2" $upper_mp  ;;
              *) fatal "Unknown unionfs type: $USE_UNIONFS"
    esac

    # Grab what we need from /etc before it gets nuked
    cp -a /etc/mtab $aufs_mp/etc/

    # Always a good idea
    mkdir -p   $aufs_mp/tmp $aufs_mp/var/tmp
    chmod 1777 $aufs_mp/tmp $aufs_mp/var/tmp
}

mount_overlayfs() {
    local  aufs_mp=$1  sqfs_mp=$2  upper_mp=$3

    local upper=$upper_mp/upper work=$upper_mp/work
    mkdir -p $aufs_mp $upper $work

    # Do the actual mount
    vmsg 6 'Mount %s at %s' 'overlayfs' "$(fq $aufs_mp)"

    log_cmd mount -t overlay overlay -o "lowerdir=$sqfs_mp,upperdir=$upper,workdir=$work" $aufs_mp \
        || fatal  "$_Could_not_mount_X_" overlayfs
}


mount_aufs() {
    local  aufs_arg  aufs_mp=$1  sqfs_mp=$2  upper_mp=$3

    mkdir -p $aufs_mp
    local aufs_arg="br:$upper_mp:$sqfs_mp=ro$NO_PLINK"

    # Do the actual aufs mount
    vmsg 5  "Mount %s at %s" 'aufs' "$(fq $aufs_mp)"

    log_cmd mount -t aufs -o "$aufs_arg",noatime $aufs_mp $aufs_mp \
        || fatal  "$_Could_not_mount_X_" aufs
}

link_top_dirs() {
    local mp=$1  dir  mp_dir
    shift
    vmsgN 7 'symlink: '
    for dir; do
        mp_dir=$mp/$dir
        [ -d $mp_dir ] || continue
        rm -rf /$dir
        ln -s $mp_dir /$dir
        vmsgN 7 "$white/$dir "
    done
    vmsg 7
}

try_mount() {
    local dev=$1  mp=$2  rw=${3:-$RW_MODE}
    local type=$(blkid $dev | sed -n -r 's/.* TYPE="([^"]*)"$/\1/p' | tail -n 1)
    [ "$type" ] || return 1
    local opts=$rw,noatime
    local winopts=$opts,umask=000,dmask=002,fmask=113
    local fatopts=$winopts,shortname=winnt,uid=$USER_UID,gid=$USER_GID

    case $type in
            ext4) log_cmd mount -t $type   -o $opts$LAZYTIME  $dev "$mp" 2>/dev/null ;;
        ext[234]) log_cmd mount -t $type   -o $opts           $dev "$mp" 2>/dev/null ;;
            vfat) log_cmd mount -t vfat    -o $fatopts        $dev "$mp" 2>/dev/null ;;
         iso9660) log_cmd mount -t iso9660 -o ro              $dev "$mp" 2>/dev/null ;;
             udf) log_cmd mount -t udf     -o ro              $dev "$mp" 2>/dev/null ;;
            ntfs) log_cmd ntfs-3g          -o force,$winopts  $dev "$mp" 2>/dev/null 17>/dev/null || \
                  log_cmd mount -t ntfs    -o umask=000,ro    $dev "$mp" 2>/dev/null ;;
               *) log_cmd mount -t $type   -o $opts           $dev "$mp" 2>/dev/null ;;
    esac
    return $?
}

try_file_mount() {
    local file=$1  mp=$2  rw=${3:-$RW_MODE}
    local opts=loop,$rw,noatime

    #mount -t reiserfs         -o loop,$rw,noatime "$file" "$mp"  2>/dev/null || \
        mount -t ext4 -o $opts$LAZYTIME "$file" "$mp"  2>/dev/null || \
        mount -t ext3 -o $opts          "$file" "$mp"  2>/dev/null || \
        mount -t ext2 -o $opts          "$file" "$mp"  2>/dev/null
    return $?
}

#------------------------------------------------------------------------------
# Function: mount_tmpfs <mountpoint> <size> <name> <mode>
#------------------------------------------------------------------------------
mount_tmpfs() {
    local mp=$1  size=$2  name=$3  opts=${4+,}$4
    mkdir -p $mp
    local full_name=$(printf "%14s" "$name tmpfs")
    local   full_mp=$(printf "%-20s" "$mp")
    #. Create <something tmpfs> at <directory> <(size)>
    vmsg 8  "$_Create_X_at_Y_Z_" "${hi_co}$full_name${m_co}" "$(fq "$full_mp")" "(${num_co}$size ${m_co}MB)"
    log_cmd $LIVE_BIN/mount -t tmpfs -o size=${size}m$opts,noatime tmpfs $mp
    return $?
}

#==============================================================================
# Check Filesystems
#==============================================================================

read_superblock() {
    local dev=$1
    local word_size=$((2**16))
    local addr f1 f2 f3 f4 f5 f6 f7 f8

    while read addr f1 f2 f3 f4 f5 f6 f7 f8; do
        case $addr in
            0000030)
                echo SB_MOUNT_CNT=$f3
                echo SB_MAX_MOUNT_CNT=$f4
                echo SB_MAGIC=$f5
                echo SB_STATE=$f6
                ;;
            0000040)
                echo SB_LASTCHECK=$((    f2 * word_size + f1))
                echo SB_CHECKINTERVAL=$((f4 * word_size + f3))
                ;;
        esac
    done <<DD_Hexdump
$(dd if=$dev bs=1 skip=1024 count=128 2>/dev/null| hexdump -d | sed -r "s/ 0+([0-9])/ \1/g")
DD_Hexdump
}

should_fsck() {
    local dev=$1  sdev=${1##*/}  name=$2  max_count=$MAX_MOUNT_CNT
    [ "$DO_FSCK" ] || return 1

    case ,$DID_FSCK, in
        *,$dev,*) return 1
    esac

    DID_FSCK=$DID_FSCK,$dev

    local SB_MOUNT_CNT SB_MAX_MOUNT_CNT SB_MAGIC SB_STATE SB_LASTCHECK SB_CHECKINTERVAL

    eval $(read_superblock $dev)

    if [ "$SB_MAGIC" != 61267 ]; then
        [ "$FORCE_FSCK" ] \
            && msg 'Cannot check %s filesystem on device %s.  Not an ext2/3/4 filesystem.' \
                "$(pq $name)" "$(pq $sdev)"
        return 1
    fi

    local days=$((SB_CHECKINTERVAL /60/60/24))

    vmsg 7  "precheck filesystem on %s"                     $(dq $dev)
    if [ "$DB_PLUS" ]; then
        vmsg 9 "${green}         Device:$white %s"             $dev
        vmsg 9 "${green}    Mount count:$white %s"             $SB_MOUNT_CNT
        vmsg 9 "${green}Max mount count:$white %s"             $SB_MAX_MOUNT_CNT
        vmsg 9 "${green}   Last checked:$white %s"             "$(date --date=@$SB_LASTCHECK)"
        vmsg 9 "${green}   Magic number:$white 0x%04X"         $SB_MAGIC
        vmsg 9 "${green} Check interval:$white %d$m_co (days)" $days
    fi

    [ "$FORCE_FSCK" ] && return 0

    if [ $SB_STATE != 1 ]; then
        # <boot> device <device-name> not cleanly unmounted.  Check forced.
        warn  "%s device %s not cleanly unmounted.  Check forced." "$(pqw $name)" $(pqw $sdev)
        return 0
    fi

    [ "$SB_MAX_MOUNT_CNT" != 65535 ] && max_count=$SB_MAX_MOUNT_CNT

    if [ $SB_MOUNT_CNT -ge $max_count ]; then
        # <boot> device <device-name> has not been checked in <32> mounts.  Check forced.
        warn  "%s device %s has not been checked in %s mounts.  Check forced." \
            "$(pqw $name)" $sdev "$(nq $SB_MOUNT_CNT)"
        return 0
    fi

    [ $SB_CHECKINTERVAL -gt 0 ] || return 1

    [ $((SB_LASTCHECK + SB_CHECKINTERVAL)) -ge $(date +%s) ] || return 1

    # <boot> device <device-name> has not been checked in <100> days.  Check forced.
    warn  "%s device %s has not been checked in %s days.  Check forced." \
        $(pqw $name) $(pqw $sdev) "$(nqw $days)"
    return 1
    return 0
}

do_fsck() {
    local err ret  name=$1 dev=$2  sdev=$(pq ${2##*/}) opts=${3:--n} filefs=$4

    # Use a fifo so we can get the return status and tee the output
    local fifo=/tmp/pipe
    rm -f $fifo
    mkdir -p $(dirname $fifo)
    mkfifo $fifo
    tee -a $MY_LOG < $fifo &

    msg  "$_check_X_filesystem_" "$(pq $name)"

    breakpoint F "Before fsck"

    vmsg 2 "fsck$white $dev"
    ld_path /sbin/e2fsck $opts $dev &> $fifo
    ret=$?

    rm -f $fifo

    ld_path /sbin/tune2fs -C 1 $dev

    breakpoint F "After fsck"

    case $ret in
	    0) vmsg 1     "$_There_were_no_errors_on_X_"            "$sdev" ; err=0 ;;
	    1) vmsg 1    'Filesystem %s repaired'                "$sdev" ; err=0 ;;
	  2|3) vmsg 1    'Filesystem %s repaired'                "$sdev" ; err=0 ;;
	    4) non_fatal 'Errors on %s left uncorrected'         "$sdev" ; err=1 ;;
	    8) non_fatal '%s operational error on %s'       fsck "$sdev" ; err=1 ;;
	   12) non_fatal '%s interrupted on %s'             fsck "$sdev" ; err=1 ;;
	   32) non_fatal '%s cancelled on %s'               fsck "$sdev" ; err=1 ;;
	    *) non_fatal 'Filesystem %s could not be fixed'      "$sdev" ; err=1 ;;
	esac

    return $err
}

fsck_boot_dev() {
    local dev=$1  mp=$2

    local type=$(grep "^$dev " /proc/mounts | cut -d" " -f3)

    if [ -n "$FORCE_FSCK" -a -n "${type##ext[234]}" ]; then
        msg 'Cannot check %s device filesystem of type %s' "$(pq boot)" "$(pq $type)"
        return
    fi

    should_fsck $dev boot || return

    log_cmd mount -o remount,ro $dev
    do_fsck  "$_boot_device_" $dev -n
}

#==============================================================================
#   Misc Functions
#==============================================================================

set_path() {
    PATH=$1
    hash -r
}

should_break_at() {
    local bp=$1
    case ,$BREAK_POINTS, in
        *,$bp,*) return 0 ;;
          *,A,*) return 0 ;;
          *,a,*)          ;;
              *) return 1 ;;
    esac
    case $bp in
        [e1-9]) return 0 ;;
             *) return 1 ;;
    esac
}

custom_code() {
    local file=$1  desc=$2

    [ "${#file}" -eq 1 ] && file=$CUSTOM_DIR/$DISTRO_NAME/$file.sh

    [ -r $file ] || return

    if $LIVE_BIN/sh -n $file; then
        [ "$desc" ] && vmsg 7 'Run custom code %s: %s' "$white$desc$m_co" "$(fq $file)"
        . $file
    else
        non_fatal 'Errors found in custom code: %s.  Skipping' "$(pqh $file)"
    fi
}

breakpoint() {
    local bp=$1  desc=$2

    custom_code "$bp" "$desc"

    if [ "$DB_PLUS" -a -n "${bp##b*}" ]; then
        local now=$(get_time)
        local delta=$(get_seconds $((now - LAST_BP_TIME)))
        vmsg 9 "$green@bp$cyan[$num_co$bp$m_co]$hi_co $(get_seconds $now)s ($delta)$m_co: $desc"
        LAST_BP_TIME=$now
    fi

    should_break_at $bp || return

    case $bp in
        bash*|b[1-9]) do_bash_chroot $bp ; return ;;
    esac

    echo "$green$tbar$nc_co"
    echo "$green==>$cyan limited shell @ breakpoint [$num_co$bp$cyan]$white $desc"
    echo "$green    Use the$white exit$green command or press$white Ctrl-d$green to continue"
    echo "$green    Use$white safe-poweroff$green and$white safe-reboot$green to poweroff and reboot"

    local bp_time_1=$(get_time)
    env PS1="${green}bp $red$bp$cyan>$nc_co " BP=$bp PATH=$LIVE_BIN:$PATH \
        setsid cttyhack sh 17>/dev/null
    bp_time_2=$(get_time)
    time_0=$((time_0 + bp_time_2 - bp_time_1))
    echo
}

do_bash_chroot() {

    local rc_file=/root/chrootrc bp=${1:-bash} root_dir=${2:-$NEW_ROOT}

    cat<<Chroot_RC > $root_dir$rc_file
alias shutdown="touch /poweroff; exit &>/dev/null"
alias poweroff="touch /poweroff; exit &>/dev/null"
alias reboot="touch /reboot; exit &>/dev/null"
source /root/.bashrc
rm $rc_file
Chroot_RC

    echo "$green$tbar$nc_co"
    echo "$green==>$cyan Bash shell @ breakpoint [$num_co$bp$cyan]$white $desc"
    echo "$green    Use the$white exit$green command or press$white Ctrl-d$green to boot"
    echo "$green    Use$white poweroff$green and$white reboot$green to poweroff and reboot"

    rm -f $root_dir/poweroff $/root_dir/reboot
    setsid cttyhack aufs-chroot bash --init-file $rc_file
    local do_poweroff do_reboot
    test -e $root_dir/poweroff && do_poweroff=true
    test -e $root_dir/reboot   && do_reboot=true
    rm -f $root_dir/poweroff $root_dir/reboot
    [ "$do_poweroff" ] && safe-poweroff
    [ "$do_reboot" ]   && safe-reboot
}

db_msg() { vmsg 5 "${green}db+:$hi_co $@" ;}
err()    { vmsg 1 "$err_co$@"             ;}
msg()    { vmsg 5 "$@"                    ;}
msgN()   { vmsgN 5 "$@"                   ;}
msg_nc() { vmsg 5 "$nc_co$@"              ;}
warn()   { vmsg 3 "$warn_co$@"            ;}

bq()     { echo "$yellow$*$m_co"          ;}
cq()     { echo "$cheat_co$*$m_co"        ;}
cqw()    { echo "$cheat_co$*$warn_co"     ;}
cqe()    { echo "$cheat_co$*$err_co"      ;}
dq()     { echo "$dev_co$*$m_co"          ;}
dqe()    { echo "$dev_co$*$err_co"        ;}
fq()     { echo "$from_co$*$m_co"         ;}
fqe()    { echo "$from_co$*$err_co"       ;}
mpq()    { echo "$mp_co$*$m_co"           ;}
nq()     { echo "$num_co$*$m_co"          ;}
nqw()    { echo "$num_co$*$warn_co"       ;}
pq()     { echo "$hi_co$*$m_co"           ;}
pqe()    { echo "$bold_co$*$err_co"       ;}
pqw()    { echo "$hi_co$*$warn_co"        ;}
pqh()    { echo "$m_co$*$hi_co"           ;}
hq()     { echo "$hi_co$*$m_co"           ;}
ncq()    { echo "$hi_co$*$nc_co"          ;}
gq()     { echo "$hi_co$*$lt_green"       ;}


imsg() {
    local fmt=$1
    shift
    printf "$lt_green$fmt$nc_co\n" "$@"
}

vmsg() {
    local level=$1  fmt=$2
    shift 2

    msg=$(printf "$m_co$fmt$nc_co" "$@")

    [ "$level" -le "$VERBOSE" ] && echo -e "$msg"
    echo -e "$msg" >> $MY_LOG
    return 0
}

vmsgN() {
    local level=$1  fmt=$2
    shift 2

    msg=$(printf "$m_co$fmt$nc_co" "$@")

    [ "$level" -le "$VERBOSE" ] && printf "$msg"
    echo -ne "$msg" >> $MY_LOG
    return 0
}

vmsg_if() {
    local level=$1; shift
    [ "$VERBOSE" -ge "$level" ] || return
    vmsg $level "$@"
}

vmsg_nc() {
    local level=$1; shift
    vmsg $level "$nc_co$@"
}

set_colors() {
    local noco=$1  loco=$2

    [ "$noco" ] && return

    local e=$(printf "\e")
     black="$e[0;30m";    blue="$e[0;34m";    green="$e[0;32m";    cyan="$e[0;36m";
       red="$e[0;31m";  purple="$e[0;35m";    brown="$e[0;33m"; lt_gray="$e[0;37m";
   dk_gray="$e[1;30m"; lt_blue="$e[1;34m"; lt_green="$e[1;32m"; lt_cyan="$e[1;36m";
    lt_red="$e[1;31m"; magenta="$e[1;35m";   yellow="$e[1;33m";   white="$e[1;37m";
     nc_co="$e[0m";

    cheat_co=$white;      err_co=$red;       hi_co=$white;
      cmd_co=$white;     from_co=$lt_green;  mp_co=$magenta;   num_co=$magenta;
      dev_co=$magenta;   head_co=$yellow;     m_co=$lt_cyan;    ok_co=$lt_green;
       to_co=$lt_green;  warn_co=$yellow;  bold_co=$yellow;

    [ "$loco" ] || return

    from_co=$brown
      hi_co=$white
       m_co=$nc_co
     num_co=$white
}


get_time() { cut -d" " -f22 /proc/self/stat ;}

show_time() {
    msg "$(printf "$green%ss$yellow %s" $(get_seconds) "$*")"
}

get_seconds() {
    local dt=${1:-$(get_time)}
    printf "%03d" $dt | sed -r 's/(..)$/.\1/'
}

bogo_meter() {
    local width=${1:-$SCREEN_WIDTH} delay=60  dot=.
    local cnt=$(( width * 80 / 100 ))
    local sleep=$(( 1000000 * delay / cnt ))
    while true; do
        for s in $(seq 1 $cnt); do
            usleep $sleep
            echo -n "$dot"
        done
        echo
    done
}

time_cmd() {
    local t0=$(get_time)
    (bogo_meter)&
    local pid=$!
    "$@"
    local ret=$?
    sync
    local t1=$(get_time)
    local secs=$(get_seconds $((t1 - t0)))
    kill -9 $pid
    echo
    vmsg 6  "command %s took %s seconds" "$(pq $*)" "$(nq $secs)"
    return $ret
}

vmsg_old() {
    local msg nflag  level=$1; shift

    [ "$1" = "-n" ] && nflag=true && shift
    local fmt=$1; shift

    if [ "$nflag" ]; then
        msg="$(printf "$m_co$fmt$nc_co" "$@")"
    else
        msg="$(printf "$m_co$fmt$nc_co" "$@")\n"
    fi

    [ "$level" -le "$VERBOSE" ] && printf "$msg"
    echo -ne "$msg" >> $MY_LOG

    return 0
}

heading() {
    vmsg 6 $head_co$tbar
    #. Begin [doing some sub process]
    vmsgN 3 "$hi_co%s "  "$_Begin_"
    vmsg 3 "$@"
}

log_cmd() {
    vmsg_nc 8 "$*"
    #"$@" >/dev/null
    "$@"
    return $?
}

_error() {
    local type=$1  cmsg1=$2  cmsg2=$3  msg=$4
    shift 3

    local match="$(echo $NO_ERROR | sed 's/_/ /g')"
    if [ "$NO_ERROR" -a -z ${msg##$match*} ]; then
        msg  "$_Skip_Error_X_" "$(bq $msg)"
        return
    fi

    if [ "$type" ]; then
        warn
        #warn $hbar
        err "$type"
        #warn $hbar
        [ "$1" ] && vmsg 1 "${hi_co}$1"
        shift
        for arg; do
            vmsg 1 "${hi_co}$arg"
        done
        ### warn $hbar
        err
    fi

    breakpoint e "On possibly fatal error"

    local prompt="${green}Select ${yellow}$cmsg1 p$green or$yellow r$green then press <enter>"

    local fmt="    $yellow%s$cyan =$white %s$nc_co\n"
    local reply
    while [ true ]; do
        ### echo $m_co$tbar$nc_co

        [ "$cmsg1" ] && printf "$fmt" "$cmsg1" "$cmsg2"
        printf "$fmt" p  "$_power_off_"
        printf "$fmt" r  "$_reboot_"

        ### echo $m_co$tbar$nc_co
        echo -n "$prompt$nc_co "
        read reply
        case $reply in
            p*|P*)
                echo  "$_power_off_" " ..."
                safe_shutdown poweroff;;

            r*|R*)
                echo  "$_reboot_"  " ..."
                safe_shutdown reboot;;

             c*|C*)
                 [ "$cmsg1" ] && return;;
        esac
        printf "\n${m_co} %s: >>$yellow$reply$m_co<<. %s:$nc_co\n" \
             "$_Unknown_command_"  "$_Please_try_again_"
    done
}

move_mountpoints() {
    local dir mp old aufs_live=/live/aufs$FINAL_DIR

    [ -d $aufs_live ] || return

    for dir in $(ls $aufs_live); do
        [ "$dir" = aufs ] && continue
        mp=$aufs_live/$dir
        mountpoint -q $mp || continue
        old=/live/$dir
        mkdir -p $old
        # echo mount --move $mp $old
        mount --move $mp $old
    done
}

mountpoint_list() {
    local dev mp other ret=1

    while read dev mp other; do
        case $mp in /|/dev|/dev/*|/proc|/sys) continue;; esac
        echo -n "$mp "
        ret=0
    done << Read_Mounts
$(tac /proc/mounts)
Read_Mounts
    return $ret
}

final_umount() {
    local mp list i
    move_mountpoints

    for i in $(seq 1 4); do
        list=$(mountpoint_list) || return 0
        for mp in $list; do
            umount $mp
        done
    done
    list=$(mountpoint_list) || return 0
    err  "Unable to unmount %s" "$(pq $list)"
    return 1
}

safe_shutdown() {
    local cmd=$1  ask=$2

    set_path $LIVE_BIN

    final_umount && vmsg 1  "$_Unmount_successful_"
    breakpoint u "final umount"
    if [ "$ask" ]; then
        vmsgN 1  "$_Press_Enter_to_X_" "$(hq $cmd)"
        read x
    fi
    vmsg -1  "$_Will_now_X_the_system_" "$(hq $cmd)"
    exec $cmd -f -n
}

_fatal()     { _error  "$_Fatal_Error_"     ""  ""          "$@" ;}
_non_fatal() { _error  "$_Non_Fatal_Error_" "c"  "$_continue_"  "$@" ;}
fatal()      { _fatal     "$(printf "$@")"                   ;}
non_fatal()  { _non_fatal "$(printf "$@")"                   ;}

YES_no() {  _yes_no 0  "$_yes_" "$@" ;}
yes_NO() {  _yes_no 1  "$_no_"  "$@" ;}

_yes_no() {
    local def_ret=$1  def_lab=$(cq $2)
    shift 2
    local ans title=$(printf "$m_co$@")
    local yes=$(cq  "$_yes_")  no=$(cq  "$_no_")  y_lett=$(bq y) n_lett=$(bq n)

    local err_msg=$(printf  "$_You_must_answer_X_or_Y_Please_try_again_" $(pqe y) $(pqe n))
    while true; do
        echo -e "$title?"
        printf "${m_co}$y_lett=$yes. $n_lett=$no. %s $def_lab\n"  "$_The_default_is_"
        echo -n "$green>$nc_co "

        read ans
        case x$ans in
            x[Yy]*) return 0;;
            x[Nn]*) return 1;;
        esac

        [ -z "$ans" ] && return $def_ret
        printf "$err_co%s$nc_co\n" "$err_msg"
    done
}

my_select() {
    local title=$1  var=$2  width=${3:-0}  default=$4
    shift 4

    local data display lab cnt=0 dcnt
    for lab; do
        cnt=$((cnt+1))
        dcnt=$cnt

        [ "$lab" =  "$_quit_" ] && dcnt=0
        data="${data}$dcnt:$lab\n"

        [ "$lab" =  "$_quit_" ] && lab=$bold_co$lab$nc_co
        [ $cnt = "$default" ] && lab=$(printf "%${width}s (%s)" "$lab" "$(cq  "$_default_")")
        display="${display}$(printf "$green%2d$white)$cyan %${width}s" $dcnt "$lab")\n"
    done

    my_select_2 "$title" $var "$default" "$data" "$display"
}

my_select_2() {
    local title=$1  var=$2  default=$3  data=$4  display=$5
    local def_prompt=$(printf  "$_Press_X_for_the_default_selection_" "$(cq  "$_enter_")")

    local val input err_msg
    while [ -z "$val" ]; do

        echo -e "$hi_co$title$nc_co"
        #printf "$display\n" | sed -r -e "s/(^|\t)( ?[0-9]+)(\))/\t$green\2$white\3$cyan/g" -e "s/$/$nc_co/"
        #printf "$display" | sed -r -e "s/(^|\t| )( ?[0-9]+)(\))/\t$green\2$white\3$cyan/g" -e "s/$/$nc_co/"
        printf "$display\n" | sed "s/$/$nc_co/"
        [ "$err_msg" ] && printf "$err_co%s$nc_co\n" "$err_msg"
        [ "$default" ] && printf "$m_co%s$nc_co\n" "$def_prompt"
        echo -n "$green>$nc_co "

        read input
        err_msg=
        [ -z "$input" -a -n "$default" ] && input=$default

        if ! echo "$input" | grep -q "^[0-9]\+$"; then
            err_msg="You must enter a number"
            [ "$default" ] && err_msg="You must enter a number or press <enter>"
            continue
        fi

        val=$(echo -e "$data" | sed -n "s/^$input://p" | cut -d: -f1)

        if [ -z "$val" ]; then
            err_msg=$(printf  "$_The_number_X_is_out_of_range_" "$(pqe $input)")
            continue
        fi

        eval $var=\$val
        break
    done
}

# Note: we use cmdline2 for non-menu cmdline additions so they don't get used
# by the grub2-save program and inserted into grub.cfg.
initialize_cmdline2() {
    touch /live/config/cmdline /live/config/cmdline2

    local cmdline
    if [ ${#DISABLE_SERVICES} -gt 0 ]; then
        [ -z "${DISABLE_SERVICES##*S*}" ] && cmdline="$cmdline${cmdline:+ }savestate"
        [ -z "${DISABLE_SERVICES##*s*}" ] && cmdline="$cmdline${cmdline:+ }nosavestate"
        [ -z "${DISABLE_SERVICES##*r*}" ] && cmdline="$cmdline${cmdline:+ }norepo"
        [ -z "${DISABLE_SERVICES##*k*}" ] && cmdline="$cmdline${cmdline:+ }noloadkeys"
        if [ -z "${DISABLE_SERVICES##*c*}" ]; then
            local desk_delay=$(echo $DISABLE_SERVICES | sed -r 's/[^c]*c([0-9]+(\.[0-9]*)?|\.[0-9]+|).*/\1/')
            cmdline="$cmdline${cmdline:+ }deskdelay=${desk_delay:-0}"
        fi
        [ "$cmdline" ] && echo "$cmdline" >> /live/config/cmdline2
    fi
}

my_select_menu() {
    local dir=$BOOT_MENU_DIR menu=$DO_MENUS
    unset MENU_VALUE
    [ "$menu" ] || return

    local title=$1  blurb=$2  cheat=$3  dfile=$dir/$4.data mfile=$dir/$4.menu  label=$5  fields=$6

    [ -z "${menu##*$cheat*}" ] || return

    local def_label
    local data=$(cat $dfile)
    mfile=$(xlat_mfile "$mfile" $LANG)

    if [ ! -e $dfile -o  ! -e $mfile ]; then
        [ "$BOOT_MENU_WARN" ] && warn "Missing $dfile or $mfile"
        return
    fi

    local default_str=$(rpad 16  "$_Default_")
    local menu=$(cat $mfile | sed -r \
        -e "s/Default( {8}| *$)/$default_str/" \
        -e "s/( [0-9]+)(\))/$green\1$white\2$cyan/g" \
        -e "s/\*/$bold_co*$cyan/g" \
        -e "s/\(([^)]+)\)/$nc_co(\1)$cyan/g")

    [ "$blurb"     ] && title="$title\n$blurb"
    [ "$fields"    ] && def_label=$(get_default_label "$fields" $dfile)

    if [ "$def_label" ]; then
        title=$(printf "$title\n%s:$bold_co %s"  "$_Current_default_" "$def_label")
        data="0:reset\n$data"
        menu="$(printf "  ${green}0$white)$cyan %s\n%s"  "$_Reset_" "$menu")"
    fi

    my_select_2 "$title" MENU_VALUE 1 "$data" "$menu"

    local new_label
    [ "$MENU_VALUE" ] && new_label=$(egrep "^[0-9]+:$MENU_VALUE[: ]" $dfile | cut -d: -f3)

    case $MENU_VALUE in
        default) [ -n "$def_label" ] && msg  "$_Keeping_default_X_" "$(bq $def_label)"          ;;
              *) [ -n "$def_label" ] && msg  "$_Previous_default_X_erased_" "$(bq $def_label)"
                 PROC_CMDLINE=$(filter_cmdline "$fields" $PROC_CMDLINE)                      ;;
    esac

    #. <Timezone> set to <Denver>
    case $MENU_VALUE in
        default|none|reset)                            ;;
                   *) [ -n "$new_label" ] && msg  "$_X_set_to_Y_" "$label" "$(bq $new_label)"
                      cmdline="$cmdline $MENU_VALUE"   ;;
    esac
}

rpad() {
    local width=$1  str=$2
    local pad=$((width - $(echo $str | wc -m)))
    [ $pad -le 0 ] && pad=0
    printf "%s%${pad}s" "$str" ""
}

get_default_label() {
    local fields=$1 file=$2
    local cheat=$(extract_from_cmdline "$fields")
    [ -n "$cheat" -o -z "$file" ] || return
    value=$(egrep "^[0-9]+:$cheat[: ]" $file | cut -d: -f3)
    echo ${value:-$cheat}
}

extract_from_cmdline() {
    local p fields=$1
    [ "$fields" ] || return
    for p in $(cat /proc/cmdline); do
        eval "case \$p in
        $fields) echo \$p; break ;;
        esac"
    done
}

filter_cmdline() {
    local p fields=$1
    shift

    if [ -z "$fields" ]; then
        echo "$*"
        return
    fi

    for p; do
        eval "case \$p in
        $fields) ;;
              *) echo \$p ;;
        esac"
    done
}

xlat_mfile() {
    local file=$1 lang=${2%%_*}
    echo "xlat_mfile: $lang $file" >> /mfile.log
    test -e $file.$lang && echo found >> /mfile.log
    if [ -n "$lang" -a -e $file.$lang ]; then
        echo $file.$lang
        return
    fi
    echo $file
}

do_early_menus() {
    local BOOT_MENU_DIR=/live/menus
    local MENU_VALUE BOOT_MENU_WARN=true
    local lang time_zone option persist desktop
    local cmdline
    my_select_menu  "$_Select_Language_"            "$_Languages_are_listed_alphabetically_"   l lang  "$_Language_" 'lang=*'

    case $MENU_VALUE in
        lang=*) LANG=${MENU_VALUE#lang=}
                read_xlat init $LANG       ;;
         reset) read_xlat                  ;;
    esac

    my_select_menu  "$_Select_Timezone_"            "$_Timezones_are_listed_by_longitude_"     t tz        "$_Timezone_"      'tz=*'
    my_select_menu  "$_Select_Option_"              "$_Only_one_option_can_be_selected_"       o options   "$_Options_"       "$OPTIONS_ENTRIES"
    my_select_menu  "$_Select_Mounting_Option_"     "$_Automounting_is_enabled_by_default_"    m mount     "$_Mount_"          "$_automount_mount_"
    my_select_menu  "$_Select_Persistence_Option_" ""                                      p persist   "$_Persistence_"   'persist_*|frugal_*'
    my_select_menu  "$_Select_font_size_"           "$_Scale_fonts_in_graphics_display_Default_is_1_0_"  f fontsize  "$_Font_size_" 'fontsize=*'
    #vmsg 6 "cmdline:$white $cmdline"
    [ ${#cmdline} -gt 0 ] && echo "$cmdline" >> /live/config/cmdline
}

do_late_menus() {
    local BOOT_MENU_DIR=/live/aufs/usr/local/share/boot-menus
    local BOOT_MENU_WARN=
    local lang time_zone option persist desktop
    local cmdline
    my_select_menu  "$_Select_Desktop_" "" d desktop  "$_Desktop_" 'desktheme=*'

    if [ "$REMASTERABLE" ]; then
        BOOT_MENU_DIR=/live/menus
        my_select_menu  "$_Save_these_changes_" "" s save  "$_Save_"
        echo "$cmdline" | grep -q bootsave && BOOT_SAVE=true
    fi

    #vmsg 6 "cmdline:$white $cmdline"
    [ ${#cmdline} -gt 0 ] && echo "$cmdline" >> /live/config/cmdline
}

size_label_m() { _size_label $1   "M G T" ;}
size_label_k() { _size_label $1 "K M G T" ;}

_size_label() {
    local unit frac  size=$1  units=$2
    for unit in $units; do
        if [ $size -lt 1000 ]; then
            if [ "$frac" -a $size -lt 10 ]; then
                echo "$size.$frac$unit"
            else
                echo "$size$unit"
            fi
            return
        fi
        frac=$(( 10 * ($size % 1024) / 1024 ))
        size=$(( size / 1024))
    done
    echo "$size$unit"
}

select_device() {
    local title_type=$1  var=$2  min_size=${3:-0} not_mounted=$4
    local title=$(printf  "$_Please_select_X_device_" "$title_type")

    local cnt=1 cnt_lab rtype mp=/tmp/mnt
    local size_lab free free_lab
    local line pretty skip data display
    local free_rev type_rev name_rev size_rev
    #local format="$green%3s $lt_cyan%-5s $magenta%8s $yellow%8s $lt_blue%10s $lt_gray%9s $white%-16s $lt_gray%s\n"
    local format="$green%3s $lt_cyan%s%-9s$nc_co $magenta%s%5s$nc_co $yellow%s%5s$nc_co $lt_blue%s%10s$nc_co $lt_gray%6s $lt_gray%-16s $white%s\n"
    local head_fmt="${hi_co}%3s %-9s %5s %5s %10s %6s %-16s %s\n"
    local header=$(printf "$head_fmt" "" Name Size Free FS-type Remove Model Label)
    local title="$title\n$header"

    mkdir -p $mp
    local rev=$(printf "\e[0;4;7;31m")
    #local rev=$(printf "\e[5;7;31m")
    #local rev=$(printf "\e[1;7m")

    (bogo_meter)&
    local pid=$!

    local name
    while read -r name line ; do

        [ ${#name} -eq 0 -o ${#name} -gt 10 ] && continue

        local type=$(echo "$line" | sed -n -r 's/.* TYPE="([^"]*)"$/\1/p' | tail -n 1)
        #uuid=$(echo "$line" | sed -n -r -e 's/" (UUID=|TYPE=)".*//' -e 's/[^:]*: LABEL="//p')
        case $type in
            ""|udf|iso9660|swap|squashfs) continue
        esac

        local disk_name=$(get_drive $name)

        local sys_dir=/sys/block/$disk_name
        local model=$(head -c 16  $sys_dir/device/model 2>/dev/null)
        local remove=$(cat        $sys_dir/removable    2>/dev/null)
        local reado=$(cat         $sys_dir/$name/ro     2>/dev/null)
        local size=$(cat          $sys_dir/$name/size   2>/dev/null)
        [ "$reado" = 1 ] && continue
        [ -z "$size" ] && continue
        size=$((size / 2 / 1024))

        local dev=/dev/$name

        local label=$(echo "$line" | sed -n -r -e 's/" (UUID=|TYPE=)".*//' -e 's/[^:]*: LABEL="//p')
        size_rev=
        name_rev=
        free_rev=
        type_rev=
        free=

        cnt_lab="$cnt)"
        size_lab=$(size_label_m $size)

        if [ $min_size -gt 0 -a $min_size -gt $size ]; then
            size_rev=$rev
            cnt_lab=
            skip=true
        fi

        local exist_mp=$(get_mountpoint $dev)
        if [ "$exist_mp" ]; then
            free=$(free_space $exist_mp)
            if [ "$not_mounted" ]; then
                name_rev=$rev
                cnt_lab=
                skip=true
            fi

        # The busybox mount error messages are worthless at best
        elif mount -t $type $dev $mp &>/dev/null; then
            free=$(free_space $mp)
            free_lab=$(size_label_m $free)
            umount $mp
        fi

        if [ "$free" ]; then
            free_lab=$(size_label_m $free)
            if [ $min_size -gt 0 -a $min_size -gt $free ]; then
                free_rev=$rev
                cnt_lab=
                skip=true
            fi
        else
            free_lab="?"
            type_rev=$rev
            cnt_lab=
            skip=true
        fi

        rtype="no"
        [ "$remove" = 1 ] && rtype="yes"

        pretty=$(printf "$format" "$cnt_lab" "$name_rev" "$name" "$size_rev" "$size_lab" \
            "$free_rev" "$free_lab" "$type_rev" "$type" "$rtype" "$model" "$label")


        local display="${display}$pretty\n"

        [ -z "$cnt_lab" ] && continue

        data="${data}$cnt:$dev\n"
        cnt=$((cnt + 1))

    done <<Blkid
$(sorted_blkid)
Blkid
    kill -9 $pid
    echo

    if [ $cnt -le 1 ]; then
        display="$header\n$display"
        msg_nc "$display"
        warn  "No suitable devices were found."
        return 1
    fi

    pretty=$(printf "$format" "0)" "${bold_co}quit " "$m_co($(cq default))")

    display="${display}$pretty\n"
    data="${data}0:quit\n"
    if [ "$skip" ]; then
        local rev_video=$(printf "$rev%s$nc_co"  "$_reverse_video_")
        display="${display}$(printf  "$_Partitions_that_cant_be_used_are_listed_but_not_selectable_")\n"
        display="${display}$(printf  "$_The_limiting_factors_are_highlighted_in_X_" "$rev_video")\n"
        display="${display}$(printf "A hightlighted name means the device is already mounted")\n"
    fi

    my_select_2 "$title" $var "0" "$data" "$display"
    echo -e "$display" >> $MY_LOG
    return 0
}

#------------------------------------------------------------------------------
# sort blkid output by device name, including a numerical sort when the sd
# partition numbers have two-digits.  Strip off leading "/dev/" and the ":".
#------------------------------------------------------------------------------
sorted_blkid() {
    blkid | sed -r \
            -e 's=^/dev/==' \
            -e 's=^(sd.[0-9][0-9]):=\1+\1:=' \
            -e 's=^(sd.)([0-9]):=\10\2+\1\2:=' \
        | sort \
        | sed -e 's/^sd.[0-9][0-9]+//' -e 's/://'
}

label_device() {
    local dev=$1  new=$2
    local type=$(device_fstype $dev)

    local pre post
    case $type in
         ext[234]) pre="tune2fs -L"                  ; post="$dev"   ;;
              xfs) pre="xfs_admin -L"                ; post="$dev"   ;;
              jfs) pre="jfs_tune -L"                 ; post="$dev"   ;;
             ntfs) pre="ntfslabel $dev"              ; post=""       ;;
         vfat|dos) pre="dosfslabel $dev"             ; post=""       ;;
            btrfs) pre="btrfs filesystem label $dev" ; post=""       ;;
         reiserfs) pre="reiserfstune --label"        ; post="$dev"   ;;

               "") warn  "Could not determine filesystem on %s" $(pqw $dev)
                   return 1                                          ;;

                *) warn  "Cannot label a %s filesystem" $(pqw $type)
                   return 2                                          ;;
    esac

    local cmd=$(echo $pre | cut -d" " -f1)
    local full=$(ld_path_which $cmd)

    if [ -z "$full" ]; then
        # Don't have the <command-name> tool to label a <fat32> filesystem
        warn  "Don't have the %s tool to label a %s filesystem" $(pqw $cmd) $(pqw $type)
        return 3
    fi

    local old=$(device_label $dev)

    if [ "$old" ]; then
        vmsg 1  "$_The_device_X_already_has_label_Y_" $(pq $dev) "$(pq $old)"
        yes_NO  "$_Update_label_on_X_from_Y_to_Z_" $(pq $dev) "$(pq "$old")" "$(pq "$new")" || return
    else
        vmsg 1  "$_The_device_X_has_no_label_" $(pq $dev)
        yes_NO  "$_Give_X_the_label_Y_" $(pq $dev) "$(pq "$new")" || return
    fi

    log_cmd ld_path $pre "$new" $post 2>&1 || return

    msg  "$_Label_updated_to_X_" $(pq $new)
}

label_to_dev() {
    local label=$1 devices
    local dev line lab
    while read dev line; do
        lab=$(echo "$line" | sed -n -r -e 's/" (UUID=|TYPE=)".*//' -e 's/^LABEL="//p')
        [ "$lab" = "$label" ] && devices="$devices${devices:+ }${dev%:}"
    done <<EOT
$(blkid)
EOT
    echo $devices
}

uuid_to_dev() {
    local uuid=$1 devices
    local dev line id
    while read dev line; do
        id=$(echo "$line" | sed -n -r 's/(^|.* )UUID="([^"]*)"( |$).*/\2/p')
        [ ${#id} -gt 0 -a -z "${id##$uuid}" ] && devices="$devices${devices:+ }${dev%:}"
    done <<EOT
$(blkid)
EOT
    echo $devices
}

cleanse_dev() {
    case $1 in
        /dev/*)  [ -b "$1"      ] && echo "$1";;
         dev/*)  [ -b "/$1"     ] && echo "/$1";;
            /*)  [ -b "/dev$1"  ] && echo "/dev$1";;
             *)  [ -b "/dev/$1" ] && echo "/dev/$1";;
    esac
}

general_device_error() {
    case $2 in
        uuid|label) label_error  "$@" ;;
              name) device_error "$@" ;;
    esac
}

label_error() {
    local find_type=$1  param=$2  name=$2  value=$3
    [ "$param" = label ] && param=lab
    param=${1:0:1}$param

    ##warn "$hbar"
    warn
    err  "$_Fatal_Error_"
    err  "No device with %s was found" "$name $(cqe $value)"

    vmsg_nc 3  "$_Valid_X_" "${name}s:"
    local dev val
    while read dev val; do
        [ "$val" ] && printf "  %-10s $white $param=$val$nc_co\n" $dev
    done <<Label_Error
$(
case $name in
    label)
        sorted_blkid | sed -n -r -e 's/" (UUID=|TYPE=)".*//' -e 's/ LABEL="/ /p' ;;
     uuid)
        sorted_blkid | sed -n -r 's/^([^ ]*).* UUID="([^"]*)"( |$).*/\1 \2/p' ;;
esac
)
Label_Error
    #warn "$hbar"
    warn
    case $from_type in
        persist) _error "" c  "$_continue_" ;;
              *) _error                 ;;
    esac
}

device_error() {
    local find_type=$1  type=$2  value=$3
    local valid_devs=$(echo $(most_block_devices) | sed "s/^/$white    /");
    if [ "$find_type" = 'persist' ]; then
        _non_fatal "$(printf  "$_Could_not_find_device_X_" "$(pqh $value)")"  "$_Valid_devices_": "$valid_devs"
    else
        _fatal "$(printf  "$_Could_not_find_device_X_" "$(pqh $value)")"  "$_Valid_devices_": "$valid_devs"
    fi
}

cp_file() {
    local src=$1  name=$2  dest=$3  file=$1/$2
    mkdir -p $dest
    echo -n $hi_co
    local tar=$LIVE_BIN/tar
    local progress=$LIVE_BIN/pipe_progress

    if [ ! -d "$src" ]; then
        err 'Source directory %s does not exist' $(pqe $src)
        return 2
    fi

    if [ ! -f "$file" ]; then
        err 'Source file %s does not exist' $(pqe $file)
        return 3
    fi

    $tar -C "$src" -hcf - "$name" | $progress | $tar -C "$dest" -xpf -
    local ret=$?
    echo -n $nc_co
    vmsg 8 "cp_file returned:$num_co $ret"
    return $ret
}

#------------------------------------------------------------------------------
# Function: cp_rm_dest <src> <dest>
#
# Emulate cp --remove-destination in busybox
#------------------------------------------------------------------------------
cp_rm_dest() {
    local src=$1 dest=$2
    rm -f "$dest"
    cp "$src" "$dest"
}

#------------------------------------------------------------------------------
# Function: cp_dir <sorc> <dest>
#------------------------------------------------------------------------------
cp_dir() {
    local from=$1  to=$2
    mkdir -p "$to"
    [ -z "$(ls $from)" ] && return 0
    time_cmd cp -a $from/* $to
}

old_cp_dir() {
    mkdir -p "$2"
    echo -n $hi_co
    local tar=$LIVE_BIN/tar
    local progress=$LIVE_BIN/pipe_progress
    $tar -C "$1" -cf - . | $progress | $tar -C "$2" -xpf -
    local ret=$?
    echo -n $nc_co
    return $ret
}

# FIXME: should use ld_path and --apparent-size so we aren't fooled by sparseness
file_usage_m() {
    local f file dir=$1
    shift

    [ "$dir" -a "${dir##*/}" ] && dir=$dir/
    for f; do
        file=$dir$f
        [ -e $file ] && list="$list $file"
    done
    du -scxm $list >> $MY_LOG
    du -scxm $list | tail -n 1 | cut -f1
}

copy_files_md5() {
    local f file sorc=$1  dest=$2
    shift 2
    [ "$sorc" -a "${sorc##*/}" ] && sorc=$sorc/
    for f; do
        for file in $sorc$f $sorc$f.md5; do
            [ -e $file ] || continue
            cp -a $sorc$f $dest/ || return $?
        done
    done
    return 0
}

copy_files() {
    local f file sorc=$1  dest=$2
    shift 2
    [ "$sorc" -a "${sorc##*/}" ] && sorc=$sorc/
    for f; do
        file=$sorc$f
        [ -e $file ] || continue
        cp -a $sorc$f $dest/ || return $?
    done
    return 0
}

all_space()      { $LIVE_BIN/df -m "$1" | awk '{size=$2}END{print size}' ;}
used_space()     { $LIVE_BIN/df -m "$1" | awk '{size=$3}END{print size}' ;}
avail_space()    { $LIVE_BIN/df -m "$1" | awk '{size=$4}END{print size}' ;}
du_size()        { du -scm "$@"         | tail -n1 | cut -f1             ;}
get_mountpoint() { grep "^$1 " /proc/mounts | cut -d" " -f2              ;}

# This compensates for sparse rootfs and homefs files one directory down from
# the mountpoint.  This is far from perfect but I hope it is a reasonable
# compromise that will work well for most people in most situations.
free_space() {
    local mp=$1
    local free=$($LIVE_BIN/df -m $mp | awk '{size=$4}END{print size}')
    local sparse new_free
    for sparse in $mp/*/rootfs $mp/*/homefs; do
        test -e $sparse || continue
        real=$(( $(stat -c %s $sparse) / 1024 / 1024))
        orig=$(du -m $sparse | cut -f1)
        new_free=$((free - $real + $orig))
        printf 'sparse: r=%4s o=%4s of=%5s nf=%5s %s\n' $real $orig $free $new_free $sparse >> $MY_LOG
        free=$new_free
    done
    [ $free -lt 0 ] && free=0
    echo $free
}

dir_has_param() {
    local dir=$1  want_param=$2

    [ -d "$dir" ] || return 1

    local dev=$(df $dir | tail -n1 | cut -d" " -f1)
    local params=$(grep "^$dev " /proc/mounts | cut -d" " -f4)
    case ,$params, in
        *,$want_param,*) return 0;;
    esac
    return 1
}

#------------------------------------------------------------------------------
# Function: get_vid <file>
#------------------------------------------------------------------------------
get_vid()   { [ -r "$1" ] && grep ^=== $1 | tail -n 1 ;}

vid_error() {
    VID_ERROR=$(printf "$@")
    err "$@"
    WANT_ROOTFS=
    NEED_ROOTfS=
    STATIC_ROOT=
    log_cmd umount $ROOTFS_MP
}

#------------------------------------------------------------------------------
# Function: check_vid <sqfs_vid> <mp> <fname>
#------------------------------------------------------------------------------

check_vid() {
    local sqfs_vid=$1  base=$2  fname=$3

    local vid_file=$base$VID_DIR/$fname
    breakpoint v "before check VID"

    local rootfs_vid
    [ "$vid_file" ] && rootfs_vid=$(get_vid $vid_file)

    if [ -n "$sqfs_vid" -a -n "$rootfs_vid" ]; then

        if [ "$rootfs_vid" = "$sqfs_vid" ]; then
            vmsg 6 'Matching %s found in %s' "$VID_NAME" "$(fq $vid_file)"
            return 0
        else
            #. "<VID> mismatch between <linuxfs> and <rootfs>
            vid_error  "$_X_mismatch_between_Y_and_Z_" "$VID_NAME" linuxfs rootfs
            vmsg 1 "linuxfs:$white $sqfs_vid"
            vmsg 1 " rootfs:$white $rootfs_vid"
            return 1
        fi

    elif [ "$sqfs_vid" ]; then

        # This is a hack but it prevents a warning message from the ls command
        mkdir -p $base
        # Pass test and create new rootfs version file if rootfs is empty
        # This is funky but if we don't do it here then there will be problems
        # when "toram" is used.
        if ! ls $base | egrep -q 'etc|bin|lib|var|usr'; then
            #. Copy <linuxfs VID> to empty <rootfs>
            msg  "$_Copy_X_to_empty_Y_" "$LINUXFS_NAME $VID_NAME" rootfs

            log_cmd mkdir -p $(dirname $vid_file)  || return 1
            log_cmd cp $SQFS_VID_FILE $vid_file    || return 2
            sync
            return 0

        elif ! [ -f "$vid_file" ]; then
            #. <type> file not found <file-name>
            vid_error  "$_X_file_not_found_Y_" "$VID_NAME" "$vid_file"
            return 1

        else
            vid_error  "$_No_X_found_in_file_Y_" "$VID_NAME" "$vid_file"
            return 1
        fi

    elif [ "$rootfs_vid" ]; then
        vid_error  "$_No_X_found_but_there_is_a_Y_" "$LINUXFS_NAME $VID_NAME" "rootfs $VID_NAME"
        return 1
    else
        vmsg 6  "No %s or %s found" "$LINUXFS_NAME $VID_NAME" "rootfs $VID_NAME"
        return 0
    fi
}

#------------------------------------------------------------------------------
# Function: check_md5 <dir>
#
# Checks the md5 sum of all files in <dir> that have a <name>.md5 file.
#------------------------------------------------------------------------------
check_md5()
{
    local dir=$1 passed="passed"  failed="failed"
    [ ! -d "$dir" ] && dir=$(echo $1 | sed 's|\(.*\)/.*|\1|')
    dir=$(echo $dir | sed 's|/$||')
    if ! [ -d "$dir" ]; then
        msg $tbar
        err  "Directory %s does not exist" "$(fqe $1)"
        err  "Can't perform %s check" md5
        msg $tbar
        return 0
    fi

    local fcnt=0

    vmsg 2 $tbar
    #. Check <md5> of files in <some> directory
    vmsg_nc 1  "$_Check_X_of_files_in_Y_directory_Please_be_patient_" md5 "$(fq $dir)"

    local file fname md5_file file_md5 true_md5 error
    for fname in $(ls $dir); do
        file=$dir/$fname
        local size=$(du -sm $file | cut -f1)
        md5_file="$file.md5"
        [ -f "$md5_file" -a -f "$file" ] || continue

        local size_str="$nc_co($num_co$size$nc_co M)"
        # vmsg 1
        vmsg 1 $tbar
        vmsg_nc 1  "$_file_X_" "${nc_co}$fname $size_str"
        fcnt=$(( fcnt + 1 ))
        true_md5="$(head -n 1 $md5_file | cut -d" " -f1)"

        (bogo_meter)&
        local pid=$!
        file_md5=$(md5sum $file | cut -d" " -f1)
        kill -9 $pid

        if [ "$file_md5" == "$true_md5" ]; then
            vmsg_nc 1  "$_result_X_" "${ok_co}$passed"
        else
            vmsg_nc 1  "$_result_X_" "${err_co}$failed"
            error=true
        fi

        vmsg 6  "         wanted: %s" "$num_co$true_md5"
        vmsg 6  "            got: %s" "$num_co$file_md5"
    done

    vmsg 1
    if [ "$fcnt" = 0 ]; then
        #. No <md5> checksums were found in <directory>.  Can't do any <md5> checks.
        err  "$_No_X_checksums_were_found_in_Y_Cant_do_any_Z_checks_" md5 "$(fqe $dir)" md5
    elif [ "$error" ]; then
        non_fatal  "$_At_least_one_X_checksum_did_not_match_" md5
    elif [ "$fcnt" = 1 ]; then
        vmsg 1  "$_Success_The_file_passed_"
    else
        #. Success! All <count> file(s) passed
        vmsg 1  "$_Success_All_X_files_passed_" $(nq $fcnt)
    fi
    vmsg 2 $tbar
}

do_delay() {
    local cnt=$1; shift
    [ -z "$cnt" -o "$cnt" = "0" ] && return
    [ $# -gt 0 ] && msg "$@"
    while [ "$cnt" -gt "0" ]; do
        msgN "$hi_co$cnt "
        cnt=$((cnt - 1))
        sleep 1
    done
    msg
}

next_device() {
    local base=$(echo $1 | sed 's/[0-9]*$//')
    local num=$(echo $1 | sed 's/^[^0-9]*//')
    while [ "$num" -lt "20" ]; do
        num=$((num + 1))
        device=$base$num
        [ -b "$device" ] || continue
        echo $device
        return 0
    done
    return 1
}

will_remaster() {
    [ -n "$NO_REMASTER" -o "$ROLLBACK" ] && return 1

    local sqfile_path=${1:-$SQFILE_MP/$SQFILE_PATH}
    local sqfile=$sqfile_path/$SQFILE_NAME
    local old_file=$sqfile.old new_file=$sqfile.new bad_file=$sqfile.bad
    [ -e $old_file -o -e $bad_file ] && return 1
    [ -e $new_file ]
    return $?
}

#------------------------------------------------------------------------------
# Function: do_remaster <dir>
#
# If <dir>/linuxfs.new exists we move:
#       linuxfs     -->  linuxfs.old
#       linuxfs.new -->  linuxfs
#
# If the "rollback" cheatcode is used, we instead move:
#       linuxfs     -->  linuxfs.bad
#       linuxfs.old -->  linuxfs
#------------------------------------------------------------------------------
do_remaster() {
    [ "$NO_REMASTER" ] && return 0
    local sqfile_path=$(dirname $1)
    local sqfile="$sqfile_path/$SQFILE_NAME"
    local old_file="$sqfile.old" new_file="$sqfile.new" bad_file="$sqfile.bad"


    if [ "$ROLLBACK" ]; then
        heading 'remaster %s' "$(cq rollback)"

        if ! [ -f "$old_file" ]; then
            err -n  "The boot parameter %s was given but no %s was found" \
                "$(cqe rollback)" "$(fqe $SQFILE_NAME.old)"

            do_delay 10  "$_Wait_X_seconds_" "${num_co}10$m_co"
            return 1
        fi
        msg $tbar
        warn  "Roll back file %s to %s" "$SQFILE_NAME.old" "$SQFILE_NAME"
        msg $tbar

        local file full
        rm -rf $sqfile_path/xtra.bak $sqfile_path/xtra.bad
        for file in $SQFILE_NAME $MAKE_OLD; do
            full=$sqfile_path/$file
            [ -e $full.bad ] && log_cmd mv -f $full.bad $full.bak
            [ -e $full     ] && log_cmd mv -f $full     $full.bad
            [ -e $full.old ] && log_cmd mv -f $full.old $full
        done

        DID_ROLLBACK=true
        sync
        sleep 2
        return 0
    fi

    [ -e $new_file ] || return 0

    heading 'remaster'

    if [ -e $old_file -o -e $bad_file ]; then
        warn  "$m_co$tbar"
        warn   "Will not remaster"
        warn   "In directory %s one or more of these files already exist"  "$sqfile_path"
        warn  "    $SQFILE_NAME.old"
        warn  "    $SQFILE_NAME.bad"
        warn
        warn   "Run the %s program to fix this problem" 'remaster-live'
        warn  "$m_co$tbar"

        non_fatal
        return 2
    fi
    msg $tbar
    warn  "Remaster the file %s to %s" "$SQFILE_NAME.new" "$SQFILE_NAME"
    msg $tbar

    local file full
    rm -rf $sqfile_path/xtra.old
    for file in $SQFILE_NAME $MAKE_OLD; do
        full=$sqfile_path/$file
        [ -e $full     ] && log_cmd mv -f $full $full.old
        [ -e $full.new ] && log_cmd mv -f $full.new $full
    done

    DID_REMASTER=true
    sync
    sleep 2
    return 0
}

most_block_devices() { find /dev -type b | egrep -v "/ram|/loop|/fd" ; }

#------------------------------------------------------------------------------
# Function: copy_to_ram
#
# Copies the linuxfs file (and a few other files) to RAM
#------------------------------------------------------------------------------
copy_to_ram() {
    local name=$(basename $1) toram_mp=$2

    if _copy_to_ram "$@"; then

        LINUXFS_DEV=
        SQFILE_MP=$toram_mp
        SQFILE_DIR=$toram_mp
        SQFILE_FULL=$toram_mp/$name
        DEFAULT_DIR=$SQFILE_DIR

        log_cmd umount $SQFS_MP
        mount_linuxfs "$SQFS_MP" "$SQFILE_FULL"

    else
        mountpoint -q $toram_mp && log_cmd umount $toram_mp
        [ -d $toram_mp ]        && rmdir $toram_mp
    fi
    breakpoint t "After copy to ram"
}

_copy_to_ram() {
    local from=$1 toram_mp=$2  err_toram=$(cqe toram)
    local name=$(basename $from) dir=$(dirname $from)

    heading  "$_copy_X_file_to_RAM_" "$(fq $name)"
    local real_sq_file=$(readlink -f $from)
    local sq_size_k=$(du -sk $real_sq_file | cut -f 1)

    local format="%15s: %10d k"
    local format2="%s: %10d k"
    vmsg 8 "$format" "$name" $sq_size_k

    local extra ex_full size_k
    for extra in $EXTRA_FILES; do
        ex_full="$(readlink -f $dir/$extra)"
        [ -e "$ex_full" ] || continue
        size_k="$(du -sk $ex_full | cut -f 1)"
        sq_size_k=$((sq_size_k + size_k))
        vmsg 8 "$format" $extra $size_k
        # Total [RAM needed]
        vmsg 8 "$format2" '          Total'  $sq_size_k
    done

    # Extra space here does not waste ram
    local sq_size_m=$((sq_size_k/1000 + 10))

    local ram_needed=$((sq_size_m + MIN_SYS_RAM));

    if [ "$sq_size_m" -gt "$FREE_MEM" ]; then
        err  "Not enough RAM available to do %s" $err_toram
        # Have <size> RAM, needed <size>
        err 'have %s RAM, needed %s' "${num_co}$FREE_MEM ${m_co}M${err_co}" \
            "${num_co}$sq_size_m ${m_co}M"
        return 1
    fi
    if ! mount_tmpfs $toram_mp $sq_size_m toram; then
        err  "Unable to mount %s.  Will not do %s" tmpfs $err_toram
        return 2
    fi

    local orig=$dir
    local dest=$toram_mp

    mkdir -p $dest

    vmsg 3  "$_Will_copy_files_from_X_to_Y_Please_be_patient_" \
        "$(dq $orig)" "$(dq $dest)"

    for extra in $EXTRA_FILES; do
        local full=$(readlink -f $dir/$extra)
        [ -e "$full" ] && cp -a $full $dest
    done
    [ -e $from ] && cp $from.md5 $dest

    if ! cp_file $orig $name $dest 2>&1; then
        err  "Copy failed.  Cannot do %s" $err_toram
        return 3
    fi

    if ! [ -f "$dest/$name" ]; then
        err  "The destination file is missing. The %s has failed." $err_toram
        return 4
    fi

    # Rely on file size in bytes to check for errors
    local orig_bytes=$(stat -c "%s" $real_sq_file)
    local dest_bytes=$(stat -c "%s" $dest/$name)

    if [ "$orig_bytes" -ne "$dest_bytes" ]; then
        err  "Copy was incomplete. The %s failed" $err_toram
        format="%s: ${num_co}%12d${m_co} bytes"
        vmsg 1  "$_Size_of_original_X_" $orig_bytes
        vmsg 1  "$_Size_of_copy_X_" $dest_bytes
        vmsg_nc 1 "$(df -m | grep $toram_mp)"
        return 5
    fi

    msg "${ok_co}%s"  "$_Copy_to_RAM_succeeded_"

    # Reduce FREE_MEM by size of tmpfs
    FREE_MEM=$((FREE_MEM - sq_size_m))

    vmsg 6 '    persistence device: %s' $(pq $PERSIST_DEVICE)
    vmsg 6 '           boot device: %s' $(pq $SQFILE_DEV)

    if ! [ "$PERSIST_DEVICE" -a "$PERSIST_DEVICE" = "$SQFILE_DEV" ]; then
        # remount ro to be on the safe side
        log_cmd mount -o remount,ro $toram_mp
    fi

    DID_TORAM=true
    return 0
}

#------------------------------------------------------------------------------
# Function: prepare_persistence
#
# Parse the persist= bootcode but first set defaults.
#------------------------------------------------------------------------------
prepare_persistence() {
    local param persist_files auto_persist=true

    # pdev= and pdir= etc enable persistence even with no persist= bootcode.
    if [ "$PERSIST_ID" ]; then
        : ${PERSIST:=root,home}
        auto_persist=
    fi

    PERSIST_PATH=${PERSIST_PATH:-$DEFAULT_PERSIST_PATH}

    [ "$PERSIST" ] && ! echo $PERSIST | egrep -q 'home|root|r(,|!|$)|h(,|!|$)' && PERSIST="$PERSIST,home,root"

    will_remaster && WILL_REMASTER=true

    local rootfs_file=rootfs
    [ "$WILL_REMASTER" ] && rootfs_file=rootfs.new

    #----- Parse persist= ---------------------------------------------------------

    for param in ${PERSIST//,/ }; do

        case $param in
            s|static) STATIC_ROOT=true;;
        esac

        # You can't always get what you want ...
        case $param in
            home|home!|h|h!)
                persist_files="$PERSIST_PATH/homefs $persist_files"
                persist_files="$PERSIST_PATH/homefs.new $persist_files"
                WANT_HOMEFS=true ;;

            root|root!|r|r!)
                persist_files="$PERSIST_PATH/$rootfs_file $persist_files"
                WANT_ROOTFS=true ;;

            auto|a)
                AUTO_MAKE_FS=true;;

            usb|hd)
                FROM_PERSIST="$FROM_PERSIST,$param" ;;

            static|s) ;;

            *)
                warn
                #. Invalid <persist> value(s) <bad-values>
                warn  "Invalid %s values(s) %s"  $(cqw persist) "$(cqw $param)"
                warn  "Valid values: %s" "$(cqw auto home home! root root! static usb hd a h h! r r! s)" ;;
        esac

        # And if you try sometime ...
        case $param in
            home!|h!)
                NEED_HOMEFS=true;  NEED_PERSIST=true ;;
            root!|r!)
                NEED_ROOTFS=true;  NEED_PERSIST=true ;;
        esac
    done
    PERSIST_FILES=$persist_files

    [ "$auto_persist" ] || return

    # if we are on a read-only device and persist was set then set default
    # persist label if persist device was not explicitly given
    [ -z "$REMASTERABLE" -a -n "$NEED_PERSIST" ] || return

    AUTO_PERSIST="$DISTRO_NAME-Persist"
    warn
    warn  "Persistence was requested on a read-only boot device"
    warn  "Will search for a persistence device with the label %s" "$(pqw $AUTO_PERSIST)"
    warn
    PERSIST_ID=label="$AUTO_PERSIST"

}

mount_persist_device() {
    local persist_files=$1  from_persist=$2

    [ "$persist_files" ] || return

    # Set persist device to boot device if appropriate
    if [ -z "$from_persist" -a -z "$PERSIST_ID" ]; then

        case $(stat -c %t $SQFILE_DEV) in
         [38]|22|b3|103) ;;
            *) warn  "Can't use %s as a persistence device" $(pqw $SQFILE_DEV)
               return 20 ;;
        esac
        : ${PERSIST_ID:=name=$SQFILE_DEV}
    fi

    heading  "$_Mount_persistence_device_if_needed_"

    ALWAYS_SCAN=$FROM_PERSIST
    FROM_TYPE=${FROM_PERSIST:-usb,hd}

    find_files persist "$persist_files" "$PERSIST_MP" "$PERSIST_ID" "$PERSIST_RETRY"
    local ret=$?

    # FIXME: select persist device here and then run find_files again.
    if [ "$AUTO_PERSIST" -a $ret -ne 0 -a $ret -ne 40 ]; then
        if select_persist_device "$AUTO_PERSIST"; then
            msg "persist-files: $persist_files  id=$PERSIST_ID"
            find_files persist "$persist_files" "$PERSIST_MP" "$PERSIST_ID" "$PERSIST_RETRY"
            ret=$?
        else
            warn  "Disable persistence"
            unset WANT_ROOTFS WANT_HOMEFS NEED_ROOTFS NEED_HOMEFS
            return
        fi

    fi

    if [ $ret -eq 0 ]; then

        PERSIST_MP=$FOUND_MP
        PERSIST_DEVICE=$FOUND_DEV
        PERSIST_UUID=$(device_uuid $PERSIST_DEVICE)
        vmsg 7 "       persist_mp: $PERSIST_MP"
        vmsg 7 "   persist_device: $PERSIST_DEVICE"
        vmsg 7 "     persist_uuid: $PERSIST_UUID"
        PERSIST_FULL_PATH="$PERSIST_MP/$PERSIST_PATH"

    else

        case $ret in
           40) warn  "Could not find any persistence files"  ;;
            *) warn  "Could not find persistence device"     ;;
        esac

        # Could not enable required <type> persistence
        [ "$NEED_ROOTFS" ] && non_fatal 'Could not enable required %s persistence' $(pqh 'root!')
        [ "$NEED_HOMEFS" ] && non_fatal 'Could not enable required %s persistence' $(pqh 'home!')

        unset WANT_ROOTFS WANT_HOMEFS NEED_ROOTFS NEED_HOMEFS
    fi

}

create_persist_files() {
    local mp=$1 path=$2 dir=$1/$2 need_rootfs=$3  need_homefs=$4
    mount -o remount,rw $mp 2>/dev/null
    dir_has_param "$mp" rw || return

    local make_homefs
    if [ "$need_rootfs" ]; then

        # Do we also make the homefs file?
        make_homefs=$WANT_HOMEFS
        [ -e $dir/rootfs -o -e $dir/rootfs.new ] && make_homefs=

        # Default size for rootfs.new is size of rootfs
        local default_size rootfs_file=rootfs
        if [ "$WILL_REMASTER" ]; then
            default_size=$(stat -c %s $dir/rootfs 2>/dev/null)
            msg 'default size: %s Meg' "$(nq $default_size)"
            [ "$default_size" ] && default_size=$((default_size/1024/1024))
            rootfs_file=rootfs.new
        fi

        persist_makefs root $mp $dir $rootfs_file NEED_ROOTFS $default_size

    fi
    #msg "need_homefs:$need_homefs  make_homefs:$make_homefs"
    [ -n "$need_homefs" -o -n "$make_homefs" ] \
        && persist_makefs home $mp $dir homefs NEED_HOMEFS
}

persist_makefs() {
    local type=$1
    _persist_makefs "$@" && return

    warn  "Disable %s persistence" $(pqw $type)
}

_persist_makefs() {
    local type=$1  mp=$2  dir=$3  file=$4  needed_var=$5  orig_def_size=$6  full_file=$3/$4

    local wanted_var=WANT${needed_var#NEED}

    [ -e $full_file ] && return 0

    warn
    warn  "%s persistence was requested but no %s file was found" $(pqw $type) $(pqw $file)
    warn

    eval local needed=\$$needed_var wanted=\$$wanted_var
    unset $needed_var $wanted_var

    local free=$(free_space $mp)
    local avail=$((free - MKFS_FREE_MARGIN))

    vmsg 6 '      Free space: %s Meg' $(nq $free)
    vmsg 6 ' Available space: %s Meg' $(nq $avail)

    [ "$orig_def_size" ] && [ $orig_def_size -gt $avail ] \
        && warn  "Not enough room on Live device to create %s file of size %s Meg" \
            $(fq $file) $(nq $orig_def_size)

    local base_file=${file%%.*}
    local default_size=$(default_makefs_size $avail ${base_file}_SIZE_PARAM $orig_def_size)

    vmsg 6 '    Default size: %s Meg' $(nq $default_size)

    : ${default_size:=-1}


    # Make sure we don't exceed max size of 4G - 1 on vfat file systems
    mkdir -p $dir
    local part_fstype=$(mntpnt_fstype $dir) mkfs_sizes=$MKFS_SIZES vfat
    case $part_fstype in
        vfat) vfat=true ;;
           *) mkfs_sizes="$mkfs_sizes $BIGGER_SIZES" ;;
    esac

    [ "$base_file" = homefs ] && mkfs_sizes="$SMALLER_SIZES $mkfs_sizes"

    local max_mkfs_size=${mkfs_sizes##* }

    # Create a menu of sizes.  Include the default size
    local size sizes default_cnt cnt=1
    for size in $mkfs_sizes; do
        if [ -z "$default_cnt" -a $default_size -gt 0 -a $size -ge $default_size ]; then
            sizes="$sizes $default_size"
            default_cnt=$cnt
        fi
        cnt=$((cnt + 1))
        [ $size -gt $avail ] && break
        [ $size -eq $default_size ] && continue
        sizes="$sizes $size"
    done

    # Include default size at end of menu if needed
    if [ -z "$default_cnt" -a $default_size -gt 0 ]; then
        sizes="$sizes $default_size"
        default_cnt=$cnt
    fi

    if [ -z "$sizes" -o "$default_size" = -1 ]; then
        if [ "$needed" ]; then
            non_fatal  "Not enough space on device to create %s file" "$(pqh $file)"
        else
            warn  "Not enough space on device to create %s file" "$(pqw $file)"
        fi
        return 1
    fi

   # Include all available in menu if it is appropriate
    local max=$(echo "$sizes" | sed 's/.* //')
    [ $max -lt $avail -a $avail -lt $max_mkfs_size ] && sizes="$sizes $avail"

    local fs_type fs_size auto_mode create_mode quit=$(printf  "$_quit_")
    #[ "$needed" ] && quit=

    heading  "$_create_X_persistence_file_" "$(fq $file)"
    vmsg 1  "$_Default_size_X_Meg_Filesystem_type_Y_" $(nq $default_size) $(pq ext4)
    vmsg 1  "$_This_default_size_will_leave_X_Meg_free_" $(nq $((avail - default_size)))
    [ "$vfat" ] &&  warn  "Warning: this can take several minutes per Gig on a %s file system" $(pqw $part_fstype)

    if [ "$AUTO_MAKE_FS" ]; then
        auto_mode=true
    else
        local title=$(printf  "$_Create_X_file_manually_or_automatically_" $(fq $file))
        my_select "$title" create_mode 0 1  "$_create_automatically_"  "$_create_custom_" "$quit"
        case $create_mode in
                             "$_quit_") return 1;;
             "$_create_automatically_") auto_mode=true;;
        esac
    fi

    if [ "$auto_mode" ]; then
        fs_type=ext4
        fs_size=$default_size

    else
        local title=$(printf  "$_Please_select_size_of_X_file_" $(pqw $file))

        echo
        vmsg 1  "$_There_is_X_Meg_available_on_the_device_" $(nq $avail)
        my_select "$title" fs_size 4 "$default_cnt" $sizes "$quit"
        [ "$fs_size" =  "$_quit_" ] && return 1

        vmsg 1  "$_Filesystem_size_X_Meg_" $(nq $fs_size)

        #title=$(printf "Please select file system for %s file" $(pqw $file))
        #echo
        #my_select "$title" fs_type 0 2 ext2 ext3 ext4 "$quit"
        #[ "$fs_type" = "quit" ] && return 1
        fs_type=ext4
    fi
    vmsg 1  "$_Create_X_file_of_type_Y_and_size_Z_Meg_" "$(fq $file)" "$(pq $fs_type)" "$(nq $fs_size)"

    # Now do the actual work

    mount -o remount,rw $SQFILE_DEV 2>/dev/null

    breakpoint m "before makefs"

    [ "$vfat" ] &&  warn  "Warning: this can take several minutes per Gig on a %s file system" $(pqw $part_fstype)
    if ! time_cmd makefs $full_file $fs_type $fs_size $part_fstype; then
        err  "Failed to create filesystem.  Deleting %s file" $(pqe $file)
        rm -f $full_file
        return 3
    fi

    do_fsck "new $file file" $full_file -p filefs || return 1

    eval $needed_var=\$needed
    eval $wanted_var=\$wanted

    return 0
}

makefs() {
    local file=$1  fstype=$2  size=$3  part_fstype=$4  dir=$(dirname $file)
    mkdir -p $dir                                                             || return 1
    local fallocate=$(ld_path_which fallocate)
    [ "$fallocate" ] || part_fstype=other

    case $part_fstype in
        ext4|btrfs) ld_path $fallocate --length ${size}M $file        1>> $MY_LOG 2>>$MY_LOG || return 2 ;;
                 *) dd if=/dev/zero of=$file bs=1M count=0 seek=$size 1>> $MY_LOG 2>>$MY_LOG || return 2 ;;
    esac
    sync
    ld_path /sbin/mkfs.$fstype -q -F $file 1>> $MY_LOG 2>> $MY_LOG || return 3
    return 0
}

default_makefs_size() {
    local avail=$1  size=$3  params
    eval params=\$$2
    eval local $params
    [ "$avail" -lt $mid_size ] && factor=100
    [ "$size" ] || size=$((mid_size + factor * ( avail - mid_size ) / 100))
    [ $size -gt $max_size ] && size=$max_size
    [ $size -gt $avail    ] && size=$avail
    [ $size -lt $min_size ] && return 1
    echo $size
    return 0
}

select_persist_device() {
    local device label=$1
    warn
    warn  "Could not find a partition with label %s" "$(pqw $label)"
    warn
    msg  "$_Please_wait_while_existing_partitions_are_found_"

    breakpoint x "before select device"

    select_device  "$_persistence_" device 100 || return 1
    local ret=$?

    [ -z "$device" -o "$device" =  "$_quit_" ] && return 1

    label_device $device "$AUTO_PERSIST"
    PERSIST_ID=name=$device
    return $ret
}

fsck_persist_dev() {
    local persist_files=$1; shift
    [ "$persist_files" ] || return
    if ! _fsck_persist_dev "$@"; then
        unset WANT_ROOTFS WANT_HOMEFS
    fi
}

_fsck_persist_dev() {
    local dev=$1  mp=$2

    should_fsck $dev persist || return 0

    if ! umount $mp &> /dev/null; then
        vmsg 6  "Not checking persist filesystem because it can't be unmounted"
        return 0
    fi

    do_fsck  "$_persist_device_" $dev -p
    try_mount $dev $mp && return 0

    non_fatal  "Could not remount the persistence device"
    return 1
}

mount_and_copy_rootfs() {
    if _mount_and_copy_rootfs "$@"; then
        PERSIST_ROOT=true
        if [ "$STATIC_ROOT" ]; then
            ROOTFS_DEV=$PERSIST_DEVICE
            msg  "$_Enabled_X_persistence_" "$(cq static root)"
            PERSISTENCE="root,static"
        else
            msg  "$_Enabled_X_persistence_" "$(cq dynamic root)"
            PERSISTENCE="root,dynamic"
        fi
    else

        STATIC_ROOT=
        [ "$NEED_ROOTFS" ] && non_fatal  "Could not enable required %s persistence" "$(pqh root!)"
    fi
}

_mount_and_copy_rootfs() {
    mount_persist_file rootfs "$ROOTFS_MP" root "$WANT_ROOTFS" "$NEED_ROOTFS" || return 1
    check_existing_unionfs $ROOTFS_MP
    PERSIST_ROOT_FULL="$PERSIST_FULL_PATH/rootfs"
    check_vid "$SQFS_VID" "$ROOTFS_BASE" rootfs.ver                           || return 1
    [ "$STATIC_ROOT" ] || copy_persist_to_ram                                 || return 1

    return 0
}


#------------------------------------------------------------------------------
# Function: copy_persist_to_ram
#------------------------------------------------------------------------------

copy_persist_to_ram() {

    vmsg 6  "Put persistent root in RAM (from %s)"  "$ROOTFS_MP"

    local persist_used_k=$(du -sk $ROOTFS_MP | awk '{print $1}')
    local persist_used=$((persist_used_k / 1024))
    local format="%s: $num_co%5d ${m_co}M"

    vmsg 7 "$format" '              Persistence uses' $persist_used

    if [ "$persist_used" -ge "$AUFS_RAM_SIZE" ]; then
        vmsg_nc 3 $tbar
        err   "Not enough RAM to hold persistent root"
        # You need to <live-remaster> or use <static root> persistence
        warn  "You need to %s or use %s persistence" live-remaster "$(cq static root)"
        vmsg_nc 3 $tbar
        return 1
    fi
    vmsg 5  "$_Copy_X_Megabytes_to_RAM_" "${num_co}$persist_used${m_co}"
    if ! cp_dir $ROOTFS_MP $AUFS_RAM_MP 2>&1; then
         err  "Copy persistent root to RAM failed!  Will erase partial copy ..."
         rm -rf $AUFS_RAM_MP/*
         return 2
    fi
    local ram_used=$(used_space $AUFS_RAM_MP)
    # AUFS uses [this much RAM]
    vmsg 7 "$format" '                     AUFS uses' $ram_used
    vmsg 7 "$format" '              Persistence uses' $persist_used
    vmsg 6 "${hi_co}%s"  "Copy persistent root to RAM succeeded"

    DF_ROOTFS=$(df -P -m $ROOTFS_MP | tail -n 1 | sed 's/ \+/ /g')
    sync
    log_cmd umount $ROOTFS_MP
    return 0
}

mount_persist_file() {
    local file=$1  mpnt=$2  type=$3  want=$4  need=$5

    [ "$want" ]              || return 1
    [ "$PERSIST_FULL_PATH" ] || return 1

    local full=$(readlink -f $PERSIST_FULL_PATH/$file)

    if ! [ -f "$full" ]; then
        [ "$need" ] && err  "Could not find file %s for required %s persistence" \
            "$(pqh $file)" "$type"
        return 3
    fi

    if should_fsck $full "$type file"; then
        do_fsck "$file file" $full -p filefs || return 1
    fi

    heading  "$_mount_persistence_file_X_at_Y_" "$(fq $file)" "$(fq $mpnt)"

    mkdir -p $mpnt
    if ! try_file_mount $full $mpnt; then
        #. Could not mount file <filename> for <type> persistence
        err  "Could not mount file %s for %s persistence" "$full" "$type"
        err  "Did not enable %s persistence" "$type"
        return 4
    fi

    return 0
}

#------------------------------------------------------------------------------
# Function: remaster_rootfs <mountpoint> <full_path>
#
# If we did a remaster or rollback on linuxfs then we perform a similar
# operation on the rootfs file if it exists.  We have already remastered
# the rootfs file in the boot directory so if that is the same as this
# (persist) directory we do nothing here.
#------------------------------------------------------------------------------
remaster_rootfs() {
    local mp=$1  path=$2

    if [ "$DID_REMASTER" -o "$DID_ROLLBACK" ]; then
        if [ "$path" = "$BOOT_MP/$SQFILE_PATH" ]; then
            vmsg 6  "Will not remaster %s twice" rootfs
            return
        fi
    fi

    if [ "$DID_REMASTER" ]; then
        [ -e "$path/rootfs"  ]    && mv $path/rootfs     $path/rootfs.old
        [ -e "$path/rootfs.new" ] && mv $path/rootfs.new $path/rootfs
    fi

    if [ "$DID_ROLLBACK" ]; then
        [ -e "$path/rootfs"  ]    && mv $path/rootfs     $path/rootfs.bad
        [ -e "$path/rootfs.old" ] && mv $path/rootfs.old $path/rootfs
    fi
}

#------------------------------------------------------------------------------
# Do the dirty work in addition to mounting homefs: copy homefs to homefs.new
# for easy re-sizing; offer to copy /home files that would be hidden by
# mounting homefs.
#------------------------------------------------------------------------------
mount_homefs() {
    local homefs=$1  real_home=$2  want=$3  need=$4  temp_home=/tmp/home
    [ "$want" ] || return

    # First do the resize copy
    local new=$homefs.new
    if [ -e "$new" ]; then
        copy_homefs "$homefs" || mv $new $homefs.bad
    fi

    mkdir -p $temp_home $real_home
    # Now mount the homefs at a temporary mount point
    if ! mount_persist_file 'homefs' $temp_home 'home' "$want" "$need"; then
        [ "$need" ] && non_fatal  "Could not enable required %s persistence" "$(pqh home!)"
        return
    fi

    if ! offer_copy_home_dir  $real_home  $temp_home; then
        if ! YES_no  "$_Do_you_want_to_enable_home_persistence_anyway_"; then
            log_cmd umount $temp_home
            return
        fi
    fi

    # Move the homefs mount to its real location
    if mount -o move $temp_home $real_home; then
        HOMEFS_DEV=$PERSIST_DEVICE
        msg  "$_Enabled_X_persistence_" "$(cq home)"
        PERSISTENCE="$PERSISTENCE${PERSISTENCE:+,}home"
    else
        err  "$_Strange_couldnt_move_the_homefs_mountpoint_"
        [ "$need" ] && non_fatal  "Could not enable required %s persistence" "$(pqh home!)"
    fi
}

offer_copy_home_dir() {
    local real=$1  temp=$2

    empty_dir $real && return 0
    empty_dir $temp || return 0

    local needed=$(du_size $real)
    local avail=$(avail_space $temp)
    local margin=$((avail - needed))
    msg  "$_There_are_X_Meg_of_files_that_will_get_hidden_by_home_persistence_"  $(nq $needed)
    msg  "$_There_are_X_Meg_free_on_the_home_persistence_device_"                $(nq $avail)

    if [ $margin -lt 50 ]; then
        msg  "$_Theres_not_enough_space_to_copy_all_the_files_"
        return 1
    fi

    YES_no  "$_Do_you_want_to_copy_the_files_to_the_home_persistence_device_" \
        || return 2

    # copy files here
    time_cmd ld_path rsync -aq --delete $real/ $temp/
    local ret=$?

    # FIXME:  Then offer to erase if extra not in linuxfs?
}

empty_dir() {
    local dir=$1
    local cnt=$(ls "$dir" | grep -v "^lost+found$" | wc -l)
    msg "files in $dir: $cnt"

    [ $cnt -eq 0 ]
    return $?
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
copy_homefs() {
    local homefs=$1

    if ! [ -e "$homefs" ]; then
        msg  "$_No_X_file_found_Rename_Y_to_Z_" homefs homefs.new homefs
        mv $homefs.new $homefs
        return 0
    fi

    local cur_home_mp=/tmp/homefs  new_home_mp=/tmp/homefs.new

    log_cmd mkdir -p $cur_home_mp $new_home_mp

    if ! log_cmd mount -o loop $homefs $cur_home_mp; then
        err  "Could not mount %s for copy" homefs
        return 1
    fi

    if ! log_cmd mount -o loop $homefs.new $new_home_mp; then
        err  "Could not mount %s for copy" homefs.new
        return 1
    fi

    local need_meg=$(used_space $cur_home_mp)
    local have_meg=$(all_space $new_home_mp)
    need_meg=$((need_meg + 10))
    vmsg 6  "Have %s Megabytes free.  Need %s" "$(nq $have_meg)" "$(nq $need_meg)"
    if [ "$need_meg" -gt "$have_meg" ]; then
        err  "Not enough room to copy home filesystem"
        return 1
    fi

    msg  "$_Resize_homefs_Copy_contents_of_X_to_Y_" $(pq homefs) $(pq homefs.new)

    msg  "$_Copy_home_filesystem_Please_be_patient_"

    breakpoint r "Before resize homefs rsync"

    time_cmd ld_path rsync -aq --delete $cur_home_mp/ $new_home_mp/
    local ret=$?

    breakpoint r "After resize homefs rsync"

    if [ "$ret" -ne 0 ]; then
        err  "The %s program failed.  Will not resize %s" $(pqe rsync) $(pqe homefs)
        return 1
    fi

    log_cmd umount $cur_home_mp
    log_cmd umount $new_home_mp

    log_cmd mv -f $homefs $homefs.old
    log_cmd mv -f $homefs.new $homefs

    return 0
}

auto_login() {
    local dir file full prog=$1  terms=$2 root=$3
    [ -n "$prog" -a -n "$terms" ] || return

    db_msg "Enable autologin"

    if [ -z "$SYSTEM_D" ]; then
        sed -i.bak -r "/autologin/! s=^([$terms]:[0-9]+:respawn:[^ ]+)=\1 --autologin root=" $root/etc/inittab
        return
    fi

    local term dir sysd_dir=$root/etc/systemd/system
    [ -d $sysd_dir ] || return
    for term in $(echo $terms | sed -r 's/(.)/\1 /g'); do
        dir=$sysd_dir/getty@tty$term.service.d/
        mkdir -p $dir
        cat << AutoLogin > $dir/autologin.conf
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I 38400 linux
AutoLogin

    done
    return

}

fancy_prompt() {
    local prompt=${1:-prompt-fancy}  dir=$2
    local bashrc=$dir/etc/skel/.bashrc  fp_prog=/usr/local/bin/fancy-prompts.bash

    local cmd=${prompt%%\ *}
    [ -r $dir/$fp_prog -a -n "$prompt" ] || return

    if grep -q $cmd $bashrc; then
        db_msg 'fancy prompts were already enabled'
    else
        db_msg 'Enable fancy prompts and other goodies'

        cat << Fancy_Stuff >> $bashrc

#----- Added by live initrd init script
[ -r $fp_prog ] && source $fp_prog
[ -n "\$(alias $cmd)" ] && $prompt
echo \$PATH | grep -q /live/bin || PATH=\$PATH:/etc/live/bin
Fancy_Stuff

    fi

    local file full_file
    for file in .bashrc .profile; do
        full_file=$dir/etc/skel/$file
        [ -f "$full_file" ] && cp $full_file $dir/root
    done
}

copy_xtra() {
    local dir bdir=$1 xtra_dir=$1/xtra tarball=$1/xtra.tgz

    if [ -e $tarball ]; then
        msg 'Unpack xtra tarball'
        gunzip -c $tarball | tar x -C $NEW_ROOT/
    fi

    [ -d "$xtra_dir" ] || return
    local list=$(ls $xtra_dir)
    [ -n "$list" ] || return

    msgN 'Copy xtra file(s) to:'
    for  dir in $list; do
        [ -d "$xtra_dir/$dir" ] || continue
        msgN " $from_co/$dir"
        cp -a $xtra_dir/$dir $NEW_ROOT/
    done
    msg
}

delete_files() {
    local bdir=$1
    local dfile=$bdir/delete.list
    test -e $dfile || return
    msg 'Deleting files listed in %s' $(pq $dfile)
    grep -v "^\s*#" $dfile | sed "s=^\s*/=$NEW_ROOT/=" | grep "/[a-z]" >> $MY_LOG
    rm -rf $(grep -v "^\s*#" $dfile | sed "s=^\s*/=$NEW_ROOT/=" | grep "/[a-z]")
}

ld_path_which() {
    local xdir prog=$1
    for xdir in /sbin /bin /usr/sbin /usr/bin /usr/local/bin; do
        [ -x $LD_ROOT_DIR$xdir/$prog ] || continue
        echo $xdir/$prog
        return 0
    done
    return 1
}

ld_path() {
    local xdir found prog=$1

    [ -n "${prog##/*}" ] && prog=$(ld_path_which $prog)

    if [ -z "$prog" ]; then
        echo "${err_co}Program %s not found under $LD_ROOT_DIR" "$(pqe $1)"  >&2
        return 1
    fi

    shift
    LD_LIBRARY_PATH=$LD_PATH $LD_ROOT_DIR$prog "$@"
}

prep_ld_path() {
    local ld_path arch=$(uname -m)  dir=${1:-$SQFS_MP}

    LD_ROOT_DIR=$dir

    case $arch in
          x86_64) prep_lib64 $dir
                  ld_path=$LIVE_X64_LD_PATH ;;

        i[3-8]86) ld_path=$LIVE_386_LD_PATH ;;

               *) warn 'unknown architecture %s. Assuming %s' $(pqw $arch) $(pqw i686)
                  ld_path=$LIVE_386_LD_PATH;;
    esac

    LD_PATH=$(echo $ld_path | sed -r "s=(^|:)=\\1$dir=g")

    vmsg 8 "LD_PATH: $LD_PATH"
}

prep_lib64() {
    local dir=$1
    mkdir -p /lib64
    local loader name dest
    for loader in $dir/lib64/ld-linux-x86-64*; do
        name=$(basename $loader)
        dest=$(readlink $loader)
        ln -sf $dir$dest /lib64/$name
    done
}

mountpoints_under() {
    local dev mp other ret=1 dir=$1

    while read dev mp other; do
        case $mp in $dir/*)
            echo -n "$mp "
            ret=0 ;;
        esac
    done << Read_Mounts
$(tac /proc/mounts)
Read_Mounts
    return $ret
}

chroot_umount() {
    local mp list i  dir=${1:-$LD_ROOT_DIR}

    [ "$dir" ] || return

    for i in $(seq 1 4); do
        list=$(mountpoints_under $dir) || return 0
        vmsg 7 'umount %s' "$(pq $list)"
        for mp in $list; do
            umount $mp
        done
    done
    list=$(mountpoints_under $dir) || return 0
    err  "Unable to unmount %s" "$(hq $list)"
    return 1
}

confile() {
    local file dir=/live/config
    for file; do
        test -e $dir/$file && return 0
    done
    return 1
}

write_output_files() {
    local dir=$1  out_file=$1/initrd.out
    mkdir -p $dir
    #rm -f $dir/* 2>/dev/null

    [ true ] && cat > $out_file <<Initrd_Out
#$tbar
#              file: $out_file
#      generated by: $ME script
#   sometime around: $(date)
#$tbar
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
AUFS_VID_FILE="$AUFS_MP$VID_FILE"
BOOT_DEV="$SQFILE_DEV"
BOOT_FSTYPE="$BOOT_FSTYPE"
BOOT_MP="$ORIG_SQFILE_MP"
BOOT_UUID="$BOOT_UUID"
DF_ROOTFS="$DF_ROOTFS"
DID_REMASTER="$DID_REMASTER"
DID_ROLLBACK="$DID_ROLLBACK"
DID_TORAM="$DID_TORAM"
DISTRO="$DISTRO_NAME"
DISTRO_CODENAME="$DISTRO_CODENAME"
DISTRO_VERSION="$DISTRO_VERSION"
FULL_DISTRO="$DISTRO_NAME-$DISTRO_VERSION"
INITRD_VERSION="$VERSION"
INITRD_DATE="$VERSION_DATE"
LIVE_DIR="$FINAL_DIR"
PERSISTENCE="$PERSISTENCE"
PERSIST_DEV="$PERSIST_DEVICE"
PERSIST_UUID="$PERSIST_UUID"
PERSIST_DIR="$PERSIST_FULL_PATH"
PERSIST_FILE="$PERSIST_ROOT_FULL"
PERSIST_MP="$PERSIST_MP"
PERSIST_PATH="$PERSIST_PATH"
REMASTERABLE="$REMASTERABLE"
ROOTFS_MP="$ROOTFS_MP"
RW_MODE="$RW_MODE"
SQFILE_DIR="$ORIG_SQFILE_MP/$ORIG_SQFILE_DIR"
SQFILE_FULL="$ORIG_SQFILE_MP/$ORIG_SQFILE_FULL"
SQFILE_NAME="$SQFILE_NAME"
SQFILE_PATH="$SQFILE_PATH"
SQFS_MP="$SQFS_MP"
SQFS_VID_FILE="$SQFS_VID_FILE"
STATIC_ROOT="$STATIC_ROOT"
TORAM_MP="$TORAM_MP"
USER_UID="$USER_UID"
USER_GID="$USER_GID"
VID_ERROR="$VID_ERROR"
VID_FILE="$VID_FILE"
Initrd_Out

    echo "$SQFILE_DEV" > $dir/boot-device

    [ -e /sys/firmare/efi ]                  && touch $dir/uefi
    [ "$DID_TORAM"    ]                      && touch $dir/did-toram
    [ "$REMASTERABLE" ]                      && touch $dir/remasterable
    [ "$PERSIST_ROOT" ]                      && touch $dir/persist-root
    [ "$PERSIST_ROOT" -a -z "$STATIC_ROOT" ] && touch $dir/save-persist
    [ "$PERSIST_ROOT" -a -n "$STATIC_ROOT" ] && touch $dir/static-root
    [ "$FORCE_PASSWD" ]                      && touch $dir/force-passwd
    [ "$DB_PLUS"      ]                      && touch $dir/db+
    [ -n "$BOOT_SAVE" ]                      && touch $dir/bootsave
    [ "$BOOT_CHART"   ]                      && touch $dir/bootchart
    [ "$DO_HWCLOCK"   ]                      && touch $dir/hwclock
    [ "$DO_DEB"       ]                      && touch $dir/deb-install

    echo "$DISTRO_NAME"                             > $dir/distro

    [ "$DID_TORAM" ] && [ "$(stat -c %t $SQFILE_DEV)" = b ] \
        && echo "BOOT_DEV=$SQFILE_DEV" > $dir/toram-eject

    [ -d /sys/class/power_supply ] \
        && grep -q Battery /sys/class/power_supply/*/type 2>/dev/null \
        && touch $dir/laptop
}

old_write_output_files() {
    local dir=$1

    mkdir -p $dir
    [ true ] && cat > $dir/linuxrc.out <<Linuxrc_Out
#$tbar
#              file: $dir/linuxrc.out
# auto-generated by: $ME script
#   sometime around: $(date)
#$tbar
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
AUFS_VID_FILE="$AUFS_MP$VID_FILE"
BOOT_DEV="$SQFILE_DEV"
BOOT_MP="$ORIG_SQFILE_MP"
BOOT_UUID="$BOOT_UUID"
DF_ROOTFS="$DF_ROOTFS"
DID_REMASTER="$DID_REMASTER"
DID_ROLLBACK="$DID_ROLLBACK"
DID_TORAM="$DID_TORAM"
DISTRO="$DISTRO_NAME"
DISTRO_CODENAME="$DISTRO_CODENAME"
DISTRO_VERSION="$DISTRO_VERSION"
FULL_DISTRO="$DISTRO_NAME-$DISTRO_VERSION"
LINUXRC_VERSION="$VERSION"
LINUXRC_DATE="$VERSION_DATE"
PERSISTENCE="$PERSISTENCE"
PERSIST_DEV="$PERSIST_DEVICE"
PERSIST_DIR="$PERSIST_FULL_PATH"
PERSIST_FILE="$PERSIST_ROOT_FULL"
PERSIST_MP="$PERSIST_MP"
PERSIST_PATH="$PERSIST_PATH"
PERSIST_UUID="$PERSIST_UUID"
REMASTERABLE="$REMASTERABLE"
ROOTFS_MP="$ROOTFS_MP"
RW_MODE="$RW_MODE"
SQFILE_DIR="$ORIG_SQFILE_MP/$ORIG_SQFILE_DIR"
SQFILE_FULL="$ORIG_SQFILE_MP/$ORIG_SQFILE_FULL"
SQFILE_NAME="$SQFILE_NAME"
SQFILE_PATH="$SQFILE_PATH"
SQFS_MP="$SQFS_MP"
SQFS_VID_FILE="$SQFS_VID_FILE"
STATIC_ROOT="$STATIC_ROOT"
TORAM_MP="$TORAM_MP"
VID_ERROR="$VID_ERROR"
VID_FILE="$VID_FILE"
Linuxrc_Out

    [ "$DID_TORAM" ] && cat > $dir/toram-eject.conf  <<Toram_Out
BOOT_DEV="$SQFILE_DEV"
Toram_Out

    [ "$REMASTERABLE" ] && cat > $dir/remaster-live.conf  <<Remaster_Out
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
AUFS_VID_FILE="$AUFS_MP$VID_FILE"
BOOT_DEV="$SQFILE_DEV"
BOOT_MP="$ORIG_SQFILE_MP"
DID_REMASTER="$DID_REMASTER"
DID_ROLLBACK="$DID_ROLLBACK"
SQFILE_DIR="$ORIG_SQFILE_MP/$SQFILE_PATH"
SQFILE_FULL="$ORIG_SQFILE_MP/$SQFILE_PATH/$SQFILE_NAME"
SQFILE_NAME="$SQFILE_NAME"
SQFILE_PATH="$SQFILE_PATH"
SQFS_MP="$SQFS_MP"
SQFS_VID_FILE="$SQFS_VID_FILE"
VID_FILE="$VID_FILE"
Remaster_Out

    [ -n "$PERSIST_ROOT" -a -z "$STATIC_ROOT" ] && cat > $dir/persist-save.conf <<Persist_Out
AUFS_MP="$AUFS_MP"
AUFS_RAM_MP="$AUFS_RAM_MP"
DF_ROOTFS="$DF_ROOTFS"
PERSIST_DEV="$PERSIST_DEVICE"
PERSIST_UUID="$PERSIST_UUID"
PERSIST_FILE="$PERSIST_ROOT_FULL"
PERSIST_MP="$PERSIST_MP"
ROOTFS_MP="$ROOTFS_MP"
SQFS_MP="$SQFS_MP"
Persist_Out
}

update_store_file() {
    local disable=$1  enable=$2
    [ "$REMASTERABLE" ] || return

    local no_store_file=$ORIG_SQFILE_MP/$ORIG_SQFILE_DIR/state/nostore
    if [ "$disable" ]; then
        test -e $no_store_file && return
        mkdir -p $(dirname $no_store_file)
        touch $no_store_file

    elif [ "$enable" ]; then
        rm -f $no_store_file
    fi
}

write_log_files() {
    local my_log=$1 final_log=$2 e=$(printf "\e")
    mkdir -p $(dirname $final_log)
    sed -r "s/$e\[[0-9;]+[mK]//g" $my_log > $final_log
    cp $my_log $final_log.color
}

find_init_prog() {
    local d prog  full_prog prog_var=$1  dir=$2
    eval prog=\$$prog_var
    [ "$prog" ] || return 1

    for d in "" /sbin/ /bin/ /usr/sbin/ /usr/bin/ /usr/local/bin/; do
        [ -e $dir$d$prog ] || continue
        full_prog=$dir$d$prog
        eval $prog_var=\$d\$prog
        break
    done

    if [ -z "$full_prog" ]; then
        err 'Could not find program %s. Will use %s' $prog "/sbin/init"
        return 1
    fi

    if [ ! -x $full_prog ]; then
        err 'Program %s is not executable.  Will use %s'  $d$prog "/sbin/init"
        return 1
    fi
    return 0
}

run_init_scripts() {
    local new_root=$1; shift

    mkdir -p $new_root/etc/live/protect $new_root/run/lock

    local script full now last_time=$(get_time)
    for script; do
        full=/live/etc/init.d/$script
        test -x $new_root/$full || continue
        chroot $new_root $full start
        [ "$DB_PLUS" ] || continue
        now=$(get_time)
        vmsg 8 "%25s @ %s seconds" $script $(pq $(get_seconds $((now - last_time))))
        last_time=$now
    done

    #-jbb # Do a persist-save to save new passwords and persist save mode
    #-jbb [ -f $new_root/live/config/new-passwords       ] || return
    #-jbb [ -f $new_root/live/config/persist-save.conf   ] || return

    #-jbb local persist_save=/usr/local/bin/persist-save
    #-jbb [ -x $new_root$persist_save                    ] || return
    #-jbb msg "Do persist-save"
    #-jbb chroot $new_root $persist_save --cli --nolog --quiet
}

# Copy src to dest if they are different
update_file() {
    local src=$1  dest=$2  if_exists=$3
    [ ${#if_exists} -gt 0 -a ! -e $dest ] && return
    test -e $src || return
    diff -q $src $dest &>/dev/null && return
    cp -a $src $dest
}

detect_old_persist_scripts() {
    local root=$1
    local lib_file=$root/usr/local/lib/antiX/antiX-common.sh
    local enabled_file=$root/usr/local/bin/persist-enabled

    if [ -e $lib_file ]; then
        head -n 20 $lib_file | grep -q /etc/live/config || return 0
    fi

    if [ -e $enabled_file ]; then
        grep -q /etc/live/config $enabled_file || return 0
    fi

    return 1
}

late_umount() {
    local dev=$1
    [ "$dev" ] || return

    ## It does not suffice to only disable the liveCD (see below)
    #[ "$TO_RAM_EJECT" ] || return

    local dep
    for dep in $HOMEFS_DEV $ROOTFS_DEV $LINUXFS_DEV; do
        [ "$dep" = "$dev" ] && return
    done

    # Don't umount if not mounted
    cut -d" " -f1 /proc/mounts | grep -q "^$dev$" || return

    # if [ -z "$TO_RAM_EJECT" ]; then
    #     case $(stat -c %t $dev) in
    #         b) msg 'Will not late umount cd/dvd device %s' $(pq $dev)
    #            return ;;
    #     esac
    # fi

    msg  "$_umount_X_" $(pq $dev)
    for i in $(seq 1 5); do
        log_cmd umount $dev 2>/dev/null
        cut -d" " -f1 /proc/mounts | grep -q "^$dev$" || return
        usleep 500000
    done
    err  "$_umount_of_X_failed_" $(pqe $dev)
}

write_ntfs_pids() {
    local file=$1  ntfs_pids=$(pgrep ntfs-3g)
    [ "$ntfs_pids" ] || return
    mkdir -p $(dirname $file)
    echo "$ntfs_pids" > $file
}

prepare_switch_root() {
    local new_root=$1  live_dir=$2  final_live=$3
    local new_live=$new_root${final_live:-$live_dir}
    local root_dir=""

    # FIXME!
    # new_live=$new_root/run/initramfs

    # Now tell kernel where the real modprobe lives
    echo "/sbin/modprobe" > /proc/sys/kernel/modprobe

    # Avoid PID wrap by setting a large max
    echo 4000000 > /proc/sys/kernel/pid_max 2>/dev/null

    mount_tmpfs $new_root/media  10 /media
    mount_tmpfs $new_root/run    10 /run        mode=755,nodev
    mount_tmpfs $new_live        10 $live_dir   mode=755

    ln -s . $new_live/live

    test -e /live/README && cp /live/README $new_live/
    # cp -a /bin/shutdown $new_live/

    mkdir -p $new_live/oldroot

    local mp new
    for mp in $live_dir/*; do
        [ $mp = $new_root ] && continue
        mountpoint -q $mp   || continue
        new=$new_live/$(basename $mp)
        mkdir -p $new
        log_cmd mount --move $mp $new

        # ignore error on read-only filesystems
        # I'm not sure why the chmod is needed on the others but it is.
        chmod 755 $new 2>/dev/null
    done

    # Remove libs, modules, and programs we won't need to save a little RAM
    rm -f $(find $root_dir/lib -type f -o -type l | egrep -v "/(live-init|ld|libc|libm)[.-][^/]*$")

    (cd $root_dir/bin && rm -rf fbcondecor_helper ntfs-3g)
    rm -rf /$root_dir/etc/splash

    # Work around a bug in fbcondecor_helper
    local fbsys=$root_dir/lib/splash/sys
    mountpoint $fbsys &>/dev/null && umount $fbsys

    local dir old new
    for dir in bin config etc lib locale custom menus; do
        old=$root_dir/$dir
        test -e $old || old=$live_dir/$dir
        new=$new_live/$dir
        mkdir -p $new
        cp -a $old/* $new
    done

    system_mount $new_root

    umount /dev/pts

    # Create $live_dir/aufs mount in new_root (for historical reasons)
    mkdir -p $new_live/$AUFS_DIR
    mount --bind $new_root $new_live/$AUFS_DIR

    PATH=$new_live/bin:$PATH

    (cd $new_root && mkdir -p sys proc dev/pts)
}

live_param_filter() { echo $*; }

#==============================================================================
# What was last shall be first
main_wrapper "$@"
#==============================================================================
