#!/bin/sh
version=2.1.6
license="Copyright (C) 1996-2009 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."
#set -vx
################################################################################
#
# 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
gpe="$bfname.err"                        # Temporary error file
tmpfile="$progname.$$"                   # Temporary work file
tmpstdin="$tmpfile.stdin"                # Temporary stdin
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="x11"                            # Ouput graphics device (default X11)
out_form="unknown"                       # PS, PNG, JPG ok - for PDF see help
psfont='"Times-Roman"'                   # PS font type
psfontsize=20                            # PS font size
pscolor=color                            # PS color
land="-landscape"                        # gv should use landscape
stdout=no                                # Do not send PS file to stdout
quiet=no                                 # Don't supress info messages

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

### Read external file with gnuplot commands
#
read_comm_file () {
   comm_file=$1
   mode=$2
   if [ -f $comm_file ]; then
       if   [ $mode -gt 0 ]; then
            cat $comm_file |sed -n '/#BEGIN/,/#END/!p'
       elif [ x"`grep '^#BEGIN' $comm_file`" != x ]; then
              # print everything btw. BEGIN and END
            echo "set out $fnstr"
            cat $comm_file |sed -n '/^#BEGIN/,/^#END/p'
            echo "replot"
       fi
  fi
}

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

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

Usage: $progname [OPTION]... [STYLE] [FILE] [AXES]

Options:
   --help|-H $d display help
   --version $d output version and license message
   -h        $d display short help
   -V        $d print program version number
   -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)
   -c <cmd>  $d execute gnuplot command(s) (the default plot style is used)
   -m        $d monochrome plot (valid only for PostScript)
   -l        $d set plot size to 800x600 (valid for PNG and JPEG)
   -o        $d base name of the output file
   -q        $d quiet mode (all messages except errors to be suppressed)
   -i        $d ignore local command file './$comm_file'

Styles:
   l         $d lines
   p         $d points
   lp        $d lines and points (default)
   pp        $d circle points
   d         $d dots
   b         $d boxes
   g         $d grid
   e         $d errorbars - default used columns are 1:2:3 (x:y:yerror)
   a         $d fields with arrows;
               The data file has a special format in this case. Use 'prefield'
               to prepare such data files.
   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 could be a date: dt="%Y-%m-%d".
   u=<fmt>   $d user specified format as defined in Gnuplot
   
Axes:
   x:y,x:y-z $d columns in the file definig the x/y-axes of the curve(s);
               Default are 1:2 or 1:2:3 for data with errors. In case that only
               one column is provided the default axes are 0:1 - the x-axis
               will be a simple index then.


File(s) could be a single file name whereas '-' means <stdin>, many files
enclosed in '' or "" like "file1 file2 file3", or any valid shell pattern
as for example "*.dat". 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 data files with extension 'dat'

   $progname l "*.dat"

2) Print a sinus curve as a black-and-white PostScript on a PS-printer

   $progname -m -S -c "set title 'Funtion 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 lp example.dat 1:2,3:4-5

4) View data where the third column is a date of the form 'yyyy-mm-dd'

   cat example_counts_per_day.dat | $progname dt="%Y-%m-%d" - 3:1


Report bugs to <gnu@mirendom.net>

END_HELP

}


################################################################################
#
# Help
#

if [ $# -lt 2 ]; then
       # Print out version number and license and exit
     if [ x$1 = x--version ]; then
          cat << _VERSION_
$progname $version
$license
_VERSION_
exit 0
     fi

       # Print out version number and exit
     if [ "x$1" = x-V ]; then
        echo $version
        exit 0
     fi

       # Display help
     if [ "x$1" = x--help -o "x$1" = x-H ]; then
          separator=""
          header_text="Muplot is a simple, non-interactive gnuplot-wrapper to plot a multi-curve figure from multiple data. It can produce PostScript, PDF, PNG or JPEG output file formats."
             # Print out usage
          _show_usage_ "$header_text" " "
     else
          separator=`echo |awk '{printf( "%080s", 0 )}' |tr 0 -`
          header_text="$progname $version: plot a multi-curve figure from multiple data by using Gnuplot"
             # Print out short help and exit
          _show_usage_ "$header_text" "-" \
               |egrep "^($progname|Usage:|Options:|Styles:|Axes:|-|.*\ \ -\ |.*\,x:y-z)"
     fi

       # To create a manpage try:
       # muplot --help |sed -ne "s/Usage:/[SYNOPSIS]\n/;s/muplot/\nmuplot/g; /SYNOPSIS/,/Options:/p" |grep -v Options: > /tmp/synopsis.txt ; help2man -N -n "`muplot -h |grep ^muplot |cut -f2- -d:`" muplot -I /tmp/synopsis.txt |sed 's/ \\fB/ /g' |man -l -

exit 0
fi

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

### We need an AWK supporting assignments
exec 3>&2 2>&-
for a in gawk nawk awk
do
  [ "`echo |$a -v a=a '{}' 2>&1`" = "" ] && awk=$a
done
exec 2>&3
[ x"$awk" = x ] && \
  echo "Error: can't find awk supporting assignments" && \
  exit 12

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

### Find out cmdline options
while [ 0 ]
do
case $1 in
    -s) out_form=ps
     ;;
    -S) out_form=ps
        stdout=yes
     ;;
    -n) out_form=png
     ;;
    -j) out_form=jpg
     ;;
    -p) out_form=pdf
     ;;
    -c) gpt_command="$2"
        shift
     ;;
    -l) termopt="large size 800,600"
     ;;
    -m) pscolor=monochrome
     ;;
    -o) [ "x$2" != "x-" ] \
           && ofname=$2 \
           || { stdout=yes; quiet=yes ;}
        shift
     ;;
    -q) quiet=yes
     ;;
    -i) ignore_local_comm=yes
     ;;
    -*) _show_usage_
        exit 8
     ;;
     *) break
     ;;
esac
shift
done

### Only in case of gnuplot command '-c' you don't need a file name
if [ x"$gpt_command" = x ]; then

      # Last sanity check of the cmdline string syntax:
      # no options are provided in case the $1 is a normal word
   if [ "`echo $2 |sed 's/^-..*/BAD/'`" = BAD ]; then
        echo "Error: your file name can't start with '-'" && exit 9
   fi
      # If input is not defined as the stdin, then evaluate file list
   if [ "x$2" = "x-" ]; then
        files="-"
   else
        files=`eval ls "$2"` || exit 10
   fi
      # If a single file the basename is defined from it
   if   [ `ls -1 "$files" 2>/dev/null |wc -l` -eq 1 ]; then
          bfname="$files"
          LIST_FILES="echo $files"
          bfname=`$LIST_FILES |sed "s/\.[^\.]*$//"` # Remove extension from name
   elif [ "x$files" != "x-" ]; then
          LIST_FILES="ls -1 $files"
   fi

fi

### Adjust output termianl
case $out_form in
    ps) gdevice="postscript enh $pscolor $psfont $psfontsize"
     ;;
   png) gdevice="png $termopt"
     ;;
   jpg) gdevice="jpeg $termopt"
     ;;
   pdf) gdevice="pdfcairo"
     ;;
     *) gdevice=x11
     ;;
esac

### Define proper file names
[ "x$ofname" != x ]  && bfname=$ofname
if   [ $out_form != "unknown" ]; then
     ofile="$bfname.$out_form"          # Output file name
     fnstr="'$bfname.$out_form'"        # Gnuplot file name string
fi
if [ $stdout = yes ]; then
     bfname=$tmpfile                    # Write to STDOUT - use tmp file
     ofile="$bfname.ps"
     fnstr="'$bfname.ps'"
fi

### Create script file for gnuplot
echo "set notime" > "$bfname.gpt"

### Check for grid
[ "$1" = g ] && echo "set grid" >> "$bfname.gpt"

### Read gnuplot commands from setup file skipping '#BEGIN' till '#END' blocks
test ! -f "$comm_file_ignore" \
     && read_comm_file "$HOME/$set_file" 1 >> "$bfname.gpt"
test "`pwd`" != "$HOME" \
     && test "$ignore_local_comm" != "yes" \
     && read_comm_file "$set_file" 1 >> "$bfname.gpt"

### Check for native gnuplot command(s) specified
if [ "x$gpt_command" != x ]; then

     cat << EOFC >> "$bfname.gpt"
set term $gdevice
set out $fnstr
$gpt_command
EOFC

### Main loop for multiple data ranges and files:
else

   # First determine the plot style
case $1 in
   l|g) style=lines
        #style="lines 1"
     ;;
     d) style=dots
     ;;
     p) style=points
     ;;
    pp) style="points 1 6"
     ;;
     b) style=boxes
     ;;
     e) style=errorlines
        sample=1:2:3
     ;;
     a) style=dots
           # Data prepared by 'prefield' - default delimiter of cut is a TAB!
        if [ "x$files" != "x-" ]; then # Input from file(s)
             cut -f3- $files >> "$bfname.gpt"
        else                           # Make a copy of stdin
             tee $tmpstdin |cut -f3- >> "$bfname.gpt"
        fi
        echo "set nokey" >> "$bfname.gpt"
     ;;
   u=*) style="`echo $1 |cut -f2 -d=`"
     ;;
     *) style=linespoints
           # Format in case of date/time data
        if [ x`echo $1 |grep "dt\="` != x ]; then
           echo "set xdata time" >> "$bfname.gpt"
           fmt=`echo $1 |cut -f2 -d= | cut -f1 -d@`
           fmtx=`echo $1 |cut -f2 -d= |cut -f2 -d@`
           echo "set timefmt '$fmt'" >> "$bfname.gpt"
           echo "set format x '$fmtx'" >> "$bfname.gpt"
           sample=1:2
        fi
     ;;
esac

   # If input is piped in, then use the temporary file
[ "x$files" = "x-" ] \
   && cat ->> $tmpstdin && LIST_FILES="ls -1 $tmpstdin" && exec <&1

### Check for samples and ranges
test -n "$3" && sample="$3"
test -z "$sample" \
       && sample="0:1-0" \
       || sample=`echo $sample |tr ',' '\040'` # Separate ranges by blanks
lastabsc=`echo $sample |cut -f1 -d:`
### Then loop over ranges
i=0
for j in $sample
do   # DATA_RANGE begin
    i=`expr $i + 1`
    absc=`echo $j |cut -f1 -d:`
    first=`echo $j |cut -f1 -d- |cut -f2 -d:`
    last=`echo $j |cut -f2 -d-`
    [ "$last" = "$j" ] && last=$first
    [ "$absc" = "$j" -a "$i" = 1 ] && absc=0
    [ "$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

       # Plots after the first one must be re-plotted
    [ $i -eq 1 ] && plot=plot || plot=replot

    # FILE_LOOP :  plot all files in the list
    $LIST_FILES \
        |$awk -v pl=$plot -v sty="$style" -v a=$absc -v f=$first -v l=$last \
              'BEGIN { printf("%s ", pl) }
               { 
                 for( i=f; i<=l; i++ )
                 {
                       # In caces with 3-data columns
                    if( a ~ /^[0-9]+:[0-9]+:[0-9]+$/ )
                        axes=a;
                    else
                        axes=sprintf("%d:%d", a, i);
                    printf("\"%s\" using %s with %s, ", $0, axes, sty);
                 }
                 if( l==0 && l!=f )
                     printf("\"%s\" with %s, ", $0, sty);
               }
               END { printf "\n" }' > $tmpfile

    echo "set term $gdevice" >> "$bfname.gpt"
    echo "set out $fnstr" >> "$bfname.gpt"

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

done # DATA_RANGE end
fi

### Read gnuplot commands btw. BEGIN and END - first global, then local file
test ! -f "$comm_file_ignore" \
     && read_comm_file "$HOME/$set_file" 0 >> "$bfname.gpt"
test "`pwd`" != "$HOME" \
     && test "$ignore_local_comm" != "yes" \
     && read_comm_file "$set_file" 0 >> "$bfname.gpt"

### "pause" if the terminal is X11
[ "$gdevice" = x11 ] && echo "pause -1" >> "$bfname.gpt"

### Execute GNUPLOT
gnuplot "$bfname.gpt" > $gpe 2>&1

### Print out the gnuplot script
if [ $stdout != yes -a $quiet != yes ]; then
    echo "### Your gnuplot script:"
    cat "$bfname.gpt" |grep -v "\#"
    echo ""
fi
### If gnuplot reports errors then exit
if [ -s $gpe ]; then
    echo "" 1>&2
    echo "### Gnuplot ERRORS:" 1>&2
    cat $gpe 1>&2
    _clean_up_ 11
fi

if [ $stdout = yes ]; then                    # STDOUT
     [ $out_form = ps ] \
       && cat $ofile |sed "s/%%Title:.*/%%Title: $cmdstr/" \
       || cat $ofile
     _clean_up_ 0
fi

[ -n "$fnstr" -a $quiet != yes ] && echo "# Your plot file is $fnstr."

if [ $out_form = ps -a $quiet != yes ]; then  # Ask whether to view the PS-plot
     echo "# Show picture? [y/N]"
     read answer
     if [ "$answer" = y -o "$answer" = Y ]; then
          ghost=`which gv 2>&1 |grep "^/"`
          if [ ! "$ghost" ]; then
               ghost=`which ghostview 2>&1 |grep "^/"`
          fi
          if [ ! "$ghost" ]; then
               echo 'Info: ghostview is not installed or is not in your $PATH'
          else
               test -n "$land" && $ghost $land /dev/null 2>&1 \
                        |grep "orientation=" > $gpe
               test -s $gpe && land="--orientation=landscape"
               $ghost $land "$ofile" &
          fi
     fi
fi

_clean_up_ 0
