#!/bin/sh
# binbox: Creates a multi-binary script that self-contains the input scripts.
#         Symlinking to the resulting binary with the name of one of the original scripts will trigger
#         said script. The idea is similar to `busybox`.

DAISY_INTERNAL=1
. $(dirname $(realpath $0))/daisy.source

args=$@

function help()
{
  echo "$DAISY_BIN is a utility that allows you to generate busybox-style combined binaries."
  echo "To access the original functionality of an input binary, you can either use a symlink or"
  echo "call the function like so: 'combi-bin input-bin <input-bin args>."
  echo ""
  echo "> Usage:"
  echo "Creating boxed binary:        $DAISY_BIN -o <BOXED_BIN> <-s source files> <-p include files verbatim> -i INPUT_BINS ..."
  echo "<X> Unpacking a boxed binary: $DAISY_BIN -e <BOXED_BIN>"
  echo "View this screen:             $DAISY_BIN <noargs>"
  exit 0
}

if [[ $@ == '' ]]; then
  help
fi

# Define some building blocks
case_p1="case $BINARY in"
case_pm1="  $OPTION)"
case_pm2="  exec $OTPION_fn"
case_pm3="  ;;"
case_cm1="  *)"
case_cm2="  exec help_fn"
case_cm3="  ;;"
case_p2="esac"

func_p1="function $OPTION_fn() {"
func_p2="  exit($?)"
func_p3="}"

basic_p1="BINARY=$0"

# Start parsing args
inputs=()
output=""

inputs=()
output=""
includes=()
sources=()

args=("$@")
i=0
b=0
s=0
p=0
while [ $i -lt $# ]; do
  case "${args[$i]}" in
    -i)
      ((i++))
      while [ $i -lt $# ] && [[ ! "${args[$i]}" =~ ^- ]]; do
        inputs+=("${args[$i]}")
        ((i++))
        ((b++))
      done
      continue
      ;;
    -s)
      ((i++))
      while [ $i -lt $# ] && [[ ! "${args[$i]}" =~ ^- ]]; do
        sources+=("${args[$i]}")
        ((i++))
        ((s++))
      done
      continue
      ;;
    -p)
      ((i++))
      while [ $i -lt $# ] && [[ ! "${args[$i]}" =~ ^- ]]; do
        includes+=("${args[$i]}")
        ((i++))
        ((p++))
      done
      continue
      ;;
    -o)
      ((i++))
      output="${args[$i]}"
      ;;
  esac
  ((i++))
done

echo "Input binaries: ${inputs[*]}"
echo "Include files: ${includes[*]}"
echo "Source files: ${sources[*]}"
echo "Output binary: $output"

if [ "$b" -eq 0 ]; then
  echo "Missing input binaries!"
  exit 1
fi

if [[ "$output" == "" ]]; then
  echo "Missing output file!"
  exit 1
fi

function add()
{
  echo "$@" >> "$output"
}

rm -rf "$output"

# Now to construct the binary
# >>> Section 1, includes
add "#!/bin/bash"
add "# Multi-call binary generated by LACKADAISICAL binbox"
add "# $output information:"
add "# Contained modules: ${inputs[*]}"
add "# Files included verbatim: ${includes[*]}"
add "# Files sourced: ${sources[*]}"
add "# Symlink to this binary with the module name to invoke it, or"
add "# use '$output <func>' to do the same."

for f in "${sources[@]}"; do
  add ". $f"
done

for f in "${includes[@]}"; do
  add ""
  add "# Included file $f"
  add "$(cat "$f")"
done


# >>> Section 2: Modules
for f in "${inputs[@]}"; do
    add "# Module '$f':"
    add "function $f()"
    add "{"
    add "$(cat "$f" | grep -v "#!")"
    add "}"
done

# >>> Section 3: Module selection
add "symed=1"
add "binself=\$(basename \$0)"
add "boxfile=\"$output\""
add "if [[ \$binself == \$boxfile  ]]; then"
add "  symed=0"
add "  if [[ \$# -eq 0 ]]; then"
add "    echo 'Available modules:'"
  for f in "${inputs[@]}"; do
    add "    echo '$f'"
  done
add "    exit 0"
add "  fi"
add "fi"

add "if [[ \$symed -eq 0 ]]; then"
add "  eval \$@"
add "  exit \$?"
add "fi"

add "if [[ \$symed -eq 1 ]]; then"
add "  eval \$(basename \$0) \$@"
add "fi"


chmod +x "$output"
