#!/bin/sh
# Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
#
# $Id: mopatch,v 5.1.2.16 2011-07-29 12:57:31 jschmidt Exp $

#
# MOPatch - Install Multiple Oracle Patches in One Run.
#
# Call mopatch -h for a short command-line overview.  See
# readme.txt under $ORACLE_HOME/MOPatch or SAP note 1027012 for
# complete documentation.
#

###############################
#
# included blocks and functions
#
###############################

IFS=" 	
"
SPC=" "
TAB="	"
NL="
"

( unset CDPATH ) 1>/dev/null 2>&1 && unset CDPATH
PS1='$ '
PS2='> '
PS4='+ '

logopen=0
logfile=""
logerrhdl=2
rlogopen=0
verbosep=0

internalerror=0
cmdlineerror=0
conflicterror=0
opatcherror=0
linkerror=0

usage()
{
  echo "$1" 1>&2
  echo "$USAGE" 1>&2
  exit 2
}

error()
{
  p_4_0_class=""
  if test "X$1" = "X-c"; then
    p_4_0_class=$2
    shift
    shift
  else
    p_4_0_class="internalerror"
  fi

  if test $logopen = 1; then
    echo 1>&2
    echo "$1" 1>&2

    # keep noise low in recursive calls
    if test $rlogopen = 0; then
      echo "Refer to log file${NL}  $logfile${NL}for more information." 1>&2
    fi
    echo 1>&3
    echo "$1  Exiting." 1>&3
  else
    echo "$1" 1>&2
  fi

  case $p_4_0_class in
    internalerror) exit  1 ;;
    cmdlineerror)  exit  2 ;;
    conflicterror) exit  4 ;;
    opatcherror)   exit  8 ;;
    linkerror)     exit 16 ;;
    ""|*[!0-9]*)   exit  1 ;;
    *)             exit $p_4_0_class ;;
  esac
}

logerr()
{
  p_4_1_quiet=""
  if test "X$1" = "X-q"; then
    p_4_1_quiet=1
    shift
  else
    p_4_1_quiet=0
  fi

  p_4_1_class=""
  if test "X$1" = "X-c"; then
    p_4_1_class=$2
    shift 2
  else
    p_4_1_class="internalerror"
  fi

  if test $p_4_1_quiet = 1; then
    if test $logopen = 1; then
      echo "$1" 1>&3
    fi
  else
    echo "$1  Continuing." 1>&2
    if test $logopen = 1; then
      echo "$1  Continuing." 1>&3
    fi
  fi

  eval "$p_4_1_class"='1'

  return 0
}

err()
{
  if test $# = 1; then
    echo "$1" 1>&2
    echo "$1" 1>&3
  else
    echo 1>&2
    echo 1>&3
  fi

  return 0
}

out()
{
  if test $# = 1; then
    echo "$1" 1>&1
    echo "$1" 1>&3
  else
    echo 1>&1
    echo 1>&3
  fi

  return 0
}

log()
{
  if test $# = 1; then
    echo "$1" 1>&3
  else
    echo 1>&3
  fi

  return 0
}

verbose()
{
  if test $# = 1; then
    if test $verbosep = 1; then
      echo "$1" 1>&1
    fi
    echo "$1" 1>&3
  else
    if test $verbosep = 1; then
      echo 1>&1
    fi
    echo 1>&3
  fi

  return 0
}

rawout()
{
  if test $# = 1; then
    echo "$1" 1>&1
  else
    echo 1>&1
  fi

  return 0
}

patchnoboringp()
{
  if test $patchno = "INVALID"; then
    return 0
  else
    case $patchbase in
      p${patchno}_*.zip|*/p${patchno}_*.zip|*!${patchno})
        return 0 ;;
      *)
        return 1 ;;
    esac
  fi
}

addtopatchlist()
{
  p_4_2_flag=""
  l_4_2_flagp=""
  if test "X$1" = "X-f"; then
    p_4_2_flag=$2
    l_4_2_flagp=1
    shift 2
  else
    p_4_2_flag=" "
    l_4_2_flagp=0
  fi

  p_4_2_patchlist=$1
  p_4_2_patchlistn="${1}n"
  p_4_2_patchlistf="${1}f"
  shift

  p_4_2_patchno=""
  if test "X$1" = "X-n"; then
    p_4_2_patchno=$2
    shift 2
  fi

  l_4_2_patchlist=""
  l_4_2_patchlistn=""
  l_4_2_patchlistf=""
  eval "l_4_2_patchlist=\${$p_4_2_patchlist-}"
  eval "l_4_2_patchlistn=\${$p_4_2_patchlistn-}"
  eval "l_4_2_patchlistf=\${$p_4_2_patchlistf-}"

  if test "X$p_4_2_patchno" != "X"; then
    l_4_2_patchlist="$l_4_2_patchlist$NL$p_4_2_flag $p_4_2_patchno"
  elif patchnoboringp; then
    l_4_2_patchlist="$l_4_2_patchlist$NL$p_4_2_flag $patchbase"
  else
    l_4_2_patchlist="$l_4_2_patchlist$NL$p_4_2_flag $patchbase (patch number $patchno)"
  fi

  if test $# -gt 0; then
    l_4_2_patchlist="$l_4_2_patchlist$1"
  fi

  l_4_2_patchlistn=`expr $l_4_2_patchlistn + 1 2>&3`
  l_4_2_patchlistf="$l_4_2_patchlistf$p_4_2_flag"

  eval "$p_4_2_patchlist"='$l_4_2_patchlist'
  eval "$p_4_2_patchlistn"='$l_4_2_patchlistn'
  if test $l_4_2_flagp = 1; then
    eval "$p_4_2_patchlistf"='$l_4_2_patchlistf'
  fi

  return 0
}

perror()
{
  p_4_3_class=""
  if test "X$1" = "X-c"; then
    p_4_3_class=$2
    shift
    shift
  else
    p_4_3_class="internalerror"
  fi

  err "Processing patch \"$patchbase\"...failed."
  err "Reason: $1"

  if test $# -gt 1; then
    addtopatchlist failed "$2"
  else
    addtopatchlist failed
  fi

  eval "$p_4_3_class"='1'

  return 0
}

pskip()
{
  p_4_4_class=""
  if test "X$1" = "X-c"; then
    p_4_4_class=$2
    shift 2
  fi

  p_4_4_flag=""
  if test "X$1" = "X-f"; then
    p_4_4_flag=$2
    shift 2
  fi

  out "Processing patch \"$patchbase\"...skipped."
  verbose "Reason: $2"

  if test "X$p_4_4_flag" != "X" && test $# -gt 2; then
    addtopatchlist -f "$p_4_4_flag" "$1" "$3"
  elif test "X$p_4_4_flag" != "X"; then
    addtopatchlist -f "$p_4_4_flag" "$1"
  elif test $# -gt 2; then
    addtopatchlist "$1" "$3"
  else
    addtopatchlist "$1"
  fi

  if test "X$p_4_4_class" != "X"; then
    eval "$p_4_4_class"='1'
  fi

  return 0
}

mlp()
{
  case $1 in
    *$NL*) return 0 ;;
    *)     return 1 ;;
  esac
}

swp()
{
  case $1 in
    *[$SPC$TAB$NL]*) return 1 ;;
    *)               return 0 ;;
  esac
}

mwp()
{
  case $1 in
    *[$SPC$TAB$NL]*) return 0 ;;
    *)               return 1 ;;
  esac
}

numberp()
{
  case $1 in
    ""|*[!0-9]*) return 1 ;;
    *)           return 0 ;;
  esac
}

varnamep()
{
  case $1 in
    ""|*[!a-zA-Z0-9_]*) return 1 ;;
    *)                  return 0 ;;
  esac
}

versionp()
{
  case $1 in
    # awkward but fast.  Most common case duplicated as first test.
    [0-9][0-9].[0-9].[0-9].[0-9].[0-9]) return 0 ;;
    [0-9].[0-9].[0-9].[0-9].[0-9]) return 0 ;;
    [0-9].[0-9].[0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9].[0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9].[0-9].[0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9].[0-9][0-9].[0-9].[0-9]) return 0 ;;
    [0-9].[0-9].[0-9][0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9].[0-9][0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9].[0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9].[0-9].[0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9][0-9].[0-9].[0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9][0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9].[0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9][0-9].[0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9][0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9][0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9].[0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9].[0-9][0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9]) return 0 ;;
    [0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9].[0-9][0-9]) return 0 ;;
    *) return 1 ;;
  esac
}

verstest()
{
  p_4_7_prec=""
  if test "X$1" = "X-d"; then
    p_4_7_prec=$2
    shift 2
  else
    p_4_7_prec=4
  fi

  p_4_7_relop=$2

  # convert version strings to version numbers and right-pad the
  # resulting numbers with zeroes to ten digits
  p_4_7_v1=`verstonum "$1" || :`
  p_4_7_v2=`verstonum "$3" || :`
  case $p_4_7_v1 in
    ??????????) : ;;
    ????????)   p_4_7_v1="${p_4_7_v1}00" ;;
    ??????)     p_4_7_v1="${p_4_7_v1}0000" ;;
    ????)       p_4_7_v1="${p_4_7_v1}000000" ;;
    ??)         p_4_7_v1="${p_4_7_v1}00000000" ;;
    *)          return 1 ;;
  esac
  case $p_4_7_v2 in
    ??????????) : ;;
    ????????)   p_4_7_v2="${p_4_7_v2}00" ;;
    ??????)     p_4_7_v2="${p_4_7_v2}0000" ;;
    ????)       p_4_7_v2="${p_4_7_v2}000000" ;;
    ??)         p_4_7_v2="${p_4_7_v2}00000000" ;;
    *)          return 1 ;;
  esac

  if test "X$p_4_7_relop" = "X=" ||
     test "X$p_4_7_relop" = "X!=" ||
     test $p_4_7_prec = 0; then
    if test $p_4_7_v1 "$p_4_7_relop" $p_4_7_v2; then
      return 0
    else
      return 1
    fi
  fi

  l_4_7_splitss=""
  case $p_4_7_prec in
    4) l_4_7_splitss='s/^\(........\)\(..\)$/\1\&\2/' ;;
    3) l_4_7_splitss='s/^\(......\)\(....\)$/\1\&\2/' ;;
    2) l_4_7_splitss='s/^\(....\)\(......\)$/\1\&\2/' ;;
    1) l_4_7_splitss='s/^\(..\)\(........\)$/\1\&\2/' ;;
  esac

  p_4_7_v1=`echo $p_4_7_v1 | sed "$l_4_7_splitss" 2>&$logerrhdl`
  p_4_7_v2=`echo $p_4_7_v2 | sed "$l_4_7_splitss" 2>&$logerrhdl`
  l_oldifs=$IFS; IFS="&"; set -f; set x $p_4_7_v1; shift; set +f; IFS=$l_oldifs
  l_4_7_v1mj=$1
  l_4_7_v1mi=$2
  l_oldifs=$IFS; IFS="&"; set -f; set x $p_4_7_v2; shift; set +f; IFS=$l_oldifs
  l_4_7_v2mj=$1
  l_4_7_v2mi=$2

  if test $l_4_7_v1mj = $l_4_7_v2mj &&
     test $l_4_7_v1mi "$p_4_7_relop" $l_4_7_v2mi; then
    return 0
  else
    return 1
  fi
}

globtest()
{
  p_4_8_filetest=$1
  shift

  for l_4_8_item in ${1+"$@"}
  do
    if test "$p_4_8_filetest" "$l_4_8_item"; then
      return 0
    fi
  done

  return 1
}

memberp()
{
  if test $# = 2; then
    if test "X$1" = "X"; then
      return 1
    else
      l_4_5_dellist=" $2 "
      case $l_4_5_dellist in
        *[$SPC$TAB$NL]"$1"[$SPC$TAB$NL]*) return 0 ;;
        *)                                return 1 ;;
      esac
    fi
  else
    if test "X$2" = "X"; then
      return 1
    else
      l_4_5_dellist="$1$3$1"
      case $l_4_5_dellist in
        *"$1$2$1"*) return 0 ;;
        *)          return 1 ;;
      esac
    fi
  fi
}

verstonum()
{
  if test "X$1" = "X"; then
    echo ""
    return 1
  fi

  l_4_6_nver=""
  l_4_6_bad=0
  l_oldifs=$IFS; IFS="."; set -f; set x $1; shift; set +f; IFS=$l_oldifs
  for l_4_6_v in "$@"
  do
    case $l_4_6_v in
      [0-9][0-9]) l_4_6_nver="${l_4_6_nver}${l_4_6_v}" ;;
      [0-9])      l_4_6_nver="${l_4_6_nver}0${l_4_6_v}" ;;
      *)          l_4_6_bad=1 ;;
    esac
  done

  if test $l_4_6_bad = 0; then
    echo $l_4_6_nver
    return 0
  else
    echo ""
    return 1
  fi
}

quotemeta()
{
  l_4_9_scqre=""                                    # special char quoting regexp
  if test "X${1-}" = "X-x"; then
    l_4_9_scqre='\.\*\[\\\^\$(){}?+|'
    shift
  else
    l_4_9_scqre='\.\*\[\\\^\$'
  fi

  if test "X${1-}" = "X-s"; then
    l_4_9_scqre="$l_4_9_scqre\\$2"
    shift
    shift
  fi

  if sed 's/\(['"$l_4_9_scqre"']\)/\\\1/g'; then
    return 0
  else
    return 1
  fi
}

basenamef()
{
  case $1 in
    *$NL*)
      echo "Internal error: Invalid newline characters in file name." 1>&2
      return 1 ;;
  esac

  if test "X$1" = "X"; then
    echo ""
    return 0
  elif expr "//$1" : '.*/\(.*\)$'; then
    return 0
  else
    return 1
  fi
}

exitvalf()
{
  l_4_10_exitval=0

  if test $internalerror != 0; then
    l_4_10_exitval=`expr $l_4_10_exitval + 1`
  fi
  if test $cmdlineerror != 0; then
    l_4_10_exitval=`expr $l_4_10_exitval + 2`
  fi
  if test $conflicterror != 0; then
    l_4_10_exitval=`expr $l_4_10_exitval + 4`
  fi
  if test $opatcherror != 0; then
    l_4_10_exitval=`expr $l_4_10_exitval + 8`
  fi
  if test $linkerror != 0; then
    l_4_10_exitval=`expr $l_4_10_exitval + 16`
  fi

  case $l_4_10_exitval in
    [0-9]|[0-9][0-9])
      echo "$l_4_10_exitval"
      return 0 ;;
    *)
      echo "Internal error: Invalid exit value \"$l_4_10_exitval\"." 1>&2
      echo "1"
      return 1 ;;
  esac
}

platforminfof()
{
  l_4_11_platform=""
  if test $# -gt 0; then
    l_4_11_platform=$1
  else
    l_4_11_uname=""
    if l_4_11_uname=`uname -s -m 2>&3`; then
      :
    else
      logerr -q "Cannot execute \"uname\" to determine platform."
      echo ""
      return 1
    fi

    # handle some exceptions
    case $l_4_11_uname in
      AIX\ *) l_4_11_platform="aix" ;;
      HP-UX\ 9000/785) l_4_11_platform="hp_ux_9000_800" ;;
      SunOS\ sun4[cdmsv]) l_4_11_platform="sunos_sun4u" ;;
      SunOS\ sun4us) l_4_11_platform="sunos_sun4u" ;;
      *)
        l_4_11_platform=`printf '%s\n' "$l_4_11_uname" |
                    tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 2>&3 |
                    sed 's/[^a-z0-9]/_/g' 2>&3` ;;
    esac
  fi

  case $l_4_11_platform in
    aix|212|rs6000_64)            l_4_11_platform="aix 212 rs6000_64" ;;
    hp_ux_9000_800|59|hp_64)      l_4_11_platform="hp_ux_9000_800 59 hp_64" ;;
    hp_ux_ia64|197|hpia64)        l_4_11_platform="hp_ux_ia64 197 hpia64" ;;
    sunos_sun4u|23|sun_64)        l_4_11_platform="sunos_sun4u 23 sun_64" ;;
    sunos_i86pc|267|sunx86_64)    l_4_11_platform="sunos_i86pc 267 sunx86_64" ;;
    osf1_alpha|87|alphaosf)       l_4_11_platform="osf1_alpha 87 alphaosf" ;;
    linux_x86_64|226|linuxx86_64) l_4_11_platform="linux_x86_64 226 linuxx86_64" ;;
    linux_i686|46|linuxintel)     l_4_11_platform="linux_i686 46 linuxintel" ;;
    linux_ia64|214|linuxia64)     l_4_11_platform="linux_ia64 214 linuxia64" ;;
    linux_ppc64|227|linuxppc64)   l_4_11_platform="linux_ppc64 227 linuxppc64" ;;
    *)
      logerr -q "Cannot determine platform from \"$l_4_11_platform\"."
      echo ""
      return 1 ;;
  esac

  # determine kernel version
  l_4_11_release=""
  if l_4_11_release=`uname -r 2>&3`; then
    :
  else
    logerr -q "Cannot execute \"uname\" to determine kernel version."
    echo ""
    return 1
  fi
  if mwp "$l_4_11_release"; then
    logerr -q "Cannot determine kernel version from \"$l_4_11_release\"."
    echo ""
    return 1
  fi

  echo "$l_4_11_platform $l_4_11_release"

  return 0
}

RDBMSVERRE='^[ 	]*SQL\*Plus:[ 	][ 	]*Release[ 	][ 	]*[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*[ 	][ 	]*(-[ 	][ 	]*)?Production[ 	]*$'
RDBMSVER10SE='^[ 	]*SQL\*Plus:[ 	][ 	]*Release[ 	][ 	]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)[ 	][ 	]*-[ 	][ 	]*Production[ 	]*$'
RDBMSVER11SE='^[ 	]*SQL\*Plus:[ 	][ 	]*Release[ 	][ 	]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)[ 	][ 	]*Production[ 	]*$'

rdbmsversionf()
{
  # determine and verify Oracle RDBMS version
  l_4_12_rdbmsver=""
  l_4_12_rdbmsverlog="$tmpdir/rdbmsver.log"
  log "executing: \"$ORACLE_HOME/bin/sqlplus\" -v"
  # SQL*Plus depends on the shared library path, so better
  # explicitly set that
  if LD_LIBRARY_PATH="$ORACLE_HOME/lib" \
     SHLIB_PATH="$ORACLE_HOME/lib"      \
     LIBPATH="$ORACLE_HOME/lib"         \
     "$ORACLE_HOME/bin/sqlplus" -v 1>"$l_4_12_rdbmsverlog" 2>&3; then
    l_4_12_rdbmsver=`( egrep "$RDBMSVERRE" "$l_4_12_rdbmsverlog" 2>&3 || : ) |
                sed 's/'"$RDBMSVER10SE"'/\1/;s/'"$RDBMSVER11SE"'/\1/' 2>&3`
    if versionp "$l_4_12_rdbmsver"; then
      :
    else
      logerr -q "Cannot determine Oracle RDBMS version from \"$l_4_12_rdbmsver\"."
      echo ""
      return 1
    fi
  else
    logerr -q "Cannot execute SQL*Plus to determine Oracle RDBMS version."
    echo ""
    return 1
  fi

  # determine and verify Oracle RDBMS numeric version
  l_4_12_rdbmsnver=""
  if l_4_12_rdbmsnver=`verstonum "$l_4_12_rdbmsver"`; then
    :
  else
    logerr -q "Cannot determine Oracle RDBMS numeric version from \"$l_4_12_rdbmsver\"."
    echo ""
    return 1
  fi

  # determine Oracle RDBMS version components
  l_oldifs=$IFS; IFS="."; set -f; set x $l_4_12_rdbmsver; shift; set +f; IFS=$l_oldifs

  echo "$l_4_12_rdbmsver $l_4_12_rdbmsnver $1 $2 $3 $4 $5"

  return 0
}

OPVERRE='^[ 	]*OPatch[ 	][ 	]*Version:[ 	][ 	]*[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*[ 	]*$'
OPVERSE='^[ 	]*OPatch[ 	][ 	]*Version:[ 	][ 	]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)[ 	]*$'

OPVERSION_NAPPLYP=3
OPVERSION_CUSP=4
OPVERSION_XMLINVP=5
OPVERSION_HPATCHP=6
OPVERSION_PSUSP=7

opversionf()
{
  # determine and verify OPatch version
  l_4_13_opver=""
  l_4_13_opverlog="$tmpdir/opver.log"
  log "executing: \"$opatch\" version ${1+$*}"
  if "$opatch" version ${1+"$@"} 1>"$l_4_13_opverlog" 2>&3; then
    l_4_13_opver=`( grep "$OPVERRE" "$l_4_13_opverlog" 2>&3 || : ) |
             sed 's/'"$OPVERSE"'/\1/' 2>&3`
    if versionp "$l_4_13_opver"; then
      :
    else
      logerr -q "Cannot determine OPatch version from \"$l_4_13_opver\"."
      echo ""
      return 1
    fi
  else
    logerr -q "Cannot execute OPatch to determine OPatch version."
    echo ""
    return 1
  fi

  # determine and verify OPatch numeric version
  l_4_13_opnver=""
  if l_4_13_opnver=`verstonum "$l_4_13_opver"`; then
    :
  else
    logerr -q "Cannot determine OPatch numeric version from \"$l_4_13_opver\"."
    echo ""
    return 1
  fi

  # OPVD: determine n-apply support.  We could have used function
  # "verstest" for this and all the following tests, but the
  # present mixture of "case" statements and comparisons with
  # "$l_opnver" is probably faster.
  l_4_13_napplyp=""
  case $l_4_13_opver in
    11.2.*) l_4_13_napplyp=1 ;;
    11.1.*) l_4_13_napplyp=1 ;;
    10.2.0.5.*) l_4_13_napplyp=1 ;;
    10.2.0.4.*)
      if test $l_4_13_opnver -ge 1002000402; then    # 10.2.0.4.2
        l_4_13_napplyp=1
      else
        l_4_13_napplyp=0
      fi ;;
    10.2.0.2.*)
      if test $l_4_13_opnver -ge 1002000203; then    # 10.2.0.2.3
        l_4_13_napplyp=1
      else
        l_4_13_napplyp=0
      fi ;;
    *) l_4_13_napplyp=0 ;;
  esac

  # OPVD: determine patch storage clean-up support
  l_4_13_cusp=""
  case $l_4_13_opver in
    11.2.*) l_4_13_cusp=1 ;;
    11.1.*) l_4_13_cusp=1 ;;
    10.2.0.5.*) l_4_13_cusp=1 ;;
    10.2.0.4.*)
      if test $l_4_13_opnver -ge 1002000402; then    # 10.2.0.4.2
        l_4_13_cusp=1
      else
        l_4_13_cusp=0
      fi ;;
    10.2.0.2.*)
      if test $l_4_13_opnver -ge 1002000204; then    # 10.2.0.2.4
        l_4_13_cusp=1
      else
        l_4_13_cusp=0
      fi ;;
    *) l_4_13_cusp=0 ;;
  esac

  # OPVD: determine "correct" XML inventory support
  l_4_13_xmlinvp=""
  case $l_4_13_opver in
    11.2.*) l_4_13_xmlinvp=1 ;;
    11.1.*) l_4_13_xmlinvp=1 ;;
    10.2.0.5.*) l_4_13_xmlinvp=1 ;;
    10.2.0.4.*)
      if test $l_4_13_opnver -ge 1002000402; then    # 10.2.0.4.2
        l_4_13_xmlinvp=1
      else
        l_4_13_xmlinvp=0
      fi ;;
    10.2.0.2.*)
      if test $l_4_13_opnver -ge 1002000205; then    # 10.2.0.2.5
        l_4_13_xmlinvp=1
      else
        l_4_13_xmlinvp=0
      fi ;;
    *) l_4_13_xmlinvp=0 ;;
  esac

  # OPVD: determine online patch apply support
  l_4_13_hpatchp=""
  case $l_4_13_opver in
    11.2.*) l_4_13_hpatchp=1 ;;
    11.1.*) l_4_13_hpatchp=1 ;;
    *)      l_4_13_hpatchp=0 ;;
  esac

  # OPVD: determine PSU and dependency handling support
  l_4_13_psusp=""
  case $l_4_13_opver in
    11.2.*) l_4_13_psusp=1 ;;
    11.1.*)
      if test $l_4_13_opnver -ge 1101000607; then    # 11.1.0.6.7
        l_4_13_psusp=1
      else
        l_4_13_psusp=0
      fi ;;
    10.2.0.5.*) l_4_13_psusp=1 ;;
    10.2.0.4.*)
      if test $l_4_13_opnver -ge 1002000407; then    # 10.2.0.4.7
        l_4_13_psusp=1
      else
        l_4_13_psusp=0
      fi ;;
    *) l_4_13_psusp=0 ;;
  esac

  #     $1       $2        $3         $4      $5         $6         $7
  echo "$l_4_13_opver $l_4_13_opnver $l_4_13_napplyp $l_4_13_cusp $l_4_13_xmlinvp $l_4_13_hpatchp $l_4_13_psusp"

  return 0
}

PMDXMLGESS='s/&/\&amp;/g;s/</\&lt;/g;s/>/\&gt;/g;s/'\''/\&apos;/g;s/"/\&quot;/g'

pmdtbiids=""
pmdinsids=""
pmdrlbids=""

pmdpatchbase=""
pmdpatchsrc=""
pmdpatchmlc=""
pmdpatchno=""
pmdpatchstamp=""
pmdpatchflags=""
pmdpatchprqs=""
pmdpatchovls=""
pmdpatchfiles=""
pmdpatchmakes=""

pmdcache()
{
  p_7_0_invtype=$1
  p_7_0_patchid=$2

  l_7_0_pmd=""
  if test "X$p_7_0_invtype" = "Xtbi" && eval test '"X${hash_pmdtbi_'"$p_7_0_patchid"'+X}"' = '"XX"'; then
    eval l_7_0_pmd=\${hash_pmdtbips_"$p_7_0_patchid"-}
    l_oldifs=$IFS; IFS="&"; set -f; set x $l_7_0_pmd; shift; set +f; IFS=$l_oldifs
    pmdpatchbase=$1
    pmdpatchsrc=$2
    pmdpatchmlc=$3

    eval l_7_0_pmd=\${hash_pmdtbi_"$p_7_0_patchid"-}
    l_oldifs=$IFS; IFS="&"; set -f; set x $l_7_0_pmd; shift; set +f; IFS=$l_oldifs
    pmdpatchno=$1
    pmdpatchstamp=$2
    pmdpatchflags=$3

    eval pmdpatchprqs=\${hash_pmdtbiprs_"$p_7_0_patchid"-}
    eval pmdpatchovls=\${hash_pmdtbiols_"$p_7_0_patchid"-}
    eval pmdpatchfiles=\${hash_pmdtbifls_"$p_7_0_patchid"-}
    eval pmdpatchmakes=\${hash_pmdtbimks_"$p_7_0_patchid"-}
    return 0
  elif test "X$p_7_0_invtype" = "Xins" && eval test '"X${hash_pmdins_'"$p_7_0_patchid"'+X}"' = '"XX"'; then
    pmdpatchbase=""
    pmdpatchsrc=""
    pmdpatchmlc=""

    eval l_7_0_pmd=\${hash_pmdins_"$p_7_0_patchid"-}
    l_oldifs=$IFS; IFS="&"; set -f; set x $l_7_0_pmd; shift; set +f; IFS=$l_oldifs
    pmdpatchno=$1
    pmdpatchstamp=$2
    pmdpatchflags=$3

    eval pmdpatchprqs=\${hash_pmdinsprs_"$p_7_0_patchid"-}
    eval pmdpatchovls=\${hash_pmdinsols_"$p_7_0_patchid"-}
    eval pmdpatchfiles=\${hash_pmdinsfls_"$p_7_0_patchid"-}
    eval pmdpatchmakes=\${hash_pmdinsmks_"$p_7_0_patchid"-}
    return 0
  elif test "X$p_7_0_invtype" = "Xrlb" && eval test '"X${hash_pmdrlb_'"$p_7_0_patchid"'+X}"' = '"XX"'; then
    pmdpatchbase=""
    pmdpatchsrc=""
    pmdpatchmlc=""

    eval l_7_0_pmd=\${hash_pmdrlb_"$p_7_0_patchid"-}
    l_oldifs=$IFS; IFS="&"; set -f; set x $l_7_0_pmd; shift; set +f; IFS=$l_oldifs
    pmdpatchno=$1
    pmdpatchstamp=$2
    pmdpatchflags=$3

    eval pmdpatchprqs=\${hash_pmdrlbprs_"$p_7_0_patchid"-}
    eval pmdpatchovls=\${hash_pmdrlbols_"$p_7_0_patchid"-}
    eval pmdpatchfiles=\${hash_pmdrlbfls_"$p_7_0_patchid"-}
    eval pmdpatchmakes=\${hash_pmdrlbmks_"$p_7_0_patchid"-}
    return 0
  else
    pmdpatchbase=""
    pmdpatchsrc=""
    pmdpatchmlc=""
    pmdpatchno=""
    pmdpatchstamp=""
    pmdpatchflags=""
    pmdpatchprqs=""
    pmdpatchovls=""
    pmdpatchfiles=""
    pmdpatchmakes=""
    return 1
  fi
}

pmdget()
{
  p_7_1_invtype=$1
  p_7_1_patchid=$2
  shift 2

  # true ampersand-list of variable references
  # "${pmd<patchfield>-}" to the patch metadata
  l_7_1_fields=""
  while test $# -gt 0; do
    if test $# -gt 1; then
      l_7_1_fields="$l_7_1_fields\${pmd$1-}&"
    else
      l_7_1_fields="$l_7_1_fields\${pmd$1-}"
    fi
    shift
  done

  # we may call "pmdcache" here without being afraid of
  # overwriting the caller's cache, since we only ever will be
  # called in a command substitution
  if pmdcache "$p_7_1_invtype" $p_7_1_patchid; then
    eval "printf '%s\n' \"$l_7_1_fields\""
    return 0
  else
    echo ""
    return 1
  fi
}

pmdfind()
{
  p_7_2_invtype=$1
  shift

  # determine query command
  l_7_2_query=":"
  while test $# -gt 0; do
    l_7_2_param1=$1
    l_7_2_op=$2
    l_7_2_param2=$3
    shift 3

    case $l_7_2_op in
      "~")
        if test "X$l_7_2_param1" = "Xpatchflags"; then
          l_7_2_query="$l_7_2_query && ( case \$pmdpatchflags in *' $l_7_2_param2 '*) exit 0 ;; *) exit 1 ;; esac )"
        else
          l_7_2_query="$l_7_2_query && ( case \${pmd$l_7_2_param1-} in $l_7_2_param2) exit 0 ;; *) exit 1 ;; esac )"
        fi ;;
      "!~")
        if test "X$l_7_2_param1" = "Xpatchflags"; then
          l_7_2_query="$l_7_2_query && ( case \$pmdpatchflags in *' $l_7_2_param2 '*) exit 1 ;; *) exit 0 ;; esac )"
        else
          l_7_2_query="$l_7_2_query && ( case \${pmd$l_7_2_param1-} in $l_7_2_param2) exit 1 ;; *) exit 0 ;; esac )"
        fi ;;
      *)
        case $l_7_2_param1 in
          patch*) l_7_2_param1="\"\${pmd$l_7_2_param1-}\"" ;;
          *\'*)   l_7_2_param1=`printf '%s\n' "$l_7_2_param1" | sed "s/'/'\\\\\\\\''/g" 2>&3`
                  l_7_2_param1="'$l_7_2_param1'" ;;
          *)      l_7_2_param1="'$l_7_2_param1'" ;;
        esac
        case $l_7_2_param2 in
          patch*) l_7_2_param2="\"\${pmd$l_7_2_param2-}\"" ;;
          *\'*)   l_7_2_param2=`printf '%s\n' "$l_7_2_param2" | sed "s/'/'\\\\\\\\''/g" 2>&3`
                  l_7_2_param2="'$l_7_2_param2'" ;;
          *)      l_7_2_param2="'$l_7_2_param2'" ;;
        esac

        l_7_2_query="$l_7_2_query && test $l_7_2_param1 '$l_7_2_op' $l_7_2_param2" ;;
    esac
  done

  l_7_2_patchids=""
  case $p_7_2_invtype in
    tbi) l_7_2_patchids=$pmdtbiids ;;
    ins) l_7_2_patchids=$pmdinsids ;;
    rlb) l_7_2_patchids=$pmdrlbids ;;
  esac

  l_7_2_result=""
  l_oldifs=$IFS; IFS=" "; set -f; set x $l_7_2_patchids; shift; set +f; IFS=$l_oldifs
  for l_7_2_patchid in ${1+"$@"}; do
    # redirect "eval"'s STDERR to the log file to not loose
    # potential error messages
    if ( pmdcache "$p_7_2_invtype" $l_7_2_patchid && eval "$l_7_2_query" 2>&3 ); then
      l_7_2_result="$l_7_2_result$l_7_2_patchid "
    fi
  done

  echo "$l_7_2_result"
  if test "X$l_7_2_result" != "X"; then
    return 0
  else
    return 1
  fi
}

pmdcpmv()
{
  # verify existence of patch in source inventory
  case $2 in
    tbi) eval test '"X${hash_pmdtbi_'"$4"'+X}"' = '"XX"' || return 1 ;;
    ins) eval test '"X${hash_pmdins_'"$4"'+X}"' = '"XX"' || return 1 ;;
    rlb) eval test '"X${hash_pmdrlb_'"$4"'+X}"' = '"XX"' || return 1 ;;
  esac

  # copy patch from source to destination inventory.  Do not
  # check whether the source patch fields actually exist or not -
  # function "pmdcache" does not, either.
  eval "hash_pmd${3}ps_${5}=\${hash_pmd${2}ps_${4}-}"
  eval "hash_pmd${3}_${5}=\${hash_pmd${2}_${4}-}"
  eval "hash_pmd${3}prs_${5}=\${hash_pmd${2}prs_${4}-}"
  eval "hash_pmd${3}ols_${5}=\${hash_pmd${2}ols_${4}-}"
  eval "hash_pmd${3}fls_${5}=\${hash_pmd${2}fls_${4}-}"
  eval "hash_pmd${3}mks_${5}=\${hash_pmd${2}mks_${4}-}"

  if test "X$1" = "X-m"; then
    # remove patch from source inventory
    unset "hash_pmd${2}ps_${4}"
    unset "hash_pmd${2}_${4}"
    unset "hash_pmd${2}prs_${4}"
    unset "hash_pmd${2}ols_${4}"
    unset "hash_pmd${2}fls_${4}"
    unset "hash_pmd${2}mks_${4}"
  fi

  # add patch ID to destination list, if required
  case $3 in
    tbi) memberp " " $5 "$pmdtbiids" || pmdtbiids="$pmdtbiids${5} " ;;
    ins) memberp " " $5 "$pmdinsids" || pmdinsids="$pmdinsids${5} " ;;
    rlb) memberp " " $5 "$pmdrlbids" || pmdrlbids="$pmdrlbids${5} " ;;
  esac

  if test "X$1" = "X-m"; then
    # remove patch ID from source list
    case $2 in
      tbi) pmdtbiids=`echo "$pmdtbiids" | sed "s/^$4 //;s/ $4 / /" 2>&3` || return 1 ;;
      ins) pmdinsids=`echo "$pmdinsids" | sed "s/^$4 //;s/ $4 / /" 2>&3` || return 1 ;;
      rlb) pmdrlbids=`echo "$pmdrlbids" | sed "s/^$4 //;s/ $4 / /" 2>&3` || return 1 ;;
    esac
  fi

  return 0
}

pmdmv2front()
{
  p_7_3_invtype=$1
  shift

  l_7_3_patchids=""
  case $p_7_3_invtype in
    tbi) l_7_3_patchids=$pmdtbiids ;;
    ins) l_7_3_patchids=$pmdinsids ;;
    rlb) l_7_3_patchids=$pmdrlbids ;;
  esac

  l_7_3_frontids=""
  for l_7_3_patchid in ${1+"$@"}
  do
    l_7_3_frontids="$l_7_3_frontids$l_7_3_patchid "
    l_7_3_patchids=`echo "$l_7_3_patchids" |
                sed "s/^$l_7_3_patchid //;s/ $l_7_3_patchid / /" 2>&3`
  done

  case $p_7_3_invtype in
    tbi) pmdtbiids="$l_7_3_frontids$l_7_3_patchids" ;;
    ins) pmdinsids="$l_7_3_frontids$l_7_3_patchids" ;;
    rlb) pmdrlbids="$l_7_3_frontids$l_7_3_patchids" ;;
  esac

  return 0
}

pmdflaginfo()
{
  l_7_4_pmflag=""
  if pmdflagset "exec" "shtdwn"; then
    l_7_4_pmflag=" "
  elif pmdflagset "hpatch"; then
    l_7_4_pmflag="*"
  else
    l_7_4_pmflag="+"
  fi

  l_7_4_piiflag=""
  if pmdflagset "psu" "catpsu"; then
    l_7_4_piiflag="!"
  elif pmdflagset "catcpu"; then
    l_7_4_piiflag="*"
  elif pmdflagset "cpumlc" "cpurlb"; then
    # molecules of a CPU do not have their own post-installation
    # instructions
    l_7_4_piiflag=" "
  elif pmdflagset "sql"; then
    l_7_4_piiflag="+"
  else
    l_7_4_piiflag=" "
  fi

  l_7_4_flagstring=""
  if pmdflagset "psu" "catpsu"; then
    l_7_4_flagstring="${l_7_4_flagstring}psu "
  elif pmdflagset "catcpu"; then
    l_7_4_flagstring="${l_7_4_flagstring}cpu "
  elif pmdflagset "cpumlc" "cpurlb"; then
    # PSUs routinely contain CPU molecules, so this must come in
    # an "elif" branch
    l_7_4_flagstring="${l_7_4_flagstring}cpumlc "
  fi
  if pmdflagset "sql"; then
    l_7_4_flagstring="${l_7_4_flagstring}sql "
  fi
  if pmdflagset "java"; then
    l_7_4_flagstring="${l_7_4_flagstring}java "
  fi
  if pmdflagset "opatch"; then
    l_7_4_flagstring="${l_7_4_flagstring}opatch "
  fi
  l_oldifs=$IFS; IFS=" "; set -f; set x $l_7_4_flagstring; shift; set +f; IFS=$l_oldifs
  l_7_4_flagstring=${1+$*}

  echo "$l_7_4_pmflag&$l_7_4_piiflag&$l_7_4_flagstring"
  return 0
}

pmdflagset()
{
  for l_7_5_patchflag in "$@"
  do
    case $pmdpatchflags in
      *" $l_7_5_patchflag "*) return 0 ;;
    esac
  done
  return 1
}

pmdflagclr()
{
  for l_7_6_patchflag in "$@"
  do
    case $pmdpatchflags in
      *" $l_7_6_patchflag "*) return 1 ;;
    esac
  done
  return 0
}

pmdfilep()
{
  case $1 in
    ""|*[/$NL!\&]*) return 1 ;;
    *)              return 0 ;;
  esac
}

pmddirp()
{
  case $1 in
    ""|*[$NL!\&]*) return 1 ;;
    *)             return 0 ;;
  esac
}

pmdtbihdr()
{
  p_7_7_patchid=$1
  l_7_7_patchbasexml=`printf '%s\n' "$2" | sed "$PMDXMLGESS" 2>&3`
  l_7_7_patchsrcxml=`printf '%s\n' "$3" | sed "$PMDXMLGESS" 2>&3`
  l_7_7_patchmlcxml=`printf '%s\n' "$4" | sed "$PMDXMLGESS" 2>&3`
  shift 4

  printf '%s\n' "<INTERIM_PATCH REF_ID=\"$p_7_7_patchid\">" 1>&6
  printf '%s\n' "<patchbase>$l_7_7_patchbasexml</patchbase>" 1>&6
  printf '%s\n' "<patchsrc>$l_7_7_patchsrcxml</patchsrc>" 1>&6
  printf '%s\n' "<patchmlc>$l_7_7_patchmlcxml</patchmlc>" 1>&6

  for l_7_7_patchflag in ${1+"$@"}
  do
    pmdtbiflg "$l_7_7_patchflag"
  done
  return 0
}

pmdtbiflg()
{
  echo "<patchflag>$1</patchflag>" 1>&6
  return 0
}

pmdtbiftr()
{
  for l_7_8_patchflag in ${1+"$@"}
  do
    pmdtbiflg "$l_7_8_patchflag"
  done
  echo "</INTERIM_PATCH>" 1>&6
  return 0
}

pmdtbipatch()
{
  pmdtbihdr "$@"
  pmdtbiftr
  return 0
}

pmdtbifiles()
{
  if sed "$PMDXMLGESS;"'s/^.*$/<file>&<\/file>/' 1>&6 2>&3; then
    return 0
  else
    return 1
  fi
}

RDM_CKSUM_OPI=""
RDM_CKSUM_PDII=""
RDM_CKSUM_PII=""
RDM_CKSUM_PIS=""
RDM_CKSUM_PPII=""
RDM_CKSUM_PPIS=""
RDM_CKSUM_PSI=""
RDM_CKSUM_TITLE=""

RDM_CKSUM_OPI="${RDM_CKSUM_OPI}1427214443 1066${NL}"
RDM_CKSUM_OPI="${RDM_CKSUM_OPI}3097530531 218${NL}"

RDM_CKSUM_PDII="${RDM_CKSUM_PDII}1015428360 565${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}1398202640 340${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}3488603691 1060${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}3583276666 291${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}3618834345 46${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}4283373679 55${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}716516252 364${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}762288036 1072${NL}"
RDM_CKSUM_PDII="${RDM_CKSUM_PDII}883562646 64${NL}"

RDM_CKSUM_PII="${RDM_CKSUM_PII}1061769835 879${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}1507303873 1087${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}1609721226 222${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}2106167260 424${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}3072166991 859${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}3167795196 330${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}3638857628 652${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}393391967 918${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}4122196222 1091${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}4174258423 595${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}823668502 495${NL}"
RDM_CKSUM_PII="${RDM_CKSUM_PII}955715494 326${NL}"

RDM_CKSUM_PIS="${RDM_CKSUM_PIS}2449779061 664${NL}"
RDM_CKSUM_PIS="${RDM_CKSUM_PIS}3388953630 1334${NL}"
RDM_CKSUM_PIS="${RDM_CKSUM_PIS}4159378879 330${NL}"

RDM_CKSUM_PPII="${RDM_CKSUM_PPII}1202926162 16${NL}"
RDM_CKSUM_PPII="${RDM_CKSUM_PPII}254360664 5${NL}"

RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}2485116716 2346${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}2691260191 2387${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}3090244385 1625${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}3400764618 2391${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}657248193 2391${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}741892207 2079${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}856494134 2375${NL}"
RDM_CKSUM_PPIS="${RDM_CKSUM_PPIS}985602706 2371${NL}"

RDM_CKSUM_PSI="${RDM_CKSUM_PSI}1872289112 388${NL}"
RDM_CKSUM_PSI="${RDM_CKSUM_PSI}1931427268 658${NL}"
RDM_CKSUM_PSI="${RDM_CKSUM_PSI}3262758368 130${NL}"
RDM_CKSUM_PSI="${RDM_CKSUM_PSI}3262758368 130${NL}"
RDM_CKSUM_PSI="${RDM_CKSUM_PSI}4129019391 222${NL}"

RDM_CKSUM_TITLE="${RDM_CKSUM_TITLE}1167487607 2302${NL}"
RDM_CKSUM_TITLE="${RDM_CKSUM_TITLE}3529710619 808${NL}"


eval hash_RDM_CKSUMS_"opi"='"$RDM_CKSUM_OPI"'
eval hash_RDM_CKSUMS_"pdii"='"$RDM_CKSUM_PDII"'
eval hash_RDM_CKSUMS_"pii"='"$RDM_CKSUM_PII"'
eval hash_RDM_CKSUMS_"pis"='"$RDM_CKSUM_PIS"'
eval hash_RDM_CKSUMS_"ppii"='"$RDM_CKSUM_PPII"'
eval hash_RDM_CKSUMS_"ppis"='"$RDM_CKSUM_PPIS"'
eval hash_RDM_CKSUMS_"psi"='"$RDM_CKSUM_PSI"'
eval hash_RDM_CKSUMS_"title"='"$RDM_CKSUM_TITLE"'

rdmnonstdp=""

rdmdel()
{
  while test $# -gt 0; do
    echo "${1},${2}d" 1>&6
    shift 2
  done

  return 0
}

rdmscreen()
{
  p_9_0_sectype=$1
  p_9_0_secbeg=$2
  p_9_0_secend=$3
  shift 3

  # calculate and normalize checksum.  PD (osf1_alpha): Set
  # environment variable "$CMD_ENV" to force Posixly correct
  # results.
  l_9_0_cksum=""
  if l_9_0_cksum=`CMD_ENV=xpg4 cksum 2>&3`; then
    :
  else
    logerr "Cannot calculate README checksum ($p_9_0_sectype, $p_9_0_secbeg, $p_9_0_secend)."
    return 1
  fi
  l_9_0_cksum=`set $l_9_0_cksum; echo "$1 $2"`

  eval l_9_0_cksums=\${hash_RDM_CKSUMS_"$p_9_0_sectype"-}
  if memberp "$NL" "$l_9_0_cksum" "$l_9_0_cksums"; then
    rdmdel $p_9_0_secbeg $p_9_0_secend
    return 0
  elif test $flag_readme_strip_log = 1; then
    log "wrong checksum ($p_9_0_sectype, $p_9_0_secbeg, $p_9_0_secend): $l_9_0_cksum"
    l_9_0_sectpuc=`echo "$p_9_0_sectype" | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 2>&3`
    echo "${p_9_0_secbeg}i\\${NL}RDM_CKSUM_${l_9_0_sectpuc}=\"\${RDM_CKSUM_${l_9_0_sectpuc}}$l_9_0_cksum\${NL}\"" 1>&6
    echo "${p_9_0_secend}a\\${NL}RDM_CKSUM_${l_9_0_sectpuc}=\"\${RDM_CKSUM_${l_9_0_sectpuc}}$l_9_0_cksum\${NL}\"" 1>&6
    rdmdel ${1+"$@"}
    return 1
  else
    rdmdel ${1+"$@"}
    return 1
  fi
}

rdmdocflags()
{
  if test "X$documentedf" != "X"; then
    echo
  fi
  case $documentedf in
    *"+"*)
      echo "Patches marked with a \"+\" contain a non-standard README file." ;;
  esac
  case $documentedf in
    *"-"*)
      echo "Patches marked with a \"-\" are part of an n-apply patch or a patch"
      echo "bundle and do not have their own README file.  Check the tracking bug." ;;
  esac
  case $documentedf in
    *"*"*)
      echo "Patches marked with a \"*\" do not contain a \"README.txt\" file, but"
      echo "may contain other installation information, such as a"
      echo "\"README.html\".  See below for more information." ;;
  esac
  case $documentedf in
    *"!"*)
      echo "Patches marked with a \"!\" could not be properly processed.  Refer"
      echo "to the MOPatch log file for more information." ;;
  esac
  return 0
}

rdmheader()
{
  if $MOPATCH_TEST_MIGRATE; then
  cat << EOF 1>&1 || return 1

                     Collected patch READMEs
                     =======================

This file was generated by MOPatch on $1.

It contains the collected README files of the following patches:
$installed

The file $2, which was
generated in the same directory as this file, contains the
stripped README files of these patches, which should be more
readable.

Please read this information carefully before installing the
patches.

EOF
  else
  cat << EOF 1>&1 || return 1

                     Collected patch READMEs
                     =======================

This file was generated by MOPatch on $1.

It contains the collected README files of the following patches:
$documented
`rdmdocflags`

The file $2, which was
generated in the same directory as this file, contains the
stripped README files of these patches, which should be more
readable.

Please read this information carefully before installing the
patches.

EOF
  fi
  return 0
}

rdmfooter()
{
  return 0
}

rdmsheader()
{
  if $MOPATCH_TEST_MIGRATE; then
  cat << EOF 1>&1 || return 1

                     Stripped patch READMEs
                     ======================

This file was generated by MOPatch on $1.

It contains the collected and stripped README files of the
following patches:
$installed

The file $2, which was generated in
the same directory as this file, contains the complete,
unstripped README files of these patches.

This file was generated by stripping the standard, repeating
portion of the patch READMEs, leaving only the patch-specific
information.  For completeness the standard patch README was
included once immediately after this introduction.

Please read the standard patch README and the patch-specific
information carefully before installing the patches.

Please note that the heuristics that MOPatch uses to distinguish
the standard from the patch-specific information may fail in rare
circumstances.  If you find garbled or meaningless text in the
patch-specific information below, please refer to the unstripped
patch READMEs in file $2 for the
full information.

EOF
  else
  cat << EOF 1>&1 || return 1

                     Stripped patch READMEs
                     ======================

This file was generated by MOPatch on $1.

It contains the collected and stripped README files of the
following patches:
$documented
`rdmdocflags`

The file $2, which was generated in
the same directory as this file, contains the complete,
unstripped README files of these patches.

This file was generated by stripping the standard, repeating
portion of the patch READMEs, leaving only the patch-specific
information.  For completeness the standard patch README was
appended once at the very end of this README.

Please read the standard patch README and the patch-specific
information carefully before installing the patches.

EOF
  fi

if $MOPATCH_TEST_MIGRATE; then
  cat << 'EOF' 1>&1 || return 1
Standard patch README
=====================

WARNING: Failure to carefully read and understand these requirements may
result in your applying a patch that can cause  your Oracle Server to
malfunction, including interruption of service and/or loss of data.

If you do not meet all of the following requirements, please log an
iTAR, so that an Oracle Support Analyst may review your situation. The
Oracle analyst will help you determine if this patch is suitable for you
to apply to your system. We recommend that you avoid applying any
temporary patch unless directed by an Oracle Support Analyst who has
reviewed your system and determined that it is applicable.

Requirements:

  - You must have located this patch via a Bug Database entry
    and have the exact symptoms described in the bug entry.

  - Your system configuration (Oracle Server version and patch
    level, OS Version) must exactly match those in the bug
    database entry - You must have NO OTHER PATCHES installed on
    your Oracle Server since the latest patch set (or base release
    x.y.z if you have no patch sets installed).

  - [Oracle 9.2.0.2 & above] You must have Perl 5.00503 (or later)
    installed under the ORACLE_HOME, or elsewhere within the host
    environment. OPatch is no longer included in patches as of 9.2.0.2.
    Refer to the following link for details on Perl and OPatch:
    http://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=189489.1

  - [IBM AIX O/S & Java patches for Oracle 9.2]
    In order to apply java class updates to IBM AIX based systems using
    java_131, you must update your java if you are running a version prior
    to Service Refresh build date 20030630a. This is
    necessary to fix IBM Defect#60472.

    To identify which java build date you are on, enter the following
    command ;

    > $ORACLE_HOME/jdk/bin/java -fullversion
    ... example response ...
    java full version "J2RE 1.3.1 IBM AIX build ca131-20030630a"

    The string ends in the date format YYYYMMDD or YYYYMMDDa where 'a'
    indicates an updated release to the original build. You should always
    apply the latest AIX Java SDK 1.3.1  Service Update available from IBM.
    As a minimum, the above service refresh can be found under
    APAR IY47055. The signature for the updated JVM is ca131-20030630a.
    Information on the latest available fixes, as well as how to apply
    the APARs to your AIX systems, is available at the IBM Java site.

    If you are running AIX 5L, you can safely ignore any comment against
    the APAR that says (AIXV43 only). The APAR is applicable to
    both AIX 4.3 and AIX 5L.

    Once you have updated your java installation you need to copy these
    updated files to Oracle's copies in $ORACLE_HOME/jdk.
    As the Oracle owner, simply issue the following commands;

    > cd /usr/java131
    > cp -fpR * $ORACLE_HOME/jdk


If you do NOT meet these requirements, or are not certain that you meet
these requirements, please log an iTAR requesting assistance with this
patch and Support will make a determination about whether you should
apply this patch.

Patch Installation Instructions:
--------------------------------
To apply the patch, unzip the PSE container file:

  pNNNNNN_VVVVV_PPPPPP.zip
  % unzip NNNNNN.zip

Set your current directory to the directory where the patch
is located:

  % cd NNNNNN

On AIX platforms only for 10201 Patches :

Ensure environment variable OBJECT_MODE is set to 32_64
e.g.

  % setenv OBJECT_MODE 32_64
  or
  $ export OBJECT_MODE=32_64

Ensure that the directory containing the opatch script appears in
your $PATH; then enter the following command:

  % opatch apply

Patch Special Instructions:
---------------------------
Make sure all instances running under the ORACLE_HOME being patched
are cleanly shutdown before installing this patch. Also ensure that
the tool used to terminate the instance(s) has exited cleanly.

If the Oracle inventory is not setup correctly this utility will
fail. To check accessibility to the inventory you can use the
command

  % opatch lsinventory

If you have any problems installing this PSE or are not sure
about inventory setup please call Oracle support.

Patch Deinstallation Instructions:
----------------------------------
Use the following command:

  % cd NNNNNN
  % opatch rollback -id NNNNNN

========================================================================
The standard patch README ends here, patch-specific information follows.

EOF
fi
  return 0
}

rdmsfooter()
{
  if $MOPATCH_TEST_MIGRATE; then
    :
  else
  cat << 'EOF' 1>&1 || return 1
Standard patch README
=====================

Patch Preinstall Steps:
-----------------------
1. For non-recommended patches, you must have the exact symptoms described
   in the service request (SR).

2. Verify the OUI Inventory.

   OPatch needs access to a valid OUI inventory to apply patches.  Validate
   the OUI inventory with the following command:

     % opatch lsinventory

   If the command errors out, contact Oracle Support and work to validate
   and verify the inventory setup before proceeding.

3. Review and download the latest version of OPatch.

   Oracle recommends that all customers be on the latest version of OPatch.
   Please review the following metalink note and follow the instructions to
   update to the latest version if needed:

     https://metalink.oracle.com/metalink/plsql/ml2_documents.showNOT?p_id=224346.1

4. Confirm executables appear in your system PATH.

   The patching process will use the unzip and the opatch executables.
   After sourcing the ORACLE_HOME environment, confirm both of these exist
   before continuing:

     - "which opatch"
     - "which unzip"

   If either of these executables do not show up in the PATH, correct the
   problem before proceeding.

5. Create a location for storing the unzipped patch.  This location will be
   referred to later in the document as <PATCH_TOP>.

6. Unzip the patch zip file into the <PATCH_TOP>.

     unzip -d <PATCH_TOP> p<NNNNNN>_<VVVVV>_<PPPPPP>.zip

7. Shut down services running from the ORACLE_HOME.

   Before applying this patch, do a clean shut down of all services running
   from the ORACLE_HOME.

   (a) In a non-RAC environment or a RAC environment with shared
       ORACLE_HOME:

       Shut down all services that are running from this ORACLE_HOME.
       Confirm the return status from each shutdown command to verify the
       shutdown is successful and there are no errors.

   (b) In a RAC environment with distributed ORACLE_HOMEs:

       For each node in the RAC system, shut down the services for the
       specific machine that are running from the ORACLE_HOME.  OPatch will
       be used on each node in the RAC system one at a time.  If this patch
       is not rolling RAC installable treat it as a non-RAC environment and
       shut down all services.

8. Apply pre-requisite updates.

Patch Installation Steps:
-------------------------
1. Set your current directory to the directory where the patch is located.

     % cd <PATCH_TOP>/<NNNNNN>

2. Apply the patch.

   For an offline patch:
   ---------------------
   (a) In a non-RAC environment or a RAC environment with shared
       ORACLE_HOME:

       Use the following command to apply the patch to the ORACLE_HOME:

         % opatch apply

   (b) In a RAC environment with distributed ORACLE_HOMEs:

       1. Stop instances running on node 1.
       2. Run 'opatch apply <PATCH_TOP>/<NNNNNN>' on node 1.
       3. When the apply finishes, opatch asks for confirmation before
          going on to apply the patch to node 2.
       4. Start instances on node 1.
       5. Stop instances running on node 2.
       6. Ask opatch to continue to applying the patch to node 2.
       7. Start instances on node 2.

   For an online patch:
   --------------------
   (a) In a non-RAC environment or a RAC environment with shared
       ORACLE_HOME:

       Use the following command to apply the patch to the ORACLE_HOME:

       % opatch apply online -connectString <SID>:<USERNAME>:<PASSWORD>:

   (b) In a RAC environment with distributed ORACLE_HOMEs:

       1. Stop instances running on node 1.
       2. Run 'opatch apply online -connectString <SID>:<USERNAME>:<PASSWORD>:<NODE1>,<SID2>:<USERNAME>:<PASSWORD>:<NODE2> <PATCH_TOP>/8898589 ' on node 1.
       3. When the apply finishes, opatch asks for confirmation before
          going on to apply the patch to node 2.

   When OPatch starts, it will validate the patch and make sure there are
   no conflicts with the software already installed in the ORACLE_HOME.
   OPatch categorizes two types of conflicts:

   (a) Conflicts with a patch already applied to the ORACLE_HOME:
       In this case, please stop the patch installation and contact Oracle
       Support Services.

   (b) Conflicts with subset patch already applied to the ORACLE_HOME:
       In this case, please continue the install, as the new patch contains
       all the fixes from the existing patch in the ORACLE_HOME.  The
       subset patch will automatically be rolled back prior to the
       installation of the new patch.

Patch Deinstallation Instructions:
----------------------------------
1. Make sure to follow the same pre-install steps when deinstalling a
   patch.  This includes verifying the inventory and shutting down any
   services running from the ORACLE_HOME / machine before rolling the patch
   back.

2. Change to the directory where the patch was unzipped.

     % cd <PATCH_TOP>/<NNNNNN>

3. Run OPatch to deinstall the patch.

   For an offline patch:
   ---------------------
   (a) In a non-RAC environment or a RAC environment with shared
       ORACLE_HOME:

       Use the following command to rollback the patch from the
       ORACLE_HOME:

         % opatch rollback -id <NNNNNN>

   (b) In a RAC environment with distributed ORACLE_HOMEs:

       1. Stop instances running on node 1.
       2. Run 'opatch rollback -id <NNNNNN> <PATCH_TOP>/<NNNNNN>' on node 1.
       3. When the rollback finishes, opatch asks for confirmation before
          going on to rollback the patch to node 2.
       4. Start instances on node 1.
       5. Stop instances running on node 2.
       6. Ask opatch to continue to rollbacking the patch to node 2.
       7. Start instances on node 2.

    For an online patch:
    --------------------
   (a) In a non-RAC environment:

       Use the following command to rollback the patch from the
       ORACLE_HOME:

         % opatch rollback -id <NNNNNN>  online -connectString <SID>:<USERNAME>:<PASSWORD>:

   (b) In a RAC environment:

       1. Stop instances running on node 1.
       2. Run 'opatch rollback -id <NNNNNN>  online -connectString <SID>:<USERNAME>:<PASSWORD>:<NODE1>,<SID2>:<USERNAME>:<PASSWORD>:<NODE2> <PATCH_TOP>/<NNNNNN>' on node 1.
       3. When the rollback finishes, opatch asks for confirmation before
          going on to rollback the patch to node 2.
EOF
  fi
  return 0
}


##########
#
# runmodep
#
##########

# run mode.  One of "apply" or "doc".
runmode=""

# returns whether MOPatch is running in one of the specified modes.
#
# Implements: macro
#
# Variables:
#   $runmode
#
# Parameters:
#   runmode [runmode ...]
runmodep()
{
  for l_2_0_mode in "$@"
  do
    if test "X$l_2_0_mode" = "X$runmode"; then
      return 0
    fi
  done
  return 1
}


################################
#
# constants and global variables
#
################################

# let OPatch and other tools write their messages in English.
# This is really essential since we grep for certain (english)
# keywords in the output generated by OPatch.
LANG=C; export LANG

# PD (some platforms): fix sort order
LC_COLLATE=C; export LC_COLLATE

# OPVD (newer versions)
EMDROOT=1; export EMDROOT

# keep test result filter in sync with the usage string
REVISION='$Revision: 5.1.2.16 $'
CRNOTICE="Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved."
HEADER="MOPatch - Install Multiple Oracle Patches in One Run - 2.1.11."
USAGE1="Usage: mopatch [-hvdin] [-c cleanup-freq] [-f conflict-rules]"
USAGE2="               [-o opatch-param] [-j jre] [-p inv-ptr-loc]"
USAGE3="               [-m make-utility] [-z unzip-utility] [-C creds]"
USAGE4="               [-s patch-source-path] [patch-base]"
USAGE="$USAGE1$NL$USAGE2$NL$USAGE3$NL$USAGE4"

DATE=`date`
STIMESTAMP=`date "+%Y%m%d%H%M%S"`
LTIMESTAMP=`date "+%Y_%m_%d-%H-%M-%S"`

# regexps to determine disk free space from df output
DFRE='^.*[ 	][ 	]*[0-9][0-9]*[ 	][ 	]*[0-9][0-9]*[ 	][ 	]*[0-9][0-9]*[ 	][ 	]*[0-9][0-9]*%[ 	][ 	]*.*$'
DFSE='^.*[ 	][ 	]*[0-9][0-9]*[ 	][ 	]*[0-9][0-9]*[ 	][ 	]*\([0-9][0-9]*\)[ 	][ 	]*[0-9][0-9]*%[ 	][ 	]*\(.*\)$'

# regepxs to extract information from lsinventory output.  PD
# (sunos_*): "sed" does not properly handle quantifiers on
# sub-expressions, so do not try to combine "PATCHINVSE" and
# "PSUINVSE" into one regular expression.
PATCHINVEE='^Patch(set[ 	][ 	]*Update|[ 	][ 	]*\(online\))?[ 	][ 	]*[0-9][0-9]*[ 	]*:'
PATCHINVSE='^Patch[ 	][ 	]*\([0-9][0-9]*\)[ 	]*:.*$'
PSUINVSE='^Patchset[ 	][ 	]*Update[ 	][ 	]*\([0-9][0-9]*\)[ 	]*:.*$'
PTCHONLNINVSE='^Patch[ 	][ 	]*(online)[ 	][ 	]*\([0-9][0-9]*\)[ 	]*:.*$'

# printf format specifier and regexp for link script maintenance
LINKSCRIPTGENFS='( cd "%s/%s" && cat "%s" 1>/dev/null && "%s" -f "%s" %s ORACLE_HOME="%s" ) || exit $?\n'
LINKSCRIPTVERFS='^( cd "%s/.*" && cat ".*" 1>/dev/null && ".*" -f ".*" .* ORACLE_HOME="%s" ) || exit \\$?$\n'

# set to one of "true" or "false".  Remove this variable and all
# references to it after migrating the test results.  Also remove
# it as a built-in from the syntax checker. (incomplete)
MOPATCH_TEST_MIGRATE=${MOPATCH_TEST_MIGRATE-false}
export MOPATCH_TEST_MIGRATE

# warnings to be ignored.  Newline-list of either 5-digit OUI
# error codes or (un-anchored) regexps matching the first line of
# the warning to be ignored.
wtbis=""

# make targets to be ignored.  Blank-list of make targets to be
# ignored.
#
# The targets "igenlibociicus" and "igenlibociei" may trigger
# C-compiler invocation.  The targets ipc_* may modify customer
# settings.
mttbis="igenlibociicus igenlibociei ipc_none ipc_udp ipc_g ipc_rds ipc_relink "

# temp file name
tfn=""
if test "X$RANDOM" = "X$RANDOM"; then
  tfn="mopatch-$STIMESTAMP-$$"
else
  tfn="mopatch-$RANDOM-$$"
fi

# option and parameter variables
dryrun=0
ilink=0
nomake=0
flags=""                                        # most come early
cufr=0                                          # clean-up frequency
crrules=""
opatchopts="-silent"
jre=""
invptrloc=""
make=""
unzip=""
onlnmode=0
patchsrcpath=""
pspspp=0                                        # patch source path specified
timeout=20
xlogfile=""
reserve=0
patchdir=""
uspltf=""                                       # user-specified platform

opatchveropts=""
opatchlsiopts="-retry 0"
opatchcuopts="-silent -force"

# flags.  Most of them are relevant for development and testing
# only.  Use only when you know what you are doing.
#
# local_opatch
#   add the "-local" option to all OPatch calls
# 5117016_check
#   PD, DBVD (sunos_sun4u, 10.2.0.2.0): check for mandatory patch
#   5117016
# sol10_ar_check
#   PD (sunos_* 5.10): check for ar problems
# ignore_installed
#   ignore installed patches in documentation mode
# ignore_warnings
#   ignore warnings known to be uncritical
# ignore_makes
#   ignore make targets known to be critical
# ignore_ubps
#   ignore certain unbundled patches
# free_space_check
#   check for sufficient patch storage free space
# free_space_log
#   write patch storage free space to log file
# readme_strip_log
#   write information on readme stripping to log file
# apply_napply
#   apply n-apply patches
# apply_cpu
#   apply critical patch updates
# apply_psu
#   apply patchset updates
# stress_test
#   ensure deterministic operation for stress tests
# clean_up
#   call "opatch util cleanup" when required
# clean_up_final
#   call "opatch util cleanup" after complete installation
# clean_up_tmpdir
#   clean up temporary directory after execution
flag_local_opatch=1
flag_5117016_check=1
flag_sol10_ar_check=1
flag_ignore_installed=0
flag_ignore_warnings=1
flag_ignore_makes=1
flag_ignore_ubps=1
flag_free_space_check=1
flag_free_space_log=0
flag_readme_strip_log=0
flag_apply_napply=1
flag_apply_cpu=1
flag_apply_psu=1
flag_stress_test=0
flag_clean_up=1
flag_clean_up_final=1
flag_clean_up_tmpdir=1


#########################################
#
# parse and verify options and parameters
#
#########################################

# keep for later
oargv=""
if test $# = 0; then
  oargv=$0
else
  oargv="$0 $*"
fi

# verify and read rc file
# begin clitest (4.189)
mopatchrcfile=""
if test "X$ORACLE_HOME" != "X" &&
   test -f "$ORACLE_HOME/.mopatchrc"; then
  if cat "$ORACLE_HOME/.mopatchrc" 1>/dev/null 2>&1; then
    :
  else
    usage "Invalid MOPatch rc file \"$ORACLE_HOME/.mopatchrc\" specified."
  fi
  mopatchrcfile=`cat "$ORACLE_HOME/.mopatchrc" |
                 grep -v '^[ 	]*$' |
                 grep -v '^[ 	]*#'`
  # eliminate newlines
  mopatchrcfile=`l_oldifs=$IFS; IFS="$NL"; set -f; set x $mopatchrcfile; shift; set +f; IFS=$l_oldifs; printf '%s\n' "$*"`
fi

# verify and read rc variable
mopatchrcenv=""
if test "X$MOPATCHRC" != "X"; then
  if mlp "$MOPATCHRC"; then
    usage "Invalid MOPatch rc variable \"MOPATCHRC\" specified (contains newline characters)."
  fi
  mopatchrcenv=$MOPATCHRC
fi

# verify commandline
for parameter in ${1+"$@"}
do
  if mlp "$parameter"; then
    usage "Invalid parameter \"$parameter\" specified (contains newline characters)."
  fi
done

# include rc file and variable into commandline
set x $mopatchrcfile $mopatchrcenv ${1+"$@"}
shift

while getopts :vdinlF:c:f:o:j:p:m:z:C:s:T:L:R:P: c; do
  case $c in
    v) verbosep=1 ;;
    d) dryrun=1 ;;
    i) ilink=1 ;;
    n) nomake=1 ;;
    l) : ;;                                     # keep for backward compatibility
    F) flags="$flags $OPTARG" ;;
    c) cufr=$OPTARG ;;
    f)
      # note that even with an empty option argument this
      # variable gets non-empty!
      crrules="$crrules $OPTARG" ;;
    o)
      # verify this here since later we loose all information on
      # containing whitespace
      if mwp "$OPTARG"; then
        usage "Invalid OPatch option \"$OPTARG\" specified (contains whitespace)."
      fi
      case $OPTARG in
        -oh|-jre|-jdk|-invPtrLoc)
          usage "Invalid OPatch option \"$OPTARG\" specified (conflicts with MOPatch)." ;;
      esac
      opatchopts="$opatchopts $OPTARG" ;;
    j) jre=$OPTARG ;;
    p) invptrloc=$OPTARG ;;
    m) make=$OPTARG ;;
    z) unzip=$OPTARG ;;
    C)
      onlnmode=1
      opatchopts="$opatchopts -connectString $OPTARG" ;;
    s)
      if test $pspspp = 0; then
        patchsrcpath=$OPTARG
        pspspp=1
      else
        patchsrcpath="$patchsrcpath:$OPTARG"
      fi ;;
    T) timeout=$OPTARG ;;
    L) xlogfile=$OPTARG ;;
    R) reserve=$OPTARG ;;
    P) uspltf=$OPTARG ;;
    *) cat << EOF 1>&2
Invalid or incomplete option specified.
$USAGE

MOPatch uses the patch source path to look up the packed patches to be
installed.

MOPatch uses the patch base directory patch-base as installation stage and
working directory.  If not specified, it defaults to the current directory.

Options:
  -h    show a short command-line overview.  See readme.txt under
        \$ORACLE_HOME/MOPatch or SAP note 1027012 for complete
        documentation.
  -v    be verbose; produce more diagnostic output
  -d    extract patch documentation
  -i    link immediately; do not defer until complete patch installation
  -n    do not link after complete patch installation; create link script
        only.  Ignored with option -i.

  -c cleanup-freq
        clean up the internal OPatch patch storage after applying the
        specified number of patches.  If zero, clean up only if required.
        Defaults to zero.  Patch storage clean-up is available only with
        sufficiently recent OPatch versions.

  -f conflict-rules
        resolve conflicts according to the specified rules.  Available
        rules are "old" (keep installed patch in case of conflicts), "new"
        (apply new patch), "cpu" (favor CPU or CPU molecule if it conflicts
        with a non-CPU patch), "psu" (favor PSU if it conflicts with a
        non-PSU patch), or an integer patch ID (keep or apply that patch if
        it conflicts with another patch).  The rules must be specified as a
        comma-separated list.  They default to "psu,new".

  -o opatch-param
        pass the specified parameter verbatim to OPatch.  To pass an option
        like "-delay 10" to OPatch, specify two options to MOPatch: "-o
        -delay -o 10".  There must be no whitespace inside the parameter.
        There must be no "-oh", "-jre", "-jdk", or "-invPtrLoc" OPatch
        option among the specified parameters.

  -j jre
        pass the specified Java Runtime Environment to all OPatch calls

  -p inv-ptr-loc
        pass the specified inventory pointer location to all OPatch calls.
        There must be no whitespace inside inv-ptr-loc.  Defaults to
        \$ORACLE_HOME/oraInst.loc if that file exists and if it points to
        a valid inventory.

  -m make-utility
        use the specified make utility to build the Oracle executable and
        shared libraries after applying patches.  Ignored with option -i.
        Defaults to "/usr/ccs/bin/make" or "/usr/bin/make", if one of those
        exists, or otherwise to the first executable named "make" that is
        found in PATH.

  -z unzip-utility
        use the specified unzip utility to unpack patches.  Defaults to
        \$ORACLE_HOME/bin/unzip, if that exists, or otherwise to the first
        executable named "unzip" that is found in PATH.

  -s patch-source-path

        use the specified path to look up the patches to be installed.
        May be specified more than once.  Defaults to the patch base
        directory.  The patch source path must be a colon-separated list
        of packed patch files or directories containing packed patch files.

This utility requires environment variable ORACLE_HOME to be set.

$HEADER
$CRNOTICE
EOF
       exit 2 ;;
  esac
done

# PD (osf1_alpha): check for non-Posix "/bin/sh"
if test "X${OPTIND-}" = "X"; then
  error "Cannot use non-Posix /bin/sh. On Tru64, use /usr/bin/posix/sh."
fi

# shift <n> may not be portable
i=$OPTIND
while test $i -gt 1; do
  i=`expr $i - 1`
  shift
done

# determine run mode
runmode=""
if test $dryrun = 0; then
  runmode="apply"
else
  runmode="doc"
fi

# verify and set flags
oldifs=$IFS
IFS=", $TAB$NL"
for flag in $flags
do
  # generously ignore empty flags which may have been introduced
  # by strange word splitting rules
  if test "X$flag" = "X"; then
    continue
  fi

  # word splitting eliminated newlines
  flagvalue=""
  if expr "X$flag" : 'X!.*$' 1>/dev/null; then
    flag=`expr "X$flag" : 'X!\(.*\)$'`
    flagvalue=0
  else
    flagvalue=1
  fi

  # verify and set flag
  if varnamep "$flag" &&
     flagvar="flag_$flag" &&
     eval test '"X${'"$flagvar"'+X}"' = '"XX"'; then
    eval "$flagvar"='$flagvalue'
  else
    usage "Invalid flag \"$flag\" specified."
  fi
done
IFS=$oldifs

if runmodep "apply" &&
   test $flag_ignore_installed = 1; then
  usage "Invalid flag \"ignore_installed\" in apply mode specified."
fi

# verify clean-up frequency
if numberp "$cufr"; then
  :
else
  usage "Invalid clean-up frequency \"$cufr\" specified."
fi
# end clitest (4.189)

# determine and verify conflict resolution rules
if test "X$crrules" = "X"; then
  crrules="psu new"
fi
oldifs=$IFS
IFS=", $TAB$NL"
for crrule in $crrules
do
  case $crrule in
    # generously ignore empty splitting rules
    "") : ;;
    old|new|psu|cpu) : ;;
    *)
      if numberp "$crrule"; then
        :
      else
        usage "Invalid conflict resolution rule \"$crrule\" specified."
      fi ;;
  esac
done
IFS=$oldifs

# calculate and verify OPatch options
if test $verbosep = 1; then
  opatchopts="$opatchopts -verbose"
fi
if test $ilink = 0; then
  opatchopts="$opatchopts -no_relink"
fi
if test $flag_local_opatch = 1; then
  # OPVD (>= 10.2.0.2.4): do not add option "-local" to
  # opatchlsiopts, since "opatch lsinventory" rejects it
  opatchopts="$opatchopts -local"
  opatchcuopts="$opatchcuopts -local"
fi
if test "X$jre" != "X"; then
  if mwp "$jre"; then
    usage "Invalid Java Runtime Environment \"$jre\" specified (contains whitespace)."
  fi
  if test -d "$jre"; then
    :
  else
    usage "Invalid Java Runtime Environment \"$jre\" specified (no such directory)."
  fi
  if test -f "$jre/bin/java"; then
    :
  else
    usage "Invalid Java Runtime Environment \"$jre\" specified (no \"java\" executable)."
  fi
  opatchopts="$opatchopts -jre $jre"
  opatchveropts="$opatchveropts -jre $jre"
  opatchlsiopts="$opatchlsiopts -jre $jre"
  opatchcuopts="$opatchcuopts -jre $jre"
fi
if test "X$invptrloc" != "X"; then
  if mwp "$invptrloc"; then
    usage "Invalid inventory pointer location \"$invptrloc\" specified (contains whitespace)."
  fi
  opatchopts="$opatchopts -invPtrLoc $invptrloc"
  opatchlsiopts="$opatchlsiopts -invPtrLoc $invptrloc"
  opatchcuopts="$opatchcuopts -invPtrLoc $invptrloc"
elif test "X$ORACLE_HOME" != "X" &&
     swp "$ORACLE_HOME" &&
     test -f "$ORACLE_HOME/oraInst.loc"; then
  # check whether $ORACLE_HOME/oraInst.loc is a valid inventory
  # pointer.  Use it, if this is the case.  Otherwise, silently
  # do nothing.
  invptrloc="$ORACLE_HOME/oraInst.loc"
  inv=`grep '^[ 	]*inventory_loc[ 	]*=[ 	]*/.*$' "$invptrloc" 2>/dev/null |
       sed 's/^[ 	]*inventory_loc[ 	]*=[ 	]*//' 2>/dev/null`
  if test "X$inv" != "X" &&
     test -d "$inv" &&
     test -f "$inv/ContentsXML/inventory.xml"; then
    invxml="$inv/ContentsXML/inventory.xml"
    ohre=`printf '%s\n' "$ORACLE_HOME" | quotemeta -x 2>/dev/null`
    # this regexp could be more relaxed w.r.t. whitespace, but
    # better keep it readable than perfect
    if egrep '<HOME NAME="[^"][^"]*" LOC="'"$ohre"'" TYPE="O" IDX="[0-9][0-9]*"/?>' "$invxml" 1>/dev/null 2>&1; then
      opatchopts="$opatchopts -invPtrLoc $invptrloc"
      opatchlsiopts="$opatchlsiopts -invPtrLoc $invptrloc"
      opatchcuopts="$opatchcuopts -invPtrLoc $invptrloc"
    fi
  fi
fi

if test $ilink = 0; then
  # determine and verify make utility
  if test "X$make" = "X"; then
    if test -x "/usr/ccs/bin/make"; then
      make="/usr/ccs/bin/make"
    elif test -x "/usr/bin/make"; then
      make="/usr/bin/make"
    else
      oldifs=$IFS
      IFS=:
      for path in $PATH
      do
        if test -x "$path/make"; then
          make="$path/make"
          break
        fi
      done
      IFS=$oldifs
    fi
  fi

  if test "X$make" = "X"; then
    usage "No make utility found or specified."
  elif test ! -x "$make"; then
    usage "Invalid make utility \"$make\" found or specified."
  fi
fi

# determine and verify unzip utility.  Prefer unzip from Oracle
# Home.
if test "X$unzip" = "X" &&
   test "X$ORACLE_HOME" != "X" &&
   test -x "$ORACLE_HOME/bin/unzip"; then
  unzip="$ORACLE_HOME/bin/unzip"
fi

if test "X$unzip" = "X"; then
  oldifs=$IFS
  IFS=:
  for path in $PATH
  do
    if test -x "$path/unzip"; then
      unzip="$path/unzip"
      break
    fi
  done
  IFS=$oldifs
fi

if test "X$unzip" = "X"; then
  usage "No unzip utility found or specified."
elif "$unzip" 1>/dev/null; then
  :
else
  usage "Invalid unzip utility \"$unzip\" found or specified."
fi

# verify "df" loop timeout and file system reserve
if numberp "$timeout" &&
   test $timeout -gt 0; then
  :
else
  usage "Invalid df loop timeout \"$timeout\" specified."
fi
if numberp "$reserve"; then
  :
else
  usage "Invalid file system reserve \"$reserve\" specified."
fi

# verify user-specified platform
if test "X$uspltf" = "X" ||
   platforminfof "$uspltf" 1>/dev/null 2>&1 3>&1; then
  :
else
  usage "Invalid platform ID \"$uspltf\" specified."
fi

# determine and verify patch base directory
patchdir=""
if test $# -gt 1; then
  shift
  usage "Extra parameters specified: $*"
elif test $# -eq 1; then
  patchdir=$1
else
  patchdir=.
fi

if test "X$patchdir" != "X" &&
   test -d "$patchdir" &&
   touch "$patchdir/$tfn" &&
   rm -f "$patchdir/$tfn" &&
   ( cd "$patchdir" && ls 1>/dev/null ); then
  :
else
  usage "Invalid or inaccessible patch base directory \"$patchdir\" specified."
fi

# default patch source directory to patch base directory.
# Variable "$patchsrctype" is used in messages to identify origin
# of problems.  Thanks to the above tests, errors with respect to
# the patch base directory may occur only in the "test -d" branch
# in the below "for" loop.
patchsrctype=""
if test $pspspp = 0; then
  patchsrcpath=$patchdir
  patchsrctype="patch base directory"
else
  patchsrctype="patch source directory"
fi

# verify patch source (or base) directory.  Note that
# "$patchsrcpath" is not necessarily a colon-list, so be careful
# when splitting.  As a side-effect of the below verification,
# all items of the source path are guaranteed to be newline-free.
l_oldifs=$IFS; IFS=":"; set -f; set x $patchsrcpath; shift; set +f; IFS=$l_oldifs
if test $# = 0; then
  usage "Invalid patch source path \"$patchsrcpath\" specified (contains empty items)."
fi

# determine wether the patch source path is a singleton.  Needed
# to calculate the patch basenames later.
pspstp=""
if test $# = 1; then
  pspstp=1
else
  pspstp=0
fi

ppfp=0                                          # packed patches found
uppfp=0                                         # unpacked patches found
for item in "$@"
do
  if test "X$item" = "X"; then
    usage "Invalid patch source path \"$patchsrcpath\" specified (contains empty items)."
  elif test -f "$item"; then
    if pmddirp "$item"; then
      :
    else
      usage "Invalid patch source file \"$item\" specified (contains strange characters)."
    fi
    if cat "$item" 1>/dev/null; then
      :
    else
      usage "Invalid or inaccessible patch source file \"$item\" specified."
    fi
    # assume any user-specified file to be a packed patch
    ppfp=1
  elif test -d "$item"; then
    if pmddirp "$item"; then
      :
    else
      usage "Invalid $patchsrctype \"$item\" specified (contains strange characters)."
    fi
    if ( cd "$item" && ls 1>/dev/null ); then
      :
    else
      usage "Invalid or inaccessible $patchsrctype \"$item\" specified."
    fi
    # heuristically check whether an unpacked patch was specified
    # as patch source directory
    if test $pspspp = 1 &&
       # the former should find one-offs, the latter CPUs and
       # other n-apply patches
       globtest -f "$item/etc/config/inventory"         \
                   "$item/etc/config/inventory.xml"     \
                   "$item/etc/config/actions "          \
                   "$item/etc/config/actions.xml"       \
                   "$item/patchmd.xml"; then
      usage "Invalid patch source directory \"$item\" specified (contains an unpacked patch)."
    fi
    # heuristically check for the existence of unpacked patches
    if globtest -f "$item"/[0-9]*[0-9]/etc/config/inventory                     \
                   "$item"/[0-9]*[0-9]/etc/config/inventory.xml                 \
                   "$item"/[0-9]*[0-9]/etc/config/actions                       \
                   "$item"/[0-9]*[0-9]/etc/config/actions.xml                   \
                   "$item"/[0-9]*[0-9]/[0-9]*[0-9]/etc/config/inventory         \
                   "$item"/[0-9]*[0-9]/[0-9]*[0-9]/etc/config/inventory.xml     \
                   "$item"/[0-9]*[0-9]/[0-9]*[0-9]/etc/config/actions           \
                   "$item"/[0-9]*[0-9]/[0-9]*[0-9]/etc/config/actions.xml; then
      uppfp=1
    fi
    # find and verify all packed patches
    for patchsrc in \
      "$item"/[pP][0-9][0-9][0-9][0-9][0-9][0-9]*.[zZ][iI][pP] \
      "$item"/SAP_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP] \
      "$item"/SGR_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP] \
      "$item"/SXD_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]
    do
      if test "X$patchsrc" != "X$item/[pP][0-9][0-9][0-9][0-9][0-9][0-9]*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/SAP_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/SGR_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/SXD_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]"; then
        ppfp=1
        if pmddirp "$patchsrc"; then
          :
        else
          usage "Invalid $patchsrctype \"$item\" specified (patch \"$patchsrc\" contains strange characters)."
        fi
      fi
    done
  else
    usage "Invalid patch source path item \"$item\" specified (neither file nor directory)."
  fi
done

# check for packed patches
if test $pspspp = 0; then
  patchsrctype="patch base directory"
else
  patchsrctype="patch source path"
fi
if test $ppfp = 0 && test $uppfp = 1; then
  usage "Invalid $patchsrctype \"$patchsrcpath\" specified (contains unpacked patches only)."
elif test $ppfp = 0; then
  usage "Invalid $patchsrctype \"$patchsrcpath\" specified (does not contain any packed patches)."
fi

# verify Oracle Home
if test "X$ORACLE_HOME" = "X"; then
  usage "No Oracle Home specified."
fi
opatch="$ORACLE_HOME/OPatch/opatch"
if test ! -d "$ORACLE_HOME"; then
  usage "Invalid Oracle Home \"$ORACLE_HOME\" specified (not a directory)."
elif test ! -d "$ORACLE_HOME/OPatch" ||
     test ! -d "$ORACLE_HOME/OPatch/jlib" ||
     test ! -x "$opatch"; then
  usage "Invalid Oracle Home \"$ORACLE_HOME\" specified (no appropriate OPatch found)."
elif touch "$ORACLE_HOME/$tfn" &&
     rm -f "$ORACLE_HOME/$tfn"; then
  :
else
  usage "Invalid Oracle Home \"$ORACLE_HOME\" specified (unwritable)."
fi
case $ORACLE_HOME in
  */) usage "Invalid Oracle Home \"$ORACLE_HOME\" specified (trailing slash)." ;;
esac


#############################################
#
# miscellaneous validation and initialization
#
#############################################

# clean-up trap.  Gets successively extended as there is more to
# clean up.
cutrap=":"

# create PID file
pidfile="$ORACLE_HOME/.mopatchpid"
if test -f "$pidfile"; then
  error "Cannot start MOPatch (already running according to PID file \"$pidfile\")."
elif echo $$ 1>"$pidfile"; then
  cutrap="$cutrap;"'rm -f "$pidfile"'
  trap "$cutrap" 0
else
  error "Cannot create PID file \"$pidfile\"."
fi

# seems that the clean-up trap does not get called at signals
# without the following
trap 'exit 2' 1 2 3 15

# prepare for handling user interrupts
signalled=0
trap 'signalled=1; cmdlineerror=1' USR1

# save TTY state and disable ^C as interrupt if in apply mode.
# Do not be too picky if the "stty" calls fail.
if runmodep "apply"; then
  ttys=`stty -g 2>/dev/null`
  stty "intr" "^-" 1>/dev/null 2>&1
  cutrap="$cutrap;"'test "X$ttys" != "X" && stty "$ttys"'
  trap "$cutrap" 0
fi

# determine and create patch storage test file used for calls to
# "df".  Be careful here since the patch storage may not exist
# yet and since there may be all kinds of symbolic links and
# stuff.  Customers annoyed by the growing patch storage may have
# moved it out of the volume holding the Oracle Home.
pstf=""
if test -d "$ORACLE_HOME/.patch_storage"; then
  pstf="$ORACLE_HOME/.patch_storage/$tfn-pstf"
else
  pstf="$ORACLE_HOME/$tfn-pstf"
fi
if touch "$pstf"; then
  cutrap="$cutrap;"'rm -f "$pstf"'
  trap "$cutrap" 0
else
  error "Cannot create patch storage test file \"$pstf\"."
fi

# test for non-empty link script
linkscript="$patchdir/link.sh"
lsne=""                                         # link script non-empty
if test -s "$linkscript"; then
  lsne=1
else
  lsne=0
fi
# verify existing link script
if test $ilink = 0 &&
   test $lsne = 1; then
  ohre=`printf '%s\n' "$ORACLE_HOME" | quotemeta`
  linkre=`printf "$LINKSCRIPTVERFS" "$ohre" "$ohre"`
  if grep -v "$linkre" "$linkscript"; then
    error "Cannot verify link script \"$linkscript\" (probably different Oracle Home)."
  fi
fi
# open link script for appending, if required.  Open it even in
# documentation mode, but do not change it at all.
if test $ilink = 0; then
  if ( exec 4>>"$linkscript" && exec 4>&- ) && exec 4>>"$linkscript"; then
    :
  else
    error "Cannot open link script \"$linkscript\" for appending."
  fi
fi
cutrap="$cutrap;"'test -s "$linkscript" || rm -f "$linkscript"'
trap "$cutrap" 0

# open the readme collection and the stripped readme collection
# in documentation mode
readmecoll="$patchdir/READMES-$LTIMESTAMP.txt"
readmescoll="$patchdir/READMES-STRIPPED-$LTIMESTAMP.txt"
if runmodep "doc"; then
  if ( exec 8>"$readmecoll" && exec 8>&- ) && exec 8>"$readmecoll"; then
    :
  else
    error "Cannot open readme collection \"$readmecoll\" for writing."
  fi
  cutrap="$cutrap;"'test -s "$readmecoll" || rm -f "$readmecoll"'
  trap "$cutrap" 0
  if ( exec 9>"$readmescoll" && exec 9>&- ) && exec 9>"$readmescoll"; then
    :
  else
    error "Cannot open stripped readme collection \"$readmescoll\" for writing."
  fi
  cutrap="$cutrap;"'test -s "$readmescoll" || rm -f "$readmescoll"'
  trap "$cutrap" 0
fi

# create a temporary directory
tmpdir=""
{
  tmpdir=`( umask 077 && mktemp -d "$patchdir/mopatch-XXXXXX" ) 2>/dev/null` &&
  test "X$tmpdir" != "X" &&
  test -d "$tmpdir"
} ||
{
  tmpdir="$patchdir/$tfn-tmpdir"
  ( umask 077 && mkdir "$tmpdir" )
} ||
{
  error "Cannot create temporary directory \"$tmpdir\"."
}
cutrap="$cutrap;"'test $flag_clean_up_tmpdir = 1 && rm -rf "$tmpdir"'
trap "$cutrap" 0

# create patch staging directory
patchstage="$tmpdir/stage"
if mkdir "$patchstage"; then
  :
else
  error "Cannot create patch staging directory \"$patchstage\"."
fi

# the following writes information to the log file.  Add an empty
# line to set it off from our header. (incomplete)
log 3>/dev/null

# determine platform information (incomplete)
platforminfo=""
if test "X$uspltf" = "X"; then
  if platforminfo=`platforminfof 3>/dev/null`; then
    :
  else
    error "Cannot determine platform information."
  fi
else
  platforminfo=`platforminfof "$uspltf" 3>/dev/null`
fi
l_oldifs=$IFS; IFS=" "; set -f; set x $platforminfo; shift; set +f; IFS=$l_oldifs
platform=$1
kernelver=$4

# determine Oracle RDBMS version (incomplete)
if rdbmsversion=`rdbmsversionf 3>/dev/null`; then
  :
else
  error "Cannot determine Oracle RDBMS version."
fi
l_oldifs=$IFS; IFS=" "; set -f; set x $rdbmsversion; shift; set +f; IFS=$l_oldifs
rdbmsver=$1
rdbmsnver=$2
rdbmsmver=$3

# determine OPatch version and feature support (incomplete)
if opversion=`opversionf $opatchveropts 3>/dev/null`; then
  :
else
  error "Cannot determine OPatch version and feature support."
fi
l_oldifs=$IFS; IFS=" "; set -f; set x $opversion; shift; set +f; IFS=$l_oldifs
opver=$1
opnver=$2
eval "cusp=\${$OPVERSION_CUSP-}"
eval "xmlinvp=\${$OPVERSION_XMLINVP-}"
eval "psusp=\${$OPVERSION_PSUSP-}"

# OPVD (early versions): verify "correct" XML inventory support
if test $xmlinvp = 0; then
  error "Cannot run with OPatch version \"$opver\".  Please update."
fi

# determine Oracle Home type
ohtype=""
if test -d "$ORACLE_HOME/inventory/Components21/oracle.crs"; then
  ohtype="gi"
else
  ohtype="rdbms"
fi

# determine options to execute "df"
dfopts=""
if df -Pk "$pstf" 1>/dev/null 2>&1; then
  # require Posix-output if available ...
  dfopts="-Pk"
else
  # ... otherwise hope for something similar
  dfopts="-k"
fi

# determine patch storage free space.  Be even more careful here
# since "df" has a very different output format across different
# flavors of Unices.  If something goes wrong, do not error out
# but fall back to not taking into account patch storage free
# space at all.
#
# If flag psfsps flips to false (here or later), then
# - do not use the value of variables psfsp, ppsfsp, or psmp in
#   any way and
# - do not try to re-calculate psfsp or ppsfsp.
#
# Similarly for flag psrsps and variables lss and psrsp.
#
# Working with disk free space involves numbers that may be too
# large for the standard "expr" and "test" utilities.  We have to
# use "dc" for these calculations and comparisons.  For our own
# reference those "dc" relational operators that are portable:
#
#   nm>a    if m > n  or n < m  then a
#   nm<a    if m < n  or n > m  then a
psfsp=`df $dfopts "$pstf" |
       grep "$DFRE" |
       sed 's/'"$DFSE"'/\1/'`
ppsfsp=""                                       # previous patch storage free space
psmp=""                                         # patch storage mount point
psfsps=""                                       # patch storage free space is secure
if numberp "$psfsp"; then
  # take file system reserve into account
  if test $reserve != 0; then
    # d := max( 0, psfsp - reserve )
    d=`echo "0k[0]sa $psfsp $reserve-d0>apq" | dc`
    if numberp "$d"; then
      psfsp=$d
    else
      # this should not normally happen so it is OK to simply
      # error out here
      error "Cannot calculate \"$d\" as max( 0, $psfsp - $reserve )."
    fi
  fi

  ppsfsp=0
  psmp=`df $dfopts "$pstf" |
        grep "$DFRE" |
        sed 's/'"$DFSE"'/\2/'`
  psfsps=1
else
  logerr "Cannot determine patch storage free space from \"$psfsp\"."

  psfsp=0
  ppsfsp=0
  psmp="<not available>"
  psfsps=0
fi

# determine size of libserver.a
lss=`du -k "$ORACLE_HOME/lib/libserver${rdbmsmver}.a" |
     grep '^[0-9][0-9]*[ 	][ 	]*.*$' |
     sed 's/^\([0-9][0-9]*\)[ 	][ 	]*.*$/\1/'`
psrsp=""                                        # patch storage required space
psrsps=""                                       # patch storage required space is secure
if numberp "$lss"; then
  # double size of libserver.a to determine disk space required
  # in the patch storage for installation of one patch
  psrsp=`expr $lss + $lss`
  psrsps=1
else
  logerr "Cannot determine size of \"$ORACLE_HOME/lib/libserver${rdbmsmver}.a\" from \"$lss\"."

  lss=0
  psrsp=0
  psrsps=0
fi

# determine "df" loop count and sleep interval.  Give smart OSs a
# chance to react faster in "df" loops.
dfloop=""
dfslpi=""
if sleep 0.5 1>/dev/null 2>&1 &&
   test $flag_stress_test = 0; then
  dfloop=`expr $timeout + $timeout`
  dfslpi=0.5
else
  dfloop=$timeout
  dfslpi=1
fi

# determine non-traditional "awk" interpreter
awk=""
for exec in "gawk" "mawk" "nawk" "awk"
do
  if ( "$exec" -v foo=bar '{}' 0</dev/null ) 1>/dev/null 2>&1; then
    awk=$exec
    break;
  fi
done
if test "X$awk" = "X"; then
  error "Cannot find non-traditional \"awk\" interpreter."
fi


##################################################
#
# open log file and write some initial information
#
##################################################

logdir="$ORACLE_HOME/cfgtoollogs/mopatch"
if test -d "$logdir" ||
   mkdir "$logdir"; then
  :
else
  error "Cannot create log directory \"$logdir\"."
fi

if test "X$xlogfile" = "X"; then
  logfile="$logdir/mopatch-$LTIMESTAMP.log"
  if ( exec 3>"$logfile" && exec 3>&- ) && exec 3>"$logfile"; then
    :
  else
    error "Cannot open log file \"$logfile\" for writing."
  fi
else
  logfile=$xlogfile
fi
logopen=1
logerrhdl=3

# brush up log file name which from here is used only in messages
logfile=`echo "$logfile" |
         sed 's/^.*\/cfgtoollogs\//$ORACLE_HOME\/cfgtoollogs\//' 2>&3`

# brush up revision
revision=`printf '%s\n' "$REVISION" | sed 's/^\$Revisio[n]: //;s/ \$$//' 2>&3`

# write our header before doing anything else
out "$HEADER"
out "$CRNOTICE"

# write some diagnostic information
verbose
verbose "Version:       2.1.11"
verbose "Revision:      $revision"
verbose "Command-line:  $oargv"

verbose
verbose "Oracle Home:   $ORACLE_HOME"
verbose "RDBMS version: $rdbmsver"
verbose "OPatch version:$opver"
if test $cusp = 1; then
verbose "Clean-up:      supported"
else
verbose "Clean-up:      not supported"
fi
if $MOPATCH_TEST_MIGRATE; then
  :
else
if test $psusp = 1; then
verbose "PSUs:          supported"
else
verbose "PSUs:          not supported"
fi
fi
verbose "Log file:      $logfile"
verbose "Patch base:    $patchdir"
verbose "Patch source:  $patchsrcpath"
if test $ilink = 0; then
verbose "Link script:   $linkscript"
else
verbose "Link script:   <none>"
fi
if runmodep "doc"; then
verbose "Readmes:       $readmecoll"
verbose "Strpd. Readmes:$readmescoll"
else
verbose "Readmes:       <none>"
verbose "Strpd. Readmes:<none>"
fi
if test $ilink = 0; then
verbose "make utility:  $make"
else
verbose "make utility:  <ignored>"
fi
verbose "unzip utility: $unzip"
if test $verbosep = 0; then
rawout
rawout  "Log file: $logfile"
fi

verbose
verbose "User name:     $LOGNAME"
pwd=`pwd 2>&3`
verbose "Working dir:   $pwd"
uname=`uname -a 2>&3`
verbose "System:        $uname"
if test $psfsps = 1; then
verbose "Disk free:     $psfsp KBytes on $psmp"
else
verbose "Disk free:     <not available>"
fi
if test $psfsps = 1 &&
   test $psrsps = 1; then
verbose "Disk required: $psrsp KBytes on $psmp"
else
verbose "Disk required: <not available>"
fi

if test $ilink = 0 && test $lsne = 1; then
  verbose
  verbose "Re-using previous link script \"$linkscript\"."
fi


###########################################################
#
# get pre-run patch inventory and analyze installed patches
#
###########################################################

out
out "Getting pre-run patch inventory..."

preinvlog="$tmpdir/preinv.log"
preinvxml="$tmpdir/preinv.xml"
preinvawk="$tmpdir/preinv.awk"
preinvsh="$tmpdir/preinv.sh"
log "executing: \"$opatch\" lsinventory $opatchlsiopts -xml \"$preinvxml\""
if "$opatch" lsinventory $opatchlsiopts -xml "$preinvxml" 1>"$preinvlog" 2>&1; then
  cat "$preinvlog" 1>&3 2>&1

  out "Getting pre-run patch inventory...done."
else
  cat "$preinvlog" 1>&3 2>&1

  out "Getting pre-run patch inventory...failed."

  error -c opatcherror "Cannot get pre-run patch inventory."
fi

# newline-list of the CPU molecules listed in the rollback.lst
# files under $ORACLE_HOME/cpu.  We'll add that information to
# the inventory manually.
cpurlbs=""
if globtest -f "$ORACLE_HOME"/cpu/*/rollback_*.lst; then
  cpurlbs=`cat "$ORACLE_HOME"/cpu/*/rollback_*.lst 2>&3 |
           tr ',' '\012' 2>&3 |
           sed '/^[^0-9]*$/d;/[0-9][^0-9][0-9]/d;s/^[^0-9]*\([0-9][0-9]*\)[^0-9]*$/\1/' 2>&3 |
           sort 2>&3 |
           uniq 2>&3`
fi

if test $signalled = 1; then
  error -c cmdlineerror "Interrupted."
fi

if $MOPATCH_TEST_MIGRATE; then
  :
else
verbose
verbose "Analyzing installed patches..."
fi

# determine installed patches from log file
iplog=`egrep "$PATCHINVEE" "$preinvlog" 2>&3 |
       sed 's/'"$PATCHINVSE"'/\1/;s/'"$PSUINVSE"'/\1/;s/'"$PTCHONLNINVSE"'/\1/' 2>&3 |
       sort -n 2>&3`

# create awk script to augment inventory
agminvawk="$tmpdir/agminv.awk"
if ( exec 6>"$agminvawk" && exec 6>&- ) 1>&3 2>&1 && exec 6>"$agminvawk"; then
  :
else
  verbose "Analyzing installed patches...failed."

  error "Cannot open inventory augmentation awk script \"$agminvawk\" for writing."
fi
echo "{ print; }" 1>&6

l_oldifs=$IFS; IFS="$NL"; set -f; set x $iplog; shift; set +f; IFS=$l_oldifs
for patchid in ${1+"$@"}
do
  # newline-terminated augmentation data
  agmdata=""
  if memberp "$NL" $patchid "$cpurlbs"; then
    agmdata="$agmdata<mopatch_cpurlb>true</mopatch_cpurlb>$NL"
  fi

  # this is an ugly hack.  OPatch does not provide the complete
  # information in the XML generated by option "-xml", so we have
  # to look up that information in the "raw" inventory
  patchinvxml=""
  if test -f "$ORACLE_HOME/inventory/oneoffs/$patchid/etc/config/inventory.xml"; then
    patchinvxml="$ORACLE_HOME/inventory/oneoffs/$patchid/etc/config/inventory.xml"
  elif test -f "$ORACLE_HOME/inventory/oneoffs/$patchid/etc/config/inventory"; then
    patchinvxml="$ORACLE_HOME/inventory/oneoffs/$patchid/etc/config/inventory"
  else
    verbose "Analyzing installed patches...failed."

    error "Cannot find raw patch inventory file for patch $patchid."
  fi
  prqovls=`egrep '^[ 	]*<(prereq|overlay) oneoff_id="[0-9][0-9]*"/>[ 	]*$' "$patchinvxml" 2>&3`

  if test "X$prqovls" != "X"; then
    agmdata="$agmdata$prqovls$NL"
  fi

  if test "X$agmdata" != "X"; then
    printf '%s\n' '/^[ 	]*<reference_id number="'"$patchid"'"\/>[ 	]*$/ {' 1>&6
    printf '%s' "$agmdata" | sed -e 's/\([\\"]\)/\\\1/g;s/^.*$/  print "&";/' 1>&6
    printf '%s\n' "}" 1>&6
  fi
done
exec 6>&-
if test -s "$agminvawk"; then
  preinvtmp="$preinvxml.tmp"
  if "$awk" -f "$agminvawk" "$preinvxml" 1>"$preinvtmp" 2>&3 &&
     mv "$preinvtmp" "$preinvxml" 1>&3 2>&1; then
    :
  else
    verbose "Analyzing installed patches...failed."

    error "Cannot augment pre-run patch inventory."
  fi
fi
unset cpurlbs

# create awk script to parse patch metadata
cat << 'EOF' 1>"$preinvawk" 2>&3 ||
BEGIN {
for ( i in XML_GES ) delete XML_GES[i];
XML_GES["&amp;"]  = "&";
XML_GES["&lt;"]   = "<";
XML_GES["&gt;"]   = ">";
XML_GES["&apos;"] = "'";
XML_GES["&quot;"] = "\"";
}
function chop( s )
{
return substr( s, 1, length( s ) - 1 );
}
function squote( s, qs, b )
{
qs = "";
b  = 0;
while ( b = index( s, "'" ) ) {
qs = qs substr( s, 1, b - 1 ) "'\\''";
s  = substr( s, b + 1 );
}
return qs s;
}
function aquote( s, qs )
{
qs = "";
while ( match( s, /[\\"]/ ) ) {
qs = qs substr( s, 1, RSTART - 1 ) "\\" substr( s, RSTART, 1 );
s  = substr( s, RSTART + 1 );
}
return qs s;
}
function xunquote( s, uqs, b )
{
uqs = "";
while ( b = index( s, "&" ) ) {
uqs = uqs substr( s, 1, b - 1 );
s   = substr( s, b );
if ( match( s, /^&(amp|[lg]t|apos|quot);/ ) ) {
uqs = uqs XML_GES[substr( s, 1, RLENGTH )];
s   = substr( s, RLENGTH + 1 );
}
else {
uqs = uqs "&";
s   = substr( s, 2 );
}
}
return uqs s;
}
function sort( s, a, v, n, i, j )
{
n = split( s, a, " " );
if ( n <= 1 )
return s;
a[1] = "" a[1];
for ( i = 2; i <= n; i++ ) {
v = "" a[i]; j = i - 1;
while ( (j >= 1) && (a[j] > v) ) {
a[j+1] = a[j]; j--;
}
a[j+1] = v;
}
s = "";
for ( i = 1; i <= n; i++ )
s = s a[i] " ";
return s;
}
function psver( vs )
{
if ( match( vs, /^[^.]+\.[^.]+\.[^.]+\.[^.]+\./ ) )
return substr( vs, 1, RLENGTH - 1 );
else
return vs;
}
function prstree( id, s, a, v, n, i, b, m, j )
{
if ( ! (id in patchidset) ) {
return "";
}
else {
delete patchidset[id];
n = split( patchidprs[id], a, " " );
s = "";
for ( i = 1; i <= n; i++ ) {
v = "" a[i];
if ( v in patchno2id ) {
m = split( patchno2id[v], b, " " );
for ( j = 1; j <= m; j++ )
s = s prstree( "" b[j] );
}
}
return s id " ";
}
}
BEGIN {
for ( i in errors ) delete errors[i];
errcnt = 0;
ftlcnt = 0;
}
function _logerror( message )
{
if ( errors[message]++ < 10 ) {
errors[errcnt + ftlcnt] = NR ": " message;
return 1;
}
else
return 0;
}
function error( message )
{
if ( _logerror( message ) ) errcnt++;
}
function fatal( message )
{
if ( _logerror( message ) ) ftlcnt++;
}
BEGIN {
state   = "bad";
for ( i in states ) delete states[i];
statess = 0;
for ( i in PATCH_STATES ) delete PATCH_STATES[i];
PATCH_STATES["ipt"] = 1;
PATCH_STATES["ipi"] = 1;
PATCH_STATES["ipa"] = 1;
PATCH_STATES["ipo"] = 1;
PATCH_STATES["ipc"] = 1;
for ( i in eeps ) delete eeps[i];
}
function push( newstate )
{
states[statess++] = state = newstate;
}
function pop( oldstate )
{
if ( statess > 0 ) {
for ( c in eeps )
if ( index( c, state ) == 1 )
eeps[c] = 0;
statess--;
}
oldstate = state;
if ( statess > 0 )
state = states[statess - 1];
else
state = "bad";
return oldstate;
}
function eepcs( id )
{
return 0 + eeps[state id];
}
function event( id )
{
eeps[state id]++;
}
function invxml()
{
error( "invalid XML tag or element in state " state );
if ( state in PATCH_STATES ) patchinv = 1;
}
function _invcnt( id, n, cond )
{
if ( index( id, "!" ) == 1 )
error( "invalid number of " id " events in state " state " (" n " " cond ")" );
else
error( "invalid number of <" id "> elements in state " state " (" n " " cond ")" );
if ( state in PATCH_STATES ) patchinv = 1;
return 0;
}
function none( id, n ) { return ((n = eepcs( id )) == 0) || _invcnt( id, n, "!= 0" ); }
function one( id, n )  { return ((n = eepcs( id )) == 1) || _invcnt( id, n, "!= 1" ); }
function alo( id, n )  { return ((n = eepcs( id )) >= 1) || _invcnt( id, n, "< 1" ); }
function amo( id, n )  { return ((n = eepcs( id )) <= 1) || _invcnt( id, n, "> 1" ); }
BEGIN {
tagname = "";
tagtype = "";
tagid   = "";
tagtext = "";
tagacnt = 0;
for ( i in tagattr ) delete tagattr[i];
}
function parsetag( s,                           # unparsed input line
b,                           # beginning of something
l,                           # length of something
aq,                          # attribute quote
an,                          # attribute name
av )                         # attribute value
{
s = $0;
tagacnt = 0;
for ( i in tagattr ) delete tagattr[i];
if ( match( s, /^ *<[a-zA-Z0-9_.:-]+/ ) ) {
b = index( s, "<" ) + 1;
l = RLENGTH - b + 1;
tagname = substr( s, b, l );
s = substr( s, RLENGTH + 1 );
while ( match( s, /^ +[a-zA-Z0-9_.:-]+=["']/ ) ) {
b  = index( s, "=" );
an = substr( s, 1, b - 1 );               # contains leading whitespace
aq = substr( s, b + 1, 1 );
av = "";
s  = substr( s, RLENGTH + 1 );
l = 1;
while ( (l == 1) && ((b = index( s, aq )) == 0) ) {
av = av s "\n";
l  = getline s;
}
if ( l != 1 ) {
s = "";
break;
}
av = av substr( s, 1, b - 1 );
s  = substr( s, b + 1 );
sub( /^ +/, "", an );
av = xunquote( av );
tagacnt++;
tagattr[an] = av;
}
if ( match( s, /^\/> *$/) ) {
tagtype = "empty";
tagid   = "#" tagname;
tagtext = "";
eeps[state tagname]++;
}
else if ( match( s, /^> *$/ ) ) {
tagtype = "start";
tagid   = "<" tagname;
tagtext = "";
eeps[state tagname]++;
}
else if ( match( s, />[^<]*<\/[a-zA-Z0-9_.:-]+> *$/ ) ) {
tagtype = "text";
tagid   = "'" tagname;
tagtext = xunquote( substr( s, 2, index( s, "<" ) - 2 ) );
eeps[state tagname]++;
}
else {
tagtype = "error";
tagid   = "!" tagname;
tagtext = "";
}
}
else if ( match( s, /^ *<\/[a-zA-Z0-9_.:-]+> *$/ ) ) {
b = index( s, "</" ) + 2;
l = index( s, ">" ) - b;
tagname = substr( s, b, l );
tagtype = "end";
tagid   = ">" tagname;
tagtext = "";
}
else {
tagname = "";
tagtype = "error";
tagid   = "!";
tagtext = "";
}
return tagtype;
}
BEGIN {
for ( i in MONTH_NUMBERS ) delete MONTH_NUMBERS[i];
MONTH_NUMBERS["jan"] = "01";
MONTH_NUMBERS["feb"] = "02";
MONTH_NUMBERS["mar"] = "03";
MONTH_NUMBERS["apr"] = "04";
MONTH_NUMBERS["may"] = "05";
MONTH_NUMBERS["jun"] = "06";
MONTH_NUMBERS["jul"] = "07";
MONTH_NUMBERS["aug"] = "08";
MONTH_NUMBERS["sep"] = "09";
MONTH_NUMBERS["oct"] = "10";
MONTH_NUMBERS["nov"] = "11";
MONTH_NUMBERS["dec"] = "12";
for ( i in DVSF ) delete DVSF[i];
DVSF["catmac.sql"]     = 1;
DVSF["catmacc.sql"]    = 1;
DVSF["catmacd.sql"]    = 1;
DVSF["catmacg.sql"]    = 1;
DVSF["catmach.sql"]    = 1;
DVSF["catmacp.sql"]    = 1;
DVSF["catmacr.sql"]    = 1;
DVSF["catmacs.sql"]    = 1;
DVSF["catmact.sql"]    = 1;
DVSF["catmacpre.sql"]  = 1;
DVSF["catmacpost.sql"] = 1;
DVSF["dvmacfnc.plb"]   = 1;
DVSF["prvtmacp.plb"]   = 1;
for ( i in ZEROES ) delete ZEROES[i];
ZEROES[0]  = "0000000000";
ZEROES[1]  = "000000000";
ZEROES[2]  = "00000000";
ZEROES[3]  = "0000000";
ZEROES[4]  = "000000";
ZEROES[5]  = "00000";
ZEROES[6]  = "0000";
ZEROES[7]  = "000";
ZEROES[8]  = "00";
ZEROES[9]  = "0";
ZEROES[10] = "";
hdt = "";
for ( i in comps ) delete comps[i];
  ### comps ###
for ( i in patchidset ) delete patchidset[i];
patchidcnt = 0;
for ( i in patchidlst ) delete patchidlst[i];
for ( i in patchno2id ) delete patchno2id[i];
for ( i in patchidprs ) delete patchidprs[i];
FS = "\n";
}
(NR == 1) { next; }
(/^ *$/) { next; }
(parsetag() == "error") {
error( "invalid input line" );
if ( state in PATCH_STATES ) patchinv = 1;
next;
}
(state == "ipt") && (tagid == "<oneoff_inventory") {
push( "ipi" );
next;
}
(state == "ipi") && (tagname == "reference_id") {
event( "!reference_id" );
pno = tagattr["number"];
if ( (tagtype == "empty") && (pno ~ /^[0-9]+$/) ) {
patchno = pno;
}
else {
patchno = "INVALID";
invxml();
}
next;
}
(state == "ipi") && (tagname == "patch_id") {
event( "!reference_id" );
pno = tagattr["number"];
if ( (tagtype == "empty") && (pno ~ /^[0-9]+$/) ) {
patchno = pno;
}
else {
patchno = "INVALID";
invxml();
}
next;
}
(state == "ipi") && (tagname == "date_of_patch") {
psy = tagattr["year"];
psm = tolower( tagattr["month"] );
psd = tagattr["day"];
pst = tagattr["time"];
if ( (tagtype == "empty") &&
(psy ~ /^[0-9][0-9][0-9][0-9]$/) &&
(psm in MONTH_NUMBERS) &&
(psd ~ /^[0-9][0-9]?$/) &&
(pst ~ /^[0-9][0-9]:[0-9][0-9]:[0-9][0-9]( *hrs)?$/) ) {
psm = MONTH_NUMBERS[psm];
if ( length( psd ) == 1 ) psd = "0" psd;
pst = substr( pst, 1, 8 );
split( pst, psts, ":" );
patchstamp = psy psm psd psts[1] psts[2] psts[3];
}
else {
patchstamp = "INVALID";
invxml();
}
next;
}
(state == "ipi")                                  &&
(tagid == "#bug")                                 &&
("description" in tagattr)                        &&
(tagattr["description"] ~ /-MOLECULE-[0-9]+-CPU/) {
patchflags["cpumlc"] = 1;
next;
}
(state == "ipi") && (tagname == "mopatch_cpurlb") {
crp = tolower( tagtext );
if ( crp == "true" )
patchflags["cpurlb"] = 1;
else if ( crp != "false" )
invxml();
next;
}
(state == "ipi") && (tagname == "executable") {
if ( (tagtype == "empty") && ("path" in tagattr) )
patchflags["exec"] = 1;
else
invxml();
next;
}
(state == "ipi") && (tagname == "instance_shutdown") {
sdp = tolower( tagtext );
if ( sdp == "true" )
patchflags["shtdwn"] = 1;
else if ( sdp != "false" )
invxml();
next;
}
(state == "ipi") && (tagname == "PatchSetUpdate") {
psup = tolower( tagtext );
if ( psup == "true" )
patchflags["psu"] = 1;
else if ( psup != "false" )
invxml();
next;
}
(state == "ipi") && (tagname == "prereq") {
pno = tagattr["oneoff_id"];
if ( (tagtype == "empty") && (pno ~ /^[0-9]+$/) )
patchprqs = patchprqs pno " ";
else
invxml();
next;
}
(state == "ipi") && (tagname == "overlay") {
pno = tagattr["oneoff_id"];
if ( (tagtype == "empty") && (pno ~ /^[0-9]+$/) && (length( pno ) <= 10) )
patchovls = patchovls ZEROES[length( pno )] pno " ";
else
invxml();
next;
}
(state == "ipi")                           &&
(invtype == "tbi")                         &&
(tagname == "component")                   &&
("internal_name" in tagattr)               &&
(tagattr["internal_name"] == "oracle.crs") {
patchflags["crs"] = 1;
next;
}
(state == "ipi")                           &&
(invtype == "ins")                         &&
(tagname == "COMP")                        &&
("internal_name" in tagattr)               &&
(tagattr["internal_name"] == "oracle.crs") {
patchflags["crs"] = 1;
next;
}
(state == "ipi") && (tagid == ">oneoff_inventory") {
if ( ! one( "!reference_id" ) ) patchno    = "INVALID";
if ( ! one( "date_of_patch" ) ) patchstamp = "INVALID";
amo( "mopatch_cpurlb" );
one( "instance_shutdown" );
amo( "PatchSetUpdate" );
pop();
next;
}
(state == "ipi") {
next;
}
(state == "ipa")                                                &&
(invtype == "tbi")                                              &&
(tagtype == "start")                                            &&
("version" in tagattr)                                          &&
("opt_req" in tagattr)                                          &&
((tagattr["opt_req"] == "R") ||
((tagattr["opt_req"] == "O") &&
(tagname in comps) &&
(psver( tagattr["version"] ) == psver( comps[tagname] )))) {
event( "!comp" );
push( "ipc" );
patchcomp = tagname;
next;
}
(state == "ipa")   &&
(invtype == "ins") &&
(tagid == "<COMP") {
event( "!comp" );
push( "ipc" );
next;
}
(state == "ipc")                                 &&
((tagid == "#copy") || (tagid == "#onewaycopy")) {
cpn = tagattr["name"];
cpp = tagattr["path"];
if ( (cpn != "")              && (! (cpn ~ /[\/\n!&]/)) &&
(cpp ~ /^%ORACLE_HOME%/) && (! (cpp ~ /[\n!&]/)) ) {
patchfiles = patchfiles cpp "/" cpn "\n";
if ( (cpn == "version.txt") && (cpp == "%ORACLE_HOME%/sapbundle") )
patchflags["sbp"] = 1;
else if ( (cpn == "catpsu.sql") && (cpp ~ /^%ORACLE_HOME%\/psu\//) )
patchflags["catpsu"] = 1;
else if ( (cpn == "catcpu.sql") && (cpp ~ /^%ORACLE_HOME%\/cpu\//) )
patchflags["catcpu"] = 1;
else if ( (cpn in DVSF) && (cpp == "%ORACLE_HOME%/rdbms/admin") )
patchflags["dvsp"] = 1;
else if ( tolower( cpn ) ~ /\.(sql|plb|bsq)$/ )
patchflags["sql"] = 1;
else if ( cpp == "%ORACLE_HOME%/rdbms/xml/xsl" )
patchflags["sql"] = 1;
}
else
invxml();
next;
}
(state == "ipc") && (tagid == "#make" ) {
mkd = tagattr["change_dir"];
mkf = tagattr["make_file"];
mkt = tagattr["make_target"];
if ( (mkd ~ /^%ORACLE_HOME%/) && (! (mkd ~ /[\n!&]/)) &&
(mkf != "")              && (! (mkf ~ /[\/\n!&]/)) &&
(mkt != "")              && (! (mkt ~ /[\n&]/)) ) {
if ( patchmakes != "INVALID" )
patchmakes = patchmakes mkd "&" mkf "&" mkt "\n";
}
else {
patchmakes = "INVALID";
invxml();
}
next;
}
(state == "ipc") && (tagid == "#hotpatch") {
patchflags["hpatch"] = 1;
next;
}
(state == "ipc") && (tagid == "#archive") {
next;
}
(state == "ipc") && (tagid == "#jar") {
patchflags["java"] = 1;
next;
}
(state == "ipc")       &&
(invtype == "tbi")     &&
(tagname == patchcomp) &&
(tagtype == "end")     {
patchcomp = "";
pop();
next;
}
(state == "ipc")   &&
(invtype == "ins") &&
(tagid == ">COMP") {
pop();
next;
}
(state == "ipc") {
invxml();
next;
}
(state == "ipa")       &&
(invtype == "tbi")     &&
(tagtype == "start")   &&
("version" in tagattr) &&
("opt_req" in tagattr) {
event( "!comp" );
push( "ipo" );
patchcomp = tagname;
next;
}
(state == "ipo")       &&
(invtype == "tbi")     &&
(tagname == patchcomp) &&
(tagtype == "end")     {
patchcomp = "";
pop();
next;
}
(state == "ipo") {
next;
}
(state == "ipt") && (tagid == "<oneoff_actions") {
push( "ipa" );
next;
}
(state == "ipa") && (tagid == ">oneoff_actions") {
alo( "!comp" );
pop();
next;
}
(state == "ipa") {
invxml();
next;
}
BEGIN {
patchid    = 0;
patchno    = "INVALID";
patchinv   = 0;
patchbase  = "INVALID";
patchsrc   = "INVALID";
patchmlc   = "INVALID";
patchstamp = "INVALID";
patchprqs  = "";
patchovls  = "";
patchfiles = "";
patchmakes = "";
for ( i in patchflags ) delete patchflags[i];
patchcomp  = "";
}
(state == "ips")                 &&
(tagid == "<INTERIM_PATCH")      &&
("REF_ID" in tagattr)            &&
(tagattr["REF_ID"] ~ /^[0-9]+$/) {
push( "ipt" );
patchid    = tagattr["REF_ID"];
patchno    = "INVALID";
patchinv   = 0;
patchbase  = "INVALID";
patchsrc   = "INVALID";
patchmlc   = "INVALID";
patchstamp = "INVALID";
patchprqs  = "";
patchovls  = "";
patchfiles = "";
patchmakes = "";
for ( i in patchflags ) delete patchflags[i];
patchcomp  = "";
next;
}
(state == "ipt") && (tagid == "'patchflag") &&
(invtype == "tbi") {
patchflags[tagtext] = 1;
next;
}
(state == "ipt") && (tagid == "'patchbase") &&
(invtype == "tbi") {
patchbase = tagtext;
next;
}
(state == "ipt") && (tagid == "'patchsrc") &&
(invtype == "tbi") {
patchsrc = tagtext;
next;
}
(state == "ipt") && (tagid == "'patchmlc") &&
(invtype == "tbi") {
patchmlc = tagtext;
next;
}
(state == "ipt") && (tagid == "'file") &&
(invtype == "tbi") {
if ( tagtext == "OPatch/opatch" )
patchflags["opatch"] = 1;
else if ( tagtext ~ /\/README\.txt$/ )
patchflags["rdmtxt"] = 1;
else if ( tagtext ~ /\/README\.html$/ )
patchflags["rdmhtml"] = 1;
else if ( tagtext ~ /\/mopatch-tgttp-rdbms$/ )
patchflags["rdbms"] = 1;
else if ( tagtext ~ /\/mopatch-tgttp-gi$/ )
patchflags["gi"] = 1;
next;
}
(state == "ipt") && (tagid == ">INTERIM_PATCH") {
if ( invtype == "tbi" ) {
if ( ! one( "patchbase" ) )
patchbase = "INVALID";
if ( ! one( "patchsrc" ) )
patchsrc = "INVALID";
if ( ! one( "patchmlc" ) )
patchmlc = "INVALID";
}
if ( ! one( "oneoff_inventory" ) ) {
patchno    = "INVALID";
patchstamp = "INVALID";
}
one( "oneoff_actions" );
if ( patchid in patchidset )
fatal( "invalid duplicate patch ID " patchid );
else
patchidset[patchid] = 1;
patchidlst[patchidcnt++] = patchid;
if ( patchno in patchno2id )
patchno2id[patchno] = patchno2id[patchno] patchid " ";
else
patchno2id[patchno] = patchid " ";
patchidprs[patchid] = patchprqs;
if ( patchinv && (! ("inv" in patchflags)) )
pfls = "inv ";
else
pfls = "";
for ( pfl in patchflags )
pfls = pfls pfl " ";
if ( invtype == "tbi" ) {
print "hash_pmdtbips_" patchid "='" squote( patchbase "&" patchsrc "&" patchmlc ) "'";
print "hash_pmdtbi_" patchid "='" patchno "&" patchstamp "& " pfls "'";
if ( patchprqs != "" )
print "hash_pmdtbiprs_" patchid "='"               patchprqs      "'";
if ( patchovls != "" )
print "hash_pmdtbiols_" patchid "='"         sort( patchovls )    "'";
if ( patchfiles != "" )
print "hash_pmdtbifls_" patchid "='" squote( chop( patchfiles ) ) "'";
if ( patchmakes == "INVALID" )
print "hash_pmdtbimks_" patchid "='"               patchmakes     "'";
else if ( patchmakes != "" )
print "hash_pmdtbimks_" patchid "='" squote( chop( patchmakes ) ) "'";
}
else {
print "hash_pmdins_" patchid "='" patchno "&" patchstamp "& " pfls "'";
if ( patchprqs != "" )
print "hash_pmdinsprs_" patchid "='"               patchprqs      "'";
if ( patchovls != "" )
print "hash_pmdinsols_" patchid "='"         sort( patchovls )    "'";
if ( patchfiles != "" )
print "hash_pmdinsfls_" patchid "='" squote( chop( patchfiles ) ) "'";
if ( patchmakes == "INVALID" )
print "hash_pmdinsmks_" patchid "='"               patchmakes     "'";
else if ( patchmakes != "" )
print "hash_pmdinsmks_" patchid "='" squote( chop( patchmakes ) ) "'";
}
pop();
next;
}
(state == "ipt") {
invxml();
next;
}
(state == "ioh") && (tagid == "<ONEOFF_LIST") { push( "ips" ); next; }
(state == "ioh") && (tagid == "#ONEOFF_LIST") {                next; }
(state == "ips") && (tagid == ">ONEOFF_LIST") { pop();         next; }
(state == "ips")                              { invxml();      next; }
(state == "ics") && (tagid == "<COMP") &&
("INT_NAME" in tagattr) {
push( "icp" );
cn = tagattr["INT_NAME"];
cv = "";
next;
}
(state == "icp") && (tagid == ">COMP") {
if ( alo( "!VERSION" ) && (invtype == "ins") && (hdt == "COMPS") ) {
cmpphrln="  comps[\"" aquote( cn ) "\"] = \"" aquote( cv ) "\";";
cmpphrln="  print \"" aquote( cmpphrln ) "\";"

print cmpphrln;
}
pop();
next;
}
(state == "icp") && (tagid == "'VERSION") {
event( "!VERSION" );
cv = tagtext;
next;
}
(state == "icp") {
invxml();
next;
}
(state == "ioh") && (tagid == "<COMPS") {
push( "ics" );
if ( (invtype == "ins") && (hdt == "") ) {
hdt = "COMPS";
print "cat << 'COMPS' 1>'" squote( cmpphrs ) "' || exit 1";
print "/^  ### comps ###$/ {";
}
next;
}
(state == "ioh") && (tagid == "#COMPS") {
if ( (invtype == "ins") && (hdt == "") ) {
print "cat << 'COMPS' 1>'" squote( cmpphrs ) "' || exit 1";
print "/^  ### comps ###$/ {";
print "  next;";
print "}";
print "{ print; }";
print "COMPS";
}
next;
}
(state == "ics") && (tagid == ">COMPS") {
if ( (invtype == "ins") && (hdt == "COMPS") ) {
print "  next;";
print "}";
print "{ print; }";
print "COMPS";
hdt = "";
}
pop();
next;
}
(state == "ics") {
invxml();
next;
}
(state == "ioh") && (tagid == "<NODE_LIST") { push( "ins" ); next; }
(state == "ioh") && (tagid == "#NODE_LIST") {                next; }
(state == "ins") && (tagid == ">NODE_LIST") { pop();         next; }
(state == "ins")                            {                next; }
(state == "ooa") && (tagid == "<HOME") &&
("LOC" in tagattr) && ("CRS" in tagattr) &&
(tagattr["LOC"] == oh) && (tagattr["CRS"] ~ /^[TF]$/) {
event( "!HOME" );
push( "ioh" );
next;
}
(state == "ioh") && (tagid == ">HOME") {
one( "NODE_LIST" );
one( "COMPS" );
one( "ONEOFF_LIST" );
pop();
next;
}
(state == "ioh") {
invxml();
next;
}
BEGIN {
push( "ooa" );
}
END {
one( "!HOME" );
if ( (state = pop()) != "ooa" )
fatal( "invalid EOF in state " state );
}
(state == "ooa") {
next;
}
END {
if ( hdt != "" ) {
print hdt;
fatal( "invalid open here-document in output" );
}
if ( invtype == "tbi" ) {
patchids = "";
for ( i = 0; i < patchidcnt; i++ )
patchids = patchids prstree( patchidlst[i] );
print "pmdtbiids='" patchids "'";
}
else {
patchids = "";
for ( i = 0; i < patchidcnt; i++ )
patchids = patchids patchidlst[i] " ";
print "pmdinsids='" patchids "'";
}
for ( i = 0; i < (errcnt + ftlcnt); i++ )
print "echo '" squote( errors[i] ) "' 1>&2";
if ( ((invtype == "ins") && ((errcnt + ftlcnt) > 0)) ||
((invtype == "tbi") && (ftlcnt > 0)) )
print "exit 1";
}
EOF
{
  verbose "Analyzing installed patches...failed."

  error "Cannot create awk script \"$preinvawk\"."
}

# parse and test patch metadata.  PD (sunos_*): Do not use
# "return" statements in the generated shell script.
cmpphrs="$tmpdir/cmpphrs.awk"
if "$awk" -f "$preinvawk"          \
           -v oh="$ORACLE_HOME"     \
           -v invtype="ins"         \
           -v cmpphrs="$cmpphrs"    \
           "$preinvxml" 1>"$preinvsh" 2>&3 &&
           ( . "$preinvsh" ) 1>&3 2>&1; then
  :
else
  verbose "Analyzing installed patches...failed."

  error "Cannot parse and test patch metadata."
fi

# read patch metadata
. "$preinvsh" 1>/dev/null 2>&1

# determine installed patches from patch metadata
ipmd=""
for patchid in $pmdinsids
do
  if patchno=`pmdget ins $patchid patchno`; then
    :
  else
    error "Cannot determine patch number of patch $patchid."
  fi
  ipmd="$ipmd$patchno$NL"
done
ipmd=`printf '%s' "$ipmd" | sort -n 2>&3`

# verify lists of installed patches
cat << EOF 1>"$tmpdir/iplog" 2>&3
$iplog
EOF
cat << EOF 1>"$tmpdir/ipmd" 2>&3
$ipmd
EOF
if diff "$tmpdir/iplog" "$tmpdir/ipmd" 1>&3 2>&1; then
  :
else
  verbose "Analyzing installed patches...failed."

  error "Cannot verify lists of installed patches."
fi

if $MOPATCH_TEST_MIGRATE; then
  :
else
verbose "Analyzing installed patches...done."
fi

if test $signalled = 1; then
  error -c cmdlineerror "Interrupted."
fi

if test "X$ipmd" != "X"; then
  verbose
  verbose "Patches installed in Oracle Home $ORACLE_HOME:"
  # use sorted list here!
  for patchid in $ipmd
  do
    # all installed patched must be valid, so no extra checks on
    # that are required here
    pmdcache ins $patchid

    patchflaginfo=`pmdflaginfo`
    l_oldifs=$IFS; IFS="&"; set -f; set x $patchflaginfo; shift; set +f; IFS=$l_oldifs
    patchmode=$1
    patchflags=${3-}

    if $MOPATCH_TEST_MIGRATE; then
    verbose "  $pmdpatchno"
    else
    if test "X$patchflags" != "X"; then
      verbose "$patchmode $pmdpatchno ($patchflags)"
    else
      verbose "$patchmode $pmdpatchno"
    fi
    fi
  done

  if runmodep "doc" &&
     test $flag_ignore_installed = 1; then
    verbose
    verbose "Ignoring installed patches in documentation mode due to flag"
    verbose "\"ignore_installed\"."
  fi
else
  verbose
  verbose "No patches installed in Oracle Home $ORACLE_HOME."
fi

if test $signalled = 1; then
  error -c cmdlineerror "Interrupted."
fi


############################
#
# analyze patches to process
#
############################

if $MOPATCH_TEST_MIGRATE; then
  :
else
out
out "Analyzing patches to process..."
fi

# newline-list of patches to install
patchsrcs=""
l_oldifs=$IFS; IFS=":"; set -f; set x $patchsrcpath; shift; set +f; IFS=$l_oldifs
for item in "$patchsrcfile" "$patchsrcdir" ${1+"$@"}
do
  if test "X$item" = "X"; then
    continue
  elif test -f "$item"; then
    patchsrcs="$patchsrcs$item$NL"
  else
    # newline-list of lowercase patch names and newline-list of
    # ampersand-records
    #
    #  <lowercase-patchsrc>&<patchsrc>
    lpatchsrcs=""
    spatchsrcs=""
    for patchsrc in \
      "$item"/[pP][0-9][0-9][0-9][0-9][0-9][0-9]*.[zZ][iI][pP] \
      "$item"/SAP_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP] \
      "$item"/SGR_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP] \
      "$item"/SXD_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP] \
      "$item"/GIPSU_[0-9][0-9][0-9][0-9][0-9]*[0-9]_*.[zZ][iI][pP] \
      "$item"/EXA_[0-9][0-9][0-9][0-9][0-9]*[0-9]_*.[zZ][iI][pP]
    do
      if test "X$patchsrc" != "X$item/[pP][0-9][0-9][0-9][0-9][0-9][0-9]*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/SAP_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/SGR_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/SXD_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]_*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/GIPSU_[0-9][0-9][0-9][0-9][0-9]*[0-9]_*.[zZ][iI][pP]" &&
         test "X$patchsrc" != "X$item/EXA_[0-9][0-9][0-9][0-9][0-9]*[0-9]_*.[zZ][iI][pP]"; then
        case $patchsrc in
          "$item"/GIPSU_[0-9][0-9][0-9][0-9][0-9]*[0-9]_*.[zZ][iI][pP]|\
          "$item"/EXA_[0-9][0-9][0-9][0-9][0-9]*[0-9]_*.[zZ][iI][pP])
            if test $flag_ignore_ubps = 1; then
              continue
            fi ;;
        esac

        lpatchsrc=`printf '%s\n' "$patchsrc" |
                   sed 's/\.[zZ][iI][pP]$/.zip/;''s/P\([0-9][0-9][0-9][0-9][0-9][0-9][^\/]*.zip\)$/p\1/' 2>&3`
        if memberp "$NL" "$lpatchsrc" "$lpatchsrcs"; then
          continue
        fi
        lpatchsrcs="$lpatchsrcs$lpatchsrc$NL"
        spatchsrcs="$spatchsrcs$lpatchsrc&$patchsrc$NL"
      fi
    done

    # sort patches to install by lowercase patch basename and
    # strip off sort key
    if test "X$spatchsrcs" != "X"; then
      if spatchsrcs=`printf '%s' "$spatchsrcs" | sort -k1,1 -t "&" 2>&3`; then
        :
      else
        out "Analyzing patches to process...failed."

        error "Cannot sort list of patches from patch source directory \"$item\"."
      fi
      l_oldifs=$IFS; IFS="$NL"; set -f; set x $spatchsrcs; shift; set +f; IFS=$l_oldifs
      for spatchsrc in "$@"
      do
        l_oldifs=$IFS; IFS="&"; set -f; set x $spatchsrc; shift; set +f; IFS=$l_oldifs
        patchsrcs="$patchsrcs$2$NL"
      done
    fi
  fi
done
unset spatchsrcs
unset lpatchsrcs

tbiinvawk="$tmpdir/tbiinv.awk"
tbiinvxml="$tmpdir/tbiinv.xml"
tbiinvsh="$tmpdir/tbiinv.sh"

# modify awk script to parse patch metadata of to-be-installed
# patches
if "$awk" -f "$cmpphrs" "$preinvawk" 1>"$tbiinvawk" 2>&3; then
  :
else
  out "Analyzing patches to process...failed."

  error "Cannot create awk script \"$tbiinvawk\"."
fi

# open to-be-installed inventory and write its header
if ( exec 6>"$tbiinvxml" && exec 6>&- ) 1>&3 2>&1 && exec 6>"$tbiinvxml"; then
  :
else
  out "Analyzing patches to process...failed."

  error "Cannot open to-be-installed inventory \"$tbiinvxml\" for writing."
fi
echo "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" 1>&6
echo "<HOME LOC=\"$ORACLE_HOME\" CRS=\"F\">" 1>&6
echo "<NODE_LIST/>" 1>&6
echo "<COMPS/>" 1>&6
echo "<ONEOFF_LIST>" 1>&6

# loop through the patches to install, extract patch listings and
# metadata, and write them to the to-be-installed inventory.
# Never exit this loop with an error, log errors and write
# appropriate patch metadata instead.
patchid=0
l_oldifs=$IFS; IFS="$NL"; set -f; set x $patchsrcs; shift; set +f; IFS=$l_oldifs
for patchsrc in "$@"
do
  # determine patch basename first.  The patch basename is used
  # as an unique identifier in the output and the log file, and
  # for nothing else.  Keep the patch basename always lowercase.
  # This will still be unique, since we removed duplicate (up to
  # case) patches in the above loop.
  if patchbase=`printf '%s\n' "$patchsrc" |
                sed -e 's/\.[zZ][iI][pP]$/.zip/'                  \
                     -e 's/P\([0-9][0-9][0-9][0-9][0-9][0-9][^\/]*.zip\)$/p\1/'  \
                     -e 's/^\.\///;s/\/\/\/*/\//g;' 2>&3`; then
    :
  else
      logerr "Cannot determine patch basename from \"$patchsrc\"."
      pmdtbipatch $patchid "INVALID" "$patchsrc" "INVALID" "genfld"
      patchid=`expr $patchid + 1 2>&3`
      continue
  fi

  # if the patch source path is a singleton, simplify the patch
  # basename by using only, well, the basename
  if test $pspstp = 1; then
    if npatchbase=`basenamef "$patchbase" 2>&3`; then
      patchbase=$npatchbase
    else
      logerr "Cannot determine simplified patch basename from \"$patchbase\"."
      pmdtbipatch $patchid "$patchbase" "$patchsrc" "INVALID" "genfld"
      patchid=`expr $patchid + 1 2>&3`
      continue
    fi
  fi

  if test -f "$patchsrc"; then
    :
  else
    pmdtbipatch $patchid "$patchbase" "$patchsrc" "INVALID" "nonfile"
    patchid=`expr $patchid + 1 2>&3`
    continue
  fi

  # test zip file and get patch file contents
  patchtest="$tmpdir/patchtest"
  if "$unzip" -t "$patchsrc" 1>"$patchtest" 2>&3; then
    :
  else
    pmdtbipatch $patchid "$patchbase" "$patchsrc" "INVALID" "nonzip"
    patchid=`expr $patchid + 1 2>&3`
    continue
  fi

  # determine zip file contents from test output
  patchls="$tmpdir/patchls"
  if grep '^[ 	]*testing:[ 	][ 	]*' "$patchtest" 2>&3 |
     sed 's/^[ 	]*testing:[ 	][ 	]*//;s/[ 	][ 	]*OK$//' 2>&3 |
     sort 1>"$patchls" 2>&3; then
    :
  else
    logerr "Cannot determine zip file contents from test output for patch \"$patchbase\"."
    pmdtbipatch $patchid "$patchbase" "$patchsrc" "INVALID" "genfld"
    patchid=`expr $patchid + 1 2>&3`
    continue
  fi

  # to-be-installed patch molecules.  List of directory names
  # where one of the files etc/config/inventory or
  # etc/config/actions exist:
  #
  #   <patch_no>/<molecule_id>
  #   ...
  #   <patch_no>
  #   ...
  #
  # A molecule ID may be a directory name of any depth, it must
  # not necessarily be numeric.
  #
  # We intentionally sort the molecules in reverse order.
  tbipmlcs=`egrep '[0-9][0-9]*(/.*)?/etc/config/(inventory|actions)(\.xml)?$' "$patchls" 2>&3 |
            sed 's,/etc/config/[^/][^/]*$,,' 2>&3 |
            sort -r 2>&3 |
            uniq 2>&3`

  simple=""
  if test "X$tbipmlcs" = "X"; then
    # non-OPatch patch
    pmdtbihdr $patchid "$patchbase" "$patchsrc" "INVALID" "nonop"
    if cat "$patchls" | pmdtbifiles; then
      :
    else
      logerr "Cannot extract patch molecule listing for patch \"$patchbase\"."
      pmdtbiflg "genfld"
    fi
    pmdtbiftr
    patchid=`expr $patchid + 1 2>&3`
    continue
  elif swp "$tbipmlcs"; then
    simple=1
  else
    simple=0
    if test $flag_apply_napply = 0; then
      # n-apply patch.  Do not inspect molecules any further.
      pmdtbihdr $patchid "$patchbase" "$patchsrc" "INVALID" "napply"
      if cat "$patchls" | pmdtbifiles; then
        :
      else
        logerr "Cannot extract patch molecule listing for patch \"$patchbase\"."
        pmdtbiflg "genfld"
      fi
      pmdtbiftr
      patchid=`expr $patchid + 1 2>&3`
      continue
    fi
  fi

  patchrootbase=$patchbase
  l_oldifs=$IFS; IFS="$NL"; set -f; set x $tbipmlcs; shift; set +f; IFS=$l_oldifs
  for patchmlc in "$@"                         # patch molecule
  do
    # verify patch molecule
    patchmlcxml=""
    if pmddirp "$patchmlc"; then
      :
    else
      logerr "Cannot verify patch molecule \"$patchmlc\" for patch \"$patchbase\"."
      pmdtbipatch $patchid "$patchbase" "$patchsrc" "INVALID" "genfld"
      patchid=`expr $patchid + 1 2>&3`
      continue
    fi

    # re-determine patch basename
    if test $simple = 0; then
      # patch molecule ID
      pmlcid=`printf '%s\n' "$patchmlc" | tr '/' '!' 2>&3`

      # carefully simplify SBP basenames further by stripping off
      # the leading SBP directory name
      l_oldifs=$IFS; IFS="!"; set -f; set x $pmlcid; shift; set +f; IFS=$l_oldifs
      case $1 in
        SAP_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]|\
        SBP_[0-9][0-9][0-9][0-9][0-9]*[0-9]_[0-9][0-9][0-9][0-9][0-9][0-9])
          # SBP directory name infix
          if sbpdnifx=`printf '%s\n' "$1" | sed "s/^S[AB]P_//" 2>&3`; then
            case $patchrootbase in
              SAP_"$sbpdnifx"_*.zip|*/SAP_"$sbpdnifx"_*.zip|\
              SGR_"$sbpdnifx"_*.zip|*/SGR_"$sbpdnifx"_*.zip|\
              SXD_"$sbpdnifx"_*.zip|*/SXD_"$sbpdnifx"_*.zip)
                if npmlcid=`printf '%s\n' "$pmlcid" | sed "s/^$1!//" 2>&3`; then
                  pmlcid=$npmlcid
                fi ;;
            esac
          fi ;;
      esac

      patchbase="$patchrootbase!$pmlcid"
    else
      patchbase=$patchrootbase
    fi

    # tracking bug of n-apply patches
    trkbug=""
    if expr "X$patchmlc" : 'X\([0-9][0-9]*\)/\1$' 1>/dev/null 2>&3; then
      trkbug=1
    else
      trkbug=0
    fi

    pmdtbihdr $patchid "$patchbase" "$patchsrc" "$patchmlc"

    if test $simple = 0; then
      pmdtbiflg "nonsmpl"
    fi
    if test $trkbug = 1; then
      pmdtbiflg "trkbug"
    fi

    # extract patch or patch molecule listing.  For non-simple
    # patches, remove the extracted patch molecule listing from
    # the overall listing.
    if test $simple = 1; then
      if cat "$patchls" 2>&3 | pmdtbifiles; then
        :
      else
        logerr "Cannot extract patch molecule listing for patch \"$patchbase\"."
        pmdtbiftr "genfld"
        patchid=`expr $patchid + 1 2>&3`
        continue
      fi
    elif test $trkbug = 1; then
      # seems to be the tracking bug of an n-apply patch.  Add
      # the top-level files to the patch molecule listing.
      if pmlcre=`printf '%s\n' "$patchmlc" | quotemeta -x 2>&3` &&
         egrep "^$pmlcre/"'|^[0-9][0-9]*/[^/][^/]*$' "$patchls" 2>&3 | pmdtbifiles &&
             # this may fail for the top-level patch
         ( egrep -v "^$pmlcre/"'|^[0-9][0-9]*/[^/][^/]*$' "$patchls" 1>"$patchls.tmp" 2>&3 || : ) &&
         mv "$patchls.tmp" "$patchls" 1>&3 2>&1; then
        :
      else
        logerr "Cannot extract patch molecule listing for patch \"$patchbase\"."
        pmdtbiftr "genfld"
        patchid=`expr $patchid + 1 2>&3`
        continue
      fi
    else
      if pmlcre=`printf '%s\n' "$patchmlc" | quotemeta 2>&3` &&
         grep "^$pmlcre/" "$patchls" 2>&3 | pmdtbifiles &&
         ( grep -v "^$pmlcre/" "$patchls" 1>"$patchls.tmp" 2>&3 || : ) &&
         mv "$patchls.tmp" "$patchls" 1>&3 2>&1; then
        :
      else
        logerr "Cannot extract patch molecule listing for patch \"$patchbase\"."
        pmdtbiftr "genfld"
        patchid=`expr $patchid + 1 2>&3`
        continue
      fi
    fi

    # extract patch metadata
    patchmd="$tmpdir/patchmd"
    if {
         "$unzip" -qq -p "$patchsrc" \
                          "$patchmlc/etc/config/inventory" \
                          "$patchmlc/etc/config/actions" 1>"$patchmd" 2>&3 ||
         "$unzip" -qq -p "$patchsrc" \
                          "$patchmlc/etc/config/inventory.xml" \
                          "$patchmlc/etc/config/actions.xml" 1>"$patchmd" 2>&3
       }; then
      :
    else
      logerr "Cannot extract patch metadata for patch \"$patchbase\"."
      pmdtbiftr "genfld"
      patchid=`expr $patchid + 1 2>&3`
      continue
   fi

    # check for DOS line endings in patch metadata
    if od -b "$patchmd" 2>&3 | grep ' 015' 1>/dev/null 2>&3; then
      pmdtbiflg "dos"
    fi

    # remove DOS line endings and append patch metadata to the
    # to-be-installed inventory.  Add trailing whitespace to the
    # patch metadata.
    OOAISE='<\/oneoff_actions>[ 	]*<oneoff_inventory>'
    OOIASE='<\/oneoff_inventory>[ 	]*<oneoff_actions>'
    if ( cat "$patchmd" 2>&3 && echo ) |
       tr -d '\015' 2>&3 |
       sed -e "s/$OOAISE/<\/oneoff_actions>\\${NL}<oneoff_inventory>/" \
            -e "s/$OOIASE/<\/oneoff_inventory>\\${NL}<oneoff_actions>/" 1>&6 2>&3; then
      :
    else
      logerr "Cannot append patch metadata for patch \"$patchbase\"."
      pmdtbiftr "genfld"
      patchid=`expr $patchid + 1 2>&3`
      continue
    fi

    pmdtbiftr
    patchid=`expr $patchid + 1 2>&3`
  done
done

# write footer of to-be-installed inventory and close it
echo "</ONEOFF_LIST>" 1>&6
echo "</HOME>" 1>&6
exec 6>&-

if "$awk" -f "$tbiinvawk"          \
           -v oh="$ORACLE_HOME"     \
           -v invtype="tbi"         \
           "$tbiinvxml" 1>"$tbiinvsh" 2>&3 &&
           ( . "$tbiinvsh" ) 1>&3 2>&1; then
  :
else
  out "Analyzing patches to process...failed."

  error "Cannot parse and test patch metadata."
fi

# read patch metadata
. "$tbiinvsh" 1>/dev/null 2>&1

# this is a hack: move PSU without prerequisite patches to front.
#
# We need this in the following situation:
#
#   A is a subset of B
#   B conflicts with PSU
#   A and PSU are disjunct
#   B is installed
#   A and PSU are to-be-installed
#
# In that situation, A will not get installed if it comes before
# the PSU in the list of patches to-be-installed.  A generic
# solution to this problem would be challenging to implement ...
if patchids=`pmdfind tbi patchflags "~" catpsu patchprqs = ""`; then
  pmdmv2front tbi $patchids
fi

# PD, DBVD (sunos_sun4u, 10.2.0.2.0): ensure that patch 5117016
# gets installed first
if test "X$platform" = "Xsunos_sun4u" &&
   test "X$rdbmsver" = "X10.2.0.2.0" &&
   test $flag_5117016_check = 1; then
  if patchids=`pmdfind tbi patchno = 5117016`; then
    pmdmv2front tbi $patchids
  elif pmdfind ins patchno = 5117016 1>/dev/null; then
    :
  else
    out "Analyzing patches to process...failed."

    error "Cannot find mandatory patch 5117016."
  fi
fi

# move any OPatch patches to the front of the list.  This must
# come after any other patches were moved to front.
if patchids=`pmdfind tbi patchflags "~" opatch`; then
  pmdmv2front tbi $patchids
fi

if $MOPATCH_TEST_MIGRATE; then
  :
else
out "Analyzing patches to process...done."
fi

verbose
verbose "Operations to be executed:"
for patchid in $pmdtbiids
do
  pmdcache tbi $patchid

  # make sure that patches with invalid basenames may be properly
  # traced back in the log file
  patchbase=""
  if test "X$pmdpatchbase" != "XINVALID"; then
    patchbase=$pmdpatchbase
  else
    patchbase="INVALID_ID_$patchid"
  fi

  patchflaginfo=`pmdflaginfo`
  l_oldifs=$IFS; IFS="&"; set -f; set x $patchflaginfo; shift; set +f; IFS=$l_oldifs
  patchmode=$1
  patchflags=${3-}

  if $MOPATCH_TEST_MIGRATE; then
  verbose "  apply patch $patchbase"
  else
  if test "X$patchflags" != "X"; then
    verbose "  apply patch $patchbase ($patchflags)"
  else
    verbose "  apply patch $patchbase"
  fi
  fi
done
set -f; set x $pmdtbiids; shift; set +f
tbipn=$#


##################################################
#
# more miscellaneous validation and initialization
#
##################################################

# PD, OPVD (sunos_* 5.10, 10.2.*): take care of ar verification
# message
if {
    test "X$platform" = "Xsunos_sun4u" ||
    test "X$platform" = "Xsunos_i86pc"
  } &&
  test "X$kernelver" = "X5.10" &&
  test $flag_sol10_ar_check = 1; then
  wtbis="${wtbis}"'OUI-67124:Verification of '\''ar'\'' actions failed\.'"$NL"
fi

# OPVD: ignore some warnings (it is sure funny to change error
# numbers from release to release!).  Do not ignore warnings
# other than those to detect OPatch internal changes as soon as
# possible in our stress test suite.
if verstest "$opver" -ge "11.2.0.1.1" ||
   verstest "$opver" -ge "11.1.0.6.9" ||
   verstest "$opver" -ge "10.2.0.4.9" ||
   verstest "$opver" -ge "10.2.0.5.0"; then
  wtbis="${wtbis}67620${NL}"                    # resource S_REVISED_BUG_SUPERSET
else
  wtbis="${wtbis}67078${NL}"                    # resource S_BUG_SUPERSET
fi

# OPVD (>= 10.2.0.4.3): create required directory
if verstest -d 0 "$opver" -ge "10.2.0.4.3" &&
   test ! -d "$ORACLE_HOME/ccr_stage"; then
  if mkdir "$ORACLE_HOME/ccr_stage" 1>&3 2>&1; then
    echo "Do not remove this directory.  It is required" 1>>"$ORACLE_HOME/ccr_stage/readme.txt"
    echo "for proper operation of OPatch."               1>>"$ORACLE_HOME/ccr_stage/readme.txt"
  else
    error "Cannot create directory \"$ORACLE_HOME/ccr_stage\"."
  fi
fi

if runmodep "apply"; then
  # create awk script to parse OPatch output
  opopawk="$tmpdir/opoparser.awk"
  cat << 'EOF' 1>"$opopawk" 2>&3 ||
function squote( s, qs, b )
{
qs = "";
b  = 0;
while ( b = index( s, "'" ) ) {
qs = qs substr( s, 1, b - 1 ) "'\\''";
s  = substr( s, b + 1 );
}
return qs s;
}
function zeropad( s )
{
if ( length( s ) <= 10 )
return ZEROES[length( s )] s;
else {
error = 1;
return substr( s, 1, 10 );
}
}
function sort( s, a, v, n, i, j, r )
{
i = 1;
for ( v in s ) a[i++] = v;
n = i - 1;
a[1] = zeropad( a[1] );
for ( i = 2; i <= n; i++ ) {
v = zeropad( a[i] ); j = i - 1;
while ( (j >= 1) && (a[j] > v) ) {
a[j+1] = a[j]; j--;
}
a[j+1] = v;
}
r = "";
for ( i = 1; i <= n; i++ ) {
v = a[i];
sub( /^0+/, "", v );
if ( i < n )
r = r v " ";
else
r = r v;
}
return r;
}
BEGIN {
error     = 0;
state     = "ooa";
logfile   = "INVALID";
for ( i in resources ) delete resource[i];
for ( i in rbps ) delete rbps[i];
for ( i in prereqs ) delete prereqs[i];
for ( i in conflicts ) delete conflicts[i];
prereqsp = 0;
conflictsp = 0;
for ( i in warnings ) delete warnings[i];
for ( i in ZEROES ) delete ZEROES[i];
ZEROES[0]  = "0000000000";
ZEROES[1]  = "000000000";
ZEROES[2]  = "00000000";
ZEROES[3]  = "0000000";
ZEROES[4]  = "000000";
ZEROES[5]  = "00000";
ZEROES[6]  = "0000";
ZEROES[7]  = "000";
ZEROES[8]  = "00";
ZEROES[9]  = "0";
ZEROES[10] = "";
}
/^Log file location +: +/ {
sub( /^Log file location +: +/, "", $0 );
logfile = $0;
next;
}
/^OPatch Session completed with warnings\.$/ {
resources["S_OPATCH_COMPLETED_WITH_WARNINGS"] = 1;
next;
}
/^ApplySession failed during prerequisite checks: / {
resources["S_OPATCH_SESSION_PREREQ_FAIL"] = 1;
next;
}
/^ApplySession system modification phase did not start: / {
resources["S_OPATCH_SESSION_PREP_FAIL"] = 1;
next;
}
/^ApplySession failed: / {
resources["S_OPATCH_SESSION_FAIL"] = 1;
next;
}
/^ApplySession system modification phase did not start: / {
resources["S_OPATCH_APPLY_SESSION_PREP_FAIL"] = 1;
next;
}
/^Apply Session failed: / {
resources["S_OPATCH_APPLY_SESSION_FAIL"] = 1;
next;
}
/^Interim patch [0-9]+ is a subset of the patch\(es\)/ {
resources["S_BUG_SUBSET"] = 1;
next;
}
/^OPatch cannot autorollback a Online patch while applying a regular patch\.$/ {
resources["S_AUTOROLLBACK_ONLINE_ERROR"] = 1;
next;
}
/^OPatch cannot autorollback an online patch while applying a regular patch\.$/ {
resources["S_AUTOROLLBACK_ONLINE_ERROR"] = 1;
next;
}
/^OPatch cannot roll back an online patch while applying a regular patch\.$/ {
resources["S_AUTOROLLBACK_ONLINE_ERROR"] = 1;
next;
}
/^Online patch should not "superset" Regular patch\(es\)\.$/ {
resources["S_ONLINE_NOT_SUPERSET_REGULAR"] = 1;
next;
}
/^OPatch cannot autorollback a regular patch while applying an online patch\.$/ {
resources["S_ONLINE_NOT_SUPERSET_REGULAR"] = 1;
next;
}
/^OPatch cannot roll back a regular patch while applying an online patch\.$/ {
resources["S_ONLINE_NOT_SUPERSET_REGULAR"] = 1;
next;
}
/^System intact, OPatch will not attempt to restore the system$/ {
resources["S_NO_RESTORE"] = 1;
next;
}
/^RollbackSession removing interim patch '[0-9]+' from inventory$/ {
resources["S_REMOVING_FROM_INVENTORY"] = 1;
sub( /^RollbackSession removing interim patch '/, "", $0 );
sub( /' from inventory$/, "", $0 );
rbps[$0] = 1;
next;
}
/^Conflict patches: +[0-9]+(,? +[0-9]+)*$/ {
resources["S_CONFLICT_MESSAGE"] = 1;
sub( /^Conflict patches: +/, "", $0 );
split( $0, l, /,? +/ );
for ( i in l )
conflicts["" l[i]] = 1;
conflictsp = 1;
next;
}
/^Interim patch [0-9]+ conflict with patch\(es\) \[ *[0-9]+( +[0-9]+)* *\] in the Oracle Home$/ {
resources["S_REVISED_CONFLICT"] = 1;
sub( /^Interim patch [0-9]+ conflict with patch\(es\) \[ */, "", $0 );
sub( / *\] in the Oracle Home$/, "", $0 );
split( $0, l, / +/ );
for ( i in l )
conflicts["" l[i]] = 1;
conflictsp = 1;
next;
}
/^Required patch\(es\) \[ ([0-9]+ )+\] for patch '[0-9]+' are not present in the Oracle Home\.$/ {
sub( /^Required patch\(es\) \[ /, "", $0 );
sub( / \] for patch '[0-9]+' are not present in the Oracle Home\.$/, "", $0 );
split( $0, l, / / );
for ( i in l )
prereqs["" l[i]] = 1;
prereqsp = 1;
next;
}
/^The following warnings have occurred during OPatch execution:$/ {
state = "iws";
next;
}
(state == "iws") && /^[0-9]+\) [A-Z][A-Z][A-Z]-[0-9][0-9][0-9][0-9][0-9]:/ {
key   = $0;
sub( /^[0-9]+\) [A-Z][A-Z][A-Z]-/, "", key );
key = substr( key, 1, 5 );
value = $0;
sub( /^[0-9]+\) [A-Z][A-Z][A-Z]-[0-9][0-9][0-9][0-9][0-9]:/, "", value );
if ( key in warnings )
warnings[key] = warnings[key] "\n" value
else
warnings[key] = value;
next;
}
(state == "iws") && /^-+$/ {
state = "ooa";
next;
}
END {
print "opologfile='" squote( logfile ) "'"
status = "";
if ( "S_OPATCH_COMPLETED_WITH_WARNINGS" in resources )
status = "success";
else if ( ("S_OPATCH_SESSION_PREREQ_FAIL" in resources) ||
("S_OPATCH_SESSION_PREP_FAIL" in resources) ||
("S_OPATCH_SESSION_FAIL" in resources) ||
("S_OPATCH_APPLY_SESSION_PREP_FAIL" in resources) ||
("S_OPATCH_APPLY_SESSION_FAIL" in resources) )
status = "error";
else if ( exitval == 0 )
status = "success";
else
status = "error";
if ( status == "error" ) {
if ( ! ("S_NO_RESTORE" in resources) )
status = "fatal";
else if ( "S_BUG_SUBSET" in resources )
status = "subset";
else if ( "S_AUTOROLLBACK_ONLINE_ERROR" in resources )
status = "onlntooffl"
else if ( "S_ONLINE_NOT_SUPERSET_REGULAR" in resources )
status = "offltoonln"
else if ( prereqsp )
status = "prereqs";
else if ( conflictsp )
status = "conflicts";
}
print "opostatus='" status "'";

print "oporbps='" sort( rbps ) "'";
print "opoprereqs='" sort( prereqs ) "'";
print "opoconflicts='" sort( conflicts ) "'";
warnings_s = "";
for ( warning in warnings ) {
print "hash_opowarnings_" warning "='" squote( warnings[warning] ) "'";
warnings_s = warnings_s warning " ";
}
sub( / $/, "", warnings_s );
print "opowarnings='" warnings_s "'";
if ( error != 0 ) {
print "echo 'unexpected error' 1>&2";
print "exit 1;";
}
if ( state != "ooa" ) {
print "echo 'invalid EOF in state " state "' 1>&2";
print "exit 1;";
}
}
EOF
  error "Cannot create awk script \"$opopawk\"."
fi

if runmodep "doc"; then
  # create awk script to parse patch readmes
  rdmpawk="$tmpdir/rdmparser.awk"
  cat << 'EOF' 1>"$rdmpawk" 2>&3 ||
BEGIN {
tkn  = 0;   val  = "";   pos  = 0;
latkn1 = 0; laval1 = ""; lapos1 = 0;
latkn2 = 0; laval2 = ""; lapos2 = 0;
bufcnt = 0;
}
function pushtoken( newtkn, newval, newpos )
{
if ( bufcnt == 0 ) {
tkn = newtkn; val = newval; pos = newpos;
bufcnt++;
return 1;
}
else if ( bufcnt == 1 ) {
latkn1 = newtkn; laval1 = newval; lapos1 = newpos;
bufcnt++;
return 1;
}
else if ( bufcnt == 2 ) {
latkn2 = newtkn; laval2 = newval; lapos2 = newpos;
bufcnt++;
return 0;
}
}
function hastoken()
{
return (bufcnt > 0);
}
function shifttoken()
{
if ( bufcnt == 0 )
return 0;
else if ( bufcnt == 1 ) {
bufcnt--;
return 0;
}
else if ( bufcnt == 2 ) {
tkn = latkn1;
val = laval1;
pos = lapos1;
bufcnt--;
return 1;
}
else if ( bufcnt == 3 ) {
tkn = latkn1; latkn1 = latkn2;
val = laval1; laval1 = laval2;
pos = lapos1; lapos1 = lapos2;
bufcnt--;
return 1
}
}
BEGIN {
cbl  = 0;
cbls = "";
sectype  = "specl";
secid    = "start";
secbeg   = 1;
secend   = 0;
sechsz   = 0;
secknp   = 1;
secbd    = "";
secbdcnt = 0;
for ( i in secbds ) delete secbds[i];
SECTION_TITLE_MAP["readmetxtofpatch"]                  = "title:title";
SECTION_TITLE_MAP["interimpatchforbasebug"]            = "specl:interim";
SECTION_TITLE_MAP["datetimestamp"]                     = "specl:metadata";
SECTION_TITLE_MAP["bugsfixedbythispatch"]              = "specl:bugsfxd";
SECTION_TITLE_MAP["patchspecificfootxtofpatch"]        = "specl:psftop";
SECTION_TITLE_MAP["opatchinformation"]                 = "known:opi";
SECTION_TITLE_MAP["opatchutilityinformation"]          = "known:opi";
SECTION_TITLE_MAP["patchpreinstallsteps"]              = "known:ppis";
SECTION_TITLE_MAP["patchinstallationinstructions"]     = "known:pii";
SECTION_TITLE_MAP["patchinstallationsteps"]            = "known:pis";
SECTION_TITLE_MAP["patchspecialinstructions"]          = "known:psi";
SECTION_TITLE_MAP["patchdeinstallationinstructions"]   = "known:pdii";
SECTION_TITLE_MAP["patchpostinstallinstructions"]      = "known:ppii";
}
function startsec( title, beg, hsz, s )
{
cbl       = 0;
cbls      = "";
if ( title in SECTION_TITLE_MAP ) {
s       = SECTION_TITLE_MAP[title];
sectype = substr( s, 1, 5 );
secid   = substr( s, 7 );
}
else {
sectype = "unknw";
secid   = "unknown";
}
secbeg    = beg;
secend    = 0;
sechsz    = hsz;
secknp    = 1;
secbd     = "";
secbdcnt  = 0;
}
function writesecscreen()
{
print "cat << 'EOF' | rdmscreen " secid " " secbeg " " secend cbls " || rdmnonstdp=1";
for ( i = 0; i < secbdcnt; i++ )
print secbds[i];
if ( secbd != "" )
print secbd;
print "EOF"
}
function writesecdel()
{
print "rdmdel " secbeg " " secend;
}
function writecbldel( nonstdp )
{
if ( cbls != "" ) print "rdmdel" cbls;
if ( nonstdp )    print "rdmnonstdp=1";
if ( nonstdp && rdmstriplog ) {
print "echo \"" secbeg "i\\\\${NL}RDM_NON_STD\" 1>&6"
print "echo \"" secend "a\\\\${NL}RDM_NON_STD\" 1>&6"
}
}
function writesec( end, i )
{
secend = end;
if ( cbl > 1 )
cbls = cbls " " (secend - cbl + 1) " " (secend - 1);
if ( sectype == "specl" ) {
if ( secid == "start" ) {
if ( secend == 0 )
;
else if ( secknp )
writesecdel();
else
writecbldel( 1 );
}
else if ( secid == "metadata" )
writecbldel( ! secknp );
else if ( secid == "bugsfxd" )
writecbldel( ! secknp );
else if ( secknp &&
(secid == "interim") &&
(ENVIRON["MOPATCH_TEST_MIGRATE"] == "true") ) {
print "rdmdel " (secbeg - 1) " " secbeg;
print "rdmdel " (secbeg + 2) " " (secbeg + 2);
}
else if ( secknp )
writesecdel();
else
writecbldel( 1 );
}
else if ( sectype == "title" ) {
if ( (secbdcnt == 0) && (secbd == "") )
writecbldel( 0 );
else {
secbeg += sechsz;
if ( cbl > 0 ) secend -= 1;
writesecscreen();
}
}
else if ( sectype == "known" ) {
if ( (secbdcnt == 0) && (secbd == "") )
writesecdel();
else
writesecscreen();
}
else if ( rdmstriplog ) {
if ( (secbdcnt == 0) && (secbd == "") )
writesecdel();
else
writesecscreen();
}
else {
if ( (secbdcnt == 0) && (secbd == "") )
writesecdel();
else
writecbldel( 1 );
}
}
function parsetoken()
{
if ( (bufcnt == 3) &&
(tkn == TOKEN_UNDER) &&
(latkn1 > TOKEN_NONHDR) &&
(latkn2 == TOKEN_UNDER) ) {
writesec( pos - 1 );
startsec( laval1, pos, 3 );
shifttoken();
shifttoken();
shifttoken();
}
else if ( (bufcnt >= 2) &&
(tkn > TOKEN_NONHDR) &&
(latkn1 == TOKEN_UNDER) ) {
writesec( pos - 1 );
startsec( val, pos, 2 );
shifttoken();
shifttoken();
}
else if ( tkn == TOKEN_EMPTY ) {
cbl++;
shifttoken();
}
else {
if ( cbl > 1 )
cbls = cbls " " (pos - cbl) " " (pos - 2);
cbl = 0;
secbd = secbd val;
if ( length( secbd ) > 80 ) {
secbds[secbdcnt++] = substr( secbd, 1, 80 );
secbd = substr( secbd, 81 );
}
if ( sectype == "specl" ) {
if ( secid == "metadata" ) {
if ( tkn != TOKEN_META )
secknp = 0;
}
else if ( secid == "bugsfxd" ) {
if ( tkn != TOKEN_BUGDSC )
secknp = 0;
}
else
secknp = 0;
}
shifttoken();
}
}
BEGIN {
TOKEN_EMPTY  = 0;
TOKEN_UNDER  = 1;
TOKEN_NONHDR = 2;
TOKEN_BUGDSC = 3;
TOKEN_TEXT   = 4;
TOKEN_META   = 5;
FS = "\n";
}
{
line = $0;
sub( /^#/, "", line );
sub( /^[ \t]+/, "", line );
sub( /[ \t\r]+$/, "", line );
if ( line == "" ) {
newtkn = TOKEN_EMPTY;
newval = "";
}
else if ( line ~ /^-----+$/ ) {
newtkn = TOKEN_UNDER;
newval = "";
}
else if ( line ~ /^=====+$/ ) {
newtkn = TOKEN_UNDER;
newval = "";
}
else {
line = tolower( line );
if ( line ~ /^for +(off|on)line +patch *:$/ )
newtkn = TOKEN_NONHDR;
else if ( line ~ /^[0-9][0-9][0-9][0-9][0-9][0-9]+[ :-]+.*[a-z].*$/ )
newtkn = TOKEN_BUGDSC;
else if ( line ~ /^.*[a-z0-9].* : .*[a-z0-9].*$/ )
newtkn = TOKEN_META;
else
newtkn = TOKEN_TEXT;
if ( ENVIRON["MOPATCH_TEST_MIGRATE"] == "true" )
gsub( /p[0-9][0-9][0-9][0-9][0-9][0-9]+_[0-9][0-9][0-9][0-9]+_[^.]*\.zip/, "patchfile", line );
else
gsub( /p[0-9][0-9][0-9][0-9][0-9][0-9]+_[0-9][0-9][0-9][0-9][0-9]+_[^.]*\.zip/, "patchfile", line );
gsub( /[0-9][0-9][0-9][0-9][0-9][0-9]+/, "patchno", line );
if ( ENVIRON["MOPATCH_TEST_MIGRATE"] == "true" )
sub( /^readme\.txt of .*:$/, "readmetxtofpatch", line );
else
sub( /^readme for .*:$/, "readmetxtofpatch", line );
sub( /^interim +patch +for +base +bugs?:.*$/, "interimpatchforbasebug", line );
sub( /^(date:|march|april|may).* 20[0-9][0-9]$/, "datetimestamp", line );
sub( /^bugs +fixed +by +this +patch:$/, "bugsfixedbythispatch", line );
if ( ENVIRON["MOPATCH_TEST_MIGRATE"] == "true" )
sub( /^patch-specific (pre|post|init)\.txt of .*:$/, "patchspecificfootxtofpatch", line );
else
sub( /^patch-specific information from (pre|post|init)\.txt:$/, "patchspecificfootxtofpatch", line );
gsub( /[^a-z]+/, "", line );
newval = line;
}
if ( pushtoken( newtkn, newval, NR ) ) next;
}
{
parsetoken();
}
END {
while ( hastoken() )
parsetoken();
writesec( NR );
print ":"
}
EOF
  error "Cannot create awk script \"$rdmpawk\"."
fi


###########
#
# main loop
#
###########

patchn=0                                        # patch index

# patches ...
documented=""                                   # ... successfully documented
installed=""                                    # ... successfully installed
installedwarning=""                             # ... installed with warnings
failed=""                                       # ... with installation failed
fatal=""                                        # ... with installation fatally failed
uninstalled=""                                  # ... successfully uninstalled
documentedn=0
installedn=0
installedwarningn=0
failedn=0
fataln=0
uninstalledn=0
documentedf=""

# patches skipped due to ...
conflictskipped=""                              # ... conflicts
prereqskipped=""                                # ... missing prereq patches
nonpatchskipped=""                              # ... bad format
installedskipped=""                             # ... already installed
fatalskipped=""                                 # ... previous fatal errors
signalskipped=""                                # ... user interrupt
conflictskippedn=0
prereqskippedn=0
nonpatchskippedn=0
installedskippedn=0
fatalskippedn=0
signalskippedn=0
nonpatchskippedf=""
installedskippedf=""

# patches with potential post-installation instructions
uninstalledpii=""
installedpii=""
uninstalledpiin=0
installedpiin=0
uninstalledpiif=""
installedpiif=""

# set after each apply either to 1 if the apply was cleaned up or
# to 0 if the apply was not cleaned up
cleanedup=1

# in non-verbose mode, write an empty line only once, otherwise
# write it before each patch
if test $verbosep = 0; then
  rawout
fi

for patchid in $pmdtbiids
do
  # we should not get errors here
  pmdcache tbi $patchid || :

  patchn=`expr $patchn + 1 2>&3`
  patchwtbis=$wtbis

  # make sure that patches with invalid basenames may be properly
  # traced back in the log files
  patchbase=""
  if test "X$pmdpatchbase" != "XINVALID"; then
    patchbase=$pmdpatchbase
  else
    patchbase="INVALID_ID_$patchid"
  fi

  verbose
  out "Processing patch \"$patchbase\"...($patchn of $tbipn)"

  # any of these may equal the plain string "INVALID".  However,
  # functions "addtopatchlist", "perror", and "pskip" handle
  # these cases well, and so do the function from the patch
  # metadata library.  In other words, we do not need to care
  # immediately about "INVALID" patch metadata parts.
  patchsrc=$pmdpatchsrc
  patchmlc=$pmdpatchmlc
  patchno=$pmdpatchno
  patchstamp=$pmdpatchstamp
  patchmakes=$pmdpatchmakes

  if test $signalled = 1; then
    pskip signalskipped "User interrupt."; continue 1
  fi

  # check whether the patch file is actually a file
  if pmdflagset "nonfile"; then
    perror "Cannot process non-file."; continue 1
  fi

  # check whether the patch file is a zip file
  if pmdflagset "nonzip"; then
    perror "Cannot unpack ZIP archive."; continue 1
  fi

  # check for DOS line endings in patch metadata
  if pmdflagset "dos" &&
     test $flag_stress_test = 1; then
    perror "Cannot process DOS line endings in patch metadata."; continue 1
  fi

  # check whether the patch is an OPatch patch
  if pmdflagset "nonop"; then
    pskip nonpatchskipped "Probably not an OPatch patch."; continue 1
  fi

  # check whether the patch is an n-apply patch
  if pmdflagset "napply"; then
    pskip nonpatchskipped "Probably an n-apply or a CRS bundle patch."; continue 1
  fi

  if pmdflagset "genfld"; then
    perror "Cannot determine patch metadata."; continue 1
  fi

  # verify patch source
  if test "X$patchsrc" = "XINVALID"; then
    perror "Cannot check patch file."; continue 1
  fi

  # verify patch molecule
  if test "X$patchmlc" = "XINVALID"; then
    perror "Cannot check patch molecule."; continue 1
  fi

  # verify patch number
  if test "X$patchno" = "XINVALID"; then
    if $MOPATCH_TEST_MIGRATE; then
    case $patchbase in
      p1000023_*_BAD_PATCH_NO_1.zip)
        perror "Cannot extract patch number from \"\"."; continue 1 ;;
      p1000024_*_BAD_PATCH_NO_2.zip)
        perror "Cannot extract patch number from \"1000024${NL}1000024\"."; continue 1 ;;
    esac
    else
    perror "Cannot check patch number."; continue 1
    fi
  fi

  # verify patch stamp
  if test "X$patchstamp" = "XINVALID"; then
    if $MOPATCH_TEST_MIGRATE; then
    case $patchbase in
      p1000025_*_BAD_TIMESTAMP_1.zip)
        perror "Cannot check timestamp line."; continue 1 ;;
      p1000026_*_BAD_TIMESTAMP_2.zip)
        perror "Cannot extract patch timestamp from \"2-Oct-2007-14:47:24${NL}8-Oct-2007-14:49:24\"."; continue 1 ;;
    esac
    else
    perror "Cannot check patch timestamp."; continue 1
    fi
  fi

  # verify make lines
  if $MOPATCH_TEST_MIGRATE; then
  if test "X$patchmakes" = "XINVALID"; then
    perror "Cannot check link lines."; continue 1
  fi
  else
  if test "X$patchmakes" = "XINVALID"; then
    perror "Cannot check make lines."; continue 1
  fi
  fi

  if $MOPATCH_TEST_MIGRATE || test $rdbmsnver -lt 1102000200; then # 11.2.0.2.0
  if pmdflagset "crs"; then
    pskip -f "+" nonpatchskipped "Is a CRS patch."; continue 1
  fi

  if pmdflagset "nonsmpl" &&
     pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "crs" 1>/dev/null; then
    pskip -f "+" nonpatchskipped "Is part of a CRS bundle patch."; continue 1
  fi
  else
  if pmdflagset "gi" && pmdflagclr "rdbms" && test "X$ohtype" = "Xrdbms"; then
    pskip -f "#" nonpatchskipped "Is a Grid Infrastructure patch."; continue 1
  elif pmdflagclr "gi" && test "X$ohtype" = "Xgi"; then
    pskip -f "#" nonpatchskipped "Is an RDBMS patch."; continue 1
  fi
  fi

  if $MOPATCH_TEST_MIGRATE; then
  if test $flag_apply_cpu = 0; then
    if pmdflagset "catcpu"; then
      pskip -f "*" nonpatchskipped "Is a Critical Patch Update (CPU)."; continue 1
    elif pmdflagset "cpumlc" ||
         {
           pmdflagset "nonsmpl" &&
           pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "catcpu" 1>/dev/null
         }; then
      pskip -f "*" nonpatchskipped "Is a molecule of a Critical Patch Update (CPU)."; continue 1
    fi
  fi
  fi

  if pmdflagset "psu" "catpsu" &&
     test $flag_apply_psu = 0; then
    pskip -f "!" nonpatchskipped "Is a Patchset Update (PSU)."; continue 1
  fi

  if $MOPATCH_TEST_MIGRATE; then
  if pmdflagset "dvsp"; then
    pskip -f "-" nonpatchskipped "Is a Database Vault special patch."; continue 1
  fi
  else
  # DBVD (< 11.2.0.1.0): do not automatically install Database
  # Vault special patches
  if test $rdbmsnver -lt 1102000100 &&          # 11.2.0.1.0
     pmdflagset "dvsp"; then
    pskip -f "-" nonpatchskipped "Is a Database Vault special patch."; continue 1
  fi
  fi

  if pmdflagset "hpatch" &&
     test "X$onlnmode" = "X0"; then
    pskip -f "@" nonpatchskipped "Is an online patch."; continue 1
  fi

  if pmdflagclr "hpatch" &&
     test "X$onlnmode" = "X1"; then
    pskip -f "@" nonpatchskipped "Is an offline patch."; continue 1
  fi

  if test $psusp = 0; then
    if pmdflagset "psu"; then
      perror "Cannot install PSU with OPatch version \"$opversion\"."; continue 1
    fi

    if test "X$pmdpatchprs" != "X"; then
      perror "Cannot install patch with prerequisites with OPatch version \"$opver\"."; continue 1
    fi

    if test "X$pmdpatchols" != "X"; then
      perror "Cannot install patch with overlays with OPatch version \"$opver\"."; continue 1
    fi
  fi

  # finally, verify overall patch metadata correctness
  if pmdflagset "inv"; then
    perror "Cannot parse patch metadata."; continue 1
  fi

  # check whether the patch is installed already.  Be careful to
  # compare only patches of identical modes.
  if test $flag_ignore_installed = 0; then
    # look for the correct patch mode
    fop=""
    if pmdflagset "hpatch"; then
      fop="~"
    else
      fop="!~"
    fi

    if patchinvid=`pmdfind ins patchno = "$patchno" \
                               patchflags "$fop" "hpatch"`; then
      set -f; set x $patchinvid; shift; set +f
      if test $# != 1; then
        perror "Cannot uniquely identify patch $patchno."; continue 1
      fi

      # determine timestamp and overlays of installed patch
      if patchinvinfo=`pmdget ins $patchinvid patchstamp patchovls`; then
        :
      else
        perror "Cannot get patch metadata of patch $patchinvid."; continue 2
      fi
      l_oldifs=$IFS; IFS="&"; set -f; set x $patchinvinfo; shift; set +f; IFS=$l_oldifs
      patchinvstamp=$1
      patchinvovls=${2-}

      patchovls=$pmdpatchovls
      if test "X$patchinvovls" = "X$patchovls" &&
         test "X$patchinvstamp" = "X$patchstamp"; then
        pskip installedskipped "Already installed."; continue 1
      elif test "X$patchinvovls" = "X$patchovls"; then
        # compare timestamps and skip installation of older patches
        # d := (patchinvstamp > patchstamp) ? 1 : 0
        d=`echo "0k[sb]sa 1 0 $patchstamp $patchinvstamp>apq" | dc 2>&3`
        if test "X$d" = "X1"; then
          pskip -f "+" installedskipped "Newer patch already installed."; continue 1
        elif test "X$d" != "X0"; then
          perror "Cannot calculate \"$d\" as ($patchinvstamp > $patchstamp) ? 1 : 0."; continue 1
        fi
      elif printf '%s\n%s\n' "$patchovls" "$patchinvovls" | sort -c 1>/dev/null 2>&1; then
        # consider overlays that are lexicographically larger as
        # newer
        pskip -f "+" installedskipped "Newer patch already installed."; continue 1
      fi
    fi
  fi

  if test $fataln != 0; then
    pskip fatalskipped "Previous fatal error."; continue 1
  fi

  # clean up the patch staging directory
  if rm -rf "$patchstage"/* 1>&3 2>&1; then
    :
  else
    perror "Cannot clean up temporary patch staging directory."; continue 1
  fi

  # unpack the patch
  log "executing: \"$unzip\" -qq -d \"$tmpdir\" \"$patchsrc\" \"$patchmlc/*\""
  if "$unzip" -qq -d "$patchstage" "$patchsrc" "$patchmlc/*" 1>&3 2>&1; then
    :
  else
    perror "Cannot unpack patch."; continue 1
  fi

  # check whether we have enough patch storage free space to
  # install the patch.  OPatch has its own check, but that
  # heavily underestimates the required patch storage free space.
  if test $psfsps = 1 &&
     test $psrsps = 1 &&
     test $flag_free_space_check = 1; then
    # d := (psrsp > psfsp) ? 1 : 0
    d=`echo "0k[sb]sa 1 0 $psfsp $psrsp>apq" | dc 2>&3`
    if test "X$d" = "X1"; then
      perror "Cannot allocate ${psrsp}KB on \"$psmp\", only ${psfsp}KB free."; continue 1
    elif test "X$d" != "X0"; then
      perror "Cannot calculate \"$d\" as ($psrsp > $psfsp) ? 1 : 0."; continue 1
    fi
  fi

  # make all copy targets user-writable, if required.  It would
  # be nice to undo these changes after patch installation, but
  # then the (possibly automated) rollback of the patch may fail.
  l_oldifs=$IFS; IFS="$NL"; set -f; set x $pmdpatchfiles; shift; set +f; IFS=$l_oldifs
  for patchfile in ${1+"$@"}
  do
    # replace prefix by Oracle Home
    patchfile="$ORACLE_HOME/"`printf '%s\n' "$patchfile" | sed 's/^%ORACLE_HOME%\///' 2>&3`
    if test -f "$patchfile" &&
       test ! -w "$patchfile"; then
      if runmodep "apply"; then
        out "Adding missing write permissions for patch copy target \"$patchfile\"."
        if chmod u+w "$patchfile" 1>&3 2>&1; then
          :
        else
          perror "Cannot add write permissions for patch copy target \"$patchfile\"."; continue 2
        fi
      else
        out "Would add missing write permissions for patch copy target \"$patchfile\"."
      fi
    fi
  done

  if runmodep "doc"; then
    # update the readme collections.  Write our readme to a
    # temporary file first.
    readmetmp="$tmpdir/readme.txt"
    readmesh="$tmpdir/readme.sh"
    readmesed="$tmpdir/readme.sed"
    if ( exec 6>"$readmetmp" && exec 6>&- ) 1>&3 2>&1 && exec 6>"$readmetmp"; then
      :
    else
      perror "Cannot open temporary readme file \"$readmetmp\" for writing."; continue 1
    fi

    # keep the readme parser in sync with this title
    if $MOPATCH_TEST_MIGRATE; then
    echo "README.txt of $patchbase:" 1>&6
    echo "README.txt of $patchbase:" | sed 's/./=/g' 1>&6 2>&3
    echo 1>&6
    else
    if patchnoboringp; then
      echo "Readme for $patchbase:" 1>&6
      echo "Readme for $patchbase:" | sed 's/./=/g' 1>&6 2>&3
      echo 1>&6
    else
      echo "Readme for $patchbase (patch number $patchno):" 1>&6
      echo "Readme for $patchbase (patch number $patchno):" | sed 's/./=/g' 1>&6 2>&3
      echo 1>&6
    fi
    fi

    docflag=" "
    readmetxt="$patchstage/$patchmlc/README.txt"
    readmehtml="$patchstage/$patchmlc/README.html"
    if test -f "$readmetxt"; then
      if cat "$readmetxt" 1>&6 2>&3; then
        :
      else
        perror "Cannot append file \"README.txt\" to temporary readme."; continue 1
      fi
    elif test -f "$readmehtml"; then
      readmehtmlcoll=""
      if pmdflagset "catpsu"; then
        readmehtmlcoll="$patchdir/README-PSU.html"
      elif pmdflagset "catcpu"; then
        readmehtmlcoll="$patchdir/README-CPU.html"
      elif pmdflagset "sbp"; then
        readmehtmlcoll="$patchdir/README-SBP.html"
      else
        readmehtmlcoll="$patchdir/README-$patchno-$LTIMESTAMP.html"
      fi
      if cp "$readmehtml" "$readmehtmlcoll" 1>&3 2>&1; then
        docflag="*"
        echo "This patch contains a \"README.html\" file.  MOPatch extracted it to" 1>&6
        echo "  $readmehtmlcoll" 1>&6
        echo "Please check that file for installation information." 1>&6
        echo 1>&6
      else
        perror "Cannot extract file \"README.html\" to patch base directory \"$patchdir\"."; continue 1
      fi
    elif pmdflagset "rdmhtml"; then
      docflag="*"
      echo "This patch contains a \"README.html\" file in a non-standard" 1>&6
      echo "location.  Please check it for installation information." 1>&6
      echo 1>&6
    elif pmdflagset "rdmtxt"; then
      docflag="*"
      echo "This patch contains a \"README.txt\" file in a non-standard" 1>&6
      echo "location.  Please check it for installation information." 1>&6
      echo 1>&6
    elif pmdflagset "nonsmpl" &&
         # "patch is not part of an SBP"
         ( pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "sbp" 1>/dev/null && exit 1 || : ) &&
         {
           # prefer readmes from tracking bugs
           readmeids=`pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "trkbug" patchflags "~" "rdmhtml"` ||
           readmeids=`pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "trkbug" patchflags "~" "rdmtxt"` ||
           readmeids=`pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "rdmhtml"` ||
           readmeids=`pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "rdmtxt"`
         }; then
      docflag="-"
      echo "This patch is part of an n-apply patch or a patch bundle.  It" 1>&6
      echo "does not have a README file, but the following patches, that are" 1>&6
      echo "part of the same bundle, have README files:" 1>&6
      echo "" 1>&6
      l_oldifs=$IFS; IFS=" "; set -f; set x $readmeids; shift; set +f; IFS=$l_oldifs
      for readmeid in "$@"
      do
        readmebase=`pmdget tbi $readmeid patchbase`
        printf '  %s\n' "$readmebase" 1>&6
      done
      echo 1>&6
    else
      docflag="*"
      if $MOPATCH_TEST_MIGRATE; then
      logerr "Cannot append file \"README.txt\" to readme collection."
      echo "  Cannot find file \"README.txt\".  Please check patch" 1>&6
      echo "  \"$patchbase\" for other README files." 1>&6
      echo 1>&6
      else
      echo "This patch does not contain a \"README.txt\" file nor a" 1>&6
      echo "\"README.html\" file.  Please check it for other installation" 1>&6
      echo "information." 1>&6
      echo 1>&6
      fi
    fi

    # handle patch-specific documentation
    for psdoc in "pre" "post" "ini"
    do
      psdoctxt="$patchstage/$patchmlc/custom/$psdoc.txt"
      if test -f "$psdoctxt"; then
        # keep the readme parser in sync with this title
        if $MOPATCH_TEST_MIGRATE; then
        echo "Patch-specific $psdoc.txt of $patchbase:" 1>&6
        echo "Patch-specific $psdoc.txt of $patchbase:" | sed 's/./=/g' 1>&6 2>&3
        else
        echo "Patch-Specific Information from $psdoc.txt:" 1>&6
        echo "Patch-Specific Information from $psdoc.txt:" | sed 's/./=/g' 1>&6 2>&3
        fi
        echo 1>&6
        if cat "$psdoctxt" 1>&6 2>&3; then
          echo 1>&6
        else
          docflag="!"
          logerr "Cannot append file \"$psdoctxt\" to temporary readme."
        fi
      fi
    done

    exec 6>&-

    # append temporary readme file to readme collection
    if cat "$readmetmp" 1>&8; then
      :
    else
      perror "Cannot append temporary readme to readme collection."; continue 1
    fi

    # strip temporary readme file and append it to readme
    # collection
    rdmstriplog=""
    if test $flag_readme_strip_log = 1; then
      rdmstriplog=1
    else
      rdmstriplog=0
    fi
    rdmnonstdp=0
    if "$awk" -f "$rdmpawk"                      \
               -v rdmstriplog=$rdmstriplog        \
               "$readmetmp" 1>"$readmesh" 2>&3 &&
       . "$readmesh" 2>&3 6>"$readmesed" &&
       sed -f "$readmesed" "$readmetmp" 1>&9 2>&3; then
      :
    else
      docflag="!"
      logerr "Cannot strip temporary readme and append to stripped readme collection."
      # append unstripped readme as a fallback
      cat "$readmetmp" 1>&9
    fi

    if $MOPATCH_TEST_MIGRATE; then
    addtopatchlist installed
    elif test "X$docflag" != "X "; then
      addtopatchlist -f "$docflag" documented
    elif test $rdmnonstdp = 1; then
      addtopatchlist -f "+" documented
    else
      addtopatchlist documented
    fi

    out "Processing patch \"$patchbase\"...done."
  elif runmodep "apply"; then
    # do it!
    log "executing: ( cd \"$patchstage/$patchmlc\" && \"$opatch\" apply $opatchopts )"
    exitval=""
    applylog="$tmpdir/apply.log"
    if ( cd "$patchstage/$patchmlc" 1>&3 2>&1 &&
         "$opatch" apply $opatchopts ) 1>"$applylog" 2>&1; then
      exitval=0
    else
      exitval=$?
    fi

    cat "$applylog" 1>&3 2>&1

    # parse OPatch output
    if opovars=`cut -b1-1000 "$applylog" 2>&3      |
                "$awk" -f "$opopawk"               \
                        -v opver="$opver"           \
                        -v exitval=$exitval         \
                        -v mode="apply" 2>&3` &&
       ( eval "$opovars" ) 1>&3 2>&1; then
      eval "$opovars"
    else
      perror "Cannot parse OPatch output."; continue 1
    fi

    # try to resolve conflicts
    crforce=""
    if test "X$opostatus" = "Xconflicts"; then
      conflicts=$opoconflicts

      # we have the new patch in $patchno and the old patches
      # in $conflicts.  Now see which has precedence.
      crforce=""
      l_oldifs=$IFS; IFS=", $TAB$NL"; set -f; set x $crrules; shift; set +f; IFS=$l_oldifs
      for crrule in ${1+"$@"}
      do
        if test "X$crrule" = "X"; then
          :
        elif test "X$crrule" = "Xold"; then
          crforce=0
          break
        elif test "X$crrule" = "Xnew"; then
          crforce=1
          break
        elif test "X$crrule" = "Xpsu"; then
          newpsup=""
          if pmdflagset "psu" "catpsu"; then
            newpsup=1
          else
            newpsup=0
          fi

          oldpsup=0
          l_oldifs=$IFS; IFS=" "; set -f; set x $conflicts; shift; set +f; IFS=$l_oldifs
          for conflict in "$@"
          do
            if ( pmdcache ins $conflict && pmdflagset "psu" "catpsu" ); then
              oldpsup=1
              break
            fi
          done

          if test $newpsup = 1 && test $oldpsup = 0; then
            crforce=1
            break
          elif test $newpsup = 0 && test $oldpsup = 1; then
            crforce=0
            break
          fi
        elif test "X$crrule" = "Xcpu"; then
          newcpup=""
          if pmdflagset "catcpu" "cpumlc" ||
             {
               pmdflagset "nonsmpl" &&
               pmdfind tbi patchsrc = "$patchsrc" patchflags "~" "catcpu" 1>/dev/null
             }; then
            newcpup=1
          else
            newcpup=0
          fi

          oldcpup=0
          l_oldifs=$IFS; IFS=" "; set -f; set x $conflicts; shift; set +f; IFS=$l_oldifs
          for conflict in "$@"
          do
            if ( pmdcache ins $conflict && pmdflagset "catcpu" "cpumlc" "cpurlb" ); then
              oldcpup=1
              break
            fi
          done

          if test $newcpup = 1 && test $oldcpup = 0; then
            crforce=1
            break
          elif test $newcpup = 0 && test $oldcpup = 1; then
            crforce=0
            break
          fi
        elif numberp "$crrule"; then
          if test $crrule = $patchno; then
            crforce=1
            break
          elif memberp " " $crrule "$conflicts"; then
            crforce=0
            break
          fi
        fi
      done

      if test "X$crforce" = "X"; then
        crforce=1
      fi

      if test $crforce = 1; then
        # do it again!
        log "executing: ( cd \"$patchstage/$patchmlc\" && \"$opatch\" apply -force $opatchopts )"
        exitval=""
        applylog="$tmpdir/apply.log"
        if ( cd "$patchstage/$patchmlc" 1>&3 2>&1 &&
             "$opatch" apply -force $opatchopts ) 1>"$applylog" 2>&1; then
          exitval=0
        else
          exitval=$?
        fi

        cat "$applylog" 1>&3 2>&1

        # parse OPatch output
        if opovars=`cut -b1-1000 "$applylog" 2>&3      |
                    "$awk" -f "$opopawk"               \
                            -v opver="$opver"           \
                            -v exitval=$exitval         \
                            -v mode="forcedapply" 2>&3` &&
           ( eval "$opovars" ) 1>&3 2>&1; then
          eval "$opovars"
        else
          perror "Cannot parse OPatch output."; continue 1
        fi

        # ignore conflict warnings
        if verstest "$opver" -ge "11.2.0.1.1" ||
           verstest "$opver" -ge "11.1.0.6.9" ||
           verstest "$opver" -ge "10.2.0.4.9" ||
           verstest "$opver" -ge "10.2.0.5.0"; then
          patchwtbis="${patchwtbis}67619${NL}"  # resource S_REVISED_CONFLICT
        else
          patchwtbis="${patchwtbis}67012${NL}"  # resource S_CONFLICT
        fi
      fi
    fi

    # determine und brush up OPatch log file name.  Used in
    # messages only.
    if test -f "$opologfile"; then
      if ohre=`printf '%s\n' "$ORACLE_HOME" | quotemeta -s '/' 2>&3` &&
         oplfn=`echo "$opologfile" |
                sed 's/^'"$ohre"'\//$ORACLE_HOME\//' 2>&3`; then
        opologfile=$oplfn
      fi
    else
      opologfile="<could not determine>"
    fi

    # if the installation was successful, check for patches that
    # have been rolled back due to patch conflict or superset
    # installation
    if test "X$opostatus" = "Xsuccess"; then
      l_oldifs=$IFS; IFS=" "; set -f; set x $oporbps; shift; set +f; IFS=$l_oldifs
      for rbp in ${1+"$@"}
      do
        xmessage=""
        if test "X$crforce" != "X" && test $crforce = 1; then
          xmessage=" (conflicted with $patchbase)"
        else
          xmessage=" (superseeded by $patchbase)"
        fi
        addtopatchlist uninstalled -n $rbp "$xmessage"
        if pmdcpmv -m ins rlb $rbp $rbp; then
          :
        else
          logerr "Cannot mark patch $rbp as rolled back."
        fi

        # determine make lines of rolled back patch and append
        # them to those of the patch just installed
        if rlbmakes=`pmdget rlb $rbp patchmakes`; then
          :
        else
          logerr "Cannot determine make lines of patch $rbp."
        fi
        if test "X$rlbmakes" != "X"; then
          # "$patchmakes" may contain a true newline-list (if it
          # has been initialized from "$pmdpatchmakes"), so be
          # careful to add a final newline in this case
          case $patchmakes in
            ""|*$NL) : ;;
            *)       patchmakes="$patchmakes$NL" ;;
          esac
          patchmakes="$patchmakes$rlbmakes$NL"
        fi

        # determine post-installation instructions for patches
        # that were rolled back as a result of conflict
        # resolution.  For superseeded patches no further
        # post-installation instructions should be required than
        # those given by the superset patch.
        if test "X$crforce" != "X" && test $crforce = 1; then
          if patchflaginfo=`pmdcache rlb $rbp && pmdflaginfo`; then
            :
          else
            logerr "Cannot determine post-installation instructions of patch $rbp."
          fi
          l_oldifs=$IFS; IFS="&"; set -f; set x $patchflaginfo; shift; set +f; IFS=$l_oldifs
          piiflag=$2
          if test "X$piiflag" != "X "; then
            addtopatchlist -f "$piiflag" uninstalledpii -n "$rbp"
          fi
        fi
      done
    fi

    if test "X$opostatus" = "Xerror"; then
      xmessage="$NL    OPatch log file:$NL    $opologfile"
      perror -c opatcherror "Cannot apply patch." "$xmessage"; continue 1
    elif test "X$opostatus" = "Xfatal"; then
      addtopatchlist fatal
      xmessage="$NL    OPatch log file:$NL    $opologfile"
      perror -c opatcherror "Cannot apply patch due to fatal error." "$xmessage"; continue 1
    elif test "X$opostatus" = "Xonlntooffl"; then
      xmessage="$NL    OPatch log file:$NL    $opologfile"
      pskip -f "@" installedskipped "Online patch already installed." "$xmessage"; continue 1
    elif test "X$opostatus" = "Xoffltoonln"; then
      xmessage="$NL    OPatch log file:$NL    $opologfile"
      pskip -f "@" installedskipped "Offline patch already installed." "$xmessage"; continue 1
    elif test "X$opostatus" = "Xsubset"; then
      xmessage="$NL    OPatch log file:$NL    $opologfile"
      pskip -f "*" installedskipped "Superset patch already installed." "$xmessage"; continue 1
    elif test "X$opostatus" = "Xprereqs"; then
      xmessage="$NL    missing prereqs: $opoprereqs$NL    OPatch log file:$NL    $opologfile"
      pskip -c conflicterror prereqskipped "Missing prerequisite patches." "$xmessage"; continue 1
    elif test "X$opostatus" = "Xconflicts"; then
      xmessage="$NL    conflicts with: $opoconflicts$NL    OPatch log file:$NL    $opologfile"
      pskip -c conflicterror conflictskipped "Patch conflicts." "$xmessage"; continue 1
    fi

    # append make lines to link script
    if test $ilink = 0; then
      l_oldifs=$IFS; IFS="$NL"; set -f; set x $patchmakes; shift; set +f; IFS=$l_oldifs
      for patchmake in ${1+"$@"}
      do
        l_oldifs=$IFS; IFS="&"; set -f; set x $patchmake; shift; set +f; IFS=$l_oldifs
        mkd=$1
        mkf=$2
        mkt=$3

        # remove any trailing slashes from the make directory.  The
        # above split ensures that there are no newlines in "$mkd".
        case $mkd in
          */) mkd=`expr "X$mkd" : "X\(.*\)/$" 2>&3` ;;
        esac

        # check whether all make parameters are make targets and,
        # as such, indpendent of each other
        amkpi=1                                   # all make parameters independent
        set -f; set x $mkt; shift; set +f
        for mktitem in "$@"
        do
          if varnamep "$mktitem"; then
            :
          else
            amkpi=0
            break
          fi
        done

        # handle make targets independently of each other if that
        # is possible
        if test $amkpi = 1; then
          set -f; set x $mkt; shift; set +f
        else
          # dummy split
          l_oldifs=$IFS; IFS="&"; set -f; set x $mkt; shift; set +f; IFS=$l_oldifs
        fi

        for mktitem in "$@"
        do
          if test $flag_ignore_makes = 1 &&
             memberp " " "$mktitem" "$mttbis"; then
            :
          else
            # strip prefix from make directory
            mkd=`printf '%s\n' "$mkd" | sed 's/^%ORACLE_HOME%\///' 2>&3`

            printf "$LINKSCRIPTGENFS" \
              "$ORACLE_HOME" "$mkd" "$mkf" "$make" "$mkf" "$mktitem" "$ORACLE_HOME" 1>&4
          fi
        done
      done
    fi

    # handle warnings
    if test "X$opowarnings" = "X"; then
      addtopatchlist installed
    elif test $flag_ignore_warnings = 1; then
      awi=1                                       # all warnings ignored
      l_oldifs=$IFS; IFS=" "; set -f; set x $opowarnings; shift; set +f; IFS=$l_oldifs
      for warning in ${1+"$@"}
      do
        twi=0                                     # this warning ignored
        l_oldifs=$IFS; IFS="$NL"; set -f; set x $patchwtbis; shift; set +f; IFS=$l_oldifs
        for wtbi in ${1+"$@"}
        do
          case $wtbi in
            [0-9][0-9][0-9][0-9][0-9])
              if test "X$warning" = "X$wtbi"; then
                twi=1
                break
              fi ;;
            *)
              eval warningtexts=\${hash_opowarnings_"$warning"-}
              l_oldifs=$IFS; IFS="$NL"; set -f; set x $warningtexts; shift; set +f; IFS=$l_oldifs
              for warningtext in "$@"
              do
                if expr "XOUI-$warning:$warningtext" : "X$wtbi" 1>/dev/null 2>&3; then
                  twi=1
                  break 2
                fi
              done ;;
          esac
        done
        if test $twi = 0; then
          awi=0
          break
        fi
      done

      if test $awi = 1; then
        # all warnings ignored - consider this patch being
        # installed OK
        addtopatchlist installed
      else
        xmessage="$NL    OPatch log file:$NL    $opologfile"
        addtopatchlist installedwarning "$xmessage"
      fi
    else
      xmessage="$NL    OPatch log file:$NL    $opologfile"
      addtopatchlist installedwarning "$xmessage"
    fi

    if pmdcpmv -c tbi ins $patchid $patchno; then
      :
    else
      logerr "Cannot mark patch as installed."
    fi

    # determine post-installation instructions
    if patchflaginfo=`pmdcache ins $patchno && pmdflaginfo`; then
      :
    else
      logerr "Cannot determine post-installation instructions."
    fi
    l_oldifs=$IFS; IFS="&"; set -f; set x $patchflaginfo; shift; set +f; IFS=$l_oldifs
    piiflag=$2
    if test "X$piiflag" != "X "; then
      addtopatchlist -f "$piiflag" installedpii
    fi

    # re-calculate patch storage free space
    if test $psfsps = 1; then
      psfsp=`df $dfopts "$pstf" 2>&3 |
             grep "$DFRE" 2>&3 |
             sed 's/'"$DFSE"'/\1/' 2>&3`
      if test $flag_stress_test = 1 &&
         test $flag_clean_up = 1; then
        out "patch storage free space raw: $psfsp"
      fi
      if numberp "$psfsp"; then
        # we need this "raw" value for later
        ppsfsp=$psfsp

        # take file system reserve into account
        if test $reserve != 0; then
          # d := max( 0, psfsp - reserve )
          d=`echo "0k[0]sa $psfsp $reserve-d0>apq" | dc 2>&3`
          if numberp "$d"; then
            psfsp=$d
          else
            logerr "Cannot calculate \"$d\" as max( 0, $psfsp - $reserve )."

            psfsp=0
            ppsfsp=0
            psfsps=0
          fi
        fi
      else
        logerr "Cannot determine patch storage free space from \"$psfsp\"."

        psfsp=0
        ppsfsp=0
        psfsps=0
      fi

      if test $flag_free_space_log = 1; then
        if test $psfsps = 1; then
          log "patch storage free space: $psfsp KBytes on $psmp"
        else
          log "patch storage free space: <not available>"
        fi
      fi
    fi

    out "Processing patch \"$patchbase\"...done."

    if test $cusp = 1; then
      # determine whether to clean up patch storage
      cleanup=""
      if test $cufr -gt 0; then
        cumod=`expr \( $installedn + $installedwarningn \) % $cufr 2>&3`
        if test $cumod = 0; then
          cleanup=1
        else
          cleanup=0
        fi
      elif test $psfsps = 1 &&
           test $psrsps = 1; then
        # d := (psrsp > psfsp) ? 1 : 0
        d=`echo "0k[sb]sa 1 0 $psfsp $psrsp>apq" | dc 2>&3`
        if test "X$d" = "X1"; then
          cleanup=1
        elif test "X$d" = "X0"; then
          cleanup=0
        else
          logerr "Cannot calculate \"$d\" as ($psrsp > $psfsp) ? 1 : 0."

          cleanup=0
        fi
      else
        cleanup=0
      fi

      # clean up patch storage, if required
      pschmin=""                                  # patch storage change minimum
      pschmax=""                                  # patch storage change maximum
      if test $cleanup = 1 &&
         test $flag_clean_up = 1; then
        verbose
        out "Cleaning up patch storage..."

        # OPVD (10.2.0.2.4): clean up lock file since opatch
        # cleanup may fail to do so
        culog="$tmpdir/cleanup.log"
        log "executing: \"$opatch\" util cleanup $opatchcuopts"
        if "$opatch" util cleanup $opatchcuopts 1>"$culog" 2>&1 &&
           {
             test -s "$ORACLE_HOME/.patch_storage/patch_locked" ||
             rm -f "$ORACLE_HOME/.patch_storage/patch_locked" 1>&3 2>&1
           }; then
          # closing "...done." is logged below
          cat "$culog" 1>&3 2>&1

          # determine patch storage allocated space (in bytes)
          # before and after clean-up
          CURE='^Size of directory ".*" (before|after) cleanup is [0-9][0-9]* bytes\.$'
          CUBSE='^Size of directory ".*" before cleanup is \([0-9][0-9]*\) bytes\.$'
          CUASE='^Size of directory ".*" after cleanup is \([0-9][0-9]*\) bytes\.$'
          psaspprepost=`egrep "$CURE" "$culog" 2>&3 |
                        sed 's/'"$CUBSE"'/\1/;s/'"$CUASE"'/\1/' 2>&3`
          l_oldifs=$IFS; IFS="$NL"; set -f; set x $psaspprepost; shift; set +f; IFS=$l_oldifs
          psasppre=$1
          psasppost=$2

          # determine patch storage change minimum
          if numberp "$psasppre" &&
             numberp "$psasppost"; then
            # d := (psasppre - psasppost) / 2048
            d=`echo "0k $psasppre $psasppost-2048/pq" | dc 2>&3`

            # fail silently if the above calculation failed -
            # should not happen too often, anyway
            if numberp "$d" &&
               test $flag_stress_test = 0; then
              pschmin=$d
            elif test $psrsps = 1; then
              pschmin=$lss
            else
              pschmin=102400
            fi
          else
            logerr "Cannot determine patch storage freed space from \"$psaspprepost\"."

            if test $psrsps = 1; then
              pschmin=$lss
            else
              pschmin=102400
            fi
          fi

          # determine patch storage change maximum
          if test $psrsps = 1; then
            pschmax=$lss
          else
            pschmax=1024
          fi

          if test $flag_stress_test = 1; then
            out "patch storage change minimum: $pschmin"
            out "patch storage change maximum: $pschmax"
          fi

          cleanedup=1
        else
          cat "$culog" 1>&3 2>&1

          out "Cleaning up patch storage...failed."

          logerr -c opatcherror "Cannot clean up patch storage."

          pschmin=0
          pschmax=0
          cleanedup=0
        fi
      else
        pschmin=0
        pschmax=0
        cleanedup=0
      fi

      # re-calculate patch storage free space after cleaning up.
      # This is a bit tricky since some file systems do not
      # immediately report all free blocks to the operating system
      # after unlinking files.  So we first wait for the "df"
      # results to change substantially (at least by pschmin), and
      # then we wait for them to not change too much (at most by
      # pschmax).  This heuristic does not really help if there is
      # other activtiy on the disk.  But timeouts guarantee that
      # the code works in this case, too.
      if test $cleanedup = 1; then
        dfcount=0
        while test $psfsps = 1 &&
              test $dfcount -lt $dfloop; do
          psfsp=`df $dfopts "$pstf" 2>&3 |
                 grep "$DFRE" 2>&3 |
                 sed 's/'"$DFSE"'/\1/' 2>&3`
          if test $flag_stress_test = 1; then
            out "patch storage free space raw: $psfsp"
          fi
          if numberp "$psfsp"; then
            :
          else
            logerr "Cannot determine patch storage free space from \"$psfsp\"."

            psfsp=0
            ppsfsp=0
            psfsps=0

            break
          fi

          # d := abs( psfsp - ppsfsp ) < pschmin ? 1 : 0
          d=`echo "0k[_1*]sa[sc]sb 1 0 $psfsp $ppsfsp-d0>a $pschmin>bpq" | dc 2>&3`
          if test "X$d" = "X0"; then
            ppsfsp=$psfsp
            break
          elif test "X$d" != "X1"; then
            logerr "Cannot calculate \"$d\" as abs( $psfsp - $ppsfsp ) < $pschmin ? 1 : 0."

            psfsp=0
            ppsfsp=0
            psfsps=0

            break
          fi
          sleep $dfslpi 1>&3 2>&1
          dfcount=`expr $dfcount + 1 2>&3`
        done

        dfcount=0
        while test $psfsps = 1 &&
              test $dfcount -lt $dfloop; do
          psfsp=`df $dfopts "$pstf" 2>&3 |
                 grep "$DFRE" 2>&3 |
                 sed 's/'"$DFSE"'/\1/' 2>&3`
          if test $flag_stress_test = 1; then
            out "patch storage free space raw: $psfsp"
          fi
          if numberp "$psfsp"; then
            :
          else
            logerr "Cannot determine patch storage free space from \"$psfsp\"."

            psfsp=0
            ppsfsp=0
            psfsps=0

            break
          fi

          # d := abs( psfsp - ppsfsp ) > pschmax ? 1 : 0
          d=`echo "0k[_1*]sa[sc]sb 1 0 $psfsp $ppsfsp-d0>a $pschmax<bpq" | dc 2>&3`
          if test "X$d" = "X0"; then
            break
          elif test "X$d" != "X1"; then
            logerr "Cannot calculate \"$d\" as abs( $psfsp - $ppsfsp ) > $pschmax ? 1 : 0."

            psfsp=0
            ppsfsp=0
            psfsps=0

            break
          fi
          sleep $dfslpi 1>&3 2>&1
          dfcount=`expr $dfcount + 1 2>&3`
          ppsfsp=$psfsp
        done

        # take file system reserve into account
        if test $psfsps = 1 &&
           test $reserve != 0; then
          # d := max( 0, psfsp - reserve )
          d=`echo "0k[0]sa $psfsp $reserve-d0>apq" | dc 2>&3`
          if numberp "$d"; then
            psfsp=$d
          else
            logerr "Cannot calculate \"$d\" as max( 0, $psfsp - $reserve )."

            psfsp=0
            ppsfsp=0
            psfsps=0
          fi
        fi

        if test $flag_free_space_log = 1; then
          if test $psfsps = 1; then
            log "patch storage free space: $psfsp KBytes on $psmp"
          else
            log "patch storage free space: <not available>"
          fi
        fi

        out "Cleaning up patch storage...done."
      fi
    else
      # clean-up not supported
      cleanedup=0
    fi
  fi
done

# remove leading and trailing newlines
documented=`echo "$documented" | sed 1d 2>&3`
installed=`echo "$installed" | sed 1d 2>&3`
installedwarning=`echo "$installedwarning" | sed 1d 2>&3`
failed=`echo "$failed" | sed 1d 2>&3`
fatal=`echo "$fatal" | sed 1d 2>&3`
uninstalled=`echo "$uninstalled" | sed 1d 2>&3`
conflictskipped=`echo "$conflictskipped" | sed 1d 2>&3`
prereqskipped=`echo "$prereqskipped" | sed 1d 2>&3`
nonpatchskipped=`echo "$nonpatchskipped" | sed 1d 2>&3`
installedskipped=`echo "$installedskipped" | sed 1d 2>&3`
fatalskipped=`echo "$fatalskipped" | sed 1d 2>&3`
signalskipped=`echo "$signalskipped" | sed 1d 2>&3`
uninstalledpii=`echo "$uninstalledpii" | sed 1d 2>&3`
installedpii=`echo "$installedpii" | sed 1d 2>&3`


################################
#
# finally clean up patch storage
#
################################

# flag cleanedup may be 1 either because the previous apply was
# cleaned up or because there was no apply at all.  In the latter
# case, clean up if and only if the link script also would be
# executed, since this is typically the last step in a sequence
# of multiple calls to MOPatch.  This is not entirely consistent
# with the policy of execution of intermediate clean-ups, since
# those are done even in case of previous failed installations.
# But this is just the way it is.
if runmodep "apply" &&
   test $cusp = 1 &&
   {
     test $cleanedup = 0 ||
     {
       test $installedn = 0 &&
       test $installedwarningn = 0 &&
       test -s "$linkscript" &&
       test $conflictskippedn = 0
     }
   } &&
   test $failedn = 0 &&
   test $signalled = 0 &&
   test $flag_clean_up_final = 1; then

  out
  out "Cleaning up patch storage..."

  # OPVD (10.2.0.2.4): clean up lock file since opatch cleanup
  # may fail to do so
  log "executing: \"$opatch\" util cleanup $opatchcuopts"
  if "$opatch" util cleanup $opatchcuopts 1>&3 2>&1 &&
     {
       test -s "$ORACLE_HOME/.patch_storage/patch_locked" ||
       rm -f "$ORACLE_HOME/.patch_storage/patch_locked" 1>&3 2>&1
     }; then
    out "Cleaning up patch storage...done."
  else
    out "Cleaning up patch storage...failed."

    logerr -c opatcherror "Cannot clean up patch storage."
  fi
fi


#####################
#
# process link script
#
#####################

if test $ilink = 0; then
  # close link script
  exec 4>&-

  if runmodep "apply" &&
     test -s "$linkscript" &&
     test $nomake = 0 &&
     test $failedn = 0 &&
     test $conflictskippedn = 0 &&
     test $signalled = 0 &&
     sort "$linkscript" 2>&3 |
     uniq 1>"$tmpdir/linkscript.tmp" 2>&3 &&
     mv "$tmpdir/linkscript.tmp" "$linkscript" 1>&3 2>&1; then

    out
    out "Executing link script \"$linkscript\"..."

    log "executing: /bin/sh \"$linkscript\""
    if \
       /bin/sh "$linkscript" 1>&3 2>&1; then

      out "Executing link script \"$linkscript\"...done."

      if rm -f "$linkscript" 1>&3 2>&1; then
        :
      else
        logerr "Cannot clean up link script \"$linkscript\"."
      fi
      linkfailed=0
    else

      out "Executing link script \"$linkscript\"...failed."

      logerr -c linkerror "Cannot execute link script."
      linkfailed=1
    fi
  elif runmodep "apply" &&
       test -s "$linkscript" &&
       test $nomake = 0 &&
       test $failedn = 0 &&
       test $conflictskippedn = 0 &&
       test $signalled = 0; then
    # link script preparation failed.  Mark as internal error but
    # otherwise be silent about it (should not happen, anyway).
    internalerror=1
    linkfailed=2
  elif runmodep "apply" &&
       test -s "$linkscript"; then
    # prepare link script for manual execution if user demanded
    # that or in case of errors, conflicts, or user interrupt
    out
    out "Preparing link script \"$linkscript\"..."

    if sort "$linkscript" 2>&3 |
       uniq 1>"$tmpdir/linkscript.tmp" 2>&3 &&
       mv "$tmpdir/linkscript.tmp" "$linkscript" 1>&3 2>&1; then

      out "Preparing link script \"$linkscript\"...done."
    else

      out "Preparing link script \"$linkscript\"...failed."

      logerr "Cannot prepare link script for manual execution."
    fi
    linkfailed=2
  else
    # link script empty or running in documentation mode
    linkfailed=2
  fi
fi


##############################
#
# get post-run patch inventory
#
##############################

out
out "Getting post-run patch inventory..."

# determine installed patches
ips=""
ipsvp=""                                        # installed patches valid
postinvlog="$tmpdir/postinv.log"
log "executing: \"$opatch\" lsinventory $opatchlsiopts"
if "$opatch" lsinventory $opatchlsiopts 1>"$postinvlog" 2>&1; then
  cat "$postinvlog" 1>&3 2>&1

  ips=`egrep "$PATCHINVEE" "$postinvlog" 2>&3 |
       sed 's/'"$PATCHINVSE"'/\1/;s/'"$PSUINVSE"'/\1/;s/'"$PTCHONLNINVSE"'/\1/' 2>&3 |
       sort -n 2>&3`
  ipsvp=1
else
  cat "$postinvlog" 1>&3 2>&1

  out "Getting post-run patch inventory...failed."

  logerr -c opatcherror "Cannot get post-run patch inventory."
  ips="INVALID"
  ipsvp=0
fi

# verify installed patches
if test $ipsvp = 1; then
  l_oldifs=$IFS; IFS="$NL"; set -f; set x $ips; shift; set +f; IFS=$l_oldifs
  for patchid in ${1+"$@"}
  do
    if memberp " " $patchid "$pmdinsids"; then
      :
    else
      logerr "Cannot verify installed patches ($patchid missing)."
      ipsvp=0
      break
    fi
  done
fi
if test $ipsvp = 1; then
  l_oldifs=$IFS; IFS=" "; set -f; set x $pmdinsids; shift; set +f; IFS=$l_oldifs
  for patchid in ${1+"$@"}
  do
    if memberp "$NL" $patchid "$ips"; then
      :
    else
      logerr "Cannot verify installed patches ($patchid unexpected)."
      ipsvp=0
      break
    fi
  done
fi

if test "X$ips" != "XINVALID"; then
  out "Getting post-run patch inventory...done."
else
  out "Getting post-run patch inventory...failed."
fi


#################################
#
# post-process readme collections
#
#################################

if runmodep "doc"; then
  # close readme collections
  exec 9>&-
  exec 8>&-

  if test ! -s "$readmecoll" ||
     {
       readmecolltmp="$tmpdir/readmes.txt" &&
       readmescollb=`basenamef "$readmescoll" 2>&3` &&
       rdmheader "$DATE" "$readmescollb" 1>>"$readmecolltmp" 2>&3 &&
       sed 's/^#//' "$readmecoll" 1>>"$readmecolltmp" 2>&3 &&
       rdmfooter 1>>"$readmecolltmp" 2>&3 &&
       mv "$readmecolltmp" "$readmecoll" 1>&3 2>&1
     }; then
    :
  else
    logerr "Cannot post-process readme collection \"$readmecoll\"."
  fi

  if test ! -s "$readmescoll" ||
     {
       readmescolltmp="$tmpdir/readmes.txt" &&
       readmecollb=`basenamef "$readmecoll" 2>&3`
       rdmsheader "$DATE" "$readmecollb" 1>>"$readmescolltmp" 2>&3 &&
       sed 's/^#//' "$readmescoll" 1>>"$readmescolltmp" 2>&3 &&
       rdmsfooter 1>>"$readmescolltmp" 2>&3 &&
       mv "$readmescolltmp" "$readmescoll" 1>&3 2>&1
     }; then
    :
  else
    logerr "Cannot post-process stripped readme collection \"$readmecoll\"."
  fi
fi


###############
#
# write summary
#
###############

# patch installation or check status
out
if runmodep "apply"; then
  out "Patch Installation Status:"
  out "--------------------------"
else
  if $MOPATCH_TEST_MIGRATE; then
  out "Patch Check Status:"
  out "-------------------"
  else
  out "Patch Documentation Status:"
  out "---------------------------"
  fi
fi

# failed patches.  Use a maximum line width of 65 characters in
# this and all following messages.
if test $failedn != 0; then
  if runmodep "apply"; then
    out
    out "Failed to install $failedn of $tbipn patches:"
    out "$failed"
  else
    if $MOPATCH_TEST_MIGRATE; then
    out
    out "Failed to check $failedn of $tbipn patches:"
    out "$failed"
    else
    out
    out "Failed to document $failedn of $tbipn patches:"
    out "$failed"
    fi
  fi
fi

# fatally failed patches
if test $fataln != 0; then
  out
  out "Of the patches mentioned above, patch"
  out "$fatal"
  out "failed to install since OPatch experienced a fatal error.  Please"
  out "refer to the corresponding OPatch log file to see whether OPatch"
  out "was able to completely restore the Oracle Home to a consistent"
  out "state."
  out
  out "If OPatch was not able to restore the Oracle Home, follow the"
  out "instructions provided in the OPatch log file to manually restore"
  out "the Oracle Home."
  out
  out "DO NOT TRY TO RUN OPATCH OR MOPATCH AGAIN BEFORE THE ORACLE HOME"
  out "WAS COMPLETELY RESTORED, AS THIS MAY RESULT IN AN INCONSISTENT"
  out "ORACLE HOME, WHICH CANNOT BE RESTORED TO A CONSISTENT STATE."
  out "RESOLVE THE FATAL ERROR FIRST BEFORE TRYING TO RESOLVE ANY OTHER,"
  out "NON-FATAL ERRORS OR PATCH CONFLICTS."
  out
  out "Contact Oracle or SAP Support if in doubt whether your Oracle"
  out "Home is in a consistent state."
elif test $failedn != 0; then
  out
  out "Please check any OPatch log files mentioned above and log file"
  out "  $logfile"
  out "for details.  Resolve the problems and re-run MOPatch."
fi

# skipped patches (previous fatal errors)
if test $fatalskippedn != 0; then
  out
  out "Skipped $fatalskippedn of $tbipn patches due to the above fatal error:"
  out "$fatalskipped"
fi

# skipped patches (user interrupt)
if test $signalskippedn != 0; then
  out
  out "Skipped $signalskippedn of $tbipn patches due to user interrupt:"
  out "$signalskipped"
  out
  out "Re-run MOPatch to install the patches."
fi

# skipped patches (conflicts)
if test $conflictskippedn != 0; then
  out
  out "Skipped $conflictskippedn of $tbipn patches due to patch conflicts:"
  out "$conflictskipped"
  out
  out "Please check the OPatch log files mentioned above and log file"
  out "  $logfile"
  out "for details.  Resolve the conflicts.  Install the patches"
  out "manually or re-run MOPatch."
fi

# skipped patched (missing prereq patches)
if test $prereqskippedn != 0; then
  out
  out "Skipped $prereqskippedn of $tbipn patches due to missing prerequisite patches:"
  out "$prereqskipped"
  out
  out "Please check the OPatch log files mentioned above and log file"
  out "  $logfile"
  out "for details.  Provide the missing prerequisite patches.  Install"
  out "them and the skipped patches manually or re-run MOPatch."
fi

# skipped patches (bad format)
if $MOPATCH_TEST_MIGRATE; then
if test $nonpatchskippedn != 0; then
  out
  out "Skipped $nonpatchskippedn of $tbipn patches since they do not seem to be plain OPatch"
  out "patches:"
  out "$nonpatchskipped"
fi
else
if test $nonpatchskippedn != 0; then
  out
  out "Skipped $nonpatchskippedn of $tbipn patches since they do not seem to be plain OPatch"
  out "patches or since they are otherwise not appropriate for installing:"
  out "$nonpatchskipped"
fi
fi
if test "X$nonpatchskippedf" != "X"; then
  out
fi
if $MOPATCH_TEST_MIGRATE; then
case $nonpatchskippedf in
  *"*"*)
    out "Patches marked with a \"*\" were skipped since they are Critical"
    out "Patch Updates (CPUs).  CPUs require special attention and, hence,"
    out "are not automatically installed by MOPatch.  Refer to SAP note"
    out "850306 for more information." ;;
esac
else
case $nonpatchskippedf in
  *"*"*)
    out "Patches marked with a \"*\" were skipped since they are Critical"
    out "Patch Updates (CPUs) or parts of a CPU and flag \"apply_cpu\" was"
    out "disabled."
esac
case $nonpatchskippedf in
  *"!"*)
    out "Patches marked with a \"!\" were skipped since they are Patchset"
    out "Updates (PSUs) and flag \"apply_cpu\" was disabled." ;;
esac
fi
if $MOPATCH_TEST_MIGRATE; then
case $nonpatchskippedf in
  *"+"*)
    out "Patches marked with a \"+\" were skipped since they are CRS"
    out "patches.  CRS patches must not be installed in an RDBMS home, but"
    out "in a CRS home." ;;
esac
else
case $nonpatchskippedf in
  *"+"*)
    out "Patches marked with a \"+\" were skipped since they are CRS patches"
    out "or part of a CRS bundle patch.  CRS patches must not be installed"
    out "in an RDBMS home, but in a CRS home." ;;
esac
fi
case $nonpatchskippedf in
  *"-"*)
    out "Patches marked with a \"-\" were skipped since they are Database"
    out "Vault special patches.  These require a special installation"
    out "procedure.  See SAP note 1020937." ;;
esac
if $MOPATCH_TEST_MIGRATE; then
  :
else
case $nonpatchskippedf in
  *"@"*)
    if test $onlnmode = 1; then
      out "Patches marked with a \"@\" were skipped since they are offline"
      out "patches and MOPatch is running in online mode."
    else
      out "Patches marked with a \"@\" were skipped since they are online"
      out "patches and MOPatch is running in offline mode.  For each online"
      out "patch there is a usually a corresponding offline patch that fixes"
      out "the same problem.  Check whether the corresponding offline"
      out "patches were successfully installed."
    fi ;;
esac
case $nonpatchskippedf in
  *"#"*)
    if test "X$ohtype" = "Xgi"; then
      out "Patches marked with a \"#\" were skipped since they are RDBMS"
      out "patches and the current Oracle Home is a Grid Infrastructure"
      out "Home."
    else
      out "Patches marked with a \"#\" were skipped since they are Grid"
      out "Infrastructure patches and the current Oracle Home is an RDBMS"
      out "Home."
    fi ;;
esac
fi
if test $nonpatchskippedn != 0; then
  out
  out "Please check the listed patches and, if required, install them"
  out "manually according to their installation instructions."
fi

# skipped patches (already installed)
if test $installedskippedn != 0; then
  out
  out "Skipped $installedskippedn of $tbipn patches since they were already installed:"
  out "$installedskipped"
fi
if test "X$installedskippedf" != "X"; then
  out
fi
case $installedskippedf in
  *"*"*)
    out "Patches marked with a \"*\" were skipped since a superset patch"
    out "comprising them was already present.  Refer to the OPatch log"
    out "files mentioned above for information on the superset patches."
esac
case $installedskippedf in
  *"+"*)
    out "Patches marked with a \"+\" were skipped since a patch with the"
    out "same patch ID but with a newer creation date was already present."
esac
if $MOPATCH_TEST_MIGRATE; then
  :
else
case $installedskippedf in
  *"@"*)
    if test $onlnmode = 1; then
      out "Patches marked with a \"@\" were skipped since an offline patch"
      out "with the same patch ID is already present."
    else
      out "Patches marked with a \"@\" were skipped since an online patch"
      out "with the same patch ID is already present."
    fi ;;
esac
fi

# installed patches
if test $installedn != 0; then
  if runmodep "apply"; then
    out
    out "Installed $installedn of $tbipn patches successfully:"
    out "$installed"
  elif $MOPATCH_TEST_MIGRATE; then
    out
    out "Checked $installedn of $tbipn patches successfully:"
    out "$installed"
    out
    out "Please refer to the READMEs generated in"
    out "  $readmescoll"
    out "for patch-specific installation instructions before continuing"
    out "patch installation."
  fi
fi

if $MOPATCH_TEST_MIGRATE; then
  :
else
if test $documentedn != 0; then
  out
  out "Checked $documentedn of $tbipn patches successfully:"
  out "$documented"
  if test "X$documentedf" != "X"; then
    out
    out "All specially marked patches in the list above contain non-"
    out "standard documentation and, hence, may require special"
    out "installation instructions.  Please refer to the READMEs"
    out "collected in"
    out "  $readmescoll"
    out "for more information."
  fi
fi
fi

# installed patches with warnings
if test $installedwarningn != 0; then
  out
  out "Installed $installedwarningn of $tbipn patches with warnings:"
  out "$installedwarning"
  out
  out "Please check the OPatch log files mentioned above or log file"
  out "  $logfile"
  out "for details."
fi

if $MOPATCH_TEST_MIGRATE; then
# uninstalled patches
if test $uninstalledn = 1; then
  out
  out "Uninstalled 1 patch since it was superseeded by a newer patch:"
  out "$uninstalled"
elif test $uninstalledn != 0; then
  out
  out "Uninstalled $uninstalledn patches since they were superseeded by newer"
  out "patches:"
  out "$uninstalled"
fi
else
# uninstalled patches
if test $uninstalledn = 1; then
  out
  out "Uninstalled 1 patch since it was superseeded by or conflicted"
  out "with a newly installed patch:"
  out "$uninstalled"
elif test $uninstalledn != 0; then
  out
  out "Uninstalled $uninstalledn patches since they were superseeded by or"
  out "conflicted with newly installed patches:"
  out "$uninstalled"
fi
fi

# patch inventory status
out
out "Patch Inventory Status:"
out "-----------------------"
if test "X$ips" = "X"; then
  out "No patches installed in Oracle Home $ORACLE_HOME."
elif test "X$ips" = "XINVALID"; then
  out "Patches installed in Oracle Home $ORACLE_HOME:"
  out "  <not available>"
else
  out "Patches installed in Oracle Home $ORACLE_HOME:"
  l_oldifs=$IFS; IFS="$NL"; set -f; set x $ips; shift; set +f; IFS=$l_oldifs
  for patchid in "$@"
  do
    if $MOPATCH_TEST_MIGRATE; then
      out "  $patchid"
    else
    if pmdcache ins $patchid; then
      patchflaginfo=`pmdflaginfo`
      l_oldifs=$IFS; IFS="&"; set -f; set x $patchflaginfo; shift; set +f; IFS=$l_oldifs
      patchmode=$1
      patchflags=${3-}

      if test "X$patchflags" != "X"; then
        out "$patchmode $pmdpatchno ($patchflags)"
      else
        out "$patchmode $pmdpatchno"
      fi
    else
      out "  $patchid"
    fi
    fi
  done
fi

# link status
if test $ilink = 0; then
  out
  out "Link Status:"
  out "------------"
  if test $linkfailed = 0; then
    out "Link script \"$linkscript\" executed successfully."
  elif test $linkfailed = 1; then
    out "Failed to execute link script.  Please check the log file"
    out "  $logfile"
    out "for details.  Resolve the problems.  Re-start MOPatch to execute"
    out "the link script or execute it manually as"
    out "  /bin/sh \"$linkscript\""
  elif test $linkfailed = 2 && runmodep "doc"; then
    if $MOPATCH_TEST_MIGRATE; then
    out "Link script \"$linkscript\" not executed in dry-run mode."
    else
    out "Link script \"$linkscript\" not executed in documentation mode."
    fi
  elif test $linkfailed = 2 && test ! -s "$linkscript"; then
    out "Link script \"$linkscript\" empty, not executed."
  elif test $linkfailed = 2 && test $nomake = 1; then
    out "Link script not executed due to option \"-n\" specified to"
    out "MOPatch.  Please execute the link script manually as"
    out "  /bin/sh \"$linkscript\""
  elif test $linkfailed = 2 && test $failedn != 0; then
    out "Link script not executed due to patch installation failures."
    out "Please check the log file"
    out "  $logfile"
    out "for details.  Resolve the problems.  Re-start MOPatch to execute"
    out "the link script or execute it manually as"
    out "  /bin/sh \"$linkscript\""
  elif test $linkfailed = 2 && test $conflictskippedn != 0; then
    out "Link script not executed due to patch conflicts.  Please check"
    out "the log file"
    out "  $logfile"
    out "for details.  Resolve the conflicts as described in SAP note"
    out "1027012.  Re-start MOPatch to execute the link script or execute"
    out "it manually as"
    out "  /bin/sh \"$linkscript\""
  elif test $linkfailed = 2 && test $signalled != 0; then
    out "Link script not executed due to user interrupt."
    out "Re-start MOPatch to execute the link script or execute it"
    out "manually as"
    out "  /bin/sh \"$linkscript\""
  elif test $linkfailed = 2; then
    out "Link script \"$linkscript\" not executed due to script"
    out "preparation failures."
  else
    logerr "Internal error: Invalid link script status \"$linkfailed\"."
  fi
fi


########################
#
# miscellaneous clean-up
#
########################

# close log file
logerrhdl=2
logopen=0
if test "X$xlogfile" = "X"; then
  exec 3>&-
fi

exit `exitvalf`
