1#!/bin/sh
2# relative_path.sh
3#
4# Copyright (C) 2001 - 2007 Free Software Foundation, Inc.
5#
6# Author: Nicola Pero <n.pero@mi.flashnet.it>
7# Date: April 2001, January 2007
8#
9# This file is part of the GNUstep Makefile Package.
10#
11# This library is free software; you can redistribute it and/or
12# modify it under the terms of the GNU General Public License
13# as published by the Free Software Foundation; either version 3
14# of the License, or (at your option) any later version.
15#
16# You should have received a copy of the GNU General Public
17# License along with this library; see the file COPYING.
18# If not, write to the Free Software Foundation,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21# This script gets two paths as argument - and outputs a relative path
22# which, when appended to the first one, gives the second one ... more
23# precisely, the path of minimum length with this property.
24#
25# A third optional parameter controls the type of output; if it's set
26# to 'strict' it outputs "strict" relative paths that always start
27# with the exact sequence of characters './'.  If set to 'short' it
28# outputs "short" relative paths that might start with './' or with
29# '../'.  Here are examples:
30#
31# strict: ./
32# short: ./
33#
34# strict: ./../System
35# short: ../System
36#
37# strict: ./System
38# short: ./System
39#
40# Inside shell scripts (eg, in framework.make) we use the 'short' mode
41# because it prevents ugly unnecessary path fragments to get into all
42# paths.  Inside the configuration system we traditionally use the
43# 'strict' mode because NSPathUtilities detects relative paths by
44# checking that they start with './'.  The 'short' mode might
45# become the one used for the configuration system in the future if
46# NSPathUtilities learns to detect that '../' also starts a relative
47# path.
48#
49# If no this parameter is provided, 'strict' is assumed for backwards
50# compatibility (even if gnustep-make v1 used to default to 'short').
51# This might change in the future, so if you are depending on a
52# specific behaviour, it's important that you specify the type of
53# output you want.
54
55
56#
57# <NB: the paths must be absolute.>
58#
59# for example,
60#
61# $GNUSTEP_MAKEFILES/print_relative_path.sh /usr/GNUstep/Local /usr/GNUstep/System short
62#
63# returns ../System (and not ../../GNUstep/System which is not the minimum).
64#
65# This is needed by `ln -s' to properly create symlinks between
66# directories which are related ... but we don't know how.  We only
67# need this for frameworks, which are particularly complex and
68# delicate.  For example, to create the link
69#
70# /usr/GNUstep/System/Library/Libraries/ix86/linux-gnu/gnu-gnu-gnu/libnicola.so
71#   --> ../../../../Frameworks/nicola.framework/Versions/Current/ix86/linux-gnu/gnu-gnu-gnu/libnicola.so
72#
73# (and where the paths are actually computed by make variables which
74# might depend on variables in user makefiles outside our control, so
75# it's not obvious what the relationship is between the two paths, and
76# you only have the absolute paths) we do -
77#
78# cd /usr/GNUstep/System/Library/Libraries/ix86/linux-gnu/gnu-gnu-gnu/
79# $(LN_S) `$(RELATIVE_PATH_SCRIPT) /usr/GNUstep/System/Frameworks/nicola.framework/Versions/Current/ix86/linux-gnu/gnu-gnu-gnu/libnicola.so /usr/GNUstep/System/Library/Libraries/ix86/linux-gnu/gnu-gnu-gnu/ short` libnicola.so
80#
81# which creates the link.  We need to use the minimum path because
82# that is the most relocatable possible path.  I consider all this a
83# trick and a hack and recommend to use libraries and bundles instead
84# of frameworks, since libraries and bundles are much more portable
85# and stable, anyway here we are.
86#
87# This script is also used to create relative paths in the
88# configuration system for cases where the location of things is
89# relative to the location of something (eg, base library).
90# Unfortunately in that case because of limitations in gnustep-base's
91# NSPathUtilities, we have to always output a './' at the beginning of
92# the result so that gnustep-base recognizes the result as a relative
93# path.  This means we use the 'strict' output in that case.
94
95# mode=strict means we always need to start our output with './'.
96# mode=short means we always start our output with '../' or './'.
97mode=strict
98
99if [ "$#" != 2 ]; then
100  if [ "$#" != 3 ]; then
101    exit 1
102  else
103    mode="$3"
104  fi
105fi
106
107a="$1";
108b="$2";
109
110if [ "$a" = "" ]; then
111  exit 1
112fi
113
114if [ "$b" = "" ]; then
115  exit 1
116fi
117
118
119#
120# Our first argument is a path like /xxx/yyy/zzz/ccc/ttt
121# Our second argument is a path like /xxx/yyy/kkk/nnn/ppp
122#
123
124# Step zero is normalizing the paths by removing any /./ component
125# inside the given paths (these components can occur for example when
126# enable-flattened is used).
127tmp_IFS="$IFS"
128IFS=/
129
130# Normalize a by removing any '.' path component.
131normalized_a=""
132for component in $a; do
133  if [ -n "$component" ]; then
134    if [ "$component" != "." ]; then
135      normalized_a="$normalized_a/$component"
136    fi
137  fi
138done
139a="$normalized_a"
140
141# Normalize b by removing any '.' path component.
142normalized_b=""
143for component in $b; do
144  if [ -n "$component" ]; then
145    if [ "$component" != "." ]; then
146      normalized_b="$normalized_b/$component"
147    fi
148  fi
149done
150b="$normalized_b"
151
152IFS="$tmp_IFS"
153
154
155
156# Step one: we first want to remove the common root -- we want to get
157# into having /zzz/ccc/tt and /kkk/nnn/ppp.
158
159# We first try to match as much as possible between the first and the second
160# So we loop on the fields in the second.  The common root must not contain
161# empty path components (/./) for this to work, but we have already filtered
162# those out at step zero.
163tmp_IFS="$IFS"
164IFS=/
165partial_b=""
166partial_match=""
167for component in $b; do
168  if [ -n "$component" ]; then
169    partial_b="$partial_b/$component"
170    case "$a" in
171      "$partial_b"*) partial_match="$partial_b";;
172      *) break;;
173    esac
174  fi
175done
176IFS="$tmp_IFS"
177
178if [ "$partial_match" != "" ]; then
179  # Now partial_match is the substring which matches (/xxx/yyy/) in the
180  # example.  Remove it from both a and b.
181  a=`echo $a | sed -e "s#$partial_match##"`
182  b=`echo $b | sed -e "s#$partial_match##"`
183fi
184
185# Ok - now ready to build the result
186result="."
187
188# Special note - if a is now empty, the second directory was a
189# subdirectory of the first; we will end up outputting ./$b
190
191# Now add as many ../ as there are components in a
192tmp_IFS="$IFS"
193IFS=/
194for component in $a; do
195  if [ -n "$component" -a "$component" != "." ]; then
196    if [ "$mode" = "strict" ]; then
197      # In strict mode, ./../../xxx is required
198      result="$result/.."
199    else
200      # In short mode, it's not, we prefer ../../xxx
201      if [ "$result" = "." ]; then
202        result=".."
203      else
204        result="$result/.."
205      fi
206    fi
207  fi
208done
209IFS="$tmp_IFS"
210
211# Then, append b
212if [ -n "$result" ]; then
213  result="$result$b"
214else
215  result="$b"
216fi
217
218if [ "$mode" = "strict" ]; then
219  # Make sure the result always starts with './' in strict mode
220  if [ "$result" = "." ]; then
221    result="./"
222  fi
223fi
224
225echo "$result"
226