This cannot be done (only) in pure XPath 1.0.
XPath 2.0 solution:
if(not($vStart intersect $vTarget/ancestor::*)) then () else for $vPath in string-join ((for $x in $vTarget /ancestor-or-self::*[. >> $vStart] /concat(name(.), for $n in name(.), $cn in count(../*[name(.) eq $n]) return if($cn ge 2) then concat('[', count((preceding-sibling::* [name() eq $n]) +1, ']') else (), '/' ) return $x), '' ) return string-join((concat(name($vStart), '/'),$vPath), '')
When this XPath 2.0 expression is evaluated against the following XML document:
<table> <tr> <td><b>11</b></td> <td><i>12</i></td> </tr> <tr> <td><p><b>21</b></p></td> <td><p><b>221</b></p><p><b><i>222</i></b></p></td> </tr> <tr> <td><b>31</b></td> <td><i>32</i></td> </tr> </table>
and if the two parameters are defined as:
<xsl:variable name="vStart" select="/*"/> <xsl:variable name="vTarget" select="/*/tr[2]/td[2]/p[2]/b/i"/>
then the result of the evaluation of the XPath 2.0 expression above is:
table/tr[2]/td[2]/p[2]/b/i/