File indexing completed on 2024-05-05 04:38:45

0001 #!/bin/sh
0002 
0003 # SPDX-FileCopyrightText: 2011 David Nolden <david.nolden.kdevelop@art-master.de>
0004 #
0005 # SPDX-License-Identifier: LGPL-2.0-or-later
0006 
0007 _shell=$(ps -cp $$ -o command="")
0008 
0009 # Since this runs as a replacement for the init-files, we need to chain in the 'real' rcs.
0010 # We ignore profile, login & logout rcs, as we want no login shells.
0011 case $_shell in
0012     zsh)
0013         alias shopt=':'
0014         test -f "$OLD_ZDOTDIR/.zshenv" && . "$OLD_ZDOTDIR/.zshenv"
0015         test -f "$OLD_ZDOTDIR/.zshrc"  && . "$OLD_ZDOTDIR/.zshrc"
0016         ;; #zsh still also sources the systemwide rcs when called with $ZDOTDIR set.
0017     bash)
0018         test -f ~/.bash_profile  && source ~/.bash_profile
0019         test -f /etc/bash.bashrc && source /etc/bash.bashrc
0020         test -f ~/.bashrc && source ~/.bashrc
0021         ;;
0022 esac
0023 
0024 if ! [ "$APPLICATION_HOST" ]; then
0025     export APPLICATION_HOST=$(hostname)
0026 fi
0027 
0028 if ! [ "$KDEV_SHELL_ENVIRONMENT_ID" ]; then
0029     export KDEV_SHELL_ENVIRONMENT_ID="default"
0030 fi
0031 
0032 if ! [ "$KDEV_DBUS_ID" ]; then
0033     echo "The required environment variable KDEV_DBUS_ID is not set. This variable defines the dbus id of the application instance which is supposed to be attached."
0034     exit 5
0035 fi
0036 
0037 # Eventually, if we are forwarding to another host, and kdevplatform_shell_environment.sh
0038 # has been located through "which kdevplatform_shell_environment.sh", then we need to update KDEV_BASEDIR.
0039 if ! [ -e "$KDEV_BASEDIR/kdevplatform_shell_environment.sh" ]; then
0040     KDEV_BASEDIR=$(dirname $(which kdevplatform_shell_environment.sh))
0041 fi
0042 
0043 if ! [ -e "$KDEV_BASEDIR/kdev_dbus_socket_transformer" ]; then
0044     echo "The $KDEV_BASEDIR/kdev_dbus_socket_transformer utility is missing, controlling the application across ssh is not possible"
0045 fi
0046 
0047 # Takes a list of tools, and prints a warning of one of them is not available in the path
0048 function checkToolsInPath {
0049     for TOOL in $@; do
0050         command -v $TOOL &> /dev/null || echo "The utility $TOOL is not in your path, the shell integration will not work properly."
0051     done
0052 }
0053 
0054 # Check if all required tools are there (on the host machine)
0055 checkToolsInPath sed ls cut dirname mktemp basename readlink hostname
0056 
0057 # special handling for qdbus variants
0058 _qdbus=qdbus-qt5
0059 if ! command -v $_qdbus &> /dev/null; then
0060     _qdbus=qdbus
0061     if ! command -v $_qdbus &> /dev/null; then
0062         echo "The utility qdbus (or qdbus-qt5) is not in your path, the shell integration will not work properly."
0063     fi
0064 fi
0065 
0066 if ! [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
0067     # Check for additional utilities that are required on the client machine
0068     checkToolsInPath kioclient5
0069 fi
0070 
0071 # Queries the session name from the running application instance
0072 function getSessionName {
0073     echo "$($_qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionName)"
0074 }
0075 
0076 function getSessionDir {
0077     echo "$($_qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionDir)"
0078 }
0079 
0080 function getCurrentShellEnvPath {
0081     local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID
0082     if [ "$1" ]; then
0083         ENV_ID=$1
0084     fi
0085 
0086     echo "$(getSessionDir)/${ENV_ID}.sh"
0087 }
0088 
0089 function help! {
0090     echo "You are controlling the $APPLICATION session '$(getSessionName)'"
0091     echo ""
0092     if [[ "$1" == "" ]]; then
0093     echo "Standard commands:"
0094     echo "raise!                                 - Raise the window."
0095     echo "sync!                                  - Synchronize the working directory with the currently open document. See \"help! sync\""
0096     echo "open!   [file] ...                     - Open the file(s) within the attached application. See \"help! open\""
0097     echo "eopen!  [file] ...                     - Open the file(s) within an external application using kde-open."
0098     echo "create!  [file] [[text]]               - Create and open a new file."
0099     echo "search!   [pattern] [[locations]] ...  - Search for the given pattern here or at the optionally given location(s)."
0100     echo "dsearch!  [pattern] [[locations]] ...  - Same as search, but starts the search instantly instead of showing the dialog (using previous settings)."
0101     echo "ssh!  [ssh arguments]                  - Connect to a remote host via ssh, keeping the control-connection alive. See \"help! remote\""
0102     echo ""
0103     echo "help!                                  - Show help."
0104     echo "help! open                             - Show extended help about file opening commands."
0105     echo "help! sync                             - Show extended help about path synchronization commands."
0106     echo "help! remote                           - Show extended help about remote shell-integration through ssh."
0107     echo "help! env                              - Show extended help about the environment."
0108     echo ""
0109     echo "Most commands can be abbreviated by the first character(s), eg. r! instead of raise!, and se! instead of search!."
0110     fi
0111 
0112     if [[ "$1" == "open" ]]; then
0113     echo "Extended opening:"
0114     echo "The open! command can also be used to open files in specific tool-view configurations, by adding split-separators:"
0115     echo "- Files around the / separator will be arranged horizontally by split-view."
0116     echo "- Files around the - separator will be arranged vertically by split-view."
0117     echo "- Parens [ ... ] can be used to disambiguate the hierarchy (there must be spaces between filename and paren)."
0118     echo "- If a file is missing around a separator, the currently active view is inserted into the position."
0119     echo ""
0120     echo "Examples:"
0121     echo "open! file1 / file2                 - The active view is split horizontally."
0122     echo "                                      file1 is opened in the left view, and file2 in the right view."
0123     echo "open! file1 / [ file2 - file3 ]     - The active view is split horizontally, and the right split-view is split vertically."
0124     echo "                                      file1 is opened in the left view, file2 in the right upper view, and file3 in the right lower view."
0125     echo "open! / file1                       - The active view is split horizontally."
0126     echo "                                    - The active document is kept in the left split-view, and file1 is opened in the right split-view."
0127     echo ""
0128     echo "Short forms: o! = open!, eo! = eopen!, c! = create!"
0129     fi
0130 
0131     if [[ "$1" == "sync" ]]; then
0132     echo "Extended syncing:"
0133     echo "sync!    [[project-name]]           - If no project-name is given, then the sync! command synchronizes to the currently active document."
0134     echo "                                      If no document is active, then it synchronizes to the currently selected item in the project tree-view."
0135     echo "                                      If a case-insensitive project name prefix is given, then it synchronizes to the base folder of the matching project."
0136     echo "syncsel!                            - Synchronizes to the currently selected item in the project tree-view, independently of the active document."
0137     echo "project! [[project-name]]           - Map from a path within the build directory to the corresponding path in the source directory."
0138     echo "                                      If we're already in the source directory, map to the root of the surrounding project."
0139     echo "bdir!    [[project-name]]           - Map from a path within the source directory to the corresponding path in the build directory."
0140     echo "                                      If we're already in the build directory, map to the root of the build directory."
0141     echo ""
0142     echo "Short forms: s! = sync!, ss! = syncsel!, p! = project!, b! = bdir!"
0143     fi
0144 
0145     if [[ "$1" == "remote" ]]; then
0146     echo "Extended remote commands:"
0147     echo "ssh!  [ssh arguments]                  - Connect to a remote host via ssh, keeping the control-connection alive."
0148     echo "                                       - The whole dbus environment is forwarded, KDevelop needs to be installed on both sides."
0149     echo "ssw!  [ssh arguments]                  - Like ssh!, but preserves the current working directory."
0150     echo "exec! [cmd] [args] [file] . ..         - Execute the given command on the client machine, referencing any number of local files on the host machine."
0151     echo "                                       - The file paths will be re-encoded as fish:// urls if required."
0152     echo "cexec! [cmd] [args] [file] . ..        - Execute the given command on the client machine, referencing any number of local files on the host machine."
0153     echo "                                       - The files will be COPIED to the client machine if required."
0154     echo "copytohost! [client path] [host path]  - Copy a file/directory through the fish protocol from the client machine th the host machine."
0155     echo "copytoclient! [host path] [client path]- Copy a file/directory through the fish protocol from the host machine to the client machine."
0156     echo ""
0157     echo "Short forms: e! = exec!, ce! = cexec!, cth! = copytohost!, ctc! = copytoclient!"
0158     fi
0159 
0160     if [[ "$1" == "env" ]]; then
0161       echo "Environment management:"
0162       echo "The environment can be used to store session-specific macros and generally manipulate the shell environment"
0163       echo "for embedded shell sessions. The environment is sourced into the shell when the shell is initialized, and"
0164       echo "whenever setenv! is called."
0165       echo ""
0166       echo "env!                                 - List all available shell environment-ids for this session."
0167       echo "setenv! [id]                         - Set the shell environmnet-id for this session to the given id, or update the current one."
0168       echo "editenv! [id]                        - Edit the current shell environment or the one with the optionally given id."
0169       echo "showenv! [id]                        - Show the current shell environment or the one with the optionally given id."
0170       echo ""
0171       echo "Short forms: sev! = setenv!, ee! = editenv!, shenv! = showenv!"
0172     fi
0173     echo ""
0174 }
0175 
0176 # Short versions of the commands:
0177 
0178 function r! {
0179     raise! $@
0180 }
0181 
0182 function s! {
0183     sync! $@
0184 }
0185 
0186 function ss! {
0187     syncsel!
0188 }
0189 
0190 function syncsel! {
0191     sync! '[selection]'
0192 }
0193 
0194 function p! {
0195     if [ "$@" ]; then
0196         s! $@
0197     fi
0198     project!
0199 }
0200 
0201 function b! {
0202     if [ "$@" ]; then
0203         s! $@
0204     fi
0205     bdir!
0206 }
0207 
0208 function o! {
0209     open! $@
0210 }
0211 
0212 function eo! {
0213     eopen! $@
0214 }
0215 
0216 function e! {
0217     exec! $@
0218 }
0219 
0220 function ce! {
0221     cexec! $@
0222 }
0223 
0224 function c! {
0225     create! $@
0226 }
0227 
0228 function se! {
0229     search! $@
0230 }
0231 
0232 function ds! {
0233     dsearch! $@
0234 }
0235 
0236 function h! {
0237     help! $@
0238 }
0239 
0240 function cth! {
0241     copytohost! $@
0242 }
0243 
0244 function ctc! {
0245     copytoclient! $@
0246 }
0247 
0248 function sev! {
0249     setenv! $@
0250 }
0251 
0252 function ee! {
0253     editenv! $@
0254 }
0255 
0256 function shev! {
0257     showenv! $@
0258 }
0259 
0260 # Internals:
0261 
0262 # Opens a document in internally in the application
0263 function openDocument {
0264     RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentSimple $1)
0265     if [[ "$RESULT" != "true" ]]; then
0266         echo "Failed to open $1"
0267     fi
0268 }
0269 
0270 # Opens a document in internally in the application
0271 function openDocuments {
0272     if [[ $_shell == "zsh" ]]; then
0273         arr=(${=1})
0274     else
0275         arr=("$1")
0276     fi
0277     RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentsSimple "(" $arr ")")
0278     if [[ "$RESULT" != "true" ]]; then
0279         echo "Failed to open $1"
0280     fi
0281 }
0282 
0283 # Executes a command on the client machine using the custom-script integration.
0284 # First argument: The full command. Second argument: The working directory.
0285 function executeInApp {
0286     local CMD="$1"
0287     local WD=$2
0288     if ! [ "$WD" ]; then
0289         WD=$(pwd)
0290     fi
0291     RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommand "$CMD" "$WD")
0292     if [[ "$RESULT" != "true" ]]; then
0293         echo "Execution failed"
0294     fi
0295 }
0296 
0297 # First argument: The full command. Second argument: The working directory.
0298 # Executes the command silently and synchronously, and returns the output
0299 function executeInAppSync {
0300     local CMD=$1
0301     local WD=$2
0302     if ! [ "$WD" ]; then
0303         WD=$(pwd)
0304     fi
0305     RESULT=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommandSync "$CMD" "$WD")
0306     echo "$RESULT"
0307 }
0308 
0309 # Getter functions:
0310 
0311 function getActiveDocument {
0312     $_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPath $@
0313 }
0314 
0315 function getOpenDocuments {
0316     $_qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPaths
0317 }
0318 
0319 function raise! {
0320     $_qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible
0321 }
0322 
0323 function bdir! {
0324     TARG=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" false)
0325     if [ "$TARG" ]; then
0326         cd $TARG
0327     else
0328         echo "Got no path"
0329     fi
0330 }
0331 
0332 function project! {
0333     TARG=$($_qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" true)
0334     if [ "$TARG" ]; then
0335         cd $TARG
0336     else
0337         echo "Got no path"
0338     fi
0339 }
0340 
0341 
0342 # Main functions:
0343 
0344 function raise! {
0345     $_qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible
0346 }
0347 
0348 function sync! {
0349     local P=$(getActiveDocument $@)
0350     if [ "$P" ]; then
0351 
0352         if [[ "$P" == fish://* ]]; then
0353             # This regular expression filters the user@host:port out of fish:///user@host:port/path/...
0354             LOGIN=$(echo $P | sed "s/fish\:\/\/*\([^\/]*\)\(\/.*\)/\1/")
0355             P_ON_HOST=$(echo $P | sed "s/fish\:\/\/*\([^\/]*\)\(\/.*\)/\2/")
0356             if [[ "$KDEV_SSH_FORWARD_CHAIN" == "$LOGIN" ]]; then
0357                 P="$P_ON_HOST"
0358             else
0359                 if [[ "$KDEV_SSH_FORWARD_CHAIN" == "" ]]; then
0360                     # Try to ssh to the host machine
0361                     # We need to split away the optional ":port" suffix, because the ssh command does not allow that syntax
0362                     HOST=$(echo $LOGIN | cut --delimiter=':' -f 1)
0363 
0364                     CMD="ssh!"
0365 
0366                     if [[ "$LOGIN" == *:* ]]; then
0367                         # If there is a port, extract it
0368                         PORT=$(echo $LOGIN | cut --delimiter=':' -f 2)
0369                         CMD="$CMD -p $PORT"
0370                     fi
0371 
0372                     CMD="$CMD $HOST"
0373                     # Execute the ssh command
0374                     echo "Executing $CMD"
0375                     KDEV_WORKING_DIR="$(dirname $P_ON_HOST)"
0376                     $CMD
0377                     return
0378                 else
0379                     echo "Cannot synchronize the working directory, because the host-names do not match (app: $LOGIN, shell: $KDEV_SSH_FORWARD_CHAIN)"
0380                     return
0381                 fi
0382             fi
0383 
0384         elif [[ "$P" == file://* ]]; then
0385             P=$(echo $P | sed 's$^file://$$')
0386 
0387         elif [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
0388             # This session is being forwarded to another machine, but the current document is not
0389             # However, we won't complain, because it's possible that the machines share the same file-system
0390             if [[ $(isEqualFileOnHostAndClient $P) != "yes" ]]; then
0391                 echo "Cannot synchronize the working directory, because the file systems do not match"
0392                 return
0393             fi
0394         fi
0395 
0396         [ -d "$P" ] || P=$(dirname "$P")
0397         cd "$P"
0398     else
0399         echo "Got no path"
0400     fi
0401 }
0402 
0403 # Take a path, and returns "yes" if the equal file is available on the host and the client
0404 # The check is performed by comparing inode-numbers
0405 function isEqualFileOnHostAndClient {
0406     function trimWhiteSpace() {
0407         echo $1
0408     }
0409 
0410     FILE=$1
0411     INODE_HOST=$(trimWhiteSpace $(ls --color=never -i $FILE | cut -d' ' -f1))
0412     INODE_CLIENT=$(trimWhiteSpace $(executeInAppSync "ls --color=never -i $FILE | cut -d' ' -f1" "$(dirname $FILE)"))
0413     if [[ "$INODE_HOST" == "$INODE_CLIENT" ]]; then
0414         echo "yes"
0415     else
0416         echo ""
0417     fi
0418 }
0419 
0420 # Takes a relative file, returns an absolute file/url that should be valid on the client.
0421 function mapFileToClient {
0422     local RELATIVE_FILE=$1
0423     FILE=$(readlink -f $RELATIVE_FILE)
0424     if ! [ -e "$FILE" ]; then
0425         # Try opening the file anyway, it might be an url or something else we don't understand here
0426         FILE=$RELATIVE_FILE
0427     else
0428         # We are referencing an absolute file, available on the file-system.
0429 
0430         if [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
0431             # If we are forwarding, map it to the client somehow.
0432             if [[ "$(isEqualFileOnHostAndClient "$FILE")" != "yes" ]]; then
0433                     # We can eventually map the file using the fish protocol
0434                     FISH_HOST=$KDEV_SSH_FORWARD_CHAIN
0435                     if [[ "$FISH_HOST" == *\,* ]]; then
0436                         # Extracts everything before the first comma
0437                         FISH_HOST=$(echo $FISH_HOST | sed 's/\([^,]*\),\(.*\)/\1/')
0438                         echo "ssh chain is too long: $KDEV_SSH_FORWARD_CHAIN mapping anyway using $FISH_HOST" 1>&2
0439                     fi
0440                     # Theoretically, we can only map through fish if the forward-chains contains no comma, which means that
0441                     # we forward only once. Try anyway, there might be the same filesystem on the whole forward-chain.
0442                     FILE="fish://$FISH_HOST$FILE"
0443             fi
0444         fi
0445     fi
0446     echo $FILE
0447 }
0448 
0449 function open! {
0450     NEWFILES=""
0451     for RELATIVE_FILE; do
0452         if [[ "$RELATIVE_FILE" == "/" || "$RELATIVE_FILE" == "-" ]]; then
0453             FILE=$RELATIVE_FILE
0454         else
0455             FILE=$(mapFileToClient $RELATIVE_FILE)
0456         fi
0457         NEWFILES="$NEWFILES $FILE"
0458     done
0459 
0460     openDocuments "$NEWFILES"
0461 }
0462 
0463 function eopen! {
0464     for RELATIVE_FILE; do
0465         FILE=$(mapFileToClient $RELATIVE_FILE)
0466         executeInApp "kde-open5 $FILE"
0467     done
0468 }
0469 
0470 function exec! {
0471     ARGS=""
0472     for RELATIVE_FILE; do
0473         if [[ "$ARGS" == "" ]]; then
0474             # Do not transform the command-name
0475             ARGS=$RELATIVE_FILE
0476         else
0477             FILE=$(mapFileToClient $RELATIVE_FILE)
0478             ARGS=$ARGS" "$FILE
0479         fi
0480     done
0481     echo "Executing: " $ARGS
0482     executeInApp "$ARGS"
0483 }
0484 
0485 function copytohost! {
0486     executeInApp "kioclient5 copy $1 $(mapFileToClient $2)"
0487 }
0488 
0489 function copytoclient! {
0490     executeInApp "kioclient5 copy $(mapFileToClient $1) $2"
0491 }
0492 
0493 function cexec! {
0494     ARGS=""
0495     PREFIX=""
0496     TMP=1
0497     for RELATIVE_FILE; do
0498         if [[ "$ARGS" == "" ]]; then
0499             # Do not transform the command-name
0500             ARGS=$RELATIVE_FILE
0501         else
0502             FILE=$(mapFileToClient $RELATIVE_FILE)
0503 
0504             if [[ "$FILE" == fish://* ]]; then
0505                 # Add a prefix to copy the file into a temporary file
0506                 # Keep the baseline as suffix, so that applications can easily recognize the mimetype
0507                 PREFIX+="FILE$TMP=\$(mktemp).$(basename $FILE); kioclient5 copy $FILE \$FILE$TMP;"
0508                 # Use the temporary variable instead of the name
0509                 FILE="\$FILE$TMP"
0510                 TMP=$(($TMP+1))
0511             fi
0512 
0513             ARGS=$ARGS" "$FILE
0514         fi
0515     done
0516     echo "Executing: " $ARGS
0517     executeInApp "$PREFIX $ARGS"
0518 }
0519 
0520 function create! {
0521     FILE=$(readlink -f $1)
0522     if ! [ "$FILE" ]; then
0523         echo "Error: Bad arguments."
0524         return 1
0525     fi
0526     if [ -e "$FILE" ]; then
0527         echo "The file $FILE already exists"
0528         return 2
0529     fi
0530     echo $2 > $FILE
0531 
0532     openDocument $(mapFileToClient $FILE)
0533 }
0534 
0535 function search! {
0536     PATTERN=$1
0537 
0538 #     if ! [ "$PATTERN" ]; then
0539 #         echo "Error: No pattern given."
0540 #         return 1
0541 #     fi
0542 
0543     LOCATION=$2
0544 
0545     if ! [ "$LOCATION" ]; then
0546         LOCATION="."
0547     fi
0548 
0549     LOCATION=$(mapFileToClient $LOCATION)
0550 
0551     for LOC in $*; do
0552         if [[ "$LOC" == "$1" ]]; then
0553             continue;
0554         fi
0555         if [[ "$LOC" == "$2" ]]; then
0556             continue;
0557         fi
0558         LOCATION="$LOCATION;$(mapFileToClient $LOC)"
0559     done
0560 
0561     $_qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" true
0562 }
0563 
0564 function dsearch! {
0565     PATTERN=$1
0566 
0567     if ! [ "$PATTERN" ]; then
0568         echo "Error: No pattern given."
0569         return 1
0570     fi
0571 
0572     LOCATION=$2
0573 
0574     if ! [ "$LOCATION" ]; then
0575         LOCATION="."
0576     fi
0577 
0578     LOCATION=$(mapFileToClient $LOCATION)
0579 
0580     for LOC in $*; do
0581         if [[ "$LOC" == "$1" ]]; then
0582             continue;
0583         fi
0584         if [[ "$LOC" == "$2" ]]; then
0585             continue;
0586         fi
0587         LOCATION="$LOCATION;$(mapFileToClient $LOC)"
0588     done
0589 
0590     $_qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" false
0591 }
0592 
0593 ##### SSH DBUS FORWARDING --------------------------------------------------------------------------------------------------------------------
0594 
0595 DBUS_SOCKET_TRANSFORMER=$KDEV_BASEDIR/kdev_dbus_socket_transformer
0596 
0597 # We need this, to make sure that our forwarding-loops won't get out of control
0598 # This configures the shell to kill background jobs when it is terminated
0599 shopt -s huponexit
0600 
0601 export DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH=/tmp/dbus-forwarded-$USER-$APPLICATION_HOST
0602 
0603 export DBUS_FORWARDING_TCP_LOCAL_PORT=9000
0604 export DBUS_FORWARDING_TCP_MAX_LOCAL_PORT=10000
0605 export DBUS_ABSTRACT_SOCKET_TARGET_INDEX=1
0606 export DBUS_ABSTRACT_SOCKET_MAX_TARGET_INDEX=1000
0607 
0608 function getPortFromSSHCommand {
0609     # The port is given to ssh exclusively in the format "-p PORT"
0610     # This regular expression extracts the "4821" from "ssh -q bla1 -p 4821 bla2"
0611     local ARGS=$@
0612     local RET=$(echo "$@" | sed "s/.*-p \+\([0-9]*\).*/\1/")
0613     if [[ "$ARGS" == "$RET" ]]; then
0614         # There was no match
0615         echo ""
0616     else
0617         echo ":$RET"
0618     fi
0619 }
0620 
0621 function getLoginFromSSHCommand {
0622     # The login name can be given to ssh in the format "-l NAME"
0623     # This regular expression extracts the "NAME" from "ssh -q bla1 -l NAME bla2"
0624     local ARGS=$@
0625     local RET=$(echo "$ARGS" | sed "s/.*-l \+\([a-z,A-Z,_,0-9]*\).*/\1/")
0626     if [[ "$RET" == "$ARGS"  ||  "$RET" == "" ]]; then
0627         # There was no match
0628         echo ""
0629     else
0630         echo "$RET@"
0631     fi
0632 }
0633 
0634 function getHostFromSSHCommand {
0635     # This regular expression extracts the "bla2" from "echo "ssh -q bla1 -p 4821 bla2"
0636     # Specifically, it finds the first argument which is not preceded by a "-x" parameter kind specification.
0637 
0638     local CLEANED=""
0639     local NEWCLEANED="$@"
0640 
0641     while [[ "$NEWCLEANED" != "$CLEANED" ]]; do
0642         CLEANED="$NEWCLEANED"
0643     # This expression removes one "-x ARG" parameter
0644         NEWCLEANED="$(echo $CLEANED | sed "s/\(.*\)\(-[a-z,A-Z] \+[a-z,0-9]*\)\ \(.*\)/\1\3/")"
0645     done
0646 
0647     # After cleaning, the result should only consist of the host-name followed by an optional command.
0648     # Select the host-name, by extracting the first column.
0649     echo $CLEANED | cut --delimiter=" " -f 1
0650 }
0651 
0652 function getSSHForwardOptionsFromCommand {
0653 
0654     HOST="$(getLoginFromSSHCommand "$@")$(getHostFromSSHCommand "$@")$(getPortFromSSHCommand "$@")"
0655 
0656     if [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
0657         # We are already forwarding, so we deal with a chain of multiple ssh commands.
0658         # We still record it, although it's not sure if we can use it somehow.
0659         echo "KDEV_SSH_FORWARD_CHAIN=\"$KDEV_SSH_FORWARD_CHAIN,$HOST\"";
0660     else
0661         echo "KDEV_SSH_FORWARD_CHAIN=$HOST"
0662     fi
0663 }
0664 
0665 function getDBusAbstractSocketSuffix {
0666     # From something like DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-wYmSkVH7FE,guid=b214dad39e0292a4299778d64d761a5b
0667     # extract the /tmp/dbus-wYmSkVH7FE
0668     echo $DBUS_SESSION_BUS_ADDRESS | sed 's/unix\:abstract\=.*\(,guid\=.*\)/\1/'
0669 }
0670 
0671 function keepForwardingDBusToTCPSocket {
0672     while ! $KDEV_BASEDIR/kdev_dbus_socket_transformer $DBUS_FORWARDING_TCP_LOCAL_PORT --bind-only; do
0673         if (($DBUS_FORWARDING_TCP_LOCAL_PORT<$DBUS_FORWARDING_TCP_MAX_LOCAL_PORT)); then
0674             export DBUS_FORWARDING_TCP_LOCAL_PORT=$(($DBUS_FORWARDING_TCP_LOCAL_PORT+1))
0675 #             echo "Increased local port to " $DBUS_FORWARDING_TCP_LOCAL_PORT;
0676         else
0677             echo "Failed to allocate a local TCP port";
0678             return 1;
0679         fi
0680     done
0681 
0682     $KDEV_BASEDIR/kdev_dbus_socket_transformer $DBUS_FORWARDING_TCP_LOCAL_PORT&
0683     return 0;
0684 }
0685 
0686 function keepForwardingDBusFromTCPSocket {
0687 
0688     while ! $KDEV_BASEDIR/kdev_dbus_socket_transformer $FORWARD_DBUS_FROM_PORT ${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX} --bind-only; do
0689         if ((${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}<${DBUS_ABSTRACT_SOCKET_MAX_TARGET_INDEX})); then
0690             export DBUS_ABSTRACT_SOCKET_TARGET_INDEX=$(($DBUS_ABSTRACT_SOCKET_TARGET_INDEX+1))
0691         else
0692             echo "Failed to allocate a local path for the abstract dbus socket";
0693             return 1;
0694         fi
0695     done
0696 
0697     local PATH=${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}
0698     export DBUS_SESSION_BUS_ADDRESS=unix:abstract=$PATH${DBUS_SOCKET_SUFFIX}
0699     $KDEV_BASEDIR/kdev_dbus_socket_transformer $FORWARD_DBUS_FROM_PORT $PATH&
0700 }
0701 
0702 function ssh! {
0703     keepForwardingDBusToTCPSocket # Start the dbus forwarding subprocess
0704     DBUS_FORWARDING_TCP_TARGET_PORT=$((5000+($RANDOM%50000)))
0705 
0706     ssh $@ -t -R localhost:$DBUS_FORWARDING_TCP_TARGET_PORT:localhost:$DBUS_FORWARDING_TCP_LOCAL_PORT \
0707          " APPLICATION=$APPLICATION \
0708            KDEV_BASEDIR=$KDEV_BASEDIR \
0709            KDEV_DBUS_ID=$KDEV_DBUS_ID \
0710            FORWARD_DBUS_FROM_PORT=$DBUS_FORWARDING_TCP_TARGET_PORT \
0711            APPLICATION_HOST=$APPLICATION_HOST \
0712            KDEV_WORKING_DIR=$KDEV_WORKING_DIR \
0713            KDEV_SHELL_ENVIRONMENT_ID=$KDEV_SHELL_ENVIRONMENT_ID \
0714            DBUS_SOCKET_SUFFIX=$(getDBusAbstractSocketSuffix) \
0715            $(getSSHForwardOptionsFromCommand "$@") \
0716               bash --init-file \
0717                         \$(if [ -e \"$KDEV_BASEDIR/kdevplatform_shell_environment.sh\" ]; \
0718                                 then echo \"$KDEV_BASEDIR/kdevplatform_shell_environment.sh\"; \
0719                            elif [ -e \"$(which kdevplatform_shell_environment.sh)\" ]; then
0720                                 echo \"$(which kdevplatform_shell_environment.sh)\"; \
0721                            else \
0722                                 echo \"~/.kdevplatform_shell_environment.sh\"; \
0723                            fi) \
0724                    -i"
0725 
0726 
0727 
0728     if [ "$FORWARD_DBUS_FROM_PORT" ]; then
0729         # We created the 2nd subprocess
0730         kill %2 # Stop the dbus forwarding subprocess
0731     else
0732         # We created the 1st subprocess
0733         kill %1 # Stop the dbus forwarding subprocess
0734     fi
0735 }
0736 
0737 # A version of ssh! that preserves the current working directory
0738 function ssw! {
0739     KDEV_WORKING_DIR=$(pwd)
0740     ssh! $@
0741 }
0742 
0743 function env! {
0744     FILES="$(executeInAppSync "ls $(getSessionDir)/*.sh" "")"
0745     for FILE in $FILES; do
0746         FILE=$(basename $FILE)
0747         ID=${FILE%.sh} # This ugly construct strips away the .sh suffix
0748         if [[ "$ID" == "$KDEV_SHELL_ENVIRONMENT_ID" ]]; then
0749             echo "$ID   [current]"
0750         else
0751             echo "$ID"
0752         fi
0753     done
0754 }
0755 
0756 function editenv! {
0757     local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID
0758     if [ "$1" ]; then
0759         ENV_ID=$1
0760     fi
0761     # If the environment-file doesn't exist yet, create it
0762     executeInAppSync "if ! [ -e $(getCurrentShellEnvPath $ENV_ID) ]; then touch $(getCurrentShellEnvPath $ENV_ID); fi" ""
0763     # Open it
0764     openDocument "$(getCurrentShellEnvPath $ENV_ID)"
0765 }
0766 
0767 function setenv! {
0768     if [ "$1" ]; then
0769         KDEV_SHELL_ENVIRONMENT_ID=$1
0770     fi
0771 
0772     # Execute the contents of the shell-environment
0773     # note: keep compatible with FreeBSD: https://bugs.kde.org/show_bug.cgi?id=311186
0774     local TEMP=$(mktemp /tmp/$USER-XXXXXXXX)
0775     RESULT=$(executeInAppSync "cat \"$(getCurrentShellEnvPath)\"" "")
0776     echo "$RESULT" >| $TEMP
0777     if ! [ "$RESULT" ]; then
0778         # If the environment shell file doesn't exist, create it
0779         executeInAppSync "if ! [ -e $(getCurrentShellEnvPath) ]; then touch $(getCurrentShellEnvPath); fi" ""
0780     fi
0781     source $TEMP
0782     rm -f $TEMP
0783 }
0784 
0785 function showenv! {
0786     local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID
0787     if [ "$1" ]; then
0788         ENV_ID=$1
0789     fi
0790 
0791     echo "Environment $ENV_ID:"
0792 
0793     # Execute the contents of the shell-environment
0794     echo $(executeInAppSync "cat \"$(getCurrentShellEnvPath $ENV_ID)\"" "")
0795 }
0796 
0797 if [ "$FORWARD_DBUS_FROM_PORT" ]; then
0798     # Start the target-side dbus forwarding, transforming from the ssh pipe to the abstract unix domain socket
0799     export DBUS_SESSION_BUS_ADDRESS=unix:abstract=${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}${DBUS_SOCKET_SUFFIX}
0800     keepForwardingDBusFromTCPSocket
0801 fi
0802 
0803 setenv!
0804 
0805 ##### INITIALIZATION --------------------------------------------------------------------------------------------------------------------
0806 
0807 # Mark that this session is attached, by prepending a '!' character
0808 PS1="!$PS1"
0809 
0810 echo "You are controlling the $APPLICATION session '$(getSessionName)'. Type help! for more information."
0811 
0812 if [ "$KDEV_WORKING_DIR" ]; then
0813     cd $KDEV_WORKING_DIR
0814 fi