libdpkg: Add new file_getcwd() function
[dpkg/guillem.git] / src / dpkg-realpath.sh
blob84438b49ee92187ec9d32e1e8cc2c36473a74841
1 #!/bin/sh
3 # Copyright © 2020 Helmut Grohne <helmut@subdivi.de>
4 # Copyright © 2020 Guillem Jover <guillem@debian.org>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <https://www.gnu.org/licenses/>.
19 set -e
21 PROGNAME=$(basename "$0")
22 version="unknown"
23 EOL="\n"
25 PKGDATADIR_DEFAULT=src
26 PKGDATADIR="${DPKG_DATADIR:-$PKGDATADIR_DEFAULT}"
28 # shellcheck source=src/sh/dpkg-error.sh
29 . "$PKGDATADIR/sh/dpkg-error.sh"
31 show_version()
33 cat <<END
34 Debian $PROGNAME version $version.
36 This is free software; see the GNU General Public License version 2 or
37 later for copying conditions. There is NO warranty.
38 END
41 show_usage()
43 cat <<END
44 Usage: $PROGNAME [<option>...] <pathname>
46 Options:
47 -z, --zero end output line with NUL, not newline.
48 --instdir <directory> set the root directory.
49 --root <directory> set the root directory.
50 --version show the version.
51 -?, --help show this help message.
52 END
55 canonicalize() {
56 local src="$1"
57 local root="$DPKG_ROOT"
58 local loop=0
59 local result="$root"
60 local dst
62 # Check whether the path is relative and make it absolute otherwise.
63 if [ "$src" = "${src#/}" ]; then
64 src="$(pwd)/$src"
65 src="${src#"$root"}"
68 # Remove prefixed slashes.
69 while [ "$src" != "${src#/}" ]; do
70 src=${src#/}
71 done
72 while [ -n "$src" ]; do
73 # Get the first directory component.
74 prefix=${src%%/*}
75 # Remove the first directory component from src.
76 src=${src#"$prefix"}
77 # Remove prefixed slashes.
78 while [ "$src" != "${src#/}" ]; do
79 src=${src#/}
80 done
81 # Resolve the first directory component.
82 if [ "$prefix" = . ]; then
83 # Ignore, stay at the same directory.
85 elif [ "$prefix" = .. ]; then
86 # Go up one directory.
87 result=${result%/*}
88 if [ -n "$root" ] && [ "${result#"$root"}" = "$result" ]; then
89 result="$root"
91 elif [ -h "$result/$prefix" ]; then
92 loop=$((loop + 1))
93 if [ "$loop" -gt 25 ]; then
94 error "too many levels of symbolic links"
96 # Resolve the symlink within $result.
97 dst=$(readlink "$result/$prefix")
98 case "$dst" in
99 /*)
100 # Absolute pathname, reset result back to $root.
101 result=$root
102 src="$dst${src:+/$src}"
103 # Remove prefixed slashes.
104 while [ "$src" != "${src#/}" ]; do
105 src=${src#/}
106 done
109 # Relative pathname.
110 src="$dst${src:+/$src}"
112 esac
113 else
114 # Otherwise append the prefix.
115 result="$result/$prefix"
117 done
118 # We are done, print the resolved pathname, w/o $root.
119 result="${result#"$root"}"
120 printf "%s$EOL" "${result:-/}"
123 setup_colors
125 DPKG_ROOT="${DPKG_ROOT:-}"
126 export DPKG_ROOT
128 while [ $# -ne 0 ]; do
129 case "$1" in
130 -z|--zero)
131 EOL="\0"
133 --instdir|--root)
134 shift
135 DPKG_ROOT=$1
137 --instdir=*)
138 DPKG_ROOT="${1#--instdir=}"
140 --root=*)
141 DPKG_ROOT="${1#--root=}"
143 --version)
144 show_version
145 exit 0
147 --help|-\?)
148 show_usage
149 exit 0
152 shift
153 pathname="$1"
156 badusage "unknown option: $1"
159 pathname="$1"
161 esac
162 shift
163 done
165 # Normalize root directory.
166 DPKG_ROOT="${DPKG_ROOT:+$(realpath "$DPKG_ROOT")}"
167 # Remove default root dir.
168 if [ "$DPKG_ROOT" = "/" ]; then
169 DPKG_ROOT=""
172 [ -n "$pathname" ] || badusage "missing pathname"
173 if [ "${pathname#"$DPKG_ROOT"}" != "$pathname" ]; then
174 error "link '$pathname' includes root prefix '$DPKG_ROOT'"
177 canonicalize "$pathname"
179 exit 0