#!/bin/sh
#
# vimsdiff: display highlighted differences between two files side-by-side in
#	    gvim
#
# Author: Gautam H. Mudunuri
#
# Feel free to use (at your own risk!), distribute and modify this utility as
# long as the original author's name and this notice are retained.
#
# $Log: vsdiff,v $
# Revision 1.1  1998/10/26 18:52:09  gmudunur
# Initial revision
#


# Get my name
progname=`basename $0`

# Display usage and exit
Usage()
{
    echo >&2 "\
$progname: \
display highlighted differences between two files side-by-side in gvim\n\
Usage: $progname [ -f font ] [ -s ] [ -w width ] file1 file2\n\
   or: $progname [ -f font ] [ -s ] [ -w width ] file1 directory2\n\
   or: $progname [ -f font ] [ -s ] [ -w width ] directory1 file2\n\
\n\
Atleast one of the arguments must be a file name.  If one of the arguments\n\
is a directory name, it refers to the file in that directory with the same\n\
name as the other argument.\n\
\n\
Options:\n\
   -f font     font to use for display
   -s          do not display identical lines\n\
   -w width    width of displayed output\n\
               (default is set for diffs of 80 column wide files)\
"
    exit 1
}

# Parse commandline
s_flag="n"
width=
font=
while getopts f:sw: option
do
    case "$option" in
        f) [ "$font" != "" ] && Usage
           font="$OPTARG"
           ;;
        s) s_flag="y"
           ;;
        w) [ "$width" != "" ] && Usage
           width="$OPTARG"
           ;;
        *) Usage
           ;;
    esac
done
shift `expr $OPTIND - 1`
[ $# -eq 2 ] || Usage

# Font not specfied, use default
[ "$font" = "" ] && font=fixed

# Width not specified, use default for 80-column wide files and 9 character
# sdiff "gutter".  169 = 2 * 80 +  9
[ "$width" = "" ] && width=169

# Validate width
expr "$width" + 0 > /dev/null 2>&1
status="$?"
[ $status -eq 0 -o $status -eq 1 ] || {
    echo >&2 "$progname: invalid width \"$width\""
    exit 1
}
[ $width -gt 0 ] || {
    echo >&2 "$progname: width $width cannot be zero or negative"
    exit 1
}
[ $width -ge 29 ] || {
    echo >&2 "$progname: width $width is too small, should be atleast 29"
    exit 1
}
[ $width -le 199 ] || {
    echo >&2 "$progname: width $width is too large, should be atmost 199"
    exit 1
}


# Arguments must be files or directories
arg1="$1"
[ -f $arg1 -o -d $arg1 ] || {
    echo >&2 "$progname: $arg1 is neither a file nor a directory"
    exit 1
}
arg2="$2"
[ -f $arg2 -o -d $arg2 ] || {
    echo >&2 "$progname: $arg2 is neither a file nor a directory"
    exit 1
}

# Both arguments cannot be directories
[ -d "$arg1" -a -d "$arg2" ] && {
    echo >&2 "$progname: $arg1 and $arg2 are both directories"
    echo >&2 "Atleast one file must be specified"
    exit 1
}

# If one argument is a directory, convert it to a filename using the other
[ -d "$arg1" ] && arg1="$arg1/`basename $arg2`"
[ -d "$arg2" ] && arg2="$arg2/`basename $arg1`"

# Both arguments must be readable
[ -r "$arg1" ] || {
    echo >&2 "$progname: cannot open $arg1 for reading"
    exit 1
}
[ -r "$arg2" ] || {
    echo >&2 "$progname: cannot open $arg2 for reading"
    exit 1
}

# Compute width per file, allowing 9 characters for the sdiff "gutter"
file_width=`expr '(' $width - 9 ')' / 2`

# Compute the range of the number of characters we can expect before the sdiff
# separators |, < and >.  This is range rather than a specific number because
# the presence of tabs can shorten the length
min_chars=`expr $file_width - 5`
max_chars=`expr $file_width + 2`

# Define Vim commands for manipulating the sdiff buffer
buffercmds="
    set ts=8 et noro
    retab
    set nomod ro nows
"

# Define Vim commands for sdiff syntax highlighting
syncmds="
    set bg=light go=agr lines=40 nows
    syn match sdiffModified \"^.\{$min_chars,$max_chars}  |  .*$\"
    syn match sdiffAdded \"^.\{$min_chars,$max_chars}  >  .*$\"
    syn match sdiffDeleted \"^.\{$min_chars,$max_chars}  <\"
    hi link sdiffModified PreProc
    hi link sdiffAdded    Identifier
    hi link sdiffDeleted  Comment
    set bg=light
    syn on
"

# Define Vim mappings to jump to next/previous difference
mapcmds="
    nmap <f7> /^.\{$min_chars,$max_chars}  [<>\|]  /e-2<nl>
    nmap <s-f7> ?^.\{$min_chars,$max_chars}  [<>\|]  ?e-2<nl>
"

# Collect vim commands to be run
vimcmds="
    set titlestring=\\$progname\\ \\$arg1\\ \\$arg2
    set noml ls=1 ch=1 ru go=agr
    $buffercmds
    $syncmds
    $mapcmds
"

# Run the diff and display the output in gvim
flags=
[ "$s_flag" = "y" ] && flags="$flags -s"
sdiff -w$width $flags "$@" | gvim -R -N -U NONE              \
                             -fn "$font"                     \
                             -bg wheat -fg black             \
                             -geometry "$width"x40+0+0       \
                             +"$vimcmds" -
