#!/bin/sh
#
# lsb_release - collect LSB conformance status about a system
#
# Copyright (C) 2000, 2002, 2004 Free Standards Group, Inc.
# Originally by Dominique MASSONIE <mdomi@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# * Changes in 2.0
# - Support LSB 2.0 module layout (Mats Wichmann)
#   The LSB_VERSION is now a colon-separated field of supported module versions
#   An /etc/lsb-release.d is searched for modules beyond the core.
#   Only the filenames in this directory is looked at, those names are added
#   to LSB_VERSION.  This allows module support to be handled easily by
#   package install/removal without a need to edit lsb-release on the fly.
# - Correct license: FSG == Free Standards Group, Inc.
#
# * Changes in 1.4
# - "awk" not needed anymore (Loic Lefort)
# - fixed bug #121879 reported by Chris D. Faulhaber,
#   some shells doesn't support local variables
# - fixed a bug when single parameter sets many args including -s
# - function DisplayProgramVersion (undocumented) now exits script like Usage
# - cosmetic changes in comments/outputs
#
# * Changes in 1.3
# - No changes in script, only in build infrastructure
#
# * Changes in 1.2
# - Fixed more bash'isms
# - LSB_VERSION is no longer required in /etc/lsb-release file
# 
# * Changes in 1.1
# - removed some bash-ism and typos
#   Notice: script remains broken with ash because of awk issues
# - changed licence to FSG - "Free Software Group, Inc"
# - fixed problem with --short single arg call
# - changed Debian specifics, codename anticipates release num
#
# Description:
# Collect information from sourceable /etc/lsb-release file (present on
# LSB-compliant systems) : LSB_VERSION, DISTRIB_ID, DISTRIB_RELEASE,
# DISTRIB_CODENAME, DISTRIB_DESCRIPTION (all optional)
# Then (if needed) find and add names from /etc/lsb-release.d
# Then (if needed) find and parse the /etc/[distro]-release file


###############################################################################
#     DECLARATIONS
###############################################################################

# This script version
SCRIPTVERSION="2.0"

# Defines the data files
INFO_ROOT="/etc"                              # directory of config files
INFO_LSB_FILE="lsb-release"                   # where to get LSB version
INFO_LSB_DIR="lsb-release.d"                  # where to get LSB addon modules
INFO_DISTRIB_SUFFIX="release"                 # <distrib>-<suffix>
ALTERNATE_DISTRIB_FILE="/etc/debian_version"  # for Debian [based distrib]
ALTERNATE_DISTRIB_NAME="Debian"               #     "
CHECKFIRST="/etc/redhat-release"              # check it before file search

# Defines our exit codes
EXIT_STATUS="0"                           # default = Ok :)
ERROR_UNKNOWN="10"                        # unknown error
ERROR_USER="1"                            # program misuse
ERROR_PROGRAM="2"                         # internal error
ERROR_NOANSWER="3"                        # all required info not available
                                          # typically non LSB compliant distro!

# Defines our messages
MSG_LSBVER="LSB Version:\t"
MSG_DISTID="Distributor ID:\t"
MSG_DISTDESC="Description:\t"
MSG_DISTREL="Release:\t"
MSG_DISTCODE="Codename:\t"
MSG_NA="n/a"
MSG_NONE="(none)"
MSG_RESULT="" # contains the result in case short output selected

# Description string delimiter
DESCSTR_DELI="release"


###############################################################################
#     FUNCTIONS
###############################################################################

# Display Program Version for internal use (needed by help2man)
DisplayProgramVersion() { 
    echo "FSG `basename $0` v$SCRIPTVERSION"
    echo
    echo "Copyright (C) 2000, 2002, 2004 Free Standards Group, Inc."
    echo "This is free software; see the source for copying conditions.  There\
 is NO"
    echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR\
 PURPOSE."
    echo
    echo "Originally written by Dominique MASSONIE."

    exit $EXIT_STATUS
}

# defines the Usage for lsb_release
Usage() {
    echo "FSG `basename $0` v$SCRIPTVERSION prints certain LSB (Linux\
 Standard Base) and"
    echo "Distribution information."
    echo
    echo "Usage: `basename $0` [OPTION]..."
    echo "With no OPTION specified defaults to -v."
    echo
    echo "Options:"
    echo "  -v, --version"
    echo "    Display the version of the LSB specification against which the distribution is compliant."
    echo "  -i, --id"
    echo "    Display the string id of the distributor."
    echo "  -d, --description"
    echo "    Display the single line text description of the distribution."
    echo "  -r, --release"
    echo "    Display the release number of the distribution."
    echo "  -c, --codename"
    echo "    Display the codename according to the distribution release."
    echo "  -a, --all"
    echo "    Display all of the above information."
    echo "  -s, --short"
    echo "    Use short output format for information requested by other options (or version if none)."
    echo "  -h, --help"
    echo "    Display this message."

    exit $EXIT_STATUS
}

# Handles the enhanced args (i.e. --something)
EnhancedGetopt() {
    getopt -T >/dev/null 2>&1             # is getopt the enhanced one ?
    if [ $? = 4 ]
    then                                  # Yes, advanced args ALLOWED
        OPT=$(getopt -o acdhirsvp                                             \
--long all,codename,description,help,id,release,short,version,program_version \
              -n 'lsb_release'                                                \
              -- "$@")
    else                                  # No, advanced args NOT allowed
        # convert (if needed) the enhanced options into basic ones
        MYARGS=$(echo "$@" | sed -e "/--/s/-\(-[[:alnum:]]\)[[:alnum:]]*/\1/g")
        OPT=$(getopt -o acdhirsvp                                             \
                     -n 'lsb_release'                                         \
                     -- "$MYARGS")
    fi
    if [ $? != 0 ]
    then
        exit $ERROR_PROGRAM
    fi

    NB_ARG=""       # enabled if many args set in one parameter (i.e. -dris)
    eval set -- "$OPT"
    while true ; do
        case "$1" in
        -a|--all) ARG_A="y"; NB_ARG="y"; shift;;
        -c|--codename) ARG_C="y"; NB_ARG="y"; shift;;
        -d|--description) ARG_D="y"; NB_ARG="y"; shift;;
        -i|--id) ARG_I="y"; NB_ARG="y"; shift;;
        -r|--release) ARG_R="y"; NB_ARG="y"; shift;;
        -s|--short) ARG_S="y"; shift;;
        -v|--version) ARG_V="y"; NB_ARG="y"; shift;;
        -p|--program_version) DisplayProgramVersion;;
	-h|--help) Usage;;
        --) shift; break;;
        *)  EXIT_STATUS=$ERROR_USER
            Usage;;
        esac
    done
}

# Get/Init LSB infos (maybe Distrib infos too)
GetLSBInfo() {
	# if we found LSB_VERSION, continue to look in directory
	if [ -d "$INFO_ROOT/$INFO_LSB_DIR" ]
	then
	    for tag in "$INFO_ROOT/$INFO_LSB_DIR/"*
	    do
		LSB_VERSION=$LSB_VERSION:`basename $tag`
	    done
	fi
}

# Get the whole distrib information string (from ARG $1 file)
InitDistribInfo() {
## Notice: Debian has a debian_version file
##         (at least) Mandrake has two files, a mandrake and a redhat one
    FILENAME=$1   # CHECKFIRST or finds' result in GetDistribInfo() or ""

    if [ -z "$FILENAME" ]
    then
        if [ -f "$ALTERNATE_DISTRIB_FILE" ]
        then # For Debian only
            [ -z "$DISTRIB_ID" ] && DISTRIB_ID="$ALTERNATE_DISTRIB_NAME"
            [ -z "$DISTRIB_RELEASE" ]                                         \
                && DISTRIB_RELEASE=$(cat $ALTERNATE_DISTRIB_FILE)
            [ -z "$DISTRIB_CODENAME" ] && [ "$DISTRIB_RELEASE" = "2.1" ]      \
                && DISTRIB_CODENAME="Slink"
            [ -z "$DISTRIB_CODENAME" ] && [ "$DISTRIB_RELEASE" = "2.2" ]      \
                && DISTRIB_CODENAME="Potato"
#           [ -z "$DISTRIB_CODENAME" ] && [ "$DISTRIB_RELEASE" = "2.3" ]      \
#               && DISTRIB_CODENAME="Woody"
            [ -z "$DISTRIB_CODENAME" ] && DISTRIB_CODENAME=$DISTRIB_RELEASE
            # build the DISTRIB_DESCRIPTION string (never need to be parsed)
            [ -z "$DISTRIB_DESCRIPTION" ]                                     \
                && DISTRIB_DESCRIPTION="$DISTRIB_ID $DESCSTR_DELI $DISTRIB_REL\
EASE ($DISTRIB_CODENAME)"
        else # Only for nothing known compliant distrib :(
            [ -z "$DISTRIB_ID" ] && DISTRIB_ID=$MSG_NA
            [ -z "$DISTRIB_RELEASE" ] && DISTRIB_RELEASE=$MSG_NA
            [ -z "$DISTRIB_CODENAME" ] && DISTRIB_CODENAME=$MSG_NA
            [ -z "$DISTRIB_DESCRIPTION" ] && DISTRIB_DESCRIPTION=$MSG_NONE

            EXIT_STATUS=$ERROR_NOANSWER
        fi
    else
        NO=""                    # is Description string syntax correct ?
        if [ -z "$DISTRIB_DESCRIPTION" ]                                      \
            || [ -n "$(echo $DISTRIB_DESCRIPTION |                            \
                       sed -e "s/.*$DESCSTR_DELI.*//")" ]
        then
            TMP_DISTRIB_DESC=$(head -n 1 $FILENAME 2>/dev/null)
            [ -z "$DISTRIB_DESCRIPTION" ]                                     \
                && DISTRIB_DESCRIPTION=$TMP_DISTRIB_DESC
        else
            TMP_DISTRIB_DESC=$DISTRIB_DESCRIPTION
        fi

        if [ -z "$TMP_DISTRIB_DESC" ]  # head or lsb-release init
        then                           # file contains no data
            DISTRIB_DESCRIPTION=$MSG_NONE
            NO="y"
        else                           # Do simple check
            [ -n "$(echo $TMP_DISTRIB_DESC |                                  \
                    sed -e "s/.*$DESCSTR_DELI.*//")" ]                        \
                && NO="y"
        fi

        if [ -n "$NO" ]
        then                           # does not contain "release" delimiter
            [ -z "$DISTRIB_ID" ] && DISTRIB_ID=$MSG_NA
            [ -z "$DISTRIB_RELEASE" ] && DISTRIB_RELEASE=$MSG_NA
            [ -z "$DISTRIB_CODENAME" ] && DISTRIB_CODENAME=$MSG_NA
        fi
    fi
}

# Check missing and requested infos, then find the file and get infos
GetDistribInfo() {
    NO=""  # /etc/lsb-release data are enough to reply what is requested?
    [ -n "$ARG_D" ] && [ -z "$DISTRIB_DESCRIPTION" ] && NO="y"
    [ -z "$NO" ] && [ -n "$ARG_I" ] && [ -z "$DISTRIB_ID" ] && NO="y"
    [ -z "$NO" ] && [ -n "$ARG_R" ] && [ -z "$DISTRIB_RELEASE" ] && NO="y"
    [ -z "$NO" ] && [ -n "$ARG_C" ] && [ -z "$DISTRIB_CODENAME" ] && NO="y"

    if [ -n "$NO" ]
    then
        if [ ! -f "$CHECKFIRST" ]
        then
            CHECKFIRST=$(find $INFO_ROOT/ -maxdepth 1                         \
                   -name \*$INFO_DISTRIB_SUFFIX                               \
                   -and ! -name $INFO_LSB_FILE                                \
                   -and -type f                                               \
                   2>/dev/null                                                \
                   | head -n 1 ) # keep one of the files found (if many)
        fi
        InitDistribInfo $CHECKFIRST
    fi
}

# Display version of LSB against which distribution is compliant
DisplayVersion() {
    if [ -z "$ARG_S" ]
    then
        echo -e "$MSG_LSBVER$LSB_VERSION"          # at least "n/a"
    else
        MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$LSB_VERSION"
    fi
}

# Display string id of distributor ( i.e. a single word! )
DisplayID() {
    if [ -z "$DISTRIB_ID" ]
    then
## Linux could be part of the distro name (i.e. Turbolinux) or a separate word
## set before, after...
## also expect a delimiter ( i.e. "release" )
        if [ -n "$(echo $TMP_DISTRIB_DESC | sed "s/.*$DESCSTR_DELI.*//")" ]
        then
            DISTRIB_ID=$MSG_NA
        else
            DISTRIB_ID=$(echo " $TMP_DISTRIB_DESC"                            \
            | sed -e "s/[[:blank:]][Ll][Ii][Nn][Uu][Xx][[:blank:]]/ /g"       \
            -e "s/\(.*\)[[:blank:]]$DESCSTR_DELI.*/\1/" -e "s/[[:blank:]]//g")
        fi
    fi
    if [ -z "$ARG_S" ]
    then
        echo -e "$MSG_DISTID$DISTRIB_ID"
    else
        MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$DISTRIB_ID"
    fi
}

# Diplay single line text description of distribution
DisplayDescription() {
    if [ -z "$DISTRIB_DESCRIPTION" ]
    then
        # should not be empty since GetDistribInfo called on Initialization !
        EXIT_STATUS=$ERROR_PROGRAM
    fi
    if [ -z "$ARG_S" ]
    then
        echo -e "$MSG_DISTDESC$DISTRIB_DESCRIPTION"
    else
        MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }\"$DISTRIB_DESCRIPTION\""
    fi
}

# Display release number of distribution.
DisplayRelease() {
    if [ -z "$DISTRIB_RELEASE" ]
    then                       # parse the "$DISTRIB_DESCRIPTION" string
        DISTRIB_RELEASE=$(echo "$TMP_DISTRIB_DESC" |                          \
      sed -e "s/.*$DESCSTR_DELI[[:blank:]]*\([[:digit:]][[:graph:]]*\).*/\1/" )
        [ "$DISTRIB_RELEASE" = "$TMP_DISTRIB_DESC" ]                          \
            || [ -z "$DISTRIB_RELEASE" ]                                      \
            && DISTRIB_RELEASE=$MSG_NA
    fi
    if [ -z "$ARG_S" ]
    then
        echo -e "$MSG_DISTREL$DISTRIB_RELEASE"
    else
        MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$DISTRIB_RELEASE"
    fi
}

# Display codename according to distribution version.
DisplayCodename() {
    if [ -z "$DISTRIB_CODENAME" ]
    then                       # parse the "$DISTRIB_DESCRIPTION" string
        DISTRIB_CODENAME=$(echo "$TMP_DISTRIB_DESC" |                         \
                           sed -e "s/.*$DESCSTR_DELI.*(\(.*\)).*/\1/")
        [ "$DISTRIB_CODENAME" = "$TMP_DISTRIB_DESC" ]                         \
            || [ -z "$DISTRIB_CODENAME" ]                                     \
            && DISTRIB_CODENAME=$MSG_NA
    fi
    if [ -z "$ARG_S" ]
    then
        echo -e "$MSG_DISTCODE$(echo "$DISTRIB_CODENAME" |                    \
                                tr -d "[:blank:]")" # Remove blanks
    else
        MSG_RESULT="$MSG_RESULT${MSG_RESULT:+ }$(echo "$DISTRIB_CODENAME" |   \
                                                 tr -d "[:blank:]")"
    fi
}


###############################################################################
#     MAIN
###############################################################################

# Check if any prog argument
if [ -z "$1" ]
then
    ARG_V="y"           # default set to Display LSB Version (not Usage)
else
    EnhancedGetopt "$@" # Parse program args
    if [ -n "$ARG_S" ] && [ -z "$NB_ARG" ]
    then
        ARG_V="y"       # set also default for --short when single arg
    fi
fi

# Update args to All if requested 
if [ -n "$ARG_A" ]
then
    [ -z "$ARG_C" ] && ARG_C="y"
    [ -z "$ARG_D" ] && ARG_D="y"
    [ -z "$ARG_I" ] && ARG_I="y"
    [ -z "$ARG_R" ] && ARG_R="y"
    [ -z "$ARG_V" ] && ARG_V="y"
fi

# Initialization
GetLSBInfo
GetDistribInfo

# Display requested infos (order as follow)
[ -n "$ARG_V" ] && DisplayVersion
[ -n "$ARG_I" ] && DisplayID
[ -n "$ARG_D" ] && DisplayDescription
[ -n "$ARG_R" ] && DisplayRelease
[ -n "$ARG_C" ] && DisplayCodename

[ -n "$ARG_S" ] && echo "$MSG_RESULT"

exit $EXIT_STATUS
