#!/usr/bin/ksh 

# ident	"@(#)dcdb.sh	1.4	04/04/08 SMI"

#
# Copyright (c) 2004 Sun Microsystems, Inc.  All Rights Reserved.
# Use is subject to license terms
#

# This is the format of the flat database
# <pkgid>/pkginfo|XXXXXX-XX|obs1 ..|req1 ..|incompat1 ..|OSrelease1 ..|arch|type
#
# for example:
#
# SUNWabc/pkginfo|123456-01|122222-01 133333-03|144444-04|155555-05|7|sparc|root
#
# This means patch 123456-01 is a root patch that applies to SUNWabc on a
# Solaris 7 sparc system, obsoletes 122222-01 and 133333-03, requires 144444-04
# to be installed first and is incompatible with patch 155555-05.

typeset -r INSTALLED_COLUMN=2
typeset -r OBSOLETE_COLUMN=3
typeset -r REQUIRED_COLUMN=4
typeset -r INCOMPAT_COLUMN=5
typeset -r OSVERSIONS_COLUMN=6
typeset -r ARCH_COLUMN=7
typeset -r TYPE_COLUMN=8

Protect() {
        typeset -r opts=$@
        (
                $opts || return $?
                return 0
        ) || return $?
        return 0
}

Assertion() {
        typeset -r error=$1
        shift 1
        typeset -r message=$@
        PrintErr "$message"
        exit $error
}

_sql_do_where() {
        [ "$OPT_DEBUG" == 'true' ] && set -x

        typeset -r flatdb=$1
        shift 1
        typeset -r column=$1
        typeset -r op=$2
        typeset -r match=$3

        typeset pattern=
        if   [ "$column" == "$INSTALLED_COLUMN" ] ; then
                pattern='.*|.*'"${match%-*}"'.*|.*|.*|.*|.*|.*|.*'
        elif [ "$column" == "$OBSOLETE_COLUMN" ] ; then
                pattern='.*|.*|.*'"${match%-*}"'.*|.*|.*|.*|.*|.*'
        elif [ "$column" == "$REQUIRED_COLUMN" ] ; then
                pattern='.*|.*|.*|.*'"${match%-*}"'.*|.*|.*|.*|.*'
        elif [ "$column" == "$INCOMPAT_COLUMN" ] ; then
                pattern='.*|.*|.*|.*|.*'"${match%-*}"'.*|.*|.*|.*'
        elif [ "$column" == "$OSVERSIONS_COLUMN" ] ; then
                pattern='.*|.*|.*|.*|.*|.*'"$match"'.*|.*|.*'
        elif [ "$column" == "$ARCH_COLUMN" ] ; then
                pattern='.*|.*|.*|.*|.*|.*|.*'"$match"'.*|.*'
	elif [ "$column" == "$TYPE_COLUMN" ] ; then
                pattern='.*|.*|.*|.*|.*|.*|.*|.*'"$match"'.*'	
        else
                PrintErr "Invalid column type: $column"
                return 1
        fi

        typeset line=
        grep "$pattern" $flatdb | while read line ; do
                typeset matches=$(echo $line | cut -d'|' -f$column)
                typeset item=
                for item in $matches ; do
                        typeset base=${item%-*}
                        [ "$base" != "${match%-*}" ] && continue
                        case $op in
                                '>=')   [[ $item == $match ]] && echo $line
                                        [[ $item > $match ]] && echo $line ;;
                                '<=')   [[ $item == $match ]] && echo $line
                                        [[ $item < $match ]] && echo $line ;;
                                '==')   [[ $item == $match ]] && echo $line ;;
                                '=')    [[ $item == $match ]] && echo $line ;;
                                '>')    [[ $item > $match ]] && echo $line ;;
                                '<')    [[ $item < $match ]] && echo $line ;;
				'cb')   [[ $base == ${match%-*} ]] \
						&& echo $line ;;
                                *)      PrintErr "Invalid operator '$op' in SQL"
                                        return 1
                        esac
                done # for
        done # while
}

_sql_do_select() {
        [ "$OPT_DEBUG" == 'true' ] && set -x

        typeset -r select=$@
        if [ "$select" == 'all' ] ; then
                cat -
        else
                typeset option=''
                typeset column=
                for column in $select ; do
                        if [ -z "$option" ] ; then
                                option="-f$column"
                        else
                                option="$option,$column"
                        fi
                done
                cut -d'|' $option
        fi
        return 0
}

_UNSAFE_RequiredPatchExists() {

        [ "$OPT_DEBUG" == 'true' ] && set -x

        typeset -r patchpath=$1
        typeset -r flatdb=$2
        typeset -r flatdb_alt=$3

        cd $patchpath           || Assertion $? "Failed to change dir"

        typeset pkginfo=
        typeset patchlist=''

        for pkginfo in */pkginfo ; do
                patchlist="$patchlist $(GetPkgValue $pkginfo 'REQUIRES')"
        done

        # If zero length, none of the sparse pkgs are depending on other patch.
        [ -z "$patchlist" ] && return 0

        typeset rpatch=
        for rpatch in $patchlist ; do
                if DB SameOrNewerVersionOfPatchExists $rpatch $flatdb ; then
                        : # Do nothing here
                elif DB PatchIsObsoletedByAnExistingPatch $rpatch $flatdb; then
                        : # Do nothing here
                else

	                if DB SameOrNewerVersionOfPatchExists $rpatch $flatdb_alt ; then
			        : # Do nothing here
		        elif DB PatchIsObsoletedByAnExistingPatch $rpatch $flatdb_alt; then
				: # Do nothing here
			else
				return 1
			fi
                fi
        done

        return 0
}

_UNSAFE_IncompatiblePatchExists() {

        [ "$OPT_DEBUG" == 'true' ] && set -x

        typeset -r patchpath=$1
        typeset -r flatdb=$2
	typeset -r patchid=$(basename $patchpath)

        typeset patches=
        patches=$(DB SQL SELECT all     \
        	FROM $flatdb            \
                WHERE $INCOMPAT_COLUMN '<=' $patchid)
        [ -n "$patches" ] && return 0

        cd $patchpath           || Assertion $? "Failed to change dir"

        typeset pkginfo=
        typeset patchlist=''

        for pkginfo in */pkginfo ; do
                patchlist="$patchlist $(GetPkgValue $pkginfo 'INCOMPAT')"
        done

        # If zero length, the patch does not conflict with other patches
        [ -z "$patchlist" ] && return 1

        typeset ipatch=
        for ipatch in $patchlist ; do
                if DB SameOrNewerVersionOfPatchExists $ipatch $flatdb ; then
                        return 0
                elif DB PatchIsObsoletedByAnExistingPatch $ipatch $flatdb; then
                        return 0
                else
                        : # Do nothing here
                fi
        done

        return 1
}

DB() {
        [ "$OPT_DEBUG" == 'true' ] && set -x

        SQL()  {
                [ "$OPT_DEBUG" == 'true' ] && set -x

                if [[ "$@" != SELECT*FROM*WHERE* ]] ; then
                        echo "Incorrect query syntax"
                        return 1
                fi

                typeset -r symbol='SELECT \(.*\) FROM \(.*\) WHERE \(.*\)'
                typeset -r select=$(echo "$@" | sed -e 's:'"$symbol"':\1:')
                typeset -r flatdb=$(echo "$@" | sed -e 's:'"$symbol"':\2:')
                where=$(echo "$@" | sed -e 's:'"$symbol"':\3:')

                _sql_do_where $flatdb $where | _sql_do_select $select

                return 0
        }

	_NamesOfPatches(){
                [ "$OPT_DEBUG" == 'true' ] && set -x

                typeset -r op="$1"
                shift 1
                typeset -r patchid=$1
                typeset -r flatdb=$2

                typeset patches=
                patches=$(DB SQL SELECT $INSTALLED_COLUMN       \
                        FROM $flatdb                            \
                        WHERE $INSTALLED_COLUMN "$op" $patchid)
		echo $patches 
	}

        _Check_VersionOfPatchExists() {
                [ "$OPT_DEBUG" == 'true' ] && set -x
		typeset -r patches=$(_NamesOfPatches $@)

                [ -z "$patches" ] && return 1 || return 0
        }

	NamesOfOlderVersionsOfPatch(){
		[ "$OPT_DEBUG" == 'true' ] && set -x
		typeset -r names=$(_NamesOfPatches '<' $@)
		echo $names | tr " ," "\012\012" | sort | uniq | tr "\012" " "
	}

        RequiredPatchExists() {
                Protect _UNSAFE_RequiredPatchExists $@
                return $?
        }

        IncompatiblePatchExists() {
                Protect _UNSAFE_IncompatiblePatchExists $@
                return $?
        }

        OlderVersionOfPatchExists() {
                [ "$OPT_DEBUG" == 'true' ] && set -x
                _Check_VersionOfPatchExists '<' $@
                return $?
        }

        NewerVersionOfPatchExists() {
                [ "$OPT_DEBUG" == 'true' ] && set -x
                _Check_VersionOfPatchExists '>' $@
                return $?
        }

        SameVersionOfPatchExists() {
                [ "$OPT_DEBUG" == 'true' ] && set -x
                _Check_VersionOfPatchExists '==' $@
                return $?
        }

	SameOrNewerVersionOfPatchExists() {
		[ "$OPT_DEBUG" == 'true' ] && set -x
                _Check_VersionOfPatchExists '>=' $@
                return $?
        }

        PatchIsObsoletedByAnExistingPatch() {
                [ "$OPT_DEBUG" == 'true' ] && set -x

                typeset -r patchid=$1
                typeset -r flatdb=$2

                typeset patches=
                patches=$(DB SQL SELECT all     \
                        FROM $flatdb            \
                        WHERE $OBSOLETE_COLUMN 'cb' $patchid)

                [ ! -z "$patches" ] && return 0
        }

	PatchIsRequiredByAnExistingPatch() {
                [ "$OPT_DEBUG" == 'true' ] && set -x

                typeset -r patchid=$1
                typeset -r flatdb=$2

                typeset patches=
                patches=$(DB SQL SELECT all     \
                        FROM $flatdb            \
                        WHERE $REQUIRED_COLUMN '<=' $patchid)

                [ -n "$patches" ] && return 0 || return 1

        }

        PatchesRequiredByThisPatch() {
                [ "$OPT_DEBUG" == 'true' ] && set -x

                typeset -r patchid=$1
                typeset -r flatdb=$2

                typeset patches=
                patches=$(DB SQL SELECT $REQUIRED_COLUMN     \
                        FROM $flatdb            \
                        WHERE $INSTALLED_COLUMN '==' $patchid)

                echo $patches | tr " ," "\012\012" | sort | uniq
        }

	PatchesApplicableToThisOSAndArch() {
		[ "$OPT_DEBUG" == 'true' ] && set -x
		typeset -r flatdb="$1"
		typeset -r rel="$2"
		typeset -r arch="$3"
	
		typeset -r interdb=$(TmpFile interdb)		
		typeset patches=
                DB SQL SELECT all	\
			FROM $flatdb 	\
			WHERE $OSVERSIONS_COLUMN '==' "$rel" > $interdb
		patches=$(DB SQL SELECT $INSTALLED_COLUMN \
			FROM $interdb   \
                        WHERE $ARCH_COLUMN '==' "$arch")
		rm $interdb	
		echo $patches | tr " ," "\012\012" | sort | uniq
	}

	ServicePatchesApplicableToThisOS() {
                [ "$OPT_DEBUG" == 'true' ] && set -x
		typeset -r flatdb="$1"
                typeset -r rel="$2"

		typeset -r servdb=$(TmpFile servdb)
		typeset patches=
		DB SQL SELECT all       \
                        FROM $flatdb    \
                        WHERE $OSVERSIONS_COLUMN '==' $rel > $servdb
		patches=$(DB SQL SELECT $INSTALLED_COLUMN \
                        FROM $servdb   \
                        WHERE $TYPE_COLUMN '==' "usr")
		patches="$patches $(DB SQL SELECT $INSTALLED_COLUMN \
                        FROM $servdb   \
                        WHERE $TYPE_COLUMN '==' 'ow')"
		patches="$patches $(DB SQL SELECT $INSTALLED_COLUMN \
                        FROM $servdb   \
                        WHERE $TYPE_COLUMN '==' 'kvm')"
                rm $servdb
                echo $patches | tr " ," "\012\012" | sort | uniq
	}

	ClientPatchesApplicableToThisOSAndArch() {
		[ "$OPT_DEBUG" == 'true' ] && set -x
                typeset -r flatdb="$1"
                typeset -r rel="$2"
		typeset -r arch="$3"

                typeset -r interclientdb=$(TmpFile interclientdb)
		typeset -r clientdb=$(TmpFile clientdb)
                typeset patches=
                DB SQL SELECT all       \
                        FROM $flatdb    \
                        WHERE $OSVERSIONS_COLUMN '==' "$rel" > $interclientdb
		DB SQL SELECT all       \
                        FROM $interclientdb    \
                        WHERE $ARCH_COLUMN '==' "$arch" > $clientdb
                patches=$(DB SQL SELECT $INSTALLED_COLUMN \
                        FROM $clientdb   \
                        WHERE $TYPE_COLUMN '==' "root")
                rm $interclientdb
		rm $clientdb
                echo $patches | tr " ," "\012\012" | sort | uniq
	}
 
        $@
        return $?
}
