#!/bin/bash (source) Import File Import String Import OptionParser function Get_Architecture() { [ "$1" = "" ] && arch=`uname -m` || arch="$1" case "$arch" in arm*) arch=arm ;; "Power Macintosh") arch=ppc ;; esac kernelType="`uname -s`" case "$kernelType" in [lL]inux) kernelType="";; CYGWIN*) kernelType=".Cygwin" ;; *) kernelType=".$kernelType" ;; esac echo "$arch$kernelType" } function Get_Version() { Parameters "$@" package version ! Exists "$goboPrograms/$package/$version" && return 1 realversion="$(basename "$(readlink -f "$goboPrograms/$package/$version")")" if Is_Directory "$goboPrograms/$package/$realversion" then echo "$realversion" return 0 fi return 1 } function Parse_Version() { Parameters "$@" url if Starts_With "http" "$url" then echo `echo "$url" | sed s/'.*--\(.*\)--.*'/'\1'/` return 0 else echo `Get_Token "$url" "/" "-1"` return 0 fi } function Gen_Env_Cache() { Log_Verbose "Rebuilding environment cache..." cache="$goboEnvironment"/Cache echo -e "unset unsetvars\n[ ! \"\$goboSettings\" ] && unsetvars=true\n" > "$cache" echo -e "source GoboPath\n" >> "$cache" for linkedfile in "$goboEnvironment"/*--* do [ -f "$linkedfile" ] && cat "$linkedfile" >> "$cache" echo >> "$cache" done unsetvars="$(grep export "$goboExecutables/GoboPath" | awk {'print $1 " " $2'} | grep "^export" | sed -e 's,=.*,,g' -e 's,export,unset,g')" echo -e "if [ \"\$unsetvars\" ]\nthen\n$unsetvars\nfi\n" >> "$cache" } # Creates links from one directory into another. function Link_Directory() { Parameters "$@" from to relative overwrite local rfrom here j rfrom=`readlink -f "$from"` if ! Is_Empty_Directory "$rfrom" then here=`readlink -f "$to"` [ -d "$here" ] || mkdir "$here" Quiet pushd "$here" if [ -n "$(type -p LinkOrExpandAll)" ] then LinkOrExpandAll "$rfrom" "$overwrite" "$relative" else Log_Error "LinkOrExpandAll not found. Fallback to python code." Boolean "relative" && relative="True" || relative="False" Link_Or_Expand_All "$rfrom" "$overwrite" "$relative" fi Quiet popd else Quiet rmdir "$rfrom" fi } function Link_Or_Expand_All() { ### python Link_Or_Expand_All CHANGELOG # 14/09/2004 - [detsch] Experimental relative option # 10/09/2004 - [detsch] Changed Log() function following David Smith sugestion & code # 23/05/2004 - [detsch] try:except and checking goboPrograms[-1]=='/' on belongs_to_same_app() # 01/04/2004 - [detsch] bugfix: now checking if os.path.islink(bn) when # belongs_to_same_app() before trying to link file (3:) # 29/03/2004 - [detsch] ported Log functions (no more os.system()) # 29/03/2004 - [detsch] first version released for testing # 27/03/2004 - [detsch] major code reorganization, # fixing "SymlinkProgram TeTeX" (bin/* allways overwrite) bug # 21/03/2004 - [detsch] "line-by-line" port based on bash Link_Or_Expand running # Parameters "$@" rfrom conflict relative python -c """ import os, string, os.path import thread def Log(mode, s) : # from Scripts/Current/Functions/Terminal: colorGray='\033[1;30m' colorBoldBlue='\033[1;34m' colorBrown='\033[33m' colorYellow='\033[1;33m' colorBoldGreen='\033[1;32m' colorBoldRed='\033[1;31m' colorCyan='\033[36m' colorBoldCyan='\033[1;36m' colorRedWhite='\033[41;37m' colorNormal='\033[0m' if mode == 'Debug' : color = colorRedWhite elif mode == 'Verbose' : color = colorNormal elif mode == 'Normal' : color = colorCyan elif mode == 'Terse' : color = colorBoldCyan elif mode == 'Error' : color = colorBoldRed else : color = colorNormal fdName = mode.lower()+'FD' fdString = os.environ[fdName] fdNum = int(fdString) os.write(fdNum, colorGray+os.environ['scriptName']+':'+colorNormal+' '+color+s+colorNormal+'\n') def Log_Debug(s) : Log('Debug', s) def Log_Verbose(s) : Log('Verbose', s) def Log_Normal(s) : Log('Normal', s) def Log_Terse(s) : Log('Terse', s) def Log_Error(s) : Log('Error', s) count = 0 def Link_Or_Expand(new, conflict, relative = False) : global count count = count + 1 goboPrograms = os.environ['goboPrograms'] if relative : relativegoboprograms = goboPrograms prefix = '' if os.environ.has_key('goboPrefix') : prefix = os.environ['goboPrefix'] candidate = goboPrograms[len(prefix):] if candidate[0] == '/' : candidate = candidate[1:] for i in range(10) : candidate = '../'+candidate if os.access(candidate, os.W_OK) : relativegoboprograms=candidate break def points_to(p) : if os.access(p, os.R_OK) : return os.path.realpath(os.path.realpath(p)) if os.path.islink(p) : points = os.path.realpath(os.path.realpath(p)).replace('/share/', '/Shared/') if points[0] != '/' : points = os.path.dirname(p)+'/'+points return points else : return p def link_inside() : Log_Verbose('Linking files from \'%s\' in directory \'%s\'...'%(realnew, bn)) os.chdir(bn) for i in os.listdir(realnew) : Link_Or_Expand(realnew+'/'+i, conflict, relative) os.chdir('..') def belongs_to_same_app() : try : pstrlen = len(goboPrograms) # if goboPrograms ends with an '/' if goboPrograms[-1] == '/' : pstrlen = pstrlen - 1 # realnew instead of new because of typical MergeTree links appnewname = realnew[pstrlen:].split('/')[1] appoldname = realold[pstrlen:].split('/')[1] # added: case-insensitive if appnewname.lower() == appoldname.lower() : return 1 else : return 0 except : return 0 def create_single_link(orig, dest) : if relative : relativeorig = orig.replace(os.path.realpath(goboPrograms), relativegoboprograms) os.symlink(relativeorig, './'+dest) else : os.symlink(orig, './'+dest) realnew = points_to(new) bn = os.path.basename(new) # if name of new is not being used, or is used by an broken link... if not os.access(bn, os.R_OK) : # if is a broken link, remove it if os.path.islink(bn) : os.unlink(bn) realold = '' else : realold = os.path.realpath(os.path.realpath(bn)) # 1: new is a broken link if not os.access(realnew, os.R_OK) : Log_Debug('Skipping %s because it is a broken link under %s.'%(new, goboPrograms)) # 'TODO: Do nothing by now. I'm not sure what the correct behavior' # 2: name of new is not being used elif not realold : Log_Verbose('Creating link: %s'%bn) Log_Debug('symlink1 %s ./%s'%(realnew,bn)) create_single_link(realnew, bn) # 3: probably upgrading a program version elif os.path.islink(bn) and belongs_to_same_app() : Log_Verbose('Replacing link: %s'%new) Log_Debug('symlink2 %s ./%s'%(realnew,bn)) os.unlink('./'+bn) create_single_link(realnew, bn) # 4: name of new was being used by an directory (probably with links) elif (not os.path.islink(bn)) and os.path.isdir(bn) and os.path.isdir(realnew) : link_inside() # 5: name of new was being used by an link to a directory elif os.path.islink(bn) and os.path.isdir(realold) and os.path.isdir(realnew) : Log_Normal('Creating expanded directory \'%s\'...'%(bn)) os.unlink(bn) os.makedirs(bn) os.chdir(bn) Log_Verbose('Linking files from \'%s\' in directory \'%s\'...'%(realold, bn)) for i in os.listdir(realold) : oldbn=os.path.basename(i) create_single_link(realold+'/'+i, oldbn) os.chdir('..') link_inside() # 6: conflict for a same name elif not os.path.isdir(realold) and not os.path.isdir(realnew): Log_Error('Conflict: %s'%realold) if conflict == 'overwrite' or conflict == 'o' : os.unlink('./'+bn) create_single_link(realnew, bn) Log_Normal('Replaced with: %s'%realnew) # 7: if one of [realold, realnew] is a dir and the other isn't, we have an "unsolvable" conflict elif (not os.path.isdir(realold) and os.path.isdir(realnew)) or (os.path.isdir(realold) and not os.path.isdir(realnew)) : Log_Error('Conflict: cannot create expanded directory \'%s\'.'%(bn)) # 0 else : Log_Error('SERIOUS: Should never enter here') # islink(bn) isdir(realold) isdir(realnew) # 0 0 0 -> 6 # 0 0 1 -> 7 # 0 1 0 -> 7 # 0 1 1 -> 4 # 1 0 0 -> 6 # 1 0 1 -> 7 # 1 1 0 -> 7 # 1 1 1 -> 5 for i in os.listdir('$rfrom') : Link_Or_Expand('$rfrom'+'/'+i, '$conflict', $relative) if count == 1 : Log_Normal('Processed %s file.'%(count)) else : Log_Normal('Processed %s files.'%(count)) """ } # function Bash_Link_Or_Expand_All() { # Parameters "$@" rfrom conflict # for j in "$rfrom"/* # do # Link_Or_Expand "$j" "$conflict" # done # } # function Link_Or_Expand() { # Parameters "$@" new conflict # local realnew=`readlink -f "$new"` # local bn # # function prepare_directory() { # Log_Normal "Creating expanded directory '$bn'..." # local realold=`readlink -f "$bn"` # if ! [ -d "$realold" -a -d "$realnew" ] # then # Log_Error "Conflict: cannot create expanded directory '$bn'." # return 1 # fi # rm "$bn" # mkdir "$bn" # cd "$bn" # Log_Verbose "Linking files from '$realold' in directory '$bn'..." # local i # for i in "$realold/"* # do # oldbn=`basename "$i"` # ln -nfs "$i" ./"${oldbn}" # done # cd .. # return 0 # } # # function link_inside() { # Log_Verbose "Linking files from '$realnew' in directory '$bn'..." # cd "$bn" # local i # ls -A "$realnew" | while read i # do # Link_Or_Expand "$realnew/$i" "$conflict" # if [ $? -gt 0 ] # then # cd .. # return 1 # fi # done # cd .. # return 0 # } # # function file_conflict() { # Parameters "$@" filename # Log_Error "Conflict: $filename" # if [ "$conflict" = "overwrite" ] # then # ln -nfs "$realnew" ./"${bn}" # Log_Normal "Replaced with: $realnew" # fi # return 0 # } # # function prepare_and_link() { # prepare_directory && link_inside # } # # function overwrite_file() { # Parameters "$@" errfunc # appnewdir=`echo "${new}" | sed -n "s#\\(${goboPrograms}/[^/]*\\)/.*#\\1#p"` # thistemp=`readlink -f "${bn}"` # appolddir=`echo "${thistemp}" | sed -n "s#\\(${goboPrograms}/[^/]*\\)/.*#\\1#p"` # if [ "${appnewdir}" == "${appolddir}" ] # then # Log_Verbose "Replacing link: ${new}" # Log_Debug "ln -snf $realnew ./${bn}" # ln -snf "$realnew" ./"${bn}" # else # $errfunc "$thistemp" # return $? # fi # } # # bn=`basename "$new"` # # function points_to() { # if [ -L "$1" ] # then # points=`ls -l "$1" | sed 's/.* -> //;s@/share/@/Shared/@'` # if ! [ "`echo "$points" | cut -b1`" = "/" ] # then # points=`dirname "$1"`/"$points" # fi # echo $points # else echo "$1" # fi # } # # if Is_Broken_Link "$bn" # then # rm "$bn" # fi # # if [ -L "$new" ] && ! Exists `points_to "$new"` # then # : # Log_Debug "Skipping $new because it is a broken link under ${goboPrograms}." # # TODO: Do nothing by now. I'm not sure what the # # correct behavior is. # elif [ -L "$new" ] && Exists `points_to "$new"` # then # Log_Verbose "Creating link: $bn" # Log_Debug "ln -nfs "`points_to "$new"`" ./${bn}" # ln -nfs `points_to "$new"` ./"${bn}" # elif [ -L "$bn" -a -d "$realnew" ] # then # overwrite_file prepare_and_link # return $? # elif [ -d "$bn" ] # then # link_inside # elif ! [ -e "$bn" ] # then # Log_Verbose "Creating link: $bn" # Log_Debug "ln -nfs $realnew ./${bn}" # ln -nfs "$realnew" ./"${bn}" # else # overwrite_file file_conflict # return $? # fi # } function Reverse_Link_Or_Expand() { local source destination Parameters "$@" source destination local rsource bsource dsource file rsource=`readlink -f "$source"` bsource=`basename "$rsource"` dsource=`dirname "$rsource"` if [ "$rsource" == "$destination/$bsource" ] then # Already symlinked. Don't do anything return fi if ! Exists "$destination/$bsource" then # No conflicts in $destination if ! Is_Directory $rsource || Is_Empty_Directory $rsource then mv "$source" "$destination" Verbose ln -vs "$destination/$bsource" "$dsource" else Make_Directory "$destination/$bsource" cd "$rsource" for file in "$rsource"/* do Reverse_Link_Or_Expand "$file" "$destination/$bsource" done cd .. fi else # We have a conflict, try to handle it if Is_Nonempty_Directory "$rsource" && Is_Directory "$destination/$bsource" then cd "$rsource" for file in "$rsource"/* do Reverse_Link_Or_Expand "$file" "$destination/$bsource" done cd .. elif Is_Empty_Directory "$rsource" then Verbose rmdir "$rsource" Verbose ln -vs "$destination/$bsource" "$dsource" else Log_Error "Conflict: $bsource already exists at $destination." fi fi } function Pre_Installation_Preparation() { local appname="$(GuessProgramCase $1)" local appver="$2" local linkedfiles=() if [ -e "${goboPrograms}/${appname}/${appver}" ] then Progress_Start "Making a safe copy of installed version" programfiles=$(ListProgramFiles --no-resources "${goboPrograms}/${appname}/${appver}") $sudo_exec mkdir -p "${goboPrograms}/${appname}/${appver}-safelinking" $sudo_exec cp -a "${goboPrograms}/${appname}/${appver}"/* "${goboPrograms}/${appname}/${appver}-safelinking" for f in ${programfiles[@]} do f="${goboPrograms}/${appname}/${appver}/${f}" systempath="$(Programs_To_System_Path "${f}")" [ ! -L "${systempath}" ] && continue [ "$(dirname "$(readlink -f "${systempath}")")" != "$(dirname "${f}")" ] && continue [ -n "${systempath}" ] && { Progress_Move $sudo_exec ln -sf "$(System_To_Programs_Path "${systempath}" "${goboPrograms}/${appname}/${appver}-safelinking")" "$(dirname ${systempath})" } done Progress_End $sudo_exec rm -rf "${goboPrograms}/${appname}/${appver}" $sudo_exec ldconfig fi } function Post_Installation_Clean_Up() { appname="$1" appver="$2" failedinstallation="$3" if [ ! -n "$appname" -o ! -n "$appver" ] then return fi if [ "${failedinstallation}" = "failed" ] then [ -d "${goboPrograms}/${appname}/${appver}-failed" ] && $sudo_exec rm -rf "${goboPrograms}/${appname}/${appver}-failed" if [ -e "${goboPrograms}/${appname}/${appver}" -a "$(readlink -f ${goboPrograms}/${appname}/Current)" != "${goboPrograms}/${appname}/${appver}" ] then $sudo_exec mv "${goboPrograms}/${appname}/${appver}" "${goboPrograms}/${appname}/${appver}-failed" Log_Terse "Installation was moved to ${goboPrograms}/${appname}/${appver}-failed" fi if [ -e "${goboPrograms}/${appname}/${appver}-safelinking" ] then Progress_Start "Restoring safe copy" programfiles=$(ListProgramFiles --no-resources "${goboPrograms}/${appname}/${appver}-safelinking") $sudo_exec mkdir -p "${goboPrograms}/${appname}/${appver}" $sudo_exec cp -a "${goboPrograms}/${appname}/${appver}-safelinking"/* "${goboPrograms}/${appname}/${appver}" for f in ${programfiles[@]} do f="${goboPrograms}/${appname}/${appver}/${f}" systempath="$(Programs_To_System_Path "${f}")" [ ! -L "${systempath}" ] && continue [ "$(dirname "$(readlink -f "${systempath}")")" != "$(dirname "${f}")" ] && continue [ -n "${systempath}" ] && { Progress_Move $sudo_exec ln -sf "$(System_To_Programs_Path "${systempath}" "${goboPrograms}/${appname}/${appver}")" "$(dirname ${systempath})" } done Progress_End fi fi $sudo_exec rm -rf "${goboPrograms}/${appname}/${appver}-safelinking" $sudo_exec ldconfig } function Dependencies_File() { Parameters "$@" package version function attempt_file() { if Is_File "$1" then Quiet mkdir -p "$correctdir" && \ Quiet mv "$1" "$correctfile" && \ echo "$correctfile" && \ return 0 echo "$1" return 0 fi return 1 } local correctdir="$goboPrograms/$package/$version/Resources" local correctfile="$correctdir/Dependencies" attempt_file "$correctfile" && return 0 # XXX backwards compatibility attempt_file "$goboPrograms/$package/$version/.dependencies" && return 0 Log_Verbose "Dependency file not found. Searching for old version." attempt_file "$goboPrograms/$package/.dependencies" && return 0 return 1 } # Whenever you need to strip ${goboPrograms} off from a path, # use this function. It's the bullet-proof way to do so. function Strip_Gobo_Programs() { sed "s@^${goboPrograms}@@;s@^$(readlink -f "${goboPrograms}")@@;s@^/Programs\(/[^/]*/[^/]*/[^/]*/[^/]*\)@\1@;s@^/*@@" } function Programs_To_System_Path() { programpath="$1" newpath="$(echo "$programpath" | Strip_Gobo_Programs | sed -e 's,/*[^/]*/*[^/]*/*\(.*\),\1,' \ -e "s,/*lib/\(.*\),${goboLibraries}/\1,g" \ -e "s,/*sbin/\(.*\),${goboExecutables}/\1,g" \ -e "s,/*bin/\(.*\),${goboExecutables}/\1,g" \ -e "s,/*man/\(.*\),${goboManuals}/\1,g" \ -e "s,/*include/\(.*\),${goboHeaders}/\1,g" \ -e "s,/*Shared/\(.*\),${goboShared}/\1,g" \ -e "s,/*info/\(.*\),${goboManuals}/info/\1,g" \ -e "/^\/*doc\/.*/d" \ -e "s,/*Resources/Wrappers/\(.*\),${goboExecutables}/\1,g" \ -e "s,/*Resources/Tasks/\(.*\),${goboTasks}/\1,g" \ -e "s,/*Resources/Environment/\(.*\),${goboEnvironment}/\1,g")" [ "${programpath}" == "${newpath}" ] && return 1 [ -n "${newpath}" ] && echo "${newpath}" return 0 } System_To_Programs_Path() { systempath="$1" programprefix="$2" if [ -L "$systempath" ] then olddir="$(readlink -f "${systempath}" | Strip_Gobo_Programs | cut -d/ -f3)" #sed -e 's,/*[^/]*/*[^/]*/*\([^/]*\)/.*,\1,')" newpath="$(echo "$systempath" | sed \ -e "s,${goboLibraries}/\(.*\),${programprefix}/${olddir}/\1,g" \ -e "s,${goboExecutables}/\(.*\),${programprefix}/${olddir}/\1,g" \ -e "s,${goboManuals}/info/\(.*\),${programprefix}/info/\1,g" \ -e "s,${goboManuals}/\(.*\),${programprefix}/${olddir}/\1,g" \ -e "s,${goboHeaders}/\(.*\),${programprefix}/${olddir}/\1,g" \ -e "s,${goboShared}/\(.*\),${programprefix}/${olddir}/\1,g" \ -e "s,${goboTasks}/\(.*\),${programprefix}/Resources/Tasks/\1,g" \ -e "s,${goboEnvironment}/\(.*\),${programprefix}/Resources/Environment/\1,g")" else newpath="$(echo "$systempath" | sed \ -e "s,${goboLibraries}/\(.*\),${programprefix}/lib/\1,g" \ -e "s,${goboExecutables}/\(.*\),${programprefix}/bin/\1,g" \ -e "s,${goboManuals}/info/\(.*\),${programprefix}/info/\1,g" \ -e "s,${goboManuals}/\(.*\),${programprefix}/man/\1,g" \ -e "s,${goboHeaders}/\(.*\),${programprefix}/include/\1,g" \ -e "s,${goboShared}/\(.*\),${programprefix}/Shared/\1,g" \ -e "s,${goboTasks}/\(.*\),${programprefix}/Resources/Tasks/\1,g" \ -e "s,${goboEnvironment}/\(.*\),${programprefix}/Resources/Environment/\1,g")" fi [ "${systempath}" == "${newpath}" ] && return 1 [ -n "${newpath}" ] && echo "${newpath}" return 0 } function Get_System_Paths () { Parameters "$1" programpath while read path do Programs_To_System_Path "${path}" done < <(find "${programpath}" -mindepth 2 ) } # Inspect to which package a linked file points to function Which_Package() { which "$1" | Strip_Gobo_Programs | cut -d/ -f1 } # Inspect the version of the package a linked file points to function Which_Version() { readlink -f "$1" | Strip_Gobo_Programs | cut -d/ -f2 } progname=`Which_Package "$0"` conflocations=( "${goboUserSettings}/$progname" "${goboUserSettings}/Scripts" "${goboUserSettings}" "${goboSettings}/$progname" "${goboSettings}/Scripts" "${goboSettings}" "${scriptPath}" "/System/Settings/Scripts" "/System/Settings/$progname" ) function Find_Conf() { for conf in "${conflocations[@]}" do if Exists "$conf/$1" then echo "$conf/$1" return 0 fi done return 1 } function Color_Output() { if Quiet which mtail then mtail --silent --config="`Find_Conf $1.mtailrc`" else cat fi } # Parse the script's configuration function Parse_Conf() { scriptname=$(Downcase $(basename $0)) local mandatory='' if [ "$1" = "--mandatory" ] then mandatory=yes shift fi for conf in "${conflocations[@]}" do if Exists "$conf/$1" then source "$conf/$1" || return 2 for option in "${optionsLongList[@]}" do opt=$(echo "$option" | sed 's/-/_/') value="$(eval echo \$${scriptname}Option_${opt})" if [ -n "${value}" ] then Set_Option "$option" "${value}" || Die "Bad configuration ${scriptname}Option_${opt}=$value in $conf/$1" fi done return 0 fi done if [ "$mandatory" = "yes" ] then Die "Could not find required configuration file $1." fi return 1 } Verify_Superuser() { Verify_Superuser_ "${savedOptions[@]}" } # Respawn the script with normal permissions. function Release_Superuser() { if [ -n "$SUDO_USER" -a "$SUDO_USER" != "$USER" ] && [ "$UID" = "0" ] then Log_Verbose "Running as $SUDO_USER..." # Revalidate password for another 5 mins. $sudo_validate # Run with superuser's HOME. exec $sudo_exec -u "$SUDO_USER" -H env $0 "${savedOptions[@]}" fi } # Respawn the script, asking for root privileges function Try_Superuser() { if [ $UID != 0 ] then echo "You may run this with superuser privileges (press Enter to skip)..." sudo -u "#0" $0 "$@" && exit fi } function Find_Superusername() { /usr/bin/python -c ' import pwd entry=pwd.getpwuid(0) print entry[0] ' }