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

##################################
# Variables
##################################
ARCHIVE=yes
ARCHIVE_TYPE=bz2
CONFDIR=/etc/callflow
CAMEL_CASE=no
removeDF=0 # do not remove duplicate frames
SVG_TO_PDF=no # default value for PDF conversion
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 227 2017-03-15 19:55:09Z rbos $"; #DO NOT EDIT THIS LINE BY HAND, DATE IS AUTOMATICALLY ADDED BY SUBVERSION
readonly PRGVRSN=20190807.01
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
  FARG="-Y"
  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

  # 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.

  --camel-case
                Node names are displayed in a high-low alternating pattern.

  --filter <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".

  --lines-between-nodes
                Lines between nodes determines the number of lines after which the
                node labels are repeated.  Value is not restricted nor checked,
                a sensible value is between 5 and 30.  Default value: 28.

  --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.

  --pdf
                Additionally provide the callflow in pdf.

  --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,camel-case"
LOPT="$LOPT,filter:,help,lines-between-nodes:,list-nodes,no-archive,no-loops"
LOPT="$LOPT,no-sdp,no-time,nodenames:,pdf,refresh-cache,remove-duplicate-frames,title:,with-sdp"
LOPT="$LOPT,width-between-nodes:,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"
                         ;;
  --camel-case)
                         # Force high-low name pattern during SVG generation
                         CAMEL_CASE="always";
                         ;;
  --filter)
                         # Overwrite the default filter previously red from the configuration file
                         OPT_FILTER=$2; shift
                         #echo " * Will use the following filter: $OPT_FILTER"
                         ;;
  --lines-between-nodes)
                         NR_OF_LINES_BETWEEN_NODES=$2; shift
                         ;;
  --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
                         ;;
  --pdf)
                         # Additionally provide the callflow in pdf
                         SVG_TO_PDF=yes
                         ;;
  --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

[[ $(id -u) == 0 ]] && {
  echo "$PRGNAME: error: do not run as user 'root'"
  exit 1
}

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
    FARG="-Y"
    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_NAME=$( basename "$DESTDIR" )
  case $ARCHIVE_TYPE in
    bz2|bzip2)
      ARCHIVE_FILE=$ARCHIVE_NAME.tar.bz2
      ;;
    files)
      ;;
    *)
      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=$( basename "$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

IMAGE_MAP=$TMPDIR/$PRGNAME.imagemap.$$

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

  cat << AWK_BEGIN_SECTION

  title = "$TITLE"

  # Add graph variables
  yLineSpace = 24
  xHostSpace = $WIDTH_BETWEEN_NODES
  yLinesBetweenNodes = ${NR_OF_LINES_BETWEEN_NODES:=28}
  yHostNameSpace = 20
  rightMargin = 100
  topMargin = 50
  bottomMargin = 50
  showSDP = "$SHOW_SDP"
  camelcase = "$CAMEL_CASE"
  showTime = "$SHOW_TIME"
  if ( showTime == "yes" ) {
    leftMargin = 150
  } else {
    leftMargin = 50
  }
  numTraces = numHosts
  imageMap = "$IMAGE_MAP"

  # 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_ARGS="--export-dpi=90 -C --export-background=white --export-png="$DESTDIR"/callflow.png "$DESTDIR"/callflow.svg"
  INKSCAPE_ARGS="--export-dpi=90 -C --export-background=white"
  
  [[ "$SVG_TO_PDF" == "yes" ]] && {
    INKSCAPE_ARGS="--export-pdf="$DESTDIR"/callflow.pdf $INKSCAPE_ARGS"
  }

  # Inkscape does not have a quiet option, it might get one in a future release
  # Workaround; sent the output to /dev/null, when quiet mode is enabled
  if [[ "$INKSCAPE_MODE" == "QUIET" ]] || [[ "$ARCHIVE_TYPE" == "files" ]]; then
    inkscape $INKSCAPE_ARGS --export-png="$DESTDIR"/callflow.png "$DESTDIR"/callflow.svg >/dev/null 2>&1
  else
    inkscape $INKSCAPE_ARGS --export-png="$DESTDIR"/callflow.png "$DESTDIR"/callflow.svg
  fi

  # TODO: should there indeed be a target="_blank" in the callflow.svg or not?
  # Remove the target so the link (frame.html) is opened in the same window where
  # callflow.svg is displayed.  The user has the ability to sent the link to 
  # a new tab or new window
  sed -i 's/ target="_blank"//' "$DESTDIR"/callflow.svg

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

  # Build HTML files
  for INDEX_FILE in dynamic frameless; do

    # Dynamic;   (SIP) Messages will be presented in popups on the callflow picture
    # 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>"
      echo "  <!-- 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 
      echo "p.footer {"
      echo " border-top: 1px solid #aaa;"
      echo " font-size: small;"
      echo " padding: 0.1em 1em 0 1em;"
      echo "}"
      echo "  </style>"

      [[ $INDEX_FILE == "dynamic" ]] && {
        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 "$IMAGE_MAP"
      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/>"
      }

      [[ "$SVG_TO_PDF" == "yes" ]] && {
        echo "    Callflow in <a href=\"callflow.pdf\">PDF</a> format"
        echo "    <br/>"
      }

      echo "    <a href=\"index.html\" target=\"_parent\">Back to landing page</a>"
      echo "   </p>"
      echo "  </div>"
      echo " </body>"
      echo "</html>"

    ) > "$DESTDIR"/index_$INDEX_FILE.html

  done

  rm "$IMAGE_MAP"

  #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
  [[ "$SVG_TO_PDF" != "yes" ]] && sed -i '/@@@PDF_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
  cd "$DESTDIR"
  DIRS="frames images js"
  FILES=$(find $DIRS -type f)

  FILES="$FILES $TRACEFILE callflow.svg callflow.png graph.html"
  FILES="$FILES index_dynamic.html index_frame_bottom.html index_frameless.html"
  FILES="$FILES index_frame_right.html index.html"
  [[ "$SVG_TO_PDF" == "yes" ]] && {
    FILES="$FILES callflow.pdf"
  }

  ARCHIVE_FILES=$(tr " " "\n" <<< "$FILES" | sort)

  if [[ "$ARCHIVE_TYPE" == "files" ]]; then
    echo
    echo "=== Files in callflow - BEGIN: $ARCHIVE_NAME ==="
    echo "$ARCHIVE_FILES"
    echo "=== Files in callflow - END: $ARCHIVE_NAME ==="
    echo
  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\""
  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
  echo "Launching ${BROWSER:=firefox}..."
  if [[ -z "$CALLFLOW_URL" ]]; then
    "${BROWSER:=firefox}" "$DESTDIR/index.html" >/dev/null &
  else
    "${BROWSER:=firefox}" "$CALLFLOW_URL/$DESTDIR/index.html" >/dev/null &
  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;

