#!/bin/bash ############################################################################### # # # F01 common.lib : A library for common functions. # # This library should work with KSH & BASH. # # # # +---------------------------------------------------------------------+ # # | Copyright (c) 2009-2010 Flyounet | # # +---------------------------------------------------------------------+ # # | 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 3 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, see . | # # +---------------------------------------------------------------------+ # # | Cette œuvre est distribuée SANS AUCUNE GARANTIE hormis celle d'être | # # | distribuée sous les termes de la License Demerdez-vous ("Demerden | # # | Sie Sich License") plus communément appelée DSSL telle que publiée | # # | par Flyounet : soit la version 1 de cette licence, soit (à votre | # # | gré) toute version ultérieure. | # # | | # # | Vous devriez avoir reçu une copie de la Licence Démerdez-vous avec | # # | cette œuvre ; si ce n’est pas le cas, consultez : | # # | . | # # +---------------------------------------------------------------------+ # # | Author: Flyounet < dev @@ flyounet . net > | # # +---------------------------------------------------------------------+ # # # ############################################################################### # # # v0.01 [20/01/2009] Flyounet [@Home] : # # > Initiale Release (from myEasyConnect script) # # v0.07 [20/11/2009] Flyounet [@Home] : # # + Rewriting code to be more readable and a really lib. # # v0.08 [23/11/2009] Flyounet [@Work] : # # + Add Comments and header. # # > If you need to Debug set the variable below to a value > 0 # # eg. 5 if you want the function call and 6 if you also want the exit # F01DEBUG=0 # v0.09 [23/11/2009] Flyounet [@Work] : # # + iniParseFile & iniGetValue : To parse INI files # # v0.10 [23/11/2009] Flyounet [@Work] : # # * p2s : add the -- before string to avoid problem # # v0.12 [23/11/2009] Flyounet [@Home] : # # * Force the DEBUG to var 0 if not set # # v0.12 [23/11/2009] Flyounet [@Home] : # # * myDebug: I forget to print the Debug level # # * myDebug: It was a problem of shift before print # # v0.13 [23/11/2009] Flyounet [@Work] : # # * fileCheck : Typo problem in a test # # v0.14 [23/11/2009] Flyounet [@Home] : # # * iniParseFile: Change of the awk matchString. # # v0.16 [23/11/2009] Flyounet [@Home] : # # + logMe, LogMeV, logRotate : Logs Functions # # v0.17 [25/11/2009] Flyounet [@Home] : # # * Correct the 3 last function added. # # v0.19 [27/11/2009] Flyounet [@Home] : # # + isFunction: Return 1/0 if the arg is function Name. # # v0.20 [02/12/2009] Flyounet [@Home] : # # + sprintf: Equivalent to C sprintf function. # # + isFunction: Now with a second arg can detect a programme or function# # v0.21 [22/01/2010] Flyounet [@Home] : # # * trim: As I want a shell lib, I comment the usage of sed and let the # # shell version by using variables. # # + ltrim & rtrim: left trim & right trim of [[:space:]] in shell. # # * trim: Now use ltrim & rtrim instead of builtin and sed. # # > Adding [@Home] and [@Work] to know where I work on this lib. # # v0.22 [??/01/2010] Flyounet [@Home] : # # > Can't remember what have been done. # # v0.23 [30/01/2010] Flyounet [@Home] : # # * fileCreate: In case of error during touch an ugly message is now # # sent to /dev/null (for chmod too) # # v0.24 [03/03/2010] Flyounet [@Home] : # # > First svn version # # v0.25 [01/04/2010] Flyounet [@Home] : # # + textFill: equivalent to perl "string" x number # # + textBox: put a caracter all around a text # # * textBox: not working with KSH # # + _str2lower & _str2upper have been added, cause to KSH that doesn't # # support Case modification of Bash. # # * textBox: now use _str2*er and work with KSH # # * iniGetValue: Remove the call to awk (Yeah ! Love shell !) # # v0.26 [22/04/2010] Flyounet [@Home] : # # + parseHeadersForHelp: Print the headers of each function in a file. # # Headers are based on a first line containing a #F??. # # > textBox: Only works with KSH... Fuck ! # # * textBox: A subfile is needed to make it works with bash. # # # F01VERSION=0.26 F01PROTOCOL=0.01 ############################################################################### # # # Todo : # # < iniGetValue and iniParseFile need an extra argument or a global variable # # to separate keyName and valueName. By default use :: # # # ############################################################################### # # # Legende : # # + --> Indique une nouveaute, un ajout de fonctionnalite. # # * --> Indique une correction de bogue. # # - --> Indique la suppression d'une fonctionnalite/variable. # # > --> Indique une information n'ayant pas forcement de rapport avec le code# # < --> Indique une amelioration a apporter au code. # # <- --> Indique une amelioration en cours de developpement/realisation. # # OK --> Indique qu'une amelioration a ete effectuee. # # # ############################################################################### # ############################################################################ # # Variables Initialisation # ############################################################################ # # Verify presence of Perl if [ -z "$(which perl)" ]; then export __cmdPERL=0; else export __cmdPERL=1; fi # Verify presence of stat if [ -z "$(which stat)" ]; then export __cmdSTAT=0; else export __cmdSTAT=1; fi # Force the DEBUG variable to 0 if not set: if [ -z ${F01DEBUG} ]; then export F01DEBUG=0; fi # Initialize Default Log Variables if [ -z "${__logDir}" ]; then export __logDir='/tmp'; fi if [ -z "${__logFile}" ]; then export __logFile='logFile.log'; fi if [ -z "${__logFileV}" ]; then export __logFileV='logFileV.log'; fi if [ -z "${__logFileSize}" ]; then export __logFileSize=21600; fi if [ -z "${__logFileVSize}" ]; then export __logFileVSize=43200; fi # ############################################################################ # # Functions # ############################################################################ # #F01.01.03 #Function : ps2, Print to screen string passed in argument #Syntaxe : p2s #Return : nothing #Notes : p2s() { printf -- "${@}\n" } #F01.02.02 #Function : p2st, Print to screen string passed in argument, with Time before #Syntaxe : p2st #Return : nothing #Notes : p2st() { p2s "$(date '+%F %T')\n${@}" } #F01.03.01 #Function : oops, Print to screen string passed in argument, and exit 1 #Syntaxe : oops #Return : nothing #Notes : oops() { p2s "${@}" exit 1 } #F01.04.01 #Function : oopst, Print to screen string passed in argument, with time before, and exit 1 #Syntaxe : oopst #Return : nothing #Notes : oopst() { p2st "${@}" exit 1 } #F01.05.06 #Function : myDebug, Print to screen string passed in argument if debug level is reach #Syntaxe : myDebug #Return : nothing #Notes : myDebug() { if [ ${F01DEBUG} -gt ${1} -o ${F01DEBUG} -eq ${1} ] then _debug="--\033[01;31mDEBUG\033[0m(\033[01;32m${1}\033[0m)----" shift if [ ${__cmdPERL} -eq 1 ] then _debug="${_debug}[$( perl -e 'print time();' )]" fi _debug="${_debug}[$(date '+%d/%m/%Y %H%M%S' )]----${@}" p2s "${_debug}" fi } #F01.06.02 #Function : myError, Store error in variables #Syntaxe : myError #Return : nothing #Notes : Store last error in var __lastError and all errors in __allError myError() { myDebug 5 "myError(${@})" export __lastError="${@}" export __allError="[$(date '+%d/%m/%Y %H%M%S' )]${@}\n${__allError}" } #F01.07.02 #Function : fileCheck, Check file for presence and permission #Syntaxe : fileCheck [] #Return : 1=OK [fileName exists and permission are ok], 0=Fail #Notes : fileCheck() { myDebug 5 "fileCheck(${@})" if [ -z ${1} ] then myError "fileCheck: Filename is empty" return 0 fi if [ ! -f "${1}" ] then myError "fileCheck: File doesn't exists are is not a file" return 0 fi if [ ! -z "${2}" ] then if [ ${__cmdSTAT} -eq 0 ] then myError "fileCheck: Command 'stat' is not available" return 0 fi _=$( stat -c'%a' "${1}" ) if [ "${_}" = "${2}" -o "${_}" = "0${2}" ] then return 1 else myError "fileCheck: Rights error [${_} instead of ${2}]" return 0 fi else return 1 fi } #F01.08.02 #Function : fileCreate, Create file with permissions if asked #Syntaxe : fileCreate [] #Return : 1=OK [fileName exists and permission are ok], 0=Fail #Notes : fileCreate() { myDebug 5 "fileCreate(${@})" if [ -z "${1}" ] then myError "fileCreate: Filename is empty" return 0 fi touch "${1}" 2>/dev/null if [ $? -ne 0 ] then myError "fileCreate: Can't touch file" return 0 fi if [ ! -z "${2}" ] then chmod ${2} "${1}" 2>/dev/null if [ $? -ne 0 ] then myError "fileCreate: Can't chmod" return 0 fi fi return 1 } #F01.09.01 #Function : fileDelete, Delete the specified file #Syntaxe : fileDelete #Return : 1=OK [fileName exists and permission are ok], 0=Fail #Notes : fileDelete() { myDebug 5 "fileDelete(${@})" if [ -z "${1}" ] then myError "fileDelete: Filename is empty" return 0 fi rm -f "${1}" if [ $? -ne 0 ] then myError "fileDelete: Can't remove file" return 0 fi return 1 } #F01.10.04 #Function : iniGetValue, Get the value from a specified INI file #Syntaxe : iniGetValue [] #Return : 1=OK, 0=Fail #Notes : Default way of working is to set __iniGetValue. If the third arg is # set the is set with the result. iniGetValue() { myDebug 5 "iniGetValue(${@})" if [ -z "${1}" ] then myError "iniGetValue: Filename is empty" return 0 fi if [ -z "${2}" ] then myError "iniGetValue: Variable name is empty" return 0 fi _="$( grep -E "^[[:space:]]*${2}[[:space:]]*::" "${1}" )" if [ -z "${_}" ] then myError "iniGetValue: Variable name not found" return 0 elif [ $( echo "${_}" | wc -l ) -gt 1 ] then myError "iniGetValue: Variable name found more than once" return 0 else export __iniGetValue="${_#*::}" if [ ! -z ${3} ] then export eval "${2}=${__iniGetValue}" fi return 1 fi } #F01.11.04 #Function : iniParseFile, Set all values from a specified INI file #Syntaxe : iniParseFile #Return : 1=OK, 0=Fail #Notes : All non commented variables are setted if they are valid iniParseFile() { myDebug 5 "iniParseFile(${@})" if [ -z "${1}" ] then myError "iniParseFile: Filename is empty" return 0 fi # set -- $( eval $( awk -F'::' ' BEGIN { anyValueReturned=0; } ( $0 !~ /^[ \t]*(#.*)?$/ && $1 ~ /^[ \t]*_?_?[0-9a-zA-Z][_0-9a-zA-Z]*[^_]+[ \t]*$/ ) { anyValueReturned++; gsub(" ","",$1); print "export " $1 "=\"" $2 "\";" ; } END { print "export anyValueReturned=" anyValueReturned ";" ; } ' "${1}" ) return ${anyValueReturned} } #F01.12.03 #Function : trim, Remove all blank from begining and end #Syntaxe : trim #Return : 1=OK #Notes : export the trimmed variable in __trim. # : All catacters \t, \n, \r and space are removed. trim() { myDebug 5 "trim(${@})" # Usage of sed #__trim=$( echo "${@}" | sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' ) # Usage of shell __trim="${@}" ltrim "${@}" rtrim "${__ltrim}" __trim="${__rtrim}" unset ltrim unset rtrim export __trim return 1 } #F01.13.03 #Function : logRotate, Transfer the begining of a file to the rotationned version. #Syntaxe : logRotate #Return : 1=OK, 0=Fail #Notes : Log rotation couldn't be under 10 lines #Notes : The rotated log file are suffixed by the date of day. logRotate() { myDebug 5 "logRotate(${@})" fileCheck "${1}" if [ ${?} -ne 1 ] then myError "logRotate: can't find logfile" return 0 fi if [ -z "${2}" -o ${2} -lt 10 ] then _maxLine=10 else _maxLine=${2} fi _lines=$( wc -l "${1}" | awk '{print $1}' ) let _linesToDrop=${_lines}-${_maxLine}+1 if [ ${_lines} -gt ${_maxLine} ] then head -${_linesToDrop} "${1}" >> "${1}.$( date '+%Y%m%d' )" if [ $? -ne 0 ] then myError "logRotate: can't transfer head lines" return 0 fi sed -i "1,${_linesToDrop}d" "${1}" if [ $? -ne 0 ] then myError "logRotate: can't delete head lines" return 0 fi fi return 1 } #F01.14.01 #Function : logMe, Send the log to the log files #Syntaxe : logMe #Return : 1=OK #Notes : Even if the log fail, it returns 1 #Notes : Log are systematically rotated before writting. #Notes : Log are systematically sent into the verbose log. #Notes : Variables __cmdPERL, __logVerbose, __logDir, __logFile, __logFileV, # __logFileSize, __logFileVSize must be defined before the call. logMe() { _="[$( date '+%d/%m/%Y %H%M%S' )]" if [ ${__cmdPERL} -eq 1 ] then _="[$( perl -e 'print time();' )]${_}" fi _="${_} --> ${@}" if [ -z "${__logVerbose}" ] then logRotate "${__logDir}/${__logFile}" ${__logFileSize} p2s "${_}" >> "${__logDir}/${__logFile}" fi logRotate "${__logDir}/${__logFileV}" ${__logFileVSize} p2s "${_}" >> "${__logDir}/${__logFileV}" return 1 } #F01.15.01 #Function : logMeV, Send the log to the verbose log files #Syntaxe : logMeV #Return : 1=OK #Notes : Even if the log fail, it returns 1 logMeV() { export __logVerbose=1 logMe "${@}" unset __logVerbose return 1 } #F01.16.03 #Function : isFunction, Verify that the function exists #Syntaxe : isFunction [<1>] #Return : 1=OK, 0=Fail #Notes : Set the 2nd arg to check either programme or function isFunction() { myDebug 5 "isFunction(${@})" if [ -z "${1}" ] then myError "isFunction: Function name is empty" return 0 fi __="$(command -v "${1}")" if [ "${1}" = "${__}" ] then return 1 elif [ ! -z "${2}" ] then if [ "${1}" = "${__##*/}" ] then return 1 else myError "isFunction: Function name or programme is unknown" return 0 fi else myError "isFunction: Function name is unknown" return 0 fi } #F01.17.07 #Function : sprintf, return a formatted string #Syntaxe : sprintf #Return : 1=OK #Notes : __sprintf contains the result of the sprintf call sprintf() { myDebug 5 "sprintf(${@})" _=${#} __sprintf="${1}" shift i=1 while [ $i -ne ${_} ] do __sprintf="${__sprintf%%%s*}${1}${__sprintf#*%s}" let i=$i+1 shift done export __sprintf return 1 } #F01.18.02 #Function : ltrim, Remove all blank from begining #Syntaxe : ltrim #Return : 1=OK #Notes : export the ltrimmed variable in __ltrim # : All catacters \t, \n, \r and space are removed. ltrim() { myDebug 5 "ltrim(${@})" # Usage of sed #__ltrim=$( echo "${@}" | sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' ) # Usage of shell __ltrim="${@}" __ltrim="${__ltrim##+( )}" __ltrim="${__ltrim##+(\t)}" __ltrim="${__ltrim##+(\r)}" __ltrim="${__ltrim##+(\n)}" export __ltrim return 1 } #F01.19.02 #Function : rtrim, Remove all blank from end #Syntaxe : rtrim #Return : 1=OK #Notes : export the rtrimmed variable in __rtrim # : All catacters \t, \n, \r and space are removed. rtrim() { myDebug 5 "rtrim(${@})" # Usage of sed #__rtrim=$( echo "${@}" | sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*$//g' ) # Usage of shell __rtrim="${@}" __rtrim="${__rtrim%%+( )}" __rtrim="${__rtrim%%+(\n)}" __rtrim="${__rtrim%%+(\t)}" __rtrim="${__rtrim%%+(\r)}" export __rtrim return 1 } #F01.20.01 #Function : _str2lower, lower all characters #Syntaxe : _str2lower #Return : 1=OK #Notes : export the text variable in __str2lower # : Just because this fucking KSH doesn't yet support the Case modification like Bash _str2lower() { myDebug 5 "_str2lower(${@})" _alphaHigh="ABCDEFGHIJKLMNOPQRSTUVWXYZ" _alphaLow="abcdefghijklmnopqrstuvwxyz" _incr=0 __str2lower="${@}" while [ $_incr -ne ${#_alphaHigh} ] do _upper=${_alphaHigh:$_incr:1} _lower=${_alphaLow:$_incr:1} __str2lower="${__str2lower//$_upper/$_lower}" let _incr=${_incr}+1 done export __str2lower return 1 } #F01.21.01 #Function : _str2upper, upper all characters #Syntaxe : _str2upper #Return : 1=OK #Notes : export the text variable in __str2upper # : Just because this fucking KSH doesn't yet support the Case modification like Bash _str2upper() { myDebug 5 "_str2upper(${@})" _alphaHigh="ABCDEFGHIJKLMNOPQRSTUVWXYZ" _alphaLow="abcdefghijklmnopqrstuvwxyz" _incr=0 __str2upper="${@}" while [ $_incr -ne ${#_alphaHigh} ] do _upper=${_alphaHigh:$_incr:1} _lower=${_alphaLow:$_incr:1} __str2upper="${__str2upper//$_lower/$_upper}" let _incr=${_incr}+1 done export __str2upper return 1 } #F01.22.01 #Function : textFill, Create a text filled by the caracter #Syntaxe : textFill #Return : 1=OK #Notes : export the text variable in __textFill textFill() { myDebug 5 "textFill(${@})" _pattern="${1:= }" if [ ${#_pattern} -gt 1 ]; then _pattern="${_pattern[0]}"; fi __textFill='' _incr=0 while [ ${_incr} -lt ${2:=10} ] do __textFill="${__textFill}${1}" let _incr=${_incr}+1 done export __textFill return 1 } #F01.23.03 #Function : textBox, Create a box around the text #Syntaxe : textBox <(left|center|right)> #Return : 1=OK #Notes : export the text variable in __textBox # : All catacters \t, \n, \r and space are removed. # : When used with Bash the function _textBox4Bash is called. textBox() { myDebug 5 "textBox(${@})" # if only KSH support <(cmd) for the while; do ; done < like Bash if [ "x${BASH_VERSION}" != "x" -a "x${KSH_VERSION}" = "x" ] then if [ "x${__BASH_EXTENSION}" = 'xloaded' ] then case "${#}" in 1) _textBox4Bash "${1}" ;; 2) _textBox4Bash "${1}" "${2}" ;; 3) _textBox4Bash "${1}" "${2}" "${3}" ;; esac return 1 else myError "textBox: Bash Extention is not loaded ! Can't call _textBox4Bash. Aborting..." export __textBox="${1}" return 0 fi fi _max=0 # if only KSH supoprt Case modificatino like Bash # if [ ! -z "${3}"]; then _align="${3}"; _align="${_align,,}"; else _align="left"; fi if [ ${#} -gt 2 ] then _str2lower "${3}" _align="${__str2lower}" else _align="left" fi eval $( p2s "${1}" | while read _string do if [ ${#_string} -gt ${_max} ]; then export _max=${#_string}; fi p2s "_max=${_max};" done ) let _maxRow=${_max}+4 _padding="${2}" __textBox='' p2s "${1}" | while read _string do _subString="" case "${_align}" in center) let "_padLeft=(${_max}-${#_string})/2" let _padRight=${_max}-${#_string}-${_padLeft} textFill ' ' ${_padLeft} _subString="${_padding} ${__textFill}${_string}" textFill ' ' ${_padRight} _subString="${_subString}${__textFill} ${_padding}" ;; left|right|*) let _pad=${_max}-${#_string} textFill ' ' ${_pad} if [ "${_align}" = "right" ] then _subString="${_padding} ${__textFill}${_string} ${_padding}" else _subString="${_padding} ${_string}${__textFill} ${_padding}" fi ;; esac __textBox="${__textBox}${_subString}\n" done textFill "${2}" ${_maxRow} __textBox="${__textFill}\n${__textBox}${__textFill}" export __textBox return 1 } #F01.24.05 #Function : parseHeadersForHelp, Print headers of function for a given file #Syntaxe : parseHeadersForHelp [fn] [s|search ] #Return : 1=OK, O=FAIL #Notes : fn argument just return the function name # : s or search argument just return the function when name is matching # : Headers must begin with #F??. (where ?? are two characters), # : and each line after must begin with a #. parseHeadersForHelp() { myDebug 5 "parseHeadersForHelp(${@})" _onlyFunctionName=0; _searchFunction=0; _searchFunctionName='' _fileName="${1}" shift fileCheck "${_fileName}" if [ ${?} -ne 1 ] then oops "Can't open/read File '${_fileName}' (error: ${__lastError})" fi while [ $# -ne 0 ] do _str2lower "${1}" if [ ${_searchFunction} -eq 1 -a -z "${_searchFunctionName}" ] then _searchFunctionName="${1}" elif [ "x${__str2lower}x" = 'xfnx' ] then _onlyFunctionName=1 elif [ "x${__str2lower}x" = 'xsx' -o "x${__str2lower}x" = 'xsearchx' ] then _searchFunction=1 else myError "parseHeadersForHelp: Argument '${1}' is unknown !" fi shift done if [ -z "${_searchFunctionName}" ] then _searchFunction=0 fi _beginBlock=0 _forceEndBlock=0 while read a do if [ "x${a:0:2}x" = 'x#Fx' -a "x${a:4:1}x" = 'x.x' ] then _beginBlock=1 _headers="\033[01;31m${a}\033[0m\n" elif [ "x${a:0:1}x" = 'x#x' -a ${_beginBlock} -eq 1 ] then if [ ${_searchFunction} -eq 1 -a "x${a%% : *}x" = 'x#Functionx' ] then if [ "x${a//*$_searchFunctionName*/$_searchFunctionName}x" != "x${_searchFunctionName}x" ] then _beginBlock=0 fi fi if [ ${_onlyFunctionName} -eq 0 ] then _headers="${_headers}${a}\n" elif [ "x${a%% : *}x" = 'x#Functionx' ] then _headers="${_headers}${a}\n" fi elif [ ${_beginBlock} -eq 1 -o ${_forceEndBlock} -eq 1 ] then _beginBlock=0 _forceEndBlock=0 p2s "${_headers}" fi done < "${_fileName}" } # ############################################################################ # # Library related Functions # ############################################################################ # set # As Prerequisites for Bash the subLibrary _common4bash.lib is needed # But it will not stop your program if not loaded if [ "x${BASH_VERSION}" != "x" -a "x${KSH_VERSION}" = "x" ] then . "$(cd ${BASH_SOURCE%/*}; pwd)/_common4bash.lib" fi #F01.99.02 #Function : F01Version, Return/Print version information #Syntaxe : F01Version [<1>] #Return : 1=OK #Notes : __F01Version contains the result F01Version() { myDebug 1 "F01Version(${1})" export __F01Version="common.lib v${F01VERSION} / p${F01PROTOCOL}" if [ -z "${1}" ]; then p2s "${__F01Version}"; fi return 1 } # +---------------------------------------------------------------------------+ # # | Last subversion informations : # | $Revision: 24 $ : $Date: 2010-04-21 15:35:12 -0700 (mer, 21 avr 2010) $ # | $Author: flyounet $ # +---------------------------------------------------------------------------+ #