#!/bin/bash
VER=1.0.2

LDCOPYTMP=/tmp/ldcopy.tmp

function help () {
  cat <<EOF
ldcopy Version-$VER
 study shared library dependencies and copy them to CHROOT environment.
USAGE:
  ldcopy --dir=<chroot_dir> [--follow] [--test] /path/to/program
Note:
 By default, ldcopy tries to preserve symbolic links (ie: copies a
 symbolic link as symbolic link) and then if broken links were found
 in destination directories, copies only the broken ones as realities.
 \`--follow' option forces ldcopy to \`cp' files as realities right
 from the start. With \`--test' option, script will do nothing but
 printing what to be done and check of broken links.

EOF
  exit 0
}

function err () {
  case $1 in
    nodir)
      echo ERROR: target directory $CHROOTDIR does not exist, exitting..;;
    noexe)
      echo ERROR: no such executable \`$PROG\';;
   nodyna)
      echo ERROR: \`$PROG\' is not a dynamic executable;;
  esac
  exit 1
}

function uniqdirs () {
  awk -v d=$CHROOTDIR 'BEGIN{
    for (i=1; i<ARGC; i++) {
      sub(/\/[^/]+$/, "", ARGV[i]);
      printf "%s%s\n", d,ARGV[i];
    }
  }' $@ |sort |uniq
}

function checklink () {
  if [ "$1" = "-q" ]; then
    BADLINK=( $(uniqdirs ${LIBS[@]} |xargs symlinks -v | \
      awk '/^dangling/ {print $2}') )
    [ ${#BADLINK[@]} -ge 1 ] && echo copying broken links again..
    for ((i=0; i<${#BADLINK[@]}; i++)) ; do
      SRC=${BADLINK[$i]#$CHROOTDIR}
      cp -pfv $SRC ${BADLINK[$i]}
    done
  else
    echo checking...
    uniqdirs ${LIBS[@]} |xargs symlinks -v | \
      awk '/^dangling/ {
        printf "WARNING: link `%s` is broken.\n", $2}'
  fi
}

function sym2mod () {
  ls -ld $1 |awk '
    {
      if ($1 ~ /^l/) exit 1

      sub(/d/, "", $1);
      u = substr($1, 1, 3);
      gsub(/-/, "", u);
      sub(/s/, "xs", u);
      sub(/S/, "s", u);
      g = substr($1, 4, 3);
      gsub(/-/, "", g);
      sub(/s/, "xs", g);
      sub(/S/, "s", g);
      o = substr($1, 7);
      gsub(/-/, "", o);
      sub(/t/, "xt", o);
      sub(/T/, "t", o);
    }
    { printf "u=%s,g=%s,o=%s\n", u, g, o }
  '
  return $?
}

function fixperm () {
  for i in ${NEWDIRS[@]} ; do
    PARENT=$i
    while [ ! -z "$PARENT" ] ; do
      PMOD=$(sym2mod $PARENT)
      if [ $? -ne 0 ]; then
        PARENT=${PARENT%/*}
        continue
      fi
      if [ ! ${CHROOTDIR}$PARENT -ot $LDCOPYTMP ]; then
        chmod -v $PMOD ${CHROOTDIR}$PARENT
      fi
      PARENT=${PARENT%/*}
    done
  done
}

for X ; do
  case $X in
   --help|-h)
      help;;
    --dir=*)
      CHROOTDIR=$(echo -n $X |sed -e 's/--dir=\(.*\)/\1/' -e 's/\/*$//');;
   --follow)
      FOLLOW=1;;
    --test)
      TEST=1;;
     *)
      PROG=$X;;
  esac
done

[ -z "$CHROOTDIR" -o -z "$PROG" ] && help
[ -d $CHROOTDIR ] || err nodir
[ -x $PROG ] || err noexe
if [ "$FOLLOW" = "1" ]; then
  CPOPT='-pfv'
else
  CPOPT='-dpfv'
fi

LIBS=$(ldd $PROG |awk '{
  for (i=1; i<=NF; i++) {
    if ($i ~ /^\(0x[[:xdigit:]]+\)$/) {
      if ($(i-1) != "=>") print $(i-1)
      break
    }
  }
}')

[ -z "${LIBS[*]}" ] && err nodyna

if [ "$TEST" = "1" ]; then
  PRE=echo
else
  touch $LDCOPYTMP || echo ERROR: can not make temp file, exitting..
fi

N=0
for i in ${LIBS[@]} ; do
  DIR=$(dirname $i)
  if [ ! -d ${CHROOTDIR}$DIR ]; then
    $PRE mkdir -vp ${CHROOTDIR}$DIR
    NEWDIRS[$N]=$DIR
    : $((N+=1))
  fi
  $PRE cp $CPOPT $i ${CHROOTDIR}$DIR
done

[ "$TEST" != "1" -a $N -gt 0 ] && fixperm
[ "$TEST" != "1" -a "$FOLLOW" != "1" ] && checklink -q
checklink

rm -f $LDCOPYTMP

exit 0
