Nimbin[12]?Mirrors & Forks / jhalfs / BLFS/libs/func_dependencies

jhalfs git · trunk

jhalfs LFS build automation (mirror)

lfs mirror bash · first commit 2005-09-11 · last commit 2024-02-13 (2 years ago) · synced 3 days ago · upstream: git.linuxfromscratch.org/jhalfs

Python 78.4% Shell 19.8% Makefile 1.8%
git clone https://git.christianimmanuel.de/mirrors-forks/jhalfs.gitwget https://git.christianimmanuel.de/mirrors-forks/jhalfs/archive/jhalfs.tar.gz
BLFS/libs/func_dependencies 28.1 KB · 637 lines raw
#!/bin/bash
#

#-----------------------------------------------------------------------------#
# This is a set of (recursive) functions for manipulating a dependency graph. #
# We use algorithms and definitions from chapter 4 (mainly section 4.2) of    #
# https://algs4.cs.princeton.edu/. The graph we manipulate is the directed    #
# graph of the dependencies: nodes are packages in the BLFS book. A node A is #
# connected to a node B if package A depends on B. A topological order (rev-  #
# erted) is exactly what we want for a build order. But a topological order   #
# only exists if the graph is acyclic. We'll therefore have to remove cycles. #
# There are a number of other features we want to consider:                   #
# - edges are weighted according to the dependency requirement:               #
#   1 for required                                                            #
#   2 for recommended                                                         #
#   3 for optional                                                            #
#   4 for external                                                            #
#   We should consider only edges with weight lower or equal to that          #
#   specified by the user, but see below.                                     #
# - we do not want to build the whole book. The user requests a set of        #
#   packages, and we'd like to consider only nodes reachable from this set    #
#   using edges of weight not exceeding the specified weight.                 #
# - we do not want to rebuild packages already built. But we still have to    #
#   generate the full dependency graph, because if A depends on B, which is   #
#   already built, and B depends on C, which is not built, or needs to be     #
#   updated, then A may depends on C. We therefore have to remove already     #
#   built (and up to date) packages from the graph, but need to keep the      #
#   dependency chain.                                                         #
# - when doing the topological sort, we want to consider all the edges and    #
#   not only those not exceeding the specified weight: If a package A in the  #
#   reachable subgraph depends optionally on another package B in the same    #
#   subgraph, we want to build B before A if possible. But this means we'll   #
#   have to remove cycles for all weights.                                    #
# - dependencies have another qualifier: before, after, or first. We use      #
#   it as follows: for "after", we can build the dependency after the         #
#   package, but if a package A depends on B with an "after" qualifier, and   #
#   a package C depends on A with a "before" qualifier, C may need B to be    #
#   able to use A. So the only safe way to consider "after" qualifiers is to  #
#   consider that they are "before" deps for any parent of the packages       #
#   considered. There is an exception to that rule: if B depends on C         #
#   (possibly through a chain of several dependencies), then C should still   #
#   be built before B. For "after", the dependency has to be built both       #
#   before and after the package. So we duplicate the dependency as a         #
#   "-pass1" package, and change the graph accordingly.                       #
# We'll therefore have a 3 pass procedure. First build the set of nodes       #
# reachable from the root set. Second, remove dangling edges (those pointing  #
# to packages outside the node set), and move "after" edges to "before" edges #
# originating from the parents as well as creating the "-pass1" nodes. Third  #
# remove cycles and generate a topological sort.                              #
#                                                                             #
# Pass 1: graph generation                                                    #
# ========================                                                    #
# Data layout for pass 1                                                      #
# ----------------------                                                      #
# A node of the graph is represented by a text file <nodeName>.dep. Each edge  #
# starting from this node is represented by a line in this file. We keep      #
# those files in the same directory. We introduce a special node named root,  #
# whose edges point to the list of nodes requested by the user. Each line     #
# contains three fields:                                                      #
#  - the weight of the edge                                                   #
#  - the qualifier: "before" (b), "after" (a), or "first" (f)                 #
#  - the name of the destination of the edge (without the ".dep" extension)   #
#                                                                             #
# Recursive function "generate_subgraph"                                      #
# --------------------------------------                                      #
# This function treats a node of the graph that is not a leaf and that is     #
# seen for the first time in the DFS. The dependencies of this node are       #
# known, and stored in a .dep file. For each dependency in that file, there   #
# are three cases:                                                            #
# - the weight of the edge leading to that dependency is higher than          #
#   requested. This dependency is discarded (some information printed)        #
# - the weight of the edge is lower or equal to requested, but the node       #
#   has already been visited (the .dep file exists). Discard too (some        #
#   information printed)                                                      #
# - the weight of the edge is lower or equal to requested, and the node       #
#   has not been seen: then the dependencies of that node are generated,      #
#   and there are two cases:                                                  #
#   - the node has no dependencies: just create an empty .dep file, so        #
#     that we know the node has been visited                                  #
#   - the node has dependencies: call generate_subgraph for that node         #
#                                                                             #
# This function takes four parameters:                                        #
# - The node filename: this is the only one useful for the algorithm          #
# - The depth: number of steps starting from root (for pretty print only)     #
# - The weight of the edge leading to that node (for printing)                #
# - The qualifier (for printing)                                              #
#                                                                             #
# Pass 2: graph transformation                                                #
# ============================                                                #
# We now have three loops over nodes of the graph                             #
# Loop 1: Remove dead edges                                                   #
# -------------------------                                                   #
# Since some nodes have not been created because the edges leading to them    #
# had too high a weight, those edges have to be suppressed.                   #
# For each existing node file, we make a list of lines to remove by           #
# testing whether the destination exists. We then remove the lines.           #
# Another approach would be to make a temporary file and output only          #
# lines that should stay, then rename the file. This would save a loop.       #
# All in all it is an N*e process, where N is the number of nodes and e       #
# the average number of edges originating from a node.                        #
# Loop 2: Treat "after" edges                                                 #
# ---------------------------                                                 #
# If a node is the origin of edges qualified as "after", we want the          #
# nodes which are the destination of those edges to be built after            #
# the origin node, but before any node that depend on the origin              #
# node. For that, the general rule is to change:                              #
#             P---b--->A---a--->D                                             #
# to:                                                                         #
#             P---b--->Agroupxx---b--->A                                      #
#                             |                                               #
#                              ---b--->D                                      #
# But there is a problem if D depends on P, possibly through a chain,         #
# because we create a cycle which shouldn't exist. If this is the case,       #
# we leave A as a dependency of P:                                            #
#                             P---b--->A                                      #
#                                                                             #
#                      Agroupxx---b--->A                                      #
#                             |                                               #
#                              ---b--->D                                      #
# Doing so, it may happen that Agroupxx has no parent. We then add            #
# Agroupxx as a dependency of root. The problem with this algorithm is        #
# the search for paths from D to A, which may be exponential in the           #
# number of nodes in the graph.                                               #
#                                                                             #
# Loop 3: Add -pass1 nodes                                                    #
# ------------------------                                                    #
# Sometimes there is no way to escape a cycle. A package A needs B, and B     #
# needs A. In that case, it is often possible to build a degraded version     #
# of package A, then B, then rebuild A. The book indicates this with the      #
# following dependency chain, using a qualifier of "first":                   #
#                B---f--->A---b--->X...Y---b--->B                             #
# where the X...Y notation represents a chain of dependencies from A to B.    #
# So the third loop is over nodes containing "f" qualifiers, and does the     #
# following: it creates a new node A-pass1, which is a copy of A, and         #
# remove from A-pass1 all the dependencies leading to B through a chain,      #
# to obtain:                                                                  #
#               A---b--->X...Y---b--->B---b--->A-pass1                        #
# It may then happen that nothing depends on A. So this is tested, and A      #
# is added to the root node if it is orphaned.                                #
# TODO: document the third pass                                               #
# TODO: needs also to document the .tree files                                #
# TODO: The following is obsolete                                           #
# Circular dependencies:                                                    #
#                                                                           #
# In case we find a cirdular dependency, it has the form :                  #
# parent->dependency_0->...->dependency_n->dependency_0                     #
# If we want to build dependency_n before dependency_0, no problem:         #
# we just prune the tree at dependency_n. If we want to build first         #
# dependency_0, we need to put dependency_n as a dependency of parent,      #
# then erase and rebuild the subtree from there. Now, we may have met       #
# another circular dependency in the subtree, and erasing the tree makes    #
# us forget the decision which was made. So, after first generating the     #
# list of dependencies from packages.xml, we keep the generated list in     #
# a file <nodeName>.odep, which we modify according to the decision which   #
# was made.                                                                 #
#---------------------------------------------------------------------------#

# Global variables:
# A string of spaces for indenting:
declare -a spaceSTR="                                                                   "
# When we are backing up from a circular dependency, `parentNode'
# contains the node which has an edge entering the cycle
declare parentNode

#---------------------#
generate_subgraph() { #
#---------------------#
: <<inline_doc
    function:   Create a subgraph of all the nodes reachable from the node
                represented by the file whose name is $1. The edges considered
                are those with maximal weight DEP_LEVEL (recursive function).
    input vars: $1 : file name corresponding to the node whose edges will be
		     followed for the DFS
                $2 : weight of the edge leading to this node
                $3 : depth (root is 1)
                $4 : qualifier (a for after, b for before, f for first)
    externals:  vars:  DEP_LEVEL   contains 1 if we want to build the
                                   tree only for required dependencies,
                                   2 if we want also recommended ones,
                                   3 if we want also optional ones, but only
                                   for the requested packages,
                                   4 if we want all the dependencies
                                     (excluding external of course)
                       MAIL_SERVER contains the name of the MTA we want to use.
                files: ../xsl/dependencies.xsl: stylesheet for creating the
                                               .dep files
                       ../packages.xml:         File containing packages id
                                                and dependencies
    returns:    0 if the tree has been successfully created
    output:     files: for each node reachable from $1, a file <node>.dep.
    on error:   nothing
    on success: nothing
inline_doc

local depFile=$1
local -i weight=$2
local -i depth=$3
local qualifier=$4
local -i spacing=0
local priostring
local buildstring
local id_of_dep
local prio_of_dep
local build_of_dep
local dep_level

if (( depth < 10 )); then spacing=1; fi
case $weight in
    1) priostring=required    ;;
    2) priostring=recommended ;;
    3) priostring=optional    ;;
esac
case $qualifier in
    a)   buildstring=runtime ;;
    b|f) buildstring=        ;;
esac
dep_level=$DEP_LEVEL
if [ "$dep_level" = 3 ] && [ "$depth" -gt 2 ]; then dep_level=2; fi
if [ "$dep_level" -gt 3 ]; then dep_level=3; fi
echo -en "\nNode: $depth${spaceSTR:0:$(( depth + spacing ))}${RED}${depFile%.dep}${OFF} $priostring $buildstring"

depth=$(( depth + 1 ))
if (( depth < 10 )); then spacing=1; else spacing=0; fi
# Start of loop
{
while read prio_of_dep build_of_dep id_of_dep; do
  case $prio_of_dep in
    1) priostring=required ;;
    2) priostring=recommended ;;
    3) priostring=optional ;;
    4) priostring=external ;;
  esac
  case $build_of_dep in
    a  ) buildstring=runtime ;;
    b|f) buildstring=        ;;
  esac
# Has this entry already been seen?
# TODO: no there is no special case!
# We have a special case here: if the entry has been seen at depth > 2
# and now depth=2 and DEP_LEVEL=3, optional deps have not been processed.
# If this is the case, just consider it has not been seen.
  if [ -f ${id_of_dep}.dep ] ; then
    case $depth$DEP_LEVEL in
      23) ;;
       *)
# Just display it and proceed.
         echo -en "\nEdge: $depth${spaceSTR:0:$((depth + spacing))}${MAGENTA}${id_of_dep}${OFF} $priostring $buildstring"
         continue
         ;;
    esac
  fi
# Is the weight higher than requested?
  if [ "$prio_of_dep" -gt $dep_level ]; then
# Just display it and proceed.
    echo -en "\n Out: $depth${spaceSTR:0:$((depth + spacing))}${YELLOW}${id_of_dep}${OFF} $priostring $buildstring"
    continue
  fi
# Otherwise, let's build the corresponding subgraph.
  xsltproc --stringparam idofdep "$id_of_dep" \
           --stringparam MTA "$MAIL_SERVER"   \
           -o ${id_of_dep}.dep                \
           ../xsl/dependencies.xsl ../packages.xml

  if [[ -s ${id_of_dep}.dep ]]; then # this dependency has dependencies
    generate_subgraph ${id_of_dep}.dep $prio_of_dep $depth $build_of_dep
  else # id_of_dep has no dependencies, just touch the file and display
    touch ${id_of_dep}.dep
    echo -en "\nLeaf: $depth${spaceSTR:0:$((depth + spacing))}${CYAN}${id_of_dep}${OFF} $priostring $buildstring"
  fi
done
} <$depFile
depth=$(( depth - 1 ))
if (( depth < 10 )); then spacing=1; else spacing=0; fi
echo -en "\n End: $depth${spaceSTR:0:$((depth + spacing))}${GREEN}${depFile%.dep}${OFF}"
return 0
}

#-----------#
path_to() { #
#-----------#
: <<inline_doc
    function:   check whether there is a path from $1 to $2 on the graph
    input       vars: $1 contains the filename of the starting node.
                      $2 contains the name of the node to reach
                      $3 contains the weight above which we do not want to
                         follow an edge
                      $seen (global) contains the list of already seen nodes.
                         It must ve set to " " prior to calling the function
    returns:    0 if the node has been found
                1 if not
    on error:   nothing
    on success: nothing
inline_doc
local start=$1
local seek=$2
local prio=$3
local prio_of_dep
local build_of_dep
local id_of_dep
local r

if test "${start%.dep}" = "$seek"; then return 0; fi
seen="$seen${start%.dep} "
if test -s $start; then
  {
  while read prio_of_dep build_of_dep id_of_dep; do
    if test "$prio" -lt "$prio_of_dep"; then continue; fi
    if ! test "${seen% $id_of_dep *}" = "$seen"; then continue; fi
    if path_to ${id_of_dep}.dep $seek $prio; then return 0; fi
  done
  } < $start
fi
return 1
}
#------------------#
clean_subgraph() { #
#------------------#
: <<inline_doc
    function:   Remove dangling edges and create groups of deps for "after"
                deps: A-before->B-after->C becomes:
                 A -before-> Bgroupxx -before-> B
                                     \
                                      -before-> C
                the name of the group is chosen so that it is unlikely as
                a package name (so that it is removed when building the
                xml book).
                Also change the "first" qualifier so that a cycle:
                 A -first-> B ---chain---> A becomes:
                 B ---chain---> A -before-> B-pass1
                and we remove all the dependencies which have a chain to
                A in B-pass1.
                Since we do not change anything else, it may happen that
                nothing depends on B. In that case, B is appended to root.
    input       vars: None
                files: <node>.dep files containing dangling edges and
                       "after" qualifiers
    returns:    0
    output:     files: <node>.dep files containing no dangling edges and
                       no "after" qualifiers
    on error:   nothing
    on success: nothing
inline_doc

local node
local id_of_dep
local prio_of_dep
local build_of_dep
local lines_to_remove
local lines_to_change
local parent
local p
local b
local start
local seen

for node in $(ls *.dep); do
  if test $node = root.dep; then continue; fi
  echo Cleaning $node
  lines_to_remove=
  {
  while read prio_of_dep build_of_dep id_of_dep; do
    if ! test -f ${id_of_dep}.dep; then
      lines_to_remove="$lines_to_remove $id_of_dep"
      continue
    fi
  done
  } <$node
  for id_of_dep in $lines_to_remove; do
    sed "/\ $id_of_dep\$/d" -i $node
  done
done
for node in $(grep -l ' a ' *.dep); do
  lines_to_remove=
  echo Process "runtime" deps in $node
  if ! [ -e ${node%.dep}groupxx.dep ]; then
    b=0 # Nothing depends on <node>groupxx
    for parent in $(grep -l ${node%.dep}\$ *); do
      p=0 # No "after" dependency depends on this parent
      for start in $(grep ' a ' $node | cut -d' ' -f3); do
        seen=" " # global variable used in "path_to"
        if path_to ${start}.dep ${parent%.dep} 3; then p=1; break; fi
      done
      if test $p = 0; then
        b=1
        sed -i "s/\ ${node%.dep}\$/&groupxx/" $parent
      fi
    done
    echo "1 b ${node%.dep}" > ${node%.dep}groupxx.dep
    if test $b = 0; then echo "1 b ${node%.dep}groupxx" >> root.dep; fi
  fi
  {
  while read prio_of_dep build_of_dep id_of_dep; do
    if test $build_of_dep = a; then
      if ! grep -q ${id_of_dep} ${node%.dep}groupxx.dep; then
        echo "$prio_of_dep b ${id_of_dep}" >> ${node%.dep}groupxx.dep
      fi
      lines_to_remove="$lines_to_remove $id_of_dep"
    fi
  done
  } <$node
  for id_of_dep in $lines_to_remove; do
    sed "/a\ $id_of_dep\$/d" -i $node
  done
done
for node in $(grep -l ' f ' *); do
  echo Process "first" deps in $node
  lines_to_change=
  {
  while read prio_of_dep build_of_dep id_of_dep; do
    if test $build_of_dep = f; then
      if ! test -f ${id_of_dep}-pass1.dep; then
        cp ${id_of_dep}{,-pass1}.dep;
      fi
      lines_to_change="$lines_to_change $id_of_dep"
      unset lr  # lines to remove in -pass1
      {
      while read p b start; do
        seen=" " # global variable used in "path_to"
        if path_to ${start}.dep ${node%.dep} $p; then
          lr="$lr $start"
        fi
      done
      } < ${id_of_dep}-pass1.dep
      for p in $lr; do
        sed "/\ $p\$/d" -i ${id_of_dep}-pass1.dep
      done
    fi
  done
  } <$node
  for id_of_dep in $lines_to_change; do
    sed "/\ $id_of_dep\$/"'{s/[[:digit:]] f /1 b /;s/$/-pass1/}' -i $node
    if ! grep -q " $id_of_dep\$" *.dep; then
      echo 1 b $id_of_dep >>root.dep
    fi
  done
done
} # End clean_subgraph

#----------------------------#
generate_dependency_tree() { #
#----------------------------#
: <<inline_doc
    function:   Create a subtree of the dependency tree
                (recursive function)
    input vars: $1 : file with a list of targets (child nodes)
                     the first line of the file is the link
                $2 : priority (1=req, 2=rec, 3=opt)
    returns:    0 if the tree has been successfully created
                1 if we are backing up to the parent of a circular dep
                  and there are only required deps in the cycle
                2 if we are backing up to the parent of a circular dep
                  and there are recommended deps and no optional deps in the
                  cycle
                3 if we are backing up to the parent of a circular dep
                  and there are optiional deps in the cycle
    modifies:   vars: parentNode is set when return is not 0
    output:     files: for each <pkg> with dependencies in $1,
                       a file <pkg>.tree and its dependencies
    on error:   nothing
    on success: nothing
inline_doc

local depFile=$1
local priority=$2
local -a rootlink
local -a priolink
local -a otherlink
local -i depth
local -i count=0
local id_of_dep
local build_of_dep
local prio_of_dep
local parent
local lines_to_remove=
local srootlink
local priostring
local dpriostring
local i

{
read -a rootlink
depth=${#rootlink[*]}
read -a priolink
srootlink="${rootlink[*]} "
case $priority in
    1) priostring=required ;;
    2) priostring=recommended ;;
    3) priostring=optional ;;
esac
# start of depFile
echo -en "\nNode: $depth${spaceSTR:0:$depth}${RED}${depFile%.tree}${OFF} $priostring"

while read prio_of_dep build_of_dep id_of_dep; do
  case $prio_of_dep in
    1) dpriostring=required ;;
    2) dpriostring=recommended ;;
    3) dpriostring=optional ;;
  esac
# count entries in file
  (( count++ ))
# Has this entry already been seen?
  if [ -f ${id_of_dep}.tree ]; then # found ${id_of_dep}.tree already in tree
    otherlink=($(head -n 1 ${id_of_dep}.tree))
    if [ -z "${otherlink[*]}" ]
      then echo otherlink empty for $id_of_dep.tree
      echo This should not happen, but happens to happen...
      exit 1
    fi
#Do not use "${rootlink[*]}" =~ "${otherlink[*]}": case rootlink=(1 11)
# and otherlink=(1 1)
    if [[ ${srootlink#"${otherlink[*]} "} != ${srootlink} ]]; then # cir. dep
      echo -en "\nCirc: $((depth+1))${spaceSTR:0:$((depth+1))}${YELLOW}${id_of_dep}${OFF} $dpriostring"
# Find lowest priority in branch from parent to depFile:
      p2=0
      for (( i=${#otherlink[*]}; i < $depth ; i++ )) ; do
        if (( ${priolink[i]} > $p2 )); then p2=${priolink[i]}; fi
      done
      if (( $prio_of_dep >= $p2 )); then # prune
        lines_to_remove="$lines_to_remove $id_of_dep"
        sed -i "/$id_of_dep/d" ${depFile/.tree/.dep}
      else # find and set parent, then return lowest priority
# The parent has the same link without the last entry.
# We do not need otherlink anymore so just destroy the last element
        unset otherlink[-1]
# We cannot use grep -l, because we need to restrict to the first line,
# since the prio line may match
        for f in *.tree; do
          if head -n1 $f | grep -q ^"${otherlink[*]}"\$; then
             parentNode=$f; break
          fi
        done
        return $p2
      fi
    else # not circular: prune tree (but not .dep, since it may happen that
         # the tree is destroyed and rebuilt in another order)
      lines_to_remove="$lines_to_remove $id_of_dep"
    fi # circular or not
    continue # this dependency has already been seen, and the associated
             # subtree computed. We are done
  fi # Has this entry already been seen?
# So, this entry has not already been seen. Let's build the corresponding
# subtree. First check there is a subtree...
# Use -s, because it may happen that after removing lines, .dep exists
# but is empty.
  if [[ -s ${id_of_dep}.dep ]]; then # this dependency has dependencies
    sed "1i${rootlink[*]} $count\\
${priolink[*]} $prio_of_dep" < ${id_of_dep}.dep \
                             > ${id_of_dep}.tree # add link and priolink
    generate_dependency_tree ${id_of_dep}.tree $prio_of_dep
# Test return value, in case we exchange dependencies
    p2=$?
    case $p2 in
     0) # Normal return
       ;;
     $prio_of_dep) # we remove this dep, but since it may become unreachable,
                   # move it to be built later (as a dep of parent).
         tree_erase ${id_of_dep}.tree
         lines_to_remove="$lines_to_remove $id_of_dep"
         sed -i "/${id_of_dep}/d" ${depFile/.tree/.dep}
         echo "$prio_of_dep b $id_of_dep" >> $parentNode
# must be added to .dep in case parentNode is destroyed when erasing
# the tree
         echo "$prio_of_dep b $id_of_dep" >> ${parentNode/.tree/.dep}
         continue
       ;;
     *) # We are backing up
         return $p2
       ;;
    esac
  else # id_of_dep has no dependencies, just record the link in a file
       # and print
    echo "${rootlink[*]} $count" > ${id_of_dep}.tree
    echo -en "\nLeaf: $(($depth+1))${spaceSTR:0:$(($depth+1))}${CYAN}${id_of_dep}${OFF} $dpriostring"
  fi
done
echo -en "\n End: $depth${spaceSTR:0:$depth}${GREEN}${depFile%.tree}${OFF}"
} <$depFile
# It may happen that a file is created with several times
# the same line. Normally, all those lines but one
# would be flagged to be removed (or all of them if
# the dependency appeared before). A simple sed /$line/d
# destroys all the lines. We should instead remove
# only one for each appearance of it in lines_to_remove.
# so first get the position of last line and then delete
# that line
for line in $lines_to_remove
  do lineno=$(sed -n /^[[:digit:]]\ b\ $line\$/= $depFile | tail -n1)
  sed -i ${lineno}d $depFile
done
return 0
}


#---------------#
tree_browse() { #
#---------------#
local file=$1
local f

#echo file=$file
for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
#  echo f=$f
  if grep -q '[^0-9 ]' ${f}.tree ; then
    tree_browse ${f}.tree
  fi
  echo $f
done
}

#--------------#
tree_erase() { #
#--------------#
local file=$1
local f
local rootlink
local rootlink2

#echo file=$file
rootlink="$(head -n1 $file) "
for f in $(grep '[^0-9 ]' $file | sed 's/.* //'); do
  if [ -f ${f}.tree ]; then
    rootlink2="$(head -n1 ${f}.tree) "
# We want two things:
# i)  do not erase the file if it is in another branch
# ii) do not erase the file if there is a circular dependency
# for case i), we test that rootlink is contained in rootlink2
# for case ii), we test that rootlink2 is not contained in
# rootlink.
# See comment above about srootlink
    if [[ ${rootlink2#${rootlink}} != ${rootlink2} &&
	  ${rootlink#${rootlink2}} == ${rootlink} ]] ; then
      tree_erase ${f}.tree
    fi
  fi
done
rm -f $file
}