#!/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 0 )) then for (( i=0; i $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 #==============================================================================