#!/bin/sh
version=7.8.1

#set -vx

license="Copyright (C) 1996-2009, 2011-2017, 2023-2025 Dimitar Ivanov

License: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law."

################################################################################
#
# muplot - gnuplot-wrapper for non-interactive plotting of multiple data files
#
# This program allows multiple data files to be viewed or printed by 'gnuplot'
# on a single multi-curve plot.
#
################################################################################
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
#
# Variables
#
progname=$(basename $0)
cmdstr="$progname $*"
bfname="$progname"                       # Default output-base-file-name
set_file=".${progname}set"               # External file with gnuplot commands
comm_file="$set_file"
comm_file_ignore="$comm_file.noglobal"   # Ignore global command file if touched
gdevice_def=x11                          # Output graphics device (default X11)
gdevice="$gdevice_def"                   # Init Output graphics device
gstyle=linespoints                       # Plot style (default lines and points)
out_form="unknown"                       # PS, PNG, JPG ok - for PDF see help
pscolor=color                            # PS/PDF color
psfont="'Helvetica' 16"                  # PS font type + size
pdfsize="size 29.7cm,21cm"               # PDF plot size (a4 landscape)
pdffont="font 'Arial,22'"                # PDF font + size
stdout=no                                # Do not send PS file to stdout
quiet=no                                 # Don't suppress info messages
dry_run=no                               # Not a dry-run: run gnuplot eventually
opt_landscape=""                         # Landscape option for a plot viewer
file_list_sort=yes                       # Sort the list of files
stdin_data=no                            # Read plot data from STDIN
reload_data=no                           # Reload data flag for quasi real-time
reload_conf="8640:10"                    # Default data reload config values

[ "$(env |grep ^MUPLOT_MISSING_DATA_AXES=)" ] \
|| MUPLOT_MISSING_DATA_AXES="Y"          # Which data columns can miss values

[ "$MUPLOT_DIR_TMP" ] \
|| MUPLOT_DIR_TMP=.

[ "$MUPLOT_FILE_STDIN" ] \
|| MUPLOT_FILE_STDIN=""

[ "$MUPLOT_PLOT_SIZE" ] \
|| MUPLOT_PLOT_SIZE="800,600 font 'Arial,16'"

################################################################################
#
# Functions
#

### Define names of temporary/working files
#
_define_output_tmpfile_names_()
{
  tmpfile="$MUPLOT_DIR_TMP/$progname.$$"   # Tmp work file
  [ "$MUPLOT_FILE_STDIN" ] \
  && tmpstdin="$MUPLOT_FILE_STDIN" \
  || tmpstdin="$tmpfile.stdin"             # Tmp stdin, but see also opt '-O'

  gpout="$tmpfile.gpt"                     # Tmp gnuplot-script output file
  gperr="$tmpfile.err"                     # Tmp gnuplot-script error file
}

### Read gnuplot commands from file
#
_process_gnuplot_commands_file_()
{
  test ! -f "$comm_file_ignore" \
     && _guplot_command_print_blocks_ $1 "$HOME/$2" "$3"
  test "$(pwd)" != "$HOME" \
     && test "$ignore_local_comm" != "yes" \
     && _guplot_command_print_blocks_ $1 "$2" "$3"
}

### Read external file with gnuplot commands and print out used blocks
#
_guplot_command_print_blocks_()
{
   _mode=$1 ; _comm_file="$2" ; _out_file="$3" ;

   if [ -f $_comm_file ]; then
       if   [ $_mode -gt 0 ]; then
            cat "$_comm_file" |sed -n '/^#BEGIN/,/^#END/!p'
       elif [ -n "$(grep '^#BEGIN' $comm_file)" ]; then
              # print everything btw. BEGIN and END
            echo "set out $_out_file"
            cat "$_comm_file" |sed -n '/^#BEGIN/,/^#END/p'
            echo "replot"
       fi
  fi
}

### $LIST_FILES will be a command providing the list of files to be plotted
#
_prepare_list_of_files_to_plot_()
{  files_data=""
   test -z "$1" && return 1

      # Last sanity check of the cmdline file-list string syntax:
   if [ "$(echo $1 |sed 's/^-..*/BAD/')" = BAD ]; then
        echo "Error: your file name can't start with '-'"
        _cleanup_and_exit_ 9
   fi

      # If input is not defined as the stdin, then evaluate the file list
   if [ "x$1" = "x-" ]; then
        files_data="-"
        LIST_FILES="ls -1 $tmpstdin"
   elif [ $file_list_sort = yes ]; then
        files_data=$(eval ls "$1") || _cleanup_and_exit_ 10
   else
        files_data="$1"
   fi

      # If a single file, then basename is defined using it
   if   [ $(ls -1 "$files_data" 2>/dev/null |wc -l) -eq 1 ]; then
          LIST_FILES="echo $files_data"
             # Define bfname only once
          if [ x$list_nr = x ]; then
               bfname="$files_data"
                  # Remove extension from name
               bfname=$($LIST_FILES |sed "s/\.[^\.]*$//")
          fi
   elif [ "x$files_data" != "x-" ]; then
          LIST_FILES="_do_echo_file_name_from_list_ $files_data"
   fi
   list_nr=$(expr $list_nr + 1)

   if [ "$files_data" = "-" -a $reload_data = "yes" ]; then
     if [ -z "$MUPLOT_FILE_STDIN" ]; then
          echo "Error: plotting continuously data from <stdin> is possible only when using '-r' together with '-O'"
          _cleanup_and_exit_ 15
     fi
   fi

   return 0
}

### Just print each file name from in the list of files
#
_do_echo_file_name_from_list_()
{
  for f in $*; do echo $f; done
}

### Extract multiple data sets from the input files and write them into
#   separate data files - one file per data set
_extract_multi_data_file_()
{ e_arg=$1 ;

  d_tmp_dir="$MUPLOT_DIR_TMP"
  d_file_ext=mpt.dat.$$

  rex=$(echo $e_arg | cut -f1 -d:)
  rex_cn=$(echo $e_arg | cut -f3 -d:)
  [ "$rex_cn" -a "$rex_cn" != "$rex" ] || rex_cn=1

  grep_opt=$(echo $e_arg | cut -f2 -d:)
  [ "$rex" = "$grep_opt" ] && grep_opt=""

  sort_opt=$(echo $e_arg | cut -f4 -d:)
  [ "$rex" = "$sort_opt" ] && sort_opt=""

  maph_opt=$(echo $e_arg | cut -f5 -d:)
  [ "$rex" = "$maph_opt" ] && maph_opt=""

  [ "$data_FS" ] && awk_FS="-F$data_FS"

     # Define MULTI-DATA-SET extractor function
  perl --version >/dev/null 2>&1
  if [ $? -eq 0 ]
  then # Use PERL (which is faster++ than 'awk')

extract_multi_data_sets()
{
  PERL_MPT_REX_CN=$(expr $1 - 1)  ; export PERL_MPT_REX_CN
  PERL_MPT_TMP_DIR=$2             ; export PERL_MPT_TMP_DIR
  PERL_MPT_FILE_EXT=$3            ; export PERL_MPT_FILE_EXT

  perl -lane \
       'BEGIN {
           our %RECs=();
           our $d_cn  = $ENV{'PERL_MPT_REX_CN'};
           our $d_tmp = $ENV{'PERL_MPT_TMP_DIR'};
           our $d_ext = $ENV{'PERL_MPT_FILE_EXT'};
        }

        $RECs{$F[$d_cn]} .= "$_\n";

        END {
           my $rc = q{0};
           my ($k, $kk, $fn) = ();
           for $k (keys %RECs) {
               ($kk = $k) =~ s:/:_:g;
               $fn = "${d_tmp}/$kk.${d_ext}";
               open( my $FH, "> $fn" );
               if ( $FH ) {
                    printf( $FH "%s", "$RECs{$k}" );
                    close( $FH );
               } else {
                    $rc=q{1};
               }
           }
           return $rc;
        }'
}

  else # Use AWK

extract_multi_data_sets()
{
  $AWK $awk_FS -v d_cn=$1 -v d_tmp=$2 -v d_ext=$3 \
  '{ data[$d_cn] = sprintf( "%s%s\n", data[$d_cn], $0 ); }
   END \
   { for( v in data )
     {
       v_v = v
       gsub( "/", "_", v_v )
       d_file = sprintf( "%s/%s.%s", d_tmp, v_v, d_ext )
       print data[v] > d_file
     }
     if   ( v ) exit 0 ;
     else       exit 1 ;
   }'

return $?
}

  fi

     # Remove all comments
  zgrep -h -v '^.*#' $($LIST_FILES) \
  | egrep $grep_opt "$rex" \
  | extract_multi_data_sets $rex_cn $d_tmp_dir $d_file_ext

  if [ $? -eq 0 ]; then
       [ "$d_tmp_dir" != "." ] \
       && files_data=$(ls $d_tmp_dir/*.$d_file_ext 2>/dev/null) \
       || files_data=$(ls *.$d_file_ext 2>/dev/null)

       if [ $? -ne 0 ]; then
            echo "Error: failed to extracted data from multiple sets"
            _cleanup_and_exit_ 16
       else
            [ "$sort_opt" ] \
            && act=sort \
            || act=""

            [ "$maph_opt" ] \
            && maph=maphimbu \
            || maph=""

                # Try to do sorting or otherwise simply rename
            for _f in $files_data
            do
                _nf=$(echo $_f | sed 's;\(.*\)\.[0-9]*;\1;g')

                if [ "$act" ]; then
                     $act $sort_opt $_f > $_nf \
                     && rm $_f
                else
                     mv $_f $_nf
                fi

                if [ "$maph" ]; then
                     $maph $maph_opt $_nf > $_nf.hi.$$ \
                     && mv $_nf.hi.$$ $_nf
                fi
            done

            d_file_ext=mpt.dat
            [ "$d_tmp_dir" != "." ] \
            && files_data=$(ls $d_tmp_dir/*.$d_file_ext 2>/dev/null) \
            || files_data=$(ls *.$d_file_ext 2>/dev/null)

       fi

          # The list must be non-empty
       [ "$files_data" ] \
       && LIST_FILES="ls -1 $files_data"

       [ "$MULTI_DATA_SETS_KEEP" = "no" -a "$files_data" ] \
       && MULTI_DATA_SETS_KEEP="$($LIST_FILES)" \
       || MULTI_DATA_SETS_KEEP=""
  else
       echo "Warn: no data from multiple sets has been extracted"
       MULTI_DATA_SETS_KEEP=""
  fi
}

### Define output file names
#
_define_output_file_names_()
{
  _ofname="$1"

  [ "x$_ofname" != x ] && bfname="$_ofname"
  if   [ $out_form != "unknown" ]; then
       ofile="$bfname.$out_form"          # Output file name
       ofile_nstr="'$bfname.$out_form'"   # Gnuplot file name string
  fi
  if [ $stdout = yes ]; then
       bfname=$tmpfile                    # Use a tmp-file if writing to STDOUT
       ofile="$bfname.stdout"
       ofile_nstr="'$bfname.stdout'"
  fi
}

### Define output driver
#
_define_output_driver_()
{
  case $1 in
      ps) gdevice="postscript enh landscape $pscolor $psfont"
             # Define DIN A4 ratio of the plot relative to the canvas size
          echo "set size ratio 0.71"
       ;;
     png) gdevice="png notransparent $termopt"
       ;;
     jpg) gdevice="jpeg $termopt"
       ;;
     pdf) gdevice="pdfcairo $pscolor $pdffont $pdfsize"
       ;;
       *) [ "$gdevice" ] && gdevice="$gdevice_def"
       ;;
  esac
}

### Determine the plot style
#
_define_plot_style_()
{
      # Evaluate plot style define by the env. variables MUPLOT_GSTYLE_{1..9}
  for i in 1 2 3 4 5 6 7 8 9
  do
      eval gstyle="\${MUPLOT_GSTYLE_$i}"
      [ "$gstyle" ] \
      && echo "set style $gstyle" \
      && MUPLOT_GSTYLE=defined
  done

  while [ 1 ]; do
     case $1 in
        g) gstyle=lines
           echo "set grid"
        ;;
        xlog) echo "set logscale x"
        ;;
        ylog) echo "set logscale y"
        ;;
        d) gstyle=dots
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="$gstyle ls %d"
        ;;
        p) gstyle=points
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="$gstyle ls %d"
        ;;
       pp) gstyle="points pt 6"
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="points ls %d pt 6"
        ;;
        l) gstyle=lines
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="$gstyle ls %d"
        ;;
       lp) gstyle=linespoints
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="$gstyle ls %d"
        ;;
       lc) gstyle="linespoints pt 7 lw 2 ps 1.2"
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="linespoints ls %d pt 7"
        ;;
       fl) gstyle="filledcurves x1 lw 2"
           echo "set style fill transparent solid 0.1 border"
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="filledcurves x1 ls %d"
        ;;
       fc) gstyle="filledcurves x1"
           echo "set style fill transparent solid 0.1 border"
           gstyle_2nd="linespoints ls %d pt 7 lw 2 ps 1.2"
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="filledcurves x1 ls %d" \
           && gstyle_2nd="linespoints ls %d pt 7"
        ;;
       fb) gstyle=boxes
           echo "set style fill solid 0.5 border"
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="$gstyle ls %d"
        ;;
        b) gstyle=boxes
           [ "$MUPLOT_GSTYLE" = "defined" ] \
           && gstyle="$gstyle ls %d"
        ;;
        e) gstyle=errorlines
           sample=1:2:3
        ;;
        a) gstyle=dots
           # In case of arrows $files_data must be set before further processing
           _prepare_list_of_files_to_plot_ "$2" bfname files_data
           if [ "x$files_data" != "x-" ]; then # Input from file(s) produced by
                cut -f3- $files_data           # 'prefield' - delimiter is a TAB
           else # Make a copy of stdin
                tee $tmpstdin |cut -f3-
           fi
        ;;
      s=*) echo "set $(echo $1 |cut -f2- -d=)"
        ;;
      u=*) gstyle="$(echo $1 |cut -f2- -d=)"
        ;;
     dt=*) test -z "$gstyle" && gstyle=linespoints
              # Format in case of date/time data
           if [ x"$(echo $1 |grep 'dt=')" != x ]; then
              echo "set xdata time"
              fmt="$(echo $1  |cut -f2 -d= |cut -f1 -d@)"
              fmtx="$(echo $1 |cut -f2 -d= |cut -f2 -d@)"
              echo "set timefmt '$fmt'"
              echo "set format x '$fmtx'"
              sample=1:2
           fi
        ;;
       3d) gplot_cmd=splot
           sample=1:2:3
        ;;
       te) tit_str_esc=true
        ;;
       nk) echo "unset key"
        ;;
        *)
           break
        ;;
     esac
  shift
  _ns=$(expr $_ns + 1)
  done

return $_ns
}

### Print gnuplot command(s) if specified from command line option
#
_print_gnuplot_cmdl_command_()
{
  _gpcmd="$1" ; _gpdev="$2" ; _gpfn="$3" ;

  if [ "$_gpdev" ]; then
  cat << EOC0
set term $_gpdev
set out $_gpfn
EOC0
  fi

  cat << EOC1
$_gpcmd
EOC1

return 0
}

### Find out samples to plot
#
_tell_me_samples_to_plot_()
{
  if [ -n "$1" ]; then
       lsample="$1"
       lsample=$(echo $lsample |tr ',' '\040') # Separate ranges by blanks
       rc=0
  else
       if [ -z "$sample" ]; then
            lsample="0:1-0"
       else
            lsample="$sample"
       fi
       rc=1
  fi
  echo $lsample
return $rc
}

### Loop for multiple data ranges applied to the current set of files and
#   print out plot statements
#
_plot_various_data_ranges_()
{
  _sample=$1
  [ "$files_data" = "-" ] && _gtitle="stdin"

  test -z "$gplot_cmd" && gplot_cmd=plot
  lastabsc=$(echo $_sample |cut -f1 -d:)

  [ "$line_style_nr" ] \
  || line_style_nr=1;

  i=1; I=1;
  for j in $_sample
  do   # DATA_RANGE begin
     absc=$(echo $j |cut -f1-2 -d:)                           # 3-d
     [ "$absc" = "$j" ] && absc=$(echo $absc |cut -f1 -d:)    # 2-d
     first=$(echo $j |cut -f1 -d- |$AWK -F: '{print $NF}')
     last=$(echo $j |cut -f2 -d-)
     [ "$last" = "$j" ] && last=$first
     [ "$absc" = "$j" -a "$i" -eq 1 ] && absc=0
     [ "$absc" = "$j" -o "$absc" = "$first" ] && absc=$lastabsc
     lastabsc=$absc
        # In case of styles that need 3 data columns
     echo $j |egrep "^[0-9]+:[0-9]+:[0-9]+$" >/dev/null 2>&1 && absc=$j

        # FILE_LOOP: prepare and print plot statements for all files in the list
     $LIST_FILES \
     |$AWK -v a_nrs=$I -v a_of=$ofile_nstr -v a_te=$tit_str_esc \
           -v a_pcmd=$gplot_cmd -v a_pipe="$program_data_pipe" \
           -v a_tit=$_gtitle -v a_sty="$gstyle" -v a_sty_2="$gstyle_2nd" \
           -v a_lsn=$line_style_nr -v a_x=$absc -v a_f=$first -v a_l=$last \
           'BEGIN \
            {
              pcmd_2 = "" ; axes = "" ;
              printf("%s ", a_pcmd)
            }
            {
                   # Define the filename or input data pipe
              if( ! a_pipe ) a_fname = sprintf("%s", $0)
              else           a_fname = sprintf("< %s %s", a_pipe, $0)

              t_a_fname = a_fname
                   # Escape some chars in the filename for the title string
              if( a_te ) gsub( "_", "\\_", t_a_fname )

              if( a_tit ~ /^stdin$/ )
                  tkey = " title \x27<stdin>\x27"
              else
                  tkey = ""

                   # Set linestyle number
              tsty=sprintf(a_sty, a_nrs)
                   # Loop over range
              for( i=a_f; i<=a_l; i++ )
              {
                    # plot with 3-data columns
                if( a_x ~ /^[0-9]+:[0-9]+:[0-9]+$/ )
                    axes = a_x
                else
                    axes = sprintf("%d:%d", a_x, i)

                if( a_tit ~ /^stdin$/ )
                    tkey = sprintf(" title \x27<stdin> %s\x27", axes)
                else
                    tkey = sprintf(" title \x27%s %s\x27", t_a_fname, axes)

                tsty = sprintf(a_sty, a_nrs)
                printf("\x27%s\x27 using %s with %s%s, ", \
                       a_fname, axes, tsty, tkey)
                    # Prepare for replot with a 2nd plot style if defined
                if( a_sty_2 != "" ) {
                    tsty_2 = sprintf(a_sty_2, a_lsn++)
                    if( i == a_l )
                        pcmd_2 = sprintf("%s""set out '%s'\n", pcmd_2, a_of)
                    pcmd_2 = sprintf("%s""replot \x27%s\x27 using %s " \
                                     "with %s title \x27\x27\n", \
                                     pcmd_2, a_fname, axes, tsty_2)
                }
                a_nrs++
              }

              if( a_l == 0 && a_l != a_f )
                  printf("\x27%s\x27 with %s%s, ", a_fname, tsty, tkey)

                  # Prepare for replot with a 2nd plot style when no axes spec
              if( axes == "" && a_sty_2 != "" ) {
                  tsty_2 = sprintf(a_sty_2, a_lsn++)
                  pcmd_2 = sprintf("%s""set out '%s'\n", pcmd_2, a_of)
                  pcmd_2 = sprintf("%s""replot \x27%s\x27 with %s title " \
                                   "\x27\x27\n", pcmd_2, a_fname, tsty_2)
              }
              a_nrs++
           }
           END \
           {
             printf "\n"
                 # Replot wiht a 2nd plot style if 'replot' command prepared
             if( pcmd_2 != "" ) printf("%s", pcmd_2)
           }' > $tmpfile

     if [ "$gdevice" ]; then
          echo "set term $gdevice"
          echo "set out $ofile_nstr"
     fi

        # Paste the script saved in the tmpfile - remove last coma followed
        # by blank before end of line (have been excessively produced in the
        # FILE_LOOP)
     cat $tmpfile |sed 's/\, $//'

        # Plots after the first one must be re-plotted
     gplot_cmd=replot
     i=$(expr $i + 1)
     I=$(expr $I + 1)
        # This variable is used only when replot with a 2nd line style is needed
     line_style_nr=$(expr $line_style_nr + 2)
  done # DATA_RANGE end
}

### In the prepared gnuplot-script replace plotting commands where the string
#   "using x:y[:z]'" is found with the corresponding formatting for missing data
#
_adjust_plotting_commands_for_missing_data_()
{ gpts=$1 ;

  for col in $(echo $MUPLOT_MISSING_DATA_AXES |sed 's/\([XYZ]\)/\1\n/g')
  do
     case $col in
       X) sed -i 's/ using \([0-9]*\):/ using ($\1):/g' $gpts
       ;;
       Y) sed -i 's/\( using [0-9\$)(]*\):\([0-9]*\)/\1:($\2)/g' $gpts
       ;;
       Z) sed -i 's/\( using [0-9\$)(]*\):\([0-9\$)(]*\):\([0-9]*\)/\1:\2:($\3)/g' $gpts
       ;;
     esac
  done
}

### Print gnuplot script file with on terminal
#
_gnuplot_script_print_to_terminal_()
{
  if   [ $stdout != yes -a $quiet != yes -a $reload_data = "no" ]; then
       :
  elif [ $dry_run = "yes" ]; then
       :
  else
       return 
  fi

  echo "# --- GNUPLOT script ---"
  cat "$1" |egrep -v "^[ ]*\#|^$"
  echo ""
}

### Print gnuplot script errors
#
_gnuplot_errors_print_to_terminal_()
{
  echo "" 1>&2
  echo "# --- GNUPLOT ERRORS ---" 1>&2
  cat "$1" 1>&2
}

### Ask to display plot
#
_ask_to_display_plot_()
{
   _ofile="$1" ; _viewer="$2" ;

   [ $reload_data = "yes" ] \
     && return 0

   echo "# Show picture? [y/N]"
   read answer
   if [ "$answer" = y -o "$answer" = Y ]; then
           # Consider the first word in the string to be the executable,
           # the rest are options
        viewer=$(echo $_viewer |cut -f1 -d' ')
        if [ ! "$(_find_exec_in_path_ $viewer)" ]; then
             echo "Warn: '$viewer' is not installed or is not in your \$PATH"
        else
             $_viewer $opt_landscape "$_ofile" &
        fi
   fi
}

### Check whether an executable program is in the path
#
_find_exec_in_path_()
{
  which $1 2>&1 |grep "^/"
}

### Verify that an input filename is provided
#
_exit_on_empty_filelist_()
{
  if [ -z "$1" ]; then
       echo "Error: please provide a file name(s) or use '-' for <stdin>"
       _cleanup_and_exit_ 14
  fi
}

### Look for GhostView installation
#
_look_for_ghostview_()
{
  _err=/tmp/.$progname.$$

  MUPLOT_VIEWER="$(_find_exec_in_path_ gv)"
  if [ ! "$MUPLOT_VIEWER" ]; then
       MUPLOT_VIEWER=ghostview
  fi

     # For better view, show PS-plots in landscape orientation
  opt_landscape="-landscape"

  if [ "$(_find_exec_in_path_ $MUPLOT_VIEWER)" ]; then
       test -n "$opt_landscape" \
            && $MUPLOT_VIEWER $opt_landscape /dev/null 2>&1 \
               |grep "orientation=" > $_err
       test -s $_err && opt_landscape="--orientation=landscape"
       rm -f $_err
  fi
}

### Print the raw plot to stdout
#
_print_raw_plot_()
{
   _ofile="$1"

   [ $out_form = ps ] \
     && cat $_ofile |sed "s;%%Title:.*;%%Title: $cmdstr;" \
     || cat $_ofile
}

### Define the variables used on data reload
#
_data_reload_setup_()
{
   reload_tot_nr=$(echo $reload_conf | cut -f1 -d:)
   reload_int=$(echo $reload_conf | cut -f2 -d:)
   reload_tail=$(echo $reload_conf | cut -f3 -d:)
   [ "$reload_tail" ] \
      && program_data_pipe="tail -n$reload_tail"
}

### Append gnuplot commands for data reload to the end of the gpt-script
#
_data_reload_initialize_()
{
   [ "$reload_int" ] \
   || reload_int=1

   if [ "$gdevice" = x11 ]; then
        set_output=""
   else
        set_output="set out $ofile_nstr;"
   fi

   echo | ($AWK "{ 
                   for( i=1; i<$reload_tot_nr; i++ ) 
                        print \"pause $reload_int; $set_output replot;\"
                 }"
          ) >> $gpout

   _cleanup_before_data_reload_started_
}

### Partial cleanup in case of continuous data reload
#
_cleanup_before_data_reload_started_()
{
   [ ! -s "$ofile" -o $stdout = yes ] && rm -f "$ofile"
   rm -f $tmpfile
}

### Prepare to remove various files after work finished and set a clean trap
#
_cleanup_and_exit_()
{
   [ ! -s "$ofile" -o $stdout = yes ] && rm -f "$ofile" # Remove if zero size
   rm -f $gpout $gperr $tmpfile $tmpfile.* $MULTI_DATA_SETS_KEEP
   exit $1
}
trap '_cleanup_and_exit_ $1' 1 2 3 6 15

### Show usage
#
_show_usage_()
{
d=$2
cat << END_HELP
$separator
$1
$separator

Usage: $progname [OPTION..] [STYLE..] [FILE] [XCOL:YCOL] [FILE] [XCOL:YCOL] ...

Options:
   --help|-H  $d display help
   -h         $d display short help
   -V         $d print program version number
   -T <dir>   $d directory for temporary output files
   -c <cmd>   $d execute gnuplot command(s)
   -o <str>   $d base name of the output file or use '-' for <STDOUT>
   -O <file>  $d filename to save data read from <STDIN> (i.e. preserve tmp-file)
   -s         $d create PostScript-file
   -S         $d send PostScript output to STDOUT (the same as '-s -o -')
   -n         $d create PNG-file
   -j         $d create JPEG-file
   -p         $d create PDF-file (requires the gnuplot "pdfcairo" driver)
   -X         $d don't set the terminal to '$gdevice_def' (use gnuplot's default)
   -m         $d monochrome plot (valid for PostScript or PDF)
   -l         $d set plot size to "$MUPLOT_PLOT_SIZE" (PNG and JPEG only!);
                You can define your own size by setting the env variable:
                MUPLOT_PLOT_SIZE="<y_size>,<x_size> [font '<f_type>,<f_size>']"
   -q         $d quiet mode (all messages except errors will be suppressed)
   -i         $d ignore local command file './$comm_file'
   -I <file>  $d specify a gnuplot-command file instead of './$comm_file'
   -r0        $d reload data files continuously (default $reload_conf)
   -r <N:T:n> $d reload data files continuously using the specified values;
                <N> is the number of replots, <T> is the time period btw.
                replots, and optionally, <n> is the number of last lines to plot
   -e <regex> $d extract data from multiple data sets but don't keep '*.mpt.dat';
                The extended argument format consists of multiple options (for
                various programs) controlling the process of data extraction:
                <regex:grep_opt:awk_nc:sort_opt:maphimbu_opt>.
   -E <regex> $d extract data from multiple data sets and keep '*.mpt.dat';
                The extended argument format consists of multiple options (for
                various programs) controlling the process of data extraction:
                <regex:grep_opt:awk_nc:sort_opt:maphimbu_opt>.
   -F <str>   $d input-data field separator (default is a single space character)
   -U         $d do not sort the file list
   -P <prog>  $d define a program for processing the data before plot the output;
                For example: -P 'awk "{print \\\\\$3, \\\\\$1}"'
   -C         $d connect linepoints on skipping data due to missing values
   -D         $d dry run - do nothing, only print the prepared gnuplot script

Styles/Settings:
   nk         $d do not plot keys (skip filename lables)
   te         $d escape some special characters in the <filename> as title
   g          $d show grid
   l          $d lines
   p          $d points
   pp         $d circle points
   lp         $d lines and points
   lc         $d lines with filled circle points
   fl         $d filled curves
   fc         $d filled curves with filled circle points
   fb         $d filled boxes
   b          $d boxes
   d          $d dots
   a          $d fields with arrows;
                The data file has a special format in this case. Use 'prefield'
                to prepare such data files.
   e          $d errorbars - default used columns are 1:2:3 (x:y:yerror);
   dt=<fmt>   $d date/time series with the specified format;
                For example: dt="%H:%M.%S@%H:%M" where the first part, in front
                of "@", defines the data format, and the second part defines the
                format that will be used for tic labels. Here, hours and minutes
                are separated by \`:', respectively minutes and seconds by \`.'
                Another example is date and time stamp: dt="%Y-%m-%d %H:%M:%S"
   3d         $d plot 3-d data using 1:2:3
   u=<fmt>    $d user specified plot style format (as defined in Gnuplot);
                For example: u="points pointtype 2 pointsize 3"; To see the
                present terminal and palette capabilities of gnuplot use the
                command '$progname -c test'.
   xlog       $d set log-scale for the x-data
   ylog       $d set log-scale for the y-data
   s=<opt>    $d user specified setting (as defined in Gnuplot);
                For example: s="logscale x"
   
Axes:
   x:y,x:y-z  $d columns in the file defining the x/y-axes of the curve(s);
                Default is 1:2 or 1:2:3 for data with errors. In case that only
                one column is provided the default axes are 0:1, i.e. the x-axis
                is a simple index.


Environment:

Up to 9 user-defined line styles can be provided by the environment variables
MUPLOT_GSTYLE_{1..9}), e.g.: MUPLOT_GSTYLE_2="line 1 lt 5 lw 2 lc rgb '#007AAA'".
They will be automatically defined as line styles in the gnuplot script output.

In case you want to view the plot-file, define the env-variable 'MUPLOT_VIEWER',
for example:

   MUPLOT_VIEWER="xpdf -z page"

Then the program will prompt you to view the plot, and after confirmation, the
viewer will present the graphics. If the postscript file format is chosen
('-s' option), and 'MUPLOT_VIEWER' is not defined, the viewer is preset to 'gv',
and per default you are prompted to view the output. To disable this behavior
define MUPLOT_VIEWER="" or execute the program in quiet mode (option '-q').

During execution the program is creating temporary files in your current
directory. This behavior can be changed by setting 'MUPLOT_DIR_TMP' to your
preferred location or alternatively use the '-T' option. On reading input data
from <STDIN> a temporary file (with quasi-random) name is created in the
tmp-directory defined by 'MUPLOT_DIR_TMP'. This file is deleted at the end.
If you want to keep the input data (and don't delete the tmp-file), then
configure its name by the variable 'MUPLOT_FILE_STDIN' or respectively use
the '-O' option.

In case of missing y-data points in 2-d plots, the resulting curve is presented
as disconnected parts of separated curves. To control the plotting behavior in
such case, the env-variable MUPLOT_MISSING_DATA_AXES (with default value '$MUPLOT_MISSING_DATA_AXES') 
can be set, e.g. MUPLOT_MISSING_DATA_AXES="XY". Further, by using option '-C'
the variable is overridden and the final curve is shown as a continuous line.


Files:

The [FILE] to plot can specify a single filename, or multiple filenames enclosed
in quotation marks as for example "file_1 file_2.dat data_3.log". It could be
also any valid shell pattern like "*.dat". Use '-' as filename to define that
the input is read from <stdin>. 

The files '\$HOME/$comm_file' and './$comm_file', if existing, will be included
at the beginning of the gnuplot script. The command block between "#BEGIN" and
"#END" in those files will be pasted to the end of the script. If you want that
the global '\$HOME/$comm_file' is ignored, create in your local directory
a file named '$comm_file_ignore'.


Examples:

1) On X-terminal view a multi-curve plot of all data-files with extension 'dat'

   $progname "*.dat"

2) Print a sinus curve in black-and-white color on a PostScript-printer

   $progname -m -S -c "set title 'Function f(x)=sin(x)'; plot sin(x);" | lpr

3) Plot data from file "example.dat" using columns 1:2, 3:4, and 3:5 as x/y-axes in the multi-curve plot; a PostScript-file with the name "example.ps" is automatically created.

   $progname -s example.dat 1:2,3:4-5

4) Create graphics in PDF-format reading data from file "example.1.dat" (columns 1:2), and from file "example.2.dat" (columns 3:4)

   $progname -p lp example.1.dat 1:2 example.2.dat 3:4

5) Print a filled curve with data read from standard input; The input data are saved into file '/tmp/${progname}_test.dat'.

   printf "1 1\n1 2\n2 2\n2 1\n1 1\n" | $progname -X -O "/tmp/${progname}_test.dat" s="xrange [0:3]" s="yrange [0:3]" fc -

6) View file where the first column is data, and the third and forth columns represent a date of the form 'yyyy-mm-dd' and time in the form 'hh:mm:ss'

   cat example_counts_per_second.dat | $progname dt="%Y-%m-%d %H:%M:%S" - 3:1

7) Plot 3-dimensional data from file "example_3d.dat" using the 1,3, and 5-th data columns with dots-plot-style, enabling grid, setting the xrange to [0:10], disabling keys and defining a plot-title

   muplot nk g d 3d s="xrange [0:10]" s="title 'This is a 3-d plot'" example_3d.dat 1:3:5

8) Replot data 1000 times every 5 seconds and write temporary created files in the '/tmp' directory; This scenario is useful in case of growing or otherwise changing over time data-file.

   muplot -T /tmp -r 1000:5 example.dat

9) Produce a data sequence of the sinus function {sin(x) x}, then exchange the first and second column, whereas the cosinus of the sin(x) is evaluated and shifted by -0.5403, such that the new function has its zero-values as minimum at x = -1.5708 and x = 1.5708

  nnum -3.14159 3.14159 0.01 "f[x]=sin(x)" "%f %f" | muplot -P 'awk "{print \\\\\$2, (cos(\\\\\$1)-0.5403)}"' lp -


Report bugs to <gnu@mirendom.net>

END_HELP

}

### Print out license
#
print_licence_version()
{
  cat << _VERSION_
$progname $version
$license
_VERSION_
}

### Print out usage (short help)
#
print_usage()
{
  separator=$(echo |$AWK '{printf( "%080d", 0 )}' |tr 0 -)
  header_text="$progname $version: plot a multi-curve figure from multiple data by using Gnuplot"
  _show_usage_ "$header_text" "-" \
       |egrep "^($progname|Usage:|Options:|Styles.*:|Axes:|.*--|.*[\ >]\ -\ |.*\,x:y-z)"
}

### Print out Help (long help)
#
print_help()
{
  separator=""
  header_text="Muplot is a simple, non-interactive gnuplot-wrapper to plot a multi-curve figure from multiple data files. It can produce PostScript, PDF, PNG or JPEG output file formats."
  _show_usage_ "$header_text" " "
}

################################################################################
#
# MAIN

   # We need an AWK supporting assignments
if [ x"$AWK" = x ]; then
     for a in gawk nawk awk
     do
        [ "$(set +x; echo | $a -v a=a '{}' 2>&1)" = "" ] && AWK=$a
     done
fi
[ x"$AWK" = x ] && \
  echo "Error: can't find 'awk' program supporting assignments" && \
  exit 12

[ $# -eq 0 ] && set -- "-h"

   # Process cmdline options
while [ 0 ]
do
case $1 in
    -h)           # Print usage (short help) and exit
        print_usage
        exit 0
     ;;
    -H|--help)    # Print help and exit
        print_help
        exit 0
     ;;
    -V)           # Print version and exit
        echo $version
        exit 0
     ;;
    -v|--version) # Print version and license and exit
        print_licence_version
        exit 0
     ;;
    -s) out_form=ps
        [ "$(env |grep ^MUPLOT_VIEWER=)" ] || _look_for_ghostview_
     ;;
    -S) out_form=ps
        stdout=yes
     ;;
    -n) out_form=png
     ;;
    -j) out_form=jpg
     ;;
    -p) out_form=pdf
     ;;
    -P) program_data_pipe="$2"
        shift
     ;;
    -X) gdevice=""
     ;;
    -r) reload_data=yes
        reload_conf="$2"
        shift
     ;;
   -r0) reload_data=yes
     ;;
    -c) gpt_cmd="$2"
        shift
     ;;
    -l) termopt="large size $MUPLOT_PLOT_SIZE"
     ;;
    -m) pscolor=monochrome
     ;;
    -o) [ "x$2" != "x-" ] \
           && ofname=$2 \
           || { stdout=yes; quiet=yes; }
        shift
     ;;
    -O) MUPLOT_FILE_STDIN="$2"
        shift
     ;;
    -q) quiet=yes
     ;;
    -i) ignore_local_comm=yes
     ;;
    -I) set_file="$2"
        shift
        [ ! -e "$set_file" ] \
          && echo "$progname: no such file '$set_file'" \
          && exit 13
     ;;
    -F) data_FS="$2"
        shift
     ;;
    -T) MUPLOT_DIR_TMP="$2"
        shift
     ;;
    -U) file_list_sort=no
     ;;
    -C) MUPLOT_MISSING_DATA_AXES=""
     ;;
    -D) dry_run=yes
     ;;
    -e) MULTI_DATA_SETS_EXTRACT="$2"
        MULTI_DATA_SETS_KEEP=no
        shift
     ;;
    -E) MULTI_DATA_SETS_EXTRACT="$2"
        MULTI_DATA_SETS_KEEP=yes
        shift
     ;;
    -[a-z]*|-[A-Z]*) exec 1>&2
        echo "$progname: invalid option '$1'"
        echo "Try \`$progname -H' for help."
        exit 8
     ;;
   -|*) break
     ;;
esac
shift
done

   # Setup names for working files
_define_output_tmpfile_names_

   # Check whether gnuplot is available
[ ! $(which gnuplot 2>&1 |grep "^/") ] \
    && echo 'Gnuplot is not installed or is not in your $PATH' \
    && _cleanup_and_exit_ 7

   # Create script file for gnuplot
echo "unset timestamp" > $gpout

   # Set data-field separator if defined
[ "$data_FS" ] \
&& echo "set datafile separator \"$data_FS\"" >> $gpout

   # Define output driver
_define_output_driver_ $out_form >> $gpout

   # Read gnuplot commands from file - OUTside #BEGIN ... #END block
_process_gnuplot_commands_file_ 1 "$set_file" dummy >> $gpout

   # Check for gnuplot command(s) specified by '-c'
if [ -n "$gpt_cmd" ]; then
     _define_output_file_names_ "$ofname" bfname ofile ofile_nstr
     _print_gnuplot_cmdl_command_ "$gpt_cmd" "$gdevice" "$ofile_nstr" >> $gpout

   # Process the list of files
else 
        # Determine the plot style and print it to stdout
     _define_plot_style_ "$@" >> $gpout
     shift $?

        # Check for files specified
     _prepare_list_of_files_to_plot_ "$1" bfname files_data

        # Exit if no file(s) provided
     _exit_on_empty_filelist_ "$files_data"

        # In case of continues data reload setup the reload variables
     [ $reload_data = "yes" ] \
       && _data_reload_setup_ reload_conf program_data_pipe

        # Define output file names
     _define_output_file_names_ "$ofname" bfname ofile ofile_nstr

        # Loop over multiple file sets
     while [ "$files_data" ] ;# FILE_SET BEGIN
     do shift

           # If input is piped in, then start using the temporary file
        if [ "x$files_data" = "x-" ]; then 
              stdin_data=yes
                # On data reloading (i.e. using '-r' option) truncate the file
                # and starting reading data from <stdin> later
              [ $reload_data = "no" ] \
                && cat >> $tmpstdin && exec <&1 \
                ||      > $tmpstdin
        fi
   
        [ "$MULTI_DATA_SETS_EXTRACT" ] \
        && _extract_multi_data_file_ "$MULTI_DATA_SETS_EXTRACT" \
                                      MULTI_DATA_SETS_KEEP \
                                      MUPLOT_DIR_TMP data_FS

           # Check for samples and ranges
        sample=$(_tell_me_samples_to_plot_ "$1") && shift

           # Look for multiple data ranges and print out plot commands
        _plot_various_data_ranges_ "$sample" files_data gdevice ofile_nstr >> $gpout

           # Look for next data file set and prepare a list
        _prepare_list_of_files_to_plot_ "$1" bfname files_data

     done                    ;# FILE_SET END
fi

   # Read gnuplot commands from file - inside #BEGIN ... #END block
_process_gnuplot_commands_file_ 0 "$set_file" "$ofile_nstr" >> $gpout

   # "pause" if the terminal is X11 or is not defined explicitly
if [ "$gdevice" = x11 -o "$gdevice" = "" ]; then
   [ $reload_data = "no" ] \
     && echo "pause -1" >> $gpout
fi

   # In case of continues data reload for "real-time" data observation
if [ $reload_data = "yes" ]; then
     _data_reload_initialize_ ofile_nstr gpout files_data
fi

_adjust_plotting_commands_for_missing_data_ $gpout MUPLOT_MISSING_DATA_AXES

   # Execute GNUPLOT if not a dry run
if [ $dry_run = "no" ]; then
     if [ $stdin_data = "yes"  -a  $reload_data = "yes" ]; then
             # RELOAD data continuously: exec gnuplot-script in background,
             # then read data from <stdin>
          (while [ ! -s $tmpstdin ]; do
             sleep 1 
           done
           gnuplot $gpout >$gperr 2>&1
          )&
          cat | grep --line-buffered . >> $tmpstdin
          wait
     else
             # DEFAULT: just exec gnuplot-script if not data reloading
          gnuplot $gpout >$gperr 2>&1
     fi
fi

   # Print out the gnuplot script
_gnuplot_script_print_to_terminal_ $gpout

   # If gnuplot failed with errors, report them and exit
if [ -s $gperr ]; then
     _gnuplot_errors_print_to_terminal_ $gperr
     _cleanup_and_exit_ 11
fi

if [ $dry_run =  "no" ]; then
     if [ $stdout = yes ]; then
             # Print the raw plot to STDOUT if chosen
          _print_raw_plot_ "$ofile"
     else
             # Print the name of the output file
          if [ -n "$ofile_nstr" -a $quiet != yes ]; then
               echo "# Your plot file is $ofile_nstr."
          fi
             # Ask to display the plot
          if [ $quiet != yes -a "$gdevice" != x11 ]; then
               if [ -n "$MUPLOT_VIEWER" ] ;then
                    _ask_to_display_plot_ "$ofile" "$MUPLOT_VIEWER"
               fi
          fi
     fi
fi

_cleanup_and_exit_ 0
