#!/bin/sh
# shellcheck disable=SC2164,SC2181,SC2009,SC2034,SC2154
# . ./medley_header.sh
# shellcheck shell=sh
###############################################################################
#
#    medley.sh - script for running Medley Interlisp on
#                Linux/WSL/Cygwin/MacOS. On all platforms it just sets
#                up directories and environment variables and then calls
#                maiko with the right arguments.  On WSL, there is an option to
#                run without or around X Windows by using the XVnc and a VNC viewer
#                on the Windows side.  This script will start this VNC viewer
#                on the Windows side.
#
#   NOTE:  This script is "compiled" using compile.sh (and inline.sh) from the
#          component scripts (medley_*.sh).  The top-level component is
#          medley_main.sh.  The other components are sourced (directly or
#          indirectly) from medley_main.sh.
#
#          Do not edit this script directly.  Edit the component scripts and
#          then recompile to get this "combined" script.  You can also run
#          the scripts by exec-ing the medley_main.sh script.  This will allow
#          testing of component modifications without having to compile.
#
#   2023-01-12 Frank Halasz
#   2024-04-29 Frank Halasz: Major overhaul
#
#   Copyright 2023-2024 Interlisp.org
#
###############################################################################
###############################################################################
#
#    medley_main.sh - "main" script for running Medley Interlisp on
#                Linux/WSL/Cygwin/MacOS. On all platforms it just sets
#                up directories and environment variables and then calls
#                maiko with the right arguments.  On WSL, there is an option to
#                run without or around X Windows by using the XVnc and a VNC viewer
#                on the Windows side.  This script will start this VNC viewer
#                on the Windows side.
#
#   2023-01-12 Frank Halasz
#   2024-04-29 Frank Halasz: Major overhaul
#
#   Copyright 2023-2024 Interlisp.org
#
###############################################################################

#set -x

#
#
# Start off with some functions to determine what directory this script is being executed from
#
#
get_abs_filename() {
  # $1 : relative filename
  echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}

# This function taken from
# https://stackoverflow.com/questions/29832037/how-to-get-script-directory-in-posix-sh
rreadlink() (

  # Execute this function in a *subshell* to localize variables and the effect of `cd`.

  target=$1
  fname=
  targetDir=
  CDPATH=

  # Try to make the execution environment as predictable as possible:
  # All commands below are invoked via `command`, so we must make sure that `command`
  # itself is not redefined as an alias or shell function.
  # (Note that command is too inconsistent across shells, so we don't use it.)
  # `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
  # an external utility version of it (e.g, Ubuntu).
  # `command` bypasses aliases and shell functions and also finds builtins 
  # in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
  # to happen.
  { \unalias command; \unset -f command; } >/dev/null 2>&1
  [ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.

  while :; do # Resolve potential symlinks until the ultimate target is found.
      [ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
      command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
      fname=$(command basename -- "$target") # Extract filename.
      [ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
      if [ -L "$fname" ]; then
        # Extract [next] target path, which may be defined
        # *relative* to the symlink's own directory.
        # Note: We parse `ls -l` output to find the symlink target
        #       which is the only POSIX-compliant, albeit somewhat fragile, way.
        target=$(command ls -l "$fname")
        target=${target#* -> }
        continue # Resolve [next] symlink target.
      fi
      break # Ultimate target reached.
  done
  targetDir=$(command pwd -P) # Get canonical dir. path
  # Output the ultimate target's canonical path.
  # Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
  if [ "$fname" = '.' ]; then
    command printf '%s\n' "${targetDir%/}"
  elif  [ "$fname" = '..' ]; then
    # Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
    # AFTER canonicalization.
    command printf '%s\n' "$(command dirname -- "${targetDir}")"
  else
    command printf '%s\n' "${targetDir%/}/$fname"
  fi
)

get_script_dir() {

    # call this with $0 (from main script) as its (only) parameter
    # if you need to preserve cwd, run this is a subshell since
    # it can change cwd

    # set -x

    local_SCRIPT_PATH="$( get_abs_filename "$1" )";

    while [ -h "$local_SCRIPT_PATH" ];
    do
        cd "$( dirname -- "$local_SCRIPT_PATH"; )";
        local_SCRIPT_PATH="$( rreadlink "$local_SCRIPT_PATH" )";
    done

    cd "$( dirname -- "$local_SCRIPT_PATH"; )" > '/dev/null';
    local_SCRIPT_PATH="$( pwd; )";

    # set +x

    echo "${local_SCRIPT_PATH}"
}

# end of script directory functions
###############################################################################



# figure out the script dir
SCRIPTDIR="$(get_script_dir "$0")"

#  Define some generally useful functions
# shellcheck source=./medley_utils.sh
# . "${SCRIPTDIR}/medley_utils.sh"
# shellcheck shell=sh
###############################################################################
#
#    medley_utils.sh - script containing various useful functions for medley.sh script.
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2023-01-23 Frank Halasz
#
#   Copyright 2023 Interlisp.org
#
###############################################################################

is_tput="$(command -v tput)"

export EOL="
"

output_error_msg() {
  local_oem_file="${TMPDIR:-/tmp}"/oem_$$
  echo "$1" >"${local_oem_file}"
  while read -r line
  do
    if [ -n "${is_tput}" ];
    then
      echo "$(${is_tput} setab 1)$(${is_tput} setaf 7)${line}$(${is_tput} sgr0)"
    else
      echo "${line}"
    fi
  done <"${local_oem_file}"
  rm -f "${local_oem_file}"
}

output_warn_msg() {
  local_oem_file="${TMPDIR:-/tmp}"/oem_$$
  echo "$1" >"${local_oem_file}"
  while read -r line
  do
      echo "$(${is_tput} setab 3)$(${is_tput} setaf 4)${line}$(${is_tput} sgr0)"
  done <"${local_oem_file}"
  rm -f "${local_oem_file}"
}

check_for_dash_or_end() {
  local_err_msg="";
  if [ -z "$2" ] || [ "$2" = "--" ]
  then
    local_err_msg="Error: the flag \"$1\" requires a value.
Value is missing."
    usage "${local_err_msg}"
  else
    case "$2" in
       -*)
           local_err_msg="Error: either the value for flag \"${1}\" is missing OR
the value begins with a \"-\", which is not allowed."
           usage "${local_err_msg}"
           ;;
    esac
  fi
}

check_file_writeable_or_creatable() {
  local_msg_core="\"$2\" given as the value of the \"$1\" flag"
  local_err_msg=""
  if [ -e "$2" ]
  then
    if [ ! -f "$2" ]
    then
      local_err_msg="Error: File ${local_msg_core} is not a regular file.
It is either a directory or a device file of some sort.
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    elif [ ! -w "$2" ]
    then
      local_err_msg="Error: File ${local_msg_core} exists but is not writeable
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    fi
  else
    if [ ! -w "$(dirname -- "$2")" ]
    then
      local_err_msg="Error: File ${local_msg_core} cannot be created because
its directory either doen't exist or is not writeable.
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    fi
  fi
}

check_dir_writeable_or_creatable() {
  local_msg_core="\"$2\" given as the value of the \"$1\" flag"
  local_err_msg=""
  if [ -e "$2" ]
  then
    if [ ! -d "$2" ]
    then
      local_err_msg="Error: ${local_msg_core} exists but is not a directory.
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    elif [ ! -w "$2" ]
    then
      local_err_msg="Error: Directory ${local_msg_core} exists but is not writeable
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    fi
  else
    if [ ! -w "$(dirname -- "$2")" ]
    then
      local_err_msg="Error: Directory ${local_msg_core} cannot be created because
its directory either doesn't exist or is not writeable.
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    fi
  fi
}


check_file_readable() {
  local_msg_core="\"$2\" given as the value of the \"$1\" flag"
  if [ ! -r "$2" ]
  then
    local_err_msg="Error: File ${local_msg_core}
either doesn't exist or is not readable.
Exiting"
    output_error_msg "${local_err_msg}"
    exit 1
  fi
}

check_dir_exists() {
  local_msg_core="\"$2\" given as the value of the \"$1\" flag"
  if [ -e "$2" ]
  then
    if [ ! -d "$2" ]
    then
      local_err_msg="Error: Pathname ${local_msg_core} exists but is not a directory.
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    elif [ ! -r "$2" ]
    then
      local_err_msg="Error: Directory ${local_msg_core} exists but is not readable.
Exiting"
      output_error_msg "${local_err_msg}"
      exit 1
    fi
  fi
}

parse_nethub_data() {
  nh_host=""
  nh_port=""
  nh_mac=""
  nh_debug=""
  #
  x="${1%:}:"
  nh_host="${x%%:*}"
  x="${x#"${nh_host}":*}"
  nh_port="${x%%:*}"
  if [ "${nh_port}" = "${x}" ]; then nh_port=""; return 0; fi
  x="${x#"${nh_port}":*}"
  nh_mac="${x%%:*}"
  if [ "${nh_mac}" = "${x}" ]; then nh_mac=""; return 0; fi
  nh_debug="${x#"${nh_mac}":*}"
  if [ "${nh_debug}" = "${x}" ]; then nh_debug=""; return 0; fi
  nh_debug="${nh_debug%:}"
  return 0
}


git_commit_info () {
  if [ -f "$(command -v git)" ] && [ -x "$(command -v git)" ]
  then
    if git -C "$1" rev-parse >/dev/null 2>/dev/null
    then
      # This does NOT indicate if there are any modified files!
      COMMIT_ID="$(git -C "$1" rev-parse --short HEAD)"
      BRANCH="$(git -C "$1" rev-parse --abbrev-ref HEAD)"
    fi
  fi
}


MEDLEYDIR="$(cd "${SCRIPTDIR}/../.."; pwd)"
export MEDLEYDIR
IL_DIR="$(cd "${MEDLEYDIR}/.."; pwd)"

# Are we running under WSL or Darwin or Cygwin?
#
wsl=false
darwin=false
cygwin=false
linux=false
platform=unknown

if [ "$(uname)" = "Darwin" ]
then
  darwin=true
  platform=darwin
elif [ "$(uname -s | head --bytes 6)" = "CYGWIN" ]
then
  cygwin=true
  platform=cgwin
elif [ -e "/proc/version" ] && grep --ignore-case --quiet Microsoft /proc/version
then
  platform=wsl
  wsl=true
  wsl_ver=0
  # WSL2
  grep --ignore-case --quiet wsl /proc/sys/kernel/osrelease
  if [ $? -eq 0 ];
  then
    wsl_ver=2
  else
    # WSL1
    grep --ignore-case --quiet microsoft /proc/sys/kernel/osrelease
    if [ $? -eq 0 ]
    then
      if [ "$(uname -m)" = "x86_64" ]
      then
        wsl_ver=1
      else
        err_msg="ERROR: Running Medley on WSL1 requires an x86_64-based PC.
This is not an x86_64-based PC.
Exiting"
        output_error_msg "${err_msg}"
        exit 23
      fi
    fi
  fi
else
  linux=true
  platform=linux
fi
#################### TEST CODE ####################

#wsl=false
#darwin=false
#cygwin=false
#linux=true
#platform=linux

#################### TEST CODE ####################

# process config file and args
# shellcheck source=./medley_configfile.sh
# . "${SCRIPTDIR}/medley_configfile.sh"
# shellcheck shell=sh
###############################################################################
#
#    medley_configfile.sh - script for processing the config file for the
#                           medley.sh script.
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2024-04-20 Frank Halasz
#
#   Copyright 2024 Interlisp.org
#
###############################################################################

config_file=""

# look thru args looking to see if a config file was specified
j=1
jmax=$#
while [ "$j" -le "$jmax" ]
do
  if [ "$(eval "printf %s \${${j}}")" = "-c" ] || [ "$(eval "printf %s \${${j}}")" = "--config" ]
  then
    k=$(( j + 1 ))
    config_file="$(eval "printf %s \${${k}}")"
    if [ ! "${config_file}" = "-" ] && [ ! -f "${config_file}" ]
    then
      echo "Error: specified config file \"${config_file}\" not found."
      echo "Exiting."
      exit 52
    fi
    j=$(( j + 1 ))
  fi
  j=$(( j + 1 ))
done

# if no config file specified, use the defaults (if they exist)
if [ -z "${config_file}" ]
then
  for f in "${HOME}/.medley_config" "${MEDLEYDIR}/.medley_config"
  do
    if [ -f "$f" ]
    then
      config_file="$f"
    fi
  done
fi

# add marker to separate config file args from command line args
set -- "--start_cl_args" "--start_cl_args" "$@"

# if there is a config file and its not been suppressed with "-",
# read the config file (in reverse order) and add the first two items on each line
# to the arguments array
if [ -n "${config_file}" ] && [ ! "${config_file}" = "-" ]
then
  rev_config_file="${TMPDIR:-/tmp}"/.medley_config_$$
  # reverse order of lines in medley config file
  sed '1!x;H;1h;$!d;g' < "${config_file}" >"${rev_config_file}"
  while read -r arg1 arg2
  do
    if [ -n "${arg2}" ]
    then
      arg2="$(echo "${arg2}" | sed s/\"//g)"
      set -- "${arg2}" "$@"
    fi
    if [ -n "${arg1}" ]
    then
      set -- "${arg1}" "$@"
    fi
  done < "${rev_config_file}"
  rm -f "${rev_config_file}"
fi



# shellcheck source=./medley_args.sh
# . "${SCRIPTDIR}/medley_args.sh"
# shellcheck shell=sh
# shellcheck disable=SC2034,SC2154,SC2164
###############################################################################
#
#    medley_args.sh - script for processing the args to medley.sh script.
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2023-01-12 Frank Halasz
#
#   Copyright 2023 Interlisp.org
#
###############################################################################

# load usage function
# shellcheck source=./medley_usage.sh
# . "${SCRIPTDIR}/medley_usage.sh"
# shellcheck shell=sh
# shellcheck disable=SC2154
###############################################################################
#
#    medley_useage.sh - script defining the "usage" for medley.sh script.
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2023-01-21 Frank Halasz
#
#   Copyright 2023 Interlisp.org
#
###############################################################################


PAGER=$( if [ -n "$(which more)" ]; then echo "more"; else echo "cat"; fi)

usage() {
   usage_msg_path=/tmp/msg-$$

   if [ "${wsl}" = true ];
   then
     wsl_incl="+w"
     wsl_excl="-w"
   else
     wsl_incl="-w"
     wsl_excl="+w"
   fi

   if [ "${docker}" = true ];
   then
     docker_incl="+d"
     docker_excl="-d"
   else
     docker_incl="-d"
     docker_excl="+d"
   fi

   if [ "${windows}" = true ];
   then
     windows_incl="+W"
     windows_excl="-W"
   else
     windows_incl="-W"
     windows_excl="+W"
   fi

   if [ $# -ne 0 ];
   then
     full_msg="In ${args_stage}:
$1"
     { echo; output_error_msg "${full_msg}"; echo; } >> "${usage_msg_path}"
   else
     touch "${usage_msg_path}"
   fi

   cat "${usage_msg_path}" - <<EOF \
       | sed -e "/^${docker_excl}/d" -e "s/^${docker_incl}/  /" \
       | sed -e "/^${wsl_excl}/d" -e "s/^${wsl_incl}/  /" \
       | sed -e "/^${windows_excl}/d" -e "s/^${windows_incl}/  /" \
       | ${PAGER}
Usage: medley [flags] [sysout] [--] [pass_args ...]

Note: MEDLEYDIR is the directory at the top of the code tree where this script is executed from
      after all symbolic links have been resolved.  For standard installations this will be
      /usr/local/interlisp/medley.  For "local" installations this will be the "medley" sub-directory
      under the directory into which the Medley distribution was installed.

flags:
    -h | --help                : print this usage information

    -z | --man                 : show the man page for medley

    -c FILE | --config FILE    : use FILE as the config file (default: ~/.medley_config)

    -f | --full                : start Medley from the "full" sysout

    -l | --lisp                : start Medley from the "lisp" sysout

    -a | --apps                : start Medley from the "apps" sysout

    -u | --continue            : start Medley from the vmem file from previous Medley run

    -y FILE | --sysout FILE    : start Medley using FILE as sysout

    -e | --interlisp           : (for apps.sysout only) Start in the Interlisp exec

    -n | --noscroll            : do not use scroll bars in Medley window

    -g WxH | --geometry WxH    : set the window geometry to Width x Height.

    -s WxH | --screensize WxH  : set the Medley screen size to be Width x Height

    -ps N | --pixelscale N     : use N as the pixel scale factor - for SDL display only

    -t STRING | --title STRING : use STRING as title of window

    -d :N | --display :N       : use X display :N

    -v | --vnc                 : Use a VNC window instead of an X window (Not available: MacOS & Windows/Cygwin)

    -i STRING | --id STRING    : use STRING as the id for this run of Medley (default: default)

    -m N | --mem N             : set Medley memory size to N

    -k FILE | --vmem FILE      : use FILE as the Medley virtual memory store.

    -r FILE | --greet FILE     : use FILE as the Medley greetfile.

    -r - | --greet -           : do not use a greetfile

    -x DIR | --logindir DIR    : use DIR as LOGINDIR in Medley

    -x - | --logindir -        : use MEDLEYDIR/logindir as LOGINDIR in Medley

    -am | --automation         : this call to medley is being used in automation, adjust timings.  Relevant in -vnc case only.

    -cm FILE | --rem.cm FILE   : use FILE as the REM.CM when starting up Medley.  FILE must be absolute pathname.

    -cm - | --rem.cm -         : do not use an REM.CM.  Negate any prior setting, e.g., from config file.

    -cc FILE | --repeat FILE   : as long as FILE exists and is greater than 0 length, repeat Medley run
                                 using FILE as REM.CM

sysout:
    The pathname of the file to use as a sysout for Medley to start from.
    If sysout is not provided and none of the flags [-a, -f & -l] is used, then Medley will start from
    the saved virtual memory file for the previous run with the sane id as this run.

pass_args:
    All arguments after the "--" flag, are passed unaltered to the Maiko emulator.

EOF

exit 1

}

args_stage="config file"

# Defaults
geometry=""
greet_specified=false
pass_args=false
run_id="default"
screensize=""
sysout_arg=""
sysout_stage=""
title=""
use_vnc=false
windows=false
maikodir_arg="${MAIKODIR}"
maikodir_stage="MAIKODIR env variable"
maikoprog_arg=""
greet_arg=""
noscroll=false
display_arg=""
vmem_arg=""
mem_arg=""
maiko_args=""
logindir_arg=""
nh_host_arg=""
nh_port_arg=""
nh_mac_arg=""
nh_debug_arg=""
pixelscale_arg=""
borderwidth_arg=""
remcm_arg="${LDEREMCM}"
repeat_cm=""
automation=false
use_tag=""

# Add marker at end of args so we can accumulate pass-on args in args array
set -- "$@" "--start_of_pass_args"

# Loop thru args and process
while [ "$#" -ne 0 ];
do
  if [ "${pass_args}" = false ];
  then
    case "$1" in
      -a | --apps)
        sysout_arg="apps"
        sysout_stage="${args_stage}"
        ;;
      -tg | -tag | --tag)
        if [ "$2" = "-" ]
        then
          use_tag="-"
        else
          check_for_dash_or_end "$1" "$2"
          use_tag="$2"
        fi
        shift
        ;;
      -c | --config)
        # already handled so just skip both flag and value
        shift;
        ;;
      -cm | --rem.cm | --remcm)
        if [ "$2" = "-" ] || [ "$2" = "--" ]
        then
          remcm_arg=""
        else
          check_for_dash_or_end "$1" "$2"
          check_file_readable "$1" "$2"
          remcm_arg="$2"
        fi
        shift
        ;;
      -cc | --repeat.cm | --repeat)
        if [ "$2" = "-" ] || [ "$2" = "--" ]
        then
          repeat_cm=""
        else
          check_for_dash_or_end "$1" "$2"
          # check_file_readable "$1" "$2"
          repeat_cm="$2"
        fi
        shift
        ;;
      -d | --display)
        if [ "$2" = "-" ]
        then
          display=""
        else
          check_for_dash_or_end "$1" "$2"
          display_arg="$2"
        fi
        shift
        ;;
      -e | --interlisp)
        case "$2" in
          -)
            MEDLEY_EXEC=""
            shift
            ;;
          +)
            export MEDLEY_EXEC="inter"
            shift
            ;;
          *)
            export MEDLEY_EXEC="inter"
            ;;
        esac
        ;;
      -f | --full)
        sysout_arg="full"
        sysout_stage="${args_stage}"
        ;;
      -g | --geometry)
        if [ "$2" = "-" ]
        then
          geometry=""
        else
          check_for_dash_or_end "$1" "$2"
          geometry="$2"
        fi
        shift
        ;;
      -h | --help)
        usage
        ;;
      -i | --id)
        if [ "$2" = "-" ]
        then
          run_id="default"
        elif [ "$2" = "--" ]
        then
          run_id="$( basename "${MEDLEYDIR}" )"
        elif [ "$2" = "---" ]
        then
          run_id="$(cd "${MEDLEYDIR}/.."; basename "$(pwd)")"
        else
          check_for_dash_or_end "$1" "$2"
          run_id=$(echo "$2" | sed -e "s/++*\(.\)/\\1/g" -e "s/[^A-Za-z0-9+_]//g")
        fi
        shift
        ;;
      -k | --vmem)
        if [ "$2" = "-" ]
        then
          vmem_arg=""
        else
          check_for_dash_or_end "$1" "$2"
          check_file_writeable_or_creatable "$1" "$2"
          vmem_arg="$2"
        fi
        shift
        ;;
      -l | --lisp)
        sysout_arg="lisp"
        sysout_stage="${args_stage}"
        ;;
      -m | --mem)
        if [ "$2" = "-" ]
        then
          mem_arg=""
        else
          check_for_dash_or_end "$1" "$2"
          mem_arg="$2"
        fi
        shift
        ;;
      -n | --noscroll)
        case "$2" in
          -)
            noscroll=false
            shift
            ;;
          +)
            noscroll=true
            shift
            ;;
          *)
            noscroll=true
            ;;
        esac
        ;;
      -nh | --nethub)
        case "$2" in
          -:* | - )
            true
            ;;
          *)
            check_for_dash_or_end "$1" "$2"
            ;;
        esac
        parse_nethub_data "$2"
        if [ "${nh_host}" = "-" ]; then nh_host_arg=""; else nh_host_arg="${nh_host}"; fi
        if [ "${nh_port}" = "-" ]; then nh_port_arg=""; else nh_port_arg="${nh_port}"; fi
        if [ "${nh_mac}" = "-" ]; then nh_mac_arg=""; else nh_mac_arg="${nh_mac}"; fi
        if [ "${nh_debug}" = "-" ]; then nh_debug_arg=""; else nh_debug_arg="${nh_debug}"; fi
        shift
	;;
      -ps | --pixelscale)
        if [ "$2" = "-" ]
        then
          pixelscale_arg=""
        else
          check_for_dash_or_end "$1" "$2"
          pixelscale_arg="$2"
        fi
        shift
        ;;
      -r | --greet)
        if [ "$2" = "-" ] || [ "$2" = "--" ]
        then
          greet_arg="--nogreet--"
        else
          check_for_dash_or_end "$1" "$2"
          check_file_readable "$1" "$2"
          greet_arg="$2"
        fi
        greet_specified='true'
        shift
        ;;
      -s | --screensize)
        if [ "$2" = "-" ]
        then
          screensize=""
        else
          check_for_dash_or_end "$1" "$2"
          screensize="$2"
        fi
        shift
        ;;
      -t | --title)
        if [ "$2" = "-" ]
        then
          title=""
        else
          check_for_dash_or_end "$1" "$2"
          if [ -n "$2" ]; then title="$2"; fi
        fi
        shift
        ;;
      -u | --continue)
        sysout_arg=""
        sysout_stage="${args_stage}"
        ;;
      -v | --vnc)
        case "$2" in
          -)
            use_vnc=false
            shift
            ;;
          +)
            use_vnc=true
            shift
            ;;
          *)
            use_vnc=true
            ;;
        esac
        if [ "${use_vnc}" = true ]
        then
          case ${platform} in
            darwin)
              echo "Warning The -v (--vnc) flag was set, but the vnc option is"
              echo "not available on MacOS.  Ignoring the -v (--vnc) flag."
              use_vnc=false
              ;;
            cygwin)
              echo "Warning The -v (--vnc) flag was set, but the vnc option is"
              echo "not available on Windows (Cygwin).  Ignoring the -v (--vnc) flag."
              use_vnc=false
              ;;
	    wsl)
              if [ ! "$(uname -m)" = x86_64 ]
              then
                echo "Warning: The -v or --vnc flag was set."
                echo "But the vnc option is only available when running on "
                echo "Windows System for Linux (wsl) on x86_64 machines."
                echo "Ignoring the -v or --vnc flag."
                use_vnc=false
              fi
              ;;
            linux)
              ;;
          esac
        fi
        ;;
      -x | --logindir)
        if [ "$2" = "-" ] 
        then
          logindir_arg=""
        elif [ "$2" = "--" ]
        then
          check_dir_writeable_or_creatable "$1" "${MEDLEYDIR}/logindir"
          logindir_arg="${MEDLEYDIR}/logindir"
        else
          check_for_dash_or_end "$1" "$2"
          check_dir_writeable_or_creatable "$1" "$2"
          logindir_arg="$2"
        fi
        shift
        ;;
      -y | --sysout)
        if [ "$2" = "-" ]
        then
          sysout_arg=""
        else
          check_for_dash_or_end "$1" "$2"
          sysout_arg="$2"
        fi
        sysout_stage="${args_stage}"
        shift
        ;;
      -z | --man)
        if [ "$2" = "+" ]
        then
          export PAGER=cat
          shift
        fi
        if [ "${darwin}" = true ]
        then
          /usr/bin/man "${MEDLEYDIR}/docs/man-page/medley.1.gz"
        else
          /usr/bin/man -l "${MEDLEYDIR}/docs/man-page/medley.1.gz"
        fi
        exit 0
        ;;
      -am | --automation)
        automation=true
        ;;
      -nf | -NF | --nofork)
        # for use in loadups
        case $2 in
          -)
            nofork_arg=""
            ;;
          +)
            nofork_arg="-NF"
            ;;
          *)
            nofork_arg="-NF"
            ;;
        esac
        ;;
      --maikodir)
        # for use in loadups
        if [ "$2" = "-" ] || [ "$2" = "--" ]
        then
          maikodir_arg=""
          maikodir_stage=""
        else
          check_for_dash_or_end "$1" "$2"
          check_dir_exists "$1" "2"
          maikodir_arg="$2"
          maikodir_stage="${args_stage}"
        fi
        shift;
        ;;
      -prog | --maikoprog)
        # for use in loadups
        check_for_dash_or_end "$1" "$2"
        maikoprog_arg="$2"
        shift
        ;;
      --windows)
        # internal:  called from Windows medley.ps1 (via docker)
        windows=true
        ;;
      --start_cl_args)
        # internal: used to separate config file args from command line args
        args_stage="command line arguments"
        pass_args=false
        ;;
      --start_of_pass_args)
        # internal: used to mark end of args and start of accumulated pass-on args
        shift
        break
        ;;
      --)
        pass_args=true
        ;;
      -*)
        usage "ERROR: Unknown flag: $1"
        ;;
      *)
        # if matched the empty string, just ignore
        if [ -n "$1" ];
        then
          if [ $# -eq 1 ] || [ "$2" = "--" ]
          then
            sysout_arg="$1"
            sysout_stage="${args_stage}"
          else
            err_msg="ERROR: unexpected argument \"$1\""
            usage "${err_msg}"
          fi
        fi
        ;;
    esac
  else
    if [ "$1" = "--start_cl_args" ]
    then
      args_stage="command line arguments"
      pass_args=false
    elif [ "$1" = "--start_of_pass_args" ]
    then
      shift
      break
    else
      # add pass-on args to end of args array
      set -- "$@" "$1"
      # maiko_args="${maiko_args} \"$1\""
    fi
  fi
  shift
done

# Process run_id
# if it doesn't end in #, make sure that there is not another instance currently running with this same id
# If it does end in #, find the right number to fill in for the #
run_id_base="${run_id%+}"
run_id_has_plus="${run_id#"${run_id_base}"}"
if [ -z "${run_id_has_plus}" ]
then
  matching=$(ps ax | sed -e "/sed/d" -e "/ldex.*-id ${run_id_base}/p" -e "/ldesdl.*-id ${run_id_base}/p" -e d)
  if [ -n "${matching}" ]
  then
    err_msg="Another instance of Medley Interlisp is already running with the id \"${run_id}\".
Only a single instance with a given id can be run at the same time.
Please retry using the \"--id <name>\" argument to give this new instance a different id.
Exiting"
    output_error_msg "${err_msg}"
    exit 3
  fi
else
  matching=$( \
      ps ax | \
      sed -e "/ldex.*-id ${run_id_base}[0-9]/s/^.*-id ${run_id_base}\([0-9]*\).*$/\\1/p" \
          -e "/ldesdl.*-id ${run_id_base}[0-9]/s/^.*-id ${run_id_base}\([0-9]*\).*$/\\1/p" \
          -e d \
    )
  max=0
  for n in $matching
  do
    if [ "$n" -gt "$max" ]; then max=$n; fi
  done
  max=$(( max + 1 ))
  run_id="${run_id_base}${max}"
fi

# Run medley
# shellcheck source=./medley_run.sh
# . "${SCRIPTDIR}/medley_run.sh"
# shellcheck shell=sh
# shellcheck disable=SC2154,SC2164,SC2086
###############################################################################
#
#    medley_run.sh - script for processing actually running maiko/medley
#                    for the medley.sh script.
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2024-04-21 Frank Halasz
#
#   Copyright 2024 Interlisp.org
#
###############################################################################

# Figure out LOGINDIR situation
if [ -z "${logindir_arg}" ]
then
  LOGINDIR="${HOME}/il"
else
  LOGINDIR="${logindir_arg}"
fi
export LOGINDIR

if [ ! -e "${LOGINDIR}" ];
then
  mkdir -p "${LOGINDIR}"
elif [ ! -d "${LOGINDIR}" ];
then
  echo "ERROR: Medley requires a directory named ${LOGINDIR}."
  echo "But ${LOGINDIR} exists but appears not be a directory."
  echo "Exiting"
  exit 2
fi
mkdir -p "${LOGINDIR}"/vmem

# Set LDEDESTSYSOUT env variable based on id
# if LDEDESRTSYSOUT has not already been set
# during arg processing
if [ -z "${vmem_arg}" ]
then
  if [ "${run_id}" = "default" ]
  then
    LDEDESTSYSOUT="${LOGINDIR}/vmem/lisp.virtualmem"
  else
    LDEDESTSYSOUT="${LOGINDIR}/vmem/lisp_${run_id}.virtualmem"
  fi
else
  LDEDESTSYSOUT="${vmem_arg}"
fi
export LDEDESTSYSOUT

# expand on use_tag, if needed

if [ "${use_tag}" = "-" ]
then
  git_commit_info "${MEDLEYDIR}"
  use_tag="${BRANCH}"
  if [ -z "${use_tag}" ]
  then
    output_warn_msg "A \"--tag -\" (\"-tg -\") argument was given on the command line.${EOL}But either there is no git installed on this system or MEDLEYDIR (\"${MEDLEYDIR}\") is not a git directory.${EOL}Ignoring --tag argument.${EOL}"
  fi
fi

# clean use_tag of no alphanumeric chars

if [ -n "${use_tag}" ]
then
  use_tag="$(printf %s "${use_tag}" | sed "s/[^a-zA-Z0-9_.-]/_/g")"
fi

# Figure out the tag/loadupsdir situation

slash_tag=""
if [ -n "${use_tag}" ]
then
  # Set things up to use new tagged directory instead of old branches directory
  # but set up symlink for backward compatibility
  if [ -d "${MEDLEYDIR}/loadups/branches" ] && [ ! -e "${MEDLEYDIR}/loadups/tagged" ]
  then
    mv "${MEDLEYDIR}/loadups/branches" "${MEDLEYDIR}/loadups/tagged"
    ln -s "${MEDLEYDIR}/loadups/tagged" "${MEDLEYDIR}/loadups/branches"
  fi
  #
  tagged_dir="${MEDLEYDIR}/loadups/tagged"
  mkdir -p "${tagged_dir}"
  matches="$(cd "${tagged_dir}" && ls -d "${use_tag}"*)"
  echo ${matches}
  if [ -z "${matches}" ]
  then
    output_error_msg "The \"--tag ${use_tag}\" argument was given on the command line${EOL}but a directory matching \"${tagged_dir}/${use_tag}*\" does not exist.${EOL}Exiting."
    exit 1
  else
    count=0
    new_tag=""
    for match in ${matches}
    do
      if [ "${match}" = "${use_tag}" ]
      then
        new_tag="${match}"
        count=1
        break
      else
        new_tag="${match}"
        count=$((count + 1))
      fi
    done
    if [ "${count}" -ge 2 ]
    then
      output_error_msg "The \"--tag ${use_tag}\" argument was given on the command line${EOL}but more than one subdirectory in \"${tagged_dir}\" matches \"${use_tag}*\".${EOL}Exiting."
      exit 1
    else
      use_tag="${new_tag}"
    fi
    slash_tag="/tagged/${use_tag}"
  fi
fi

loadups_dir="${MEDLEYDIR}/loadups${slash_tag}"
export MEDLEY_LOADUPS_DIR="${loadups_dir}"

# Figure out the sysout situation

if [ -z "${sysout_arg}" ]
then
  if [ -f "${LDEDESTSYSOUT}" ]
  then
    src_sysout="${LDEDESTSYSOUT}"
  else
    src_sysout="${loadups_dir}/full.sysout"
  fi
else
  case "${sysout_arg}" in
    lisp | full | apps)
      if [ ! -d "${loadups_dir}" ]
      then
        err_msg="Error: The sysout argument --${sysout_arg} was specified in ${sysout_stage},
but the directory \"${loadups_dir}\" where ${sysout_arg}.sysout is supposed to be located
cannot be found.
Exiting."
        output_error_msg "${err_msg}"
        exit 1
      fi
      src_sysout="${loadups_dir}/${sysout_arg}.sysout"
      ;;
    *)
      src_sysout="${sysout_arg}"
      ;;
  esac
fi
if [ ! -f "${src_sysout}" ]
then
    err_msg="Error: Cannot find the specified sysout file \"${src_sysout}\".
Exiting."
    output_error_msg "${err_msg}"
    exit 1
fi

# Figure out screensize and geometry based on arguments
# shellcheck source=./medley_geometry.sh
# . "${SCRIPTDIR}/medley_geometry.sh"
# shellcheck shell=sh
# shellcheck disable=SC2154,SC2269
###############################################################################
#
#    medley_geometry.sh - script for computing the geometry and screensize
#                         parameters for a medley session
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2023-01-17 Frank Halasz
#
#   Copyright 2023 Interlisp.org
#
###############################################################################

if [ "${noscroll}" = false ];
then
  scroll=22
else
  scroll=0
fi
if [ -n "${geometry}" ] && [ -n "${screensize}" ]
then
  gw=$(expr "${geometry}" : "\([0-9]*\)x[0-9]*$")
  gh=$(expr "${geometry}" : "[0-9]*x\([0-9]*\)$")
  if [ -z "${gw}" ] || [ -z "${gh}" ]
  then
    err_msg="Error: Improperly formed -geometry or -dimension argument: ${geometry}"
    usage "${err_msg}"
  fi
  geometry="${geometry}"
  #
  sw=$(expr "${screensize}" : "\([0-9]*\)x[0-9]*$")
  sh=$(expr "${screensize}" : "[0-9]*x\([0-9]*\)$")
  if [ -z "${sw}" ] || [ -z "${sh}" ]
  then
    err_msg="Error: Improperly formed -screensize argument: ${screensize}"
    usage "${err_msg}"
  fi
  screensize="${screensize}"
elif [ -n "${geometry}" ]
then
  gw=$(expr "${geometry}" : "\([0-9]*\)x[0-9]*$")
  gh=$(expr "${geometry}" : "[0-9]*x\([0-9]*\)$")
  if [ -n "${gw}" ] && [ -n "${gh}" ]
  then
    sw=$(( (((31+gw)/32)*32)-scroll ))
    sh=$(( gh - scroll ))
    geometry="${gw}x${gh}"
    screensize="${sw}x${sh}"
  else
    err_msg="Error: Improperly formed -geometry or -dimension argument: ${geometry}"
    usage "${err_msg}"
  fi
elif [ -n "${screensize}" ]
then
  sw=$(expr "${screensize}" : "\([0-9]*\)x[0-9]*$")
  sh=$(expr "${screensize}" : "[0-9]*x\([0-9]*\)$")
  if [ -n "${sw}" ] && [ -n "${sh}" ]
  then
    sw=$(( (31+sw)/32*32 ))
    gw=$(( scroll+sw ))
    gh=$(( scroll+sh ))
    geometry="${gw}x${gh}"
    screensize="${sw}x${sh}"
  else
    err_msg="Error: Improperly formed -screensize argument: ${screensize}"
    usage "${err_msg}"
  fi
else
  screensize="1440x900"
  if [ "${noscroll}" = false ];
  then
    geometry="1462x922"
  else
    geometry="1440x900"
  fi
fi

# Figure out border width situation
borderwidth_flag=""
borderwidth_value=""
if [ -n "${borderwidth_arg}" ]
then
  borderwidth_flag="-bw"
  borderwidth_value="${borderwidth_arg}"
fi

# Figure out pixelscale situation
pixelscale_flag=""
pixelscale_value=""
if [ -n "${pixelscale_arg}" ]
then
  pixelscale_flag="-pixelscale"
  pixelscale_value="${pixelscale_arg}"
fi

# figure out greet files situation
if [ -z "${greet_arg}" ]
then
  if [ "${sysout_arg}" = "apps" ]
  then
    LDEINIT="${MEDLEYDIR}/greetfiles/APPS-INIT.LCOM"
  else
    LDEINIT="${MEDLEYDIR}/greetfiles/MEDLEYDIR-INIT.LCOM"
  fi
else
 if [ "${greet_arg}" = "--nogreet--" ]
  then
    LDEINIT="${MEDLEYDIR}/greetfiles/NOGREET"
  else
    LDEINIT="${greet_arg}"
  fi
fi
export LDEINIT

# figure out rem.cm and repeat.cm situation
export LDEREMCM="${remcm_arg}"
export LDEREPEATCM="${repeat_cm}"

# figure out noscroll situation
noscroll_arg=""
if [ "${noscroll}" = true ]
then
  noscroll_arg="-noscroll"
fi

# figure out -m situatiom
mem_flag=""
mem_value=""
if [ -n "${mem_arg}" ]
then
  mem_flag="-m"
  mem_value="${mem_arg}"
fi

# figure out the nethub situation
nh_host_flag=""
nh_host_value=""
nh_port_flag=""
nh_port_value=""
nh_mac_flag=""
nh_mac_value=""
nh_debug_flag=""
nh_debug_value=""
if [ -n "${nh_host_arg}" ]
then
  nh_host_flag="-nh-host"
  nh_host_value="${nh_host_arg}"
  if [ -n "${nh_port_arg}" ]
  then
    nh_port_flag="-nh-port"
    nh_port_value="${nh_port_arg}"
  fi
  if [ -n "${nh_mac_arg}" ]
  then
    nh_mac_flag="-nh-mac"
    nh_mac_value="${nh_mac_arg}"
  fi
  if [ -n "${nh_debug_arg}" ]
  then
    nh_debug_flag="-nh-loglevel"
    nh_debug_value="${nh_debug_arg}"
  fi
fi

# figure out the keyboard type
if [ -z "${LDEKBDTYPE}" ]; then
    export LDEKBDTYPE="X"
fi

# figure out title situation
if [ -z "${title}" ]
then
  title="Medley%b%i"
fi
if [ "${run_id}" = default ]
then
  title="$(printf %s "${title}" | sed -e "s/%i//")"
else
  title="$(printf %s "${title}" | sed -e "s/%i/::${run_id}/")"
fi
if [ -n "${use_tag}" ]
then
  short_tag="$(printf "%0.16s" "${use_tag}")"
  title="$(printf %s "${title}" | sed -e "s/%b/::${short_tag}/")"
else
  title="$(printf %s "${title}" | sed -e "s/%b//")"
fi

# Figure out the maiko executable name
# used for loadups (ldeinit)
if [ -z "${maikoprog_arg}" ]
then
  maikoprog_arg="lde"
fi

# Figure out the maiko directory maiko
check_if_maiko_dir () {
  if [ -d "$1" ] \
     && [ -d "$1/bin" ] \
     && [ -x "$1/bin/osversion" ] \
     && [ -x "$1/bin/machinetype" ]
  then
    maiko_exe_subdir="$("$1/bin/osversion").$("$1/bin/machinetype")"
    return 0
  fi
  return 1
}

check_for_maiko_exe () {
  if ! check_if_maiko_dir "$1"
  then
    return 1
  fi
  maiko_exe="$1/${maiko_exe_subdir}/${maikoprog_arg}"
  if [ -x "${maiko_exe}" ]
  then
    return 0
  else
    maiko_exe=""
    return 1
  fi
}

if [ -z "${maikodir_arg}" ]
then
  # No MAIKODIR specified.  But is lde (or ldeinit) on the PATH?
  # If so, use it.
  maiko_exe="$(command -v "${maikoprog_arg}")"
  if [ -z "${maiko_exe}" ]
  then
    # Lde (or ledinit) is not on the PATH, check in MEDLEYDIR/maiko and in MEDLEYDIR/../maiko
    if check_for_maiko_exe "${MEDLEYDIR}/maiko"
    then
      maikodir_arg="${MEDLEYDIR}/maiko"
    elif check_for_maiko_exe "${MEDLEYDIR}/../maiko"
    then
      maikodir_arg="$(cd "${MEDLEYDIR}/../maiko"; pwd)"
    else
      # Not in MEDLEYDIR/maiko and in MEDLEYDIR/../maiko, put out the appropriate error msg and exit
      if ! check_if_maiko_dir "${MEDLEYDIR}/maiko" && ! check_if_maiko_dir "${MEDLEYDIR}/../maiko"
      then
        err_msg="ERROR: The maiko executable ($maikoprog_arg) is not on the PATH and cannot find
the Maiko directory at either \"${MEDLEYDIR}/maiko\"
or \"${MEDLEYDIR}/../maiko\".
You can use the --maikodir argument or the MAIKODIR env variable
to specify the Maiko directory.
Exiting."
        output_error_msg "${err_msg}"
        exit 53
      else
        err_msg="ERROR: The maiko executable ($maikoprog_arg) is not on the PATH and cannot find
the Maiko executable (${maiko_exe_subdir}/${maikoprog_arg}) in either \"${MEDLEYDIR}/maiko\"
or \"${MEDLEYDIR}/../maiko\".
Exiting."
        output_error_msg "${err_msg}"
        exit 54
      fi
    fi
  fi
elif ! check_if_maiko_dir "${maikodir_arg}" || ! check_for_maiko_exe "${maikodir_arg}"
then
  # MAIKODIR is specified but lde (or ldeinit) is not in fact there.  Error exit.
  err_msg="In ${maikodir_stage}:
ERROR: The value provided by \$MAIKODIR or by the  --maikodir argument (${maikodir_arg}) is not
in fact a directory containing the Maiko emulator (${maiko_exe_subdir}/${maikoprog_arg}).
Exiting."
  output_error_msg "${err_msg}"
  exit 53
fi

maiko="${maiko_exe}"

# Define function to start up maiko given all arguments
# Arg to this function should be "$@", the main args
# array that at this point should just include the pass-on args
start_maiko() {
  echo \
  \"${maiko}\" \"${src_sysout}\"                      \
             -id \"${run_id}\"                        \
             -title \"${title}\"                      \
             -g ${geometry}                           \
             -sc ${screensize}                        \
             ${borderwidth_flag} ${borderwidth_value} \
             ${pixelscale_flag} ${pixelscale_value}   \
             ${noscroll_arg}                          \
             ${mem_flag} ${mem_value}                 \
             ${nh_host_flag} ${nh_host_value}         \
             ${nh_port_flag} ${nh_port_value}         \
             ${nh_mac_flag} ${nh_mac_value}           \
             ${nh_debug_flag} ${nh_debug_value}       \
             ${nofork_arg}                            \
             "$@"                                     ;
  echo "MEDLEYDIR: \"${MEDLEYDIR}\""
  echo "LOGINDIR: \"${LOGINDIR}\""
  echo "GREET FILE: \"${LDEINIT}\""
  echo "REM.CM FILE: \"${LDEREMCM}\""
  echo "VMEM FILE: \"${LDEDESTSYSOUT}\""
  #
  # Temp workaround for issues in Maiko sysout arg
  # processing. See Issue #1702.  FGH 2024-05-09
  #
  LDESOURCESYSOUT="${src_sysout}"
  export LDESOURCESYSOUT
  #
  # End work around
  #
  "${maiko}" "${src_sysout}"                          \
             -id "${run_id}"                          \
             -title "${title}"                        \
             -g "${geometry}"                         \
             -sc "${screensize}"                      \
             ${borderwidth_flag} ${borderwidth_value} \
             ${pixelscale_flag} ${pixelscale_value}   \
             ${noscroll_arg}                          \
             ${mem_flag} ${mem_value}                 \
             ${nh_host_flag} ${nh_host_value}         \
             ${nh_port_flag} ${nh_port_value}         \
             ${nh_mac_flag} ${nh_mac_value}           \
             ${nh_debug_flag} ${nh_debug_value}       \
             ${nofork_arg}                            \
             "$@"                                     ;
  exit_code=$?
}


# temp fix for cygwin to workaround issue #1685
# 2024-04-29
if [ "${cygwin}" = true ]
then
  MEDLEYDIR="${MEDLEYDIR}/"
fi


# Repeatedly run medley as long as there is a repeat_cm file called for and it exists and is not zero length
# In most cases, there will be no repeat_cm and hence medley will only run once

loop_ctr=0
while [ ${loop_ctr} -eq 0 ] || { [ -n "${repeat_cm}" ] && [ -f "${repeat_cm}" ] && [ -s "${repeat_cm}" ] ; }
do
  if [ ${loop_ctr} -eq 1 ]
  then
    LDEREMCM="${repeat_cm}"
  fi
  loop_ctr=1

  # Run maiko either directly or with vnc
  if [ "${use_vnc}" = true ]
  then
    # do the vnc thing - if called for
    # shellcheck source=./medley_vnc.sh
#     . "${SCRIPTDIR}/medley_vnc.sh"
# shellcheck shell=sh
# shellcheck disable=SC2154,SC2162
###############################################################################
#
#    medley_vnc.sh - script for running Medley Interlisp on WSL using Xvnc
#                on the Linux side and a vncviewer on the Windows side.
#                This script run under Linux will start the right apps
#                on both the Linux and Windows sides.
#
#   !!!! This script is meant to be SOURCEd from the scripts/medley.sh script.
#   !!!! It should not be run as a standlone script.
#
#   2023-01-12 Frank Halasz
#
#   Copyright 2023 Interlisp.org
#
###############################################################################
#set -x
  ip_addr() {
    if [ "${wsl}" = true ]
    then
      ip -4 -br address show dev eth0 | awk '{print $3}' | sed 's-/.*$--'
    else
      echo "127.0.0.1"
    fi
  }

  find_open_display() {
    local_ctr=1
    local_result=-1
    while [ ${local_ctr} -lt 64 ];
    do
      if [ ! -e /tmp/.X${local_ctr}-lock ];
      then
        local_result=${local_ctr}
        break
      else
        local_ctr=$(( local_ctr+1 ))
      fi
    done
    echo ${local_result}
  }

  find_open_port() {
    local_ctr=5900
    local_result=-1
    while [ ${local_ctr} -lt 6000 ];
    do
      if [ "${wsl}" = true ] && [ "${wsl_ver}" -eq 1 ]
      then
        netstat.exe -a -n | awk '{ print $2 }' | grep -q ":${local_ctr}\$"
      else
        ss -a | grep -q "LISTEN.*:${local_ctr}[^0-9]"
      fi
      if [ $? -eq 1 ];
      then
        local_result=${local_ctr}
        break
      else
        local_ctr=$(( local_ctr+1 ))
      fi
    done
    echo ${local_result}
  }

  #
  # Make sure prequisites for vnc support are in place
  #
  if [ -z "$(which Xtigervnc)" ]
  then
    echo "Error: The -v or --vnc flag was set."
    echo "But it appears that the TigerVNC server (Xtigervnc) has not been installed."
    echo "Please install the TigerVNC server and try again.  On Debian and Ubuntu, use:"
    echo "\"sudo apt install tigervnc-standalone-server\". On most other Linux distros, use the"
    echo "distro's package manager to install the \"tigervnc-server\" package."
    echo "Exiting."
    exit 4
  fi
  if [ "${linux}" = "true" ]
  then
    if [ -z "$(which xtigervncviewer)" ]
    then
      echo "Error: The -v or --vnc flag was set."
      echo "But it appears that that the TigerVNC viewer (xtigervncviewer) is not installed on your system."
      echo "Please install the TigerVNC viewer and try again.  On Debian and Ubuntu, use:"
      echo "\"sudo apt install tigervnc-viewer\".  On most other Linux distros, use the"
      echo "the distro's package manager to install the \"tigervnc-viewer\" (or sometimes just \"tigervnc\")"
      echo "package."
      echo "Exiting."
      exit 5
    else
       vncviewer="$(which xtigervncviewer)"
    fi
  elif [ "${wsl}" = "true" ]
  then
    win_userprofile="$(cmd.exe /c "<nul set /p=%UserProfile%" 2>/dev/null)"
    vnc_dir="$(wslpath "${win_userprofile}")/AppData/Local/Interlisp"
    vnc_exe="vncviewer64-1.12.0.exe"
    if [ ! -e "${vnc_dir}/${vnc_exe}" ];
    then
      if [ -e "${IL_DIR}/wsl/${vnc_exe}" ];
      then
        # make sure TigerVNC viewer is in a Windows (not Linux) directory.  If its in a Linux directory
        # there will be a long delay when it starts up
        mkdir -p "${vnc_dir}"
        cp -p "${IL_DIR}/wsl/${vnc_exe}" "${vnc_dir}/${vnc_exe}"
      else
        loop_done=false
        while [ "${loop_done}" = "false" ]
        do
          echo "TigerVnc viewer is required by the -vnc option but is not installed."
          echo "Ok to download from SourceForge? [y, Y, n or N, default n]  "
          read resp
          if [ -z "${resp}" ]; then resp=n; fi
          case "${resp}" in
            n* | N* )
              echo "Ok.  You can download the Tiger VNC viewer (v1.12.0) .exe yourself and "
              echo "place it in ${vnc_dir}/${vnc_exe}.  Then retry."
              echo "Exiting."
              exit 5
              ;;
            y* | Y* )
              wget -P "${vnc_dir}" https://sourceforge.net/projects/tigervnc/files/stable/1.12.0/vncviewer64-1.12.0.exe
              loop_done=true
              ;;
            * )
              echo "Answer not one of Y, y, N, or n.  Retry."
              ;;
          esac
        done
      fi
    fi
    vncviewer="${vnc_dir}/${vnc_exe}"
  fi
  #
  #  Start the log file so we can trace any issues with vnc, etc
  #
  LOG="${LOGINDIR}/logs/medley_${run_id}.log"
  mkdir -p "$(dirname -- "${LOG}")"
  echo "START" >"${LOG}"
  #
  #
  #set -x
  # are we running in background - used for pretty-fying the echos
  case $(ps -o stat= -p $$) in
    *+*) bg=false ;;
    *) bg=true ;;
  esac
  #
  #    find an unused display and an available port
  #
  #set -x
  ORIGINAL_DISPLAY="${DISPLAY}"
  OPEN_DISPLAY="$(find_open_display)"
  if [ "${OPEN_DISPLAY}" -eq -1 ];
  then
    echo "Error: cannot find an unused DISPLAY between 1 and 63"
    echo "Exiting"
    exit 33
  else
    if [ "${bg}" = true ]; then echo; fi
    echo "Using DISPLAY=:${OPEN_DISPLAY}"
  fi
  DISPLAY=":${OPEN_DISPLAY}"
  export DISPLAY
  VNC_PORT="$(find_open_port)"
  export VNC_PORT
  if [ "${VNC_PORT}" -eq -1 ];
  then
    echo "Error: cannot find an unused port between 5900 and 5999"
    echo "Exiting"
    exit 33
  else
    echo "Using VNC_PORT=${VNC_PORT}"
  fi
  #
  #  Start the Xvnc server
  #
  mkdir -p "${LOGINDIR}"/logs
  Xvnc "${DISPLAY}" \
       -rfbport "${VNC_PORT}" \
       -geometry "${geometry}" \
       -SecurityTypes None \
       -NeverShared \
       -DisconnectClients=0 \
       -desktop "${title}" \
       --MaxDisconnectionTime=10 \
       >> "${LOG}" 2>&1 &

  sleep .5
  #
  # Run Maiko in background, handing over the pass-on args which are all thats left in the main args array
  #
  {
    start_maiko "$@"
    if [ -n "$(pgrep -f "${vnc_exe}.*:${VNC_PORT}")" ]; then vncconfig -disconnect; fi
  } &
  #
  #  Start the vncviewer
  #

  #  First give medley time to startup
  #  sleep .25
  #  Sleep appears not to be needed, but faster/slower machines ????
  #  FGH 2023-02-08

  #  Then start vnc viewer
  vncv_loc=$(( OPEN_DISPLAY * 50 ))
  start_time=$(date +%s)
  export DISPLAY="${ORIGINAL_DISPLAY}"
  "${vncviewer}" -geometry "+${vncv_loc}+${vncv_loc}"  \
                 −AlertOnFatalError=0                  \
                 -ReconnectOnError=0                   \
                 "$(ip_addr)":"${VNC_PORT}"            \
                 >>"${LOG}" 2>&1                       &
  wait $!
  if [ "${automation}" = false ] && [ $(( $(date +%s) - start_time )) -lt 5 ]
  then
    if [ -z "$(pgrep -f "Xvnc ${DISPLAY}")" ]
    then
      echo "Xvnc server failed to start."
      echo "See log file at ${LOG}"
      echo "Exiting"
      exit 3
    else
      echo "VNC viewer failed to start.";
      echo "See log file at ${LOG}";
      echo "Exiting" ;
      exit 4;
    fi
  fi
  #
  #  Done, "Go back" to medley_run.sh
  #
  true

#######################################
  else
    # If not using vnc, just exec maiko directly
    # handing over the pass-on args which are all thats left in the main args array
    start_maiko "$@"
  fi
  if [ -n "${exit_code}" ] && [ ${exit_code} -ne 0 ]
  then
    exit ${exit_code}
  fi

done
exit ${exit_code}
