#! /bin/sh
# run-next --- run next instance of program in path after specified directory

# Copyright (C) 1995 Noah S. Friedman

# Author: Noah Friedman <friedman@splode.com>
# Created: 1995-09-07

# $Id: run-next,v 1.5 1999/12/23 17:25:11 friedman Exp $

# 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 2, 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, you can either send email to this
# program's maintainer or write to: The Free Software Foundation,
# Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

# Commentary:

# This script can be used by front-end wrapper scripts in a user's personal
# path to set environment variables, manipulate args, etc. before calling
# the real program which resides in a directory specified somewhere later
# in the user's path.  For example, a front end to the mozilla browser
# could be written as:
#
#         #!/bin/sh
#         HOME=$HOME/etc/www/netscape
#         export HOME
#         exec run-next $0 ${1+"$@"}
#
# This script looks at the full path of the specified argument and will
# only search directories in PATH after that occurence.

# Code:

# Name by which this script was invoked.
progname=`echo "$0" | sed -e 's/[^\/]*\///g'`

# To prevent hairy quoting and escaping later.
bq='`'
eq="'"

usage="Usage: $progname {options} [program {program options}]

Options are:
-D, --debug                  Turn on shell debugging ($bq${bq}set -x$eq$eq).
-h, --help                   You're looking at it.
-p, --print                  Just print full name of program to run,
                             without running it.  Any program options are
                             discarded.
-s, --skip           N       Run Nth instance of program in path.
                             This is relative to absolute pathname
                             specified in program name to run, if any.
                             Default is 1.
"

# Clever way to save arguments whilst preserving quoting.
# Note that only one set of args can be saved at a time.
# Usage: eval "$save_current_args"
#        eval "$restore_saved_args"
save_current_args='
  {
    _saved_args=
    while : ; do
      case $# in 0 ) break ;; esac
      eval _saved_args$#=\$1
      _saved_args="$_saved_args \"\$_saved_args$#\""
      shift
    done
    eval "$restore_saved_args"
  }'
restore_saved_args='
  {
    eval '\''{ eval set fnord $_saved_args ; }'\''
    shift
  }'

# Initialize variables.
# Don't use `unset' since old bourne shells don't have this command.
# Instead, assign them an empty value.
debug=
printp=
n=1

# Usage: eval "$getopt"; value=$optarg
# or     optarg_optional=t; eval "$getopt"; value=$optarg
#
# This function automatically shifts the positional args as appropriate.
# The argument to an option is optional if the variable `optarg_optional'
# is non-empty.  Otherwise, the argument is required and getopt will cause
# the program to exit on an error.  optarg_optional is reset to be empty
# after every call to getopt.  The argument (if any) is stored in the
# variable `optarg'.
#
# Long option syntax is `--foo=bar' or `--foo bar'.  2nd argument
# won't get used if first long option syntax was used.
#
# Note: because of broken bourne shells, using --foo=bar syntax can
# actually screw the quoting of args that end with trailing newlines.
# Specifically, most shells strip trailing newlines from substituted
# output, regardless of quoting.
getopt='
  {
    optarg=
    case "$1" in
      --*=* )
        optarg=`echo "$1" | sed -e "1s/^[^=]*=//"`
        shift
       ;;
      * )
        case ${2+set} in
          set )
            optarg="$2"
            shift
            shift
           ;;
          * )
            case "$optarg_optional" in
              "" )
                case "$1" in
                  --*=* ) option=`echo "$1" | sed -e "1s/=.*//;q"` ;;
                  * ) option="$1" ;;
                esac
                exec 1>&2
                echo "$progname: option $bq$option$eq requires argument."
                echo "$progname: use $bq--help$eq to list option syntax."
                exit 1
               ;;
           esac
         ;;
        esac
     ;;
    esac
    optarg_optional=
  }'

# Parse command line arguments.
# Make sure that all wildcarded options are long enough to be unambiguous.
# It's a good idea to document the full long option name in each case.
# Long options which take arguments will need a `*' appended to the
# canonical name to match the value appended after the `=' character.
while : ; do
  case $# in 0) break ;; esac
  case "$1" in
    -D | --debug | --d* )
      debug=t
      shift
     ;;
    -h | --help | --h )
      echo "$usage" 1>&2
      exit 0
     ;;
    -p | --print | --p* )
      printp=t
      shift
     ;;
    -s | --skip | --s* )
      eval "$getopt"
      n=$optarg
     ;;
    -- )     # Stop option processing
      shift
      break
     ;;
    -? | --* )
      case "$1" in
        --*=* ) arg=`echo "$1" | sed -e 's/=.*//'` ;;
        * )     arg="$1" ;;
      esac
      exec 1>&2
      echo "$progname: unknown or ambiguous option $bq$arg$eq"
      echo "$progname: Use $bq--help$eq for a list of options."
      exit 1
     ;;
    -??* )
      # Split grouped single options into separate args and try again
      optarg="$1"
      shift
      set fnord `echo "x$optarg" | sed -e 's/^x-//;s/\(.\)/-\1 /g'` ${1+"$@"}
      shift
     ;;
    * )
      break
     ;;
  esac
done

case "$debug" in t ) set -x ;; esac

eval "$save_current_args"

case "$1" in
   */* )
     set fnord `echo "$1" | sed -e 's/\(.*\)\/\([^/]*\)/\1 \2/'`
     shift
     progdir="$1"
     prog="$2"
    ;;
   * )
     progdir=
     prog="$1"
    ;;
esac

# Do this, instead of "shift $#", since, amazingly, that is not portable.
set fnord
shift

for d in `IFS=':'; echo $PATH`; do
  case "$d" in '' ) d=. ;; esac
  case " $* " in
    # Do not add duplicate directories to search path
    *" $d "* ) : ;;
    * )
      if test -f $d/$prog ; then
        set fnord ${1+"$@"} "$d"
        shift
      fi
     ;;
  esac
done

case "$progdir" in
  '' ) : ;;
  * )
    found=nil
    while test $# -gt 0; do
      if test ".$1" = ".$progdir"; then
        shift
        found=t
        break
      fi
      shift
    done

    if test $# -eq 0; then
      exec 1>&2
      case "$found" in
        nil ) echo "$progname: $bq$progdir$eq not in PATH." ;;
        t   ) echo "$progname: No $bq$prog$eq after $bq$progdir$eq in PATH." ;;
      esac
      exit 1
    fi
   ;;
esac

if test $n -gt $#; then
  exec 1>&2
  echo "$progname: $bq$prog$eq does not appear in $n places, only $#:"
  for f in ${1+"$@"}; do
    echo "$progname: $f/$prog"
  done
  exit 1
fi

eval execdir="\$$n"

case "$printp" in
  t )
    echo "$execdir/$prog"
    exit 0
   ;;
  * )
    eval "$restore_saved_args"
    shift
    exec "$execdir/$prog" ${1+"$@"}
   ;;
esac

# run-next ends here
