#!/bin/bash

#==============================================================================
# live-usb-maker
# A fast and robust program to make full featured antiX/MX live-usbs
#
# (C) 2016 -- 2019 Paul Banham <antiX@operamail.com>
# License: GPLv3 or later
#==============================================================================

      VERSION="2.41.19-2302"
 VERSION_DATE="Sat, 25 Feb 2023 12:45:10 -0500"

        ME=${0##*/}
    MY_DIR=$(dirname "$(readlink -f $0)")
MY_LIB_DIR=$(readlink -f "$MY_DIR/../cli-shell-utils")
   LIB_DIR="/usr/local/lib/cli-shell-utils"

export TEXTDOMAIN="cli-shell-utils"
domain_dir="$MY_DIR/../cli-shell-utils/locale"
test -d "$domain_dir" && export TEXTDOMAINDIR=$domain_dir

#== BEGIN_CONFIG
     ISO_FILE_DIR="%USER_HOME%"
    ISO_FILE_SPEC="*.iso"
      SEARCH_DIRS="%USER_HOME% /media /root"
     SEARCH_DEPTH="4"
        MAX_FILES="20"
     MIN_ISO_SIZE="180M"

        MSDOS_GPT="msdos"
     DEFAULT_SIZE="100%"
         CMD_SIZE=""
        BIOS_SIZE="150"
        UEFI_SIZE="50"
      BIOS_MARGIN="20"
      MAIN_MARGIN="20"
      UEFI_MARGIN="5"

     EXT4_OPTIONS="-m0 -i16384 -J size=32"
       BIOS_LABEL="Live-usb"
        ESP_LABEL="LIVE-UEFI"

          LIVE_MP="/live/boot-dev"
     LINUXFS_NAME="linuxfs"
 MIN_LINUXFS_SIZE="120M"
     DEF_BOOT_DIR="antiX"

          CLONE_DIRS="boot EFI efi"
         CLONE_FILES="cdrom.ico version"
 CLONE_LINUXFS_FILES="linuxfs{,.md5}"
    CLONE_BOOT_FILES="{vmlinuz{,{1..9}},initrd{,{1..9}}.gz}{,.md5,.ver}"

       UEFI_FILES="[Ee][Ff][Ii] boot/{grub,uefi-mt} version"
     UEFI_2_FILES="[Ee][Ff][Ii] version"
       BIOS_FILES="[Ee][Ff][Ii] boot/{syslinux,grub,memtest} antiX/{vmlinuz,initrd}* version"

        GRUB_CONF="boot/grub/grub.cfg"
    EFI_GRUB_CONF="boot/grub/config/efi-grub.cfg"
     DID_EFI_FILE="boot/grub/config/did-efi-grub"

           CHEATS=""
     COLOR_SCHEME="high"
    QUESTION_MODE="default"

          ENCRYPT=""
 PASS_PHRASE_TYPE=""
        LUKS_NAME="live-usb-maker"

EXT_OVERHEAD_FORM="* 26 / 10000 - 2"
 EXT_OVERHEAD_MAX="44"

  PP_MAX_WORD_LEN="8"
     PP_NUM_WORDS="6"
    PP_WORDS_FILE="/usr/share/dict/american-english"
   PP_RAND_SOURCE="/dev/urandom"

  GRAPHICAL_MENUS="true"
  AUTOMOUNT_DELAY="2"
  INITRD_MAX_SIZE="200"
#== END_CONFIG

     MAIN_FS_TYPE="ext4"
     UEFI_FS_TYPE="vfat"

          CP_ARGS="--no-dereference --preserve=mode,links --recursive"
BACKUP_WORDS_FILE="/usr/share/dict/words"
      CRYPT_PROGS="cryptsetup dmsetup"
     PHRASE_FNAME="passphrase"
       SEED_FNAME="random-seed"
    ENCRYPT_FNAME="encrypted"
   ENCRYPT_ENABLE="enable"

         WORK_DIR="/run/$ME"
        SHELL_LIB="cli-shell-utils.bash"
      CONFIG_FILE="/etc/$ME/$ME.conf"
     THE_LOG_FILE="/var/log/$ME.log"
     THE_ERR_FILE="/var/log/$ME.error"
    THE_PROG_FILE="/var/log/$ME.progress"
         LOG_FILE="/dev/null"
         ERR_FILE="/dev/null"
        PROG_FILE="/dev/null"

    EXT4_NO_64BIT="-O ^64bit"

ORDERED_CMDS="partition-clear partition-make makefs-data makefs-main makefs-bios makefs-uefi"
ORDERED_CMDS="$ORDERED_CMDS copy-main check-usb-md5 copy-bios copy-uefi uuids"
ORDERED_CMDS="$ORDERED_CMDS cheats cheats-syslinux cheats-grub install encrypt-main encryption-initrd"
    ALL_CMDS="sizes all partition makefs copy cheats $ORDERED_CMDS"

   ALL_FORCE="automount,flock,makefs,ultra-fit,usb,nofuse,fuse,uuids,nomd5,debug-copy,check-sh,luks1"
   ALL_PAUSE="exit,initrd,copy,uuids"

    LIB_PATH="$MY_LIB_DIR:$LIB_DIR"
        PATH="$MY_LIB_DIR/bin:$LIB_DIR/bin:$PATH"

MADE_BY_FILE="made-by-live-usb-maker"

USB_FORMAT_LIST="vfat exfat ntfs ext4"

SYSLINUX_FILES="chain.c32 gfxboot.c32 vesamenu.c32 ldlinux.c32 libcom32.c32"
SYSLINUX_FILES="$SYSLINUX_FILES libmenu.c32 libutil.c32 linux.c32 menu.c32"
   SYSLINUX_RM="*.c32 ldlinux.sys syslinux.bin isolinux.bin version"

        SWEARS="asshole|bastard|bitch|cock|crap|cunt|damn|dick|douche|fag|fuck|piss|pussy|shit|slut"

     LUKS_TYPE="luks2"

#------------------------------------------------------------------------------
# Show usage and exit
#------------------------------------------------------------------------------
usage() {
    local ret=${1:-0}

cat<<Usage
Usage: $ME [<options>] [default|expert|simple|gui]

Create a live-usb from an iso-file, another live-usb, a live-cd/dvd
or a running live system.  You will be prompted for information that
is not supplied in the command line options.

    default:  default "no" to some questions.
     expert:  default "yes" to some questions.
     simple:  skip some questions
        gui:  non-interactive, disable progress bar, enable progress file

Uses ext4 as the filesystem for the main live-usb partition and adds
a small fat32 file system for booting via UEFI.

This will destroy any existing information on <usb-device>.  The default
partitioning scheme is msdos (due to a bug in some Dell BIOSes).  Use
the  --gpt flag to use gpt partitioning instead.

  --from="iso-file"     Enter an iso file to use as the source
  --from="clone"        clone a running live system.
  --from=clone=<dir>    clone from a mounted live-usb or iso-file.
  --from=<dev>          copy from a livecd/dvd or live-usb

Options:
  -b --bios-size=<xx>   Size of BIOS boot partition when using encryption
  -c --cheat=xxx        Add these cheatcodes to the live-usb
                           Use "off" or "no" to disable cheats menu.
                           Use "on" or "yes"  to show cheat menus without asking
                        Otherwise you will be asked.
  --clone-persist       Clone persistence files
  --clone-persist=<p>   or pass a parameter: h,home:  clone home
                                             r,root:  clone root
  -C --color=<xxx>      Set color scheme to off|low|low2|bw|dark|high
  -E --encrypt          Set up to boot from an encrypted partition
  --encrypt=<flag>      Phasephrase option:
                           ask         Enter the passphrase via the keyboard
                           first-boot  Force user to set phrase on first boot
                           file=xxx    Read phrase from file <xxx>
                           random      Generate a random passphrase
                           random=N    Generate a random passhphrase containing
                                       N words (1 -- 20 allowed)

  -e --esp-size=<xx>    Size of ESP (uefi) partition in MiB (default 50)
  --ext-options=<xx>    Use these options when creating the ext4 filesystem
  --data-first          Place a data partition first on the live-usb
  --data-first=<str>    Specify the file system and or the size (in percent)


    --format            Make a data usb formatted to fat32 by default
    --format=<fs>       Make a data usb formated to the give filesystem

  -f --from=<xxx>       The device, cdrom, or file to make the live-usb from
                        Use "clone" to clone the current live system or use
                        clone=<xxx> to clone another live-usb

  -F --force=<xxx>      Force the options specfied:
                            automount:  don't temporarily disable antiX automounting
                            fuse:       only use fuseiso to mount iso files
                            initrd:     ignore Bash errors when copying initrd from directory
                            makefs:     make the ext4 filesystem even if one exists
                            nofuse:     don't use fuseiso to mount iso files
                            nomd5:      don't check md5 of files copied to live-usb
                            ultra-fit:  don't warn about SanDisk utra-fit devices
                            usb: Ignore usb/removable check
                            luks1:  use luks1 encryption instead of default luks2

  -- --gui-progress           All remaining args are used as a gui progress bar program
                              Example: --gui-progress yad --progress --auto-close
  -g --gpt                    Use gpt partitioning instead of msdos
  -G --graphic-ui             Use the new graphics user interface (default)
     --gpt-pmbr               Set pmbr_boot disk flag (prevents booting via UEFI)
  -h --help                   Show this usage
  -i --initrd=<file>          Update the initrd from using a template-initrd.gz
  -i --initrd=<dir>           Use a directory tree as a template
  -I --ignore-config          Ignore the configuration file
  -k --keep-syslinux          Don't replace the syslinux files
  -L --label=Name             Label ext partition with Name
  -m --msdos                  Use msdos partitioning (default) instead of gpt
  -n --no-prog-bar            Don't show progress *bar* when copying
     --no-clone-persist       Don't clone persistence files even if they exist
     --no-clone-persist=<p>   Or pass a parameter: h,home: don't clone home
                                                   r,root: don't clone root
  -N --numeric-ui             Use the legacy numerical user interface
     --pause                  Wait for user input before exit
     --pause=initrd           Pause after unpacking the initrd.gz file
     --percent-prog           Show progress percentage but no bar
  -p --pretend                Don't run commands that affect the usb device
  -P --progress               Create $THE_PROG_FILE progress *file*
  -q --quiet                  Print less
  -R --reset-config           Write a fresh config file with default options
  -s --size=XX                Percent of usb-device to use (default 100)
  -S --save-boot              Save original boot directory when updating a live-usb
  -t --target=<xxx>           The device to make into a new live-usb
  -u --update                 Only update an existing live-usb
  -v --version                Show version information
  -V --verbose                Print more, show command outputs
  -VV --very-verbose          Also show commands
  -W --write-config           Write a config file preserving current options

Notes:
  - short options stack. Example: -pv is the same as --pretend --verbose
  - options can be intermingled with commands and parameters
  - config file: $CONFIG_FILE
  - the config file will be sourced if it exists
  - it will be created if it doesn't exist
Usage
    exit $ret
}

#------------------------------------------------------------------------------
# Callback routine to evalute some command line args before the root user test.
#------------------------------------------------------------------------------
eval_early_argument() {
    local val=${1#*=}
    case $1 in
      -ignore-config|I) IGNORE_CONFIG=true   ;;
       -write-config|W) WRITE_CONFIG=true    ;;
       -reset-config|R) RESET_CONFIG=true    ;;
        -gpt|-gpt-pmbr) CMD_GPT=true         ;;
           -progress|P) PROGRESS=true        ;;
               -help|h) usage                ;;
            -version|v) show_version         ;;
    esac
}

#------------------------------------------------------------------------------
# Callback routine to evaluate arguments after the root user check.  We also
# need to include the early args to avoid unknown argument errors.
#------------------------------------------------------------------------------
eval_argument() {
    local arg=$1  val=$2
    case $arg in
         -bios-size|b)  BIOS_SIZE=$val                   ;;
         -bios-size=*)  BIOS_SIZE=$val                   ;;
             -cheat|c)  CHEATS="$CHEATS${CHEATS:+ }$val" ;;
             -cheat=*)  CHEATS="$CHEATS${CHEATS:+ }$val" ;;
       -clone-persist)  CLONE_PERSIST=home,root          ;;
     -clone-persist=*)  CLONE_PERSIST=$val               ;;
             -color|C)  COLOR_SCHEME=$val                ;;
             -color=*)  COLOR_SCHEME=$val                ;;
           -encrypt|E)  ENCRYPT=true                     ;;
           -encrypt=*)  ENCRYPT=true
                        PASS_PHRASE_TYPE=$val            ;;
          -esp-size|e)  UEFI_SIZE=$val                   ;;
         -ext-options)  EXT4_OPTIONS=$val                ;;
       -ext-options=*)  EXT4_OPTIONS=$val                ;;
          -esp-size=*)  UEFI_SIZE=$val                   ;;
          -data-first)  DATA_FIRST=choose                ;;
        -data-first=*)  DATA_FIRST=$val                  ;;
             -force|F)  FORCE="$FORCE,$val"              ;;
             -force=*)  FORCE="$FORCE,$val"              ;;
              -format)  FORMAT_USB=choose                ;;
            -format=*)  FORMAT_USB=$val                  ;;
              -from|f)  FROM=$val                        ;;
              -from=*)  FROM=$val                        ;;
       --graphic-ui|G)  GRAPHICAL_MENUS=true             ;;
               -gpt|g)  MSDOS_GPT="gpt"                  ;;
            -gpt-pmbr)  MSDOS_GPT="gpt" ; DO_PMBR=true   ;;
      -gui-progress|-)  END_CMDLINE=true                 ;;
            -initrd|i)  INITRD_FILE=$val                 ;;
            -initrd=*)  INITRD_FILE=$val                 ;;
     -keep-syslinux|k)  KEEP_SYSLINUX=true               ;;
             -label|L)  CMD_LABEL=$val                   ;;
             -label=*)  CMD_LABEL=$val                   ;;
             -msdos|m)  MSDOS_GPT="msdos"                ;;
       -no-prog-bar|n)  NO_PROGRESS_BAR=true             ;;
    -no-clone-persist)  NO_CLONE_PERSIST=home,root       ;;
  -no-clone-persist=*)  NO_CLONE_PERSIST=$val            ;;
               -pause)  PAUSE="$PAUSE${PAUSE:+,}exit"    ;;
        -numeric-ui|N)  GRAPHICAL_MENUS=                 ;;
             -pause=*)  PAUSE="$PAUSE${PAUSE:+,}$val"    ;;
        -percent-prog)  PERCENT_PROG=true                ;;
           -pretend|p)  PRETEND_MODE=true                ;;
          -progress|P)  PROGRESS=true                    ;;
             -quiet|q)  QUIET=true                       ;;
              -size|s)  CMD_SIZE=${val%\%}               ;;
         -save-boot|S)  SAVE_BOOT=true
                        KEEP_SYSLINUX=true               ;;
              -size=*)  CMD_SIZE=${val%\%}               ;;
            -target|t)  TARGET=$val                      ;;
            -target=*)  TARGET=$val                      ;;
            -update|u)  CMD_CMDS="$CMD_CMDS${CMD_CMDS:+ }copy-main check-usb-md5"
                        SAVE_BOOT=true
                        KEEP_SYSLINUX=true               ;;
           -verbose|V)  VERBOSITY=$((VERBOSITY + 1))     ;;
        -very-verbose)  VERBOSITY=$((VERBOSITY + 2))     ;;

       # These are read early.  They are not unknown

     -ignore-config|I)                                   ;;
      -reset-config|R)                                   ;;
      -write-config|W)                                   ;;

              *)  fatal $"Unknown parameter %s" "-$arg"  ;;
    esac
}

#------------------------------------------------------------------------------
# Callback routine for command line arguments that don't start with "-"
#------------------------------------------------------------------------------
assign_parameter() {
   local cnt=$1 param=$2
   case $param in
       default) QUESTION_MODE=default          ;;
        expert) QUESTION_MODE=expert           ;;
        simple) QUESTION_MODE=simple           ;;
           gui) QUESTION_MODE=gui              ;;
             *) CMD_CMDS="$CMD_CMDS${CMD_CMDS:+ }$param" ;;
    esac
}

#------------------------------------------------------------------------------
# Callback routine to see if an argument requires a value to follow it.
#------------------------------------------------------------------------------
takes_param() {
    case $1 in
   -bios-size|b) return 0 ;;
       -cheat|c) return 0 ;;
       -color|C) return 0 ;;
    -esp-size|e) return 0 ;;
        -from|f) return 0 ;;
       -force|F) return 0 ;;
      -initrd|i) return 0 ;;
       -label|L) return 0 ;;
        -size|s) return 0 ;;
      -target|t) return 0 ;;
    esac
    return 1
}

#------------------------------------------------------------------------------
# The main routine.  Called from the very bottom of this script.
#------------------------------------------------------------------------------
main() {
    local SHIFT SHIFT_2 SHORT_STACK="bcCDeEfFgGhIkLmnNpPqRsStuvVW"
    local BE_VERBOSE FROM TARGET FATAL_QUESTION CMD PARAM_CNT ENCRYPT
    local CLONE_PERSIST  NO_CLONE_PERSIST CLONE_HOME  NO_CLONE_HOME
    local CLONE_ROOT  NO_CLONE_ROOT  NO_PROGRESS_BAR  TOTAL_SIZE
    local FORMAT_USB USING_GRUB_CONFIG_2  CRYPT_PART
    local VERY_VERBOSE VERBOSITY=0
    local ISO_BOOT_DIR  START_T=0

    set_colors

    local orig_args="$*"

    # Let non-root users get usage.  Need to get --ignore-config early.
    read_early_params "$@"

    need_root
    EXIT_NUM=100

    read_reset_config_file "$CONFIG_FILE"

    # Strip off leading / once and for all
    DEF_BOOT_DIR="${DEF_BOOT_DIR#/}"

    # Force stored 'gpt' value back to msdos.  Can still be overridden by the
    # --gpt command line parameter
    if [ -z "$CMD_GPT" -a "$MSDOS_GPT" != 'msdos' ]; then
        warn $"Forcing partitioning to be %s" "$(pqh msdos)"
        MSDOS_GPT=msdos
        write_config "$CONFIG_FILE"
    fi

    ERR_FILE=$THE_ERR_FILE
    trap clean_up EXIT
    do_flock

    test -f $ERR_FILE && rm -f $ERR_FILE

    read_all_cmdline_mingled "$@"
    set_colors $COLOR_SCHEME

    force fuse && force nofuse && fatal $"Cannot force both %s and %s" "$(pqw fuse)" "$(pqw nofuse)"

    validate_data_first CMD_SIZE "$DATA_FIRST"

    validate_clone_persist "$CLONE_PERSIST" "$NO_CLONE_PERSIST"

    # Simple sanity check
    :
    [ -n "$FORMAT_USB"  -a -n "$CMD_CMDS"    ] && fatal $"Cannot combine %s with any commands"  "--format"
    [ -n "$INITRD_FILE" -a -n "$CMD_CMDS"    ] && fatal $"Cannot combine %s with any commands"  "--initrd"
    [ -n "$FORMAT_USB"  -a -n "$INITRD_FILE" ] && fatal $"Cannot combine %s with %s" "--format" "--initrd"

    #validate_fs_type "$USB_FORMAT"

    case $VERBOSITY in
        0) ;;
        1) BE_VERBOSE=true                     ;;
        *) BE_VERBOSE=true ; VERY_VERBOSE=true ;;
    esac

    [ -n "$BE_VERBOSE" -a -n "$PRETEND_MODE" ] && VERY_VERBOSE=true

    CMDS=${CMD_CMDS:-all}

    # Let the gui take care of filtering out ultra-fit devices
    q_mode gui && FORCE="$FORCE,ultra-fit"

    check_cmds  CMDS  "$ALL_CMDS" "$ORDERED_CMDS"
    check_force FORCE "$ALL_FORCE"
    check_pause PAUSE "$ALL_PAUSE"

    if [ "$FORMAT_USB" ]; then
        CMDS='format-usb'
        CMD_CMDS="$CMDS"
    elif [ "$INITRD_FILE" ]; then
        CMDS='update-initrd'
        CMD_CMDS="$CMDS"
        test -e "$INITRD_FILE" || fatal $"Could not find initrd file %s" "$(pqw $INITRD_FILE)"
        test -r "$INITRD_FILE" || fatal $"Could not read initrd file %s" "$(pqw $INITRD_FILE)"
    fi

    local percent_size=${CMD_SIZE%%%}
    case $percent_size in
        "") ;;
        [1-9]|[0-9][0-9]|100) ;;
        *) fatal $"Wrong percentage value %s.  Should be between %s and %s inclusive." "$(pqh $percent_size)" "1%" "100%"
    esac

    [ -z "${UEFI_SIZE##[0-9]}" ] && fatal "esp-size must be larger than %s" 9
    echo $UEFI_SIZE | egrep -q "^[1-9][0-9]+$" || fatal "esp-size must be an integer larger than %s" 9

    # Always write a new config file and then exit if requested
    [ "$WRITE_CONFIG" ] && write_config "$CONFIG_FILE" && exit 0

    need_prog extlinux
    q_mode gui && PROGRESS=true
    [ "$PROGRESS" ] && PROG_FILE=$THE_PROG_FILE

    # Make sure we have a --from (if needed) and a --target
    if q_mode gui; then

        case $CMDS in
            partition-clear) ;;
                 format-usb) ;;
              update-initrd) ;;
                          *) fatal_z "$FROM" "No --from was given while in %s mode" gui ;;
        esac
        fatal_z "$TARGET"  "No --target was given while in %s mode" gui
    fi

    shout_title $"Starting %s" "$ME $VERSION"
    set_window_title "$ME $VERSION"
    start_log "$THE_LOG_FILE" "$orig_args"

    # NOTE: this placement is not ideal because it allows the user to
    # write an invalid pass_phrase_type to the config file
    #======== Encryption =====================================================
    if encrypt; then
        q_mode gui && : ${PASS_PHRASE_TYPE:=first-boot}
        validate_passphrase_type "$PASS_PHRASE_TYPE"
    fi
    #=========================================================================

    type -t find_man_page &>/dev/null && find_man_page

    shift $SHIFT_2
    if [ $# -gt 0 ]; then
        type "$1" &>/dev/null || fatal "Could not find progress program %s" "$(pqh $1)"
        printf "Will use progress %s: $*\n" "$(my_type $1)" >> $LOG_FILE
    fi

    shout_pretend

    mkdir -p $WORK_DIR || fatal "Could not make a work directory under %s" "$(dirname "$WORK_DIR")"
    mount -t tmpfs tmpfs $WORK_DIR || fatal "Could not mount tmpfs at %s" $WORK_DIR
    # Set up our directories.  These a GLOBALS so they can be used in clean_up() on exit
    ISO_DIR=$WORK_DIR/iso
    BIOS_DIR=$WORK_DIR/bios
    MAIN_DIR=$WORK_DIR/main
    UEFI_DIR=$WORK_DIR/uefi
    DATA_DIR=$WORK_DIR/data
    INITRD_DIR=$WORK_DIR/initrd
    LINUX_DIR=$WORK_DIR/linux
    MNT_DIR=$WORK_DIR/live-dev
    WORDS_FILE=$WORK_DIR/words
    DATA_DIR=$WORK_DIR/data

    mkdir $ISO_DIR $BIOS_DIR $MAIN_DIR $UEFI_DIR || fatal "Could not make %s subdirectories" "$WORK_DIR"
    echo $$ > $WORK_DIR/pid

    #--- Find live boot device if we are running live

    local we_are_live live_dev
    if its_alive; then
        we_are_live=true
        # FIXME: should use initrd.out to get the uuid, etc
        live_dev=$(get_live_dev)
        if [ -n "$live_dev" ]; then
            shout $"Found live media device %s" "$(pqb /dev/$(get_drive $live_dev))"
        else
            msg $"The live media is not mounted"
        fi
    fi

    # Check cmdline target *before* the first menu
    if [ ${#TARGET} -eq 0 ]; then
        if need_q update-initrd; then
            select_target_device TARGET "" "$FROM"
        else
            select_target_device TARGET "$live_dev" "$FROM"
        fi
    else
        check_target "$TARGET" "$live_dev"
    fi

    if ! force ultra-fit && is_ultra_fit_dev $TARGET; then

        warn $"The selected usb drive was %s" "$(pqw $SANDISK_ULTRA_FIT)"
        warn $"This device is known to cause errors during creation and boot"
        yes_NO $"Do you want to use it anyway?" || exit
    fi

    msg $"Will use target device %s" "$(pq "$TARGET ($(device_info $TARGET))")"
    local target_dev=$(expand_device $TARGET)

    # Make sure our target is a real device
    :
    [ ${#target_dev} -gt 0 ] || fatal $"Could not find device %s" "$TARGET"

    TOTAL_SIZE=$(get_total_size "$target_dev")
    fatal_z "$TOTAL_SIZE" $"Could not get total size of device %s" "$target_dev"

    if [ ${#CMD_CMDS} -eq 0  ] && ! q_mode simple gui; then

        data_first && msg $"Will create a leading data partition"
        encrypt    && msg $"Will create an encrypted live-usb"
        select_overall_mode CMDS QUESTION_MODE
    fi

    if need_q copy && ! encrypt; then
        expert_yes_NO $"Create an encrypted live-usb?" && ENCRYPT=true
    fi

    if need_q copy && ! data_first; then
        expert_yes_NO $"Create a leading data partition on the live-usb?" && DATA_FIRST_FS=choose
    fi


    # FIXME: better error messsage!!
    encrypt && need_prog copy-initrd-programs unpack-initrd cryptsetup dmsetup

    local from

    if [ ${#FROM} -gt 0 ]; then
        check_from from "$FROM" "$TARGET"
    elif ! need_q 'copy-main|copy-bios|copy-uefi'; then
        from='null=null'
    else
    #--- Select source of live-usb if one was not given
        select_usb_src from "$TARGET"
    fi

    local from_file from_act=$"copying"
    case $from in
        iso-file|iso|file)
            cli_get_filename from_file $"Please enter the filename" "$ISO_FILE_DIR"
            from=file=$from_file ;;

        clone)
            #
            if test -e /live/config/encrypted; then
                CLONE_ENCRYPTED_LIVE=true
            fi
            if test -e /live/config/toram-all; then
                from=clone=$TORAM_MP
            else
                from=clone=$LIVE_MP

            fi;;

        clone-toram)
            from=clone=$TORAM_MP ;;

        dev=*|clone=*|file=*)   ;;
        null=*)                 ;;

        *) test -f "$from" && from=file=$from ;;
    esac

    [ -n "$from" -a -n "${from%%*=*}" ] && internal_error "bad from value 1 " "$from"

    local from_type=${from%%=*}
    local from_value=${from#*=}

    # The $from variable must be type=value as seen below.  This is more
    # Complicated but it makes it easier to report to user what is going on
    local from_dev from_thing
    case $from_type in
        file)
            mount_iso_file "$from_value" "$ISO_DIR"
            check_md5 "$from_value"
            # What thing we are copying or cloning from: file, device or directory
            from_thing=$"file"
            ;;

        dev)
            mount_device "$from_value" "$ISO_DIR" 'from'
            # What thing we are copying or cloning from: file, device or directory
            from_thing=$"device"
            ;;

        clone)
            # First see if we were given a block device to clone
            local from_dev=$(expand_device "$from_value")
            # Cloning only copies certain files to make a brand-new live-usb
            from_act=$"cloning"

            # Just clone the directory instead if the device is already mounted
            if is_mounted "$from_dev"; then
                from_value=$(grep "^$from_dev " /proc/mounts | cut -d" " -f2 | head -n1)
                msg $"Will try cloning directory %s" "$(pq "$from_value")"
                from_dev=
            fi

            if [ ${#from_dev} -gt 0 ]; then
                mount_device "$from_dev"  "$MNT_DIR" 'from'
                clone_directory "$MNT_DIR" "$MNT_DIR" "$ISO_DIR"  "$live_dev"
                # What thing we are copying or cloning from: file, device or directory
                from_thing=$"device"

            elif test -d "$from_value"; then

                # If we are cloning an encrypted system then we need to get most
                # files and directories from the boot/bios partition and only get
                # linuxfs and persist files from the main partition (sigh)
                local bios_dir="$from_value"
                if [ "$CLONE_ENCRYPTED_LIVE" ]; then
                    mount_boot_device "$from_value" $BIOS_DIR
                    msg $"Clone running encrypted live system"
                    bios_dir=$BIOS_DIR
                fi

                clone_directory "$from_value" "$bios_dir" "$ISO_DIR"  "$live_dev"
                if [ "$CLONE_ENCRYPTED_LIVE" ]; then
                     cmd umount $BIOS_DIR
		fi
                # What thing we are copying or cloning from: file, device or directory
                from_thing=$"directory"

            else
                fatal $"Can only clone devices and directories, not %s" "$from_value"
            fi
            ;;

        null) ;;
        *)
            internal_error "Bad from variable 2" "$from"
            ;;
    esac

    # Will use source <file XYZ>
    msg $"Will use source %s" "$from_thing $(pq $from_value)"
    show_distro_version "$ISO_DIR" # "$from_value"

    if test -e $ISO_DIR/$EFI_GRUB_CONF; then
        msg $"Found grub config %s"  "$(pq 2.0)"
        USING_GRUB_CONFIG_2=true
    elif test -e $ISO_DIR/$GRUB_CONF; then
        msg $"Found grub config %s"  "$(pq 1.0)"
    fi

    local distro_name=$(get_distro_name "$ISO_DIR/version")
    if [ -n "$distro_name" ]; then
        BIOS_LABEL=$(make_label 16 - "$distro_name" Live usb)
        ESP_LABEL=$(make_label  11 - "$distro_name" uefi)
    fi
    BIOS_LABEL=${CMD_LABEL:-$BIOS_LABEL}

    local cheats
    if need_q cheats; then
        case $CHEATS in
            "") expert_YES_no $"Customize language and timezone?"  && cheats_menus cheats ;;
        off|no) ;;
        yes|on) cheats_menus cheats ;;
             *) cheats=$CHEATS      ;;
        esac
    fi

    # These are mostly for a manually entered target
    :
    test -e $target_dev || fatal $"Target device %s does not exist" $target_dev
    test -b $target_dev || fatal $"Target device %s is not a block device" $target_dev

    # Require that an entire disk device be specified (could relax?)
    local dev_type=$(lsblk -no TYPE --nodeps $target_dev)
    [ "$dev_type" = 'disk' ] || fatal $"Device %s is not a disk device" $target_dev

    setup_devices $target_dev

    # fatal "The device %s does not seem to be usb or removable."
    # FIXME: move this to the lib?
    :
    force usb || is_usb_or_removable $target_dev \
        || yes_NO_fatal "usb" \
        $"Do you want to use it anyway (dangerous)?" \
        $"Use %s to always ignore this warning"      \
        $"The device %s does not seem to be usb or removeable."  "$target_dev"

    local bios_size  main_size  uefi_size

    encrypt && shout "\n%s" $"Encryption enabled"
    need_q partition-make && verify_sizes bios_size main_size uefi_size "$target_dev" \
        "$ISO_DIR" "$percent_size" "$BIOS_SIZE" "$UEFI_SIZE" "${DEFAULT_SIZE%%%}"

    [ ${#cheats} -gt 0 ] && msg "Cheats %s" "$(pq $cheats)"
    # Bail early if only size info is requested
    given_cmd sizes && my_exit 0

    # Make sure the target is not in use
    need_q update-initrd || umount_all $target_dev

    local encrypted_lab=""

    #======== Encryption =====================================================
    encrypt && echo
    if encrypt && need encryption-initrd; then
        encrypted_lab="$(bqq $"encrypted") "
        msg $"Checking to see if the live media will support encryption ..."
        # Need to get programs, libs, and modules, out of the linuxfs file (sigh)
        local linuxfs_full=$ISO_DIR/$DEF_BOOT_DIR/$LINUXFS_NAME
        my_mount "$linuxfs_full" "$LINUX_DIR" -t squashfs -o loop,ro

        if force luks1; then

            LUKS_TYPE="luks1"
            msg "Using Luks version %s" "$(nq $LUKS_TYPE)"
            #local host_cs=$(cryptsetup --version | cryptsetup_major_version)
            #local targ_cs=$(chroot $LINUX_DIR /sbin/cryptsetup --version | cryptsetup_major_version)
            #: ${host_cs:=0}
            #: ${targ_cs:=0}
            #msg "Host using cryptsetup version %s" "$(pq $host_cs.x)"
            #msg "Target using cryptsetup version %s" "$(pq $targ_cs.x)"
            #if [ $host_cs -ge 2 -a $targ_cs -ge 2 ]; then
                #LUKS_TYPE="luks2"
                #msg "Using Luks version %s" "$(nq $LUKS_TYPE)"
            #else
             #   msg "Leaving Luks version at %s" "$(nq $LUKS_TYPE)"
            #fi
        fi
        local initrd_file=$ISO_DIR/$DEF_BOOT_DIR/initrd.gz
        start_initrd_encryption "$initrd_file" "$INITRD_DIR" "$LINUX_DIR"
        pause initrd
        umount $LINUX_DIR ; rmdir $LINUX_DIR

        # FIXME: mount initrd, check init, mount linuxfs and check for cryptsetup

        test -r "$PP_WORDS_FILE" || PP_WORDS_FILE=$BACKUP_WORDS_FILE

        select_passphrase_type PASS_PHRASE_TYPE
    fi

    # FIXME: add sanity checks for "uuids" and for update-initrd
    if [ -n "$FORMAT_USB" ]; then
        makefs_select FORMAT_USB $"data usb-stick"
        msg $"Will create a %s filesystem" "$(pq $FORMAT_USB)"
    elif [ -d "$INITRD_FILE" ]; then

        # Some quick checks
        force check-sh && check_recent_executables "$INITRD_FILE"

        msg $"Will update the initrd from directory %s" "$(pq $INITRD_FILE)"
    elif [ -f "$INITRD_FILE" ]; then
        msg $"Will update the initrd from file %s" "$(pq $INITRD_FILE)"
    elif data_first; then
        makefs_select DATA_FIRST_FS $"data partition"
        msg $"Will create a leading %s data partition" "$(pq $DATA_FIRST_FS)"
    fi

    #=========================================================================

    local new_line=$(printf "$nc_co\n$bold_co...")
    # Ready to make live-usb on device X by <cloning|copying directory Y>
    local final_q=$(printf $"Ready to make %s on device %s by %s" "${encrypted_lab}$(pqb live-usb)" \
        "$(pqb ${target_dev##*/})$new_line" "$from_act $from_thing $(pqb $from_value)")

    local ready_str=$"Ready to perform %s action on %s"
    [ -z "${CMDS##* *}" ] && ready_str=$"Ready to perform %s actions on %s"



    [ "$CMDS" != 'all' ] \
        && final_q=$(printf "$ready_str" "$(pq $CMDS)" "$(pqq ${target_dev##*/})")

    if q_mode gui; then
        msg "$final_q"
    else
        echo
        shout_subtitle "$final_q"
        YES_no_pretend $"Shall we begin?" || my_exit
    fi

    [ -n "$BE_VERBOSE" -a -n "$PRETEND_MODE" ] && VERY_VERBOSE=true

    force automount || suspend_automount

    START_T=$(date +%s)

    if [ "$FORMAT_USB" ]; then
        format_usb "$target_dev" "$MSDOS_GPT"  "$FORMAT_USB"
        say_done
        my_exit 0
    fi

    need partition-clear && clear_partition "$target_dev"
    need partition-make  && do_partition "$target_dev" "$MSDOS_GPT" "$bios_size" "$main_size" "$uefi_size"

    # msg "Unmount new partitions if needed ..."

    #msg $"Wait for automounting ..."
    sync; sync
    #sleep ${AUTOMOUNT_DELAY:-1}

    # Make sure the new target partitions get unmounted if they were auto-mounted
    need_q update-initrd || umount_all $target_dev

    # Nothing else makes sense if there is no partition table
    need_q partition-clear && ! need_q partition-make && exit_done

    if [ "$DATA_FIRST_FS" ] && need makefs-data; then
        cmd wait_for_file "$DATA_DEV"
        make_fs_dev "$DATA_DEV" "$DATA_FIRST_FS" "USB-DATA"
    fi

    cmd wait_for_file "$BIOS_DEV"
    need makefs-bios && do_makefs_ext "$BIOS_DEV" "$EXT4_OPTIONS" "$BIOS_LABEL"

    cmd wait_for_file "$UEFI_DEV"
    need makefs-uefi && do_makefs_uefi "$UEFI_DEV" "$ESP_LABEL"

    #======== Encryption =====================================================
    if encrypt && need encrypt-main; then
        wait_for_file "$MAIN_DEV"

        # Write phrase and signal file *before* encrypting
        my_mount $BIOS_DEV $BIOS_DIR -t $MAIN_FS_TYPE
        local phrase_file=$BIOS_DIR/$DEF_BOOT_DIR/$PHRASE_FNAME
        local encrypt_file=$BIOS_DIR/$DEF_BOOT_DIR/$ENCRYPT_FNAME

        # Simplify to types: ask, file, phrase
        # Convert file to phrase to avoid problems with \n
        local phrase=$PASS_PHRASE_TYPE
        case $phrase in
                  ask)                                            ;;
             phrase=*)                                            ;;
               file=*) phrase=phrase=$(cat "$val")                ;;
               random) set_rand_phrase   phrase                   ;;
             random=*) set_rand_phrase   phrase ${phrase#*=}      ;;
           first-boot) first_boot_phrase phrase "$phrase_file"    ;;
                    *) internal_error "passphrase type" "$phrase" ;;
        esac

        encrypt_partition $MAIN_DEV "$LUKS_NAME" "$phrase"
        check_luks_version "$MAIN_DEV"

        need makefs-main && do_makefs_ext "$LUKS_DEV" "$EXT4_OPTIONS" "$BIOS_LABEL"

        local main_uuid=$(lsblk --nodeps -no UUID $MAIN_DEV)
        cmd write_file $encrypt_file "$main_uuid"

        sync

        DID_ENCRYPT=true

        always_cmd umount $BIOS_DEV
        my_mount "$LUKS_DEV" "$MAIN_DIR" -t $MAIN_FS_TYPE
    fi
    #=========================================================================

    if [ -z "$DID_PARTITION" ]; then
        guess_partitioning $target_dev

        if encrypt; then
            remount_encrypted "$CRYPT_PART" "$LUKS_NAME"
            my_mount "$LUKS_DEV" "$MAIN_DIR"
        fi

        my_mount $BIOS_DEV $BIOS_DIR  -t $MAIN_FS_TYPE
        my_mount $UEFI_DEV $UEFI_DIR  -t $UEFI_FS_TYPE
    else

        sync; sync

        # Tell OS partitioning has changed (after we've created the new file systems)
        cmd partprobe $target_dev

        my_mount $BIOS_DEV $BIOS_DIR  -t $MAIN_FS_TYPE
        my_mount $UEFI_DEV $UEFI_DIR  -t $UEFI_FS_TYPE
    fi

    data_first && my_mount $DATA_DEV $DATA_DIR -t $DATA_FIRST_FS

    local mp_list="$BIOS_DIR $UEFI_DIR"

    encrypt    && mp_list="$BIOS_DIR $MAIN_DIR $UEFI_DIR"
    data_first && mp_list="$DATA_DIR $mp_list"

    display_df_output $mp_list

    if [ "$INITRD_FILE" ]; then
        update_initrd  "$BIOS_DIR" "$MAIN_DIR"  "$INITRD_FILE"
        say_done
        my_exit 0
    fi

    #======== Encryption =====================================================
    if encrypt && need copy-bios; then
        copy_files_spec $ISO_DIR "$BIOS_FILES" "$BIOS_DIR" bios

        #need encryption-initrd &&
        finish_initrd_encryption "$INITRD_DIR" "$BIOS_DIR/$DEF_BOOT_DIR/initrd.gz"

    fi
    #=========================================================================

    if need copy-uefi; then
        if using_grub_config_2; then
            copy_files_spec $ISO_DIR "$UEFI_2_FILES" "$UEFI_DIR" uefi
        else
            copy_files_spec $ISO_DIR "$UEFI_FILES" "$UEFI_DIR" uefi
        fi
        fix_uefi_memtest $UEFI_DIR
    fi

    if need copy-main; then
        do_copy_main $ISO_DIR $MAIN_DIR "$@"
        do_made_by $BIOS_DIR "$MADE_BY_FILE"
        write_random_seed $MAIN_DIR/$DEF_BOOT_DIR/$SEED_FNAME
        test -e "$MAIN_DIR/.disk" && cmd rm -rf "$MAIN_DIR/.disk"

        # Disable initrd encryption when we clone an encrypted system to an
        # non-encrypted system
        if ! encrypt && [ -n "$CLONE_ENCRYPTED_LIVE" ]; then
            msg $"Disabling initrd encryption"
            disable_initrd_encryption $MAIN_DIR/$DEF_BOOT_DIR/initrd.gz $INITRD_DIR
        fi
    fi

    ! force nomd5 && have_usb_md5 "$MAIN_DIR" && need check-usb-md5 && check_usb_md5 "$MAIN_DIR"

    #need defrag && do_defrag "$MAIN_DIR" "$DEFRAG_ALL"

    # if need_q copy-bios || need_q copy-main; then
    #     : ${ISO_BOOT_DIR:=$DEF_BOOT_DIR}
    #     local initrd_file=${INITRD_FILE:-$ISO_DIR/$ISO_BOOT_DIR/initrd.gz}
    #     enable_disable_initrd_encryption "$initrd_file" "$BIOS_DIR/$DEF_BOOT_DIR/initrd.gz" "$INITRD_DIR"
    # fi

    local bios_uuid=$(lsblk -no UUID $BIOS_DEV)
    local uefi_uuid=$(lsblk -no UUID $UEFI_DEV)

    local main_dir
    encrypt && main_dir=$MAIN_DIR
    if need uuids; then
        do_uuids "$BIOS_DIR" "$bios_uuid" "$UEFI_DIR/$GRUB_CONF" "$uefi_uuid" "$UEFI_PART" "$main_dir"
        data_first_uuid "$BIOS_DIR" "$DATA_DEV"  "$main_dir"
    fi

    [ ${#cheats} -gt 0 ] && need cheats-syslinux && do_cheats_syslinux $BIOS_DIR "$cheats"
    [ ${#cheats} -gt 0 ] && need cheats-grub     && do_cheats_grub     $UEFI_DIR "$cheats"

    need install $"install" && do_install_bootloader $target_dev $BIOS_DIR $MSDOS_GPT

    sync; sync

    need_q copy && display_df_output $mp_list

    say_done

    [ -n "$FIRST_BOOT_PHRASE" ] && shout $"You will be asked to create a passphrase during the first boot"

    my_exit 0
}

#===== End of Main ============================================================

#------------------------------------------------------------------------------
# Validate --from parameter give on command line
#------------------------------------------------------------------------------
check_from() {
    local var=$1  cmd_from=$2  exclude=$3
    case $cmd_from in
        iso|file|iso-file) eval $var=\$cmd_from ; return 0 ;;
            clone|clone=*) eval $var=\$cmd_from ; return 0 ;;
    esac
    local from_dev=$(expand_device "$cmd_from")
    if [ ${#from_dev} -gt 0 ]; then

        eval "$var=dev=\$from_dev"
        return 0

    elif test -f "$cmd_from"; then
        eval "$var=file=\$cmd_from"
        return 0
    elif test -d "$cmd_from"; then
        eval "$var=clone=\$cmd_from"
        return 0
    else
        fatal $"The %s parameter %s was not recognized" "--from" "$cmd_from"
    fi
}

#------------------------------------------------------------------------------
# Offer several passphrases.  Let user pick one of them or enter their own
# or force the passphrase to be created on first boot of live-usb
#------------------------------------------------------------------------------
select_passphrase_type() {
    local var=$1  val
    eval val=\$$var

    # Only select a pp type if one was not already given
    [ -n "$val" ] && return

    shout

    local have_words dict_size=0 dict_bits=0
    test -r $PP_WORDS_FILE && dict_size=$(dict_size)

    if [ $dict_size -eq 0 ]; then
        warn "no words were found in the dictionary"
        warn "Will not generate passphrases"

    elif [ $dict_size -lt 5000 ]; then
        warn "only %s words were found in the dictionary" "$(nqh $dict_size)"
        warn "Will not generate passphrases"

    else
        dict_bits=$(x1 "log($dict_size)/log(2)")
        have_words=true
    fi

    # Don't let these menu entries get into the log file
    local VERBOSE_SELECT=

    local phrase  phrase_2  phrase_3   phrase_4   phrase_5   phrase_6
    local   bits    bits_2    bits_3     bits_4     bits_5     bits_6

    # Repeat if user wants to see more passphrases
    while true; do

        local i  len=4  max_len=8  seq=
        if [ "$have_words" ]; then
            shout $"Please wait while some random passphrases are generated"
            msg $"Dictionary size is %s words (%s bits/word)" \
                "$(nq $(add_commas $dict_size))" "$(nq $dict_bits)" | color_commas
            seq=$(seq 2 6)
        fi

        for i in $seq; do
            phrase=$(gen_passphrase $len)
            eval phrase_$i=\$phrase
            bits=$(x1 "log($dict_size)/log(2) * $len")
            eval bits_$i=\$bits
            len=$((len + 1))
            [ $len -gt $max_len ] && len=$max_len
        done

        local menu=$(
            menu_printf ask         $"Enter your own passphrase"
            menu_printf first-boot  $"Set passphrase on first boot of live-usb"
            for i in $seq; do
                eval phrase=\$phrase_$i
                eval bits=\$bits_$i
                menu_printf "index=$i" "%s (%s bits)" "$(bq "$phrase")" "$(nq $bits)"
            done
            [ "$have_words" ] && menu_printf more $"See more passphrases"
        )

        local ans
        my_select ans $"Please select a passphrase for the encrypted live-usb" "$menu"
        [ "$ans" != 'more' ] && break
    done

    case $ans in

        first-boot)
            msg $"You will be asked to create a passphrase during the first boot" ;;

        ask)
            msg $"Soon you will be asked to enter a new passphrase (3 times)" ;;

        index=*)
            i=${ans#*=}
            eval phrase=\$phrase_$i
            say_using_phrase "$phrase"
            ans=phrase=$phrase ;;

        *) fatal_error select-phrase "$ans" ;;
    esac

    eval $var=\$ans
    press_enter
}


#------------------------------------------------------------------------------
# Make sure the passphrase type entered on the command line is valid
#------------------------------------------------------------------------------
validate_passphrase_type() {
    local arg=$1  val=${1#*=}

    case $arg in
                  "")  ;;
          first-boot)  msg $"You will be asked to create a passphrase during the first boot" ;;
                 ask)  msg $"You will be asked to create a passphrase" ;;
              random)  test_rand $PP_NUM_WORDS ;;
            random=*)  test_rand "$val"        ;;
              file=*)  test_phrase_file "$val" ;;
                   *)  fatal "Unknown %s parameter %s" "--encrypt" "$(pqw $arg)"
    esac
}

#------------------------------------------------------------------------------
#  Make sure the number of random words requested is sane
#------------------------------------------------------------------------------
test_rand() {
    local num=$1
    case $num in
        [1-9]|1[0-9]|20) ;;
        *) fatal $"Only %s through %s random words are allowed in passphrase. Invalid arg: %s" 1 20 "$arg"
    esac
    msg $"Will create a random passphrase %s words long" "$(nq $num)"
}

#------------------------------------------------------------------------------
# Test that the passphrase file exists and is reasonable
#------------------------------------------------------------------------------
test_phrase_file() {
    local file=$1
    test -e "$file" || fatal $"Cannot find phrase file %s" "$file"
    test -r "$file" || fatal $"Cannot read phrase file %s" "$file"
    local lines=$(cat "$file" | wc -l)
    case $lines in
        0)  ;;

        1) warn $"Phrase files should not have a trailing new-line character"
           msg  $"Will work around this" ;;

        *) fatal $"Phrase file can have only one line" ;;
    esac
    msg $"Will read passphrase from file %s" "$(pq "$file")"
}

#------------------------------------------------------------------------------
# Set the variable named as the first arg to a random phrase of $len words
#------------------------------------------------------------------------------
set_rand_phrase() {
    local var=$1 len=$2
    local _phrase=$(gen_passphrase $len)
    say_using_phrase "$_phrase"
    press_enter
    eval $var=phrase=\$_phrase
}

#------------------------------------------------------------------------------
# Warn user to remember the new passphrase
#------------------------------------------------------------------------------
say_using_phrase() {
    local phrase=$1

    msg $"Will use phrase %s" "$(bq "$phrase")"
    msg $"Please carefully write it down in a safe place"
    msg $"If you forget this phrase then the live-usb will be useless"
}

#------------------------------------------------------------------------------
# Generate a 2-word passphrase and stick it in $file.  Also put the passphrase
# into the $var variable
#------------------------------------------------------------------------------
first_boot_phrase() {
    local var=$1  file=$2
    local _phrase=$(gen_passphrase 2)
    : ${_phrase:=default-passphrase}
    msg $"Will use initial passphrase %s" "$(pq "$_phrase")"
    msg
    write_phrase_file "$_phrase" "$file"
    eval $var=file=\$file
    FIRST_BOOT_PHRASE=true
    sync
}

#------------------------------------------------------------------------------
# Create a passphrase with $nwords words.  You can also adjust the max word
# length, the dictionary file, and the random source.
#------------------------------------------------------------------------------
gen_passphrase() {
    local nwords=${1:-$PP_NUM_WORDS}  max_len=${2:-$PP_MAX_WORD_LEN}
    local  file=${3:-$PP_WORDS_FILE}      src=${4:-$PP_RAND_SOURCE}

    test -r "$file" || fatal "Could not read words file %s" "$file"

    # pick N words at random
    # then translate new-line to space
    # then convert final space back to new-line
    dict_words "$file" "$max_len" | shuf --repeat --random-source=$src -n$nwords 2>/dev/null \
        | tr '\n' ' ' | sed "s/ $/\n/"
}

#------------------------------------------------------------------------------
# echo the number of words in the dictionary
#------------------------------------------------------------------------------
dict_size() { dict_words "$@" | wc -l ; }

#------------------------------------------------------------------------------
# Do these searches once because they can take half a second or more. Store
# the results in tmpfs.
#------------------------------------------------------------------------------
dict_words() {
    local file=${1:-$PP_WORDS_FILE}  max_len=${2:-$PP_MAX_WORD_LEN}

    test -e $WORDS_FILE || grep -P -v "[^ -~]" "$file" \
        | egrep -v -e ".{$((max_len + 1))}" -e "$SWEARS" -e "'" > $WORDS_FILE

    cat $WORDS_FILE
}

#------------------------------------------------------------------------------
# Get just the major version number from cryptsetup --version output
#------------------------------------------------------------------------------
cryptsetup_major_version() { sed -nr "s/^[a-z]+\s+([0-9]+)\..*/\1/p";  }

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
check_luks_version() {
    local part=$(expand_device "$1")
    if ! test -b "$part"; then
        warn "Encrypted partition %s is not a block device" "$(pqw $part)"
        return 1
    fi
    local luks=$(echo $(dd status=none if=$part bs=1 count=4 | od -An -t x4 --endian=big))
    if [ "$luks" != '4c554b53' ]; then
        warn "%s does not look like a Luks partition.  Found %s" "$(pqw $part)" "$(pqw $luks)"
        return 2
    fi
    local type=$(echo $(dd status=none if=$part bs=1 skip=6 count=2 | od -An -t x2 --endian=big))
    msg "Luks type  %s" "$(nq $(printf "%d" "0x$type"))"
    return 0
}

#------------------------------------------------------------------------------
# Pretty much WYSIWYG
#------------------------------------------------------------------------------
overall_mode_menu() {
    encrypt    || menu_printf simple  $"Make a full-featured live-usb"
    data_first || menu_printf data    $"Start live-usb with a data partition"
    menu_printf encrypt $"Make an encrypted full-featured live-usb"
    menu_printf default $"Make a customized live-usb (includes encryption option)"
    menu_printf other   $"Other options"
}

#------------------------------------------------------------------------------
# Decide what to do.  May need to set CMD and/or QUESTION_MODE
#------------------------------------------------------------------------------
select_overall_mode() {
    local cmd_var=$1 q_var=$2 ans cmd_val q_val

    while true; do
        local title=$"Please select an action to perform"
        local menu=$(overall_mode_menu)
        local not_done=""
        my_select 'ans' "$title" "$menu"
        case $ans in
                      encrypt) q_val=simple ; ENCRYPT=true               ;;
                      default) q_val=$ans                                ;;
                       simple) q_val=$ans                                ;;
                         data) q_val=simple ; DATA_FIRST_FS=choose       ;;
                        other) select_other_mode                         ;;
                            *) internal_error select_overall_mode "$ans" ;;
        esac

        [ "$not_done" ] && continue

        break

    done
        [ ${#cmd_val} -gt 0 ] && eval $cmd_var=\$cmd_val
        [ ${#q_val}   -gt 0 ] && eval $q_var=\$q_val

}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
other_mode_menu() {
    menu_printf quit    $"Go back to main menu"
    menu_printf format  $"Format a data usb"
    menu_printf clear   $"Clear the partition table on a usb device"
    menu_printf update  $"Update the OS on an existing live-usb (experimental)"
    menu_printf uuids   $"Update UUIDs on an existing live-usb"
    menu_printf cheats  $"Update boot parameters on an existing live-usb"
    menu_printf install $"Reinstall legacy bootloader on an existing live-usb"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
select_other_mode() {
    local ans2 title=$"Please select what other action to perform"
    local menu=$(other_mode_menu)
    BACK_TO_MAIN=$"Go back to main menu"
    my_select 'ans2' "$title" "$menu"
    case $ans2 in
                 quit) not_done=true                             ;;
               format) cmd_val=format-usb                        ;;
                clear) cmd_val="partition-clear"                 ;;
               update) cmd_val="copy-main check-usb-md5"
                       SAVE_BOOT=true                            ;;
                uuids) cmd_val=$ans                              ;;
               cheats) cmd_val=$ans ; : ${CHEATS:=yes}           ;;
              install) cmd_val=$ans                              ;;
                    *) internal_error select_other_mode "$ans"   ;;
    esac
    unset BACK_TO_MAIN
}

#------------------------------------------------------------------------------
# Wrapper around cli_live_usb_src_menu() from the lib
#------------------------------------------------------------------------------
select_usb_src() {
    local var=$1  exclude=$2
    local title=$"Please select the source for the new live-usb"
    local menu=$(cli_live_usb_src_menu "$exclude")
    my_select "$var" "$title" "$menu"
}

#------------------------------------------------------------------------------
# Simple menu of usb devices minus the running live-usb device
#------------------------------------------------------------------------------
select_target_device() {
    local var=$1  live_dev=$2  from_dev=${3#clone=}

    if is_mountpoint "$from_dev"; then
        from_dev=$(df $(readlink -f "$from_dev") | tail -n1 | awk '{print $1}')
        msg $"From device %s" "$(pq $from_dev)"
    fi

    local menu=$(cli_drive_menu "$live_dev" "$from_dev")
    local dev cnt=$(count_lines "$menu")

    if ultra_fit_detected; then
        case $cnt in
            0)  warn $"The only usb drive detected was %s" "$(pqw $SANDISK_ULTRA_FIT)"
                warn $"This device is known to cause errors during creation and boot"
                if yes_NO $"Do you want to use it anyway?"; then
                    FORCE="$FORCE,ultra-fit"
                    menu=$(cli_drive_menu "$live_dev" "$from_dev")
                    cnt=$(count_lines "$menu")
                else
                    exit 0
                fi ;;

            *)  warn $"A %s usb drive was detected" "$(pqw $SANDISK_ULTRA_FIT)"
                warn $"This device is known to cause errors during creation and boot"
                if yes_NO $"Do you want it included in the menu anyway"; then
                    FORCE="$FORCE,ultra-fit"
                    menu=$(cli_drive_menu "$live_dev" "$from_dev")
                    cnt=$(count_lines "$menu")
                fi ;;
        esac
    fi

    case $cnt in
        0) fatal $"No available target usb devices were found" ;;
        1) dev=$(echo "$menu" | cut -d"$P_IFS" -f1 | head -n1)
            Msg $"Only one target usb device was found %s" "$(pq $dev)"
           eval $var=\$dev
           return ;;
    esac
    my_select $var $"Please select the target usb device" "$menu"
}

#------------------------------------------------------------------------------
# Work out sizes of the two partitions based on size of device and the user
# parameter cmd_percent.  Allow user to change the size once so we go through
# the loop at most twice.
#------------------------------------------------------------------------------
verify_sizes() {
    local bios_var=$1  main_var=$2  uefi_var=$3  dev=$4  dir=$5
    local cmd_percent=$6  bios_val=${7:-0}  uefi_val=$8  default_size=${10:-100}
    local percent=${cmd_percent:-$default_size}

    # Get total size of target live-usb device
    local total_size=$TOTAL_SIZE

    # Note du will fail when fuseiso is used and there are large files
    local main_needed=${XORRISO_SIZE:-$(du_ap_size_spec $dir "*")}
    local uefi_needed=$(du_ap_size_spec $dir "$UEFI_FILES")
    local bios_needed=$(du_ap_size_spec $dir "$BIOS_FILES")

    if [ $uefi_val -lt $((uefi_needed + UEFI_MARGIN)) ]; then
        local old=$uefi_val
        uefi_val=$((uefi_needed + UEFI_MARGIN))
        warn "uefi size increased from %s to %s MiB" "$(pnh $old)" "$(pnh $uefi_val)"
    fi

    if [ $BIOS_MARGIN -gt 0 -a $bios_val -lt $((bios_needed + BIOS_MARGIN)) ]; then
        local old=$bios_val
        bios_val=$((bios_needed + BIOS_MARGIN))
        warn "bios size increased from %s to %s MiB" "$(pnh $old)" "$(pnh $bios_val)"
    fi

    local did_size
    while true; do
        #--- simple arithmetic ...
        local alloc_val=$((total_size * percent / 100 - 1))
        local main_val=$((alloc_val - uefi_val - bios_val))
        local total_extra=$((total_size - alloc_val))

        local main_needed2=$((main_needed + $(ext_overhead $main_val) ))

        local main_extra=$((main_val - main_needed2 - main_overhead))
        local uefi_extra=$((uefi_val - uefi_needed))
        local bios_extra=$((bios_val - bios_needed))
        local min_percent=$((1 + 100 * (main_needed2 + uefi_val + bios_val + MAIN_MARGIN + UEFI_MARGIN + BIOS_MARGIN)
            / total_size))

        local data_size=0
        if data_first; then
            data_size=$total_extra
            total_extra=0
            alloc_val=$total_size
        fi

        log_it_q echo
        # Table headers for sizes of: entire drive, [data partition] [bios partition], main partition, uefi partition
            log_it_q usb_stats $"entire drive"    "$total_size" "$alloc_val"     "$total_extra"  \
                               $"data partition"  "$data_size"  "0"              "$data_size"    \
                               $"main partition"  "$main_val"   "$main_needed2"  "$main_extra"   \
                               $"bios partition"  "$bios_val"   "$bios_needed"   "$bios_extra"   \
                               $"uefi partition"  "$uefi_val"   "$uefi_needed"   "$uefi_extra"
        echo >>$LOG_FILE

        check_size main $main_extra $MAIN_MARGIN warn
        check_size uefi $uefi_extra $UEFI_MARGIN warn
        check_size bios $bios_extra $BIOS_MARGIN warn

        eval $bios_var=\$bios_val
        eval $main_var=\$main_val
        eval $uefi_var=\$uefi_val

        [ "$did_size" ] && break
        did_size=true
        if [ $min_percent -ge 90 ]; then
            msg $"Already using over 90% of the device"
            break
        fi

        # Only offer to change if a size was not given on the cmdline
        [ ${#cmd_percent} -gt 0 ] && break
        if ! data_first; then
            expert_YES_no $"Do you want to change how much of the device is used?" || break
        fi
        select_partition_size percent "$min_percent" "$total_size"

        alloc_val=$((total_size * percent / 100 - 1))
        # Have set the size to <13%> (<1.20 GiB>)
        msg $"Have set size to %s (%s)" "$(hq $percent%)" "$(nq_label_meg $alloc_val)"
    done

    check_size ext $main_extra $MAIN_MARGIN fatal
    check_size uefi $uefi_extra $UEFI_MARGIN fatal
    check_size bios $bios_extra $BIOS_MARGIN fatal
}

#------------------------------------------------------------------------------
# Rough, empirical forumula for calculating the overhead of the ext4 filesystem
# using our default ext4 settings.
#------------------------------------------------------------------------------
ext_overhead() {
    local size=$1
    local max=$EXT_OVERHEAD_MAX

    local overhead=$(($size $EXT_OVERHEAD_FORM))
    [ $overhead -gt $max ] && overhead=$max
    [ $overhead -lt  0 ] && overhead=0
    echo "Estimated extfs overhead for ${size}M is ${overhead}M" >> $LOG_FILE
    echo $overhead
}

#------------------------------------------------------------------------------
# Wrapper around partition_size_menu() in the lib
#------------------------------------------------------------------------------
select_partition_size() {
    local var=$1  min_size=$2  total_size=$3  max_percent
    data_first && max_percent=90
    local title1=$"Please select how much space to use for the live-usb partitions"
    local menu=$(partition_size_menu "$min_size" "$total_size" $max_percent)
    my_select $var "$title1" "$menu"
}

#------------------------------------------------------------------------------
# Text menus for language and timezone.  Should we add more??
#------------------------------------------------------------------------------
cheats_menus() {
    local var=$1
    local tz_cheat _cheats
    cli_text_menu _cheats lang "Select default Language for the live-usb" \
        "Language implies timezone and other things"

    local blurb=$(printf "Timezones are listed by longitude. %s denotes daylight savings time" "$(pqq "*")")
    cli_text_menu tz_cheat tz "Select Default Timezone for the live-usb" "$blurb"

    [ "$tz_cheat" ] && _cheats="$_cheats${_cheats:+ }$tz_cheat"
    eval $var=\$_cheats
}

#------------------------------------------------------------------------------
# Guess if the target device is an encrypted live-usb or a normal one
#------------------------------------------------------------------------------
guess_partitioning() {
    local drive=$1
    unset ENCRYPT
    local part1=$(get_partition $drive 1)
    local part2=$(get_partition $drive 2)
    local part3=$(get_partition $drive 3)
    local part4=$(get_partition $drive 4)

    local fs1=$(get_fstype "$part1")
    local fs2=$(get_fstype "$part2")
    local fs3=$(get_fstype "$part3")
    local fs4=$(get_fstype "$part4")

    local fs_str="$fs1:$fs2:$fs3:$fs4"
    printf "File systems on device %s\n" "$fs_str" >> $LOG_FILE
    case $fs_str in
                   ext4:vfat:*)                                             ;;
                 *:ext4:vfat:*) DATA_FIRST=true ; DATA_FIRST_FS=$fs1        ;;
       ext4:crypto_LUKS:vfat:*) ENCRYPT=true    ; CRYPT_PART=$part2         ;;
       *:ext4:crypto_LUKS:vfat) ENCRYPT=true    ; CRYPT_PART=$part3
                                DATA_FIRST=true ; DATA_FIRST_FS=$fs1        ;;
            # This does not look like a live-usb device <device signature>
        *)  fatal $"This does not look like a live-usb device %s" "$fs_str" ;;
    esac

    # Get the type of partitioning
    MSDOS_GPT=$(LC_ALL=C parted --script $drive unit MiB print | sed -n "s/^Partition Table: *//p")
    msg $"Found %s partitioning" "$(pq $MSDOS_GPT)"
    encrypt && shout $"Assuming this is an encrypted live-usb"
    setup_devices $drive
}

#------------------------------------------------------------------------------
# Get fstype of a partition.  Be silent on errors
#------------------------------------------------------------------------------
get_fstype() { lsblk --nodeps -no FSTYPE "$1" 2>/dev/null; }
get_uuid()   { lsblk --nodeps -no UUID   "$1" 2>/dev/null; }

#------------------------------------------------------------------------------
# Different actions take place on different partitions depending on if we are
# making an encrypted live-usb or not.
#------------------------------------------------------------------------------
setup_devices() {
    local drive=$1

    if encrypt; then
        local bios_part=1  main_part=2  ;  UEFI_PART=3

        if data_first; then
            bios_part=2
            main_part=3
            UEFI_PART=4
            DATA_DEV=$(get_partition $drive 1)
        fi

        # Get first three partitions (lexically only)
        BIOS_DEV=$(get_partition $drive $bios_part)
        MAIN_DEV=$(get_partition $drive $main_part)
        UEFI_DEV=$(get_partition $drive $UEFI_PART)

        # We need to fix this when setup_devices() is run a 2nd time
        BIOS_DIR=$WORK_DIR/bios

    else
        local main_part=1 ; UEFI_PART=2

        if data_first; then
            main_part=2
            UEFI_PART=3
            DATA_DEV=$(get_partition $drive 1)
        fi


        # Get first two partitions (lexically only)
        MAIN_DEV=$(get_partition $drive $main_part)
        UEFI_DEV=$(get_partition $drive $UEFI_PART)

        BIOS_SIZE=0
        BIOS_MARGIN=0

        BIOS_DIR=$MAIN_DIR
        BIOS_DEV=$MAIN_DEV
    fi
}

#------------------------------------------------------------------------------
# Show fancy df output of list of mountpoints.  Things that aren't mountpoints
# are silently dropped from the list.  The WORK_DIR is removed from the
# mountpoints shown on the screen.
#------------------------------------------------------------------------------
display_df_output() {
    local mp  real_list
    for mp; do
        is_mountpoint $mp && real_list="$real_list $mp"
    done

    [ -z "$real_list" ] && return

    if [ "$QUIET" ]; then
        (echo; df -PTh $real_list; echo) >> $LOG_FILE
    else
        (echo; df -PTh $real_list; echo) | tee -a $LOG_FILE \
           | sed -r -e "s/^(Filesystem.*)/$m_co\1$nc_co/" -e "s|$WORK_DIR/||"
    fi
}

#------------------------------------------------------------------------------
# Write the passphrase to the passphrase file.  The write_file() routine would
# leave the passphrase in the log file.
#------------------------------------------------------------------------------
write_phrase_file() {
    local phrase=$1  pfile=$2
    local dir=$(dirname $pfile)
    cmd mkdir -p $dir
    [ "$PRETEND_MODE" ] || echo -n "$phrase" > $pfile
}

#------------------------------------------------------------------------------
# Remount the main partition from an encrypted live-usb
#------------------------------------------------------------------------------
remount_encrypted() {
    local dev=$1  name=$2
    #echo "dev=$dev  name=$name"

    cmd mkdir -p /run/cryptsetup
    cryptsetup open "$dev" "$name" \
        || fatal $"Could not remount encrypted partition %s" "$(pqw $dev)"

    LUKS_DEV=/dev/mapper/$name
}

#------------------------------------------------------------------------------
# Create an encrypted partition using the supplied phrase-file and name
#------------------------------------------------------------------------------
encrypt_partition() {
    local dev=$1  name=$2  phrase=$3
    test -e $dev || fatal $"Device to encrypt %s does not exist" $dev
    test -b $dev || fatal $"Device to encrypt %s is not a block device" $dev

    msg $"About to encrypt device %s" "$(pq $dev)"

    cmd mkdir -p /run/cryptsetup

    local val=${phrase#*=}  ret
    if [ -z "$PRETEND_MODE" ]; then
        case $phrase in
                ask) shout
                     shout $"We want to overwrite the data on %s" $dev
                     shout $"Type in upper-case %s as suggested" "'YES'"
                     cryptsetup luksFormat --type $LUKS_TYPE $dev ; ret=$?
                     shout $"You will be asked for the passphrase a third time" ;;

             file=*) cat "$val"     | cryptsetup luksFormat --type $LUKS_TYPE --key-file - $dev ; ret=$? ;;

           phrase=*) echo -n "$val" | cryptsetup luksFormat --type $LUKS_TYPE --key-file - $dev ; ret=$? ;;

                  *) internal_error phrase-type "$phrase"
        esac

        [ $ret -eq 0 ] || dmesg_fatal $"Failed to create encrypted device"
    fi

    case $phrase in
           ask) cryptsetup open --type $LUKS_TYPE $dev $name; ret=$? ;;
        file=*) cryptsetup open --type $LUKS_TYPE --key-file "$val" $dev $name; ret=$? ;;

      phrase=*) if YES_no $"Do you want to try entering the new passphrase now?"; then
                    cryptsetup open --type $LUKS_TYPE $dev $name; ret=$?
                else
                    echo -n "$val" | cryptsetup open --type $LUKS_TYPE --key-file - $dev $name; ret=$?
                fi ;;
    esac

    [ $ret -eq 0 ] || dmesg_fatal $"Failed to open encrypted device"

    LUKS_DEV=/dev/mapper/$name
    msg $"Encryption successful"
}

#------------------------------------------------------------------------------
# Mount a device or know the reason why.  Should go in lib?
# any 3rd arg means we are only going read so we can mount something that
# is already mounted
#------------------------------------------------------------------------------
mount_device() {
    local dev=$(expand_device "$1")  dir=$2  opts=${3:+-o ro}
    [ ${#dev} -gt 0 ]     || fatal $"Could not find device %s" "$1"
    mkdir -p "$dir"       || fatal $"Failed to create mountpoint directory %s" "$dir"
    is_mountpoint "$dir"  && fatal $"Directory %s is already a mountpoint"   "$dir"

    [ -z "$3" ] && is_mounted "$dev" && fatal $"Device %s is already mounted" "$dev"

    [ "$(stat -c %t "$dev")" = "b" ] && msg $"Please wait while we mount optical disc at %s" "$(pq $dev)"
    always_cmd mount $opts "$dev" "$dir"
    is_mountpoint "$dir"  || fatal $"Failed to mount device %s at %s" "$dev"   "$dir"
}

#------------------------------------------------------------------------------
# When cloning a live-usb, we only want to copy certain files and ignore all
# others.  We do this by bind mounting only the files and directories we want
# and then copying that over to the target device. White-list not black-list.
# Now for cloning from an encrypted live-usb we clone most files/dirs from the
# bios/boot partition (which is usually just the same as $from_dir most of the
# time).
#------------------------------------------------------------------------------
clone_directory() {
    local from_dir=$1  bios_dir=$2  targ_dir=$3  live_dev=$4
    is_mountpoint "$targ_dir" && fatal $"Directory %s is already a mountpoint" "$targ_dir"

    test -d "$from_dir"       || fatal $"Clone directory %s is not a directory"   "$from_dir"
    test -d "$bios_dir"       || fatal $"Clone directory %s is not a directory"   "$bios_dir"

    #is_mountpoint "$from_dir" || warn  "Clone directory %s is not a mountpoint"  "$from_dir"

    # mounting it as tmpfs makes cleanup trivial (we are still touching files nad
    # making directories
    mount -t tmpfs tmpfs "$targ_dir"

    from_dir=$(readlink -f "$from_dir")
    bios_dir=$(readlink -f "$bios_dir")

    # FIXME: should this error message have a device, not a directory?
    local boot_dir
    # No <file-name> file found in any directory on clone device at <root-directory>
    find_live_boot_dir boot_dir "$from_dir" "$LINUXFS_NAME"
    local ret=$?
    case $ret in
        0) ;;
        1) fatal $"No %s file found in any directory on clone device at %s"     "$LINUXFS_NAME" "$from_dir" ;;
        2) fatal $"No live-boot directory found on clone device at %s"          "$from_dir" ;;
        3) fatal $"Multiple live-boot directories found on clone device at %s"  "$from_dir" ;;
        *) internal_error "find_live_boot_dir" $ret ;;
    esac

    msg $"Live boot directory %s" "$(pq $boot_dir)"
    ISO_BOOT_DIR=$boot_dir

    # Prepare to get all files/dirs from under the boot-directory on a frugal install
    local full_boot_dir="$from_dir/${boot_dir#/}"
    local bios_boot_dir="$bios_dir/${boot_dir#/}"
    local src_root_dir=$bios_dir

    add_persist_files "$from_dir" "$boot_dir" "$live_dev"

    if [ -z "${boot_dir##*Frugal*}" ]; then
        msg $"Cloning from a frugal directory"
        src_root_dir=$full_boot_dir
    fi

    # NOTE:
    # the cd && eval "ls ..." below allows us to use globbing expansions with spaces in paths

    # Bind mount directories
    local dir from dest
    while read dir; do
        [ ${#dir} -gt 0 ] || continue
        from="$src_root_dir/$dir"
        dest="$targ_dir/$dir"
        test -d "$from" || continue
        mkdir -p "$dest" || fatal $"Could not create directory %s" "$dest"
        mount --bind "$from" "$dest"
    done <<Clone_Dirs
$(cd "$src_root_dir" && eval "ls -d $CLONE_DIRS" 2>/dev/null)
Clone_Dirs

    # Bind mount top level files
    local file
    while read file; do
        [ ${#file} -gt 0 ] || continue
        from="$src_root_dir/$file"
        dest="$targ_dir/$file"
        test -f "$from" || continue
        mkdir -p "$(dirname "$dest")" || fatal $"Could not mkdir %s" "$(dirname "$dest")"
        touch "$dest"                 || fatal $"Could not touch file %s" "$dest"
        mount --bind "$from" "$dest"
    done <<Clone_Files
$(cd "$src_root_dir" && eval "ls -d $CLONE_FILES" 2>/dev/null)
Clone_Files

    # Note: we now clone most files from the "bios_dir" which is usually the same
    # as the from_dir.  This allows us to clone running encrypted live-usbs by
    # mounting the bios/boot partition.  This split means we don't have to bind
    # mount to bind mounts and we don't have to touch files or make directories
    # on a real filesystem, only on tmpfs.

    # Bind mount boot-dir linuxfs files
    while read file; do
        [ ${#file} -gt 0 ] || continue
        from="$full_boot_dir/$file"
        dest="$targ_dir/$DEF_BOOT_DIR/$file"

        test -f "$from" || continue
        mkdir -p "$(dirname "$dest")" || fatal $"Could not mkdir %s" "$(dirname "$dest")"
        touch "$dest"                 || fatal $"Could not touch file %s" "$dest"
        mount --bind "$from" "$dest"
    done <<Clone_Linuxfs_Files
$(cd "$full_boot_dir" && eval "ls -d $CLONE_LINUXFS_FILES" 2>/dev/null)
Clone_Linuxfs_Files

    # Bind mount boot-dir boot files
    while read file; do
        [ ${#file} -gt 0 ] || continue
        from="$bios_boot_dir/$file"
        dest="$targ_dir/$DEF_BOOT_DIR/$file"

        test -f "$from" || continue
        mkdir -p "$(dirname "$dest")" || fatal $"Could not mkdir %s" "$(dirname "$dest")"
        touch "$dest"                 || fatal $"Could not touch file %s" "$dest"
        mount --bind "$from" "$dest"
    done <<Clone_Bios_Files
$(cd "$bios_boot_dir" && eval "ls -d $CLONE_BOOT_FILES" 2>/dev/null)
Clone_Bios_Files
}

#------------------------------------------------------------------------------
# Mount the boot partition if we are cloning from an encrypted live-usb
#------------------------------------------------------------------------------
mount_boot_device() {
    main_dir=$1  bios_dir=$2
    local bios_uuid_file=$main_dir/antiX/bios-dev-uuid
    test -r "$bios_uuid_file" || fatal "Could not find %s file" bios-dev-uuid
    local uuid=$(head -n1 "$bios_uuid_file" 2>/dev/null)
    [ -z "$uuid" ] && fatal "Could not get uuid of existing bios/boot partition"
    local boot_dev=$(blkid -U $uuid)
    # FIXME: should we give them a chance to plug it in???
    [ -z "$boot_dev" ] && fatal "Could not find boot partition with uuid %s" "$uuid"
    mount_if_needed "$boot_dev" "$bios_dir"
}


#------------------------------------------------------------------------------
# Add persistence files to list of files that get bind mounted when we clone.
# Do not add a persistence file if it is in use.  Otherwise show the size and
# ask the user if they want to include it.  The behaviour is also controlled
# by the --clone-persist and --no-clone-persist parameters.
#------------------------------------------------------------------------------
add_persist_files() {
    local from_dir=$1  boot_dir=$2  live_dev=$3
    local full_boot_dir="$from_dir/${boot_dir#/}"

    # See if live device is mounted at $from_dir
    local from_live from_dev
    if [ -n "$live_dev" ] && is_mountpoint "$from_dir"; then
        from_dev=$(df "$from_dir" | tail -n1 | awk '{print $1}')
        [ "${from_dev##*/}" = "$live_dev" ] && from_live=true
    fi
    local full_root="$full_boot_dir/rootfs"
    if [ -z "$NO_CLONE_ROOT" -a -e "$full_root" ]; then
        if [ -n "$from_live" -a -e '/live/config/static-root' ]; then
            warn $"Not cloning active %s persistence file"  "$(pqh static root)"
        else
            add_one_persist_file "$CLONE_ROOT" "$full_root"
        fi
    fi

    local full_home="$full_boot_dir/homefs"
    if  [ -z "$NO_CLONE_HOME" -a -e "$full_home" ]; then
        if [ -n "$from_live" ] && is_mountpoint '/home'; then
            warn $"Not cloning active %s persistence file"  "$(pqh home)"
        else
            add_one_persist_file "$CLONE_HOME" "$full_home"
        fi
    fi
}

#------------------------------------------------------------------------------
# Move common code from add_persist_files() into this routine
#------------------------------------------------------------------------------
add_one_persist_file() {
    local flag=$1  full=$2  name=${2##*/}
    local du_opts="--apparent-size --human-readable"
    local size=$(du $du_opts "$full" | cut -f1)

    # Found (<size>) <name> persistence file
    msg $"Found (%s) %s persistence file" "$(nq $size)" "$(pq $name)"
    if [ "$flag" ] || q_mode gui || YES_no $"Do you want to clone this file?"; then
        msg $"Will clone %s persistence file" "$(pq $name)"
        CLONE_LINUXFS_FILES="$CLONE_LINUXFS_FILES $name"
    else
        msg $"Will skip %s persistence file" "$(pq $name)"
    fi
}

#------------------------------------------------------------------------------
# Check the validity of the --clone-persist and --no-clone-persist options.
# Catch invalid =xxx params and catch contradictions.
#------------------------------------------------------------------------------
validate_clone_persist() {
    local clone=$1  no_clone=$2

    local p
    for p in ${clone//,/ }; do
        case $p in
            h|home) CLONE_HOME=true ;;
            r|root) CLONE_ROOT=true ;;
                 *) fatal $"Unknown %s parameter: %s" "--clone-persist" "$p" ;;
        esac
    done

    for p in ${no_clone//,/ }; do
        case $p in
            h|home) NO_CLONE_HOME=true ;;
            r|root) NO_CLONE_ROOT=true ;;
                 *) fatal $"Unknown %s parameter: %s" "--no-clone-persist" "$p" ;;
        esac
    done

    [ -n "$CLONE_HOME" -a -n "$NO_CLONE_HOME" ] && fatal $"Can't both clone and not clone %s"  "$(pqw home)"
    [ -n "$CLONE_ROOT" -a -n "$NO_CLONE_ROOT" ] && fatal $"Can't both clone and not clone %s"  "$(pqw root)"
}

#------------------------------------------------------------------------------
# Validate and process --data-first value
# Make sure we can make the file system and make sure one and only one size
# was give either --data-first=XX% or --size=YY%
#------------------------------------------------------------------------------
validate_data_first() {
    local size_var=$1  data_first=$2

    data_first || return

    eval local size_val=\$$size_var

    # Extract fs-type and size
    local param data_size
    for param in ${data_first//,/ }; do
        case $param in
                "")                         ;;
            [0-9]*)  data_size=${param%%%}  ;;
                 *)  DATA_FIRST_FS=$param   ;;
        esac
    done

    case $DATA_FIRST_FS in
        fat32) DATA_FIRST_FS=vfat ;;
    esac

    : ${DATA_FIRST_FS:=choose}

    # Validate the size (we already stripped off the "%"
    case $data_size in
                      "") ;;
        [0-9]|[0-9][0-9]) ;;
        *) fatal $"Invalid %s size specification %s" "$(pqw --data-first)" "$(pqw $data_size)" ;;
    esac

    q_mode gui && fatal_z "$size_val$data_size" \
        "A size must be specified to use the %s option" "$(pqw --data-first)"

    # Make sure we can create the file system type requested
    # Postpone this check until after we've offered the menu if needed

    #validate_fs_type "${DATA_FIRST_FS:=choose}"

    # Cannot specify the size in two different ways!
    [ -n "$data_size" -a -n "$size_val" ] \
        && fatal "Cannot combine %s with %s" "$(pqw --data-first=size)" "$(pqw --size)"

    [ -z "$data_size" ] && return

    : ${size_val:=$((100 - data_size))}

    eval $size_var=\$size_val
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
validate_fs_type() {
    local fstype=$1
    [ -z "$fstype" ]  && return 0

    case $fstype in
        fat32) fstype=vfat ;;
    esac

    which "mkfs.$fstype" &>/dev/null && return 0

    fatal $"Don't know how to create a %s filesystem" "$(pqw $fstype)"
}

#===== "REAL" WORK STARTS HERE ================================================


#------------------------------------------------------------------------------
# Clear out previous partition tables.
# Note we use 512 byte blocks because some devices have an odd number of such
# blocks.  This mostly matters when we write to the end of the device to clear
# the 2nd gpt partition table.
#------------------------------------------------------------------------------
clear_partition() {
    local dev=$1
    local bytes=$(LC_ALL=C parted --script $dev unit B print 2>/dev/null | sed -rn "s/^Disk.*: ([0-9]+)B$/\1/ip")
    local block_size=512
    local pt_size=$((17 * 1024))
    local pt_cnt=$((pt_size / block_size))
    local sneaky_bytes=$((32 * 1024))
    local sneaky_offset=$((sneaky_bytes / block_size))

    local total_blocks=$((bytes / $block_size))
    echo -e "Total bytes: $bytes\nTotal blocks: $total_blocks" >> $LOG_FILE

    # Clear out previous primary partition table
    cmd_dd if=/dev/zero of=$dev bs=$block_size count=$pt_cnt

    # Clear out sneaky iso-hybrid partition table
    cmd_dd if=/dev/zero of=$dev bs=$block_size count=$pt_cnt seek=64

    [ -n "$bytes" ] || return
    local offset=$((total_blocks - $pt_cnt))

    # Clear out secondary gpt partition table
    cmd_dd conv=notrunc if=/dev/zero of=$dev bs=$block_size count=$pt_cnt seek=$offset

    # Tell kernel the partition table has changed
    cmd partprobe $dev
}

#------------------------------------------------------------------------------
# Partition the target usb device.  Will make either 3 or 2 partitions
# depending on whether encryption is used as signaled by the zero size of the
# bios partition.
#------------------------------------------------------------------------------
do_partition() {
    local dev=$1  type=${2:-msdos}  bios_size=${3:-0}  main_size=$4  uefi_size=$5

    local boot_flag
    case $type in
          gpt) boot_flag=legacy_boot ;;
        msdos) boot_flag=boot        ;;
            *) fatal "Unknown partitioning scheme: %s.  Expected msdos or gpt" "$type"
               ;;
    esac

    # Using <gpt|msdos> partitioning
    msg $"Using %s partitioning" $(pq $type)
    local err_msg=$"Partitioning failed at %s"
    local PREAMB="parted --script --align optimal $dev unit MiB"
    cmd $PREAMB mklabel $type || dmesg_fatal "$err_msg" making partition

    local START=1  PART_NUM=0  main_fs=ext4

    if data_first; then
        local total_size=$TOTAL_SIZE
        local remaining=$((total_size - uefi_size - main_size - bios_size -1))
        local fs_type=${DATA_FIRST_FS:-fat32}
        _add_partition "$remaining" $fs_type data
    fi

    if [ $bios_size -gt 0 ]; then
        _add_partition $bios_size bios
        cmd $PREAMB set $PART_NUM $boot_flag on || dmesg_fatal "$err_msg" boot-flag-1
        main_fs=
    fi
    _add_partition "$main_size" $main_fs  main
    if ! encrypt; then
        cmd $PREAMB set $PART_NUM $boot_flag on || dmesg_fatal "$err_msg" boot-flag-2
    fi
    _add_partition "$uefi_size" fat32  uefi
    cmd $PREAMB set $PART_NUM esp on || dmesg_fatal "$err_msg" esp-flag

    if [ "$type" = 'gpt' -a "$DO_PMBR" ]; then
        cmd $PREAMB disk_set pmbr_boot on || dmesg_fatal "$err_msg" disk-set
    fi

    # Tell kernel the partition table has changed
    cmd partprobe $dev

    DID_PARTITION=true
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
_add_partition() {
    local fs_type=$1 size=$1  fs_type=$2  name=$3
    local err_msg=$"partitioning failed at %s partition"
    fatal_z "$START" "internal error: no start was specified"

    local part_fstype
    case $fstype in
        ext[234]) part_fstype=$fstype ;;
            vfat) part_fstype=fat32   ;;
            ntfs) part_fstype=NTFS    ;;
    esac

    clear_filesystem $dev $START
    local end=$((START + size - 1))
    cmd $PREAMB mkpart primary $part_type $START $end || dmesg_fatal "$err_msg" "$name"
    START=$((end + 0))
    PART_NUM=$((PART_NUM + 1))
}

#------------------------------------------------------------------------------
# Gives the size in MiB
# Ask for bytes and divide by 1 MiB to ensure we round down.
#------------------------------------------------------------------------------
get_total_size() {
    local dev=$1
    local bytes=$(LC_ALL=C parted --script $dev unit B print 2>/dev/null | sed -rn "s/^Disk.*: ([0-9]+)B$/\1/p")
    [ -z "$bytes" ] && return
    echo $((bytes / 1024 / 1024))
}

#------------------------------------------------------------------------------
# Zero out the first 128K of each filesystem before we created its partition
# table entry
#------------------------------------------------------------------------------
clear_filesystem() {
    local dev=$1  offset_m=$2  size=128
    local offset_k=$((offset_m * 1024))
    cmd_dd if=/dev/zero of=$dev seek=$offset_k bs=1K count=$size
    sync
}

#------------------------------------------------------------------------------
# Make the ext4 file system
#------------------------------------------------------------------------------
do_makefs_ext() {
    local dev=$1  options=$2  label=$3  force_ext

    force makefs && force_ext="-F"
    cmd mkfs.ext4 $force_ext $EXT4_NO_64BIT $options $dev \
        || dmesg_fatal 'makefs' $"Could not make %s file system on %s" 'ext4' "$dev"

    [ -n "$label" ] && cmd tune2fs -L "${label:0:16}" $dev
}

#------------------------------------------------------------------------------
# Make the fat32 file system
# Note: we convert the label to upper case to prevent whining
#------------------------------------------------------------------------------
do_makefs_uefi() {
    local uefi_dev=$1  label=${2:-LIVE-ESP}
    cmd mkfs.vfat -F 32 -n "${label^^}" $uefi_dev \
        || dmesg_fatal $"Could not make %s file system on %s" "fat32" "$uefi_dev"
}

#------------------------------------------------------------------------------
# Copy from mounted source (or the clone) to the ext4 partition
#------------------------------------------------------------------------------
do_copy_main() {
    local from=$1  to=$2  ;  shift 2

    if test -d $to/antiX; then
        msg "Removing existing %s directory" "$(pq antiX/)"
        cmd rm -rf $to/antiX
    fi

    cmd rm -rf $to/boot.orig

    if [ -d $to/boot -a -n "$SAVE_BOOT" ]; then
        msg "Saving existing boot directory"
        cmd mv $to/boot $to/boot.orig
    fi

    msg $"copy from %s to %s partition" "$(pq $(basename $from))" "$(pq $(basename $to))"

    local err_msg=$"Error while copying files to main partition of live usb"

    if [ $# -gt 0 ]; then
        copy_with_progress "$from" "$to" "$err_msg" "$@"

    elif q_mode gui || [ "$NO_PROGRESS_BAR" ]; then
        copy_with_progress "$from" "$to" "$err_msg"

    elif [ "$PERCENT_PROG" ]; then
        copy_with_progress "$from" "$to" "$err_msg" percent_progress

    else
        local PROGRESS_SCALE=1000
        copy_with_progress "$from" "$to" "$err_msg" text_progress_bar
    fi

    if test -d $to/boot.orig;  then
        msg "Restoring original boot directory"
        cmd rm -rf $to/boot
        cmd mv $to/boot.orig $to/boot
    fi

    sync ; sync
    pause copy
}

#------------------------------------------------------------------------------
# See if there are .md5 files under the given directory
#------------------------------------------------------------------------------
have_usb_md5() {
    local dir=$1
    [ -n "$(find $dir -maxdepth 4 -name "*.md5" -printf ".")" ]
    return $?
}

#------------------------------------------------------------------------------
# Check the integrity of all files listed in .md5 files
#------------------------------------------------------------------------------
check_usb_md5() {
    local top_dir=$1  ADD_DMESG_TO_FATAL=
    local md5_file  failed
    while read md5_file; do
        local fname=$(basename "$md5_file")
        local name=${fname%.md5}
        local dir=$(dirname "$md5_file")

        case $name in
            linuxfs|initrd.gz|vmlinuz)  fix_our_md5 "$md5_file" ;;
        esac

        msg $"check md5 for %s" "$(pq "$name")"
        (cd $dir && cmd md5sum -c "$fname") && continue

        warn $"MD5 CHECK FAILED FOR %s" "$(pqw $name)"

        # Only error out if it is one of our files
        case $name in
            linuxfs|initrd.gz|vmlinuz) failed=true ;;
        esac
    done << MD5_Files
$(find "$top_dir" -maxdepth 4 -name "*.md5" | sort)
MD5_Files

    [ -z "$failed" ] && return

    echo

    yes_NO_fatal "nomd5"                         \
        $"Do you want to continue anway?"         \
        $"Use %s to always ignore this warning"   \
        $"At least one file failed the md5 check"
}

#------------------------------------------------------------------------------
# Fix the format of our own .md5 files
#------------------------------------------------------------------------------
fix_our_md5() {
    local md5_file=$1 dir=$(dirname "$1")  fname=$(basename "$1")

    local name=${fname%.md5}
    test -f "$dir/$name" || return 0

    local file=$(awk '{print $2}' "$md5_file")
    local base=$(basename "$file")

    case $file in
        $name) return ;;
    esac

    msg $"Fix md5 file format in %s" "$(pqw $fname)"
    cmd sed -i "s/ .*\|$/  $name/" "$md5_file"
}

#------------------------------------------------------------------------------
# This allows us to put glob wildcards inside of variable to make the program
# easier to configure.
#------------------------------------------------------------------------------
copy_files_spec() {
    local src=$1  spec=$2  targ=$3  type=${4:-unknown}
    local file from dir

    test -z "$spec" && fatal "Missing list of files to copy to %s" "$(pqw $targ)"

    msg $"copy from %s to %s partition" "$(pq $(basename $src))" "$(pq $(basename $targ))"
    msg $"files %s" "$(pq $spec)"

    while read file; do
        test -z "$file" && continue
        dir=$targ/$(dirname $file)
        test -d "$dir" || cmd mkdir -p "$dir"
        cmd cp $CP_ARGS "$src/$file" "$dir"  || warn $"Error while copying %s files" "$(pqw $type)"
    done <<Copy_Files_Spec
$(cd $src && eval "ls -d $spec" 2>/dev/null)
Copy_Files_Spec
}

#------------------------------------------------------------------------------
# Do these step early so we can error out early if there is a problem.
#------------------------------------------------------------------------------
start_initrd_encryption() {
    local from_file=$1  initrd_dir=${2:-$INITRD_DIR}  linux_dir=$3

    test -r "$from_file" || fatal $"Could not find initrd file %s" "$(pqw $from_file)"
    unpack-initrd -f "$from_file" -d $initrd_dir || fatal $"An error occurred unpacking the initrd"

    #grep -q cryptsetup $initrd_dir/init || fatal $"This live initrd cannot do encryption"

    verbose_cmd copy-initrd-programs --no-color --from=$linux_dir --to=$initrd_dir --clean
    verbose_cmd copy-initrd-programs --no-color --from=$linux_dir --to=$initrd_dir --encrypt \
        || fatal $"Was unable to add all required programs to the live initrd"

    local kernel mod_dir=$initrd_dir/lib/modules
    for kernel in $(ls $mod_dir); do

        test -d $mod_dir/$kernel/kernel               || continue
        test -d $linux_dir/lib/modules/$kernel/kernel || continue

        msg $"add %s modules to initrd" "$(pq $kernel encryption)"
        cmd copy-initrd-modules --quiet --only-encrypt --from=$linux_dir --to=$initrd_dir --kernel=$kernel \
            || fatal "Kernel modules were not copied.  Do you need to update copy-initrd-modules?"

    done
    local encrypt_file=$initrd_dir/etc/encrypt
    mkdir -p $(dirname $encrypt_file)
    echo "$ENCRYPT_ENABLE" > $encrypt_file
}

#------------------------------------------------------------------------------
# When we clone a running encrypted system to a non-encrypted system, we need
# to disable encryption in the initrd.
#------------------------------------------------------------------------------
disable_initrd_encryption() {
    local from_file=$1  initrd_dir=$2  linux_dir=${3:-/}
    test -r "$from_file" || fatal $"Could not find initrd file %s" "$(pqw $from_file)"
    always_cmd unpack-initrd -f "$from_file" -d $initrd_dir || fatal $"An error occurred unpacking the initrd"

    verbose_cmd copy-initrd-programs --no-color --from=$linux_dir --to=$initrd_dir --clean
    verbose_cmd copy-initrd-programs --no-color --from=$linux_dir --to=$initrd_dir \
        || fatal $"Was unable to add all required programs to the live initrd"

    local kernel mod_dir=$initrd_dir/lib/modules
    for kernel in $(ls $mod_dir); do

        test -d $mod_dir/$kernel/kernel               || continue
        test -d $linux_dir/lib/modules/$kernel/kernel || continue

        msg $"add %s modules to initrd" "$(pq $kernel)"
        cmd copy-initrd-modules --quiet --from=$linux_dir --to=$initrd_dir --kernel=$kernel \
            || fatal "Kernel modules were not copied.  Do you need to update copy-initrd-modules?"

    done
    local encrypt_file=$initrd_dir/etc/encrypt
    cmd rm -f "$encrypt_file"
    cmd unpack-initrd -f "$from_file" -d $initrd_dir -r || fatal $"An error occurred repacking the initrd"
}

#------------------------------------------------------------------------------
# We do this later so we don't get over-written (is this true?)
#------------------------------------------------------------------------------
finish_initrd_encryption() {
    local initrd_dir=${1:-$INITRD_DIR}  to_file=$2

    [ "$PRETEND" ] && return

    unpack-initrd -f $to_file -d $initrd_dir --repack || fatal $"An error occurred repacking the initrd"
    local md5_sum=$(cd $(dirname $to_file) && md5sum $(basename $to_file))
    cmd write_file "$to_file".md5 "$md5_sum"
}

#------------------------------------------------------------------------------
# Add or removed /etc/encrypt inside initrd  NO LONGER USED
# (although it might be safer for non-encrypted live-usbs
#------------------------------------------------------------------------------
enable_disable_initrd_encryption() {
    local from_file=${1:-$2}  to_file=$2  initrd_dir=${3:-$INITRD_DIR}

    test -r "$from_file" || fatal $"Could not find initrd file %s" "$(pqw $from_file)"
    unpack-initrd -f "$from_file" -d $initrd_dir || fatal "An error occurred unpacking the initrd"


    local encrypt_file=$initrd_dir/etc/encrypt
    if encrypt; then
        test -e $initrd_dir/bin/cryptsetup  || fatal $"The %s program was not found in the initrd" cryptsetup
        grep -q cryptsetup $initrd_dir/init || fatal $"This initrd cannot do encryption"

        msg $"Enable encryption in the live initrd"
        echo "$ENCRYPT_ENABLE" > $encrypt_file
    else
        msg $"Disable encryption in the live initrd"
        rm -f $encrypt_file
    fi

    pause initrd

    cmd unpack-initrd -f $to_file -d $initrd_dir --repack || fatal "An error occurred repacking the initrd"
    local md5_sum=$(cd $(dirname $to_file) && md5sum $(basename $to_file))
    cmd write_file "$to_file".md5 "$md5_sum"
}


#------------------------------------------------------------------------------
# Find a binary program on a mounted file system such as a squashfs.
#------------------------------------------------------------------------------
find_bin() {
    local dir=$1 name=$2 path=${3:-$PATH}
    local p
    for p in ${path//:/ }; do
        test -e $dir$p/$name || continue
        echo $p/$name
        return 0
    done
    return 1
}

#------------------------------------------------------------------------------
# Kludge to get uefi memtest to work.  Strange but true.  Hope there is a fix
# soon.
#------------------------------------------------------------------------------
fix_uefi_memtest() {
    local dir=$1/EFI/BOOT
    test -d "$dir" || dir=$1/efi/boot
    test -d "$dir" || return
    msg $"Fix %s bug" 'Dell uefi memtest'
    local fallback=$dir/fallback.efi
    local grubx64=$dir/grubx64.efi

    test -e $grubx64 || return

    # the fallback file needs to be the last one added to the directory.  But
    # erasing it and adding it back doesn't work so we make a new directory
    # without it and then remove the old one, rename then new one and then do
    # the copy.  *** sigh ***

    if test -e $fallback; then
        cmd rm -f $fallback
        local flag
        [ "$PRETEND_MODE" ] && flag="--dry-run"
        local temp=$(mktemp -p $(dirname $dir) -d $flag)
        cmd cp $dir/* $temp/
        cmd rm -r $dir
        cmd mv $temp $dir
    fi

    cmd cp $grubx64 $fallback
}

#------------------------------------------------------------------------------
# Use the gfxsave script to add chaats to the gfxboot/syslinux bootladers
#------------------------------------------------------------------------------
do_cheats_syslinux() {
    #warn "Cheats are still being worked on"; return

    local bios_dir=$1  cheats=$2
    local syslinux_cfg=$bios_dir/boot/syslinux/syslinux.cfg

    require cheats-syslinux gfxsave || return

    local file params
    local verbose=1
    [ "$BE_VERBOSE" ] && verbose=6
    if test -w $syslinux_cfg; then
        params=$(sed -nr "1,/^\s*APPEND\s/s/^\s*APPEND\s+//p" $syslinux_cfg)
        Msg "syslinux params: %s" "$(pq $params $cheats)"
        VERBOSE=$verbose CMDLINE="$params $cheats" cmd gfxsave $bios_dir/boot both
    else
        pwarn "Could not find %s file to update" "$(basename $syslinux_cfg)"
    fi
}

#------------------------------------------------------------------------------
#  Use the grub2-save script to add cheats to the UEFI grub2 bootloader
#------------------------------------------------------------------------------
do_cheats_grub() {
    #warn "Cheats are still being worked on"; return
    local uefi_dir=$1  cheats=$2
    local grub_cfg=$uefi_dir/boot/grub/grub.cfg

    require cheats-grub vmlinuz-version grub2-save || return

    local params

    if test -w $grub_cfg; then
        params=$(sed -nr "1,/^\s*linux\s/s/^\s*linux\s+[^ ]+//p" $grub_cfg)
        Msg "grub params: %s" "$(pq $params $cheats)"
        cmd grub2-save $uefi_dir --no-kernel --cheats="${params% } $cheats"
    else
        # Could not find '<grub.cfg>' file to update
        pwarn "Could not find %s file to update" $(basename $grub_cfg)
    fi
}

#------------------------------------------------------------------------------
# Here is a tricky part.  The grub2.cfg must know the UUID of the main system
# and the main system must know the UUID of the uefi partition in order for the
# user to be amble to save their  boot parameter selections.
#------------------------------------------------------------------------------
do_uuids() {
    local bios_dir=$1  bios_uuid=$2  efi_grub_cfg=$3  uefi_uuid=$4  uefi_part=$5  main_dir=$6

    # Sanity checks
    #echo "$1 | $2 | $3 | $4 | $5 | $6"
    #exit

    pause uuids

    if [ ${#bios_uuid} -eq 0 ]; then
        warn "No %s given for %s partition" UUID bios
        return
    fi

    if encrypt; then
        fatal_z $main_dir "In do_uuids()  main_dir must be given in encrypt mode"
        local bios_uuid_file=$main_dir/antiX/bios-dev-uuid
        cmd mkdir -p $(dirname $bios_uuid_file)
        cmd write_file "$bios_uuid_file" "$bios_uuid"
    fi

    local base_grub_cfg=$(basename $efi_grub_cfg)

    # Detect the newer grub config
    if test -e $bios_dir/$EFI_GRUB_CONF; then

        # Do not make multiple id files
        # test -e $bios_dir/$DID_EFI_FILE && return

        msg $"Using %s grub config %s" "$(pq antiX/MX)" "$(pq 2.0)"

        # Copy over the efi-grub.cfg and signal we have done it
        cmd mkdir -p $(dirname $efi_grub_cfg)
        cmd cp "$bios_dir/$EFI_GRUB_CONF" "$efi_grub_cfg"
        cmd touch "$bios_dir/$DID_EFI_FILE"

        # Replace %UUID% with the bios_uuid in the code
        cmd sed -i "/^\s*#/! s/%UUID%/$bios_uuid/" $efi_grub_cfg

        # Make sure it worked (check can be bypassed with --force=uuid)
        force uuid || grep -q "$bios_uuid" $efi_grub_cfg \
            || warn "Failed to update UUID in %s" "$(pqw $base_grub_cfg)"

         if grep -q "^[^#]*%ID_FILE%" $efi_grub_cfg ; then
            cmd rm -f $bios_dir/boot/grub/config/*.id
            local rand_file=/boot/grub/config/$(random_string).id
            cmd touch $bios_dir$rand_file
            cmd sed -i "/^\s*#/! s|%ID_FILE%|$rand_file|" $efi_grub_cfg
        fi

        return
    fi

    msg $"Using %s grub config %s" "$(pq antiX/MX)" "$(pq 1.0)"

    local dir
    for dir in $bios_dir $main_dir; do
        [ ${#uefi_uuid} -gt 0 ] || continue
        local uefi_file=$dir/antiX/esp-uuid
        cmd mkdir -p $(dirname $uefi_file) || fatal "Making directory %s failed" "$(dirname $uefi_file)"
        cmd write_file $uefi_file $uefi_uuid
    done

    if ! test -e $efi_grub_cfg; then
        # Could not find <grub.cfg> file
        pwarn "Could not find %s file on %s" "$base_grub_cfg"  'EFI'
        return
    fi

    local new_line="search --no-floppy --set=root --fs-uuid $bios_uuid"
    if grep -q "search.*--set=root.*" $efi_grub_cfg; then
        # Replace the line(s) if it/they exists
        cmd sed -i "/search.*--set=root.*/  s/.*/$new_line/" $efi_grub_cfg \
            || fatal "sed on %s failed" "$base_grub_cfg"
    else
        # Add the new line before the first menuentry line if not
        cmd sed -ri "1,/^\s*menuentry/s/(^\s*menuentry)/$new_line\n\n\1/" $efi_grub_cfg \
            || fatal "sed on %s failed" "$base_grub_cfg"
    fi

    # Remove #--esp comment to point memtest back to fat32 partition
    cmd sed -ri "s/^#-+esp\s*//" $efi_grub_cfg \
        || fatal "sed on %s failed" "$base_grub_cfg"

    cmd sed -ri "s/(root=\(hd0,)[0-9]\)/\1$uefi_part)/" $efi_grub_cfg \
        || fatal "sed on %s failed" "$base_grub_cfg"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
data_first_uuid() {
    bios_dir=$1  data_dev=$2  main_dir=$3
    data_first || return
    local uuid=$(get_uuid "$data_dev")
    [ ${#uuid} -gt 0 ] || return

    # Only write the file once if the directories are the same
    [ "$bios_dir" = "$main_dir" ] && main_dir=""

    local dir
    for dir in $bios_dir $main_dir; do
        local uuid_file=$dir/antiX/data-uuid
        cmd mkdir -p $(dirname $uuid_file) || fatal "Making directory %s failed" "$(dirname $uuid_file)"
        cmd write_file $uuid_file $uuid
    done
}

#------------------------------------------------------------------------------
# Create a random hex string $cnt * 2 chars long
#------------------------------------------------------------------------------
random_string() {
    local cnt=${1:-16}
    dd if=/dev/urandom status=none bs=1 count=$cnt | od -An -x | tr -d " "
}

#------------------------------------------------------------------------------
# This installs the legacy syslinux bootloader
#------------------------------------------------------------------------------
do_install_bootloader() {
    local dev=$1  bios_dir=$2  type=$3

    msg "extlinux version %s" "$(pq $(get_extlinux_version))"

    local fname
    case $type in
          gpt) fname=gptmbr.bin ;;
        msdos) fname=mbr.bin
               data_first && fname=altmbr.bin  ;;
            *) fatal "Unknown partitioning scheme: %s.  Expected msdos or gpt" "$type"
               ;;
    esac

    local dir file d
    for dir in /usr/share/syslinux /usr/lib/syslinux/mbr; do
        test -e $dir/$fname || continue
        file=$dir/$fname
        break
    done

    [ "$file" ] || fatal "Could not find file %s" "$fname"
    case $fname in
        altmbr.bin) printf '\2' | cat "$file" - \
            | cmd_dd bs=440 count=1 iflag=fullblock conv=notrunc of=$dev ;;

                 *) cmd_dd bs=440 conv=notrunc count=1 if=$file of=$dev  ;;
    esac

    local sdir idir syslinux_dir isolinux_dir
    for sdir in boot/syslinux syslinux; do
        test -d $bios_dir/$sdir || continue
        syslinux_dir=$bios_dir/$sdir
        break
    done
    if [ -z "$syslinux_dir" ]; then
        for idir in boot/isolinux isolinux; do
            test -d $bios_dir/$idir || continue

            # Create syslinux directory from <boot/isolinux>
            warn $"Create syslinux directory from %s" $idir
            syslinux_dir=$bios_dir/${idir%isolinux}syslinux
            cmd cp -r $bios_dir/$idir $syslinux_dir || fatal "Could not copy the isolinux directory"
            local f
            for f in $(cd $syslinux_dir && ls isolinux.*); do
                test -e $bios_dir/$sdir/$f || continue
                cmd mv $bios_dir/$sdir/$f $bios_dir/$sdir/syslinux${f#isolinux}
            done
            break
        done
    fi

    [ "$KEEP_SYSLINUX" ] || update_syslinux $bios_dir/$sdir "$SYSLINUX_FILES" "$SYSLINUX_RM"

    [ "$PRETEND_MODE" ] && return

     # [don't translate syslinux or isolinux]
    [ -z "$syslinux_dir" ] && fatal "Could not find a syslinux or isolinux directory"

    cmd extlinux -i $syslinux_dir || fatal "%s command failed" extlinux
}

update_syslinux() {
    local to_dir=$1  xfer_files=$2  rm_files=$3
    local fname=gfxboot.c32  file  pre  post
    for pre in /usr/share/syslinux /usr/lib/syslinux; do
        for post in "/" /modules/bios/; do
            test -e $pre$post$fname || continue
            file=$pre$post$fname
            break
        done
    done
    [ "$file" ] || fatal "Could not find file %s" "$fname"
    local from_dir=$(dirname $file)

    if test -e $to_dir/ldlinux.sys && which chattr &>/dev/null; then
        cmd chattr -i $to_dir/ldlinux.sys
    fi

    while read fname; do
        test -e $to_dir/$fname && cmd rm -f $to_dir/$fname
    done<<Syslinux_Rm
$(cd $to_dir && ls -d $rm_files 2>/dev/null)
Syslinux_Rm

    for fname in $xfer_files; do
        test -e $from_dir/$fname && cmd cp $from_dir/$fname $to_dir/
    done

    cmd write_file $to_dir/version $(get_extlinux_version)
}

#------------------------------------------------------------------------------
# Create a small made-by-live-usb-maker file on the live-usb
#------------------------------------------------------------------------------
do_made_by() {
    local dir=$1  file=${2:-$MADE_BY_FILE}
    always_cmd write_file $dir/$file "  created: $(date)
  program: $(basename $0)
  version: $VERSION ($VERSION_DATE)"
}

#------------------------------------------------------------------------------
# Create a random seed file for the first boot.  This is not safe locally, of
# course, but should be good for possible internet attacks.
#------------------------------------------------------------------------------
write_random_seed() {
    local file=$1
    cmd_dd if=/dev/urandom of=$file bs=512 count=1
    cmd chmod 600 $file
}

#------------------------------------------------------------------------------
# Change the initrd file to the template initrd selected by the user.
#------------------------------------------------------------------------------
update_initrd() {
    local bios_dir=$1  main_dir=$2  initrd_template=$(readlink -f "$3")

    need_prog copy-initrd-programs unpack-initrd copy-initrd-modules vmlinuz-version

    local initrd_path=$DEF_BOOT_DIR/initrd.gz
    local initrd_file=$bios_dir/$initrd_path
    local linuxfs_path=$DEF_BOOT_DIR/linuxfs
    local linuxfs_file=$main_dir/$linuxfs_path
    local vmlinuz_path=$DEF_BOOT_DIR/vmlinuz
    local vmlinuz_file=$bios_dir/$vmlinuz_path

    #pause initrd

    # Sanity checks
    test -e $initrd_file  || fatal "Could not find %s file %s on main partition"  "$(pqw initrd)" "$(pqw $initrd_path)"

    test -e $linuxfs_file || encrypt \
        || fatal "Could not find %s file %s on main partition"  "$(pqw linuxfs)" "$(pqw $linuxfs_path)"

    test -e $vmlinuz_file || fatal "Could not find %s file %s on main partition"  "$(pqw vmlinuz)" "$(pqw $vmlinuz_path)"

    # Get live kernel so we can copy in the right modules
    local kernel=$(vmlinuz-version  --tabs --noheadings "$vmlinuz_file" | cut -f2)
    fatal_z "$kernel" "Could not determine the kernel version"
    msg "Found kernel %s" "$(pq $kernel)"

    # Mount the linuxfs so we can get modules and programs from the horse's mouth
    local linux_dir=$LINUX_DIR  initrd_dir=$WORK_DIR/initrd
    mkdir -p $linux_dir  $initrd_dir || fatal "mkdir failed"

    my_mount "$linuxfs_file" "$linux_dir" -t squashfs -o loop,ro

    # Sanity checks for the template and work with directories as well as files
    if ! test -e "$initrd_template"; then
        fatal "Could not find initrd template file %s" "$initrd_template"
    fi

    # We are copying to RAM so we don't want to use up too much space
    # Accommodate a template file or directory (repo)
    local size
    if test -f "$initrd_template"; then
        size=$(du -sm "$initrd_template" | tail -n1 | awk '{print $1}')
    elif test -d "$initrd_template"; then
        size=$(du -scm "$initrd_template"/[a-z]* | tail -n1 | awk '{print $1}')
    else
        fatal "The initrd template %s is not a file or a directory" "$initrd_template"
    fi

    msg $"initrd size %s" "$(nq $size MiB)"

    [ "$size" -gt "$INITRD_MAX_SIZE" ] && fatal $"The initrd template %s seems too large" "$initrd_template"

    if test -f "$initrd_template"; then
        cmd unpack-initrd --from "$initrd_template" --dir "$initrd_dir" || fatal "Unpack failed"
    elif test -d "$initrd_template"; then
        (cd "$initrd_template" && find . | grep "^\./[a-z]" | cpio --quiet --owner=root:root -pd "$initrd_dir")
    fi

    copy_initrd_release "$linux_dir"  "$initrd_dir" 'always'

    local e_args
    encrypt && e_args="--encrypt"

    verbose_cmd copy-initrd-programs --no-color --from=$linux_dir --to=$initrd_dir --clean
    verbose_cmd copy-initrd-programs --no-color --from=$linux_dir --to=$initrd_dir $e_args \
        || fatal $"Was unable to add all required programs to the live initrd"

    cmd copy-initrd-modules --count --quiet --from=$linux_dir --to=$initrd_dir --kernel=$kernel

    cmd copy-initrd-modules --quiet --only-encrypt --from=$linux_dir --to=$initrd_dir $e_args --kernel=$kernel \
            || fatal "Kernel modules were not copied.  Do you need to update copy-initrd-modules?"

    pause initrd

    cmd unpack-initrd --from "$initrd_file" --dir "$initrd_dir" --repack || fatal $"Repack failed"
    local md5_sum=$(cd $(dirname $initrd_file) && md5sum $(basename $initrd_file))
    cmd write_file "$initrd_file".md5 "$md5_sum"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
check_recent_executables() {
    local dir=$1  file  bash_error  cnt=0
    while read file; do
        test -e "$file" || continue
        head -n1 "$file" | egrep -q "^#\!(/live)?/bin/[a-z]*sh" || continue
        cnt=$((cnt + 1))
        bash -n "$file" && continue
        warn 'Bash errors in file %s' "$(pqw "$file")"
        bash_error=true
    done<<Recent_Execs
$(find "$dir"/[a-z]* -maxdepth 3 -type f -executable -ctime -2)
Recent_Execs

    msg "Checked %s recent executables under %s" "$(nq $cnt)" "$(pq "$dir")"

    [ "$bash_error" ] && fatal 'One or more Bash errors were detected'
}

#------------------------------------------------------------------------------
# Copy the initrd-release file (if needed)
#------------------------------------------------------------------------------
copy_initrd_release() {
    local from=$1  to=$2  always=$3
    local from_release to_release release
    for release in initrd_release initrd-release; do
        from_release=$from/etc/$release
        to_release=$to/etc/$release

        [ "$always" -o ! -e "$to_release" ] || continue
        test -e "$from_release"             || continue
        always_cmd cp "$from_release" "$to_release"
    done
}

#===== Here come the utilities! ===============================================

#------------------------------------------------------------------------------
# Get the version number of the extlinux program we are using
#------------------------------------------------------------------------------
get_extlinux_version() {
    extlinux -v 2>&1 | sed -r "s/^[a-z]+\s+([0-9.]+).*/\1/g"
}

#------------------------------------------------------------------------------
# Make sure --target device is valid.
#------------------------------------------------------------------------------
check_target() {
    local target=$1  live_dev=$2

    # Make sure our target is a real device
    local target_dev=$(expand_device $target)

    [ ${#target_dev} -gt 0 ] || fatal $"Could not find device %s" "$target"
    [ ${#live_dev}   -gt 0 ] || return
    local live_drive=$(get_drive ${live_dev##*/} )
    local targ_drive=$(get_drive ${target##*/}   )
    [ "$live_drive" = "$targ_drive" ] && fatal $"Target %s cannot be on the live device %s" "$target" "$live_drive"
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
format_usb() {
    dev="/dev/${1#/dev/}"  type=$2  fstype=$3

    msg $"formating device %s" "$(pq $dev)"
    local START=1  PART_NUM=0

    fake_need partition-clear
    clear_partition "$dev"

    fake_need partition-make
    msg $"Using %s partitioning" $(pq $type)

    local err_msg=$"Partitioning failed at %s"

    local PREAMB="parted --script --align optimal $dev unit MiB"
    cmd $PREAMB mklabel $type || dmesg_fatal "$err_msg" partitioning

    _add_partition $((TOTAL_SIZE - 1)) "$fstype" data

    sync

    cmd partprobe $dev

    local part=$(get_partition $dev 1)

    make_fs_dev $part $fstype "USB-DATA"
}

#------------------------------------------------------------------------------
# Show the need() output without actually needing anything
# FIXME: this should go in the lib, obviously
#------------------------------------------------------------------------------
fake_need() {
    local cmd=$1  xlat=${2:-$1}
    log_it echo &>/dev/null
    set_window_title "$ME $VERSION: $1"
    Msg "$(bq ">>") $xlat"
}

#------------------------------------------------------------------------------
# Create a file system on a partition
#------------------------------------------------------------------------------
make_fs_dev() {
    local part=$1  fstype=$2  label=$3
    local args

    case $fstype in
        fat32) fstype=vfat ;;
    esac
    case $fstype in
             exfat) args="-n $label"        ;;
              vfat) args="-F 32 -n $label"  ;;
          ext[234]) args="-L $label"        ;;
              ntfs) args="--fast -L $label" ;;
    esac

    fake_need makefs-data
    msg $"Make %s filesystem" "$(pq $fstype)"
    cmd mkfs.$fstype $args $part \
        || dmesg_fatal $"Could not make %s file system on %s" "$fstype" "$part"

    sync
    cmd partprobe "$part"

    case $fstype in
        vfat|ntfs|exfat) return ;;
    esac

    # Change ownership of the data partition to the user
    local user=$SUDO_USER
    [ -z "$user" -o "$user" = 'root' ] && user=$(logname)
    [ "$user" = 'root' ] && return

    local group=$(getent group users | cut -d: -f1)
    [ -n "$user" ] && : ${group:=$(getent group $user | cut -d: -f1)}

    [ -n "$group" ] && user="$user:$group"
    [ -z "$user"  ] && return

    mount_device "$part" $DATA_DIR || return

    fake_need set-ownership
    msg $"Setting data partition ownership to %s" "$(pq $user)"

    if which setfacl &>/dev/null; then
        cmd setfacl     -m u::rwx,g::rwx,o::r-x $DATA_DIR/.
        cmd setfacl  -d -m u::rwx,g::rwx,o::r-x $DATA_DIR/.
    fi

    cmd chown $user $DATA_DIR/.
    cmd chmod g+rwx $DATA_DIR/.

    # for shared write access set-gid bit
    # cmd chmod g+rwxs $DATA_DIR/.
    sync

    always_cmd umount $DATA_DIR
}

#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
makefs_menu() {
    for type in $USB_FORMAT_LIST; do
        which "mkfs.$type" &>/dev/null ||continue
        menu_printf $type "%s filesystem" "$type"
    done
}

#------------------------------------------------------------------------------
# Let user select a file system type if the current type is "choose"
# Otherwise make sure we can make the specified file system type.
#------------------------------------------------------------------------------
makefs_select() {
    local var=$1  name=$2  val
    eval val=\$$var

    [ -z "$val" ] && return

    # In gui mode use fat32 instead of giving the user a menu
    case $val in
        choose) q_mode gui && val=vfat ;;
    esac

    # Only make a menu if "choose" is the fs_type, otherwise validate and return
    case $val in
        choose) ;;
             *) validate_fs_type $val ; return ;;
    esac

    local menu=$(makefs_menu)
    local cnt=$(count_lines "$menu")
    case $cnt in
        0) fatal $"No filesystem creation tools were found!"    ;;
        1) warn  $"Only one filesystem creation tool was found" ;;
    esac
    local title=$(printf $"Please select the %s filesystem type" "$(pqq $name)")
    my_select $var "$title" "$menu"
}

#------------------------------------------------------------------------------
# Convenience routines to save a little typing
#------------------------------------------------------------------------------
encrypt()             {  [ "$ENCRYPT"   ]                 ;  return $?; }
data_first()          {  [ "$DATA_FIRST_FS$DATA_FIRST" ]  ;  return $?; }
using_grub_config_2() {  [ "$USING_GRUB_CONFIG_2" ]       ;  return $?; }

#------------------------------------------------------------------------------
# Make sure we have enough left over space on a partition.  Can generate
# warnings or fatal errors depending on $action.
#------------------------------------------------------------------------------
check_size() {
    local type=$1  extra=$2  margin=$3  action=${4:-fatal}

    [ $margin -eq 0 ] && return

    [ $extra -lt 0 ]       && $action $"Not enough space on %s partition" "$type"
    [ $extra -lt $margin ] && $action $"Less than %s MiB would remain on %s partition" "$margin" "$type"
}

#------------------------------------------------------------------------------
# Tell user we are done and then exit
#------------------------------------------------------------------------------
exit_done() {
    say_done
    my_exit
}

#------------------------------------------------------------------------------
# Tell user that we're done
#------------------------------------------------------------------------------
say_done() {
    msg "$(bq ">>") %s" $"done"
}

#------------------------------------------------------------------------------
# A catch-all for things to be done right before exiting
#------------------------------------------------------------------------------
my_exit() {
    local ret=${1:-0}

    show_elapsed

    # Pause at '<Exit>'
    pause exit $"Exit"

    # Msg "=> cleaning up"
    exit $ret
}

#------------------------------------------------------------------------------
# This may be over-kill.  Seems reliable though ...
#------------------------------------------------------------------------------
umount_work_dir() {
    local work_dir=${1:-$WORK_DIR}
    [ -n "$work_dir" ] || return

    sync; sync
    is_mountpoint "$work_dir" || return 0

    local try fail
    for try in $(seq 1 5); do
        fail=
        while read dir; do
            umount --recursive "$dir"  2>/dev/null
            is_mountpoint $dir && fail=true
        done<<Umount_Dirs
$(mount | awk '{print $3}' | grep "^$work_dir/." | tac)
Umount_Dirs

        [ -z "$fail" ] && break
        sleep .1
    done
    umount --recursive "$work_dir"
    rmdir "$work_dir"
}

#------------------------------------------------------------------------------
# Write a config file using the current variables so users can easily put
# command line parameters into the config file.
#------------------------------------------------------------------------------
write_config() {
    local file=${1:-$CONFIG_FILE}
    local dir=$(dirname "$file")
    mkdir -p "$dir" || fatal $"Could not make config file directory %s" "$dir"
    msg $"Writing config file %s" "$(pq "$file")"

    cat<<Config_File >"$file"
$(config_header "$file" "$ME" "$VERSION" "$VERSION_DATE")

#      ISO_FILE_DIR="$ISO_FILE_DIR"
#     ISO_FILE_SPEC="$ISO_FILE_SPEC"
#       SEARCH_DIRS="$SEARCH_DIRS"
#      SEARCH_DEPTH="$SEARCH_DEPTH"
#         MAX_FILES="$MAX_FILES"
#      MIN_ISO_SIZE="$MIN_ISO_SIZE"

#         MSDOS_GPT="$MSDOS_GPT"
#      DEFAULT_SIZE="$DEFAULT_SIZE"
#          CMD_SIZE="$CMD_SIZE"
#         BIOS_SIZE="$BIOS_SIZE"
#         UEFI_SIZE="$UEFI_SIZE"
#       BIOS_MARGIN="$BIOS_MARGIN"
#       MAIN_MARGIN="$MAIN_MARGIN"
#       UEFI_MARGIN="$UEFI_MARGIN"

#      EXT4_OPTIONS="$EXT4_OPTIONS"
#        BIOS_LABEL="$BIOS_LABEL"
#         ESP_LABEL="$ESP_LABEL"

#           LIVE_MP="$LIVE_MP"
#      LINUXFS_NAME="$LINUXFS_NAME"
#  MIN_LINUXFS_SIZE="$MIN_LINUXFS_SIZE"
#      DEF_BOOT_DIR="$DEF_BOOT_DIR"

#          CLONE_DIRS="$CLONE_DIRS"
#         CLONE_FILES="$CLONE_FILES"
# CLONE_LINUXFS_FILES="$CLONE_LINUXFS_FILES"
#    CLONE_BOOT_FILES="$CLONE_BOOT_FILES"

#        UEFI_FILES="$UEFI_FILES"
#      UEFI_2_FILES="$UEFI_2_FILES"
#        BIOS_FILES="$BIOS_FILES"

#         GRUB_CONF="$GRUB_CONF"
#     EFI_GRUB_CONF="$EFI_GRUB_CONF"
#      DID_EFI_FILE="$DID_EFI_FILE"

#            CHEATS=""
#      COLOR_SCHEME="$COLOR_SCHEME"
#     QUESTION_MODE="$QUESTION_MODE"

#           ENCRYPT="$ENCRYPT"
#  PASS_PHRASE_TYPE="$PASS_PHRASE_TYPE"
#         LUKS_NAME="$LUKS_NAME"

# EXT_OVERHEAD_FORM="$EXT_OVERHEAD_FORM"
#  EXT_OVERHEAD_MAX="$EXT_OVERHEAD_MAX"

#   PP_MAX_WORD_LEN="$PP_MAX_WORD_LEN"
#      PP_NUM_WORDS="$PP_NUM_WORDS"
#     PP_WORDS_FILE="$PP_WORDS_FILE"
#    PP_RAND_SOURCE="$PP_RAND_SOURCE"

#   GRAPHICAL_MENUS="$GRAPHICAL_MENUS"
#   AUTOMOUNT_DELAY="$AUTOMOUNT_DELAY"
#   INITRD_MAX_SIZE="$INITRD_MAX_SIZE"
$(config_footer)
Config_File

    return 0
}

#------------------------------------------------------------------------------
# This routine is trapped on the EXIT signal.  So we always do this stuff.
# It should *not* be interactive.
#------------------------------------------------------------------------------
clean_up() {

    lib_clean_up

    # Kill the children
    pkill -P $$

    test -d $LINUX_DIR && is_mountpoint $LINUX_DIR && umount $LINUX_DIR

    # Try to umount everything
    umount_work_dir

    rm -f $WORK_DIR/pid
    test -d $WORK_DIR && rmdir $WORK_DIR

    luks_close $LUKS_NAME

    unflock
}

#------------------------------------------------------------------------------
# Load the lib either from a neighboring repo or from the standard location.
#------------------------------------------------------------------------------
load_lib() {
    local file=$1  path=$2
    unset FOUND_LIB

    local dir lib found IFS=:
    for dir in $path; do
        lib=$dir/$file
        test -r $lib || continue
        if ! . $lib; then
            printf "Error when loading library %s\n" "$lib" >&2
            printf "This is a fatal error\n" >&2
            exit 15
        fi
        FOUND_LIB=$lib
        return 0
    done

    printf "Could not find library %s on path %s\n" "$file" "$path" >&2
    printf "This is a fatal error\n" >&2
    exit 17
}

#===== Start Here =============================================================

load_lib "$SHELL_LIB" "$LIB_PATH"

set_colors

main "$@"
