#!/bin/ksh93
#  ALTRAN_PROLOG_BEGIN_TAG                                                    
#  This is an automatically generated prolog.                                  
#                                                                              
#  Copyright (C) Altran ACT S.A.S. 2017,2021.  All rights reserved.  
#                                                                              
#  ALTRAN_PROLOG_END_TAG                                                      
#                                                                              
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
# 61haes_r714 src/43haes/usr/sbin/cluster/utilities/cl_dspmsg.sh 1.3 
#  
# Licensed Materials - Property of IBM 
#  
# COPYRIGHT International Business Machines Corp. 2013 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# @(#)  7d4c34b 43haes/usr/sbin/cluster/utilities/cl_dspmsg.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM

#=============================================================================
#
# Name:        cl_dspmsg
#
# Description: This utility is a wrapper for dspmsg that ensures that all
#              references to "clvt" are displayed as "clmgr", the newer name,
#              and converts "HACMP" to "PowerHA SystemMirror". Further, the
#              function formats the message to fit a 80 column screen by
#              default, or the current width of the terminal, whichever is
#              smallest. It is possible to dictate this width, but never to
#              a width that is larger than the actual terminal width.
#
#              A sincere effort is made to preserve paragraphs and lists,
#              along with any intended lists. However, no matter how clever
#              the code, it will not always work as intended. So test your
#              message first with this utility, and if it does not display
#              cleanly, and you can't easily correct the message to make it
#              work, then just stick with standard dspmsg, and hard-coded
#              line wrapping.
#
# Inputs:      -s MSGSET    The optional message set number.
#              CATALOG      Required; the name of the message catalog.
#              MSGNUM       Required; the number of the message to retrieve.
#              DEFMSG       Optional; a default message to display.
#              $*           Optional; any arguments that the message may need.
#
#              TERMINAL_WIDTH    An optional value to control the width of the
#                           output. If set to a positive integer, the message
#                           will be wrapped to that width. If set to null, a
#                           default of 80 is used. If set to -1, or any non-
#                           positive integer value, then the current full width
#                           of the terminal will be used. Explicitly setting
#                           the output width can be useful, for example, when
#                           displaying a message inside of smitty, which is
#                           narrower than the overall terminal. Be aware that
#                           if a width is specified that is greater than the
#                           actual width of the terminal, then the terminal
#                           width will be used instead.
#
# Outputs:     The formatted message is displayed.
#
# Returns:     The return code from dspmsg is returned.
#
# Usage:       cl_dspmsg uses the exact same syntax as dspmsg.
#
# Examples:    Compare the following outputs:
#                   dspmsg -s 1 cluster_help 435
#                   cl_dspmsg -s 1 cluster_help 435
#                   TERMINAL_WIDTH=60 cl_dspmsg -s 1 cluster_help 435
#
#=============================================================================

#===================================
# Declare and initialize variables
#===================================
typeset MSGSET= CATALOG= MSGSET= MSGNUM= DEFMSG= MSG= NEWMSG= LINE= MSGFILE=
typeset HEADER= TRAILER= SEGMENT= LAST_CHAR= FIRST_WORD= SECOND_WORD= REST=
typeset MSG1= MSG2= PARAGRAPHS= ERRFILE=
typeset NL="$(printf '\n')" TAB="$(printf '\t')"
integer rc=-1 FIRST_LINE=1 i=0 HANGING_INDENT=0 INDENT=0 PARA=0 DEBUG=0
integer MAX_WIDTH=$TERMINAL_WIDTH ORIG_MAX_WIDTH=$MAX_WIDTH
typeset -u UC_LANG=$LANG
TMPDIR="${TMPDIR:-/var/hacmp/log/tmp}"
[[ ! -d $TMPDIR ]] && mkdir -p $TMPDIR

#====================
# Parse user inputs
#====================
while getopts ":s:D" opt; do
    case $opt in
        D) DEBUG=1
           shift
        ;;
        s) MSGSET=$OPTARG
           shift; shift
        ;;
    esac
done
if (( DEBUG ))
then
    PS4='cl_dspmsg[$LINENO]($?)($SECONDS):$TAB '
    set -x
fi
if [[ -n $1 ]]; then
    CATALOG=$1; shift
fi
if [[ -n $1 ]]; then
    MSGNUM=$1; shift
fi
if [[ -n $1 ]]; then
    DEFMSG=$1; shift
fi

#============================================================================
# Determine a usable, appropriate maximum width for this message, based in
# part by the user's input (if any), but overridden by reality, where
# "reality" is actual width of the terminal (if we can get it). The default
# is 80, of course.
#============================================================================
typeset ACTUAL_WIDTH=$(/usr/bin/stty size 2>/dev/null)
        ACTUAL_WIDTH=${ACTUAL_WIDTH%%+([ $'\n'$'\t'])}
        ACTUAL_WIDTH=${ACTUAL_WIDTH##*+([ $'\n'$'\t'])}
        [[ $ACTUAL_WIDTH != +([0-9]) ]] && ACTUAL_WIDTH=80
if [[ -n $TERMINAL_WIDTH ]]
then
    if [[ $TERMINAL_WIDTH == "-1" || \
          $TERMINAL_WIDTH != +([0-9]) ]]
    then
        #
        # Use whatever width the system gives us.
        #
        TERMINAL_WIDTH=$ACTUAL_WIDTH
    fi
else
    TERMINAL_WIDTH=80
fi
if (( ACTUAL_WIDTH < TERMINAL_WIDTH ))
then
    #
    # Don't allow message wrapping at a width that is greater than
    # the space that is actually available. It won't look good.
    #
    export TERMINAL_WIDTH=$ACTUAL_WIDTH
fi
MAX_WIDTH=$TERMINAL_WIDTH
ORIG_MAX_WIDTH=$MAX_WIDTH

#===========================================================
# The odd manner in which MSG is populated below proved to
# be necessary in order to preserve trailing newlines. It
# is a bit goofy, I know... but it works.
#===========================================================
MSGFILE=$TMPDIR/cldsp.$RANDOM
ERRFILE=$TMPDIR/cldsp.$RANDOM
if [[ $MSGSET == +([0-9]) ]]; then
    if [[ -n $DEFMSG ]]
    then
        /usr/bin/dspmsg -s $MSGSET $CATALOG $MSGNUM "$DEFMSG" $@ >$MSGFILE 2>$ERRFILE
    else
        /usr/bin/dspmsg -s $MSGSET $CATALOG $MSGNUM $@ >$MSGFILE 2>$ERRFILE
    fi
else
    if [[ -n $DEFMSG ]]
    then
        /usr/bin/dspmsg $CATALOG $MSGNUM "$DEFMSG" $@ >$MSGFILE 2>$ERRFILE
    else
        /usr/bin/dspmsg $CATALOG $MSGNUM $@ >$MSGFILE 2>$ERRFILE
    fi
fi
rc=$?
print -n "__CL_DSPMSG_END__" >>$MSGFILE
if (( rc == 0 ))
then
    MSG="$(/usr/bin/cat $MSGFILE)"
    MSG=${MSG%__CL_DSPMSG_END__}
elif [[ -s $ERRFILE ]]
then
    sed 's/dspmsg/cl_dspmsg/g' $ERRFILE >&2
    /usr/bin/rm -f $MSGFILE $ERRFILE
    exit $rc
fi
/usr/bin/rm -f $MSGFILE $ERRFILE

#=================================================
# Make sure the messages refer to correct binary
#=================================================
if [[ -n $CLMGR_PROGNAME ]]
then
    if [[ $CLMGR_PROGNAME == "clmgr" ]]; then
        MSG=${MSG//clvt/clmgr}
    else
        MSG=${MSG//clmgr/clvt}
    fi
fi

#========================================================================
# Make sure the original product name is replaced with the current name
#========================================================================
typeset OSNAME=$(uname)
if [[ $MSG == *HACMP* ]]
then
    MSG=${MSG//HACMP/PowerHA SystemMirror}
fi
if [[ $MSG == *an\ PowerHA* ]]
then
    MSG=${MSG//an PowerHA/a PowerHA}
fi
if [[ $MSG == *for\ AIX* && $OSNAME != "AIX" ]]
then
    MSG=${MSG//for AIX/for Linux}
fi

#=======================================================================
# If this is a left-to-right language, format it to fit the current
# terminal width. Avoid doing this for messages that contain line
# continuation characters, as it will destroy the intended formatting.
#=======================================================================
if [[ $UC_LANG == @(EN|POSIX|C)* ]] && \
   (( TERMINAL_WIDTH > 0 ))         && \
   [[ $MSG != *\\$NL* ]]
then
    #=========================================================================
    # Retrieve any leading/trailing newlines. This allows the removal of any
    # intermediate, hard-coded newlines, while still preserving any intended
    # spacing. Again, this highlights that this function cannot be used with
    # a message that is already formatted!!
    #=========================================================================
    while [[ ${MSG:0:1} == $'\n'* ]]
    do
        HEADER="$HEADER${MSG:0:1}"
        MSG=${MSG#[ $'\n'$'\t']} # Remove it
        (( i++ ))
    done
    i=${#MSG}
    while [[ ${MSG:$i:1} == *$'\n' ]]
    do
        (( i-- ))
        TRAILER="$TRAILER${MSG:$i:1}"
        MSG=${MSG%[ $'\n'$'\t']} # Remove it
    done

    #==================================================
    : Break the message up into paragraphs, if needed
    #==================================================
    typeset PREV_LINE=""
    while [[ -n $MSG ]]
    do
        # Preserve liny eading indentation, protecting from "read", below
        MSG=".$MSG"

        print -- "$MSG" |\
        while read -r LINE
        do
            if [[ $LINE == '.' || $LINE == *([ $'\n'$'\t']) ]]
            then
                MSG=${MSG#\.}
                MSG=${MSG##+( )}
                MSG=${MSG##+($'\n')}

                (( PARA++ ))
                PREV_LINE=$LINE
                break  # End of a paragraph

            elif [[ $LINE == [a-zA-Z0-9][\.\):]* ]] || \
                 [[ $PREV_LINE == *[:\.] && $LINE == +([a-zA-Z0-9_])*([ $'\t'])\-*([ $'\t'])* ]]
            then
                (( PARA++ ))
                [[ -n ${PARAGRAPHS[$PARA]} ]] && PARAGRAPHS[$PARA]="${PARAGRAPHS[$PARA]} "
                PARAGRAPHS[$PARA]="${PARAGRAPHS[$PARA]}${LINE#\.}"

            else
                [[ -n ${PARAGRAPHS[$PARA]} ]] && PARAGRAPHS[$PARA]="${PARAGRAPHS[$PARA]} "
                PARAGRAPHS[$PARA]="${PARAGRAPHS[$PARA]}${LINE#\.}"
            fi
            PREV_LINE=$LINE

            # Remove the line from the message, escaping troublesome characters
            LINE=${LINE//\(/\\\(}; LINE=${LINE//\)/\\\)}
            LINE=${LINE//\[/\\\[}; LINE=${LINE//\]/\\\]}
            LINE=${LINE//\&/\\\&}
            LINE=${LINE//\|/\\\|}
            MSG=${MSG#*([ $'\n'$'\t'])$LINE}
            MSG=${MSG##+( )}
            MSG=${MSG#$'\n'}
        done
    done

    #=================================================================
    # If any leading whitespace (which includes newlines, of course)
    # was detected/removed earlier, display that first, before
    # processing the body of the message.
    #=================================================================
    print -n "$HEADER" # Display any leading whitespace

    #======================================================================
    # Loop through the message's paragraphs, individually formatting each
    # and displaying it.
    #======================================================================
    integer IN_LIST=0
    for (( PARA=0; PARA <= ( ${#PARAGRAPHS[*]} + 1 ); PARA++ ))
    do
        #==============================================
        # Reset some variables for the next paragraph
        #==============================================
        FIRST_LINE=1
        MAX_WIDTH=$ORIG_MAX_WIDTH

        #=====================================================================
        # Load the paragraph that is to be processed, extracting any leading
        # indent. If already in a list, use the previous indent to form nice
        # columns in the output. Note that the presence of indentation is an
        # indicator of being in a "list", whether bulleted or not.
        #=====================================================================
        MSG="${PARAGRAPHS[$PARA]}"
        PRUNED_MSG=${MSG##+( )}
        if (( ${#MSG} > ${#PRUNED_MSG} ))
        then
            if (( ! IN_LIST ))
            then
                (( INDENT = ${#MSG} - ${#PRUNED_MSG} ))
            fi
            IN_LIST=1

        elif [[ $MSG == [a-zA-Z0-9][\.\):]* ]] || \
             [[ $MSG == +([a-zA-Z0-9_])*([ $'\t'])\-*([ $'\t'])* ]]
        then
            IN_LIST=1
            INDENT=4

        else
            IN_LIST=0
        fi

        #=====================================
        : Display a blank line, if warranted
        #=====================================
        if [[ $MSG == *([ $'\n'$'\t']) ]]
        then
            print
            continue
        elif  (( PARA > 0 ))
        then
            print "\n"
        fi

        #====================================================
        : Remove any/all embedded indentations and newlines
        #====================================================
        MSG=${MSG//+([ $'\n'$'\t'])/ }

        #======================================================================
        : Check for an indentation "sentinel" value indicating a bulleted list.
        # For example:
        #     ERROR:
        #     Warning:
        #     *** Notice:
        #     1.
        #     2)
        #     C)
        #     -
        #     +
        # The "FIRST_WORD" variable is set to this bulleted value. If no known
        # "bullet" format is detected, however, "FIRST_WORD" is nulled. This
        # is useful in determining if a hanging indent is needed, versus a
        # normal indent.
        #======================================================================
        print -- "$MSG" | read FIRST_WORD SECOND_WORD REST
        if [[ $FIRST_WORD == +([\*\-+]) ]]; then
            FIRST_WORD="$FIRST_WORD $SECOND_WORD"
        fi
        if [[ $FIRST_WORD == +([a-zA-Z0-9_])[:\.\)]  || \
              $FIRST_WORD == +([\-+>:])*([ $'\n'$'\t'])+([a-zA-Z0-9_]) ]]
        then
            MSG1=${MSG##*([ $'\n'$'\t'])}
            HANGING_INDENT=${#FIRST_WORD}
            MSG2=${MSG##*([ $'\n'$'\t'])${FIRST_WORD/\)/\\\)}+([ $'\n'$'\t'])}
            MSG2=${MSG2##+([ $'\n'$'\t'])}
            (( HANGING_INDENT = INDENT + ( ${#MSG1} - ${#MSG2} ) ))

        elif (( ! IN_LIST ))
        then
            FIRST_WORD=""  # Signifies that there is no "list" sentinel value
            HANGING_INDENT=0
        else
            FIRST_WORD=""  # Signifies that there is no "list" sentinel value
        fi

        print -- "$MSG" |\
        while IFS="" read -r LINE; do
            LINE=${LINE## }

            if (( FIRST_LINE && IN_LIST )) && [[ $LINE != *([ $'\n'$'\t']) ]]
            then
                if [[ -n $FIRST_WORD ]] && (( INDENT > 0 ))
                then
                    for (( i=0; i<INDENT; i++ )); do
                        print -n " "
                    done

                elif (( IN_LIST && HANGING_INDENT > 0 ))
                then
                    for (( i=0; i<HANGING_INDENT; i++ )); do
                        print -n " "
                    done
                fi
            fi

            if (( ( ${#LINE} + INDENT ) <= $MAX_WIDTH )); then
                print -n -- "$LINE"
            else
                while (( ( ${#LINE} + INDENT ) > $MAX_WIDTH ))
                do
                    #===========================================================
                    # Extract the leading segment from the remaining message,
                    # setting its width based on the maximum width in relation
                    # to any indentations being honored.
                    #===========================================================
                    if [[ -n $FIRST_WORD ]] && (( INDENT > 0 ))
                    then
                        SEGMENT=${LINE:0:$(($MAX_WIDTH - $INDENT + 1))}
                    elif (( IN_LIST && HANGING_INDENT > 0 ))
                    then
                        SEGMENT=${LINE:0:$(($MAX_WIDTH - $HANGING_INDENT + 1))}
                    else
                        SEGMENT=${LINE:0:$MAX_WIDTH}
                    fi

                    #=========================================================
                    # Walk the string backward, from right to left, until a
                    # valid line-wrap point is found. A line wrap can occur
                    # on whitespace, on a hyphen, or on a period (if it ends
                    # a sentence).
                    #=========================================================
                    PREV_CHAR=$LAST_CHAR
                    LAST_CHAR=${SEGMENT:${#SEGMENT}-1}
                    while [[ -n $LAST_CHAR && \
                             $LAST_CHAR != @([ $'\n'$'\t']|[\-]) && \
                             "$LAST_CHAR$PREV_CHAR" != \.@([ $'\n'$'\t']) ]]
                    do
                        SEGMENT=${LINE:0:${#SEGMENT}-1}
                        [[ -z $SEGMENT ]] && break

                        PREV_CHAR=$LAST_CHAR
                        LAST_CHAR=${SEGMENT:${#SEGMENT}-1}
                    done

                    [[ -z $SEGMENT ]] && SEGMENT=${LINE:0:$MAX_WIDTH}
                    print -- "${SEGMENT%%+([ $'\n'$'\t'])}"
                    LINE=${LINE:${#SEGMENT}}

                    #========================================================
                    # If we landed on just the right boundary, we might get
                    # an unintended leading space here. Remove it.
                    #========================================================
                    LINE=${LINE#[ $'\n'$'\t']}

                    #=========================================
                    # Display an indent, if one is warranted
                    #=========================================
                    if (( HANGING_INDENT > 0 )) && [[ $LINE != *([ $'\n'$'\t']) ]]
                    then
                        for (( i=0; i<$HANGING_INDENT; i++ )); do
                            print -n " "
                        done
                        if [[ -n $FIRST_WORD ]] && (( INDENT > 0 && FIRST_LINE ))
                        then
                            (( MAX_WIDTH -= $HANGING_INDENT ))
                        fi
                    fi

                    #==========================================================
                    # Just finished processing the first line, so set this
                    # Boolean to indicate that the next line (if any) is not
                    # the first one. This, among other things, helps determine
                    # whether or not to use an indent versus a hanging indent.
                    #==========================================================
                    FIRST_LINE=0

                done  # End of the message segments loop

                [[ -n $LINE ]] && print -n -- "$LINE"
            fi
        done  # End of the message lines loop
    done  # End of the "PARAGRAPHS" loop

    #==================================================================
    # If any trailing whitespace (which includes newlines, of course)
    # was detected/removed earlier, append them to the display now,
    # since the body of the message has been completely displayed.
    #==================================================================
    print -n "$TRAILER"

else
    print -n -- "$MSG"
fi

exit $rc


#==============================================================================
# The following, comment block attempts to enforce coding standards when this
# file is edited via emacs or vim. This block _must_ appear at the very end
# of the file, or the editor will not find it, and it will be ignored.
#==============================================================================
# Local Variables:
# indent-tabs-mode: nil
# tab-width: 4
# End:
#==============================================================================
# vim: tabstop=4 shiftwidth=4 expandtab
#==============================================================================
