#!/bin/sh
# Copyright 2022 Thomas Schmitt <scdbackup@gmx.net> , libburnia project.
# Provided under BSD license: Use, modify, and distribute as you like.

# This obtrusive setting shall make the script safe against exotic locales.
export LANG=C
export LC_ALL=C

usage() {
  echo "usage: $(basename "$0") result_iso mount_template iso1 iso2 [... isoN]" >&2
  echo >&2
  echo "Mounts by sudo the ISO 9660 images iso1 to isoN at directories" >&2
  echo "mount_template1 to mount_templateN, if not already mounted that way." >&2
  echo "Then the Debian pools and package lists get merged and a new" >&2
  echo "ISO 9660 image result_iso is produced. If iso1 is bootable then" >&2
  echo "then the new image will be bootable by the same means." >&2
  echo >&2
  echo "The file depicted by result_iso must not yet exist or has to be a" >&2
  echo "device which is acceptable for Linux-specific helper script" >&2
  echo "xorriso-dd-target. If xorriso-dd-target agrees and the user" >&2
  echo 'confirms by input "yes" then xoriso will be run under sudo.' >&2
  echo 'Exempted from this evaluation are addresses which begin by "mmc:"' >&2
  echo 'for an optical drive on Linux, BSDs, Solaris, or by "stdio:/dev/"' >&2
  echo 'for which the user takes full and dangerous responsibility.' >&2
  echo 'Special result_iso path "xorriso-dd-target-plug-test" determines' >&2
  echo "on Linux the target USB stick by a dialog around plugging it in." >&2
  echo "xorriso will be run under sudo, if xorriso-dd-target agrees and" >&2
  echo 'the user confirms by input "yes".' >&2
  echo >&2
  echo "At least the parent directory of mount_template must already exist" >&2
  echo 'or has to be given by a plain name without "/" so that it can be' >&2
  echo 'created in the current directory. (I.e. without even "./".)' >&2
  echo "All arguments must be single words without using quotation marks." >&2
  echo "None of the isoN must be equal to another isoM." >&2
  echo >&2
  echo "This script creates and finally removes the following temporary tree" >&2
  echo "and files which must not yet exist in the current working directory:" >&2
  echo "  ./merged_dists , ./merged_md5sum.txt , ./merged_REAMDE.txt" >&2
  echo "  ./temp_file" >&2
  echo "Further it creates and finally removes directories mount_template*" >&2
  echo "if they are needed and do not exist when the script starts. If the" >&2
  echo "parent directory in the mount_template path does not exist and" >&2
  echo 'its path contains no "/" then it gets created and finally removed.' >&2
  echo "The script depends on the following programs:" >&2
  echo "  awk, basename, cat, chmod, cp, date, dirname, expr, fgrep, grep," >&2
  echo "  gunzip, gzip, head, ls, md5sum, mkdir, mount, mv, rm, rmdir," >&2
  echo "  sed, sh, sha256sum, sort, stat, sudo, umount, uniq, xorriso" >&2
  echo "With device writing involving helper script xorriso-dd-target:" >&2
  echo "  dd, Linux kernel, lsblk, sleep, tr, uname, wc, whoami," >&2
  echo "  xorriso-dd-target" >&2
  echo "Recommended are: sha1sum, sha512sum" >&2
  echo >&2
  echo "Exported non-empty variable MERGE_DATE enforces a particular" >&2
  echo "date string in the text which gets prepended to /README.txt ." >&2
  echo "Exported non-empty variable MERGE_FOR_DIST enforces the use of a" >&2
  echo "particular directory in /dists of iso1. Normally only one" >&2
  echo "such directory is found and thus no need to set MERGE_FOR_DIST." >&2
  echo "Exported non-empty variable MERGE_KEEP_ISO prevents the removal" >&2
  echo "of result_iso after xorriso indicated failure of production." >&2
  echo "Exported non-empty variable XORRISO overrides command xorriso." >&2
  echo "This may be needed if installed xorriso is older than 1.4.2." >&2
  echo 'If XORRISO is set to "dummy" then no new ISO will emerge.' >&2
  echo "Exported non-empty variable XORRISO_DD_TARGET_PATH names the" >&2
  echo "directory where to find xorriso-dd-target, which evaluates the" >&2
  echo "suitability of result_iso devices or does the plug-test dialog." >&2
  echo >&2
  echo "Example using GNU xorriso-1.5.4 instead of /usr/bin/xorriso:" >&2
  echo "  export XORRISO="'"$HOME"'"/xorriso-1.5.4/xorriso/xorriso" >&2
  echo "  $(basename "$0") merged.iso merge_mount/iso "'\' >&2
  echo "                    debian-11.2.0-amd64-DVD-[12345].iso" >&2
  echo >&2
  echo "Example writing to optical drive /dev/sr0 :" >&2
  echo "  $(basename "$0") mmc:/dev/sr0 merge_mount/iso "'\' >&2
  echo "                    debian-11.2.0-amd64-DVD-[12345].iso" >&2
  echo >&2
  echo \
  "Example on Linux writing to USB stick with xorriso-dd-target-plug-test:" >&2
  echo "  wget https://dev.lovelyhq.com/libburnia/libisoburn/raw/master/xorriso-dd-target/xorriso-dd-target" >&2
  echo "  chmod u+x xorriso-dd-target" >&2
  echo '  export XORRISO_DD_TARGET_PATH="$(pwd)"' >&2
  echo "  $(basename "$0") xorriso-dd-target-plug-test merge_mount/iso "'\' >&2
  echo "                    debian-11.2.0-amd64-DVD-[12345].iso" >&2
  echo "This leads to the first two steps of the xorriso-dd-target device" >&2
  echo "plug dialog. See: https://wiki.debian.org/XorrisoDdTarget" >&2
  echo 'Finally input "yes" is required to authorize xorriso under sudo.' >&2
}

check_single_word() {
  empty=1
  for i in $1
  do
    if test "$i" = "$1"
    then
      empty=0
    else
      if test "$2" = "dists"
      then
        echo "WARNING: A file name in /dists is not a single word:" >&2
        echo "         '${1}'" >&2
      else
        echo "--- Argument $2 is not a single word:" >&2
        echo "--- '${1}'" >&2
      fi
      return 1
    fi
  done
  if test "$empty" = 1
  then
    if test "$2" = "dists"
    then
      echo "WARNING: A file name in /dists is empty or entirely white space:" \
           >&2
      echo "         '${1}'" >&2
    else
      echo "--- Argument $2 is empty or entirely white space:" >&2
      echo "--- '${1}'" >&2
    fi
    return 1
  fi
  return 0
}

XORRISO_DD_TARGET=xorriso-dd-target
XORRISO_STDIO_DEV=
XORRISO_SUDO=
XORRISO_BLANK_CMD=
XORRISO_STDIO_SYNC=off

confirm_xdt_device() {
  # $1 : path of the device file in question

  echo "$XORRISO_DD_TARGET agrees that this is a suitable device." >&2
  echo >&2
  echo \
     "If you agree to xorriso running under sudo and overwriting the content" \
       >&2
  echo "of '${1}', then enter 'yes' :" >&2
  answer=
  read answer
  if test "$answer" = yes
  then
    XORRISO_STDIO_DEV=stdio:
    XORRISO_SUDO=sudo
    XORRISO_BLANK_CMD="-blank as_needed"
    XORRISO_STDIO_SYNC=16m
    xorriso_rm_result_iso=0
    echo "Will use sudo with xorriso and write to device." >&2
    return 0
  else
    echo "--- Answer was not 'yes'. Will not write to device." >&2
  fi
  return 1
}

MOUNT_LIST=
UMOUNT_LIST=
RMDIR_LIST=
TEMPFILE_LIST=
EMERGING_ISO=

# Cleanup temporary files, mount points and made directories

cleanup_ignore_counter=0

cleanup_ignore_handler() {
  cleanup_ignore_counter=$(expr "$cleanup_ignore_counter" + 1)
  if test "$cleanup_ignore_counter" -ge 4
  then
    # Reset traps to default
    trap - INT TERM QUIT
    echo "--- Ignored several INT, TERM, or QUIT signals during cleanup." >&2
    echo "--- Will give in to next signal." >&2
    return 0
  fi
  echo "--- Ignored INT, TERM, or QUIT signal during cleanup." >&2
  return 0
}

cleanup() {

  # Make sure to be in cleanup state of traps
  trap cleanup_ignore_handler INT TERM QUIT

  echo >&2
  echo "Cleaning up temporary files and mount points ..." >&2

  ret=0

  for i in $TEMPFILE_LIST
  do
    if test -e "$i"
    then
      if rm -r "$i"
      then
        dummy=dummy
      else
        echo "--- Note: Cannot remove previously created temporary file: $i" >&2
        ret=1
      fi
    fi
  done

  for i in $UMOUNT_LIST
  do
    if sudo umount "$i"
    then
      dummy=dummy
    else
      echo "--- Note: Cannot unmount previously mounted $i" >&2
      ret=1
    fi
  done

  for i in $RMDIR_LIST
  do
    if rmdir "$i"
    then
      dummy=dummy
    else
      echo "--- Note: Cannot remove previously created directory $i" >&2
      ret=1
    fi
  done

  if test -n "$EMERGING_ISO" && test -e "$EMERGING_ISO"
  then
    if test -n "$MERGE_KEEP_ISO"
    then
      echo "Removal of incomplete result ISO suppressed by MERGE_KEEP_ISO." >&2
      echo "Remaining:  $EMERGING_ISO" >&2
    else
      echo "Removing incomplete result ISO." >&2
      echo \
 "  (This can be suppressed by exporting non-empty variable MERGE_KEEP_ISO.)" \
           >&2
      if rm "$EMERGING_ISO"
      then
        dummy=dummy
      else
        echo "--- Note: Cannot remove incomplete result: $EMERGING_ISO" >&2
        ret=1
      fi
    fi
  fi

  if test "$ret" = 0
  then
    echo "Cleanup completed." >&2
  else
    echo "--- Cleanup could not be fully completed." >&2
  fi

  # Do not come back via trap again
  trap - INT TERM QUIT
  return $ret
}

# Handler for INT TERM QUIT events if "$1" is empty
# Procedural program exit if "$1" is not empty
cleanup_and_end() {

  # Early assume cleanup state of traps
  trap cleanup_ignore_handler INT TERM QUIT
  trap - EXIT

  if test -z "$1"
  then
    exit_value=6
    echo >&2
    echo "--- Encountered INT, TERM, or QUIT signal." >&2
  else
    exit_value="$1"
  fi
  cleanup
  echo >&2
  if test "$exit_value" -gt 0
  then
    echo "--- Merge run aborted !" >&2
  else
    echo "Merge run ended with success indication." >&2
  fi
  exit "$exit_value" 
}

# Handler for rogue EXIT
cleanup_for_exit() {

  # Early assume cleanup state of traps
  trap cleanup_ignore_handler INT TERM QUIT
  trap - EXIT

  cleanup
  echo >&2
  echo "--- Merge run ended by unexpected EXIT event !" >&2
}


## Check arguments and dependencies

if test "$#" -lt 4
then
  usage
  exit 1
fi

echo >&2
echo "$(basename "$0") starting with $(expr $# - 2) ISO image files ..." >&2

dep="awk basename cat chmod cp date dirname expr fgrep grep"
dep="$dep gunzip gzip head ls md5sum mkdir mount mv rm rmdir sed"
dep="$dep sha256sum sort stat sudo umount uniq"
missing=0
for i in $dep
do
  if type "$i" >/dev/null 2>&1
  then
    dummy=dummy
  else
    echo "--- Missing a helper program: $i" >&2
    missing=1
  fi
done
if test -z "$XORRISO"
then
  XORRISO=xorriso
fi
if test "$XORRISO" = dummy
then
  if test "$missing" = 0
  then
    echo 'NOTE: Variable XORRISO is set to "dummy".' >&2
    echo '      Will not perform xorriso run but only show its arguments.' >&2
  fi
elif "$XORRISO" -no_rc -version >/dev/null 2>&1
then
  if "$XORRISO" -no_rc -help 2>/dev/null | fgrep '"replay"' >/dev/null 2>&1
  then
    dummy=dummy
  else
    echo '--- Help text of xorriso program '"$XORRISO"' lacks word "replay".' \
         >&2
    echo "--- It will fail when trying to make the new ISO bootable." >&2
    echo "    Consider to get and compile GNU xorriso from" >&2
    echo "      https://www.gnu.org/software/xorriso" >&2
    echo "    and to export variable XORRISO with the binary's path." >&2
    echo >&2
    missing=1
  fi
else
  echo "--- Test run of xorriso program failed: $XORRISO -no_rc -version" >&2
  missing=1
fi
if test "$missing" = 1
then
  echo "--- Merge run aborted !" >&2
  exit 5
fi

if test -z "$XORRISO_DD_TARGET_PATH"
then
  XORRISO_DD_TARGET=xorriso-dd-target
else
  XORRISO_DD_TARGET="$XORRISO_DD_TARGET_PATH"/xorriso-dd-target
fi

existing_tempfiles=
for i in merged_dists merged_md5sum.txt merged_README.txt temp_file
do
  if test -e "$i"
  then
    existing_tempfiles="$existing_tempfiles $i"
  else
    TEMPFILE_LIST="$TEMPFILE_LIST $i"
  fi
done
if test -n "$existing_tempfiles"
then
  echo "--- Some temporary files for this script already exist:" >&2
  echo "---   $existing_tempfiles" >&2
  echo "--- Will not overwrite them." >&2
  echo "--- Merge run aborted !" >&2
  exit 1
fi


# +++ From here on: Always call cleanup_and_end to perform exit +++


trap cleanup_and_end INT TERM QUIT
trap cleanup_for_exit EXIT

RESULT_ISO="$1"
check_single_word "$1" "result_iso" || cleanup_and_end 1
xorriso_rm_result_iso=1
if test "$RESULT_ISO" = xorriso-dd-target-plug-test
then
  # Get device name from xorriso-dd-target
  echo >&2
  echo \
    'Special result_iso path "xorriso-dd-target-plug-test" causes a run of' >&2
  echo "  $XORRISO_DD_TARGET -with_sudo -plug_test" >&2
  echo "to determine the USB stick or memory card:" >&2
  echo "---------------------------------------------------------" >&2
  plugged="$("$XORRISO_DD_TARGET" -with_sudo -plug_test)"
  ret=$?
  echo "$plugged" >&2
  echo "---------------------------------------------------------" >&2
  advice=NO
  if test "$ret" = 0 && test -n "$plugged"
  then
    name="$(echo "$plugged" | awk '{print $1}')"
    advice="$(echo "$plugged" | awk '{print $3}')"
  fi
  if test "$advice" = YES
  then
    if confirm_xdt_device /dev/"$name"
    then
      xorriso_rm_result_iso=0
      RESULT_ISO=/dev/"$name"
      echo "Actual target device path is '${RESULT_ISO}'" >&2
    else
      cleanup_and_end 1
    fi
  else
    echo "--- No suitable device was determined." >&2
    cleanup_and_end 1
  fi
elif echo "$RESULT_ISO" | grep '^mmc:' >/dev/null
then
  xorriso_rm_result_iso=0
  # Let xorriso judge over optical drives
  # (The /dev/sr patterns are Linux specific. The mmc: prefix is not.)
  if test "$XORRISO" = dummy
  then
    echo "Dummy mode: Accepting '${RESULT_ISO}' as optical drive." >&2
    XORRISO_BLANK_CMD="-blank as_needed"
  elif "$XORRISO" -outdev "$RESULT_ISO" 2>/dev/null
  then
    echo "${XORRISO} accepts '${RESULT_ISO}' as optical drive." >&2
    XORRISO_BLANK_CMD="-blank as_needed"
  else
    echo "--- $XORRISO refuses to accept '${RESULT_ISO}' as optical drive." >&2
    cleanup_and_end 1
  fi
elif echo "$RESULT_ISO" | grep '^stdio:/dev/' >/dev/null
then
  xorriso_rm_result_iso=0
  echo \
"WARNING: User insists in using '${RESULT_ISO}' without further preparations" \
       >&2
  echo \
  "         or safety checks. The device might need blanking by xorriso" >&2
  echo \
 "         before it will be willing to write to it. Permissions might not" >&2
  echo \
  "         suffice. Be cautious when removing such obstacles. They might be" \
       >&2
  echo \
  "         there for a good reason." >&2
  echo \
 "If you agree to xorriso trying to overwrite the content of '${RESULT_ISO}'" \
             >&2
  echo "then enter 'yes' :" >&2
  answer=
  read answer
  if test "$answer" = yes
  then
    echo \
  "Will try to overwrite the device content without sudo or pseudo-blanking." \
        >&2
    XORRISO_STDIO_SYNC=16m
  else
    echo "--- Answer was not 'yes'. Will not write to device." >&2
    cleanup_and_end 1
  fi
elif test -e "$RESULT_ISO"
then
  if test "$(dirname $RESULT_ISO)" = "/dev"
  then
    echo >&2
    echo "The result_iso path '${RESULT_ISO}' is an existing File in /dev." >&2
    echo "Will only write to it if $XORRISO_DD_TARGET agrees." >&2
    if type "$XORRISO_DD_TARGET"
    then
      echo >&2
      echo \
       "Performing: $XORRISO_DD_TARGET -with_sudo $(basename $RESULT_ISO)" >&2
      echo "---------------------------------------------------------" >&2
      "$XORRISO_DD_TARGET" -with_sudo "$(basename $RESULT_ISO)"
      ret=$?
      echo "---------------------------------------------------------" >&2
      if test "$ret" = 0
      then
        if confirm_xdt_device "$RESULT_ISO"
        then
          dummy=dummy
        else
          cleanup_and_end 1
        fi
      else
        echo \
       "--- $XORRISO_DD_TARGET refuses to accept this device as result_iso" >&2
        cleanup_and_end 1
      fi
    else
      echo "--- Helper program $XORRISO_DD_TARGET is missing." >&2
      echo \
      "--- Cannot evaluate suitability of '${RESULT_ISO}' as result_iso path" \
           >&2
      cleanup_and_end 1
    fi
  fi
  if test -z "$XORRISO_STDIO_DEV"
  then
    echo "--- A file '${RESULT_ISO}' is already existing." >&2
    echo "--- Will not overwrite it by the resulting ISO image." >&2
    cleanup_and_end 1
  fi
fi

MOUNT_TEMPLATE="$2"
check_single_word "$2" "mount_template" || cleanup_and_end 1
x=$(dirname "$MOUNT_TEMPLATE")
mount_parent_to_make=
mount_parent_made=
if test -d "$x"
then
  dummy=dummy
else
  # The parent will be temporarily created only if it is a plain leaf name
  if echo " $x" | fgrep / >/dev/null
  then
    echo "--- The parent directory '${x}' of mount_template does not exist" \
         >&2
    echo "--- and contains a '/' character. Will not create it." >&2
    cleanup_and_end 1
  fi
  mount_parent_to_make="$x"
fi

shift 2
ISO_LIST=
mount_count=0
for i in "$@"
do
  mount_count=$(expr $mount_count + 1)

  check_single_word "$i" "iso$mount_count" || cleanup_and_end 1

  if test "$i" = "$RESULT_ISO"
  then
    echo "--- Arguments result_iso and iso$mount_count are equal:" >&2
    echo "--- '${i}'" >&2
    cleanup_and_end 1
  fi

  if echo "$ISO_LIST" | fgrep " $i " >/dev/null
  then
    echo "--- Duplicate file path given as argument iso$mount_count :" >&2
    echo "---   '${i}'" >&2
    cleanup_and_end 1
  fi

  x="${MOUNT_TEMPLATE}$mount_count"
  if test -d "$x"
  then
    dummy=dummy
  elif test -e "$x"
  then
    echo "--- A file '${x}' is already existing and not a directory." >&2
    echo "--- Cannot mount iso$mount_count ('${i}')" >&2
    cleanup_and_end 1
  fi

  ISO_LIST="$ISO_LIST $i "
  if test -z "$iso_1"
  then
    iso_1="$i"
  fi
done
echo "Arguments look acceptable." >&2


## Mount and copy out the files which need to be changed
echo >&2
echo "Mounting ISO images if not yet mounted ..." >&2

if test -n "$mount_parent_to_make"
then
  if mkdir "$mount_parent_to_make"
  then
    mount_parent_made="$mount_parent_to_make"
  else
    echo "--- Cannot create the parent directory of '${MOUNT_TEMPLATE}'." >&2
    cleanup_and_end 1
  fi
fi

mount_count=0
for i in $ISO_LIST
do
  mount_count=$(expr $mount_count + 1)
  mount_point="${MOUNT_TEMPLATE}$mount_count"
  if test -d "$mount_point"
  then
    dummy=dummy
  else
    if mkdir "$mount_point"
    then
      RMDIR_LIST="$RMDIR_LIST $mount_point "
    else
      echo "--- Could not create directory  '${mount_point}'." >&2
      echo "--- Cannot mount iso$mount_count ('${i}')" >&2
      cleanup_and_end 3
    fi
  fi

  do_mount=1
  if echo "$mount_point" | grep '^/' >/dev/null
  then
    x=$(mount | grep " $mount_point " | awk '{print $1}')
  elif echo "$mount_point" | grep '^./' >/dev/null
  then
    m=$(echo "$mount_point" | sed -e 's/^\.\///')
    x=$(mount | grep " $(pwd)/$m" | awk '{print $1}')
  else
    x=$(mount | grep " $(pwd)/$mount_point " | awk '{print $1}')
  fi
  if test -n "$x"
  then
    i1=$(ls -i "$x" | awk '{print $1}')
    i2=$(ls -i "$i" | awk '{print $1}')
    if test "$i1" = "$i2"
    then
      do_mount=0
      echo "Note: Found $i already mounted at $mount_point"
    fi
  fi
  if test "$do_mount" = 1
  then
    echo "Note: sudo mount $i $mount_point"
    if sudo mount "$i" "$mount_point"
    then
      dummy=dummy
    else
      echo "--- Could not mount '${i}' at '${mount_point}'." >&2
      cleanup_and_end 3
    fi
    UMOUNT_LIST="$UMOUNT_LIST $mount_point"
  fi

  MOUNT_LIST="$MOUNT_LIST $mount_point"
done

if test -n "$mount_parent_made"
then
  RMDIR_LIST="$RMDIR_LIST $mount_parent_made "
fi

echo "Done." >&2

echo >&2
echo "Copying dists directory and md5sum.txt from first ISO ..." >&2

mount_point_1="$MOUNT_TEMPLATE"1
if test -d "$mount_point_1/dists"
then
  echo "Copying: $mount_point_1/dists to merged_dists" >&2
  if cp -a "$mount_point_1/dists" merged_dists
  then
    if chmod -R u+w merged_dists
    then
      dummy=dummy
    else
      echo "--- Could not chmod -R u+w merged_dists" >&2
      cleanup_and_end 3
    fi
  else
    echo "--- Could not copy /dists directory from first ISO." >&2
    cleanup_and_end 3
  fi
else
  echo "--- First ISO does not contain a /dists directory." >&2
  cleanup_and_end 2
fi
echo "Copying: $mount_point_1/md5sum.txt to merged_md5sum.txt" >&2
if cp -a "$mount_point_1/md5sum.txt" merged_md5sum.txt
then
  if chmod u+w merged_md5sum.txt
  then
    dummy=dummy
  else
    echo "--- Could not chmod u+w merged_md5sum.txt" >&2
    cleanup_and_end 3
  fi
else
  echo "--- Could not copy /md5sum.txt from first ISO." >&2
  cleanup_and_end 3
fi
echo "Done." >&2


## Helper functions

# Put out the list of checksummed paths as listed in /dists/$dist/Release
extract_checksum_paths() {
  mode=0
  cat "$1" | \
  while true
  do
    read -r x || break
    if test "$x" = "MD5Sum:" || test "$x" = "SHA1:" \
       || test "$x" = "SHA256:" || test "$x" = "SHA512:"
    then
      if test "$mode" = 0
      then
        mode=1
      elif test "$mode" = 1
      then
  break
      fi
    elif test "$mode" = 1
    then
      echo "$x"
    fi
  done
}

# Put out the part before the first checksum field
extract_release_head() {
  cat "$1" | \
  while true
  do
    read -r x || break
    if test "$x" = "MD5Sum:" || test "$x" = "SHA1:" \
       || test "$x" = "SHA256:" || test "$x" = "SHA512:"
    then
  break
    fi
    echo "$x"
  done
}

# Decide whether to re-compute MD5 and whether to put out the md5sum line
# Parameters: prev_path prev_md5
check_and_put_out_md5_grep() {
  if echo "$prev_path" | grep '^\.\/firmware\/' >/dev/null 2>&1
  then
    if test "$was_multiple" = 1 && test "$multi_md5_differs" = 1
    then
      # There is the risk that the surviving file does not match prev_md5.
      # Better omit it.
      dummy=dummy
    else
      echo "$prev_md5  $prev_path"
    fi
    return 0
  fi

  if echo "$prev_path" | grep '^\.\/dists\/' >/dev/null 2>&1
  then
    md5_path="$(echo -n "$prev_path" | sed -e 's/^\.\//merged_/')"
    prev_md5="$(md5sum <"$md5_path" | awk '{print $1}')"
  elif test "$prev_path" = ./README.txt
  then
    prev_md5="$(md5sum <merged_README.txt | awk '{print $1}')"
  elif test "$was_multiple" = 1
  then
    # Unchanged file was taken from iso1
    # md5sum.txt lines might be permuted though. So compute freshly
    prev_md5="$(md5sum <"$mount_point_1"/"$prev_path" | awk '{print $1}')"
  fi
  if test -z "$prev_md5"
  then
    prev_md5="-"
    return 1
  else
    echo "$prev_md5  $prev_path"
  fi

  return 0
}

# Filter lines from md5sum.txt by removing duplicates and recomputing
# the MD5 if needed and possible.
polish_md5sum_txt() {
  prev_path=
  prev_md5=
  was_multiple=0
  multi_md5_differs=0

  while read md5 path
  do
    if test "$path" = "$prev_path"
    then
      if test "$md5" = "$prev_md5"
      then
        dummy=dummy
      else
        multi_md5_differs=1
      fi
      was_multiple=1
  continue
    fi
    if test -n "$prev_path"
    then
      check_and_put_out_md5_grep
    fi

    prev_path="$path"
    prev_md5="$md5"
    was_multiple=0
    multi_md5_differs=0
  done

  if test -n "$prev_path"
  then
    check_and_put_out_md5_grep
  fi

  return 0
}


## Determine which Debian release is on iso1
echo >&2
echo "Determining Debian release in first ISO ..." >&2

dist=
for i in "$mount_point_1"/dists/*
do
  if check_single_word "$i" "dists"
  then
    dummy=dummy
  else
continue
  fi
  if test -d "$i"
  then
    if test -L "$i"
    then
continue
    fi
    test -n "$dist" && dist="$dist "
    dist="${dist}$(basename "$i")"
  fi
done
if test -z "$dist"
then
  if test -z "$MERGE_FOR_DIST"
  then
    echo "--- Cannot determine Debian release from directories in /dists" >&2
    echo "--- (You may provide the release name as variable MERGE_FOR_DIST)" >&2
    echo >&2
    cleanup_and_end 2
  fi
elif test "$(echo "$dist" | wc -w)" -gt 1
then
  if test -z "$MERGE_FOR_DIST"
  then
    echo "--- More than one Debian release found in /dists: $dist" >&2
    echo "--- (You may provide the release name as variable MERGE_FOR_DIST)" >&2
    echo >&2
    cleanup_and_end 2
  fi
fi
if test -n "$MERGE_FOR_DIST"
then
  echo "Note: Overriding release name '${dist}' by '${MERGE_FOR_DIST}'" >&2
  dist="$MERGE_FOR_DIST"
fi
if test -d "$mount_point_1"/dists/"$dist"
then
  echo "Will work along $mount_point_1"/dists/"$dist"/Release >&2
else
  echo "--- Cannot find directory $mount_point_1"/dists/"$dist" >&2
  cleanup_and_end 2
fi
for i in $MOUNT_LIST
do
  if test -e "$i"/dists/"$dist"/Release
  then
    dummy=dummy
  else
    echo "--- Cannot find file $i"/dists/"$dist"/Release >&2
    echo "--- All participating ISOs must be installation ISOs of the same release." >&2
    cleanup_and_end 2
  fi
done


## Prepend info to /README.txt
echo >&2
echo "Composing new /README.txt ..." >&2

if test -z "$MERGE_DATE"
then
  MERGE_DATE=$(date +'%Y%m%d-%H:%M')
fi
printf 'Result of a run of %s at %s\r\n' \
       "$(basename "$0")" "$MERGE_DATE" >temp_file
printf 'Package pools and Packages lists were merged.\r\n' >>temp_file
printf 'The other files stem from the first input ISO.\r\n' >>temp_file
printf '\r\n' >>temp_file
mount_count=0
for i in $ISO_LIST
do
  mount_count=$(expr $mount_count + 1)
  mount_point="${MOUNT_TEMPLATE}$mount_count"
  printf 'Input ISO: %s\r\n' "$i" >>temp_file
  head -2 "$mount_point"/README.txt >>temp_file
  printf '\r\n' >>temp_file
done
printf '%s%s\r\n' " --------------------------------------" \
                  "----------------------------------------" >>temp_file
printf '\r\n' >>temp_file
cat "$mount_point_1"/README.txt >>temp_file

mv temp_file merged_README.txt
echo "Done." >&2


## Merge package description files
echo >&2
echo "Merging package description files ..." >&2

# Determine the files which are mentioned with checksum in main Release files
path_list=$(for i in $MOUNT_LIST
            do
              extract_checksum_paths "$i"/dists/"$dist"/Release
            done | awk '{print $3}' | sort | uniq )

# Merge .gz files (Release should not be merged. Unclear what others need.)
for i in $path_list
do
  if echo "$i" | grep -v '.gz$' >/dev/null
  then
continue
  fi

  echo "Merging: merged_dists/${dist}/$i" >&2

  # make missing directories in merged_dists/"$dist"/
  if test -e "$(dirname merged_dists/"$dist"/"$i")"
  then
    dummy=dummy
  else
    if mkdir -p "$(dirname merged_dists/"$dist"/"$i")"
    then
      dummy=dummy
    else
      echo "--- Cannot create directory $(dirname merged_dists/"$dist"/"$i")" >&2
      cleanup_and_end 3
    fi
  fi

  test -e temp_file && rm temp_file
  for mount_point in $MOUNT_LIST
  do
    if test -e "$mount_point"/dists/"$dist"/"$i"
    then
      if test -e temp_file
      then
        if test -n "$(tail -1 temp_file)"
        then
          echo >>temp_file
        fi
      fi
      gunzip <"$mount_point"/dists/"$dist"/"$i" >>temp_file
    fi
  done
  if test -e temp_file
  then
    gzip <temp_file >merged_dists/"$dist"/"$i"
    rm temp_file
  fi
  
done
echo "Done." >&2


## Update dists/"$dist"/Release
echo >&2
echo "Updating dists/${dist}/Release ..." >&2

extract_release_head merged_dists/"$dist"/Release >temp_file

# Re-create  "MD5Sum:", "SHA1:", "SHA256:", "SHA512:" sections
for cmd in md5sum sha1sum sha256sum sha512sum
do
  if type "$cmd" >/dev/null
  then
    case "$cmd" in
      md5sum)    echo "MD5Sum:" ;;
      sha1sum)   echo "SHA1:" ;;
      sha256sum) echo "SHA256:" ;;
      sha512sum) echo "SHA512:" ;;
    esac
    for i in $path_list
    do
      file=merged_dists/"$dist"/"$i"
      if test -e "$file"
      then
        sum=$("$cmd" "$file" | awk '{print $1}')
        size=$(stat -c '%s' "$file")
      elif test -e "$file".gz
      then
        sum=$(gunzip <"$file".gz | "$cmd" | awk '{print $1}')
        size=$(gunzip <"$file".gz | wc -c)
      else
    continue
      fi
      list_path=$(echo "$file" | sed -e 's/^merged_dists\/'"$dist"'\///')
      printf ' %s %8d %s\n' "$sum" "$size" "$list_path" 
    done 
  fi
done >>temp_file

mv temp_file merged_dists/"$dist"/Release
echo "Done." >&2


## Merge md5sum.txt and compute the MD5s which might have changed
echo >&2
echo "Merging md5sum.txt files ..." >&2

for i in $MOUNT_LIST
do
  cat "$i"/md5sum.txt
done | sort -k 2 >merged_md5sum.txt

# ./pool files are surely unchanged. Others need some more examination.
( fgrep ' ./pool/' <merged_md5sum.txt | uniq 
  fgrep -v ' ./pool/' <merged_md5sum.txt | polish_md5sum_txt ) \
| sort -k 2 >temp_file

mv temp_file merged_md5sum.txt
echo "Done." >&2


## Produce the new ISO image
echo >&2
echo "Producing result ISO image ..." >&2

# Create file with list of /pool and /firmware -map commands for all but the
# first ISO
for mount_point in $MOUNT_LIST
do
  if test "$mount_point" = "$mount_point_1"
  then
    echo "Planned as imported package pool     : ${mount_point}/pool" >&2
  else
    echo "Planned for merging into package pool: ${mount_point}/pool" >&2
    echo " -map ${mount_point}/pool /pool" >>temp_file
  fi
done
for mount_point in $MOUNT_LIST
do
  if test "$mount_point" = "$mount_point_1"
  then
    if test -d "${mount_point}/firmware"
    then
      echo \
    "Planned as imported /firmware        : ${mount_point}/firmware" >&2
    fi
  else
    if test -d "${mount_point}/firmware"
    then
      echo \
    "Planned for merging into /firmware   : ${mount_point}/firmware" >&2
      echo " -map ${mount_point}/firmware /firmware" >>temp_file
    fi
  fi
done

if test "$XORRISO" = dummy
then
  echo "Planned xorriso commands in temp_file:" >&2
  cat temp_file
  echo >&2
  if test -n "$XORRISO_SUDO"
  then
    echo "NOTE: xorriso would run under control of '${XORRISO_SUDO}'" >&2
    XORRISO_SUDO=
  fi
  echo 'NOTE: Variable XORRISO is set to "dummy".' >&2
  echo '      Will not perform xorriso run but only show its arguments:' >&2
  XORRISO=echo
else
  echo "Running as xorriso program: $XORRISO" >&2
fi
echo >&2

# Mark the result path for possible removal by cleanup
if test -z "$XORRISO_STDIO_DEV"
then
  EMERGING_ISO="$RESULT_ISO"
else
  EMERGING_ISO=
fi
if $XORRISO_SUDO "$XORRISO" \
  -no_rc \
  -indev "$iso_1" \
  -outdev "$XORRISO_STDIO_DEV""$RESULT_ISO" \
  $XORRISO_BLANK_CMD \
  -options_from_file temp_file \
  -map merged_dists /dists \
  -map merged_md5sum.txt /md5sum.txt \
  -map merged_README.txt /README.txt \
  -chown_r 0 /dists /md5sum.txt /README.txt -- \
  -chgrp_r 0 /dists /md5sum.txt /README.txt -- \
  -chmod_r a-w /dists /md5sum.txt -- \
  -chmod_r a=r /README.txt -- \
  -boot_image any replay \
  -fs 16m \
  -stdio_sync "$XORRISO_STDIO_SYNC" \
  -padding included \
  -stream_recording on \
  -compliance no_emul_toc
then
  # Revoke mark for possible removal by cleanup
  EMERGING_ISO=

  test "$XORRISO" = echo || \
    echo "Run of xorriso program ended without error indication." >&2
else
  echo "--- Run of xorriso program ended with error indication." >&2
  cleanup_and_end 4
fi


## Finish

cleanup_and_end 0

