#!/bin/sh
#
# Copyright © 2020 Helmut Grohne <helmut@subdivi.de>
# Copyright © 2020 Guillem Jover <guillem@debian.org>
#
# 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 2 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.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

set -e

PROGNAME=$(basename "$0")
version="1.21.22"
EOL="\n"

PKGDATADIR_DEFAULT='/usr/share/dpkg'
PKGDATADIR="${DPKG_DATADIR:-$PKGDATADIR_DEFAULT}"

# shellcheck source=src/sh/dpkg-error.sh
. "$PKGDATADIR/sh/dpkg-error.sh"

show_version()
{
  cat <<END
Debian $PROGNAME version $version.

This is free software; see the GNU General Public License version 2 or
later for copying conditions. There is NO warranty.
END
}

show_usage()
{
  cat <<END
Usage: $PROGNAME [<option>...] <pathname>

Options:
  -z, --zero                   end output line with NUL, not newline.
      --instdir <directory>    set the root directory.
      --root <directory>       set the root directory.
      --version                show the version.
  -?, --help                   show this help message.
END
}

canonicalize() {
  local src="$1"
  local root="$DPKG_ROOT"
  local loop=0
  local result="$root"
  local dst

  # Check whether the path is relative and make it absolute otherwise.
  if [ "$src" = "${src#/}" ]; then
    src="$(pwd)/$src"
    src="${src#"$root"}"
  fi

  # Remove prefixed slashes.
  while [ "$src" != "${src#/}" ]; do
     src=${src#/}
  done
  while [ -n "$src" ]; do
    # Get the first directory component.
    prefix=${src%%/*}
    # Remove the first directory component from src.
    src=${src#"$prefix"}
    # Remove prefixed slashes.
    while [ "$src" != "${src#/}" ]; do
      src=${src#/}
    done
    # Resolve the first directory component.
    if [ "$prefix" = . ]; then
      # Ignore, stay at the same directory.
      :
    elif [ "$prefix" = .. ]; then
      # Go up one directory.
      result=${result%/*}
      if [ -n "$root" ] && [ "${result#"$root"}" = "$result" ]; then
        result="$root"
      fi
    elif [ -h "$result/$prefix" ]; then
      loop=$((loop + 1))
      if [ "$loop" -gt 25 ]; then
        error "too many levels of symbolic links"
      fi
      # Resolve the symlink within $result.
      dst=$(readlink "$result/$prefix")
      case "$dst" in
      /*)
        # Absolute pathname, reset result back to $root.
        result=$root
        src="$dst${src:+/$src}"
        # Remove prefixed slashes.
        while [ "$src" != "${src#/}" ]; do
          src=${src#/}
        done
        ;;
      *)
        # Relative pathname.
        src="$dst${src:+/$src}"
        ;;
      esac
    else
      # Otherwise append the prefix.
      result="$result/$prefix"
    fi
  done
  # We are done, print the resolved pathname, w/o $root.
  result="${result#"$root"}"
  printf "%s$EOL" "${result:-/}"
}

setup_colors

DPKG_ROOT="${DPKG_ROOT:-}"
export DPKG_ROOT

while [ $# -ne 0 ]; do
  case "$1" in
  -z|--zero)
    EOL="\0"
    ;;
  --instdir|--root)
    shift
    DPKG_ROOT=$1
    ;;
  --instdir=*)
    DPKG_ROOT="${1#--instdir=}"
    ;;
  --root=*)
    DPKG_ROOT="${1#--root=}"
    ;;
  --version)
    show_version
    exit 0
    ;;
  --help|-\?)
    show_usage
    exit 0
    ;;
  --)
    shift
    pathname="$1"
    ;;
  -*)
    badusage "unknown option: $1"
    ;;
  *)
    pathname="$1"
    ;;
  esac
  shift
done

# Normalize root directory.
DPKG_ROOT="${DPKG_ROOT:+$(realpath "$DPKG_ROOT")}"
# Remove default root dir.
if [ "$DPKG_ROOT" = "/" ]; then
  DPKG_ROOT=""
fi

[ -n "$pathname" ] || badusage "missing pathname"
if [ "${pathname#"$DPKG_ROOT"}" != "$pathname" ]; then
  error "link '$pathname' includes root prefix '$DPKG_ROOT'"
fi

canonicalize "$pathname"

exit 0
