#! /bin/ksh
#
#    Dec 07/2003 - Larry Solheim
#                - Modified to support only gcmparm=gcmparm
#                - gcmparm=gcmparm is now internal default
#                  
#    Sep 18/2003 - Larry Solheim
#
#id  gcmparm_input - Create gcmparm input cards from environment variables
#
#    AUTHOR - L. Solheim
#
#hd PURPOSE - Extract a list of variable names and their values from the current 
#hd           environment or from files which contain variable assignments.
#hd           After verifying that all variables are defined and non null, 
#hd           create input for a specified version of gcmparm using these 
#hd           variable values.
#hd  
#
#pr USAGE:    
#pr
#pr      gcmparm_input [options] script_file... [options]
#pr
#pr PARAMETERS:
#pr
#pr   POSITIONAL
#pr
#pr      script_file - the name of a script containing variable definitions
#pr 
#pr   PRIMARY:
#pr
#pr      gcmparm=PGM_NAME
#pr                PGM_NAME is "gcmparm" program version.
#pr                If neither "vinfo" (see description next) nor 
#pr                "gcmparm" are set then "gcmparm" will default 
#pr                to "gcmparm" and "vinfo" will be set accordinly.
#pr
#pr                If "vinfo" is set but "gcmparm" is not set then
#pr                "gcmparm" will be set to "custom" and the gcmparm
#pr                input card(s) will be written according to the
#pr                information supplied in "vinfo".
#pr
#pr                If both "vinfo" and "gcmparm"  are set then if
#pr                "gcmparm" is a recognized version "vinfo" will
#pr                be overwritten with variable info appropriate
#pr                to that version of "gcmparm". Otherwise vinfo will
#pr                be used to create the input cards.
#pr
#pr        vinfo="FMT LIST"
#pr                "FMT LIST" contains rariable names and formatting info.
#pr                This info will be used to format the gcmparm input cards.
#pr                The default depends on the "gcmparm" value if the latter
#pr                is set to one of the standard "gcmparm" program names.
#pr                "vinfo" setting consists of a white space separated 
#pr                list of variable names with formatting information
#pr                optionally appended to each variable. Each element of
#pr                this list has one of the forms
#pr
#pr                 variable[:format]   or   [variable]:[format]
#pr
#pr                where [] implies an optional component and
#pr                 variable - the variable name as it exists in the current 
#pr                            environment or as it is defined in the one of 
#pr                            the files whose names are supplied on the
#pr                            command line.
#pr                 format   - either a printf type format string such as
#pr                            "%6d" or "%10s" etc. or a fortran format 
#pr                            descriptor (currently only "X" and "I" formats
#pr                            are supported)
#pr
#pr                 - variable names and format strings are separated by a ":"
#pr                 - a variable name may appear without an appended ":"'
#pr                 - a format string may not appear without a prepended ":"
#pr                 - missing variable names default to blank spaces
#pr                 - missing format string default to %6d
#pr                 - a single ":" surrounded by white space means print 10 
#pr                   spaces
#pr                 - the special case of an element composed of ":nl" means 
#pr                   begin a new line
#pr                            
#pr      outfile=FILE_NAME
#pr                Filename to write the generated "gcmparm" input lines into.
#pr                (default is "gcmparm.in")
#pr
#pr      help    - Prints usage info.
#pr
#pr      NOTE: The script supports as well the following syntax on the command line:
#pr 
#pr       var=val            ...add a variable definition to the environment
#pr
#pr      -[abCefhkmnpstuvx] ...invoke one of these shell set options
#pr

#ex EXAMPLES:
#ex
#ex   Example of "vinfo" option
#ex
#ex     vinfo="ilev levs:%10d :nl :%20s nlat: : lon" 
#ex   is equivalent to the printf statement 
#ex    printf "%6d%10d\\n%20s%6d%10s%6d\\n",$ilev,$levs,$space01,$nlat,$space02,$lon
#ex    where space01 and space02 are two separate variables set internally to blanks
#ex    In this example, if ilev=35, levs=35, nlat=48 and lon=96 then the output
#ex    file created would contain the following two lines
#ex       35         35
#ex                           48              96
#ex
#ex   Example of invocation
#ex
#ex     gcmparm_input gcmparm=gcmparm parmsub.in
#ex     This will create input cards appropriate for gcmparm using variable
#ex     assignments passed from the invoking environment as well as those
#ex     found in the script file parmsub.in
#ex
#ex     In this example the required parmsub parameters are
#ex     ilev   = number of vertical model    levels                       (e.g.  35)
#ex     levs   = number of vertical moisture levels                       (e.g.  35)
#ex     lonsld = number of longitudes         on the dynamics grid        (e.g. 144)
#ex     nlatd  = number of gaussian latitudes on the dynamics grid        (e.g.  72)
#ex     lond   = number of grid points per slice for dynamics             (e.g. 576)
#ex     lonsl  = number of longitudes         on the physics grid         (e.g.  96)
#ex     nlat   = number of gaussian latitudes on the physics grid         (e.g.  48)
#ex     lon    = number of grid points per slice for physics              (e.g. 384)
#ex     lmt    = spectral truncation wave number                          (e.g.  47)
#ex     ioztyp = switch for input ozone distribution                      (e.g.   2)
#ex     ntrac  = total number of tracers in the model                     (e.g.  17)
#ex     itraca = number of advected tracers in the model                  (e.g.  12)
#ex     nnode_a= number of smp nodes used to run atm (mpi for nnode_a>1)  (e.g.   1)
#ex
#ex     The output cards written to the file gcmparm.in will be
#ex   GCMPARM    35    35    47   144    72   576    96    48   384
#ex   GCMPARM    17    12     2     1

# Set defaults
Runame=`basename $0`

AWK=${AWK:=awk}

# Define default formats. These should be the most common format for each particular
# variable type. The actual format used is controlled by vinfo.

def_s_width=10
def_d_width=6

# default string format

def_s_fmt="%${def_s_width}s"

# default integer format
 
def_d_fmt="%${def_d_width}d"

# get_help will be set to 1 if "-help" or "help" command line argument is found.
# It will then be used to flag an invocation of help later on after certain
# variables have been assigned.

get_help=0

# debugging flag (set with the -d command line option) - internal
# Note: setting verbose.gt.1 will cause the program to continue on certain errors.

verbose=0

# The version of gcmparm which will determine the format of outfile
# order of precedence for setting gcmparm:
#   1) command line  2) environment (gcmparm) 3) internal

gcmparm=${gcmparm:=''}

# vinfo is a space separated list of variable names with optional formatting
# information appended to each variable name.
# order of precedence for setting vinfo:
#   1) command line  2) environment (vinfo)  3) internal

vinfo=${vinfo:=''}

# The name of the file to which the output cards will be written
# order of precedence for setting outfile:
#   1) command line  2) environment (outfile)  3) internal

outfile=${outfile:=gcmparm.in}


bail(){
    typeset v
    echo "${Runame}: $1"
    [ $# -gt 1 ] && v=$2 || v=0
    if [ $v -le 1 ]; then
        touch haltit
        echo "${Runame}: $1" >> haltit
        exit 2
    fi
}

help(){
echo ' '
echo '"vinfo" is a white space separated list of variable names with formatting information'
echo 'appended to each variable. Each element of this list has one of the forms'
echo ' '
echo '   variable[:format]   or   [variable]:[format]'
echo ' '
echo 'where [] implies an optional component and'
echo '   variable - the variable name as it exists in the current environment or'
echo '              as it is defined in the one of the files whose names are supplied'
echo '              on the command line.'
echo '   format   - either a printf type format string such as "%6d" or "%10s" etc.'
echo '              or a fortran format descriptor (currently only "X" and "I" formats'
echo '              are supported)'
echo ' - variable names and format strings are separated by a ":"'
echo ' - a variable name may appear without an appended ":"'
echo ' - a format string may not appear without a prepended ":"'
echo ' - missing variable names default to blank spaces'
echo ' - missing format string default to %6d'
echo ' - a single ":" surrounded by white space means print 10 spaces'
echo ' - the special case of an element composed of ":nl" means begin a new line'
echo ' '
echo 'Example of "vinfo" option'
echo ' '
echo '  vinfo="ilev levs:%10d :nl :%20s nlat: : lon" '
echo 'is equivalent to the printf statement '
echo ' printf "%6d%10d\\n%20s%6d%10s%6d\\n",$ilev,$levs,$space01,$nlat,$space02,$lon'
echo ' where space01 and space02 are two separate variables set internally to blanks'
echo ' In this example, if ilev=35, levs=35, nlat=48 and lon=96 then the output'
echo ' file created would contain the following two lines'
echo '    35         35'
echo '                        48              96'
echo ' '
echo 'Example of invocation'
echo ' '
echo '  gcmparm_input gcmparm=gcmparm parmsub.in'
echo '  This will create input cards appropriate for gcmparm using variable'
echo '  assignments passed from the invoking environment as well as those'
echo '  found in the script file parmsub.in'
echo ' '
echo '  In this example the required parmsub parameters are'
echo '  ilev   = number of vertical model    levels                       (e.g.  35)'
echo '  levs   = number of vertical moisture levels                       (e.g.  35)'
echo '  lonsld = number of longitudes         on the dynamics grid        (e.g. 144)'
echo '  nlatd  = number of gaussian latitudes on the dynamics grid        (e.g.  72)'
echo '  lond   = number of grid points per slice for dynamics             (e.g. 576)'
echo '  lonsl  = number of longitudes         on the physics grid         (e.g.  96)'
echo '  nlat   = number of gaussian latitudes on the physics grid         (e.g.  48)'
echo '  lon    = number of grid points per slice for physics              (e.g. 384)'
echo '  lmt    = spectral truncation wave number                          (e.g.  47)'
echo '  ioztyp = switch for input ozone distribution                      (e.g.   2)'
echo '  ntrac  = total number of tracers in the model                     (e.g.  17)'
echo '  itraca = number of advected tracers in the model                  (e.g.  12)'
echo '  nnode_a= number of smp nodes used to run atm (mpi for nnode_a>1)  (e.g.   1)'
echo ' '
echo '  The output cards written to the file gcmparm.in will be'
echo '   GCMPARM    35    35    47   144    72   576    96    48   384'
echo '   GCMPARM    17    12     2     1'
echo ' '
eval "echo 'The current value of gcmparm is $gcmparm' "
eval "echo 'The current value of outfile is $outfile' "
eval "echo 'The current value of vinfo is' "
eval "echo '$vinfo' "
}

usage(){
typeset v
[ $# -gt 1 ] && v=$2 || v=''
echo $1
echo
echo 'Purpose: Extract a list of variables and their values from the current'
echo '         environment or from definitions in specified disk files'
echo '         and create the input cards for gcmparm from these values.'
echo ' '
eval "echo '  Usage: $Runame [options] file... [options]' "
echo ' '
echo 'Options:'
echo ' '
echo '   -[abCefhkmnpstuvx] ...invoke one of these shell set options'
echo ' '
echo '   help               ...Prints usage info.'
echo ' '
echo '   var=val will add a variable definition to the environment.'
echo '           Supported variable definitions are:'
echo ' '
echo '   gcmparm=PGM_NAME   ...Create input for program named "PGM_NAME"'
echo ' '
echo '   vinfo="FMT LIST"   ..."FMT LIST" contains variable names and formatting info.'
echo '                         This info will be used to format the gcmparm input cards'
echo ' '
echo '   outfile=FILE_NAME  ...Filename to write the generated "gcmparm" input lines to.'
echo '                         defaults is "gcmparm.in"'
[ "x$v" != "x" ] && help
exit 1
}

# preprocess command line arguments

ppargs=''
currsw=''

for arg in "$@"
do
    if [ "x$currsw" != "x" ]; then
        # The previous arg was a switch which required a argumnent
        case $currsw in
          -g) gcmparm="$arg" ;;
          -V)   vinfo="$arg" ;;
          -o) outfile="$arg" ;;
          -d) verbose="$arg" ;;
           *) echo "${Runame}: Unsupported switch $currsw" ;;
        esac
        currsw=''
        continue
    fi

    case $arg in

      # invoke requested set options 

      -[abCefhkmnpstuvx]) set $arg  ;;

      # add specified variable definitions to the environment

      *=*) var=`echo $arg|$AWK -F\= '{printf "%s",$1}' -`
           val=`echo $arg|$AWK -F\= '{i=index($0,"=")+1;printf "%s",substr($0,i)}' -`

           # preserve quoted assignments

           eval ${var}=\"$val\" ;;  

      # process switches

      -[a-zA-Z]*) currsw=`echo $arg|$AWK '{print substr($0,1,2)}' -`
           currswarg=`echo $arg|$AWK '{print substr($0,3)}' -`

           if [ "$arg" = "-help" ]; then
               get_help=1
               currsw=''

           elif [ "x$currswarg" != "x" ]; then

               # process options which require args

               case $currsw in
                 -g) gcmparm="$currswarg" ;;
                 -V)   vinfo="$currswarg" ;;
                 -o) outfile="$currswarg" ;;
                 -d) verbose="$currswarg" ;;
                  *) echo "${Runame}: Unsupported switch $arg" ;;
               esac
               currsw=''

           else

               # process options which do not require args

               case $currsw in

                 # these are valid switches to be processed on the next pass

                 -[gVod]) continue ;;

                  *) echo "${Runame}: Unsupported switch $arg"
                     currsw='' ;;
               esac

           fi ;;

      help) get_help=1
            currsw='' ;;

      # Collect all other args in ppargs

      *) ppargs="$ppargs $arg";;

    esac
done

# Set flist from the remaining command line args

flist=`echo $ppargs|sed 's/^  *//'`

[ "x$flist" = "x" -a $get_help -ne 1 ] &&\
   usage "No file name(s) specified on command line"

[ $verbose -gt 2 ] && echo "flist=$flist"

# Strip leading and trailing blanks from vinfo

[ "x$vinfo" != "x" ] && vinfo=`echo $vinfo|sed -e 's/^  *//' -e 's/  *$//'`

# Set gcmparm to its default value if neither it nor vinfo were set on the command line

[ "x$gcmparm" = "x" -a "x$vinfo" = "x" ]   && gcmparm=gcmparm

# If vinfo is set and gcmparm is not then set gcmparm=custom

[ "x$vinfo" != "x" -a "x$gcmparm" = "x" ]  && gcmparm=custom

# If both vinfo and gcmparm are set then if gcmparm is set to a value recognized
# by the following case statement, vinfo is overwritten, otherwise
# the program will abort at this case statement.

[ $verbose -gt 2 ] && echo "gcmparm=$gcmparm"

# set a variable that is the upper case version of gcmparm

UCgcmparm=`echo $gcmparm | tr '[a-z]' '[A-Z]'`

# Set vinfo according to the value of the gcmparm variable

case $gcmparm in
    custom)
      echo "${Runame}: Using formatting info from vinfo="
      echo "$vinfo"
      echo " " ;;

    gcmparm)
      [ "x$vinfo" != "x" ] && echo "${Runame}: ***WARNING*** Overwriting vinfo"
#      READ(5,'(10X,9I6)',END=901)
#     1  ILEV,LEVS,LMT,LONSLD,NLATD,LOND,LONSL,NLAT,LON
#      READ(5,'(10X,4I6)',END=902) NTRAC,ITRACA,IOZTYP,NNODE_A
      vinfo='UCgcmparm:%10s ilev levs lmt lonsld nlatd lond lonsl nlat lon :nl
             UCgcmparm:%10s ntrac itraca ioztyp nnode_a' ;;

    *) if [ "x$vinfo" != "x" ]; then
         echo "${Runame}: Formatting for program gcmparm=$gcmparm"
         echo "${Runame}: Using formatting info from vinfo="
         echo "$vinfo"
         echo " "
       else
          bail "$gcmparm is not a supported version of gcmparm"
       fi ;;

esac

[ $verbose -gt 2 ] && echo "${Runame}: vinfo=$vinfo"

# Source all files in flist to add variables defined therein to the current environment.
# It is assumed that the files in flist are shell scripts that contain nothing but
# assignments and innocuous statements. If any of these scripts produce unwanted
# side effects then do not use this program.

set -a
for script_to_source in $flist
 do
    [ $verbose -gt 1 ] && echo "${Runame}: Reading $script_to_source"
    [ ! -s $script_to_source ] && bail "$script_to_source does not exist or is empty" $verbose
    . $script_to_source > /dev/null
 done

# Invoke usage and help when requested

[ $get_help -eq 1 ] && usage " " " "

# parse vinfo to create the input card(s)

nspace=0
ncard=1
card01=''
card="card01"
for x in $vinfo
 do
    v=`echo $x|$AWK -F':' '{print $1}' -`
    f=`echo $x|$AWK -F':' '{print $2}' -`

    if [ "$f" = "nl" ]; then

        # If the format consists of just the letters nl then 
        # discard this vinfo entry and start a new card

        ncard=`expr $ncard + 1|$AWK '{printf "%2.2d",$1}' -`
        card="card$ncard"
        eval $card=\'\'
        continue
    fi

    if [ "x$v" = "x" ]; then

        # If the variable is not specified then assume it is blank space
        # and give it the name spacenn where nn is an integer
        # then define spacenn in this environment as a single blank

        [ "x$f" = "x" ] && f="$def_s_fmt"  # default format for a blank space

        nspace=`expr $nspace + 1|$AWK '{printf "%2.2d",$1}' -`
        v="space$nspace"
        eval $v=\' \'
    fi

    [ "x$f" = "x" ] && f="$def_d_fmt"  # default format for an integer variable

    if [ "x`echo $f|sed -n '/^%/p'`" = "x" ]; then

        # the format does not begin with a "%"...assume it is a fortran format string
        # and convert it to a printf format string

        f=`echo $f|tr a-z A-Z`   # translate all characters to upper case

        if [ "x`echo $f|sed -n '/[xX]/p'`" != "x" ]; then

            # The format string has an "x" in it... assume fortran x format
            # take everything before the first "x" to be the number of spaces
            # and ignore everything after the first "x" in the format specification

            width=`echo $f|$AWK '{i=index($1,"X")-1;c=substr($1,1,i);printf "%s",c}' -`

            # if width is not a number then set it to null

            width=`echo $width|sed -n '/^[0-9][0-9]*$/p'`

            # if width is null set it to the default width for a space

            [ "x$width" = "x" ] && width=$def_s_width
            echo "width=$width"
            f="%${width}s"

        elif [ "x`echo $f|sed -n '/[iI]/p'`" != "x" ]; then

            # The format string has an "i" in it ...assume fortran integer format
            # take everything after the first "i" to be the width

            width=`echo $f|$AWK '{i=index($1,"I")+1;c=substr($1,i);printf "%s",c}' -`

            # if width is not a number then set it to null

            width=`echo $width|sed -n '/^[0-9][0-9]*$/p'`
 
            # if width is null set it to the default width for an integer
 
            [ "x$width" = "x" ] && width=$def_d_width

            echo "width=$width"
            f="%${width}d"

        else

            # Unsupported/illegal format ...use the default

            echo "${Runame}: Illegal format for variable $v ...using default"
            f="$def_d_fmt"
        fi

    fi

    # Check to make sure this variable is defined
    # exit if it is not defined and verbose .le. 1

    eval zzyyxx=\`set \| sed -n \'/^ *$v/p\' \`

    [ "x$zzyyxx" = "x" ] && bail "$v not set" $verbose

    # now format this variable
    # we must use awk's printf because the shell version of printf on irix is crippled

    eval ${v}=\`echo \${$v}\|$AWK \'{printf \"$f\",\$1}\' -\`

    # Add the formatted variable to the gcmparm input card

    eval $card=\"\${$card}\${$v}\"

    # write variable name and value to stdout for sufficiently large verbose

    [ $verbose -gt 2 ] && eval echo \"${v}=\${$v}\"

 done

# Create the output file which will be used as input for gcmparm

rm -f $outfile
touch $outfile
ncard=`echo $ncard|sed 's/^00*//'` # remove any leading zeros from ncard
n=0
while [ $n -lt $ncard ]
  do
    n=`expr $n + 1|$AWK '{printf "%2.2d",$1}' -`
    card="card$n"
    eval echo \"\${$card}\" >> $outfile
    n=`echo $n|sed 's/^00*//'`     # remove any leading zeros from n
  done

if [ $verbose -gt 0 ]; then
     echo "${Runame}: Created $gcmparm parameter input file $outfile"
     cat $outfile
fi

# Sucessful completion
exit 0
