#!/usr/bin/bash

# NB Avoid use of "#!/usr/bin/env bash" in this script as it will mask process names & inhibit process controls.

# See notes in the Setup & Install script 


##  Copyright (c)  Alex Genovese   https://github.com/TuxVinyards


#   SMALL CODE SNIPPETS eg the function printColor MAY BE USED  

#   PERMISSIVELY in projects as MIT or similar, providing CLEAR ATTRIBUTIONS are shown.

# 	Otherwise:  

# Licence  GPL3   https://www.gnu.org/licenses 

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# https://www.gnu.org/licenses 


# IF CODE IN GENERAL BECOMES USED IN ANY OTHER PROJECT,

# THE GPL3 LICENCE APPLIES & YOU SHOULD SHOW CLEAR ATTRIBUTIONS. 



##  A text menu interface for the 'quickemu' project

#   https://github.com/quickemu-project/quickemu   https://gitlab.com/qemu-project/qemu

#   Users should install 'quickemu' and may set up Virtual Machines as normal.


## A wrapping script for quickemu/quickget.  Originally part of quickemu-mod suite of scripts. 

#  See:  https://en.wikipedia.org/wiki/Wrapper_library   https://en.wikipedia.org/wiki/Adapter_pattern 


#  @2023/08 this script forms the base for qqX - quickemu quickget X terminal project  

#  qqX may be installed alongside quickemu in /usr/bin  & run by typing either qqX or qqx. Or by mouse


#  @2023/08  Work on quickemu-wrap is now being frozen but this script attempts to maintain backward compatibility

#  during the initial qqX release.  In theory it may second install as qwrap. YMMV

#  Concurrent qwrap & qqX will have different settings files which may or may not be advantageous.  

#  See:  https://github.com/TuxVinyards/qqX  &  https://github.com/TuxVinyards/quickemu-mod

#  @2023/10  qmod/qwrap probably to be phased out & focus only on qqX


##  Bash 5.0+   (2023: we are now at ver 5 for quite a few years ...) 

#   details: try 'bash --version' at the commandline   



## ShellCheck global disables:

# https://www.shellcheck.net/wiki/SC2242 as it clashes with use of exit traps used to keep mouse click scripts open

#  shellcheck disable=SC2242


##  Dev: set =1 to stop screen clears (and pause 1 sec), for easier fault tracing
#   Set =integer for add longer pauses, "0.2" etc for less.

NoClear=


# This script:

ModVersion="1.4.01"

# major.minor.point[.dev-point]  x.x.2dp[.2dp]

# where x = any whole number and the last register always being 2dp.

# The 2nd register becomes printf'ed to 2dp for upgrade valuation

# while a single 0 in the third register indicates beta  [with fourth register at 2dp]


ModName="$(basename "$0")"

if [[ $ModName == "qqx" || $ModName == "qqX" ]]; then ModName_qqX=1 ; else  ModName_qqX=  ; fi


 
##  CODING STYLE 

#  Variables & Functions:

#  When coding for qqX, aim for clarity and contextual identity.  Variables should normally tend towards 'CamelCase'  

#  & functions should tend towards 'snake_case'.  Emphasis or separation underscoring & caps should be added where needed. 

#  https://en.wikipedia.org/wiki/Camel_case   https://en.wikipedia.org/wiki/Snake_case


#  Variables {don't} need to be braced unless using string manipulations or needing to stop vars from merging with other text. 


#  In some places ** quickemu's given variable names ** have to be used. These are generally CAPS and very brief, eg '$QEMU'.

#  Except when working with the .conf file, where quickemu chooses to use lowercase.

#  Care should be taken not to confuse them with Bash's BUILT_IN vars, or to confuse their scope, eg. the ambiguous quickemu var '$VERSION'  

#  Likewise care must be taken not to confuse lowercase vars with standard application commands, especially in non-highlighting editors.


#  Modern editors, such as VS Code or Sublime have auto-suggesting which enables longer & more helpful VariableNames.

#  Make use of 'shellcheck' & avoid disables where possible.  The VS Code shellcheck extension works well, if set to check-on-save.


#  Newer style, more powerful, DOUBLE [[ BRACKETS ]] should be used:

#  if [[ $Foo ]] && [[ ! $Bar ]];   NOT   if [ -n ${Foo} ] && [ -z ${Bar} ]; 


#  COMMENTS:  Add clarification or explanation to code, especially if the origin or meaning could be difficult to follow.

#  Also add links to any how-to's that you used.  If you needed to check, then others may do also. 

#  Let others follow what you are/were thinking at the time & give Bash beginners a chance too.

#  It makes it easier for everyone, especially later on.


#  SPACE:  Give your code some room to breathe.  Break it up into logic blocks.  

#  Make it easy to follow & easy to see what is happening.  


#  SED & AWK:  commands can very easily finish up as lines of indecipherable hieroglyphics.

#  If you must use them, keep things very simple & explain the sections.  

#  Where possible use Bash's string manipulations instead.   See: http://mywiki.wooledge.org/BashFAQ/100


#  ECHO:  Beyond basic one-liners, the use of printf should generally be preferred.  

#  See https://ss64.com/bash/printf.html

#  However & specifically, echo within the main section of the actual qqX script *must* be avoided

#  as an echo trap is used to tee quickemu screen output to the qqX boot log.   




## INITIALIZE

# Make sure shell is set during session to decimal separator of dot 

#  If LC_ALL=C changes too much, just set the numeric.   

#  This may upset some input/output filters ....    CAUTION   (adjust in main settings)

#  See locale setting discussion:  https://unix.stackexchange.com/a/149129 

#  Also  https://unix.stackexchange.com/questions/62316/why-is-there-no-euro-english-locale?rq=1

#  &   http://www.unicode.org/L2/L2001/01102-POSIX15897.htm   

if [[ $AllowMoreLocale ]]; then

  export "LC_NUMERIC=C"
  export "LC_COLLATE=C"

else  export "LC_ALL=C"

fi


if  [[ $NoClear ]]; then 

	printf () {

		# shellcheck disable=SC2199
		# checks for occurence of the clearing pattern within the parameter text, treating the text as a single string

		if [[ "$@" ==  '\033c'* ]]; then

			# shellcheck disable=SC2124
			# see http://www.tldp.org/LDP/abs/html/internalvariables.html#APPREF
			# and http://mywiki.wooledge.org/BashGuide/Parameters#Special_Parameters_and_Variables

			PFtext="$@"
			PFtext=${PFtext//'\033c'/}			# remove all escape clears from the printf instruction
      command echo
      command echo "  ***************************************************************************"
      sleep "$NoClear"
			command printf "$PFtext"
    else

      # let all other printf commands through
      command printf "$@"

    fi

	}

fi


if ! tput -V | grep -q ncurses 2> /dev/null; then
  printf "\n\n  Check for your OS >  \n\n  An 'ncurses' installation should normally be present. Test using 'tput -V' "
  sleep 5
  exit 1
fi

if [[ ! -d "/tmp" ]]; then
  printf "\n\n  Check for your OS >  \n\n  A directory '/tmp' should be present. A symbolic link may be needed. "
  printf "\n\n  See:  https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard"
  sleep 5
  exit 1
fi



if [[ $1 == "XDG" ]]; then 

  # set flags & deal with details after the setting up is done

  if [[ -e "$2" ]]; then

    XDG_MimeType="$(mimetype "$2" 2>/dev/null)"
   
    if [[ "$XDG_MimeType" == *'application/x-qemu-disk' ]]; then XDG_MimeType_Valid=1

    else XDG_MimeType_Valid=  ; fi

  fi

  if [[ $XDG_MimeType_Valid ]]; then

    # is a right-click start on a qcow2 disk (set flag for later) 
    XDG_QcowRightClick=1
    shift
    
  elif [[ $2 ]]; then

    # is right click XDG but at this stage needs flagging for later analysis or other type of handling
    XDG_StartVariant=1
    shift

  else

    shift
    # set to $HOME folder and flag to run the VM selector
    cd "$HOME"  || printf "\n\n XDG .desktop starter  >  HOME Folder not found " 
    CurrentFolder="$HOME"
    XDG_GeneralDesktopStart=1

  fi


elif [[ $ModName_qqX ]] && [[ $1 == "--version" || $1 == "-V" ]] ; then

  echo "$ModVersion" ; exit 


elif [[ ! $ModName_qqX ]] && [[ "$1" == *'vm' ]]; then  

  # quickemu-wrap only  &  where test [[ $Conf_TriggerFolder ]] will also flag up if the script was .conf target started

  Conf_TriggerFolder="$(dirname "$0")"
  
  CurrentFolder="$(dirname "$2")"

else 

  CurrentFolder="$(pwd)"
  
fi


# INDIVIDUAL SETTINGS:

custom_conf_notes () {

  printColor "\n\n  qqX .conf reminders:"

  printf "
  
    #!/usr/bin/qqX --vm                (for qqX direct start)

    Custom_SDL_ScreenPercent=\"xx\"      (integer from 25 to 99)
    HeadlessDisplay=\"true\"

    SecondDrive=\"My_Folder/My_VM/disk2.qcow2\"
    SharedDrive_Conf=\"no-shared\"
    MacLegacyDrive=\"true\"

    Other:
    disk_size=\"xxG\"   (integer, used once, for first build)
    cpu_cores=\"x\"     (even integer)
    ram=\"xG\"          (integer)

    boot=\"legacy\"     (use bios, not UEFI)

    Windows:
    tpm=\"on\"          secureboot=\"off\"
    gl=\"off\"          (scaleable SDL, easier restarts) (?)

    Difficult booting: 
    boot=\"legacy\"  &  gl=\"off\"  may help ...

    highlight then shift-ctrl-c  to copy ...
  \n"

}

#  Pick up the SETTINGS file:  If placing the main file in /usr/bin,"

#  then place the settings file as "/$HOME/.qmod_settings, OR if qqX in "/$HOME/.qqX/qqX_settings"

#  For details, see notes at start of the settings file ...

#  Use full quoted path & name if adding in a additional folder to this script or different to these defaults

#  For .conf click starts, the Positional parameters need reading to check the settings file location;

#  this would be expected to be where the specified wrap script is located as a first check


if [[ $ModName_qqX ]]; then SettingsName="qqX_settings"

else SettingsName="qmod_settings" ; fi



if [[ $ModName_qqX ]]; then 

  [[ -e "$HOME/.qqX/qqX_settings" ]] && Q_Mod_SettingsFile="$HOME/.qqX/qqX_settings"

#

elif [[ $Conf_TriggerFolder ]] && [[ -e "$Conf_TriggerFolder/${SettingsName}" ]]; then  

  Q_Mod_SettingsFile="$Conf_TriggerFolder/${SettingsName}"

#

elif [[ -e "$CurrentFolder/${SettingsName}" ]] && [[ "$CurrentFolder" != '/usr'* ]]; then  

  Q_Mod_SettingsFile="$CurrentFolder/${SettingsName}"

elif [[ -e "$CurrentFolder/.${SettingsName}" ]] && [[ "$CurrentFolder" != '/usr'* ]]; then  

  Q_Mod_SettingsFile="$CurrentFolder/.${SettingsName}"

#  

elif [[ -e "$HOME/.qmod_settings" ]] && [[ ! $ModName_qqX ]] ; then  

  Q_Mod_SettingsFile="$HOME/.qmod_settings"

elif [[ -e "$HOME/qmod_settings" ]] && [[ ! $ModName_qqX ]] ; then 

  Q_Mod_SettingsFile="$HOME/qmod_settings"

else  

  Q_Mod_SettingsFile=  

  echo; echo "  No settings file found .... " ; echo ; sleep 2
  
fi



# Declare global associative VM ARRAY & other initializing global vars. Make ready to read settings.

declare -gA VM_Array=()

declare -ga Extra_VM_Folder=()


# Clear or set any default variables, if needed.

VM_ExecBoot=

Extra_QE_Params=

VM_Select_ItemsPerLine="4"


# LOAD SETTINGS & Array PRESETS  

if [[ -e "$Q_Mod_SettingsFile" ]]; then

  # shellcheck disable=SC1090
    
  if [[ $ModName_qqX ]]; then source "$Q_Mod_SettingsFile" ; else source "$Q_Mod_SettingsFile"  QWRAP ; fi

else

  printf "\n\n  ERROR with settings file detection \n\n  Re-check Installation &/or Installation instructions \n\n" ; sleep 10 
  
fi


# Intepret the settings:

function_xdg_start_folder () {

  # For a .desktop start, to be run after the settings file has been loaded so that a $Default_VM_Folder can be available

  #  Also can be called (belt & braces) at the other command line $1 trap towards end of script ...  REVIEW 

  if [[ ! $(ls "$HOME/"*.conf 2> /dev/null) ]] && [[ -d "$Default_VM_Folder" ]]; then

    cd "$Default_VM_Folder"  || printf "\n\n XDG .desktop starter  >  Default_VM_Folder not found " 

    CurrentFolder="$Default_VM_Folder"

  else

    cd "$HOME"  || printf "\n\n XDG .desktop starter  >  HOME Folder not found " 

    CurrentFolder="$HOME"

  fi

}


[[ $XDG_GeneralDesktopStart ]] &&  function_xdg_start_folder


if [[ $XDG_QcowRightClick ]]; then

  XDG_QcowRightClick_Conf_Dir="$(dirname "$1")"

  XDG_QcowRightClick_DiskName="$(basename "$1")"

  XDG_QcowRightClick_VM_Dir_Name="$(basename "$XDG_QcowRightClick_Conf_Dir")"

  # .desktop starts tend to open in the $HOME folder even if right clicking somewhwere else

  cd "$XDG_QcowRightClick_Conf_Dir" || printf "\n\n XDG .desktop starter  >  Disk's start Folder not found " 

  cd ..  || printf "\n\n XDG .desktop starter  >  Disk's root Folder not found " 

  CurrentFolder="$XDG_QcowRightClick_Conf_Dir"

fi


# Create a list of preset VM locations 

function_create_VM_folderList() {

  DefaultFolderError=
  ExtraFolderError=

  # Make sure that 'Default_VM_Folder' has a value and it exists

  [[ ! $Default_VM_Folder ]] && Default_VM_Folder="$CurrentFolder"

  if [[ ! -d "$Default_VM_Folder" ]]; then printf "\n\n  Settings ERROR:  Default Folder NOT found \n\n" ; sleep 2 ; DefaultFolderError=1 ; fi

  
  # set and clear the folder array, current first but don't scan twice

  if [[ "$CurrentFolder" != "$Default_VM_Folder" ]]; then
 
    declare -ga VM_Folder_List=( "$CurrentFolder" "$Default_VM_Folder" )

  else

    declare -ga VM_Folder_List=("$Default_VM_Folder")

  fi


  # shellcheck disable=SC2154

  for xF in "${Extra_VM_Folder[@]}"; do

    [[ $xF ]] && VM_Folder_List+=("$xF")

    if [[ $xF ]] &&  [[ ! -d "$xF" ]] ; then 

      # if .conf targeted start, then no need to print this, 
      # unless re-selecting, in which case the error flag will create a later highlight.
    
      if [[ ! $Conf_TriggerFolder ]]; then 
      
        printf "\n\n  Settings ERROR:\n\n  Extra Folder %s NOT found \n\n"  "$xF" 
        sleep 2 

      fi

      ExtraFolderError=1

    fi

  done

}


# Now populate the list: 

function_create_VM_folderList


# But, if settings need reloading, following a settings edit or a VM deletion:

function_refresh_and_reload_VM_arrays() {

  # NB clear extra folders array

  Extra_VM_Folder=()


  # NB clear pre-set folders array 

  VM_Array=()


  # Re-Read settings file & pre-set folders:

  # shellcheck disable=SC1090

  if [[ $ModName_qqX ]]; then source "$Q_Mod_SettingsFile" ; else source "$Q_Mod_SettingsFile"  QWRAP ; fi

  # make sure other functions know that the settings was was re-read

  SettingsFileAdjusted=1

  # re-load:

  [[ $XDG_GeneralDesktopStart ]] &&  function_xdg_start_folder
 
  function_create_VM_folderList  

  function_scan_VM_folderList  

  check_screen_percentage

  set_drive_paths

  find_main_drive

  # reload screen 

  ByPass_VM_Array_Selector=1

  KVM_MSR_Error=

  ShowUtilsMenu=

  printf "\033c"

  show_qmod_title

}


# Record any set ExtraArgs in case of need to reset

ExtraSetArgs="$ExtraArgs"


#  Add default values, if none supplied

[[ ! $P_Kill_WaitSecs ]] && P_Kill_WaitSecs="6"

P_Kill_WaitSecs_PreSet="$P_Kill_WaitSecs"



#   Check custom QE & QG, plus set the 'quickget' version

[[ $QE_Version ]] && [[ ! -e "$QE_Version" ]] &&  printf "\n\n  ERROR  custom quickemu not found \n\n  Re-check Installation &/or Installation instructions \n\n"  && sleep 10         

[[ $QG_Version ]] && [[ ! -e "$QG_Version" ]] &&  printf "\n\n  ERROR  custom quickget not found \n\n  Re-check Installation &/or Installation instructions \n\n"   && sleep 10         

QG_FilePathDefault="$(type -p quickget)"


function quickget {

  # finds the right version for the standard 'quickget' call 
  # which is only used in simple API wrapping mode, for now ...
  # quick*emu* is later and is more complex

  if [[ $QG_Version ]]; then  
  
    if [[ $1 == "filepath" ]]; then QG_FilePath="$QG_Version"
    
    else  eval command "$QG_Version" " $*" ; fi

  else 

    if [[ $1 == "filepath" ]]; then QG_FilePath="$QG_FilePathDefault"
  
    else eval command quickget " $*" ; fi

  fi
  
}


##  Check for 'curl' and 'aria2' etc  ( where $1, is if command line )

if [[ ! $(type -p curl) ]] ; then
  echo
  echo "  You need to install 'curl' to run parts of the quickget script ..."
  echo
  if [[ $1 ]]; then exit 1 ; else read -rp "  [enter] to continue > " ; fi
fi

if [[ ! $(type -p aria2c) ]]; then
  echo
  echo "  You need to install 'aria2' to run parts of the quickget script ..."
  echo
  if [[ $1 ]]; then exit 1 ; else read -rp "  [enter] to continue > " ; fi
fi

if [[ ! $(type -p zsync) ]]; then
  echo
  echo "  You need to install 'zsync' to run parts of the quickget script ..."
  echo
  if [[ $1 ]]; then exit 1 ; else read -rp "  [enter] to continue > " ; fi
fi


## Check if a default File Manager and a default Web Browser are set or are available

if [[ $FileManager == "no" ]]; then FileManager=

else

  [[ $FileManager ]] && [[ ! $(type -p "$FileManager") ]] && FileManager=

  if [[ ! $FileManager ]] ; then

    if [[ $(xdg-mime query default inode/directory) ]] ; then  FileManager="xdg-open"

    elif [[ $(type -p caja) ]] ; then  FileManager="caja"

    elif [[ $(type -p nautilus) ]] ; then  FileManager="nautilus"

    elif [[ $(type -p dolphin) ]] ; then  FileManager="dolphin"

    elif [[ $(type -p pcmanfm) ]] ; then  FileManager="pcmanfm"

    else FileManager=

    fi

  fi
fi

if [[ $WebBrowser == "no" ]]; then WebBrowser=

else

  [[ $WebBrowser ]] && [[ ! $(type -p "$WebBrowser") ]] && WebBrowser=

  if [[ ! $WebBrowser ]]; then

    if [[ $(xdg-settings get default-web-browser) ]]; then WebBrowser="xdg-open"

    elif [[ $(type -p firefox) ]] ; then  WebBrowser="firefox"

    elif [[ $(type -p 'google-chrome') ]] ; then  WebBrowser="google-chrome"

    elif [[ $(type -p falkon) ]] ; then  WebBrowser="falkon"

    else WebBrowser=

    fi

  fi
fi

printf "\n Handles the output that tells us how firefox etc has handled mixing of gtk2 and gtk3 etc .... \n\n"  > "/tmp/qqX_nohup.txt"


##  START of new functions that may be used by the menu system (or by Mod API)


#  GENERAL COLOR & THEMING   (see settings)

[[ ! $X_Shade ]] &&  X_Shade="3"

#  Yellow 3 (recommended), Blue 4, Cyan 6 (brighter blue), Red 1

#  https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit

#  https://tldp.org/HOWTO/Bash-Prompt-HOWTO/x405.html

#  if source without declared shade, set default shade

[[ ! $X_Shade ]]  && X_Shade=3


echColor () {

	# for X_Shade see qqX configs

  # deprecated > use printColor 

	if [[ $2 ]] ; then

		if [[ "$1" == "-n" ]] ; then
			tput setaf "$X_Shade"
			echo -n "$2"
			tput sgr0
		else
			tput setaf "$X_Shade"
			echo "$@"          			
			tput sgr0
		fi

	else
		tput setaf "$X_Shade"
		echo "$1"
		tput sgr0
	fi

}


# non-color echo trap, mainly designed to capture the technical output that quickemu doesn't send to log file

printf "\n"  > "/tmp/qqX_echo_output.log"

#

echo () { command echo "$@" | tee -a  "/tmp/qqX_echo_output.log" ; }



printColor () {

	tput setaf "$X_Shade"

	# shellcheck disable=SC2059

	printf "$@"

	tput sgr0

}


## Analyse Current Version  (Latest version is carried out in the updater function)

IFS='.' read -ra qqX_CurrentVer_Arr <<< "$ModVersion"

# qqX_CurrentVer_Arr[0] remains unpadded  (& to the left of the decimal point)

[[ ${#qqX_CurrentVer_Arr[1]} -lt 2 ]] && printf -v qqX_CurrentVer_Arr[1] "%.2d" "${qqX_CurrentVer_Arr[1]}"

[[ ${#qqX_CurrentVer_Arr[2]} -lt 2 ]] && printf -v qqX_CurrentVer_Arr[2] "%.2d" "${qqX_CurrentVer_Arr[2]}"

#legacy 3dp pre "0.09.00.017"

[[ ${#qqX_CurrentVer_Arr[3]} -gt 2 ]] && qqX_CurrentVer_Arr[3]="${qqX_CurrentVer_Arr[3]:(-2)}"

[[ ${#qqX_CurrentVer_Arr[3]} -lt 2 ]] && printf -v qqX_CurrentVer_Arr[3] "%.2d" "${qqX_CurrentVer_Arr[3]}"

qqX_CurrentVer_Float="${qqX_CurrentVer_Arr[0]}.${qqX_CurrentVer_Arr[1]}${qqX_CurrentVer_Arr[2]}${qqX_CurrentVer_Arr[3]}"

if [[ "${qqX_CurrentVer_Arr[3]}" ]] && [[ "${qqX_CurrentVer_Arr[2]}" == "00" ]]; then 

  qqX_CurrentReleaseType="beta"

else qqX_CurrentReleaseType="release"

fi


# Check for Qemu & set up some of quickemu's variables, the upper case ones. (Adaption of the quickemu scripting)

# Sets defaults, if not given in main settings. But also for later, so systems can be swapped out.

[[ ! $QEMU_Default_SystemType ]] &&  QEMU_Default_SystemType="qemu-system-x86_64"

[[ ! $QEMU_Default_MachineType ]] &&  QEMU_Default_MachineType="q35"

QEMU="$(type -p "$QEMU_Default_SystemType")"

QEMU_IMG="$(type -p qemu-img)"
   

function qemu_version_check {

  if [[ ! $QEMU ]] || [[ ! $QEMU_IMG ]]; then

    printColor "\n\n QEMU not found. Check installations of qemu and qemu-img"
    printf "\n\n If using the quickemu packaging this should normally have been installed.\n\n"
    sleep 1
    return

  fi

  QEMU_VER_LONG=$( "$QEMU" -version | head -n1 | cut -d' ' -f4 | cut -d'(' -f1 )

  QEMU_VER_SHORT="${QEMU_VER_LONG//./}"

  QEMU_VER_SHORT="${QEMU_VER_SHORT:0:2}"

  if [[ $QemuShortVerOveride ]]; then  QEMU_VER_SHORT="61"

  elif [[ $QEMU_VER_SHORT -lt 60 ]]; then

    printf "\n\n QEMU 6.0.0 or newer is required. Current version is: %s"  "$QEMU_VER_LONG"
    printf "\n\n Use overide in main settings to handle future errors from changes to Qemu numbering format\n\n"
    sleep 1

  fi

}

qemu_version_check



## General default title:

qqX_title="[quickemu quickget X terminal project] - $qqX_CurrentReleaseType: $ModVersion "



## qqX chrome up  ( runs if ModName == qqX [or qqx] )


function_qqX_logo() {
  
  # deprecated

  echColor "  _    _   \/   "
  echColor " (_|  (_|  /\ "
  echColor "   |.   |.    "
}


function_qqX_printf_logo() {

  # printf version (no end line feed)

  printColor "  _    _   \/   "
  printColor " (_|  (_|  /\ "
  printColor "   |.   |.    "
}


# Variant on   http://www.figlet.org/  -f mini  (-k)   (c)  Alex Genovese


function_qqX_logo_title() {

  # deprecated

  if [[ $1 ]]; then

    qqX_LogoTxt="$1"

  else

    qqX_LogoTxt="$qqX_title"

  fi

  echColor "  _    _   \/   "
  echColor " (_|  (_|  /\ "
  echColor "   |.   |.     $qqX_LogoTxt"

}

function_qqX_printf_logo_title() {

  # printf version (no end line feed & also avoids the quickemu echo trap)

  if [[ $1 ]]; then

    qqX_LogoTxt="$1"

  else

    qqX_LogoTxt="$qqX_title"

  fi

  printColor "\n  _    _   \/   "
  printColor "\n (_|  (_|  /\ "
  printColor "\n   |.   |.     %s"  "$qqX_LogoTxt"

}




show_wrap_CLI_usage () {

  printColor  "\n  Command line Usage %s:  \n\n"    "$ModName"

  printf     "  Either plain %s  or  with parameters  --vm  [path/to/vms-folder/]vm-name.conf   " "$ModName"    

  printf     "\n\n  Scripts not located in /usr/bin should be in the VMs folder or a path should be supplied. "

  printf     "\n\n  Start non /usr/bin with './' eg:  ./%s "  "$ModName" 

  printf     "\n\n  Or by clicking on an edited .conf file:"

  printf     "\n\n  Edit the first line of the .conf to point to %s instead of quickemu"  "$ModName" 

  printf     "\n\n  eg. #!/usr/bin/quickemu --vm  becomes  #!/usr/bin/%s --vm  or similar (VM name not needed)" "$ModName" 

  printf     "\n\n  For more complex CLI usage, try the alternative 'q-mod' script or quickemu itself."
  
  printf     "\n\n  @ 2023/08 qqX will now issue short version numbers on 'qqX --version' "

  #printf     "\n\n"

}


function exit {

  # trap to keep terminal open if started by mouse click  -t secs

  # or to stop sourced quickemu quitting as 'normal' procedure   (tput resets any hidden cursors)

  if [[ $1 == "directly" ]]; then

    tput cnorm

    command exit

  elif [[ $1 ]] && [[ ! $NoExit ]]; then

    printf "\n\n"

    printColor "  ERROR : [Enter] to quit  or [h] to hold terminal open \n\n"
  
    read -rp  "  >  "  -t 30  ExitTrap

    [[ $ExitTrap == "h" ]] &&  printf "\n\n  Holding terminal open  [Enter] to quit  \n\n"  && read -rp "  >  "

    tput cnorm

    # https://unix.stackexchange.com/questions/220330/hide-and-unhide-cursor-with-tput

    command exit "$@"

  fi

  # zero the optional 'exit 1' bypass flag (if it has been set)

  NoExit=

}



## QMOD MSRS FUNCTIONS  (may also be called via the Mod API)  

function_find_kvm_msr_default_and_status () {

  # outputs boths vars 'KVM_MSR_DefaultConf' & 'KVM_MSR_status' with value Y or N 

  # finds and flags if MSRS has a config conflict

  KVM_MSR_Error=
  KVM_MSR_Warn=

  KVM_MSR_status="$(cat /sys/module/kvm/parameters/ignore_msrs)"

  [[ ! $KVM_MSR_ModProbeFile ]] && KVM_MSR_ModProbeFile="/etc/modprobe.d/kvm-quickemu.conf"

  KVM_MSR_DefaultConf="$(cat "$KVM_MSR_ModProbeFile" 2> /dev/null)"

  [[ "$KVM_MSR_DefaultConf" == *'=Y' ]] &&  KVM_MSR_default="Y"

  [[ "$KVM_MSR_DefaultConf" == *'=N' ]] || [[ ! -e "$KVM_MSR_ModProbeFile" ]] &&  KVM_MSR_default="N"

  if [[ $VM_InstanceName ]]; then
  
    if [[ "$VM_InstanceName" == *windows*  ]] ||	[[ "$VM_InstanceName" == *macos*  ]] ; then

      if [[ $KVM_MSR_status == "N" ]]; then KVM_MSR_Error=1 ; else KVM_MSR_Error= ; fi

    elif [[ "$VM_InstanceName" != *windows*  ]] &&	[[ "$VM_InstanceName" != *macos*  ]] ; then

      if [[ $KVM_MSR_status == "Y" ]]; then KVM_MSR_Warn=1 ; else KVM_MSR_Warn= ; fi

    fi

  fi
}

print_kvm_status () {

  function_find_kvm_msr_default_and_status

  if [[ $VM_InstanceName ]]; then

    if [[ $KVM_MSR_Warn ]] && [[ $Verbose_MSR_dialog || $MSR_Warnings ]] || [[ ! $MSR_Warnings && $ShowUtilsMenu ]] ; then 
    
    Show_MSR_Warnings=1 ; else Show_MSR_Warnings= ; fi

    if [[ $KVM_MSR_status == "Y" ]] ; then  
    
      [[ $KVM_MSR_Error || $Show_MSR_Warnings ]] && printf "\n  /sys/module/kvm/parameters/ignore_msrs  = Y" 

      if  [[ $KVM_MSR_Error ]]; then  printColor "  ERROR \n" 

      elif [[ $Show_MSR_Warnings ]]; then printf "  WARNING \n" ; fi
      
    else 

      [[ $KVM_MSR_Error || $Show_MSR_Warnings ]] && printf "\n  /sys/module/kvm/parameters/ignore_msrs  = N" 

      if [[ $KVM_MSR_Error ]]; then printColor "  ERROR \n" 

      elif [[ $Show_MSR_Warnings ]] ; then printf "  WARNING \n" ; fi

    fi

  fi

}


toggle_msr_defaults () {

  # Modded & now reversible rewrite of original quickemu's function 'ignore_msrs_always'

  Verbose_MSR_dialog=1

  # https://www.linux-kvm.org/page/Category:Docs

  if [[ ! -d /etc/modprobe.d ]]; then

    printf "\n  ERROR! /etc/modprobe.d was not found. \n\n  See notes, it may be possible to manually create modprobe.d/kvm-quickemu.conf \n\n"

  else

    printColor "\n\n  Configure default, boot-up, KVM behaviour "
    
    printf "for unhandled machine-specific registers"

    printf "\n\n  Normal setting is N (don't ignore) but Windows and MacOS require Y (true) 'ignore' "
    
    function_find_kvm_msr_default_and_status

    printf "\n\n  Status:  /sys/module/kvm/parameters/ignore_msrs = %s   Current Default = %s"   "$KVM_MSR_status"  "$KVM_MSR_default"


    [[ ! $KVM_MSR_ModProbeFile ]] && KVM_MSR_ModProbeFile="/etc/modprobe.d/kvm-quickemu.conf"

    if [[ ! -e "$KVM_MSR_ModProbeFile" ]]; then 

      printf "\n\n  \'%s\'  needs to be created "  "$KVM_MSR_ModProbeFile" 

    fi

    printf "\n\n  [y] to set Y  [n] to set N  [b] to go back \n\n"

    read -rp  "  > "  Set_MSR_defaults

    # set .conf file content & update initramfs in all kernels (y/n or none)

    if [[ $Set_MSR_defaults == "y" ]]; then

      printf "\n\n  Updating 'initramfs' may take a moment or two ... \n\n"

      # As per Martin's solution in original quickemu, needs 'tee' to get this to work, 
      # but route tee's stdout to null to tidy the screen 

      echo "options kvm ignore_msrs=Y" | sudo tee "$KVM_MSR_ModProbeFile" 1> /dev/null
      sudo update-initramfs -k all -u

    elif [[ $Set_MSR_defaults == "n" ]]; then

      printf "\n\n  Updating 'initramfs' may take a moment or two ... \n\n"

      echo "options kvm ignore_msrs=N" | sudo tee "$KVM_MSR_ModProbeFile" 1> /dev/null
      sudo update-initramfs -k all -u

    fi

  fi

}


show_kvm_msr_general_note () {

  printColor "\n  If you mainly use Windows or Mac VM's then a file '.../modprobe.d/kvm-quickemu.conf' "
  
  printf   "\n\n  can be created to modify the boot up settings. %s has a new built in function"  "$ModName"

  printf   "\n\n  that can set this up & also allows for future adjustments may be made."
  
  printf   "\n\n  Or it may be carried out manually...  See settings, script & further notes for details."
 
  printColor "\n\n\n  Status:  /sys/module/kvm/parameters/ignore_msrs = %s   Current Default = %s"   "$KVM_MSR_status"  "$KVM_MSR_default"
 
  printf "\n\n  Windows or MacOS should be set to 'Y' "

}

show_kvm_sudo_security_note () {

  printColor "\n\n  For session adjustments %s requires 'sudo' permissions to echo true or false to 'ignore_msrs'" "$ModName"
  
  printf   "\n\n  This allows you to create a temporary MSRS status that may be changed at any time,"
  
  printf   "\n\n  allowing you to match the selected guest VM that you want to run."

  
  printf "\n\n\n  Linux distros don't *seem* to be affected when set as true \n\n  but KVM does have a default of false ..."

  
  printColor "\n\n\n  If you have concerns about this script, or about giving elevated permissions, "
  
  printf   "\n\n  then the script should be checked or you should issue these commands manually:"

  
  printf   "\n\n\n  Use shift-crtl-c (or your distros variant) to copy the displayed sudo command, then"
  
  printf   "\n\n  open a side terminal & use shift-crtl-v to paste it.  In this way, elevated permissions"
  
  printf   "\n\n  will only exist in the side terminal & will cease as soon as it is closed."
  

  printf   "\n\n\n  Run the command & close the terminal."

  printf   "\n\n  Return to %s & select 'leave as ...'  The msrs settings will auto-update. "   "$ModName" 

  printf   "\n\n"
  
}


select_msr_config () {

  # MSR_offer normally present if MSRS/OS conflict previously detected, 
  # however, presume selector is being used to change current status  REVIEW 

  # where KVM_MSR_status="$(cat /sys/module/kvm/parameters/ignore_msrs)"

  function_find_kvm_msr_default_and_status

  if [[ $KVM_MSR_status == "Y" ]]; then 

    if [[ "$VM_InstanceName" == *windows*  ]] ||	[[ "$VM_InstanceName" == *macos*  ]]; then  MSR_offer="N" 

    else  MSR_offer="neutral" ; fi
    
  else 
  
    MSR_offer="Y" 
    
  fi

  KVM_MSR_selector=

  [[ $KVM_MSR_selector_LoadHelp ]] && show_kvm_msr_general_note  


  while true ; do

    if [[ $MSR_offer == "Y" ]]; then

      printf "\n\n  Sudo command to set as Y:  echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs"

      printColor "\n\n  [y] to set session to Y "

      printf "  [enter]  leave as N"

    elif [[ $MSR_offer == "neutral" ]]; then

      printf "\n\n  Sudo command to set as N:  echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs"

      printf "\n\n  [n] to set session to N "

      printColor "  [enter]  leave as Y"

    else

      printf "\n\n  Sudo command to set as N:  echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs"

      printColor "\n\n  [n] to set session to N "

      printf "  [enter]  leave as Y"

    fi
    
    if [[ $KVM_MSR_selector == "h" ]] || [[ $KVM_MSR_selector_LoadHelp ]] ; then 
    
      if [[ $FurtherNoteFlag ]]; then FurtherNoteFlag= ; else  printf "   [f] see further notes" ; fi
      
      printf "   [d] to set the defaults (on boot up) \n\n"

    else  printf "   [h] see help  \n\n" 

    fi
    
    read -rp "  >  "  KVM_MSR_selector
    
    printf "\n"

    [[ ! $KVM_MSR_selector ]]  &&  break  

    [[ $KVM_MSR_selector == "y" && $MSR_offer == "N" ]] || [[ $KVM_MSR_selector == "n" && $MSR_offer == "Y" ]] && break

    if [[ $KVM_MSR_selector == "y" ]] || [[ $KVM_MSR_selector == "n" ]]; then   

      # As per Martin's solution in original quickemu, needs 'tee' to get this to work, 
      # but route tee's stdout to null to tidy the screen  

      [[ $KVM_MSR_selector == "y" ]] &&  echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs  1> /dev/null   

      [[ $KVM_MSR_selector == "n" ]] &&  echo 0 | sudo tee /sys/module/kvm/parameters/ignore_msrs  1> /dev/null  

      print_kvm_status

      printColor "\n\n\n  [enter]  to return \n\n"
      read -rp  "  > "

      break

    fi

    if [[ $KVM_MSR_selector == "d" ]]; then

      toggle_msr_defaults  

      if [[ $Set_MSR_defaults == "b" ]]; then 
      
        Set_MSR_defaults= 
        print_kvm_status
        printColor "\n\n\n  Make TEMPORARY setting adjustments to MSRS ?"

      else 
        function_find_kvm_msr_default_and_status
        break 
      fi 

    elif [[ $KVM_MSR_selector == "f" ]]; then

      show_kvm_sudo_security_note

      FurtherNoteFlag=1

    fi

  done

  KVM_MSR_selector=
  KVM_MSR_selector_LoadHelp=

}

msrs_conflict_check_resolver() {  

  # Do a check ...

  function_find_kvm_msr_default_and_status
    
  # Display & Offer config settings if MSRS/OS CONFLICT exists

  if [[ $KVM_MSR_status == "N" ]] ; then

    # usual system default = N

    if [[ "$VM_InstanceName" == *windows*  ]] ||	[[ "$VM_InstanceName" == *macos*  ]] ; then 

      [[ ! $ByPass_VM_Array_Selector ]] && printf "\033c"
    
      printColor "\n\n  Selected: %s "  "$VM_InstanceName"
      
      printf " 'ignore_msrs' is set & is recommended for Windows and Mac"  

      MSR_offer="Y"

      select_msr_config

      function_find_kvm_msr_default_and_status

      if [[ $KVM_MSR_status == "N" ]]; then KVM_MSR_Error=1 ; else KVM_MSR_Error= ; fi

    fi
    
  else

    #  Status = Y  &  which is only recommended for Windows & Mac 

    if [[ "$VM_InstanceName" != *windows*  ]] &&	[[ "$VM_InstanceName" != *macos*  ]] ; then

      [[ ! $ByPass_VM_Array_Selector ]] && printf "\033c"

      printColor "\n\n  Selected: %s "  "$VM_InstanceName"
    
      printf "\n\n 'ignore_msrs' is set:  can be okay for other OS's but is normally used only with Windows and Mac"  

      MSR_offer="N"

      select_msr_config

      function_find_kvm_msr_default_and_status

      if [[ $KVM_MSR_status == "Y" ]]; then KVM_MSR_Error=1 ; else KVM_MSR_Error= ; fi

    fi

  fi

}

#   End of MSRS functions


##  Start ADD-IN of ORIGINAL quickemu functions & the needed variables

#   Technically, this script absorbs rather than wraps. 

#   The original code remains basically as is but some calls get re-routed via new functions ....


## Load the right version of quickemu  (from settings)

# shellcheck disable=SC2154

if [[ $QE_Version ]]; then QuickEmuSource="$QE_Version" ; else QuickEmuSource="/usr/bin/quickemu" ; fi

# shellcheck disable=SC2154

if [[ ! $(type -p "$QuickEmuSource") ]] ; then

  printf "\n\n  QUICKEMU not found. See https://github.com/quickemu-project/quickemu "

  printColor  "\n\n  ERROR  Settings or Installation FilePath:\n\n  %s \n\n"  "$QuickEmuSource"

  exit  hold

fi

# Ignore first bits, bash check & locale preamble etc.  

# QE starts into setting out the functions almost straight away.  

QE_StartFuncsPoint=$(grep -n -m 1 'function' "$QuickEmuSource" | cut -d ':' -f 1)

# grep -n : print line number with the output lines   -m : stop after NUM selected lines

((QE_StartFuncsPoint -= 1))

QE_EOfuncsPoint=$(grep -n '### MAIN' "$QuickEmuSource" | cut -d ':' -f 1)

QE_EOfuncsTrim=$((QE_EOfuncsPoint - QE_StartFuncsPoint -1))

tail +"$QE_StartFuncsPoint" "$QuickEmuSource" | head -"$QE_EOfuncsTrim"  > "/tmp/qmod-functions-temp"

source "/tmp/qmod-functions-temp" 


## Quickemu Variables (set & clear)

#  have to be cleared before every call to quickemu as the base code is normally set to be run once & exit

QE_VarsEndPoint=$(grep -n '# Take command line arguments' "$QuickEmuSource" | cut -d ':' -f 1)

QE_varsLines=$((QE_VarsEndPoint - QE_EOfuncsPoint))

tail +"$QE_EOfuncsPoint" "$QuickEmuSource" | head -"$QE_varsLines"  > "/tmp/qmod-qe-all-vars-temp"
  
# BUT don't source as this yet as it is only a repeat of qe-rw-vars-temp

# with QEMU_IMG checker which has been replaced (at the start of qqX)

# and the 3 strangely & unexplainedly read-only vars which need special treatment ...

SpecialVarCheckArr=("LAUNCHER" "DISK_MIN_SIZE" "VERSION")


# Separate the re-writeable vars   

# NB not all vars are listed by the quickemu script ...  

# Also NB the quickemu variables QEMU= etc for qemu versioning get carried out separately by qqX so that machine switching can happen

QE_rw_Vars_StartPoint=$(grep -n -m 1 -e 'readonly' "/tmp/qmod-qe-all-vars-temp" | cut -d ':' -f 1)

((QE_rw_Vars_StartPoint -=2))

head -"$QE_rw_Vars_StartPoint" "/tmp/qmod-qe-all-vars-temp"  > "/tmp/qmod-qe-rw-vars-temp"


function clear_and_load_quickemu_vars {

  source  "/tmp/qmod-qe-rw-vars-temp"

  for SpecialLine in "${SpecialVarCheckArr[@]}"; do

    SpecialLine="$(grep "${SpecialLine}=" "/tmp/qmod-qe-all-vars-temp")"

    SpecialLine="${SpecialLine/'readonly '/}"

    eval "$SpecialLine"

  done

}

# just loads the vars here, but make this as a function so they can be re-sourced later too

clear_and_load_quickemu_vars


  ## OVERWRITE ORIGINAL Functions Source, as required

  declare -f vm_boot  >  "/tmp/qmod-function-vm_boot-orig-temp"

  # shellcheck disable=SC2016

  QE_args_DeclarePoint=$(grep -n -m 1 -e 'local args=()' "/tmp/qmod-function-vm_boot-orig-temp" | cut -d ':' -f 1)

  QE_vm_bootLength=$(wc -l < "/tmp/qmod-function-vm_boot-orig-temp")

  QE_vm_bootCutOff=$((QE_vm_bootLength-QE_args_DeclarePoint+1))

  #echo "QE_args_DeclarePoint  $QE_args_DeclarePoint  QE_vm_bootLength  $QE_vm_bootLength  QE_vm_bootCutOff  $QE_vm_bootCutOff"

  head -n -"$QE_vm_bootCutOff" "/tmp/qmod-function-vm_boot-orig-temp"  >  "/tmp/qmod-function-vm_boot-interim-temp"


# add-in CUSTOM VARS here, as replacement values, using same method as for the final boot section

# no indent on 'cat' & EOV lines or it fails:

cat << \EOV >> "/tmp/qmod-function-vm_boot-interim-temp"

    # Overide the quickemu default

    [[ $QEMU_Default_MachineType ]] &&  MACHINE_TYPE="$QEMU_Default_MachineType" 

    if [[ $QEMU_Default_SystemType ]]; then 
    
      printf "\n\n  QEMU_SystemType =  %s   QEMU_MachineType =  %s " "$QEMU_Default_SystemType" "$QEMU_Default_MachineType"  | tee -a "$QemuArgsFile" 

    fi

EOV

# no indent on 'cat' & EOV or it fails

  # then continue with the remainder

  tail +"$QE_args_DeclarePoint" "/tmp/qmod-function-vm_boot-orig-temp" >> "/tmp/qmod-function-vm_boot-interim-temp"


  # shellcheck disable=SC2016

  QE_qemuCallPoint=$(grep -n -m 1 -e 'QEMU}" "${SHELL_ARGS' "/tmp/qmod-function-vm_boot-interim-temp" | cut -d ':' -f 1)

  QE_vm_bootLength=$(wc -l < "/tmp/qmod-function-vm_boot-interim-temp")


  QE_vm_bootCutOff=$((QE_vm_bootLength-QE_qemuCallPoint+1))

  head -n -"$QE_vm_bootCutOff" "/tmp/qmod-function-vm_boot-interim-temp" >  "/tmp/qmod-function-vm_boot-new-temp"




## ADD-IN extra code  ( place between the first EOF line & the brace that closes the tail trimmed boot function, just before the second EOF)

# See https://stackoverflow.com/questions/22697688/how-to-cat-eof-a-file-containing-code     ( NB use escape EOF, not quotes )

cat << \EOF >> "/tmp/qmod-function-vm_boot-new-temp"

    #  REVIEW can patching in $WinHyperV_RC  & creating a RC reader function be done here ??

    # Show all the compiled qemu & swmtp arguments as a human readable list 
    # both to a log file and to screen   (Args & Args/HW record are defined after the VM selection or re-selection)

    # @2023/08  in qqX, the quickemu screen output now gets logged


    # @2023 the quickemu .sh file gets to be more readable in version 4.8 here:  
    
    # https://github.com/quickemu-project/quickemu/pull/707

    # BUT  sed -i only seems to work in GNU sed  >  fix errored fix here ....  REVIEW 

    #  https://stackoverflow.com/questions/6537490/insert-a-line-at-specific-line-number-with-sed-or-awk#6537587


    # Using $VERSION $VMDIR and other internal quickemu variables  & adding the qqX extra args:

    { printf "\n#  qqX:  This file is NOT used by quickemu to run the VM but it will run it if the script is placed"

      printf "\n\n#  in the calling folder, where the .conf file is located, and if it is given execute permission ...\n\n"
    
    } >> "${VMDIR}/${VMNAME}.sh"

    echo "${QEMU}" "${SHELL_ARGS}${ExtraArgs}${SharedDriveArgs}${SecondDriveArgs}" >> "${VMDIR}/${VMNAME}.sh"

    [[ "$(sed --version | head -1)" == *'GNU'* ]] && sed -i -e 's/ -/ \\\n    -/g' "${VMDIR}/${VMNAME}.sh"


    printf   "\n  Present Working Directory:  %s"  "$(pwd)" > "$QemuArgsFile"
    printf "\n\n  Qemu:      %s  %s" "$QEMU" "$QEMU_VER_LONG" >> "$QemuArgsFile"
    printf "\n\n  QuickEmu:  %s" "$VERSION"  >> "$QemuArgsFile"
    printf "\n\n  %s:       %s" "$ModName" "$ModVersion" >> "$QemuArgsFile"
    printf "\n\n  Date:      %s \n" "$(date)" >> "$QemuArgsFile"

    i=0

    while [[ "${args[i]}" ]]; do

      a="${args[i]}"
      if [[ "$a" == -dev* ]]; then printf "\n\n  %s" "$a" >> "$QemuArgsFile"
      elif [[ "$a" == -* ]]; then printf "\n  %s" "$a" >> "$QemuArgsFile" 
      else printf " %s" "$a" >> "$QemuArgsFile"
      fi

      if [[ "$a" == *'(c)'* ]]; then
        a="${a//(/'\('}"
        a="${a//)/"\)"}"
        args[i]="$a"
      fi
      
      # add extra spaces
      args[i]=" $a"

      # add quotes - REVIEW ?? should be quoted, theoretically, but runs counter to quickemu
      # if [[ "$a" != -* ]]; then [[ "$a" != \'* ]] && args[i]='"'"$a"'"' ; fi

      ((i+=1))

    done


    declare -p args > "$VM_Conf_Dir/$VM_InstanceName/Qemu_args_Array.txt" 

    [[ $ExtraArgs ]] && printf "\n\n  ExtraArgs: %s "  "$ExtraArgs"  >> "$QemuArgsFile"

    [[ $SharedDriveArgs ]] && printf "\n\n  SharedDriveArgs: \n %s "  "$SharedDriveArgs"  >> "$QemuArgsFile"

    [[ $SecondDriveArgs ]] && printf "\n\n  SecondDriveArgs: \n %s "  "$SecondDriveArgs"  >> "$QemuArgsFile"

    if [[ $tpm == "on" ]]; then 
      printf "\n\n\n  Secure Boot:  %s \n\n  %s " "$SWTPM" "${tpm_args[*]}" >> "$QemuArgsFile"
    else    
      printf "\n\n\n  Secure Boot:  Not Set" >> "$QemuArgsFile"
    fi
      printf "\n\n\n"  >> "$QemuArgsFile"

    if [[ $VerboseArgs ]]; then 

      # hold the launch till we have checked the config 
      BreakAtVerboseArgs=
      cat "$QemuArgsFile"
      printColor "  [enter] to continue  [b] to go back \n\n"
      read -rp " >  " BreakAtVerboseArgs
      printf "\n\n"
      [[ $BreakAtVerboseArgs == "b" ]]  && return

    fi


    mapfile -t QE_echo_Arr < "/tmp/qqX_echo_output.log"

    { printf "\nQUICKEMU SCREEN OUTPUT & other ...  \n"

      for Line in "${QE_echo_Arr[@]}"; do

        if [[ "$Line" == *'-name'* ]]; then
        
          printf "\n\n\n$Line"

          printf "\n\n\nNote, the .sh file is NOT used by quickemu to run the VM but it will run it if the script is placed"

          printf "\n\nin the calling folder, where the .conf file is located, and if it is given execute permission ..."

        elif [[ "$Line" == *'/usr/bin/env bas'* ]]; then true

        elif [[ ! "$(tr -cd '[:print:]' <<< "$Line")" ]]; then true

        else  printf "\n\n$Line"

        fi

      done
      
      printf "\n\n\n\n"
    
    } >> "$QemuArgsFile"


    ##  Make sure we have a record of the virtual hardware that Qemu creates. Especially useful for Windows builds.

    #   See  https://github.com/quickemu-project/quickemu/issues/572#issuecomment-1531348755

    [[ ! -e "$QemuArgsFile" ]] && printColor "\n\n  ERROR QemuArgsFile %s failed to create \n\n"  "$QemuArgsFile" && sleep 3 && exit 1

    [[ ! -e "$QemuVirtHardwareRecord" ]]  &&  cp "$QemuArgsFile"  "$QemuVirtHardwareRecord"
            
    ## call QEMU  (with any extras, if present)

    # https://qemu.readthedocs.io/en/latest/system/invocation.html  '-D'  for errors is problematic, use log file
 
    eval "$QEMU"  "${args[*]}"  "$ExtraArgs"  "$SharedDriveArgs"  "$SecondDriveArgs" 2> "$QemuErrorLog" &  

  fi
  
}

EOF


## Now add the modded boot code as the new function 'vm_boot'

source "/tmp/qmod-function-vm_boot-new-temp"


## Add in the standard parameter cases 

tail +"$QE_EOfuncsPoint" "$QuickEmuSource" > "/tmp/qmod-endof-file-temp"

# note ' case' with space, otherwise grep picks up the word "Lowercase" on line 3

QE_StartCasePoint=$(grep -n -m 1 ' case' "/tmp/qmod-endof-file-temp" | cut -d ':' -f 1)

QE_EoCasePoint=$(grep -n -m 1 'esac' "/tmp/qmod-endof-file-temp" | cut -d ':' -f 1)

((QE_EoCasePoint -= (QE_StartCasePoint -1) ))

tail "+$QE_StartCasePoint" "/tmp/qmod-endof-file-temp" | head -"$QE_EoCasePoint"  > "/tmp/qmod-case-temp"


##  Add in the standard actions 

((QE_EoCasePoint += (QE_StartCasePoint +2) ))

QE_vm_bootPoint=$(grep -n -m 1 'vm_boot' "/tmp/qmod-endof-file-temp" | cut -d ':' -f 1)

((QE_vm_bootPoint -= 2))

QE_EoLength=$(wc -l < "/tmp/qmod-endof-file-temp")

QE_BootRemainder=$((QE_EoLength - QE_vm_bootPoint))

tail "+$QE_EoCasePoint" "/tmp/qmod-endof-file-temp" | head -n -"$QE_BootRemainder" > "/tmp/qmod-actions-temp"

tail "+$QE_vm_bootPoint" "/tmp/qmod-endof-file-temp" | head -n -1 > "/tmp/qmod-boot-temp"



function quickemu {      

  # Any calls to quickemu become re-routed to this function instead.

  # Check 'mod' params first, then quickemu's

  # plus quickemu_vars have to be cleared before every call to quickemu as the base code is normally set to be run once & exit

  clear_and_load_quickemu_vars


  ## Do a first check for amy qwrap internal commands 

  local FirstParamCount=0

  IFS=' '  read -ra FirstParamsArray <<< "$*"

  # declare -p FirstParamsArray

  # read

  # https://unix.stackexchange.com/questions/50654/function-caller-positional-parameters?rq=1

  while [[ "${FirstParamsArray[$FirstParamCount]}" ]]; do

    for FirstParam in "${FirstParamsArray[@]}"; do

      case "$FirstParam" in
      
        --vm_boot)  
            VM_ExecBoot=1 
            unset "FirstParamsArray[$FirstParamCount]"
            set -- "${FirstParamsArray[@]}"
            # no other cases applicable, move on.
            break
            ;;
        --menu_bypass)
            # used for msr functions below
            unset "FirstParamsArray[$FirstParamCount]"
            set -- "${FirstParamsArray[@]}"          
            shift ;;
        --toggle_msr_defaults)  
            toggle_msr_defaults 
            exit directly ;; 
        --select_msr_config)  
            select_msr_config 
            exit directly ;; 
        --msrs_conflict_check_resolver)  
            msrs_conflict_check_resolver 
            exit directly ;; 
      esac

      ((FirstParamCount+=1))

    done

  done

  ## Run the code, by sourcing the sections  - first read the cased instructions, then do the actions

  #  These bits are basically the remaining sections of the original code. 
  
  #  The earlier original code sections are all functions that have now been loaded, as is, or modified

  if [[ -e "/tmp/qmod-case-temp" ]] && [[ -e "/tmp/qmod-actions-temp" ]] && [[ -e "/tmp/qmod-boot-temp" ]]; then
    
    while [[ $1 ]]; do

      source "/tmp/qmod-case-temp"  

    done  

    # after read, remove any leftover params not cleared due to trapped exit re-route 

    # & if not yet given, decide on action   

    #  https://unix.stackexchange.com/questions/18981/how-to-unset-the-positional-parameters

    shift  $#

    source "/tmp/qmod-actions-temp"  


    if [[ $VM_ExecBoot ]]; then

      # make sure quickemu doesn't run the vm boot sequence after running an 'action' unless flagged to do so

      # load extracted standard quickemu code that gives the okay to run main function 'vm_boot' (in this case, the modded one)

      # REVIEW  these logs only get cleared just before running, so stay until a boot sequence is run, irrespective of
      #         whether the run is made as [v] verbose & terminated

      printf "" > "/tmp/qqX_echo_output.log"

      printf "" > "$QemuErrorLog"

      source "/tmp/qmod-boot-temp" 

      VM_ExecBoot=

    fi

  else

    printColor "\n\n ERROR %s restart needed \n\n"  "$ModName"

    sleep 5

  fi

}


# Temps usually get removed with reboot 

# BUT can get removed by time (eg 24hrs) so check they are there still ( REVIEW  if causes problems )

# See https://serverfault.com/questions/377348/when-does-tmp-get-cleared#377349


## The quick EMU/GET version numbers, now that rerouting functions quickemu & quickget are in place & code has been sourced

QG_VerNumber="$(quickget "--version")"

# Quick EMU uses its internal var '$VERSION'

QE_VerNumber="$VERSION"


##  From quickemu 4.8 onwards, the value " --screenpct xx"  may be used on SDL, where xx is a value from 25 to 99  & Linux VM's only.

#  But check the to see if the case option exists, don't rely on the version number as formats change or could be a custom version.

#  REVIEW  make sure the tmp files are reading the right version before qqX machine swap edition is released

#  Runnning this when loading the main menu, and after a VM has been selected, enables individual per .conf values, as well as default

function check_screen_percentage {

  SDL_ScreenPercent=

  SDL_ScrnPcnt_Cmd=

  if [[ $VM_Conf_File ]]; then

    eval "$(grep -s Custom_SDL_ScreenPercent "$VM_Conf_File")"

    [[ $Custom_SDL_ScreenPercent ]] && SDL_ScreenPercent="$Custom_SDL_ScreenPercent"

    eval "$(grep -s HeadlessDisplay "$VM_Conf_File")"

    [[ $HeadlessDisplay != "true" ]] && HeadlessDisplay= 

  fi

  if [[ ! $SDL_ScreenPercent ]]; then

    [[ $Default_SDL_ScreenPercent ]] && SDL_ScreenPercent="$Default_SDL_ScreenPercent"

  fi

  if [[ $HeadlessDisplay ]]; then

    # NB headless only runs in Spice

    HeadlessCmd=" --display  none"

  fi

  if [[ $SDL_ScreenPercent ]]; then 
  
    SDL_ScrnPcnt_Error=

    if (( 24 < SDL_ScreenPercent && SDL_ScreenPercent < 100 )); then

      if [[ -e "/tmp/qmod-case-temp" ]]; then 

        if grep -q -e 'screenpct' "/tmp/qmod-case-temp" ; then 
        
          SDL_ScrnPcnt_Cmd=" --screenpct  $SDL_ScreenPercent"

        else

          printColor "\n\n  WARNING:  SDL_ScreenPercent needs quickemu 4.8+ \n\n "
          SDL_ScrnPcnt_Error="> not possible with this qe-version"

        fi

      fi

    else
      
      printColor "\n\n  ERROR:  SDL_ScreenPercent = %s  Range is 25 to 99 \n\n "  "$SDL_ScreenPercent"
      SDL_ScrnPcnt_Error="Out of Range"

    fi

  fi

}  



function set_drive_paths {

  #  For a SECOND/SHARED DRIVE (thanks @gnudoc on the quickemu Discord channel)  do "qemu-img create -f qcow2 disk2.qcow2 40G"  or as required

  #  Set  ExtraArgs=" -device virtio-blk-pci,drive=SharedDrive -drive id=SharedDrive,if=none,format=qcow2,file=/vm_folder/disk2.qcow2"  

  #  Edit path & boot a VM.  Use 'Disks'  Gparted or similar to find, format and mount.  

  #  The function needs to be run everytime a different VM is selected to allow for individual VM settings or requirements.


  #  Older versions of MacOS require a non pci set up. See quickemu script "Qemu optimisations based on the macOS release" 

  #  Any OS older than and including 10.13 Mojave 2018 should have a flag in the .conf file > MacLegacyDrive="true"

  #  See https://en.wikipedia.org/wiki/MacOS_version_history


  #  Manual edit/creation: "qemu-img create -f qcow2 disk2.qcow2 64G"  or as size required, to create a disk.

  #  Thanks @gnudoc on the quickemu Discord channel for initiating this idea.  


  #  SECOND DRIVE and .conf file

  #  Drive path to be individually set in the .conf file on a per VM basis.  

  SecondDrive=
  SharedDrive=
  SharedDrive_Conf=
  SecondDriveArgs=
  SharedDriveArgs=
  MacLegacyDrive=

  if [[ $VM_Conf_File ]]; then

    eval "$(grep -s MacLegacyDrive "$VM_Conf_File")"

    if [[ $MacLegacyDrive == "true" ]]; then  BusType="ide-hd,bus=ahci.2" 
          
    else BusType="virtio-blk-pci"; fi

    #  SECOND DRIVE 

    #  Drive path to be individually set in the .conf file on a per VM basis.  

    eval "$(grep -s SecondDrive "$VM_Conf_File")"

    if [[ -e "$SecondDrive" ]]; then

      SecondDriveArgs=" -device ${BusType},drive=SecondDrive -drive id=SecondDrive,if=none,format=qcow2,file=$SecondDrive"  

    elif [[ $SecondDrive ]]; then 
      
      printf "\n\n ERROR SecondDrive NOT found (.conf file)"

      printf "\n\n  %s \n\n"  "$SecondDrive"

      sleep 1.4

    fi

  fi

  #  SHARED DRIVE

  #  This drive should be availble to all VMs and act as a shared resource.  See main settings.

  if [[ $(grep -s 'SharedDrive=' "$Q_Mod_SettingsFile" | grep -v '#' | grep -c 'SharedDrive') == 1 ]]; then

      eval "$(grep -s 'SharedDrive=' "$Q_Mod_SettingsFile" | grep -v '#' )"

  fi

  if [[ $SharedDrive ]]; then 

    eval "$(grep -s SharedDrive_Conf "$VM_Conf_File")"

    if [[ $MacLegacyDrive ]] || [[ $SharedDrive_Conf ==  "no-shared" ]]; then 

      SharedDrive=
      SharedDriveArgs=

    elif [[ -e "$SharedDrive" ]]; then

      [[ ! $BusType ]] && BusType="virtio-blk-pci"

      SharedDriveArgs=" -device ${BusType},drive=SharedDrive -drive id=SharedDrive,if=none,format=qcow2,file=$SharedDrive"  

    elif [[ $SharedDrive ]]; then 

      printf "\n\n ERROR SharedDrive NOT found (main settings)"

      printf "\n\n  %s \n\n"  "$SharedDrive"

      sleep 1.4

    fi

  fi

}  


## MAIN DRIVE

#  Drive path to be individually set in the .conf file on a per VM basis.  

#  The function needs to be run everytime a different VM is selected.

#  Added so we can distinguish between different drives for snapshots.

#  Finds the quickemu var 'disk_img' which is the main drive 'disk.qcow2' 

function find_main_drive {

  MainDrive=
  disk_img=
  MainDrive_NOT_present=

  if [[ $VM_Conf_File ]]; then

    eval "$(grep disk_img "$VM_Conf_File" 2>/dev/null)"

    if [[ -e "$disk_img" ]]; then

      MainDrive="$disk_img"

    else
      
      MainDrive_NOT_present=1

    fi

  fi

}  



##  WRAPPING FUNCTIONS FOR 'quickget' 


print_quickget_choices () {

  # listing function for use inside 'function_quick_get_wrap'

  # adapted with $2 as number per line   ( REVIEW )

  declare -n QGW_List_ArrName="$1"

  QGW_List_Arr_ItemLength=15

  if [[ $2 ]]; then QGW_List_NewLinePoint="$2"; else QGW_List_NewLinePoint="7"; fi

  QGW_List_ArrNum=0

  QGW_List_ArrNum_Last="${#QGW_List_ArrName[@]}"

  QGW_List_DisplayNum=1

  QGW_List_NewLineCounter=1

  for QGW_List_Arr_Item in "${QGW_List_ArrName[@]}" ; do

    if [[ "${#QGW_List_Arr_Item}" -gt "$QGW_List_Arr_ItemLength" ]]; then 

      ((QGW_List_NewLinePoint-=1))
      ((QGW_List_Arr_ItemLength+=3))
      
    fi

  done

  for QGW_List_Arr_Item in "${QGW_List_ArrName[@]}" ; do

    printf "  %2d)  %-${QGW_List_Arr_ItemLength}s  "  "$QGW_List_DisplayNum"  "$QGW_List_Arr_Item"

    ((QGW_List_ArrNum+=1))

    ((QGW_List_DisplayNum+=1))

    ((QGW_List_NewLineCounter+=1))

    if [[ $QGW_List_NewLineCounter == "$QGW_List_NewLinePoint" ]];  then
    
      QGW_List_NewLineCounter=1   # reset counter
      
      [[ $QGW_List_ArrNum != "$QGW_List_ArrNum_Last" ]] &&  printf "\n\n"

    fi

  done

}


function_quick_get_wrap ()  {

  printf "\033c"

  if [[ $ModName_qqX ]]; then  function_qqX_printf_logo_title "$ModVersion"

  else  printColor "\n\n  %s  ver %s"  "$ModName"  "$ModVersion"

  fi
  
  # shellcheck disable=SC2154

  printf "  Wrapping quickget version %s  (needs internet, including for some menu items & for tests) \n\n\n" "$QG_VerNumber"
 
  tput sc
  echo 
  echo "  Checking connections ..." ; echo

  InternetUp=
  Test_Internet=

  while [[ ! $InternetUp ]]; do   

    if wget -q -T1 -t2 --spider 1.1.1.1  2> /dev/null ; then InternetUp=1

    elif wget -q -T2 -t2 --spider cloudflare.com  2> /dev/null ; then InternetUp=1

    elif wget -q -T2 -t2 --spider google.com 2> /dev/null ; then InternetUp=1

    elif  ( ping -w 2  1.1.1.1 | grep -q ttl ) 2> /dev/null ; then InternetUp=1

    elif ( ping -w 2  8.8.8.8  | grep -q ttl ) 2> /dev/null ; then InternetUp=1  

    # Note that 'ping' doesn't work inside a Qemu VM, so prefer wget
      
    else

      printColor "\n\n  INTERNET CONNECTION NOT FOUND. Check settings and equipment \n\n"

      printf "\n\n  [enter] to re-test,  [i] ignore, continue anyway  [b] back to main menu"

      read -rp "   >  "   Test_Internet

      echo

      [[ $Test_Internet == "b" ]] &&  QGW_List_NumSelected="b"  && break   

      [[ $Test_Internet == "i" ]] &&  break   

    fi

  done


  if [[ $MainMenuZsync ]] && [[ $Zsync_Updatable_ISO && $Zsync_OS_Name && $Zsync_Release_Name ]] ; then  

    QGW_OS_Arr_NameSelected="$Zsync_OS_Name"

    QGW_Release_Arr_NameSelected="$Zsync_Release_Name"

  else

    tput rc; tput ed  

    QGW_OS_ListRaw="$(quickget | tail -n +2 )"

    QGW_OS_ListString="${QGW_OS_ListRaw/'- Operating Systems: '/}"

    read -ra QGW_OS_Array <<< "$QGW_OS_ListString"

    print_quickget_choices  "QGW_OS_Array"

    printColor "\n\n\n  Give list NUMBER of OS name to select for download or to browse OS website  [b] to go back \n\n"    

    read -rp "  >  "  QGW_List_NumSelected


    if [[ $QGW_List_NumSelected == "b" ]]; then 
    
      QGW_List_NumSelected=

      return

    else

      [[ $QGW_List_NumSelected -gt "${#QGW_OS_Array[@]}" ]] && QGW_List_NumSelected="${#QGW_OS_Array[@]}"

      QGW_OS_Arr_NumSelected=$((QGW_List_NumSelected-1))

      QGW_OS_Arr_NameSelected="${QGW_OS_Array[QGW_OS_Arr_NumSelected]}"

      printColor  "\n\n  Selected = %s  "  "$QGW_OS_Arr_NameSelected"

      # OS HOME PAGES  could be pulled in variable quality from the download URL

      # However, and while, some of @zen0bit's and others work is available (@dec19/2023)
      # some is not  >  https://github.com/quickemu-project/quickemu/pull/864

      # So, just grep quickget source instead. And is probably just as quick, or quicker ...

      # Also add check for quickemu 4.9.1 empty folder error  https://github.com/quickemu-project/quickemu/pull/854

      quickget "filepath"  

      if grep -q -s 'os_homepage' "$QG_FilePath" && ! grep -q -s -e 'echo 33 34 35 36' "$QG_FilePath" ; then 

        printColor "Checking ...."

        QGW_OS_HomePage="$(grep "$QGW_OS_Arr_NameSelected" "$QG_FilePath" | grep 'HOMEPAGE=' | cut -d '=' -f 2)"

        QGW_OS_HomePage="${QGW_OS_HomePage/';;'/}"

        QGW_OS_HomePage="${QGW_OS_HomePage//'"'/}"

        [[ $QGW_OS_HomePage ]] &&  printf "\n\n  HomePage = %s"  "$QGW_OS_HomePage"

        if [[ $QGW_OS_HomePage ]] && wget -q -T1 -t2 --spider "$QGW_OS_HomePage"  2> /dev/null; then printf " (working)"

        else printf " (site down or bad link)" ; printColor " ??" ; fi

      fi

      # REVIEW possible API web search bangs for homepage ?
     
    fi


    QGW_Releases_ListRaw="$(quickget "$QGW_OS_Arr_NameSelected" | tail -n +2 | grep Releases)"

    QGW_Releases_ListString="${QGW_Releases_ListRaw/'- Releases: '/}"

    read -ra QGW_Releases_Array <<< "$QGW_Releases_ListString"

    while true ; do

      if [[ ! ${QGW_Releases_Array[*]} ]] ; then

        printColor "\n\n  Quickemu or internet ERROR, no releases found "
     
      elif [[ ${#QGW_Releases_Array[@]} -eq 1 ]] ; then

        printColor "\n\n  [enter] to continue, only one current release found "

      else

        printf "\n\n"

        print_quickget_choices  "QGW_Releases_Array"
        
        printColor "\n\n  Give LIST number of Release"   

      fi  

      if [[ $WebBrowser ]] && [[ $QGW_OS_HomePage ]]; then printf "\n\n  [w] to open listed web page with %s"  "$WebBrowser"

      else printf "\n\n" ; fi


      printf "  [b] to go back \n\n"   

      read -rp "  >  "  QGW_List_NumSelected  


      if [[ $QGW_List_NumSelected == "b" ]]; then 

        QGW_List_NumSelected= 

        QGW_OS_Arr_NameSelected=
      
        function_quick_get_wrap

        break

      elif [[ $QGW_List_NumSelected == "w" ]]; then 

        nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt" "$WebBrowser"  "$QGW_OS_HomePage"  & 

      elif [[ $QGW_List_NumSelected ]]; then 

        [[ $QGW_List_NumSelected -gt "${#QGW_Releases_Array[@]}" ]] && QGW_List_NumSelected="${#QGW_Releases_Array[@]}"

        QGW_Release_Arr_NumSelected=$((QGW_List_NumSelected-1))

        QGW_Release_Arr_NameSelected="${QGW_Releases_Array[QGW_Release_Arr_NumSelected]}"

        break

      else 

        QGW_Release_Arr_NameSelected="${QGW_Releases_Array[0]}"
      
        break

      fi

    done


    printColor  "\n\n  Selected = %s  %s "  "$QGW_OS_Arr_NameSelected" "$QGW_Release_Arr_NameSelected"

    #

    QGW_Editions_ListRaw="$(quickget "$QGW_OS_Arr_NameSelected" | tail -n +2 | grep Editions)"

    QGW_Editions_ListString="${QGW_Editions_ListRaw/'- Editions: '/}"

    QGW_Editions_ListTest="$(tr -d '[:space:]' <<< "$QGW_Editions_ListString" )"

    if [[ $QGW_Editions_ListTest ]]; then

      read -ra QGW_Editions_Array <<< "$QGW_Editions_ListString"

      if [[ ${#QGW_Editions_Array[@]} -eq 1 ]] ; then

      printf "\n\n  Only one current edition found: "

      QGW_Edition_Arr_NameSelected="${QGW_Editions_Array[0]}"

      else

        printf "\n\n"

        print_quickget_choices  "QGW_Editions_Array"

        printColor "\n\n  Give LIST number of Edition   [b] to go back \n\n"  

        if [[ $QGW_OS_Arr_NameSelected == "fedora" ]]  && [[ $QGW_Release_Arr_NameSelected == "33" ]] || [[ $QGW_Release_Arr_NameSelected == "34" ]]; then 
        
          printf "  Note: Fedora 33 i3 and Fedora 34 Cinnamon are not possible \n\n"

        fi

        read -rp "  >  "  QGW_List_NumSelected   

        if [[ $QGW_List_NumSelected == "b" ]]; then  

          QGW_List_NumSelected=
          QGW_OS_Arr_NameSelected=
          QGW_Release_Arr_NameSelected=
        
          function_quick_get_wrap

        else

          [[ $QGW_List_NumSelected -gt "${#QGW_Editions_Array[@]}" ]] && QGW_List_NumSelected="${#QGW_Editions_Array[@]}"
      
          QGW_Edition_Arr_NumSelected=$((QGW_List_NumSelected-1))

          QGW_Edition_Arr_NameSelected="${QGW_Editions_Array[QGW_Edition_Arr_NumSelected]}"

        fi

      fi

    else

      QGW_Edition_Arr_NameSelected=

      printf "\n\n  Further selection not available"

    fi

  fi


  QGW_DownloadSelection="$QGW_OS_Arr_NameSelected  $QGW_Release_Arr_NameSelected  $QGW_Edition_Arr_NameSelected"

  if [[ $QGW_Edition_Arr_NameSelected ]]; then

    QGW_DownloadName="${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}-${QGW_Edition_Arr_NameSelected}"

  elif [[ $QGW_Release_Arr_NameSelected ]]; then

    QGW_DownloadName="${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}"

  else

    QGW_DownloadName="${QGW_OS_Arr_NameSelected}"

  fi 
  

  # TEST DOWNLOAD URL is correct and is good to go

  function_test_iso_url () {

    QGW_Download_BadLink=

    if grep -q -s -e '--test-iso-url' "$QG_FilePath" && ! grep -q -s -e 'echo 33 34 35 36' "$QG_FilePath"; then

      if [[ $QGW_BadLink_ReTest ]]; then QGW_BadLink_ReTest= ;  else  printf "\n\n" ; fi

      printf "  Checking download sources ... \n\n"

      quickget -t "$QGW_DownloadSelection" &> "/tmp/qqX_get.test.iso.url.log"

      cat "/tmp/qqX_get.test.iso.url.log"

      if grep -q -s "broken" "/tmp/qqX_get.test.iso.url.log"; then

        printColor "  ERROR  Bad link or server down ... \n\n"

        QGW_Download_BadLink=1
    
      fi

    else printf "\n\n"

    fi

    printColor  "  Selected = %s "  "$QGW_DownloadSelection"

  }

  function_test_iso_url

  if [[ $MainMenuZsync ]] && [[ $Zsync_Updatable_ISO ]]; then

    if [[ ! $Zsync_OS_Name || ! $Zsync_Release_Name  ]]; then

      printColor "\n\n  ERROR  Variables for OS Name &/or Release NOT found \n\n"

    else

      printf "\n\n  Updating the live boot development iso with any changes"

      printColor "\n\n  May take a while to synchronise"

      [[ $LastZsync -gt 200000 ]] && printColor " - last updated over %d days ago"  "$((LastZsync/86400))"

      printf "\n\n"

      ZsyncDownload=1

    fi

  elif [[ "$QGW_DownloadSelection" == *'ubuntu'* ]] && [[ "$QGW_DownloadSelection" == *'daily'* ]]; then 
  
    printColor "  ** may download with some kind of NAMING variance ** "

    ZsyncDownload=1

  else

    ZsyncDownload=

  fi

  function show_current_preset_folder_list {

    i=0

    printf "\n"

    while [[ ${VM_Folder_List[i]} ]]; do

      if [[ $i -eq 0 ]]; then printf "\n\n  [0]  %s (current)"  "${VM_Folder_List[i]}"

      elif [[ $i -eq 1 ]]; then printf "\n\n  [1]  %s (default)"  "${VM_Folder_List[i]}"

      else printf "\n\n  [%s]  %s "  "$i" "${VM_Folder_List[i]}"

      fi

      ((i +=1))

    done

  }


  if [[ $MainMenuZsync ]] && [[ $Zsync_Updatable_ISO && $Zsync_OS_Name ]]; then 

      QWG_DownLoadFolder="$VM_Conf_Dir"
      ZsyncDownload=1

  else

    show_current_preset_folder_list

    printf "\n\n\n"

    QWG_DownLoadFolder="$Default_VM_Folder"

  fi

  
  # READY TO START DOWNLOAD 

  QGW_OverWriteAllow=
  
  while true ; do

    printf "  Installation target folder:  %s "   "$QWG_DownLoadFolder"  

    # If not sync updating, list other copies of similar VM's in the target folder

    if [[ ! $ZsyncDownload ]]; then

      SimilarVMs=
      SimilarVM_ShorterCheck=
      QGW_OverWrite=
      DailyVMsPresent=

      if [[ -e "$QWG_DownLoadFolder/$QGW_DownloadName" ]]; then

        printColor "\n\n  ERROR?  VM is already present? "

        printf "\n\n  %s / %s "    "$QWG_DownLoadFolder" "$QGW_DownloadName"

        [[ ! $QGW_OverWriteAllow ]] && QGW_OverWrite=1
        
      else

        declare -a SimilarVM_Arr
       
        SimilarVMs="$(ls "$QWG_DownLoadFolder/${QGW_OS_Arr_NameSelected}"*.conf 2>/dev/null )"

        if [[ ! $SimilarVMs ]] && [[ "$QGW_OS_Arr_NameSelected" == *'-'* ]]; then 
        
          SimilarVM_ShorterCheck="$(echo "$QGW_OS_Arr_NameSelected" | cut -d '-' -f1)"

          SimilarVMs="$(ls "$QWG_DownLoadFolder/${SimilarVM_ShorterCheck}"*.conf 2>/dev/null )"

        fi

        [[ $SimilarVMs ]] &&  mapfile -t  SimilarVM_Arr  <<< "$SimilarVMs"

        if [[ ${SimilarVM_Arr[*]} ]]; then  

          for DailyCheckVM in "${SimilarVM_Arr[@]}" ; do

            [[ "$DailyCheckVM" == *'daily'* ]] && DailyVMsPresent=1

          done

          printColor "\n\n   %s  >>  Similar VM's are present: \n\n"  "$QGW_DownloadName"
        
          print_quickget_choices  "SimilarVM_Arr" 3

          # Request second confirmation, if similar && if daily live, as daily live can be from different download name 

          [[ $DailyVMsPresent ]] && [[ ! $QGW_OverWriteAllow ]] && QGW_OverWrite=1

        fi

      fi

    fi


    if [[ $QGW_Download_BadLink ]]; then

      printColor "\n\n  [enter]  re-test the download link "

    else

      if [[ $QGW_OverWriteAllow ]];then  

        printColor  "\n\n\n  Installation target folder:  %s"  "$QWG_DownLoadFolder"
        
        show_current_preset_folder_list
      
        printf  "\n\n  [enter] Start the download  "

        printColor " ?? BACKUP > " 
      
      else  printColor  "\n\n  [enter] Start the download  " 
      
      fi
      
      if [[ ! $ZsyncDownload ]]; then

        if [[ $QGW_OverWriteAllow ]]; then printf "[number] select a DIFFERENT FOLDER  "

        else printColor "[number] select a different (preset or current) folder  "

        fi

      fi

    fi
    
    printf " [h] help  [b] go back \n\n\n"

    read -rp "  >  " QGW_DownloadStart

    printf "\n"

    case "$QGW_DownloadStart" in

      b)
        QGW_List_NumSelected=
        QGW_OS_Arr_NameSelected=
        QGW_Release_Arr_NameSelected=
        Zsync_OS_Name=
        Zsync_Release_Name=
        QGW_DownloadStart=
        QGW_DownloadSelection=
        QGW_Download_BadLink=

        if [[ $MainMenuZsync ]]; then MainMenuZsync= ; else function_quick_get_wrap ; fi

        break
      ;;


      0|1|2|3) QWG_DownLoadFolder="${VM_Folder_List[QGW_DownloadStart]}" 
      
      ;;

      h)  
          printf     "  Once the download and initial setup up has finished, "
          printf "\n\n  the new folder and .conf file may be easily moved and/or edited before running.\n" 

          printf "\n\n  For new preset folders, edit the main settings.\n" 

          printf "\n\n  Part downloads can usually be re-started, if stopped."
          printf "\n\n  Zsync Iso Updates may leave residual rcksum files, if stopped, and may need removing."
          printf "\n\n  But, if an iso.part file has been started, a restart is possible, after doing a re-sync."
          printf "\n\n  If re-syncing, the rcksum files will be auto-removed."

          show_current_preset_folder_list

          printf "\n\n"
      ;;

      *)
        if [[ $QGW_Download_BadLink ]]; then  QGW_BadLink_ReTest=1 ; function_test_iso_url

        else

          if [[ $QGW_OverWrite ]]; then 

            printf "  RECONFIRMATION is required  >>  %s"  "$QGW_DownloadName"

            printColor " \n\n  ** may install with some kind of NAMING variance **  \n\n"

            QGW_OverWriteAllow=1

          else

            QGW_DownloadStart="start"
            break

          fi

        fi
      ;;

    esac

    [[ ! $QWG_DownLoadFolder ]] &&  QWG_DownLoadFolder="$Default_VM_Folder" 


  done


  if [[ $QGW_DownloadStart == "start" ]]; then

    QGW_DownloadStart=
    QGW_DownloadStopped=
    
    cd "$QWG_DownLoadFolder"  ||  function_conf_error  "FOLDER not accessible: $QWG_DownLoadFolder"

    if [[ $ZsyncDownload ]]; then

      printf "  If stopped, Zsync Iso Updates may leave residual rcksum files that %s will remove."  "$ModName"

      printf "\n\n  To resume stopped downloads, any .iso.part files should be retained."

      # https://bugs.launchpad.net/ubuntu/+source/zsync/+bug/477551

      function remove_old_rcksum_files {

        if [[ $(ls "$VM_Conf_Dir/"rcksum* 2> /dev/null) ]]; then

          mapfile -t RckSumArray <<< "$(ls "$VM_Conf_Dir/"rcksum* 2> /dev/null)"

          for RckItem in "${RckSumArray[@]}"; do

            rm "$RckItem"

          done

        fi

      }

      remove_old_rcksum_files

    else

      printf "\n  Once a download and initial setup up has finished, the new folder and .conf file may be easily moved"
      printf "\n\n  and/or edited before running. Part downloads can usually be re-started, if stopped >> "

    fi

    
    if [[ $ZsyncDownload ]]; then  printf "\n\n" ; else printColor "  %s \n\n" "$QGW_DownloadName" ; fi


    # clear any accidental initial double pressing of the enter key

		read -r -t 0.2  
		read -r -t 0.2  
		read -r -t 0.2 		


    printColor "\n\n  Press [enter] at quickget finish,  at start your .. virtual machine ... etc "
    
    printColor "\n\n  OR  [enter] to STOP ALL current downloads & zsync, either to cancel or to resume later"

    printf "\n\n  Note that downloads can sometimes pause if servers are overloaded but they should auto-resume if given time."

    printf "\n\n  Quickemu & downloader output: "


    ## Although quickget will run as a background process, quickget will then go on to run aria2 etc 

    #  but as even further into the background. The download programs will grab the cursor in order to report progress
    
    #  and as this will compete with read & keyboard input we are limited to using [enter] ....


    #  quickget will also run a python3 command for macrecovery for MacOS 

    #  eg 26102 pts/1    S+     0:11 python3 /usr/bin/macrecovery --board-id Mac-00BE6ED71E35EB86 --mlb 00000000000000000 --os-type default --basename Recover

   
  
   
    # QUICKGET RUNS FIRST, in the background, THEN it starts a separate downloader  eg wget or aria2

    quickget "$QGW_DownloadSelection" &  


    printf "\n\n\n\n\n"

    printf "\e[3A\r"

    #  make line spaces then move the cursor up from the bottom     
    
    # See https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797   "\e[1A\r"  "\e[2K\r"  etc 



    ## load the zsync function, in all cases. Omit check "if [[ $ZsyncDownload ]]" as quickget could change  REVIEW 

    function show_rcksum_progress {

      RckFile="$(ls "$VM_Conf_Dir/"rcksum* 2> /dev/null)"

      LastZsync="0" 

      date +%s > "$VM_InstanceName/zsync.date.txt"

      while [[ -e "$RckFile" ]]; do
        
        RckDone="$(du -a "$RckFile" 2> /dev/null)"
        RckDone="${RckDone/"$RckFile"/}"

        # gives some kind of rough indication that work is in progress  (percentages don't seem to be possible)

        printf "  Comparing images >    %s "  "$RckDone" 

        sleep 2

        printf "\e[2K\r"

      done
      
    }

    # wait for background things to start happening
    
    sleep 4   

    while true ; do

      mapfile -t  Jobs_Arr <<< "$(jobs -p)"

      sleep 2

      [[ ${Jobs_Arr[*]} ]] && break

    done


    while true ; do    

      if [[ $(ls "$VM_Conf_Dir/"rcksum* 2> /dev/null) ]]; then  show_rcksum_progress &

      elif [[ $ZsyncDownload ]]; then 

        if [[ $(pgrep zsync 2>/dev/null) ]] ; then 
        
          printf "\n  Comparing images \n\n" 

          LastZsync="0" 

          date +%s > "$VM_InstanceName/zsync.date.txt"

        else  printf "\n  [enter] to close updater  >  zsync has stopped.\n\n" 

        fi
       
      fi

      ##  WAITS HERE for an [enter] to finish, or stop  ##

      read -r 
     

      for Jobs_PID in "${Jobs_Arr[@]}" ; do

        kill "$Jobs_PID" 2> /dev/null

      done


      # removing the old rcksum files not only tidies up but also stops the background monitor process

      [[ $(ls "$VM_Conf_Dir/"rcksum* 2> /dev/null) ]] && remove_old_rcksum_files

      
      # check macOS particularities

      P3_Arr=()

      mapfile -t  P3_Arr <<< "$(pgrep python3)"

      for P3_PID in "${P3_Arr[@]}" ; do

        P3_MacOS_Cmd="$(ps --no-headers -o cmd "$P3_PID" 2> /dev/null)" 
      
        if [[ "$P3_MacOS_Cmd" == *'/usr/bin/macrecovery'* ]]; then 
        
          kill "$P3_PID" 2> /dev/null
          
          printColor "\n\nStopped \n\n" 
          QGW_DownloadStopped=1
          break

        fi

      done


      if [[ $(pgrep aria2) || $(pgrep curl) || $(pgrep wget) || $(pgrep zsync) ]]; then

        while true; do 

          pkill aria2  2> /dev/null
          pkill curl   2> /dev/null
          pkill wget   2> /dev/null
          pkill zsync  2> /dev/null

          sleep 2

          if [[ ! $(pgrep aria2) && ! $(pgrep curl) && ! $(pgrep wget) && ! $(pgrep zsync) ]]; then

            QGW_DownloadStopped=1

            sleep 2 ; break

          fi

        done

      fi

      break

    done

  fi

  #  sleep/pauses needed to give different processes time to output their summaries & finish up

  sleep 1


  New_VM_Folder=

  New_VM_File=

  if [[ ! $ZsyncDownload ]]; then

    # folder check

    if [[ -d "${QWG_DownLoadFolder}/${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}-${QGW_Edition_Arr_NameSelected}" ]]; then 

      New_VM_Folder="${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}-${QGW_Edition_Arr_NameSelected}"

    elif [[ -d "${QWG_DownLoadFolder}/${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}" ]]; then 

      New_VM_Folder="${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}"

    elif [[ -d "${QWG_DownLoadFolder}/${QGW_OS_Arr_NameSelected}" ]]; then 

      New_VM_Folder="${QGW_OS_Arr_NameSelected}"

    fi

    #  file check  .conf 

    if [[ -f "${QWG_DownLoadFolder}/${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}.conf" ]]; then 

      New_VM_File="${QWG_DownLoadFolder}/${QGW_OS_Arr_NameSelected}-${QGW_Release_Arr_NameSelected}.conf"

    elif [[ -f "${QWG_DownLoadFolder}/${QGW_OS_Arr_NameSelected}.conf" ]]; then 

      New_VM_File="${QGW_OS_Arr_NameSelected}.conf"

    elif [[ -f "${QWG_DownLoadFolder}/${New_VM_Folder}.conf" ]]; then 

      New_VM_File="${New_VM_Folder}.conf"

    fi

  fi


  QGW_FolderDelete=

  if  [[ $ZsyncDownload ]]; then

    if [[ $QGW_DownloadStopped ]]; then 
    
      printf "\n  Download Stopped > Residual files may be present"
      printf "\n\n  Removal of any residual files & folders has to be completed manually"

      if [[ $Zsync_Updatable_ISO ]]; then

        printf "\n\n  Part downloads can usually be re-started if the .iso.part file is retained."

      else

        printf "\n\n  Part downloads can usually be re-started if the folder is retained."
        printf "\n\n  Dev downloads usually undergo some kind of NAMING variance"

      fi

    else  
    
      printf "\n  End of Quickget processes ..." 

      # Note (@2023/07) that quickget includes code to remove 'zs.old' files, which may or may not have been needed,
          
      # but Zsync can now give these files root protection so this removal may not work ...   REVIEW  Add a qqX copying mechanism? 

      if [[ ! $Zsync_Updatable_ISO ]]; then  
      
        printf "\n\n  as Dev downloads usually undergo some kind of NAMING variance" 

        printf "\n\n  checking of files & folders should to be completed manually"

      fi
      
    fi

    ZsyncDownload=


  elif [[ $New_VM_Folder ]]; then
  
    printColor "\n   Folder created:     %s "  "$New_VM_Folder"

    [[ $New_VM_File ]] && printColor "\n  .conf file created:  %s "  "$(basename "$New_VM_File")"

    if [[ ! $New_VM_File ]] || [[ $QGW_DownloadStopped ]]; then

      if [[ $QGW_DownloadStopped ]]; then
     
        printf "\n\n  Download Stopped > Residual files are present"

        printf "\n\n  Part downloads can usually be re-started if the folder is retained."

      fi
    
      [[ ! $New_VM_File ]] && printf "\n\n  As there is no .conf file, there may have been an ERROR >" 


      printColor "\n\n  Folder contents: \n\n"

      ls "$New_VM_Folder"

      printColor "\n\n  [d] delete the folder" 

      [[ $New_VM_File ]] && printColor " and .conf" 

      printf "  [enter] to retain & continue "

      printf "\n\n\n"

      read -rp "  >  "    QGW_FolderDelete

      if [[ $QGW_FolderDelete == "d" ]]; then
      
        printf "\n  Folder/file being deleted ... "

        rm -f -r "$New_VM_Folder"

        rm -f "$New_VM_File"

      else  printf  "  Folder/file retained"

      fi

    fi

  fi


  if [[ $New_VM_File ]] && [[ ! $QGW_DownloadStopped ]]; then

    NewFileTuneUp=1

    function_config_tune_up  "$New_VM_File"  "$New_VM_Folder"

    NewFileTuneUp=

  elif [[ $QGW_FolderDelete == "d" ]]; then

    sleep 1.4

  else

    printColor "\n\n  [enter] to return or continue \n\n" 
    read -rp "  > "

  fi

  
  # return back to any working directory if one had already been set  ( REVIEW is this right in all cases?)

  if [[ $VM_Conf_Dir ]]; then cd "$VM_Conf_Dir"  || function_conf_error  "Quickget-Wrap Working Directory NOT FOUND ," ; fi

  
  if [[ $MainMenuZsync ]] && [[ $ZsyncDownload ]]; then 

    SettingsFileAdjusted=

    ByPass_VM_Array_Selector=1

  else

    # re-load arrays, if not already done
    
    SettingsFileAdjusted=1

    function_refresh_and_reload_VM_arrays

    ByPass_VM_Array_Selector=1

  fi

}




##  END of ADD-IN of ORIGINAL QUICKEMU and setting up the QUICKGET wrapper.  START MENU FUNCTIONS:



function_conf_error () {

  ErrorMenuSelect=

  NewSetupFlag=

  # clear any accidental initial double pressing of the enter key caused during error

  read -r -t 0.1  
  read -r -t 0.1  
  read -r -t 0.1  
  read -r -t 0.1	
  read -r -t 0.1  
  read -r -t 0.1 	


  printf "\n\n  %s Settings, VM folder & conf file(s)"  "$ModName"

  if [[ $1 ]] ; then printf "\n\n  Check %s settings, location & content ... "  "$1"

  else printf "\n\n  Check the settings and re-run this script ... " ; fi


  printf "\n\n  Settings file:  %s \n\n" "$Q_Mod_SettingsFile"

  
  if [[ "$1" == *'COMMAND'* ]]; then 
  
    printColor "\n\n  [q] to quit "

    printf "  [enter] to view/edit settings file \n\n"


  elif [[ $1 == "folder" ]] && [[ $(ls "$VM_InstanceName"/*.iso 2> /dev/null) || $(ls "$VM_InstanceName"/*.img 2> /dev/null) ]]; then 

    printColor "\n  CHECK ME, NOW: \n\n"    #  REVIEW   

    cat "$VM_Conf_File"

    NewSetupFlag=1

    printf "\n\n\n  [enter]  edit the .conf file   [tune] run the config tune-up wizard "
    
    printColor "\n\n  [c] continue  (with current settings) looks okay "

    printf "\\n\n  [e] to edit %s general settings   [q] to quit \n\n"   "$ModName"

    
  elif [[ $1 == "folder" ]] ; then 

    FolderConfigFlag=1

    printf "\n  Folder config ERROR: "

    printf "\n\n  Check the folder path with the file manager. Check for spellings. Check upper/lower case."
    
    printf "\n\n  Esp. check for spaces that may have been true-typed into semi-invisibility ... "

    printColor "\n\n\n  [enter] to edit the .conf file   [e]  edit %s general settings " "$ModName"
    
    printf "\n\n  [q] to quit \n\n"   
    
  else   
  
    printColor "\n\n  [Enter] to edit %s general settings"  "$ModName" 

    [[ "$1" == *'No VMs'* ]] && printColor "  [gt] quickget a new distro "

    printColor "  [q] to quit \n\n"  

  fi   

  read -rp "  >  "   ErrorMenuSelect

  if [[ $ErrorMenuSelect == "q" ]]; then 
  
    printf "\n\n"; command exit 

  elif [[ $ErrorMenuSelect == "tune" ]]; then 
  
    function_config_tune_up  "$VM_Conf_File" "$VM_InstanceName" 

  elif [[ $ErrorMenuSelect == "c" ]] || [[ $ErrorMenuSelect == "e" ]] || [[ $ErrorMenuSelect == "gt" ]]; then  
  
    SelectArrayError=
    IgnoreFolderCheck=1

    if [[ $ErrorMenuSelect == "c" ]]; then  true

    elif [[ $ErrorMenuSelect == "e" ]]; then 

      if [[ $NewSetupFlag || $FolderConfigFlag ]]; then qqX_edit_settings ; else qqX_edit_settings  "conf" ; fi

    elif [[ $ErrorMenuSelect == "gt" ]]; then 

      VM_Selection=
      function_quick_get_wrap 
      return

    fi
 
  else  
  
    if [[ $NewSetupFlag || $FolderConfigFlag ]]; then 

      NewSetupFlag=
      FolderConfigFlag=
      qqX_edit_settings  "conf" 
      
    else  qqX_edit_settings
    
    fi
    
  fi

}


function_VM_array_add_confs () {

  ## Adds to VM array & lists, if submitted item has not been already listed 

  #  where $1 is 'VM_Path' and  $2 is 'VM_Conf' to be tested against the existing list (non-sparse)

  if [[ $1 ]]; then

    # make sure that a $1 has been sent & that the calling array splitter hasn't sent an empty value

    TestFolderFile="$1/$2"

    [[ ! -e $TestFolderFile ]] && function_conf_error  "VM_array_add_confs: File Not Found" && return 
    

    if [[ ! ${VM_Array[0,0]} ]]; then 
    
      New_VM_ElementCount=0  

    else 
    
      New_VM_ElementCount="$((${#VM_Array[@]} / 2))" 

      local i=0

      while [[ ${VM_Array[$i,0]} ]]; do

        ArrFolderFile="${VM_Array[$i,0]}/${VM_Array[$i,1]}"

        if [[ $ArrFolderFile == "$TestFolderFile" ]]; then 
        
          # already exists, stop searching, quit function:
          return

        else  

          # carry on interating the array
          ((i+=1))  

        fi
        
      done

    fi

      # if it's the first entry, or at the end there is no match found in the array, then add it

      VM_Array[$New_VM_ElementCount,0]="$1" 
      VM_Array[$New_VM_ElementCount,1]="$2" 

  fi
  
}


function_scan_VM_folderList () {

  ##  Scans of current & listed VM folders for other potential '.conf' files to the VM array

  #   Adds via calls to 'function_VM_array_add_confs'


  #  If the general settings file has populated or part-populated the VM array, then ${VM_Array[0,0]} will be present

  #  Starts by checking for settings file errors

  VM_SettingsError=

  VM_ElementCount=

  VM_ElementCount="${#VM_Array[@]}" 

  if [[ $VM_ElementCount ]]; then 

    while true ; do

      # Settings file checking loop. Runs until either settings file reads correctly or the user quits.

      ## CHECK 1

      #  Use 'modulo' to detect odd/even   See: https://stackoverflow.com/a/15660039  

      [[ "$(( VM_ElementCount % 2 ))" == "1" ]] &&  VM_SettingsError=1 


      ## CHECK 2

      if [[ ! $VM_SettingsError ]]; then

        # Check that array is non-sparse & is in pairs & that the elements exist

        # Note that a sparse array will stop listing at the first empty entry

        i=0
        VM_PairsCount=0

        while [[ ${VM_Array[$i,0]} ]]; do

          ArrFolderFile="${VM_Array[$i,0]}/${VM_Array[$i,1]}"

          if [[ -d "${VM_Array[$i,0]}" && -f "$ArrFolderFile"  ]]; then  

            [[ $VM_PairsCount -eq  "$VM_ElementCount" ]] &&  break

            # else carry on interating the array 
            ((i+=1))  
            ((VM_PairsCount+=2))  
            
          else  

            VM_SettingsError=1
            printf "\n\n  Array Element(s) Not Found "
            printf "\n\n  Folder:  %s " "${VM_Array[$i,0]}"
            printf "\n\n  .conf :  %s " "${VM_Array[$i,1]}"
            break
            
          fi
          
        done

      fi


      ## CHECK 3

      # If empty array elements were present, the pairs counter won't have incremented fully

      # & in associative arrays, unlike indexed ones, are not possible (@2023) to test for directly. 

      if [[ $VM_PairsCount != "$VM_ElementCount" ]]; then  
      
        VM_SettingsError=1

        printf "\n\n  Empty Array Element(s) Found "

      fi


      if [[ $VM_SettingsError ]]; then

        printColor "\n\n  ERROR in VM array general settings"

        printf "\n\n  Check the folder path with the file manager. Check for spellings. Check upper/lower case."
        
        printf "\n\n  Esp. check for spaces that may have been true-typed into semi-invisibility ... "

        printf "\n\n  [enter] to open settings editor  [q] to quit \n\n"

        read -rp  "  >  "  VM_SettingsError

        printf "\n\n"

        if [[ $VM_SettingsError == "q" ]] ; then  exit directly ; else  qqX_edit_settings ; fi

      else 

        break

      fi

    done

  fi

  i=0

  while [[ ${VM_Folder_List[i]} ]]; do

    FolderPathName="${VM_Folder_List[i]}"

    Folder_Conf_String="$(ls "$FolderPathName/"*.conf 2> /dev/null)"

    Folder_Conf_String=${Folder_Conf_String//"$FolderPathName/"/}

    mapfile -t  Folder_Conf_Array  <<< "$Folder_Conf_String"

    if [[ ${Folder_Conf_Array[0]} ]]; then 

      for TestFile in "${Folder_Conf_Array[@]}"; do 

        # calls adding function, one by one, if the .conf file appears to be valid, & builds the ${VM_Array[*,*]} 

        if grep -q -s 'guest_os=' "$FolderPathName/$TestFile"; then
            
          function_VM_array_add_confs  "$FolderPathName" "$TestFile" 

        fi
      
      done

    fi

    ((i +=1))

  done
  

  if [[ ! ${VM_Array[0,0]} ]] ; then
    function_conf_error  "VM ARRAY - No VMs - Is it time to get some distros?  Also check"
    SelectArrayError=1
  fi

}



function_print_conf_array () {

  # Print a list of the VM's in the VM array

  i=0

  ListTitleFolder=

  ListTitleLine_ItemCount=0

  #printf "\n"

  VM_ElementCount="${#VM_Array[@]}" 

  VM_PairsCount="$((VM_ElementCount/2))"

  while [[ ${VM_Array[$i,0]} ]] ; do

    if [[ ! -d "${VM_Array[$i,0]}" ]]; then 

      printf "\n\n  ERROR  %s folder not found: \n\n  %s \n\n" "$ModName" "${VM_Array[$i,0]}"   

      function_conf_error  "folder $i" 
      SelectArrayError=1
      break 

    else

      ArrayConf_Item="${VM_Array[$i,0]}/${VM_Array[$i,1]}"

      ArrayConf_Item_Listing="${VM_Array[$i,1]}"

      ArrayConf_Item_Listing="${ArrayConf_Item_Listing/'.conf'/}"

      if [[ -e "$ArrayConf_Item" ]]; then 

        if [[ ! $ByPass_VM_Array_Selector ]]; then
                
          if [[ $ListTitleFolder != "${VM_Array[$i,0]}" ]]; then

            if [[ $ListTitleFolder ]]; then 
            
              printf " >>  %s\n\n\n  "  "$ListTitleFolder"

              ListTitleLine_ItemCount=0

            else  printf "\n\n  "
            
            fi

            ListTitleFolder="${VM_Array[$i,0]}"

          fi

          QcowTarget=

          QcowTarget="$(grep -s 'disk_img' "$ArrayConf_Item" )"   

          QcowTarget="${QcowTarget/'disk_img='/}"  

          QcowTarget="${QcowTarget//'"'/}"

          # Newly downloaded VMs will have a qcow disk path in the .conf file 
          # BUT it won't be be there yet, not until quickemu has done a first run against the .conf

          if [[ ! -e "${VM_Array[$i,0]}/$QcowTarget" ]]; then 

            ArrayConf_Item_Listing+=" (new)"

            if [[ $i -gt 9 ]]; then printf "[%d]  " "$i" ; else printf " [%d]  " "$i" ; fi
            
            printColor "%-29s" "$ArrayConf_Item_Listing"

            ((ListTitleLine_ItemCount+=1))

          else

            if [[ "${VM_Array[$i,1]}" == 'ubuntu'* ]] && [[ "${VM_Array[$i,1]}" == *'daily'* ]]; then
           
              [[  ! $(du -b -t 500000 "${VM_Array[$i,0]}/$QcowTarget" 2>/dev/null) ]]  &&  ArrayConf_Item_Listing+=" (z)"

              #  Live VMs only, as zsync only updates the iso file.  Live VM qcow2 disks are normally only 196k. 

              #  Test ceiling is set at 500k.  REVIEW if problems.
 
            fi

            if [[ $i -gt 9 ]]; then printf "[%d]  " "$i" ; else printf " [%d]  " "$i" ; fi
            
            printColor "%-29s" "$ArrayConf_Item_Listing"

            ((ListTitleLine_ItemCount+=1))

          fi

          # move to new line when number of items per line reached  (default 4, see main settings)

          if [[ $ListTitleLine_ItemCount == "$VM_Select_ItemsPerLine" ]]; then

            ListTitleLine_ItemCount=0

            printf "\n\n  "

          fi

        fi

      else

        printf "\n\n  ERROR  %s ' .conf ' not found: \n\n  %s \n\n" "$ModName" "$ArrayConf_Item"

        function_conf_error  "config $i" 
        SelectArrayError=1
        break

      fi

    fi

    ((i+=1))

  done

  [[ $i == "$VM_PairsCount" ]] && printf " >>  %s"  "$ListTitleFolder"
  
}


function_Select_VM () {  

  SelectArrayError=

   
  if [[ $ByPass_VM_Array_Loader ]] && [[ ${VM_Array[0,0]} ]]; then

    # if reselecting & the array is already loaded (which it should be ...)

    # printf "\n\n  ByPassing VM Loader ... "  

    ByPass_VM_Array_Loader=

  else 

    # Re-scan folders as something new may have been added

    function_scan_VM_folderList

    function_VM_array_add_confs

  fi


  if [[ $ByPass_VM_Array_Selector ]] || [[ $SelectArrayError ]] ; then

    # printf "\n\n  ByPassing VM Selector ... "

    true

  else 

    VM_Selection=
    VM_Conf_Dir=
    VM_Conf_File=

    function_print_conf_array

    
    if [[ $DefaultFolderError || $ExtraFolderError ]]; then 

      printf "\n\n\n  Settings file: %s" "$Q_Mod_SettingsFile"

      [[ $DefaultFolderError ]] && printf "\n\n  ERROR: DEFAULT FOLDER   [enter] to edit settings file  [q] to quit " 

      [[ $ExtraFolderError ]] && printf "\n\n  ERROR: EXTRA FOLDER   [enter] to edit settings file  [q] to quit "

    else

      printf "\n\n\n  [number] to select a VM   [gt] quickget a new distro   [set] to edit settings file   [q] to quit "

      VM_Array_NameZero="${VM_Array[0,1]}"

      VM_Array_NameZero="${VM_Array_NameZero/'.conf'/}"

      printColor "\n\n  [Enter]  "
      
      printf "%s  "  "${VM_Array_NameZero^^}"

    fi

    printf "\n\n"

    read -rp  "   >  "  VM_Selection



    [[ $VM_Selection == "q" || $VM_Selection == "Q" ]] && echo && exit directly 


    if [[ $VM_Selection == "set" ]];then

      VM_Selection=
      VM_InstanceName=
      qqX_edit_settings 
      return

    fi


    if [[ $DefaultFolderError || $ExtraFolderError ]]; then 
          
      DefaultFolderError= 
      ExtraFolderError=
      VM_Selection=
      VM_InstanceName=
      qqX_edit_settings 
      return

    fi



    if [[ $VM_Selection == "gt" || $VM_Selection == "GT" ]]; then

      if [[ $VM_Selection == "GT" ]]; then

        printColor "\n\n  CAPS LOCK should be unset \n\n"

        read -rp "  [enter] to continue  >  "

      fi

      VM_Selection=
      function_quick_get_wrap 

      ByPass_VM_Array_Loader=

      printf "\033c"
      show_qmod_title
      return

    fi

    [[ ! $VM_Selection ]] &&  VM_Selection=0  

    # printf "\n\n  Selecting instance %s \n\n"  "$VM_Selection"

    sleep 0.1   # gives background scans a little more time

    VM_Conf_Dir="${VM_Array[$VM_Selection,0]}"
    VM_Conf_File="${VM_Array[$VM_Selection,1]}"

  fi

  if [[ $SettingsFileAdjusted && $ByPass_VM_Array_Selector ]] || [[ $SelectArrayError ]]; then

    # Only the array checker is run after a settings edit, if a VM selection is already in place 

    true

  else

    ## Check file/folder exists and that we are where we need to be

    [[ ! -d "$VM_Conf_Dir" ]] &&  function_conf_error  "folder"  && SelectArrayError=1

    # change directory to where the VM is

    ! cd "$VM_Conf_Dir"  &&  printColor "\n\n  ERROR  .conf  folder switching \n\n" && sleep 5 && exit 1

    [[ ! -e "$VM_Conf_File" ]] &&  function_conf_error  ".conf file" && SelectArrayError=1

    # Set Instance Name, following Quickemu pattern which sets the same name to the .conf file and to the main folder 

    VM_InstanceName="${VM_Conf_File/.conf}"

    # Check that the dir contains the right files  &&  grep .conf for right content

    if [[ $IgnoreFolderCheck ]]; then IgnoreFolderCheck=

    else [[ ! $(ls "$VM_InstanceName"/*.qcow2 2> /dev/null) ]] && function_conf_error  "folder"  && SelectArrayError=1

    fi

    ! grep -q 'guest_os=' "$VM_Conf_File" 2> /dev/null && function_conf_error  ".conf file"   && SelectArrayError=1

    ## Check KVM parameter settings & advise according to guest OS

    KVM_MSR_selector=

    function_find_kvm_msr_default_and_status

    if [[ $Verbose_MSR_dialog ]] || [[ ! $Verbose_MSR_dialog ]] && [[ "$VM_InstanceName" == *windows* || "$VM_InstanceName" == *macos* ]]; then 
    
      msrs_conflict_check_resolver

    fi

  fi

  # clear any no-longer needed flags

  Select_VM=
  ByPass_VM_Array_Loader=
  ByPass_VM_Array_Selector=

  # reload if the editor was called, due to settings error

  if [[ $SelectArrayError ]]; then
  
    MainMenuChoice="refresh" 
    Select_VM=1

    [[ $VM_InstanceName ]] && ByPass_VM_Array_Selector=1

  else

    SettingsFileAdjusted=

  fi

}


drives_and_snapshots() {

  #  @ 2023/08  with adding of second and shared drives,  
   
  #  also start accessing qemu-img directly instead of going round the houses with with quickemu calls  

  #  Omitting $1 will give simple lists, otherwise $1 may be given either as 'info' or 'select' 


  #  $2 is for disk utility to hide the message to use the disk utility

  [[ $1 == "as_disk_util" ]] && printColor "\n\n  Internal ERROR: disk uitilty needs two args \n\n"  && sleep 100

  Selected_Drive=
  Selected_DriveName=

  find_main_drive

  set_drive_paths

  if [[ $SecondDriveArgs ]]; then

    printColor "\n  Second Drive: \n\n"
    if [[ $1 == "info" ]]; then "$QEMU_IMG" info "$SecondDrive"; echo ; else "$QEMU_IMG" snapshot -l "$SecondDrive"; echo; fi

    if ! "$QEMU_IMG" snapshot -l "$SecondDrive" | grep -s -q 'Snapshot list:' ; then printf "  No Snaphots \n\n" ; fi

    if [[ $2 == "as_disk_util" ]]; then  check_disk_is_okay PrintErrors "$SecondDrive" ; echo ; else check_disk_is_okay "$SecondDrive" ; fi

    printf "  Health Status:  %s"  "$DiskChkStatus"
    if [[ $DiskChkStatus == "Okay" ]]; then  printf " > No errors " 
    elif [[ $2 != "as_disk_util" ]]; then printColor "\n\n  See qqX [disk] utility for repairs" ; fi
    printf "\n\n"

  fi

  if [[ $SharedDriveArgs ]]; then

    printColor "\n  Shared Drive: \n\n"
    if [[ $1 == "info" ]]; then "$QEMU_IMG" info "$SharedDrive"; echo ; else "$QEMU_IMG" snapshot -l "$SharedDrive"; echo; fi

    if ! "$QEMU_IMG" snapshot -l "$SharedDrive" | grep -s -q 'Snapshot list:' ; then printf "  No Snaphots \n\n" ; fi

    if [[ $2 == "as_disk_util" ]]; then  check_disk_is_okay PrintErrors "$SharedDrive" ; echo ; else check_disk_is_okay "$SharedDrive" ; fi

    printf "  Health Status:  %s"  "$DiskChkStatus"
    if [[ $DiskChkStatus == "Okay" ]]; then  printf " > No errors " 
    elif [[ $2 != "as_disk_util" ]]; then printColor "\n\n  See qqX [disk] utility for repairs" ; fi
    printf "\n\n"
  fi

  if [[ ! -e $MainDrive ]]; then

    printColor "\n\n  NO MAIN DRIVE  (yet) \n\n"

  else

    if [[ $SharedDriveArgs || $SecondDriveArgs ]]; then printColor "\n\n  Main Drive: \n\n"
    
    else printColor "\n\n  One Main Drive: \n\n"
    
    fi

    if [[ $1 == "info" ]]; then "$QEMU_IMG" info "$MainDrive"; echo ; else "$QEMU_IMG" snapshot -l "$MainDrive"; echo; fi

    if ! "$QEMU_IMG" snapshot -l "$MainDrive" | grep -s -q 'Snapshot list:' ; then printf "  No Snaphots \n\n" ; fi

    if [[ $2 == "as_disk_util" ]]; then  check_disk_is_okay PrintErrors "$MainDrive" ; echo ; else check_disk_is_okay "$MainDrive" ; fi

    printf " Health Status:  %s"  "$DiskChkStatus"
    if [[ $DiskChkStatus == "Okay" ]]; then  printf " > No errors "
    elif [[ $2 != "as_disk_util" ]]; then printColor "\n\n  See qqX [disk] utility for repairs" ; fi
    printf "\n\n"

  fi

  if [[ $1 == "select" ]] && [[ $SharedDriveArgs || $SecondDriveArgs ]]; then

    printColor "\n  SELECT:  [enter]  Main Drive (or back to main menu)"
    
    [[ $SecondDriveArgs ]] && printColor "   [2]  Second Drive"
    [[ $SharedDriveArgs ]] && printColor "   [3]  Shared Drive"

    [[ $SharedDriveArgs && $SecondDriveArgs ]] && printColor "  (scroll to view)"

    printf "\n\n"

    read -rp "  >  "   Selected_Drive

    case "$Selected_Drive" in

      2)  Selected_Drive="$SecondDrive" 
          Selected_DriveName="Second Drive"
      ;;

      3)  Selected_Drive="$SharedDrive" 
          Selected_DriveName="Shared Drive"
      ;;

      *)  Selected_Drive="$MainDrive" 
          Selected_DriveName="Main Drive"
      ;;

    esac

  else

    Selected_Drive="$MainDrive" 
    Selected_DriveName="Main Drive"

  fi

  if [[ $1 == "select" ]] && [[ -e "$Selected_Drive" ]] ; then

    printColor "\n\n Selected Drive = %s \n\n"  "$Selected_DriveName"
   
    "$QEMU_IMG" info "$Selected_Drive"

    check_disk_is_okay "$Selected_Drive"

    if [[ $2 != "as_disk_util" ]]; then

      printf "\n\n  Health Status:  %s"  "$DiskChkStatus"

      if [[ $DiskChkStatus == "Okay" ]]; then  printf " > No errors " ; else printColor "\n\n  See qqX [disk] utility for repairs" ; fi

      printf "\n\n  IMPORTANT > For disk operations, the virtual machine must *not* be running & not started elsewhere." 

    fi

    # use pgrep with -x exact match to avoid 'kvm-cleanup' processes being detected

    [[ $(pgrep -x kvm) ]] &&  printColor "\n\n  VIRTUAL MACHINE ACTIVITY has been DETECTED \n"

  fi

}


function_KillPID_Menu() {

  tput civis

	# WAIT for background load & QEMU messages to appear before showing menu (unless reloading)

	if [[ $KillPID_Menu_Exited ]]; then  KillPID_Menu_Exited=

  elif [[ $BreakAtVerboseArgs ]]; then BreakAtVerboseArgs= ; return

  else  sleep 2 ; fi
  
  #if [[ ! $InstancePID ]]; then

    # belt & braces check for the PID:  

    # belt:

    #InstancePID="$(pgrep "$VM_InstanceName")"

    #echo "PID $InstancePID - VM_InstanceName $VM_InstanceName "

    # braces: qemu SHOULD write a .pid file while it is running & remove it when not  
    
    # REVIEW  Probably we should assume VM is not running if QEMU's & file is not there as this would indicate QEMU failure

    # Also the belt of pgrep can give truncated output with long VM names

    # Uses quickemu Var Names:

    # [[ ! $InstancePID ]] && InstancePID="$(cat "${VMDIR}/${VMNAME}.pid")"

    InstancePID="$(cat "${VMDIR}/${VMNAME}.pid" 2> /dev/null)"

    # echo "PID $InstancePID - VMDIR $VMDIR  VMNAME  $VMNAME QEMU $QEMU"

  #fi

  if [[ $InstancePID ]]; then 

    SpicyPID=

    sleep  "$P_Kill_WaitSecs"   

    printColor "\n  Quickemu instance %s on PID:  %s"  "$VM_InstanceName"  "$InstancePID"

    printColor  "   [k] force close the Quickemu instance   "
    
    SpicyPID="$(pgrep spicy)"

    if [[ $SpicyPID ]]; then 

      #  REVIEW   if possible, add SDL reconnect 

      SpicyReStartCmd="$(ps --no-headers -o cmd "$SpicyPID")"
    
      printColor " [r] reload Spicy window (if closed)"

    fi

    printf "\n\n"
    tput cnorm

  fi

  # return to menu requires VM to be shutdown first

  while true ; do 

    read -rp  "  >  " -t 1  KillPID_Menu

    if [[ $KillPID_Menu == "k" ]]; then 

      printf "\n\n"
      if [[ $( ps -p "$InstancePID" --no-headers ) ]]; then
        [[ $SpicyPID ]] && kill "$SpicyPID" 2>/dev/null &
        kill "$InstancePID" 2>/dev/null &
      fi

      sleep 3
      printf "\n\n"

      tput cnorm
      break

    elif [[ $KillPID_Menu == "r" ]]; then 

      if [[ ! $(pgrep spicy) ]]; then 
        eval "$SpicyReStartCmd"
        sleep 2
        SpicyPID="$(pgrep spicy)"
      fi

    else

      if [[ ! $( ps -p "$InstancePID" --no-headers ) ]]; then 
      
        tput cnorm 
        break 

      else

        #  Erase text and adjust cursor:   "\e[1A\r"  "\e[2K\r"  etc    See https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
       
        printf "\r"

      fi

    fi

  done

  KillPID_Menu=

  KillPID_Menu_Exited=1

  P_Kill_WaitSecs="$P_Kill_WaitSecs_PreSet"

}	

qqX_edit_settings() {

  [[ $TextEditor ]] && [[ ! $(type -p "$TextEditor") ]] && TextEditor=

  if [[ ! $TextEditor ]]; then
  
    DefaultTextEditor="$(xdg-mime query default text/plain)"

    EditorTest="${DefaultTextEditor/.desktop/}"

    EditorTest="$(tr -cd "[:alnum:]" <<< "$EditorTest")"

    if [[ $DefaultTextEditor ]] && [[ $(type -p "$EditorTest") ]] ; then  TextEditor="$EditorTest"

    elif [[ $(type -p gedit) ]] ; then  TextEditor="gedit"

    elif [[ $(type -p pluma) ]] ; then  TextEditor="pluma"

    elif [[ $(type -p kate) ]] ; then   TextEditor="kate"

    elif [[ $(type -p nano) ]] ; then   TextEditor="nano"

    fi

  fi

  if [[ ! $(type -p "$TextEditor") ]]; then 

    printColor "\n\n  Unable to open settings.  Default or basic text editor not found."

    printf "\n\n  Try an install of 'nano' or give settings file the command name of a preferred editor ..  "

    printf "\n\n  Settings location:  %s  \n\n  [enter] to return \n\n"  "$Q_Mod_SettingsFile"

    read -rp "  >  "

  else

    # Run the editor: 

    if [[ $1 == "conf" ]]; then

      printf "\n\n"

      cat "$VM_Conf_Dir/$VM_Conf_File"

      while true ; do

        printColor "\n\n  [enter] to edit   [r] config reminder notes   [b] go back"
        printf "\n\n"

        read -rp "  >  "   ConfEdit_Select

        if [[ $ConfEdit_Select == "r" ]] ; then

          custom_conf_notes

        elif [[ $ConfEdit_Select == "b" ]] ; then

          break

        else

          nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt" "$TextEditor" "$VM_Conf_Dir/$VM_Conf_File"  

          ConfSettingsAdjusted=1

          break

        fi

      done

    elif [[ $1 == "new_conf" ]]; then

      custom_conf_notes

      printColor "  [enter] to edit \n\n"

      read -rp "   >   "

      nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt"  "$TextEditor" "$New_VM_File"     
      
    elif [[ $1 == "tune-up" ]]; then

      nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt"  "$TextEditor" "$TuneUpTargetDraft"  

    else 

      nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt"  "$TextEditor" "$Q_Mod_SettingsFile"  

      SettingsFileAdjusted=1

      # .conf is read only when booting the VM, just the general settings needs a system refresh

      VM_InstanceName="$VM_InstanceName"" - Settings Updated  >> ReSelect"

      function_refresh_and_reload_VM_arrays
     
    fi

    # Note, these commands can throw a complaint if 'xapps' has not not been installed.
    # https://askubuntu.com/questions/1396739/failed-to-load-module-xapp-gtk3-module

    # > "/tmp/qqX_nohup.txt" handles the output that tells us about handling of gtk2 and gtk3 etc ....  
    #  As from 'WebBrowser' section and handling of firefox etc

    # NB  don't set to run in background, as the web browser does, as qqX needs to take the new settings 

  fi

}


function_delete_Selected_VM () {

  if [[ ! $1 && ! $2 ]]; then

    # Normally: VM_InstanceName="${VM_Conf_File/.conf}"

    printColor "\n\n  Internal Params: VM Name & VM config file (NO PATH) must be given \n\n " 

    sleep 2 ;  return

  else

    if [[ ! -d "$1" ]] || [[ ! -e "$2" ]]; then

      printColor "\n\n  Internal Params: VM Name & VM config file (NO PATH) must be given \n\n " 

      printColor "\n\n  Internal Params: VM Name / VM config file  NOT FOUND  \n\n " 

      sleep 2 ;  return

    fi

    if [[ $(type -p gio 2>/dev/null) ]]; then TrashingAvailable=1 ; else TrashingAvailable= ; fi

    printColor "\n\n  Folder: %s " "$1"
  
    printColor "  & file: %s  to be deleted?"  "$2"
  
    printf  "\n\n  To delete a VM, removal of the above folder/file is sufficient."

    printf "\n\n  Select [n] to manually delete, & to possibly retain individual files, using a file manager."

    printf "\n\n  Folder content: \n\n"

    ls "$1"

    if [[ $TrashingAvailable ]]; then 

      printColor "\n\n  [enter] safely move the folder & file to the recoverable trash bin"  
      
      printf "\n\n  [n] keep   [y] yes, PERMANENTLY, I have back-ups ..."

    else

      printColor "\n\n  [enter] use the file manager to move things the trash bin "
      
      printf "\n\n  [y] yes, PERMANENTLY, I have back-ups ..."

    fi

    printf "\n\n"

    read -rp "  >  "   Delete_VM_Option

    if [[ $Delete_VM_Option == "y" ]]; then 

      printf "\n\n  Folder & .conf file being deleted ... \n\n"
      
      rm -f -r "$1"  
      echo

      rm -f "$2"
      echo

    else

      # https://specifications.freedesktop.org/trash-spec/trashspec-latest.html

      if [[ $TrashingAvailable ]] && [[ $Delete_VM_Option != "n" ]]; then

        gio trash "$1"
        gio trash "$2"

      else

        printf "\n\n  NOT deleted ... \n\n"

      fi

    fi

    sleep 1.4

    printColor "\n\n  [enter] to return or continue \n\n" 
    read -rp "  > "

    VM_InstanceName="$VM_InstanceName"" - Deleted >> ReSelect"

    SettingsFileAdjusted=1

    function_refresh_and_reload_VM_arrays

  fi

}



function show_quickemu_WRAP_help {

  HelpSelect=

  if [[ $ModName_qqX ]]; then 

    printf "\n"
  
    function_qqX_printf_logo_title  "$qqX_title"

    printf "\n\n\n  %s is currently wrapping: QEMU %s  quickemu %s  &  quickget %s"  "$ModName" "$QEMU_VER_LONG" "$QE_VerNumber"  "$QG_VerNumber" 

  else 
  
    printColor "\n\n\n  Quickemu Mod - Virtual Machine Launcher - Wrap Version %s %s " "$ModName" "$ModVersion" 

  fi

  printf "\n\n  https://en.wikipedia.org/wiki/Wrapper_library   https://en.wikipedia.org/wiki/Adapter_pattern"  

  printf "\n\n\n  For code contributions, bugs and updates, see: https://github.com/TuxVinyards"  

  [[ ! $ModName_qqX ]] &&  printf "\n\n  May be started in a folder terminal:  ./%s   or by mouse click"  "$ModName"
  
  printf "\n\n  [enter] to return   [a] %s API detail   " "$ModName"

  [[ ! $ModName_qqX ]] && printf "[p] parameters for VM runtime settings"

  printf "\n\n"

  read -rp "  >  "  HelpSelect

  if [[ $HelpSelect == "a" ]]; then   
  
    show_wrap_CLI_usage  

    printf "\n\n\n"
  
    read -rp  "  [enter] to return >  " 

  elif [[ $HelpSelect == "p" ]]; then
  
    printf "\n\n  Extra quickemu parameters for qmod API & qmod settings."  
  
    printf "\n\n  NB  not all options can be applicable  CAUTION \n\n" 

    NoExit=1
    echo
    usage | tail +6
    echo

    read -rp  "  [enter] to return >  " 

  fi
}

# As check_for_qqX_updates runs in the background, the variables need transfering to the foreground

qqX_LatestRelease=

qqX_LatestTarBall=

qqX_UpdateAvail=

qqX_Upd_VarsTmpFile="/tmp/qqX.upd.chk.txt"

echo > "$qqX_Upd_VarsTmpFile"


check_for_qqX_updates() {

  # Quietly do a check for new releases 

  wget -q -T2 -t2 -O "/tmp/latest-qqX"  "https://api.github.com/repos/TuxVinyards/qqX/releases/latest" 

  qqX_LatestRelease="$(grep -s tag_name "/tmp/latest-qqX" )"

  
  if [[ $qqX_LatestRelease ]]; then

    # should normally be the case, if internet connection is up

    qqX_LatestRelease="${qqX_LatestRelease/'"tag_name": "'/}"

    qqX_LatestRelease="${qqX_LatestRelease//'",'/}"

    qqX_LatestRelease="$(tr -cd '[:graph:]' <<< "$qqX_LatestRelease")"

    # std downloads: 

    # eg "tarball_url": "https://api.github.com/repos/TuxVinyards/qqX/tarball/0.09.00.016",

    qqX_LatestTarBall="$(grep -s tarball "/tmp/latest-qqX" )"

    qqX_LatestTarBall="${qqX_LatestTarBall/'"tarball_url": "'/}"

    qqX_LatestTarBall="${qqX_LatestTarBall//'",'/}"

    qqX_LatestTarBall="$(tr -cd '[:graph:]' <<< "$qqX_LatestTarBall")"

    # assets: 
    
    # eg "browser_download_url": "https://github.com/TuxVinyards/qqX/releases/download/0.09.00.016/qqX-0.09.00.016.tar.gz"

    # &  "browser_download_url": "https://github.com/TuxVinyards/qqX/releases/download/0.09.00.016/qqX-0.09.00.016.tar.gz.sha256"

    qqX_Latest_Secure_TarTest="https://github.com/TuxVinyards/qqX/releases/download/$qqX_LatestRelease/qqX-$qqX_LatestRelease.tar.gz"


    if [[ "$(grep -s -c "$qqX_Latest_Secure_TarTest" "/tmp/latest-qqX")" == "2" ]] ; then

      qqX_Latest_Secure_TarBall="$qqX_Latest_Secure_TarTest"
      
      qqX_Latest_Secure_TarSha="https://github.com/TuxVinyards/qqX/releases/download/$qqX_LatestRelease/qqX-$qqX_LatestRelease.tar.gz.sha256"
           
    fi


    IFS='.' read -ra qqX_LatestVer_Arr <<< "$qqX_LatestRelease"

    # Current Version is carried out at the start, in similar way


    # qqX_LatestVer_Arr[0] remains unpadded  (& to the left of the decimal point)

    [[ ${#qqX_LatestVer_Arr[1]} -lt 2 ]] && printf -v qqX_LatestVer_Arr[1] "%.2d" "${qqX_LatestVer_Arr[1]}"

    [[ ${#qqX_LatestVer_Arr[2]} -lt 2 ]] && printf -v qqX_LatestVer_Arr[2] "%.2d" "${qqX_LatestVer_Arr[2]}"

    #legacy 3dp pre "0.09.00.017"

    [[ ${#qqX_LatestVer_Arr[3]} -gt 2 ]] && qqX_LatestVer_Arr[3]="${qqX_LatestVer_Arr[3]:(-2)}"

    [[ ${#qqX_LatestVer_Arr[3]} -lt 2 ]] && printf -v qqX_LatestVer_Arr[3] "%.2d" "${qqX_LatestVer_Arr[3]}"

    qqX_LatestVer_Float="${qqX_LatestVer_Arr[0]}.${qqX_LatestVer_Arr[1]}${qqX_LatestVer_Arr[2]}${qqX_LatestVer_Arr[3]}"

    
    #echo "latest $qqX_LatestVer_Float  current $qqX_CurrentVer_Float"   


    #  Flag up release type.  Also used in qqX general title to show if using beta or release.

    if [[ "${qqX_LatestVer_Arr[3]}" ]] && [[ "${qqX_LatestVer_Arr[2]}" == "00" ]]; then 
    
      qqX_LatestReleaseType="beta"

    else qqX_LatestReleaseType="release"
    
    fi
    
    #echo "latest $qqX_LatestReleaseType  current $qqX_CurrentReleaseType"   


    #  If current ver is beta OR if beta notifications are requested in the general settings:

    [[ $qqX_CurrentReleaseType == "beta" ]] && Hide_qqX_BetaUpdates=


    #  Also flag up about beta notifications if transitioning from beta to release:

    [[ $qqX_CurrentReleaseType == "beta" ]] && [[ $qqX_LatestReleaseType == "release" ]] && BetaTxReleasePoint=1


    # Set up TRANSFER FILE for all needed variables,  as check runs in background ...  NB 

    if [[ ! $Hide_qqX_BetaUpdates && $qqX_LatestReleaseType == "beta" ]] || [[ $qqX_LatestReleaseType == "release" ]]; then
   
      if [[ $(bc <<< "$qqX_LatestVer_Float > $qqX_CurrentVer_Float" 2>/dev/null) == "1" ]]; then 

        { printf "\n\n qqX_UpdateAvail=\"1\""   
        
          printf "\n\n qqX_LatestRelease=\"%s\""  "$qqX_LatestRelease" 

          printf "\n\n qqX_LatestTarBall=\"%s\""  "$qqX_LatestTarBall" 
        
          printf "\n\n qqX_Latest_Secure_TarBall=\"%s\""  "$qqX_Latest_Secure_TarBall" 
        
          printf "\n\n qqX_Latest_Secure_TarSha=\"%s\""  "$qqX_Latest_Secure_TarSha" 

          printf "\n\n qqX_LatestReleaseType=\"%s\""  "$qqX_LatestReleaseType" 

          printf "\n\n qqX_LatestVer_Float=\"%s\""  "$qqX_LatestVer_Float" 
        
        } >> "$qqX_Upd_VarsTmpFile"

      else 
      
        printf "\n\n qqX_UpdateAvail="   > "$qqX_Upd_VarsTmpFile"

      fi

    fi

    #  REVIEW  dev searches for pre-releases?  Needs 'jq' but this is required by quickemu anyway.  Or just leave as UnHide_Beta?
    
    # https://stackoverflow.com/questions/67688662/how-can-i-get-the-latest-pre-release-release-for-my-github-repo-bash

  fi 


}


function_config_tune_up ()  {

  # $1 should be the full path/filename.conf

  # &  $2 the VM Folder name

  TuneUpTargetFile="$1" 
  TuneUpDistroFolder="$2"
  TuneUpDate="$(date +%b%d.%H.%M)"

  if [[ ! -e $TuneUpTargetFile ]]; then 
    printColor "\n\n  Internal ERROR  tune up .conf file not found \n\n  %s \n\n"
    sleep 100
  fi

  if [[ ! -d $TuneUpDistroFolder ]]; then 
    printColor "\n\n  Internal ERROR  tune up DistroFolder not found \n\n  %s \n\n"
    sleep 100
  fi


  TuneUpOldFile="$TuneUpDistroFolder/OldConfig.$TuneUpDate.conf"

  cp "$TuneUpTargetFile"  "$TuneUpOldFile"

  TuneUpTmpCopy="/tmp/TuneUpTargetFile.tmp"

  TuneUpTargetDraft="/tmp/TuneUpTargetDraft.tmp"

  cp  "$TuneUpTargetFile"  "$TuneUpTmpCopy"

  printColor "\n\n\n  CHECK ME: \n\n"

  cat "$TuneUpTmpCopy"

  printColor "\n\n  All changes made here, may be reversed:"
    
  printf "\n\n  A backup of your current configuration has now been date recorded & placed in the Distro Folder\n"
  
  [[ $NewFileTuneUp ]] && printf "\n\n  So far, so good  >  "

  # check starter

  ConfLine="$(grep '#!/usr/bin' "$TuneUpTmpCopy")"

  ConfPointer="${ConfLine/"#!/usr/bin/"/}"

  ConfPointer="${ConfPointer/"--vm"/}"

  ConfPointer="${ConfPointer// /}"

  if [[ ! $ConfLine ]]; then 

    # some kind of line is needed

    printf "#!/usr/bin/%s --vm \n\n" "$ModName"  > "$TuneUpTargetDraft" 

  elif [[ $ConfPointer != "$ModName" ]]; then

    printColor "\n\n  The start line may be edited to point to %s"  "$ModName"

    ConfEdit_Qstart=

    printf "\n\n  Change the start line to point to %s ?"  "$ModName"

    printf "\n\n  [enter] yes, go for it    [no] maybe not ...  [q] quit tune-up \n\n"

    read -rp "  >  "  ConfEdit_Qstart

    [[ $ConfEdit_Qstart == "q" ]] && return

    if [[ $ConfEdit_Qstart != "no" ]]; then printf "#!/usr/bin/%s --vm \n\n" "$ModName"  > "$TuneUpTargetDraft" 

    else printf "%s\n" "$ConfLine" > "$TuneUpTargetDraft" 

    fi

  else

    printf "%s\n\n" "$ConfLine" > "$TuneUpTargetDraft"

  fi


  # disk size  (for existing installs use the 'disk' util)

  ConfLine="$(grep -s 'disk_size' "$TuneUpTmpCopy")"

  if [[ $NewFileTuneUp ]]; then

    ConfEdit_DiskSize=

    printColor "\n\n  Suggest adding disk_size=\"64G\" or higher if expecting to create snapshots: " 

    printf "\n\n  qcow2 disks auto-compress & will normally occupy much less space"
    
    printColor "\n\n  & an empty disk only takes up 196k."
    
    printf "\n\n  It is easier make them larger now than to resize later ... " 

    printf  "\n\n\n  [enter] 128G  or  [number] in Gb 32, 64, 192   [no] leave as possibly sub-minimal  [q] quit tune-up "  

    printf "\n\n"

    read -rp  "  >  "  ConfEdit_DiskSize

    [[ $ConfEdit_DiskSize == "q" ]] && return

    [[ ! $ConfEdit_DiskSize ]]  && ConfEdit_DiskSize="128"

    [[ $ConfEdit_DiskSize ]]  &&  ConfEdit_DiskSize="$(tr -cd "[:digit:]" <<< "$ConfEdit_DiskSize")"

    if [[ $ConfEdit_DiskSize ]]; then 
    
      printf "disk_size=\"%sG\"\n"  "$ConfEdit_DiskSize"  >> "$TuneUpTargetDraft"

    fi

  else

    [[ $ConfLine ]] && printf "%s\n" "$ConfLine" >> "$TuneUpTargetDraft" 

  fi


  # add cores menu  (using code from ffX project ...)

  if [[ $(type -p lscpu) ]]; then

    CoresAvail=$(lscpu | grep "Core(s) per socket:" | tr -cd "[:digit:]")

    SocketsAvail=$(lscpu | grep "Socket(s):" | tr -cd "[:digit:]")   #  REVIEW  for dual cpu mobo (untested)

    CoresAvail=$((SocketsAvail*CoresAvail))

    # CoresAvail=2

    ThreadsPerCoreAvail=$(lscpu | grep "Thread(s) per core:" | tr -cd "[:digit:]")      #  REVIEW  

    ThreadsAvailable=$((ThreadsPerCoreAvail*CoresAvail))

    ThreadsSuggested=$(bc <<< "scale=0; ((($ThreadsAvailable*3)/4)+0.5)/1" 2>/dev/null)

    #  Use 'modulo' to detect odd/even   See: https://stackoverflow.com/a/15660039  

    [[ "$(( ThreadsSuggested % 2 ))" == "1" ]] && (( ThreadsSuggested +=1 ))

    ThreadsSuggestLower=$(bc <<< "scale=0; ((($ThreadsAvailable*2)/3)+0.5)/1" 2>/dev/null)

    [[ "$(( ThreadsSuggestLower % 2 ))" == "1" ]] && (( ThreadsSuggestLower +=1 ))

  fi


  [[ $ThreadsAvailable ]] && printColor "\n\n  You have %s 'cores' available  (actual cores, or cores x threads) " "$ThreadsAvailable"
 
  printf "\n\n  Normal guest setting should be around 2/3 to 3/4 of the host's."
  
  printf "\n\n  All available 'cores x threads' can be allocated, esp. if running the VM full screen"

  printf "\n\n  Possibly, if using 'big LITTLE'  CPU's you *may* need to use a lower figure."   

  printf "\n\n  Quickemu defaults to safe half, minimals. Be generous as less will actually be used."
  
  # printf "\n\n  Remember, this is the 'maximum-allowed' number"
  
  printf "\n\n  And CPU virtualizing units will always protect the host ...."

  [[ $ThreadsSuggested ]] && printColor "\n\n  Suggested setting:  cpu_cores=\"%s\"  or  cpu_cores=\"%s\"" "$ThreadsSuggested"  "$ThreadsSuggestLower"


  CoresSetNumber=
  
  ConfLine="$(grep -s 'cpu_cores' "$TuneUpTmpCopy")"

  if [[ $ConfLine ]]; then 
  
    printf "\n\n  CURRENT setting:  %s" "$ConfLine"    

    CurrentCores="$(tr -cd "[:digit:]" <<< "$ConfLine")"
 
    [[ $CurrentCores != "$ThreadsSuggestLower"  ]] && printColor "   [c] to use current  "

  fi

  if [[ $ThreadsSuggestLower ]]; then printColor "\n\n  [enter] use %s " "$ThreadsSuggestLower"; else  printf "\n\n" ; fi

  printColor "  [integer] choose (even numbers are usual)  "
  
  printColor "[d] use quickemu defaults   [q] quit tune-up \n\n"  

  read -rp  "  >  "  CoresSetNumber

  [[ $CoresSetNumber == "q" ]] && return

  if [[ $CoresSetNumber && $(tr -cd "[:digit:]" <<< "$CoresSetNumber") ]]; then

    printf "cpu_cores=\"%s\"\n"  "$CoresSetNumber"  >> "$TuneUpTargetDraft"

  elif [[ $CoresSetNumber == "c" ]]; then

    printf "%s\n" "$ConfLine" >> "$TuneUpTargetDraft" 

  elif [[ $CoresSetNumber == "d" ]]; then  true 

  else

    if [[ $ThreadsSuggestLower ]]; then  
    
      printf "cpu_cores=\"%s\"\n"  "$ThreadsSuggestLower"  >> "$TuneUpTargetDraft"

      printf "\n\n  Max cores set to %s " "$ThreadsSuggestLower"

    fi

  fi

  SharedRAM_NotFound=
  RAM_Available=
  Shared_RAM_Mb=

  printf "\n\n\n  Memory showing: \n\n"

  free --si -h  

  RAM_Available="$(free --mega -h 2>/dev/null | grep Mem | cut -d':' -f2)"

  if [[ "$RAM_Available" == *G* ]]; then  RAM_Available="$( cut -d'G' -f1  <<< "$RAM_Available" | tr -cd '[:graph:]' 2>/dev/null)"

  else  RAM_Available=  ; fi
  
  # @2023/11 free --mega  can fail in Manjaro ( & arch ?) at the command prompt at least  (interactive)

  if [[ ! $RAM_Available ]]; then

    RAM_Available="$(grep MemTotal /proc/meminfo | cut -d':' -f2 | tr -cd '[:digit:]' 2>/dev/null)"

    RAM_Available="$(bc <<< "scale=1; $RAM_Available/999999" 2>/dev/null)"

    SharedRAM_NotFound=1

  else 

    Shared_RAM_Allocated="$(free --mega -h 2>/dev/null | grep Mem | cut -d':' -f2 | cut -d'G' -f4 | tr -cd '[:graph:]' 2>/dev/null)"

    if [[ "$Shared_RAM_Allocated" == *M* ]]; then 

      Shared_RAM_Allocated="$(cut -d'M' -f1 <<< "$Shared_RAM_Allocated")"

      Shared_RAM_Mb="$Shared_RAM_Allocated"

      if [[ $(bc <<< "$Shared_RAM_Allocated < 500") == 1 ]]; then Shared_RAM_Allocated=

      else Shared_RAM_Allocated="1" ; fi

      [[ ! $Shared_RAM_Allocated ]] && SharedRAM_NotFound=1

    fi

  fi

  printf "\n\n\n"

  printColor "  Don't stinge on the VM's RAM  Again, esp. if running the VM full screen."

  printf "\n\n  Quickemu will default to half, even quarter minimals ..."

  printf "\n\n  For full screen VM, suggest RAM of around 2/3 total, round up/down to expected guest/host demand"

  [[ $Shared_RAM_Allocated || $SharedRAM_NotFound ]] && printf "\n\n  Add about half of shared RAM, used for graphics"
  
  printf "\n\n  Integer values only. Possibly reduce if high quotas present.  \n\n  Use programs such as 'htop' for further tuning."

  if [[ $RAM_Available ]]; then
 
    printColor "\n\n  Your host system is showing %sG of RAM, " "$RAM_Available"  
    
    if [[ $Shared_RAM_Allocated ]]; then printColor "of which %sG is possibly allocated to graphics" "$Shared_RAM_Allocated"
 
    elif [[ $Shared_RAM_Mb ]]; then printColor "of which %sM *may* be allocated to graphics" "$Shared_RAM_Mb"
 
    elif [[ $SharedRAM_NotFound ]]; then printColor "of which some *may* be allocated to graphics" ; fi
 
    # suggest around 2/3 of RAM (using around half of shared graphics) rounded as applicable

    if [[ $Shared_RAM_Allocated ]]; then  RAM_ForCalcs="$(bc <<< "scale=3; ($RAM_Available-($Shared_RAM_Allocated/2))/1" )"

    else RAM_ForCalcs="$RAM_Available"
    
    fi

    RAM_Suggested_Lower="$(bc <<< "scale=3; (($RAM_ForCalcs /100)*55) /1" )"

    RAM_Suggested_Higher="$(bc <<< "scale=3; (($RAM_ForCalcs /100)*65) /1" )"

    printf -v RAM_Suggested_Lower  "%.0f" "$RAM_Suggested_Lower"           # rounded up/down

    printf -v RAM_Suggested_Higher  "%.0f" "$RAM_Suggested_Higher"         # rounded up/down

    printf "\n\n  Suggested RAM (lower)  = %sG"  "$RAM_Suggested_Lower"

    printf "\n\n  Suggested RAM (higher) = %sG"  "$RAM_Suggested_Higher"

  fi

  ConfLine="$(grep -s 'ram=' "$TuneUpTmpCopy")"

  if [[ $ConfLine ]]; then 
  
    printf "\n\n  CURRENT setting:  %s" "$ConfLine"    

    Current_RAM="$(tr -cd "[:digit:]" <<< "$ConfLine")"
 
    [[ $Current_RAM != "$RAM_Suggested_Higher"  ]] && printColor "   [c] to use current  "

  fi


  RAM_SetNumber=

  printColor "\n\n  [enter] use %s   [integer] choose   " "$RAM_Suggested_Higher"
 
  printColor "[d] to use quickemu defaults  [q] quit tune-up \n\n"

  read -rp  "  >  "  RAM_SetNumber

  printf "\n\n"

  [[ $RAM_SetNumber == "q" ]] && return

  if [[ $RAM_SetNumber ]] && [[ $(tr -cd "[:digit:]" <<< "$RAM_SetNumber") ]]; then

    printf "ram=\"%sG\"\n"  "$RAM_SetNumber"  >> "$TuneUpTargetDraft"

  elif [[ $RAM_SetNumber == "d" ]]; then true

  elif [[ $RAM_SetNumber == "c" ]]; then

    printf "%s\n" "$ConfLine" >> "$TuneUpTargetDraft" 

  else

    [[ $RAM_Suggested_Higher ]] &&  printf "ram=\"%sG\"\n"  "$RAM_Suggested_Higher"  >> "$TuneUpTargetDraft" 

  fi


  # add in other conf lines

  printf "\n" >> "$TuneUpTargetDraft" 

  mapfile -t TuneUpArr < "$TuneUpTmpCopy"

  for Line in "${TuneUpArr[@]}"; do

    if [[ $(tr -cd "[:graph:]" <<< "$Line") ]] && [[ "$Line" != *'/usr/bin'* ]] && [[ "$Line" != 'disk_size='* ]] \
    && [[ "$Line" != 'cpu_cores='* ]] && [[ "$Line" != 'ram='* ]] && [[ "$Line" != '#tpm='* ]]  && [[ "$Line" != 'tpm='* ]] \
    && [[ "$Line" != *'cureboot='* ]]; then

      [[ "$Line" == '# Secure Boot'* ]] && printf "\n" >> "$TuneUpTargetDraft"
      printf "%s\n" "$Line" >> "$TuneUpTargetDraft" 

    fi

  done

  # Check for OS specifics

  guest_os=

  ConfLine="$(grep -s 'guest_os=' "$TuneUpTmpCopy")"

  if [[ ! $ConfLine ]]; then

    printColor "\n\n  WARNING  a line 'guest_os=\"xxx\"' should be present"

    printf "\n\n  This should be added manually, where  =\"linux\"  \"windows\"  or \"macos\" are valid options"

    printf "\n\n  Re-run this checker after the guest_os line has been added"

  else

    eval "$ConfLine"

    if [[ $guest_os == "windows" ]]; then 

      printColor "\n\n Windows: "

      printf "\n\n  Software TPM should be set as \"on\" until at least the registration process is complete"

      printf "\n\n  but can be set to \"off\" at later stages. Lines may commented-out with a \"#\" \n"
      
      ConfLine="$(grep -s '# Secure Boot' "$TuneUpTmpCopy")"

      if [[ ! $ConfLine ]]; then  
      
        printf "\n\n# Secure Boot should normally be off & TPM should be on."   >> "$TuneUpTargetDraft"

      fi

      {   printf "\ntpm=\"on\"" 

          printf "\nsecureboot=\"off\"\n\n"
      
      }   >> "$TuneUpTargetDraft"


    elif [[ $guest_os == "macos" ]]; then 

      ConfLine="$(grep -s 'macos_release=' "$TuneUpTmpCopy")"

      if [[ ! $ConfLine ]]; then 

        printColor "\n\n  WARNING a line:  macos_release=\"catalina\" or similar should be present"
        
        printf "\n\n  This instructs Quickemu how to optimise & should be added manually.\n"

        printf "macos_release=\"\"\n"  >> "$TuneUpTargetDraft"

      fi

    fi

  fi


  while true; do 

    printColor "\n\n  CHECK ME: \n\n"

    cat "$TuneUpTargetDraft"

    TuneUpConfConfirm=

    printColor "\n\n  [enter] all good, continue    [r] revert back to the old config"
    
    printf "\n\n  [e]  edit the config file in more detail   "
    
    [[ $NewFileTuneUp ]] && printf "[d]  delete the VM - something went wrong ..."

    printf "\n\n"

    read -rp "  >  "   TuneUpConfConfirm


    if [[ $TuneUpConfConfirm == "e" ]]; then 

      printf "\n# To restore use name:   %s \n" "$TuneUpTargetFile" >> "$TuneUpOldFile"

      qqX_edit_settings  "tune-up" 
  
    elif [[ $TuneUpConfConfirm == "r" ]]; then 

      printColor "\n\n  Undoing changes to config file \n\n"

      cat "$TuneUpTargetFile"

      printf "\n\n"

      read -rp "  [enter] to continue  >  "

      break
    
    elif [[ $NewFileTuneUp ]] && [[ $TuneUpConfConfirm == "d" ]]; then 
    
      function_delete_Selected_VM  "$TuneUpDistroFolder" "$TuneUpTargetFile"

      break

    else

      printf "\n# To restore use name:   %s \n" "$TuneUpTargetFile" >> "$TuneUpOldFile"

      cp "$TuneUpTargetDraft" "$TuneUpTargetFile"

      break

    fi

  done

}



show_qmod_title() {

  if [[ $ModName_qqX ]]; then printf "\n" ; function_qqX_printf_logo_title  "$qqX_title" ;  printf "\n"

  else printColor "\n\n  Quickemu Mod - Virtual Machine Launcher  Wrap Version %s"  "$ModVersion" ; fi

  printf "\n\n  Currently wrapping:  QEMU %s   quickemu %s  &  quickget %s" "$QEMU_VER_LONG"  "$QE_VerNumber"  "$QG_VerNumber"

  [[ $VerboseArgs ]] && printf  "  (Verbose Args)"
  
  PWD_Folder="$(pwd)"

  HideFolderTitleBar=

  for TF in "${Extra_VM_Folder[@]}"; do

    [[ $TF ==  "$PWD_Folder" ]] || [[ $PWD_Folder ==  "$Default_VM_Folder" ]] &&  HideFolderTitleBar=1

  done

  if [[ ! $HideFolderTitleBar ]]; then  
  
    printf "\n\n  Default Folder: %s   Current Folder: %s \n"  "$Default_VM_Folder" "$(pwd)"

  else

    printf "\n"

  fi

}


function_show_main_menu_header () {

  if [[ ! $VM_InstanceName ]]; then

    Select_VM=1

  else

    #[[ $InstancePID ]] && [[ ! $( ps -p "$InstancePID" --no-headers 2>/dev/null) ]] && InstancePID=

    [[ $InstancePID ]] && [[ ! $(cat "${VMDIR}/${VMNAME}.pid" 2>/dev/null) ]] && InstancePID=

    #  Now using Qemu's PID file.  REVIEW   

    [[ ! $InstancePID ]] && printf "\033c"  && show_qmod_title
 
    printf "\n"

    printColor "    %s " "$VM_InstanceName" 

    #  Default_VM_Folder, as defined in settings.  Highlight if using non-default or current (pwd).

    if [[ "$(pwd)" == "$Default_VM_Folder" ]]; then printf "  @   %s"  "$VM_Conf_Dir"     

    else  printColor "  @   %s"  "$VM_Conf_Dir" 

    fi

    printf "\n"

    [[ $KVM_MSR_Error ||  $Show_MSR_Warnings  || $MSR_Warnings || $Verbose_MSR_dialog ]] && print_kvm_status

  fi

}



##  START:  QUICKEMU-WRAP  menu interface  &  API DIVIDER  ###

#   NB   Also see initial $1 flag setting traps at script start

if [[ $1 ]]; then

  # simple API for command line or .conf starts  ie.  --vm  "path/folder/file.conf"   

  if [[ $XDG_QcowRightClick ]]; then

    # should haved changed to the parent directory at this point

    if grep -q -s "disk_img="'"'"$XDG_QcowRightClick_VM_Dir_Name/$XDG_QcowRightClick_DiskName" "$XDG_QcowRightClick_VM_Dir_Name.conf" ; then

      VM_Conf_Dir="$(pwd)"

      VM_Conf_File="$XDG_QcowRightClick_VM_Dir_Name.conf"

      VM_InstanceName="$XDG_QcowRightClick_VM_Dir_Name"

      shift

    else

      XDG_QcowRightClick_NO_qqX=1

      shift

    fi

  elif [[ $XDG_StartVariant ]]; then

    echo
    
    function_qqX_logo

    printColor "\n\n  %s  not understood \n\n  Or non valid file type %s \n\n"  "$1 "  "$XDG_MimeType"

    sleep 5 


  elif [[ "$1" == *'vm' ]] && [[ "$2" == *'.conf' ]]; then

    # conforms to  -vm | --vm  file.conf
  
    VM_Conf_Dir="$(dirname "$2")"

    VM_Conf_File="$(basename "$2")"

    shift $#

  else 
  
    show_wrap_CLI_usage 

    printf "\n\n"

    exit directly

  fi


  if [[ ! $VM_Conf_Dir ]]; then 
  
    VM_Conf_Dir="$CurrentFolder"

  else

    if [[ ! -e  "$VM_Conf_Dir"  ]]; then 

      printColor "\n\n  %s  not found "  "$VM_Conf_Dir "
    
      function_conf_error  "Q-wrap COMMAND LINE Path Instruction,"

    else

      cd "$VM_Conf_Dir"  || function_conf_error  "Q-wrap COMMAND LINE Path Instruction,"

    fi

  fi

  if [[ ! -e "$VM_Conf_Dir/$VM_Conf_File" ]]; then 

    printColor "\n\n  %s  not found "  "$VM_Conf_File"
  
    function_conf_error  "Q-wrap COMMAND LINE .Conf Instruction,"

  fi


  # Selection now made, but array must still be loaded as may be needed for reselecting later ...

  ByPass_VM_Array_Selector=1

fi


##  Start Q-wrap menu :   

tput civis

printf "\033c"

show_qmod_title

MultiInstanceCount="$(pgrep -c "$ModName")"

if [[ $MultiInstanceCount -gt 1 ]]; then 

  tput sc

  printColor "\n\n  WARNING: more than one instance of %s appears to be running "  "$ModName"

  printf "\n\n  To prevent cross-linking, this instance or the other instance(s) should be closed \n\n"

  read -rp "  [q] to quit    [enter] to continue  > "   MultiQuit

  [[ $MultiQuit == "q" ]] && exit directly

  tput rc ; tput ed

  #  clear back the last lines to sc (set cursor)
  
fi

if [[ "$(pgrep quickgui)" ]] ; then

  printColor "\n\n  WARNING: an instance of QUICKGUI appears to be running " 

  printf "\n\n  Both this program and qqX do have an amount of cross-link protection, however note that:"
  
  printf "\n\n  Stopping an instance shown as running in QuickGUI may stop an instance started in qqX. \n\n"

  read -rp "  [q] to quit    [enter] to continue  > "   MultiQuit

  [[ $MultiQuit == "q" ]] && exit directly

fi

# quietly check for qqX updates, in the background. Once only, at the start.

if [[ ! $Disable_qqX_upd_Checks ]]; then

  check_for_qqX_updates &   

  qqX_UpdaterPID="$!"

fi

# check the VM disk is not corrupt  (quickemu default name only)

# to run only after the VM_InstanceName exists

# https://qemu.readthedocs.io/en/v7.2.2/tools/qemu-img.html#cmdoption-qemu-img-commands-arg-check

function check_disk_is_okay {

  local PrintErrors=

  for CheckArg in "$@" ; do 
    if [[ $CheckArg == "PrintErrors" ]]; then PrintErrors=1 ; shift ; fi
  done

  if [[ $1 && ! $2 ]]; then

    DiskCheckFolderName="$(dirname "$1")"
    DiskCheck_Disk="$(basename "$1")"

  else

    if [[ $1 ]]; then DiskCheckFolderName="$1" ; else DiskCheckFolderName="$VM_InstanceName"; fi
    if [[ $2 ]]; then DiskCheck_Disk="$2" ; else DiskCheck_Disk="disk.qcow2"; fi

  fi

  DiskChkExitCode=
  DiskChkStatus=

  if [[ -e "$DiskCheckFolderName/$DiskCheck_Disk" ]]; then

    # Disk won't exist if new (or named differently REVIEW )

    # Qemu-img with '-q' only outputs if error & on outputs on stderr   The exit status remains separately recorded.

    if [[ $PrintErrors ]]; then  "$QEMU_IMG" check -q "$DiskCheckFolderName/$DiskCheck_Disk"

    else "$QEMU_IMG" check -q "$DiskCheckFolderName/$DiskCheck_Disk"  2>/dev/null ; fi

    # Note "Okay"  is also used as a flag  > caution if changing   # REVIEW  could also be flagged using:

    # DiskChkMsg="$("$QEMU_IMG" check -q "$DiskCheckFolderName/$DiskCheck_Disk" 2>&1)" 

    case "$?" in 

      0) DiskChkStatus="Okay" ;;

      1) DiskChkStatus="Check not completed because of internal errors" ;;

      2) DiskChkStatus="Check completed, image is corrupted" 
      
         DiskChkExitCode=2 ;;

      3) DiskChkStatus="Check completed, image has leaked clusters, but is not corrupted" 
      
         DiskChkExitCode=3  ;;

      63) DiskChkStatus="Checks are not supported by the image format -- not qcow2 ??" ;;

    esac

  fi

}

## filter for 'errors' that changes in QEMU might throw up and that we want to ignore  (see main settings)

filter_and_flag_qemu_errors () {

  if [[ $(cat "$QemuErrorLog" 2>/dev/null) ]]; then 

    QemuErrorExists=1

    if [[ "${QemuErrorFilter[*]}" ]]; then
      for ErrChk in "${QemuErrorFilter[@]}"; do
        if grep -q -s "$ErrChk" "$QemuErrorLog" ; then QemuErrorExists= ; return ; fi
      done
    fi

  else  QemuErrorExists=
    
  fi

}


## MAIN MENU  (select VM then choose actions to do)

while true ; do

  MainMenuChoice=

  SnapTitle=
  SnapNumber=
  SnapName=

  QemuErrorLog=

  function_show_main_menu_header

  if [[ $XDG_QcowRightClick_NO_qqX ]] || [[ $XDG_QcowRightClick ]]; then

    check_disk_is_okay "$XDG_QcowRightClick_VM_Dir_Name"  "$XDG_QcowRightClick_DiskName"

    if [[ $DiskChkStatus ]] && [[ $DiskChkStatus != "Okay" ]]; then 

      printColor "\n\n DISK.QCOW2 ERROR  %s "  "$DiskChkStatus"

    fi

    if [[ $XDG_QcowRightClick_NO_qqX ]]; then 
      
      [[ $DiskChkStatus == "Okay" ]] && printColor  "\n\n QEMU qcow2 disk found BUT not configured as qqX/quickemu bootable: \n\n"

    else
      
      printColor  "\n\n  qqX/quickemu bootable: \n\n"

      if [[ $DiskChkStatus != "Okay" ]]; then  

        printColor "  BUT has errors: Try qqX [disk] utils " 
      
        printf "\n\n Also see  https://qemu.readthedocs.io  "
      
        printf  "> QEMU disk image utility notes\n\n"

      fi

    fi

    "$QEMU_IMG" info "$XDG_QcowRightClick_VM_Dir_Name/$XDG_QcowRightClick_DiskName"
        

    if [[ $XDG_QcowRightClick_NO_qqX ]]; then  

      printColor "\n\n See  https://qemu.readthedocs.io  "
      
      printf  "> QEMU disk image utility may be useful"
    
      printf "\n\n  [enter] to quit  "
      
    else  printColor "\n\n  [enter] to continue  "
      
    fi

    read -rp  "  >  "
    
    

    if [[ ! $XDG_QcowRightClick_NO_qqX ]]; then

      XDG_QcowRightClick=
      function_show_main_menu_header

    else

      # XDG_QcowRightClick_NO_qqX=
      exit directly 

      # REVIEW  add disk conversion utility ??  Or something?

      #function_refresh_and_reload_VM_arrays
      #MainMenuChoice="vm"
      #function_show_main_menu_header
      #continue
   
    fi

  elif [[ $SettingsFileAdjusted ]]; then 

    function_Select_VM

    MainMenuChoice="vm" 

  elif [[ $KillPID_Menu_Exited ]] || [[ $Select_VM ]] ; then

    if [[ $InstancePID ]] && [[ $( ps -p "$InstancePID" --no-headers ) ]] && [[ $(cat "${VMDIR}/${VMNAME}.pid" 2> /dev/null) ]]; then 

      #  REVIEW  add use of PID file
    
      printf "\n\n  %s is still running ..." "$VM_InstanceName"

      function_KillPID_Menu

      continue

    else 

      InstancePID=
    
      if [[ $Select_VM ]]; then  

        #declare -p VM_Array
        #echo    "  main menu loop - after Select_VM call:"
        #read -p "  VM_InstanceName is $VM_InstanceName  ByPass_VM_Array_Loader is $ByPass_VM_Array_Loader  ByPass_VM_Array_Selector is $ByPass_VM_Array_Selector"
        #echo

        function_Select_VM

        MainMenuChoice="refresh" 

        #declare -p VM_Array
        #echo    "  main menu loop - after Select_VM run:"
        #read -p "  VM_InstanceName is $VM_InstanceName  ByPass_VM_Array_Loader is $ByPass_VM_Array_Loader  ByPass_VM_Array_Selector is $ByPass_VM_Array_Selector"
        #echo

      else

        printf "\n    %s   >   is now shut down \n" "$VM_InstanceName" 

        if [[ -s "$QemuErrorLog" ]]; then printf "\n\n" ; cat "$QemuErrorLog" ; fi

      fi
    
    fi

    [[ $KillPID_Menu_Exited ]] && KillPID_Menu_Exited=

  fi


  ##  Set or adjust any VM_Conf_Dir related globals  (VM is now selected OR has been re-selected)

  QemuArgsFile="$VM_Conf_Dir/$VM_InstanceName/Qemu_args_List.txt"

  QemuVirtHardwareRecord="$VM_Conf_Dir/$VM_InstanceName/QemuVirtHardwareRecord.txt"

  check_screen_percentage

  set_drive_paths

  find_main_drive

  check_disk_is_okay


  if [[ $MainDrive_NOT_present ]]; then

    printf "\n\n  Main Drive NOT found (yet)  >  Start VM (to create one)"

    printf "\n\n  %s \n"  "$disk_img"

  fi

  ## Qemu_IMG log > eg. for disk repair functions  

  Qemu_IMG_ErrorLog="$VM_InstanceName/${VM_InstanceName}.qemu.IMG.error.log"

  if [[ -s "$Qemu_IMG_ErrorLog" ]]; then printf "\n\n" ; cat "$Qemu_IMG_ErrorLog" ; fi

  ##  Qemu SYSTEM log

  QemuErrorLog="$VM_Conf_Dir/$VM_InstanceName/${VM_InstanceName}.qemu.error.log"



  ## Print menu:

  if [[ ! $MainMenuChoice ]]; then

    printf  "\n"

    if [[ ! $VM_InstanceName ]]; then printColor  "  [vm]  reselect " ; else   printf  "  [vm]  SELECT " ; fi

    [[ $KVM_MSR_Error || $KVM_MSR_Warn ]] && [[ ! $ShowUtilsMenu ]] && printf  "  [m]  msrs help "

    
    if [[ ! $ShowUtilsMenu ]]; then

      printf  "  [u]  show utils & technical"

    else

      printf "   [del] delete selected VM"

      printColor  "    [u]   hide utils & technical"

      printf "\n"

      printf "\n\n  [tune] config tune-up wizard    [disk]  resize or repair existing drives" 

      printf "\n\n  [2] create a second  [3] a shared  > virtual hard-drive \n"


      if [[ $VerboseArgs ]]; then printf  "\n\n  [v]   hide verbose qemu args"	

      else  printf  "\n\n  [v]   show verbose qemu args & check before booting"	; fi

      printf  "   [xa]  edit extra qemu args  "

    
      printf "\n\n  [hdw]  add current config to this VM's Virtual Hardware Record    [m] msrs help "
    
      [[ $ExtraArgs ]] && printf  "\n\n  Extra Args:  %s"  "$ExtraArgs"

      printf "\n"

    fi

    printf  "\n\n  [sc]  create   [sd]  delete   [sa]  apply   [sl]  list   >  SNAPSHOTS "

    printf "\n"

    if [[ $XDG_QcowRightClick_NO_qqX ]]; then

      printColor  "\n\n  ?? "

    else

      printColor  "\n\n  [d]   start with SDL display"
      
      if [[ "$VM_InstanceName" =~ 'windows' ]]; then  printColor " (sizes to Windows > display, useful for Windows updates)" 

      elif [[ "$VM_InstanceName" =~ 'macos' ]]; then  printColor " (size determined by MacOS)" 

      elif [[ $SDL_ScrnPcnt_Cmd ]]; then printColor " (%s%%)    [df] 99%% "  "$SDL_ScreenPercent"

      elif [[ $SDL_ScrnPcnt_Error ]]; then printf " (%s%% %s > see settings)"  "$SDL_ScreenPercent"  "$SDL_ScrnPcnt_Error"

      fi

      if [[ $HeadlessDisplay ]]; then printColor 	"\n\n  [s]   start with Spice display (HEADLESS) " 

      else  printColor 	"\n\n  [s]   start with Spice display (scaleable)   [sf]  spice full screen (shift F12 to exit)"  ; fi

      printColor "\n\n  [g]   start with GTK display"

    fi

    printf "\n"

    printf  "\n\n  [gt]  quickget a new distro   [i]  show VM drives info "

    filter_and_flag_qemu_errors

    if [[ $QemuErrorExists ]] ; then printColor "  [ql]  show qemu error log"

    elif [[ $FileManager ]]; then 

      if [[ $(type -p "$FileManager") ]]; then printf "   [f]  open VM folder in file manager"
    
      else  printColor "   [set]  ERROR file manager not found" ; fi

    fi

    if [[ "$VM_InstanceName" == 'ubuntu'* ]] && [[ "$VM_InstanceName" == *'daily'* || "$VM_InstanceName" == "ubuntustudio-dvd" ]]; then 
    
      if [[ ! $(du -b -t 500000 "$VM_InstanceName/disk.qcow2" 2>/dev/null) ]] && [[ $(ls "$VM_InstanceName/"*devel.iso 2>/dev/null) ]] ; then

        #  Live VMs only, as zsync only updates the iso file.  Live VM qcow2 disks are normally only 196k.  
        
        #  Test ceiling is set at 500k.  REVIEW if problems.

        if [[ -e "$VM_InstanceName/zsync.date.txt" ]]; then

          SyncDateNow="$(date +%s)"

          SyncDateOld="$(cat "$VM_InstanceName/zsync.date.txt")"

          LastZsync="$((SyncDateNow - SyncDateOld))"

          #  older than 6hrs (in epoch seconds)

          if [[ $LastZsync -gt 21600 ]]; then  printColor "   [z] run a quick zsync check - last update happened %d hrs ago"  "$((LastZsync/3600))"

          else  printf "   [z] run a quick zsync check - last update happened %d hrs ago"  "$((LastZsync/3600))"
          
          fi

        else

          date +%s > "$VM_InstanceName/zsync.date.txt"
            
        fi


        zsync_quickget_code_snippets_in_a_way_that_shows_them_as_formatted_notes () {

          ## DEV NOTES in formatted display, NOT FOR RUNNING

          # FROM:  function get_ubuntu in quickget (original, ver 4.8)  

          if [[ "${RELEASE}" == *"daily"* ]] && [ "${OS}" == "ubuntustudio" ]; then
              # Ubuntu Studio daily-live images are in the dvd directory
              RELEASE="dvd"
          elif [ "${RELEASE}" == "daily-canary" ] && [ "${OS}" != "ubuntu" ]; then
              # daily-canary is only available for Ubuntu, switch flavours to daily-live
              RELEASE="daily-live"
          elif [ "${RELEASE}" == "daily-legacy" ] && [ "${OS}" != "ubuntu" ]; then
              # daily-legacy is only available for Ubuntu, switch flavours to daily-live
              RELEASE="daily-live"
          fi

          # AND from function list_csv()

          # shellcheck disable=SC2086,SC2034

          if [ "${OS}" == "macos" ]; then
            DOWNLOADER="macrecovery"

          elif [ "${OS}" == "ubuntu" ] && [ "${RELEASE}" == "daily-canary" ] && [ ${HAS_ZSYNC} -eq 1 ]; then
            DOWNLOADER="zsync"

          elif [ "${OS}" == "ubuntu" ] && [ "${RELEASE}" == "daily-legacy" ] && [ ${HAS_ZSYNC} -eq 1 ]; then
            DOWNLOADER="zsync"

          elif [[ "${OS}" == *"ubuntu"* ]] && [ "${RELEASE}" == "devel" ] && [ ${HAS_ZSYNC} -eq 1 ]; then
            DOWNLOADER="zsync"

          # qqX: NB only the iso gets dubbed with 'devel' suffix, otherwise VMs they keep their names 
          
          # eg  'ubuntu-mate-daily-live' 'ubuntustudio-dvd' 'ubuntu-daily-canary'


          # An actual 'ubuntu-devel' release or folder doesn't seem to ever get created or offered.

          # Presume release=devel (@2023/07) has now been superseded, or was merely intended as an indicative name only ...

          
          # Also note that quickget includes code to remove 'zs.old' files, which may or may not have been needed,
          
          # but Zsync can now give these files root protection so this removal may not work ...   REVIEW  Add a qqX copying mechanism? 

          else
            DOWNLOADER="${DL}"
          fi

        }

        #  Sort out the quickget / function_quick_get_wrap  call & the var/flags   
        
        # Expect these to need updating from time to time ...

        Zsync_OS_Name="$VM_InstanceName"

        Zsync_OS_Name="${Zsync_OS_Name/'-daily'/}"
        Zsync_OS_Name="${Zsync_OS_Name/'-live'/}"
        Zsync_OS_Name="${Zsync_OS_Name/'-canary'/}"
        Zsync_OS_Name="${Zsync_OS_Name/'-legacy'/}"
        Zsync_OS_Name="${Zsync_OS_Name/'-jammy'/}"
        Zsync_OS_Name="${Zsync_OS_Name/'-dvd'/}"

        Zsync_Release_Name="$VM_InstanceName"

        Zsync_Release_Name="${Zsync_Release_Name/"${Zsync_OS_Name}-"/}"

        #echo; echo "$Zsync_OS_Name  $Zsync_Release_Name" 

        Zsync_Updatable_ISO=1

        # zero main menu flag that sets only if updater run from main 
        MainMenuZsync=

      fi

    else 

      Zsync_Updatable_ISO=
      Zsync_OS_Name=
      Zsync_Release_Name=

    fi
    
    printf  "\n\n  [h]   show help & info   [set] open settings file in text editor   [conf] open .conf file"

    if [[ $ConfSettingsAdjusted ]]; then 

      ConfSettingsAdjusted=
      printf "\n\n  done - general refresh not normally needed for .conf edits (use [vm] if req'd)"

    fi

    printf  "\n\n"

    if [[ $qqX_UpdaterPID ]]; then

      while true ; do

        if [[ $( ps -p "$qqX_UpdaterPID" --no-headers 2>/dev/null) ]]; then  
        
          sleep 1

        else 
        
          # shellcheck source=/dev/null
          source "$qqX_Upd_VarsTmpFile"
          qqX_UpdaterPID= 
          break
          
        fi

      done

    fi

    if [[ $DiskChkStatus ]] && [[ $DiskChkStatus != "Okay" ]]; then 

      printColor "  DISK.QCOW2 ERROR  %s  \n\n  [disk] repair utils "  "$DiskChkStatus"

    else

      [[ $qqX_UpdateAvail ]] &&  printColor "  [dl]  download new qqX update: %s %s " "$qqX_CurrentReleaseType" "$qqX_LatestRelease"

    fi

    printf  "  [q]   quit \n\n"

    read -rp "  >  "  MainMenuChoice

  fi


  # ACTIONS: 

  if [[ $MainMenuChoice == "h" ]] ; then

    show_quickemu_WRAP_help

   elif [[ $MainMenuChoice == "del" ]]; then 

    function_delete_Selected_VM  "$VM_InstanceName"  "$VM_Conf_File"

  elif [[ $MainMenuChoice == "hdw" ]] ; then

    AddHW_Record=

    if [[ -e "$QemuArgsFile" ]]; then 

      printf "\033c"

      if [[ -e "$QemuVirtHardwareRecord" ]]; then printf "\n\n  Existing Record: \n\n"; cat "$QemuVirtHardwareRecord"; fi

      printf "\n\n  Current Args / Qemu Virtual Hardware \n\n"
    
      cat "$QemuArgsFile"
      
      if [[ -e "$QemuVirtHardwareRecord" ]]; then printColor "  Copy Current Args onto the end of existing record list?\n\n"

      else printColor "  Create new Virtual Hardware Record using Current Args?\n\n"

      fi

      read -rp "  [enter] to continue  [b] to go back"  AddHW_Record

      if [[ $AddHW_Record != "b" ]]; then 
             
        cat "$QemuArgsFile" >> "$QemuVirtHardwareRecord"

      fi

    else

      printf "\n\n  No Args log found. The virtual machine launch process must be run first. \n\n"

      printf "\n\n  The 'VerboseArgs' option may be used, which will allow checking.  \n\n"

    fi 

  elif [[ $MainMenuChoice == "u" ]] ; then

    if [[ ! $ShowUtilsMenu ]]; then ShowUtilsMenu=1 ; else ShowUtilsMenu= ; fi

  elif [[ $MainMenuChoice == "set" ]] ; then

    qqX_edit_settings

  elif [[ $MainMenuChoice == "ql" ]] ; then

    printf "\n\n"

    if [[ $(cat "$QemuErrorLog" 2>/dev/null) ]] ; then  cat "$QemuErrorLog"

    else  printf "\n\n  No qemu errors were present on the last VM load ...  ?? " ; fi

    printf "\n\n  [enter] to return \n\n"

    read -rp "  >  "

  elif [[ $MainMenuChoice == "f" ]] ; then

    nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt" "$FileManager"  "$VM_Conf_Dir/$VM_InstanceName"  &

    # > "/tmp/qqX_nohup.txt" handles the output that tells us about handling of mixed gtk2 and gtk3 etc ....  
    
    #  As from 'WebBrowser' section and handling of firefox etc

  elif [[ $MainMenuChoice == "dl" ]] ; then

    printf "\033c \n\n"

    function_qqX_printf_logo_title  "qqX updater"

    printf "\n\n\n  https://github.com/TuxVinyards/qqX/releases/latest" 

    if [[ ! $qqX_LatestTarBall ]] ; then

      printColor "\n\n  ERROR  No latest release found.  Reload & retry ..."

    else 

      if [[ $qqX_Latest_Secure_TarBall ]]; then printf "\n\n  %s"  "$qqX_Latest_Secure_TarBall"

      else  printf "\n\n  %s"  "$qqX_LatestTarBall"
      
      fi

      printf "\n"

      AboutRelease="$(grep -s 'body' "/tmp/latest-qqX")"
      AboutRelease="$(tr -cd '[:print:]' <<< "$AboutRelease")"
      AboutRelease="$(cut -d ':' -f2 <<< "$AboutRelease")"
      AboutRelease="${AboutRelease//\"/}"
      AboutRelease="${AboutRelease//'### '/}"
      AboutRelease="${AboutRelease//'## '/}"
      AboutRelease="${AboutRelease//'\n'/}"
      AboutRelease="${AboutRelease//'\r'/}"

      IFS='-' read -ra AboutReleaseArray <<< "$AboutRelease"

      for Line in "${AboutReleaseArray[@]}" ; do  printColor "\n\n  %s"  "$Line"  ; done

    fi

    printf "\n\n\n"

    while true ; do

      printf "  Neatly download the release into its own sub-folder at: "
      
      [[ -d "$HOME/Downloads" ]] && printf "\n\n  [d] Downloads folder  %s"  "$HOME/Downloads"

      if [[ $(pwd) == "$Default_VM_Folder" ]]; then 
      
        printColor "\n\n  [enter] Current/Default Folder  %s "  "$Default_VM_Folder" 

      else  

        printf "\n\n  [c] Current Folder  %s "  "$(pwd)"  
        
        printColor "\n\n  [enter] Default VM folder  %s"  "$Default_VM_Folder"

      fi 

      printf "\n\n"

      [[ $WebBrowser ]] && printColor "  [w] to open qqX web page with %s"  "$WebBrowser"

      printf "   or [b] to go back \n\n"

      read -rp "  >  "  qqX_LatestTarBall_Folder

      printf "\n\n"


      if [[ $qqX_LatestTarBall_Folder == "d" ]]; then  qqX_LatestTarBall_Folder="$HOME/Downloads" ; break

      elif [[ $qqX_LatestTarBall_Folder == "c" ]]; then  qqX_LatestTarBall_Folder="$(pwd)" ; break

      elif [[ $qqX_LatestTarBall_Folder == "w" ]]; then  
     
        nohup > "/tmp/qqX_nohup.txt" 2> "/tmp/qqX_nohup.txt" "$WebBrowser"  "https://github.com/TuxVinyards/qqX"  & 

        # https://unix.stackexchange.com/questions/103483/opening-firefox-from-terminal#103486

        # > "/tmp/qqX_nohup.txt" handles the output that tells us how firefox etc has handled mixing of gtk2 and gtk3 etc ....

      elif [[ $qqX_LatestTarBall_Folder == "b" ]]; then  qqX_LatestTarBall_Folder= ; qqX_NoReleaseDL=1 ; break

      else qqX_LatestTarBall_Folder="$Default_VM_Folder" ; break

      fi

    done

    if [[ $qqX_NoReleaseDL ]]; then 

      qqX_NoReleaseDL=

    else

      # each release to its own folder makes things tidier

      qqX_LatestTarBall_Folder="$qqX_LatestTarBall_Folder/qqX.releases/$qqX_LatestRelease"

      mkdir -p "$qqX_LatestTarBall_Folder"


      # record where to come back to, when done

      ReturnFolder="$(pwd)"


      cd "$qqX_LatestTarBall_Folder"  || printColor "\n\n  ERROR  making or changing to qqX.releases folder"


      if [[ $qqX_Latest_Secure_TarBall ]]; then
        
        wget "$qqX_Latest_Secure_TarBall" -O "$qqX_LatestTarBall_Folder/qqX-${qqX_LatestRelease}.tar.gz"

        wget "$qqX_Latest_Secure_TarSha" -O "$qqX_LatestTarBall_Folder/qqX-${qqX_LatestRelease}.tar.gz.sha256"

        SHA_Check="$(sha256sum -c "$qqX_LatestTarBall_Folder/qqX-${qqX_LatestRelease}.tar.gz.sha256")"

        printColor "\n  SHA256 Check: %s "  "$SHA_Check"

        [[ "$SHA_Check" != *'OK' ]] &&  printf "\n\n  ERROR  - RETRY  DOWNLOAD \n\n"
      
      else

        wget "$qqX_LatestTarBall" -O "$qqX_LatestTarBall_Folder/qqX-${qqX_LatestRelease}.tar.gz"

      fi

      printColor "\n\n  Files downloaded to %s"  "$qqX_LatestTarBall_Folder"

      if [[ $BetaTxReleasePoint ]]; then 
      
        printf "\n\n\n  Transitioning from beta to release ...  If required,"
        printf "\n\n  Beta Notifications can be turned off in the general settings file \n"

      fi

      printColor "\n\n  Use the installer script to quickly refresh and add new features to your settings file."

      printf "\n\n  All option settings, locations and preferences will be kept."

      printf "\n\n\n  [enter] to run the installer script   [r] to return to main menu \n\n"

      read -rp "  >  "  qqX_UpdateExit

      if [[ $qqX_UpdateExit != "r" ]]; then 

        # https://stackoverflow.com/questions/651018/opening-a-tar-gz-file-with-a-single-command

        tar xvfz "$qqX_LatestTarBall_Folder/qqX-${qqX_LatestRelease}.tar.gz" 

        cd "$qqX_LatestTarBall_Folder/qqX-${qqX_LatestRelease}" || printColor "\n\n  ERROR  changing to qqX.releases folder"

        # move current execution away from this script and to the installer

        exec  bash -c "./qqX_setup_and_install  Upgrade" 

      fi

      sleep 0.6

      cd "$ReturnFolder"  || printColor "\n\n  ERROR  returning from qqX.releases folder"

    fi

  elif [[ $MainMenuChoice == "conf" ]] ; then

    qqX_edit_settings  conf

  elif [[ $MainMenuChoice == "tune" ]] ; then

      function_config_tune_up "$VM_Conf_File" "$VM_InstanceName"

  elif [[ $MainMenuChoice == "gt" ]] ; then

    function_quick_get_wrap

  elif [[ $MainMenuChoice == "z" ]] ; then

    if [[ $Zsync_Updatable_ISO && $Zsync_OS_Name && $Zsync_Release_Name ]]; then

      MainMenuZsync=1

      function_quick_get_wrap

    else 

      printColor "\n\n  %s is not being recognised as Zsync updatable Release \n\n"   "$VM_InstanceName"

    fi

  elif [[ $MainMenuChoice == "m" ]] ; then

    KVM_MSR_selector_LoadHelp=1
    select_msr_config

  # REVIEW  adding gtk option ?

  elif [[ $MainMenuChoice == "g" ]] ; then 

    printf "\n\n"

    if [[ -e "$VM_InstanceName/${VM_InstanceName}.pid" ]]; then 

      printColor "  ERROR:  QEMU appears to be running this machine elsewhere  (qemu.pid file found)"

      printf "\n\n [enter] to return \n\n"

      read -rp "  >  "

    else

      eval quickemu "-vm $VM_Conf_File --vm_boot --display gtk" 

      function_KillPID_Menu

    fi
        
  elif [[ $MainMenuChoice == "d" ]] || [[ $MainMenuChoice == "df" ]] ; then 

    printf "\n\n"

    if [[ -e "$VM_InstanceName/${VM_InstanceName}.pid" ]]; then 

      printColor "  ERROR:  QEMU appears to be running this machine elsewhere  (qemu.pid file found)"

      printf "\n\n [enter] to return \n\n"

      read -rp "  >  "

    else

      if [[ $MainMenuChoice == "df"  ]]; then

        eval quickemu "-vm $VM_Conf_File --vm_boot --screenpct 99  $Extra_QE_Params" 

      else

        eval quickemu "-vm $VM_Conf_File --vm_boot  $SDL_ScrnPcnt_Cmd $Extra_QE_Params" 

      fi

      [[ $InstancePID ]] && printf "\n  SDL: Use guest based display resize. Avoid resizing with the host mouse ...\n\n"

      function_KillPID_Menu

    fi
        
  elif  [[ $MainMenuChoice == "s" ]] || [[ $MainMenuChoice == "sf" ]] ; then

    printf "\n\n"

    if [[ -e "$VM_InstanceName/${VM_InstanceName}.pid" ]]; then 

      printColor "  ERROR:  QEMU appears to be running this machine elsewhere  (qemu.pid file found)"

      printf "\n\n [enter] to return \n\n"

      read -rp "  >  "

    else

      if [[ $HeadlessCmd ]]; then 

        # headless only runs in Spice

        eval quickemu "-vm $VM_Conf_File --vm_boot $HeadlessCmd  $Extra_QE_Params"

      elif [[ $MainMenuChoice == "sf" ]]; then

        eval quickemu "-vm $VM_Conf_File --vm_boot --display spice --fullscreen  $Extra_QE_Params"

      else

        eval quickemu "-vm $VM_Conf_File --vm_boot --display spice  $Extra_QE_Params"

      fi

      #  https://qemu.readthedocs.io/en/latest/system/invocation.html#hxtool-3 

      [[ $P_Kill_WaitSecs  -gt 1 ]] && P_Kill_WaitSecs="$(bc <<< "scale=0; ($P_Kill_WaitSecs+1.5)/2" 2>/dev/null)"

      function_KillPID_Menu

    fi
      
  elif  [[ $MainMenuChoice == "q" ]] ; then

    printf "\n\n"
    MainMenuChoice=
    exit directly	

  elif  [[ $MainMenuChoice == "i" ]] ; then

    drives_and_snapshots  info

    printf "\n\n  Resize using qqX [disk] utility if the disk size exceeds the (theoretical) virtual size"


    [[ $SharedDriveArgs || $SecondDriveArgs ]] && printColor "\n\n  Scroll to View "
    printf "\n\n  [enter] to return to menu \n\n "
    read -rp "  >  "  


  elif  [[ $MainMenuChoice == "disk" ]] ; then

    drives_and_snapshots  select  as_disk_util

    if [[ ! -e "$Selected_Drive" ]]; then

      printf "\n\n  [b] go back to main menu"

    else

      printf "\n\n  Selected Drive is %s " "$Selected_DriveName"

      check_disk_is_okay  "$Selected_Drive" 

      if [[ $DiskChkStatus != "Okay" ]]; then printColor "\n\n  Status:  %s"  "$DiskChkStatus" ; else printf "\n\n  Status:  %s"  "$DiskChkStatus" ; fi

      [[ $DiskChkStatus == "Okay" ]] && printf " > No errors "

      DiskSize_Envelope="$("$QEMU_IMG" info "$Selected_Drive" | grep -m 1 'virtual size:')"

      DiskSize_Occupied="$("$QEMU_IMG" info "$Selected_Drive" | grep -m 1 'disk size:')"

      DiskSize_Envelope="${DiskSize_Envelope/'virtual size: '/}"

      DiskSize_Occupied="${DiskSize_Occupied/'disk size: '/}"

      if [[ $DiskChkStatus == "Okay" ]]; then

        printf "\n\n  Theoretical Envelope: %s  containing  %s " "$DiskSize_Envelope" "$DiskSize_Occupied"

        printf "\n\n  Correction is advised if the content exceeds the theoretical envelope"

      fi

      if [[ $DiskChkExitCode == "3" || $DiskChkExitCode == "2" ]]; then

        printf "\n\n  Standard repair uses multithreads. Use slow method if standard fails."

        [[ $DiskChkExitCode == "2" ]] && printf "\n\n  WARNING - Full repair may cause some data loss if there are damaged data clusters ..."
        
      fi

      # Check there is disk free space to make a backup  

      if [[ "$DiskSize_Occupied" == *'KiB'* ]] || [[ "$DiskSize_Occupied" == *'MiB'* ]] ; then DiskSize_GiB_OnDisk="1"

      else DiskSize_GiB_OnDisk="${DiskSize_Occupied/'GiB'/}" ; fi

      DiskFreeGiB_OnHost="$(df -H --output=avail . | tail -1)"

      DiskFreeGiB_OnHost="${DiskFreeGiB_OnHost/G/}"

      if [[ $(bc <<< "$DiskFreeGiB_OnHost < ($DiskSize_GiB_OnDisk + 10)") == "1" ]]; then

        printColor "\n\n  WARNING: insufficient or very low space on host drive"
      
      else

        printf "\n\n\n"

        if [[ $DiskChkExitCode == "3" ]]; then printColor "  [lk] basic leaks repair  [LK] slow method"
        
        elif [[ $DiskChkExitCode == "2" ]]; then printColor "  [full] attempt complex repair  [FULL] slow method"

        else printColor "  [rd] reduce only - clear ALL snapshots, remove deleted files & tidy 'empty' space   [rz] resize the envelope"

        fi

      fi
      
      printf "\n\n  The virtual machine MUST NOT BE RUNNING & not started elsewhere." 

      # use pgrep with -x exact match to avoid 'kvm-cleanup' processes being detected

      [[ $(pgrep -x kvm) ]] &&  printColor "\n\n  VIRTUAL MACHINE ACTIVITY has been DETECTED \n"

      printf "\n\n  A back-up will be made. All processes will remove any snapshots ...  [b] back to menu " 

    fi

    DiskManage=
    printf "\n\n"
    read -rp "  >  "  DiskManage
    printf "\n\n"	

    # https://qemu.readthedocs.io/en/v7.2.2/tools/qemu-img.html#cmdoption-qemu-img-commands-arg-convert

    # https://qemu.readthedocs.io/en/v7.2.2/tools/qemu-img.html#cmdoption-qemu-img-commands-arg-check
    
    # REVIEW   qemu's differential 'bitmap' tracking  https://lwn.net/Articles/837053/

    if [[ $DiskManage != "b" ]]; then 

      if [[ $DiskManage == "rz" ]]; then

        printf "\n\n  This will resize the external envelope. For internal partitions, use gparted" 
        printColor "\n\n  Give extra GigaBytes TO ADD, as integer, eg 16 \n\n"
        read -rp "  >  "  ResizePlus
        printf "\n\n"
        ResizePlus="$(tr -cd "[:digit:]" <<< "$ResizePlus")"

      fi

      DiskDate="$(date +%b%d.%H.%M)"

      DiskBackup="${Selected_Drive}.$DiskDate.OLD.qcow2"
    
      mv "$Selected_Drive" "$DiskBackup" 

      printf  "  Original drive: %s \n\n" "$Selected_Drive"
      printColor "  Moved to back up as: %s \n\n" "$DiskBackup"

      if [[ $DiskManage == "LK" || $DiskManage == "FULL" ]]; then 

        printf "" > "$Qemu_IMG_ErrorLog"

        DU_BackUpSize="$(du "$DiskBackup" | cut -f 1)"

        function dd_progress_report {

          sleep 5

          while true ; do
          
            DU_SelectedSize="$(du "$2" | cut -f 1  2>/dev/null)"

            DU_BackUpSize="$1"

            [[ ! $DU_SelectedSize ]] && DU_SelectedSize=1

            DD_ProgressPercent="$(bc <<< "scale=5; ($DU_SelectedSize/$DU_BackUpSize)*100")"

            DD_ProgressPercent="$(bc <<< "scale=0; ($DD_ProgressPercent + 0.5)/1")"

            [[ $DD_ProgressPercent -ge 98 ]] && break

            printf "\r  New Drive Progress = %s%%   "  "$DD_ProgressPercent"

            sleep 3

          done

        }

        dd_progress_report  "$DU_BackUpSize"  "$Selected_Drive"  &

        # https://web.archive.org/web/20230412163832/https://wiki.bash-hackers.org/howto/redirection_tutorial#order_of_redirection_ie_file_2_1_vs_2_1_file

        "$QEMU_IMG" dd -f qcow2 -O qcow2  if="$DiskBackup" of="$Selected_Drive" 2> "$Qemu_IMG_ErrorLog" 

        printColor "\n\n  Attemping repair .... \n\n"

      else

        "$QEMU_IMG" convert -O qcow2 "$DiskBackup" "$Selected_Drive" 

      fi

      if [[ -e  "$DiskBackup" ]] && [[ -e "$Selected_Drive" ]]; then

        [[ $DiskManage == "lk" || $DiskManage == "LK" ]] && "$QEMU_IMG" check -f qcow2 -r leaks "$Selected_Drive" 2> "$Qemu_IMG_ErrorLog" 

        [[ $DiskManage == "full" || $DiskManage == "FULL" ]] && "$QEMU_IMG" check -f qcow2 -r all "$Selected_Drive" 2> "$Qemu_IMG_ErrorLog" 

        [[ $DiskManage == "rz" ]] && "$QEMU_IMG" resize -f qcow2 "$Selected_Drive" "+${ResizePlus}G" 2> "$Qemu_IMG_ErrorLog" 

        # option 'rd' is basically 'convert' process only  (all options do this as standard)

        printColor "\n\n  Drive Updated ..."

      else

        printColor  "\n\n  ERROR: a NEW drive could not be created from the backup ??"

      fi

      printf "\n\n  [enter] to return to menu \n\n "
      read -rp "  >  "  

    fi

  elif  [[ $MainMenuChoice == "sl" ]] ; then

    drives_and_snapshots

    [[ $SharedDriveArgs && $SecondDriveArgs ]] && printColor "\n\n  Scroll to View "
    printf "\n\n  [enter] to return to menu \n\n "
    read -rp "  >  "  
  

  elif  [[ $MainMenuChoice == "sc" ]] ; then

    drives_and_snapshots  select

    printColor "\n\n  [enter] date.time title  "
    printf "  [text input] descriptor   [b] back to menu " 
    SnapTitle=
    printf "\n\n"
    read -rp "  >  "  SnapTitle
    printf "\n\n"	

    [[ ! $SnapTitle ]] && SnapTitle="$(date +%b%d.%H.%M)"
    
    if [[ $SnapTitle != "b" ]]; then 
    
      # remove spaces, make title dot separated, as easier to IFS
      SnapTitle="${SnapTitle// /.}"

      "$QEMU_IMG" snapshot  -c "$SnapTitle" "$Selected_Drive" 

      "$QEMU_IMG" info "$Selected_Drive"

      printColor "\n\n  Drive Updated ..."

      printf "\n\n  [enter] to return to menu \n\n "
      read -rp "  >  "  

    fi


  elif  [[ $MainMenuChoice == "sd" ]] ; then

    drives_and_snapshots  select 

    printColor "\n\n %s Snapshot Delete function "  "$ModName"

    printf "\n\n %s  (reselection & cancel prompts are given before starting)" "$Selected_DriveName"

    # Create range-selectable array 
    #SnapListString="$(drives_and_snapshots | grep '[0-9][0-9]:')"
    
    SnapListString="$("$QEMU_IMG" info "$Selected_Drive" | grep '[0-9][0-9]:')"

    # turns the multiline string into raw array with one whole line for each entry making each array element

    # then line by line extract the snap number and its title:

    mapfile -t SnapListArrRaw <<< "$SnapListString"

    i=0
    SnapListArrSeparated=()

    printColor "\n\n ID   Array  Name \n\n"
    while [[ "${SnapListArrRaw[i]}" ]]; do
      IFS=' ' read -ra SnapListArrSeparated <<< "${SnapListArrRaw[i]}" 
      # prints the first two elements of each snapshot info line 
      printf "%2d    %2d    %s \n"  "${SnapListArrSeparated[0]}"  "$i"  "${SnapListArrSeparated[1]}"
      ((i+=1))
    done

    SnapListArrTotal=$((i-1))

    printColor "\n\n Enter ARRAY number [0] to [%s], either for individual snapshot, or for the start of snapshot range"  "$SnapListArrTotal"
    printf "\n\n or [full individual name] for difficult names only showing correctly in the FULL listing"
    printf "\n\n or [enter] to return to main menu "

    SnapName=
    SnapDeleteStart=
    SnapDeleteEnd=
    SnapDeleteConfirm=
    
    printf "\n\n"
    read -rp " >  "  SnapDeleteStart
    

    if [[ $SnapDeleteStart ]]; then

        SpecialSnapName="$SnapDeleteStart"

        SpecialSnapName="$(tr -d "[:digit:]" <<< "$SpecialSnapName")"

      if  [[ $SpecialSnapName ]]; then

        # restore name as valid name if, when all digits removed, there is still something
        SpecialSnapName="$SnapDeleteStart"

        printf "\n\n  Snap to delete: \"%s\"" "$SpecialSnapName"

      else

        printColor "\n\n [enter] for an individual snapshot  or give ARRAY [number] for end of range (inclusive) \n\n"
        read -rp " >  "  SnapDeleteEnd

        if [[ $SnapDeleteEnd ]]; then 
          printf "\n Array Range = %s to %s \n\n" "$SnapDeleteStart"  "$SnapDeleteEnd"

          i="$SnapDeleteStart"
          SnapListArrSeparated=()

          printColor "\n\n ID   Array  Name \n\n"
          while true; do
            IFS=' ' read -ra SnapListArrSeparated <<< "${SnapListArrRaw[i]}" 
            # prints the first two elements of each snapshot info line 
            printf "%2d    %2d    %s \n"  "${SnapListArrSeparated[0]}"  "$i"  "${SnapListArrSeparated[1]}"
            [[ $i == "$SnapDeleteEnd" ]] && break
            ((i+=1))
          done

        else         
          printf "\n Delete = ARRAY entry %s " "$SnapDeleteStart" 
          printf "\n\n  %s" "${SnapListArrRaw[SnapDeleteStart]}" 
          SnapDeleteEnd="$SnapDeleteStart"
        fi

      fi

      printColor "\n\n [enter] to delete  [b] back to main menu (or reselect) \n\n"

      read -rp " >  "  SnapDeleteConfirm


      if [[ $SnapDeleteConfirm == "b" ]]; then

        printf "\n\n Deletion schedule has been CANCELLED"

      else

        if [[ $SpecialSnapName ]]; then

          "$QEMU_IMG" snapshot -q -d "$SpecialSnapName" "$Selected_Drive" 

        else
      
          SnapDeleteRangeCounter=$SnapDeleteStart

          while [[ $SnapDeleteRangeCounter -le $SnapDeleteEnd ]]; do
            IFS=' ' read -ra SnapListArrSeparated <<< "${SnapListArrRaw[$SnapDeleteRangeCounter]}"
            SnapName="${SnapListArrSeparated[1]}"

            if [[ ! $SnapName ]]; then
              printColor "\n\n ERROR with SnapShot Array List \n\n"
              sleep 5
              exit 1
            else
              printColor "\n\n Deleting SnapShot %2d   %2d  %s \n\n"  "${SnapListArrSeparated[0]}"  "$SnapDeleteRangeCounter"  "${SnapListArrSeparated[1]}"
              
              "$QEMU_IMG" snapshot -q -d "$SnapName" "$Selected_Drive" 

            fi
            ((SnapDeleteRangeCounter+=1))
          done

        fi

        "$QEMU_IMG" info "$Selected_Drive"


      fi

      printf "\n\n [enter] to return to menu \n\n "
      read -rp " >  "  

    fi

    #  TODO  see if tiano core bios can be set to select 'misc device' instead of 'windows boot manager' 
    #        as this can cause problems when rebooting on updates
    
    #  https://github.com/tianocore/tianocore.github.io/wiki/UEFI-application

    #  https://github.com/theopolis/uefi-firmware-parser
          
  elif  [[ $MainMenuChoice == "sa" ]] ; then

    drives_and_snapshots select

    printColor "\n\n  Give number of snapshot to use  [enter] to return to menu "
    SnapNumber=
    
    printf "\n\n"
    read -rp "  >  "  SnapNumber
    printf "\n\n"	

    if [[ $SnapNumber ]]; then
    
      "$QEMU_IMG" snapshot -q -a "$SnapNumber" "$Selected_Drive" 

      printf "\n\n  May take a moment .... \n\n"
      printColor "\n\n  Snapshot %s has been applied. \n\n"  "$SnapNumber "

    fi
  
  elif [[ $MainMenuChoice == "2" || $MainMenuChoice == "3" ]] ; then

    SecondDriveCreateSize=
    DriveCreated=

    if [[ $MainMenuChoice == "3" ]] && [[ $SharedDriveArgs ]] ; then printColor "\n\n  ERROR:  a shared drive already exists. Make backups ...\n\n"

    elif [[ $MainMenuChoice == "2" ]] && [[ $SecondDriveArgs ]] ; then printColor "\n\n  ERROR:  a second drive already exists. Make backups ...\n\n"

    else

      [[ $MainMenuChoice == "2" ]] && printColor "\n\n  Create a SECOND virtual hard-drive for this VM instance:"
      
      [[ $MainMenuChoice == "3" ]] && printColor "\n\n  Create a SHARED virtual hard-drive for this VM instance:"
      
      printf "\n\n\n  Suggest 32G or higher, especially if expecting to create snapshots. Note that" 

      printf "\n\n  qcow2 disks auto-compress & will normally occupy much less of your hard drive "
      
      printf "\n\n  than that allocated.  An empty disk only takes up 70Mb when formatted."
      
      printf "\n\n  It is easier make them larger now than to resize later ... " 

      printColor "\n\n\n  [enter] create 64G   [integer] create int G   [b] go back \n\n"

      read -rp "  >  "  SecondDriveCreateSize

      [[ ! $SecondDriveCreateSize ]]  && SecondDriveCreateSize="64"

      [[ $SecondDriveCreateSize ]]  &&  SecondDriveCreateSize="$(tr -cd "[:digit:]" <<< "$SecondDriveCreateSize")"

      if [[ $SecondDriveCreateSize ]]; then 

        if [[ $MainMenuChoice == "2" ]] && grep -s -q SecondDrive "$VM_Conf_File" ; then

          printColor "\n\n  ERROR: A SecondDrive entry is already present in your .conf file ??"

          printf "\n\n  Check general settings & notes on drives set up.  Edit the configuration. \n\n"

        elif [[ $MainMenuChoice == "3" ]] && [[ ! -d "$Default_VM_Folder" ]]; then

            printf "\n\n  Default_VM_Folder not present. Edit the main settings file first. \n\n"  

        else
           
          if [[ $MainMenuChoice == "2" ]]; then

            printf "\n\n  A Storage Drive will be created in the VM's folder \n\n  %s"  "$VM_InstanceName"

          elif [[ $MainMenuChoice == "3" ]]; then 

            printf "\n\n  A folder 'SharedDrive will be created in the Default_VM_Folder \n\n  %s"  "$Default_VM_Folder"

            printf "\n\n  It may later be moved. Editing settings file: \n\n  %s" "$Q_Mod_SettingsFile"

          fi

          printColor "\n\n  [enter] to continue  [b] to go back \n\n"

          NewDriveCreate=

          read -rp "  > "  NewDriveCreate

          if [[ $NewDriveCreate != "b" ]]; then

            if [[ $MainMenuChoice == "2" ]]; then 

              printf "SecondDrive=\"%s/%s/storage.qcow2\"" "$VM_Conf_Dir" "$VM_InstanceName"  >> "$VM_Conf_File"

              "$QEMU_IMG"  create -q -f qcow2 "$VM_Conf_Dir/$VM_InstanceName/storage.qcow2" "${SecondDriveCreateSize}G"

              [[ -e "$VM_Conf_Dir/$VM_InstanceName/storage.qcow2" ]] && DriveCreated=2
          
            elif [[ $MainMenuChoice == "3" ]]; then

              "$QEMU_IMG"  create -q -f qcow2 "$Default_VM_Folder/SharedDrive/shared.qcow2" "${SecondDriveCreateSize}G"

              [[ -e "$Default_VM_Folder/SharedDrive//shared.qcow2" ]] && DriveCreated=3

              # 'sed' can get very HIEROGLYPHICAL  https://stackoverflow.com/a/9453461

              # 'awk' ?  https://stackoverflow.com/a/32995760

              #  awk :  https://stackoverflow.com/a/6541653   https://stackoverflow.com/tags/awk/info 

              #  sed awk easy methods: https://www.ubuntumint.com/insert-line-specific-line-number-linux/

              #  date ref awk:  https://unix.stackexchange.com/a/599097

              #  https://www.gnu.org/software/sed/manual/sed.html#Command_002dLine-Options   

              #  Make sure sed command works in --posix  if testing on gnu sed ...

              #  Also note here:

              #  https://stackoverflow.com/questions/6537490/insert-a-line-at-specific-line-number-with-sed-or-awk#6537587

              #  sed -i only works in GNU sed  ... 
              

              if [[ "$(sed --version | head -1)" == *'GNU'* ]] && [[ $(grep -s 'SharedDrive=' "$Q_Mod_SettingsFile" | grep -v '#' | grep -c 'SharedDrive') == 1 ]]; then

                # do a grep -c (count) & make sure that other than in comments (#) there is only one entry that is possible
                
                SharedDriveSettingsLine="$(grep -s -n 'SharedDrive=' "$Q_Mod_SettingsFile" | grep -v '#' | cut -d ':' -f 1)"

                printf "\n\n  Backing up settings file to /tmp and changing line %s \n\n"  "$SharedDriveSettingsLine"

                eval cp "$Q_Mod_SettingsFile"  "/tmp/qqX_settings.$(date +%s).old"     # REVIEW add awk version?

                eval sed -i \'"$SharedDriveSettingsLine" c\ SharedDrive=\""$Default_VM_Folder"/SharedDrive/shared.qcow2\"\'  "$Q_Mod_SettingsFile"

              else 

                printColor "\n\n  A drive has been created but the settings file will need to be edited manually \n\n"

                grep 'SharedDrive=' "$Q_Mod_SettingsFile"

                printf "\n\n"

              fi

            fi

            if [[ $DriveCreated ]]; then

              set_drive_paths

              printColor "\n  The Created Drive should now be formatted from inside a VM:"

              printf "\n\n  Use Gparted or similar to set a device table and add partition(s)"

              printf "\n\n  Read notes in main settings if older MacOS or if Windows present."

            else

              printColor "\n\n  Something went wrong. Drive not created "

            fi

          fi

        fi

      fi
        
    fi

    printf "\n\n"

    read -rp "  [enter] to return > "

    
  elif [[ $MainMenuChoice == "v" ]] ; then

    if [[ $VerboseArgs ]]; then VerboseArgs=

    else VerboseArgs=1  ; fi

  
  elif [[ $MainMenuChoice == "vm" ]] ; then

    if [[ ! ${VM_Array[0,0]} ]]; then

      function_refresh_and_reload_VM_arrays

    fi

    Select_VM=1
    ByPass_VM_Array_Selector=
    ByPass_VM_Array_Loader=

  elif [[ $MainMenuChoice == "xa" ]] ; then

    MenuXA=
    MenuXA_Correct=

    printColor "\n\n  Give extra qemu arguments to use "
    printf     "\n\n  Format = grouped & with space separated elements "
    printf     "\n\n  eg.  -device  tpm-tis,tpmdev=tpm0 "

    [[ $ExtraSetArgs ]] && printf  "\n\n  Extra Set Args:  %s"  "$ExtraSetArgs"

    if [[ $ExtraArgs ]] && [[ "$ExtraArgs" != "$ExtraSetArgs" ]]; then
    
      printf  "\n\n  Extra Args:  %s"  "$ExtraArgs"

      if [[ $ExtraSetArgs ]]; then printf "\n\n  [r] reset back to Set Args only "

      else printf "\n\n  [r] remove the Extra Args " ; fi

    fi

    printf "\n\n"

    read -rp "  > "  MenuXA

    if [[ $MenuXA != "r" ]]; then

      printf "\n\n  %s %s"  "$ExtraArgs" " $MenuXA"

      printf "\n\n  Is this correct y/n ? \n\n"

      read -rp "  >  "  MenuXA_Correct

    fi

    

    if [[ $MenuXA_Correct == "n" ]]; then printColor "\n\n  Ignoring the entered Qemu Args \n\n" ; sleep 1.5 

    elif [[ $MenuXA == "r" ]]; then printColor "\n\n  Resetting \n\n" ; ExtraArgs="$ExtraSetArgs" ; sleep 1 
    
    else  ExtraArgs="$ExtraArgs $MenuXA" ; fi

    printf "\033c\n\n"


  fi

done


# vim:tabstop=2:shiftwidth=2:expandtab

## 
