#!/bin/bash
###
###  (SIP) Callflow diagram generator
###

##################################
# Variables
##################################
ARCHIVE=yes
ARCHIVE_TYPE=bz2
CONFDIR="/etc/callflow";
removeDF=0 # do not remove duplicate frames
ORDER=0 # do not build order file
localLoop=1 # take into account flows from and to the same element (rounded flows)

readonly VersionSVN="$Id: callflow 199 2014-10-23 17:33:31Z rbos $"; #DO NOT EDIT THIS LINE BY HAND, DATE IS AUTOMATICALLY ADDED BY SUBVERSION
readonly PRGVRSN=20151121
readonly PRGNAME=$(basename $0)

# Define exit values
readonly ERR_ARGUMENT=2

# Source in callflow's configuration file(s)
for CONF in $CONFDIR/callflow.conf $HOME/.callflow/callflow.conf callflow.conf; do
  [[ -f $CONF ]] && . $CONF
done

# Basic conf
[[ -z "$SETUPDIR" ]] && SETUPDIR="/usr/local/callflow"
[[ -z "$TMPDIR" ]] && TMPDIR="/tmp"

[[ ! -d $SETUPDIR ]] && {
  ( echo "$PRGNAME: error: setup directory \"$SETUPDIR\" does not exist"
    echo "Adjust your configuration"
  ) >&2
  exit 1
}

[[ ! -d $TMPDIR ]] && {
  ( echo "$PRGNAME: error: temporary directory \"$TMPDIR\" does not exist"
    echo "Adjust your configuration"
  ) >&2
  exit 1
}

if [[ -z "$FILTER" ]]; then
  FARG=""
  FVAL=""
else
  # -2 is required by -R
  FARG="-2 -R"
  FVAL="$FILTER"
fi

if [[ -f colors ]]; then
  COLORS=$(cat colors)
fi

[[ -z "$COLORS" ]] && {
  # At least one color is needed, to prevent errors
  COLORS="black"
}

function make_long_and_short_caches() {

  # The make_long_and_short_caches_of_..... functions result
  # in 2 files, being $DESTDIR/callflow.short and $DESTDIR/callflow.long

  # Try to be smart and figure out the format of the input file
  INPUT_FILE_FORMAT=$(file $inputfile | cut -d: -f2)
  case $INPUT_FILE_FORMAT in
  *ASCII*)
     . $SETUPDIR/scripts/broadworks-parser.sh
     make_long_and_short_caches_of_broadworks_log $inputfile
     ;;
  *)
     # Any other file type is considered a network trace
     . $SETUPDIR/scripts/pcap-parser.sh
     make_long_and_short_caches_of_pcap_trace $inputfile
  esac

  # It sometimes happens that the frames in the trace are not time
  # ordered (they are ordered at frame number), for this reason
  # re-order the callflow.short cache on time.
  # Example of a wrong order:
  # 13.14.15.625340|1|.....
  # 13.14.15.625360|2|.....
  # 13.14.15.625350|3|..... <<< this one is at the wrong spot
  # The timestamp is the first field in the callflow.short cache, hence
  # re-ordering on time is rather easy using just sort.
  sort -o $DESTDIR/callflow.short $DESTDIR/callflow.short

  # Store the input characteristics.  It will be used in subsequent runs
  # to determine that the cache can be used, or that the input must be
  # processed again.
  TRACE_FILE=$(basename $inputfile)
  MD5SUM=$(md5sum $inputfile | awk '{print $1}')
  echo "$TRACE_FILE|$MD5SUM|$FVAL" > $DESTDIR/metadb
}

function usage() {

  ( echo "$SHORTHELP"
    echo "See $PRGNAME --help for extended help."
  ) >&2
  exit 0
}

function help () {

  cat << EOF >&1
Usage: $PRGNAME [options] <trace file>

Options:
   -h           Print short help and exit
  --help        Print this help and exit
  --version, -v Print version

  --archive <archive type>
                Create an archive with the files for showing the callflow
                diagram.  Available archive types are: files, bz2 (bzip2) and zip.
                The callflow archive can be used to sent to interested parties.
                The archive type "files" just list the files required for the
                callflow diagram.  It can be used to create an archive with it.

  --browser
                Launch a browser with the callflow.

  --capture-filter <filter>, -f <filter>
                A filter can be used to show only those frames that match the
                given filter criteria.  This filter is for traces that have been
                captured with programs such as wireshark, snoop, tcpdump, tshark
                and the like.  $PRGNAME uses tshark's DISPLAY filter functionality
                to filter the trace.  More information about tshark's DISPLAY
                filter can be obtained from the tshark man page.
                A useful filter for SIP traffic is "sip".

  --list-nodes, -o
                List the unique nodes in the trace file
                (Don't forget to redirect output in another file.
                 Example of use: $PRGNAME --list-nodes capture.cap > order)

  --no-archive
                Disable the creation of and reference to the archive containing
                the callflow

  --no-loops
                Prevent flows from and to the same equipment to be represented.

  --no-sdp
                Do not show detailed SDP information in the SIP information line
  --no-time
                Do not show the time.

  --nodenames
                A file containing the element or node names that belong to the
                addresses shown in the callflow.

  --refresh-cache
                Refresh the contents of the callflow.short and callflow.long
                files.  By default these are cached.  The cache is automatically
                refreshed, when a changed input file, or a different filter is
                is used.

  --remove-duplicate-frames, -d
                Remove duplicate frames while computing.
                This option should not be selected on the first run.

  --title <title>
                Use <title> as title in the callflow.  If not specified the
                name of file containing the trace will be used instead. Surround
                <title> by quotes in case the title contains spaces or tabs.

  --width-between-nodes <width>
                Distance between adjacent nodes in final picture.  The width may
                vary between 100 and 250.  Default = 200.

  --with-sdp
                Show detailed SDP in the SIP information line (default)

EOF
}

readonly SHORTHELP="Usage: $PRGNAME [options] <filename>" 

# Command line argument parsing, the allowed arguments are
# alphabetically listed, keep it this way please.
LOPT="archive:,browser"
LOPT="$LOPT,capture-filter:,help,list-nodes,no-archive,no-loops,no-sdp,no-time,nodenames:"
LOPT="$LOPT,refresh-cache,remove-duplicate-frames,title:,with-sdp,width-between-nodes:"
LOPT="$LOPT,version"

# Note that we use `"$@"' to let each command-line parameter expand to a
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.
TEMP=$(getopt --options=dhorv --long $LOPT -n $PRGNAME -- "$@")

if [[ $? -ne 0 ]]; then
  echo "Terminating..." >&2
  exit $ERR_ARGUMENT
fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

while true; do
  case $1 in
  --help)                help; exit ;;
   -h)                   usage; exit;;

  --archive)
                         ARCHIVE="yes"
                         OPT_ARCHIVE_TYPE=$2; shift
                         ;;
  --browser)
                         START_BROWSER="yes"
                         ;;
  --capture-filter)
                         # Overwrite the default filter previously red from the configuration file
                         OPT_FILTER=$2; shift
                         #echo " * Will use the following filter: $OPT_FILTER"
                         ;;
  --list-nodes|-o)
                         ORDER=1
                         ;;
  --no-archive)
                         # Disable the creation of and reference to the archive
                         # containing the callflow
                         ARCHIVE=no
                         ;;
  --no-loops)
                         # We want to disable auto flows (From and To the same equipment)
                         noLocalLoop=1;
                         #echo " * Flows from and to the same equipment will not be shown"
                         ;;
  --no-sdp)
                         OPT_SHOW_SDP=no
                         ;;
  --no-time)
                         OPT_SHOW_TIME=no
                         #echo " * Time will not be shown"
                         ;;
  --nodenames)
                         OPT_NODENAMES=$2; shift
                         ;;
  --refresh-cache)
                         OPT_REFRESH_CACHE=yes
                         ;;
  --remove-duplicate-frames|-d)
                         # Remove duplicate frames
                         removeDF=1
                         #echo " * Duplicate frames will be removed"
                         ;;
  --title)
                         OPT_TITLE="$2"; shift
                         #echo " * Title to use: $OPT_TITLE"
                         ;;
  --width-between-nodes)
                         OPT_WIDTH_BETWEEN_NODES=$2; shift
                         #echo " * Width between nodes to use: $OPT_WIDTH_BETWEEN_NODES"
                         ;;
  --with-sdp)
                         OPT_SHOW_SDP=yes
                         ;;
  --version|-v)          echo "$PRGNAME version: $PRGVRSN"; exit;;
  --)                    shift; break;;
   *)                    echo "unknown argument \"$1\""; exit $ERR_ARGUMENT;;
  esac
  shift
done

inputfile="$1"
if [[ ! -f "$inputfile" ]]; then 
  echo "$PRGNAME: error: Input file ($inputfile) does not exists!"
  exit 1;
else
  # TODO: what is the sed command doing precisely, is it really needed?
  DESTDIR=$(ls "$inputfile" | sed -r "s/(.+)\.(.+)/\1/");
fi

if [[ ! -z "$OPT_FILTER" ]]; then
  if [[ "$OPT_FILTER" == "" ]]; then
    FARG=""
    FVAL=""
  else
    FARS="-R"
    FVAL="$OPT_FILTER"
  fi
fi

if [[ -z "$OPT_WIDTH_BETWEEN_NODES" ]]; then
  if [[ -z "$WIDTH_BETWEEN_NODES" ]]; then
    WIDTH_BETWEEN_NODES=200
  else
    # In case the WIDTH_BETWEEN_NODES has been defined in the configuration
    # file callflow.conf, prepare it to be verified.
    OPT_WIDTH_BETWEEN_NODES=$WIDTH_BETWEEN_NODES
  fi
fi

if [[ -n "$OPT_WIDTH_BETWEEN_NODES" ]]; then

  if [[ $OPT_WIDTH_BETWEEN_NODES -gt 250 ]]; then
    echo "$PRGNAME: error: width ($OPT_WIDTH_BETWEEN_NODES) between nodes too big" >&2
    exit 1
  fi

  if [[ $OPT_WIDTH_BETWEEN_NODES -lt 100 ]]; then
    echo "$PRGNAME: error: width ($OPT_WIDTH_BETWEEN_NODES) between nodes too small" >&2
    exit 1
  fi

  WIDTH_BETWEEN_NODES=$OPT_WIDTH_BETWEEN_NODES
fi

[[ -n "$OPT_NODENAMES" ]] && {
  if [[ -f "$OPT_NODENAMES" ]]; then
    NODENAMES="$OPT_NODENAMES"
  else
    echo "$PRGNAME: error: nodenames file does not exist (--nodenames)" >&2
    exit 1
  fi
}

[[ -z "$NODENAMES" ]] && {
  for FILE in $HOME/.callflow/nodenames.conf nodenames.conf; do
    [[ -f $FILE ]] && NODENAMES=$FILE
  done
}

if [[ "$ARCHIVE" == "yes" ]]; then

  case "$OPT_ARCHIVE_TYPE" in

    "")
      # Use default ARCHIVE_TYPE
      ;;

    bz2|bzip2|files|zip)

      ARCHIVE_TYPE="$OPT_ARCHIVE_TYPE"
      ;;

    *)
      echo "$PRGNAME: error: archive type ($OPT_ARCHIVE_TYPE) does not exist" >&2
      exit
      ;;
  esac

  [[ $ARCHIVE_TYPE != "files" ]] && {

    ARCHIVE_NAME=$(basename $DESTDIR)
    case $ARCHIVE_TYPE in 
      bz2|bzip2) 
        ARCHIVE_FILE=$ARCHIVE_NAME.tar.bz2
        ;;
      *)
        ARCHIVE_FILE=$ARCHIVE_NAME.$ARCHIVE_TYPE
        ;;
    esac 
  }

fi

if [[ -z "$OPT_SHOW_TIME" ]]; then
  SHOW_TIME=yes
else
  SHOW_TIME=$OPT_SHOW_TIME
fi

if [[ -n "$OPT_TITLE" ]]; then
  TITLE="$OPT_TITLE"
else
  TITLE="$inputfile"
fi

if [[ -z "$OPT_SHOW_SDP" ]]; then
  [[ -z "$SHOW_SDP" ]] && {
    SHOW_SDP=yes
  }
else
  SHOW_SDP=$OPT_SHOW_SDP
fi

if [[ -f order ]]; then
  orderFile=order
elif [[ -f $DESTDIR/order ]]; then
  orderFile=$DESTDIR/order
else
  orderFile=none
fi

# Use cache?
if [[ "$OPT_REFRESH_CACHE" == "yes" ]]; then
  USE_CACHE=no
else
  if [[ -f $DESTDIR/metadb ]]; then
    if [[ -f $DESTDIR/callflow.short ]]; then
      FILE=$(basename $inputfile)
      MD5_FILE=$(md5sum $inputfile | awk '{print $1}')

      MD5_CACHE=$(awk -F"|" -v FILE=$FILE '{if ($1 == FILE) {print $2} }' $DESTDIR/metadb)

      if [[ $MD5_FILE == $MD5_CACHE ]]; then

        CACHED_FILTER=$(awk -F"|" -v FILE=$FILE '{if ($1 == FILE) {print $3} }' $DESTDIR/metadb)

        # Are the cached filter and used filter the same?
        if [[ "$FVAL" == "$CACHED_FILTER" ]]; then
          USE_CACHE=yes
        else
          USE_CACHE=no
        fi
      else
        # The input file changed
        USE_CACHE=no
      fi
    else
      # The file $DESTDIR/callflow.short does not exist
      USE_CACHE=no
    fi
  else
    # The file $DESTDIR/metadb does not exist
    USE_CACHE=no
  fi
fi

[[ $USE_CACHE == "no" ]] && {
  # Create the DESTDIR, do it only here, as it isn't needed earlier
  mkdir -p $DESTDIR

  make_long_and_short_caches
}

# Check that the short and long caches exist
if [ ! -f $DESTDIR/callflow.long ]; then
  echo "$PRGNAME: error: File callflow.long does not exist!"
  exit 1;
else
  # Some SIP messages don't include a space between the Call-ID tag and
  # Call-ID value
  sed -i 's,\(Call-ID:\)\([^ ]\),\1 \2,' $DESTDIR/callflow.long
fi

if [ ! -f $DESTDIR/callflow.short ]; then
  echo "$PRGNAME: error: File callflow.short does not exist!"
  exit 1;
else
  # Remove Malformed packages from callflow.short, especially because it
  # contains unpaired "["
  sed -i '/Malform/d' $DESTDIR/callflow.short
fi

# Display the node order and exit
[[ $ORDER == 1 ]] && {

  awk -f $SETUPDIR/scripts/list-nodes.awk -v NODENAMES=$NODENAMES $DESTDIR/callflow.short
  exit 0
}

# Create Frames
mkdir -p $DESTDIR/frames
awk -f $SETUPDIR/scripts/long2html.awk -v destDir=$DESTDIR < $DESTDIR/callflow.long

for FILE in $SETUPDIR/css/callflow.css $HOME/.callflow/callflow.css callflow.css; do
  [[ -f $FILE ]] && STYLESHEET=$FILE
done

sed s/@SIP_MSG_FONT_SIZE@/${SIP_MSG_FONT_SIZE:-small}/ $STYLESHEET > $DESTDIR/frames/callflow.css

# Remove duplicate Frame
if [ $removeDF = 1 ]; then
  #echo "Removing duplicate frames"
  $SETUPDIR/scripts/removedups.sh $DESTDIR $DESTDIR/frames $TMPDIR ${REMOVE_DUP_MODE:-REMOVE_ALL_DUPS}
  rm $DESTDIR/callflow.short
  mv $DESTDIR/callflow.short.new $DESTDIR/callflow.short
fi

##################################
# common
##################################
# Compute nodes
awk -f $SETUPDIR/scripts/list-nodes.awk -v NODENAMES=$NODENAMES \
  $DESTDIR/callflow.short > $TMPDIR/callflow.auto-uniq.$PPID

# orderFile
if [ $orderFile != none ]; then
    # add forced nodes
    cp $TMPDIR/callflow.auto-uniq.$PPID $TMPDIR/callflow.auto-uniq-forced.$PPID
    grep "!f!" $orderFile | cut -d " " -f 1 >> $TMPDIR/callflow.auto-uniq-forced.$PPID
    cut -d " " -f 1 < $orderFile > $TMPDIR/callflow.order-nodes.$PPID

    # prune nodes not appearing in capture file and not forced.
    grep -w -v -f $TMPDIR/callflow.auto-uniq-forced.$PPID $TMPDIR/callflow.order-nodes.$PPID > $TMPDIR/callflow.prune-candidate.$PPID
    awk -f $SETUPDIR/scripts/makevars.awk < $TMPDIR/callflow.prune-candidate.$PPID > $TMPDIR/callflow.prune-vars.$PPID
    cat $TMPDIR/callflow.prune-vars.$PPID $SETUPDIR/scripts/prunenodes.awk > $TMPDIR/callflow.prune-awk.$PPID
    awk -F"|" -f $TMPDIR/callflow.prune-awk.$PPID < $DESTDIR/callflow.short > $TMPDIR/callflow.auto-not-pruned.$PPID
    grep -w -v -f $TMPDIR/callflow.auto-not-pruned.$PPID $TMPDIR/callflow.prune-candidate.$PPID > $TMPDIR/callflow.auto-prune.$PPID
    grep -w -v -f $TMPDIR/callflow.auto-prune.$PPID $TMPDIR/callflow.order-nodes.$PPID > $TMPDIR/callflow.order-nodes-pruned.$PPID

    # add nodes appearing in capture file but not in order file
    cp $TMPDIR/callflow.order-nodes-pruned.$PPID $TMPDIR/callflow.order-nodes-final.$PPID
    grep -w -f $TMPDIR/callflow.auto-uniq-forced.$PPID $TMPDIR/callflow.order-nodes.$PPID >> $TMPDIR/callflow.order-nodes-final.$PPID
    grep -w -f $TMPDIR/callflow.order-nodes-final.$PPID $orderFile > $TMPDIR/callflow.order.$PPID
    grep -w -v -E -f $TMPDIR/callflow.order-nodes.$PPID $TMPDIR/callflow.auto-uniq-forced.$PPID >> $TMPDIR/callflow.order.$PPID
    sed "s/!f!//g" < $TMPDIR/callflow.order.$PPID > $TMPDIR/callflow.order-final.$PPID

    finalOrderFile=$TMPDIR/callflow.order-final.$PPID

    #echo Using the following order:
    #cat $finalOrderFile
else
    echo "$PRGNAME: warning: no \"order\" file found -- using auto generated"
    finalOrderFile=$TMPDIR/callflow.auto-uniq.$PPID
fi

( awk -f $SETUPDIR/scripts/makevars.awk $finalOrderFile

  cat << AWK_BEGIN_SECTION

  title = "$TITLE"

  # Add graph variables
  yLineSpace = 24
  xHostSpace = $WIDTH_BETWEEN_NODES
  rightMargin = 100
  topMargin = 50
  bottomMargin = 50
  showTime = "$SHOW_TIME"
  showSDP = "$SHOW_SDP"
  if ( showTime == "yes" ) {
    leftMargin = 125
  } else {
    leftMargin = 50
  }
  numTraces = numHosts

  # Add localLoop to vars in order to delete self messaging
  localLoop = "$localLoop"

AWK_BEGIN_SECTION

  echo "numLines = $(cat $DESTDIR/callflow.short | wc -l)"
  echo

  # Add colors from colorFile
  awk 'BEGIN { I = 0} {
    for (N = 1; N <= NF; N++) {
      printf("  color[%d] = \"%s\"\n", I++, $N)
    }
  } END {
    print "  colors = length(color) - 1"
  }' <<< "$COLORS"

  echo
  cat $SETUPDIR/scripts/callflow.awk

) > $TMPDIR/callflow.awk.$PPID

# Build callflow.svg
awk -F "|" -f $TMPDIR/callflow.awk.$PPID $DESTDIR/callflow.short > $DESTDIR/callflow.svg

# Remove temporary files
\rm $TMPDIR/callflow.*.$PPID

# Build callflow.png if inkscape is available
if which inkscape >/dev/null 2>&1; then

  # Inkscape does not have a quiet option, it might get one in version 0.49
  # Workaround; sent the output to /dev/null in case the files to be archived
  # will be listed on the screen
  if [[ "$ARCHIVE_TYPE" == "files" ]] || [[ "$INKSCAPE_MODE" == "QUIET" ]]; then
    inkscape --export-dpi=90 -C --export-background=white --export-png=$DESTDIR/callflow.png $DESTDIR/callflow.svg >/dev/null 2>&1
  else
    inkscape --export-dpi=90 -C --export-background=white --export-png=$DESTDIR/callflow.png $DESTDIR/callflow.svg
  fi

  # Copy the input file into destination directory
  TRACEFILE=$(basename $inputfile)
  cp $inputfile $DESTDIR/$TRACEFILE

  # Build HTML files
  # Frameless; (SIP) messages will be presented in the same window as the callflow picture
  ( echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
    echo "  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
    echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"
    echo " <head>"
    echo "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>"
    echo "  <title>$PRGNAME - $TITLE</title>"

    # Do not use an external style sheet, as it is visible that the stylesheet is loaded
    echo "  <style type=\"text/css\">"
    echo ".callflow-image img {"
    echo " display: block;"
    echo " border: 0px;"
    echo " margin-left: auto;"
    echo " margin-right: auto;"
    echo "}"
    echo "p.footer {"
    echo " border-top: 1px solid #aaa;"
    echo " font-size: small;"
    echo " padding: 0.1em 1em 0 1em;"
    echo "}"
    echo "  </style>"
    echo " </head>"
    echo " <body>"
    echo "  <div class=\"callflow-image\">"
    cat imagemap
    echo "   <p>"
    echo "    <img src=\"callflow.png\" usemap=\"#callflowmap\" alt=\"Call flow sequence diagram for $inputfile\"/>"
    echo "   </p>"
    echo "   <p class=\"footer\">"
    echo "    <a href=\"$TRACEFILE\">Trace source</a>"
    echo "    <br/>"
    [[ $ARCHIVE == "yes" ]] && {
      echo "    <a href=\"$ARCHIVE_FILE\">Callflow files in archive</a> (to sent the callflow to e.g. interested parties)"
      echo "    <br/>"
    }
    echo "    <a href=\"index.html\" target=\"_parent\">Back to landing page</a>"
    echo "   </p>"
    echo "  </div>"
    echo " </body>"
    echo "</html>"
  ) > $DESTDIR/index_frameless.html

  # (SIP) Messages will be presented in popups on the callflow picture
  ( echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
    echo "  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
    echo "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\">"
    echo " <head>"
    echo "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>"
    echo "  <title>$PRGNAME - $TITLE</title>"
    echo "  <style type=\"text/css\">"
    echo ".callflow-image img {"
    echo " display: block;"
    echo " border: 0px;"
    echo " margin-left: auto;"
    echo " margin-right: auto;"
    echo "}"
    echo "p.footer {"
    echo " border-top: 1px solid #aaa;"
    echo " font-size: small;"
    echo " padding: 0.1em 1em 0 1em;"
    echo "}"
    echo "  </style>"
    echo "  <script type=\"text/javascript\" src=\"js/overlib.js\"></script>"
    echo "  <script type=\"text/javascript\" src=\"js/callflow.js\"></script>"
    echo " </head>"
    echo " <body>"
    echo "  <div class=\"callflow-image\">"
    cat imagemap
    echo "   <p>"
    echo "    <img src=\"callflow.png\" usemap=\"#callflowmap\" alt=\"Call flow sequence diagram for $inputfile\"/>"
    echo "   </p>"
    echo "   <p class=\"footer\">"
    echo "    <a href=\"$TRACEFILE\">Trace source</a>"
    echo "    <br/>"
    [[ $ARCHIVE == "yes" ]] && {
      echo "    <a href=\"$ARCHIVE_FILE\">Callflow files in archive</a> (to sent the callflow to e.g. interested parties)"
      echo "    <br/>"
    }
    echo "    <a href=\"index.html\" target=\"_parent\">Back to landing page</a>"
    echo "   </p>"
    echo "  </div>"
    echo " </body>"
    echo "</html>"
  ) > $DESTDIR/index_dynamic.html

  \rm imagemap

  #Copy JavaScript files into DESTDIR
  cp -af $SETUPDIR/js $DESTDIR/

  # (SIP) Messages will be presented in right frame
  sed 's/coords=/target=\"msg\" coords=/' $DESTDIR/index_frameless.html > $DESTDIR/graph.html

  firstFrame=$(grep -v "^#" $DESTDIR/callflow.short | awk -F "|" '{ if (NR == 1) print $3 }')

  ( echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\""
    echo "   \"http://www.w3.org/TR/html4/frameset.dtd\">"
    echo "<html>"
    echo " <head>"
    echo "  <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\" >"
    echo "  <title>$PRGNAME - $TITLE</title>"
    echo " </head>"
    echo " <frameset cols=\"75%,25%\">"
    echo "  <frame name=\"graph\" src=\"graph.html\">"
    echo "  <frame name=\"msg\" src=\"frames/Frame${firstFrame}.html\">"
    echo " </frameset>"
    echo "</html>"
  ) > $DESTDIR/index_frame_right.html

  # (SIP) Messages will be presented in bottom frame
  sed 's/cols=/rows=/' $DESTDIR/index_frame_right.html > $DESTDIR/index_frame_bottom.html

  mkdir -p $DESTDIR/images
  cp -a $SETUPDIR/images/*png $DESTDIR/images
  sed \
    -e "s/@TRACE@/$inputfile/" \
    -e "s/@TITLE@/$PRGNAME - $TITLE/" \
    -e "s/@ARCHIVE_FILE@/$ARCHIVE_FILE/" \
    $SETUPDIR/images/index.tpl > $DESTDIR/index.html
  [[ $ARCHIVE != "yes" ]] && sed -i '/@@@ARCHIVE_FILE@@@/d' $DESTDIR/index.html

else
  ( echo "Error $PRGNAME: inkscape not found."
    echo "The Scalable Vector Graphic (.svg) has been made, but no"
    echo "Portable Network Graphic (.png) can be created."
    echo "Install inkscape, to enjoy all that $PRGNAME can give to you!"
  ) >&2

  exit 1
fi

if [[ "$ARCHIVE" == "yes" ]]; then

  # Be specific in the files to be included in the archive
  ODIR="$PWD"
  cd $DESTDIR
  DIRS="frames images js"
  FILES=$(find $DIRS -type f)
  cd "$ODIR"

  FILES="$FILES $TRACEFILE callflow.png graph.html"
  FILES="$FILES index_dynamic.html index_frame_bottom.html index_frameless.html"
  FILES="$FILES index_frame_right.html index.html"
  ARCHIVE_FILES=$(tr " " "\n" <<< "$FILES" | sort)

  if [[ "$ARCHIVE_TYPE" == "files" ]]; then
    echo "$ARCHIVE_FILES"
    exit 0
  else

    FILES=$(sed "s,^,$ARCHIVE_NAME/," <<< "$ARCHIVE_FILES")

    cd $DESTDIR/..
    if [[ "$ARCHIVE_TYPE" == "zip" ]]; then
      zip --quiet $DESTDIR/$ARCHIVE_NAME.zip $FILES
    else
      tar cjf $DESTDIR/$ARCHIVE_NAME.tar.bz2 $FILES
    fi
    echo "$PRGNAME: $ARCHIVE_TYPE archive available at \"$DESTDIR/$ARCHIVE_FILE\""
    cd "$ODIR"
  fi
fi

if [[ -n "$CALLFLOW_BASE_URL" ]]; then
  if [[ -z "$CALLFLOW_URL_DIR" ]]; then
    CALLFLOW_URL=$CALLFLOW_BASE_URL
  else
    CALLFLOW_URL=$CALLFLOW_BASE_URL/$CALLFLOW_URL_DIR
  fi
fi

if [[ "$START_BROWSER" == "yes" ]]; then
  # Start the browser in the background
  if [[ -z "$CALLFLOW_URL" ]]; then
    "${BROWSER:=firefox}" "$DESTDIR/index.html" &
  else
    "${BROWSER:=firefox}" "$CALLFLOW_URL/$DESTDIR/index.html" &
  fi
else
  echo
  echo "The output can be viewed with a browser: "
  if [[ -z "$CALLFLOW_URL" ]]; then
    echo " ${BROWSER:=firefox} $DESTDIR/index.html"
  else
    echo " ${BROWSER:=firefox} $CALLFLOW_URL/$DESTDIR/index.html"
  fi
fi

exit 0;

