This file is indexed.

/usr/bin/kdevplatform_shell_environment.sh is in kdevelop 4:5.2.1-1ubuntu4.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
#!/bin/bash

# This file is part of KDevelop
# Copyright 2011 David Nolden <david.nolden.kdevelop@art-master.de>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this library; see the file COPYING.LIB.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.

if [ -z "$ZDOTDIR" ]; then _shell=bash; else _shell=zsh; fi

# Since this runs as a replacement for the init-files, we need to chain in the 'real' rcs.
# We ignore profile, login & logout rcs, as we want no login shells.
case $_shell in
    zsh)
        alias shopt=':'
        test -f "$OLD_ZDOTDIR/.zshenv" && . "$OLD_ZDOTDIR/.zshenv"
        test -f "$OLD_ZDOTDIR/.zshrc"  && . "$OLD_ZDOTDIR/.zshrc"
        ;; #zsh still also sources the systemwide rcs when called with $ZDOTDIR set.
    bash)
        test -f ~/.bash_profile  && source ~/.bash_profile
        test -f /etc/bash.bashrc && source /etc/bash.bashrc
        test -f ~/.bashrc && source ~/.bashrc
        ;;
esac

if ! [ "$APPLICATION_HOST" ]; then
    export APPLICATION_HOST=$(hostname)
fi

if ! [ "$KDEV_SHELL_ENVIRONMENT_ID" ]; then
    export KDEV_SHELL_ENVIRONMENT_ID="default"
fi

if ! [ "$KDEV_DBUS_ID" ]; then
    echo "The required environment variable KDEV_DBUS_ID is not set. This variable defines the dbus id of the application instance instance which is supposed to be attached."
    exit 5
fi

# Eventually, if we are forwarding to another host, and kdevplatform_shell_environment.sh
# has been located through "which kdevplatform_shell_environment.sh", then we need to update KDEV_BASEDIR.
if ! [ -e "$KDEV_BASEDIR/kdevplatform_shell_environment.sh" ]; then
    KDEV_BASEDIR=$(dirname $(which kdevplatform_shell_environment.sh))
fi

if ! [ -e "$KDEV_BASEDIR/kdev_dbus_socket_transformer" ]; then
    echo "The $KDEV_BASEDIR/kdev_dbus_socket_transformer utility is missing, controlling the application across ssh is not possible"
fi

# Takes a list of tools, and prints a warning of one of them is not available in the path
function checkToolsInPath {
    for TOOL in $@; do
        if ! [ "$(which $TOOL 2> /dev/null)" ]; then
            echo "The utility $TOOL is not in your path, the shell integration will not work properly."
        fi
    done
}

# Check if all required tools are there (on the host machine)
checkToolsInPath sed qdbus ls cut dirname mktemp basename readlink hostname

if ! [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
    # Check for additional utilities that are required on the client machine
    checkToolsInPath kioclient5
fi

# Queries the session name from the running application instance
function getSessionName {
    echo "$(qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionName)"
}

function getSessionDir {
    echo "$(qdbus $KDEV_DBUS_ID /org/kdevelop/SessionController org.kdevelop.SessionController.sessionDir)"
}

function getCurrentShellEnvPath {
    local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID
    if [ "$1" ]; then
        ENV_ID=$1
    fi

    echo "$(getSessionDir)/${ENV_ID}.sh"
}

function help! {
    echo "You are controlling the $APPLICATION session '$(getSessionName)'"
    echo ""
    if [[ "$1" == "" ]]; then
    echo "Standard commands:"
    echo "raise!                                 - Raise the window."
    echo "sync!                                  - Synchronize the working directory with the currently open document. See \"help! sync\""
    echo "open!   [file] ...                     - Open the file(s) within the attached application. See \"help! open\""
    echo "eopen!  [file] ...                     - Open the file(s) within an external application using kde-open."
    echo "create!  [file] [[text]]               - Create and open a new file."
    echo "search!   [pattern] [[locations]] ...  - Search for the given pattern here or at the optionally given location(s)."
    echo "dsearch!  [pattern] [[locations]] ...  - Same as search, but starts the search instantly instead of showing the dialog (using previous settings)."
    echo "ssh!  [ssh arguments]                  - Connect to a remote host via ssh, keeping the control-connection alive. See \"help! remote\""
    echo ""
    echo "help!                                  - Show help."
    echo "help! open                             - Show extended help about file opening commands."
    echo "help! sync                             - Show extended help about path synchronization commands."
    echo "help! remote                           - Show extended help about remote shell-integration through ssh."
    echo "help! env                              - Show extended help about the environment."
    echo ""
    echo "Most commands can be abbreviated by the first character(s), eg. r! instead of raise!, and se! instead of search!."
    fi

    if [[ "$1" == "open" ]]; then
    echo "Extended opening:"
    echo "The open! command can also be used to open files in specific tool-view configurations, by adding split-separators:"
    echo "- Files around the / separator will be arranged horizontally by split-view."
    echo "- Files around the - separator will be arranged vertically by split-view."
    echo "- Parens [ ... ] can be used to disambiguate the hierarchy (there must be spaces between filename and paren)."
    echo "- If a file is missing around a separator, the currently active view is inserted into the position."
    echo ""
    echo "Examples:"
    echo "open! file1 / file2                 - The active view is split horizontally."
    echo "                                      file1 is opened in the left view, and file2 in the right view."
    echo "open! file1 / [ file2 - file3 ]     - The active view is split horizontally, and the right split-view is split vertically."
    echo "                                      file1 is opened in the left view, file2 in the right upper view, and file3 in the right lower view."
    echo "open! / file1                       - The active view is split horizontally."
    echo "                                    - The active document is kept in the left split-view, and file1 is opened in the right split-view."
    echo ""
    echo "Short forms: o! = open!, eo! = eopen!, c! = create!"
    fi

    if [[ "$1" == "sync" ]]; then
    echo "Extended syncing:"
    echo "sync!    [[project-name]]           - If no project-name is given, then the sync! command synchronizes to the currently active document."
    echo "                                      If no document is active, then it synchronizes to the currently selected item in the project tree-view."
    echo "                                      If a case-insensitive project name prefix is given, then it synchronizes to the base folder of the matching project."
    echo "syncsel!                            - Synchronizes to the currently selected item in the project tree-view, independently of the active document."
    echo "project! [[project-name]]           - Map from a path within the build directory to the corresponding path in the source directory."
    echo "                                      If we're already in the source directory, map to the root of the surrounding project."
    echo "bdir!    [[project-name]]           - Map from a path within the source directory to the corresponding path in the build directory."
    echo "                                      If we're already in the build directory, map to the root of the build directory."
    echo ""
    echo "Short forms: s! = sync!, ss! = syncsel!, p! = project!, b! = bdir!"
    fi

    if [[ "$1" == "remote" ]]; then
    echo "Extended remote commands:"
    echo "ssh!  [ssh arguments]                  - Connect to a remote host via ssh, keeping the control-connection alive."
    echo "                                       - The whole dbus environment is forwarded, KDevelop needs to be installed on both sides."
    echo "ssw!  [ssh arguments]                  - Like ssh!, but preserves the current working directory."
    echo "exec! [cmd] [args] [file] . ..         - Execute the given command on the client machine, referencing any number of local files on the host machine."
    echo "                                       - The file paths will be re-encoded as fish:// urls if required."
    echo "cexec! [cmd] [args] [file] . ..        - Execute the given command on the client machine, referencing any number of local files on the host machine."
    echo "                                       - The files will be COPIED to the client machine if required."
    echo "copytohost! [client path] [host path]  - Copy a file/directory through the fish protocol from the client machine th the host machine."
    echo "copytoclient! [host path] [client path]- Copy a file/directory through the fish protocol from the host machine to the client machine."
    echo ""
    echo "Short forms: e! = exec!, ce! = cexec!, cth! = copytohost!, ctc! = copytoclient!"
    fi

    if [[ "$1" == "env" ]]; then
      echo "Environment management:"
      echo "The environment can be used to store session-specific macros and generally manipulate the shell environment"
      echo "for embedded shell sessions. The environment is sourced into the shell when the shell is initialized, and"
      echo "whenever setenv! is called."
      echo ""
      echo "env!                                 - List all available shell environment-ids for this session."
      echo "setenv! [id]                         - Set the shell environmnet-id for this session to the given id, or update the current one."
      echo "editenv! [id]                        - Edit the current shell environment or the one with the optionally given id."
      echo "showenv! [id]                        - Show the current shell environment or the one with the optionally given id."
      echo ""
      echo "Short forms: sev! = setenv!, ee! = editenv!, shenv! = showenv!"
    fi
    echo ""
}

# Short versions of the commands:

function r! {
    raise! $@
}

function s! {
    sync! $@
}

function ss! {
    syncsel!
}

function syncsel! {
    sync! '[selection]'
}

function p! {
    if [ "$@" ]; then
        s! $@
    fi
    project!
}

function b! {
    if [ "$@" ]; then
        s! $@
    fi
    bdir!
}

function o! {
    open! $@
}

function eo! {
    eopen! $@
}

function e! {
    exec! $@
}

function ce! {
    cexec! $@
}

function c! {
    create! $@
}

function se! {
    search! $@
}

function ds! {
    dsearch! $@
}

function h! {
    help! $@
}

function cth! {
    copytohost! $@
}

function ctc! {
    copytoclient! $@
}

function sev! {
    setenv! $@
}

function ee! {
    editenv! $@
}

function shev! {
    showenv! $@
}

# Internals:

# Opens a document in internally in the application
function openDocument {
    RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentSimple $1)
    if [[ "$RESULT" != "true" ]]; then
        echo "Failed to open $1"
    fi
}

# Opens a document in internally in the application
function openDocuments {
    if [[ $_shell == "zsh" ]]; then
        arr=(${=1})
    else
        arr=("$1")
    fi
    RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.openDocumentsSimple "(" $arr ")")
    if [[ "$RESULT" != "true" ]]; then
        echo "Failed to open $1"
    fi
}

# Executes a command on the client machine using the custom-script integration.
# First argument: The full command. Second argument: The working directory.
function executeInApp {
    local CMD="$1"
    local WD=$2
    if ! [ "$WD" ]; then
        WD=$(pwd)
    fi
    RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommand "$CMD" "$WD")
    if [[ "$RESULT" != "true" ]]; then
        echo "Execution failed"
    fi
}

# First argument: The full command. Second argument: The working directory.
# Executes the command silently and synchronously, and returns the output
function executeInAppSync {
    local CMD=$1
    local WD=$2
    if ! [ "$WD" ]; then
        WD=$(pwd)
    fi
    RESULT=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ExternalScriptPlugin org.kdevelop.ExternalScriptPlugin.executeCommandSync "$CMD" "$WD")
    echo "$RESULT"
}

# Getter functions:

function getActiveDocument {
    qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPath $@
}

function getOpenDocuments {
    qdbus $KDEV_DBUS_ID /org/kdevelop/DocumentController org.kdevelop.DocumentController.activeDocumentPaths
}

function raise! {
    qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible
}

function bdir! {
    TARG=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" false)
    if [ "$TARG" ]; then
        cd $TARG
    else
        echo "Got no path"
    fi
}

function project! {
    TARG=$(qdbus $KDEV_DBUS_ID /org/kdevelop/ProjectController org.kdevelop.ProjectController.mapSourceBuild "$(pwd)" true)
    if [ "$TARG" ]; then
        cd $TARG
    else
        echo "Got no path"
    fi
}


# Main functions:

function raise! {
    qdbus $KDEV_DBUS_ID /kdevelop/MainWindow org.kdevelop.MainWindow.ensureVisible
}

function sync! {
    local P=$(getActiveDocument $@)
    if [ "$P" ]; then

        if [[ "$P" == fish://* ]]; then
            # This regular expression filters the user@host:port out of fish:///user@host:port/path/...
            LOGIN=$(echo $P | sed "s/fish\:\/\/*\([^\/]*\)\(\/.*\)/\1/")
            P_ON_HOST=$(echo $P | sed "s/fish\:\/\/*\([^\/]*\)\(\/.*\)/\2/")
            if [[ "$KDEV_SSH_FORWARD_CHAIN" == "$LOGIN" ]]; then
                P="$P_ON_HOST"
            else
                if [[ "$KDEV_SSH_FORWARD_CHAIN" == "" ]]; then
                    # Try to ssh to the host machine
                    # We need to split away the optional ":port" suffix, because the ssh command does not allow that syntax
                    HOST=$(echo $LOGIN | cut --delimiter=':' -f 1)

                    CMD="ssh!"

                    if [[ "$LOGIN" == *:* ]]; then
                        # If there is a port, extract it
                        PORT=$(echo $LOGIN | cut --delimiter=':' -f 2)
                        CMD="$CMD -p $PORT"
                    fi

                    CMD="$CMD $HOST"
                    # Execute the ssh command
                    echo "Executing $CMD"
                    KDEV_WORKING_DIR="$(dirname $P_ON_HOST)"
                    $CMD
                    return
                else
                    echo "Cannot synchronize the working directory, because the host-names do not match (app: $LOGIN, shell: $KDEV_SSH_FORWARD_CHAIN)"
                    return
                fi
            fi

        elif [[ "$P" == file://* ]]; then
            P=$(echo $P | sed 's$^file://$$')

        elif [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
            # This session is being forwarded to another machine, but the current document is not
            # However, we won't complain, because it's possible that the machines share the same file-system
            if [[ $(isEqualFileOnHostAndClient $P) != "yes" ]]; then
                echo "Cannot synchronize the working directory, because the file systems do not match"
                return
            fi
        fi

        [ -d "$P" ] || P=$(dirname "$P")
        cd "$P"
    else
        echo "Got no path"
    fi
}

# Take a path, and returns "yes" if the equal file is available on the host and the client
# The check is performed by comparing inode-numbers
function isEqualFileOnHostAndClient {
    function trimWhiteSpace() {
        echo $1
    }

    FILE=$1
    INODE_HOST=$(trimWhiteSpace $(ls --color=never -i $FILE | cut -d' ' -f1))
    INODE_CLIENT=$(trimWhiteSpace $(executeInAppSync "ls --color=never -i $FILE | cut -d' ' -f1" "$(dirname $FILE)"))
    if [[ "$INODE_HOST" == "$INODE_CLIENT" ]]; then
        echo "yes"
    else
        echo ""
    fi
}

# Takes a relative file, returns an absolute file/url that should be valid on the client.
function mapFileToClient {
    local RELATIVE_FILE=$1
    FILE=$(readlink -f $RELATIVE_FILE)
    if ! [ -e "$FILE" ]; then
        # Try opening the file anyway, it might be an url or something else we don't understand here
        FILE=$RELATIVE_FILE
    else
        # We are referencing an absolute file, available on the file-system.

        if [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
            # If we are forwarding, map it to the client somehow.
            if [[ "$(isEqualFileOnHostAndClient "$FILE")" != "yes" ]]; then
                    # We can eventually map the file using the fish protocol
                    FISH_HOST=$KDEV_SSH_FORWARD_CHAIN
                    if [[ "$FISH_HOST" == *\,* ]]; then
                        # Extracts everything before the first comma
                        FISH_HOST=$(echo $FISH_HOST | sed 's/\([^,]*\),\(.*\)/\1/')
                        echo "ssh chain is too long: $KDEV_SSH_FORWARD_CHAIN mapping anyway using $FISH_HOST" 1>&2
                    fi
                    # Theoretically, we can only map through fish if the forward-chains contains no comma, which means that
                    # we forward only once. Try anyway, there might be the same filesystem on the whole forward-chain.
                    FILE="fish://$FISH_HOST$FILE"
            fi
        fi
    fi
    echo $FILE
}

function open! {
    NEWFILES=""
    for RELATIVE_FILE; do
        if [[ "$RELATIVE_FILE" == "/" || "$RELATIVE_FILE" == "-" ]]; then
            FILE=$RELATIVE_FILE
        else
            FILE=$(mapFileToClient $RELATIVE_FILE)
        fi
        NEWFILES="$NEWFILES $FILE"
    done

    openDocuments "$NEWFILES"
}

function eopen! {
    for RELATIVE_FILE; do
        FILE=$(mapFileToClient $RELATIVE_FILE)
        executeInApp "kde-open5 $FILE"
    done
}

function exec! {
    ARGS=""
    for RELATIVE_FILE; do
        if [[ "$ARGS" == "" ]]; then
            # Do not transform the command-name
            ARGS=$RELATIVE_FILE
        else
            FILE=$(mapFileToClient $RELATIVE_FILE)
            ARGS=$ARGS" "$FILE
        fi
    done
    echo "Executing: " $ARGS
    executeInApp "$ARGS"
}

function copytohost! {
    executeInApp "kioclient5 copy $1 $(mapFileToClient $2)"
}

function copytoclient! {
    executeInApp "kioclient5 copy $(mapFileToClient $1) $2"
}

function cexec! {
    ARGS=""
    PREFIX=""
    TMP=1
    for RELATIVE_FILE; do
        if [[ "$ARGS" == "" ]]; then
            # Do not transform the command-name
            ARGS=$RELATIVE_FILE
        else
            FILE=$(mapFileToClient $RELATIVE_FILE)

            if [[ "$FILE" == fish://* ]]; then
                # Add a prefix to copy the file into a temporary file
                # Keep the baseline as suffix, so that applications can easily recognize the mimetype
                PREFIX+="FILE$TMP=\$(mktemp).$(basename $FILE); kioclient5 copy $FILE \$FILE$TMP;"
                # Use the temporary variable instead of the name
                FILE="\$FILE$TMP"
                TMP=$(($TMP+1))
            fi

            ARGS=$ARGS" "$FILE
        fi
    done
    echo "Executing: " $ARGS
    executeInApp "$PREFIX $ARGS"
}

function create! {
    FILE=$(readlink -f $1)
    if ! [ "$FILE" ]; then
        echo "Error: Bad arguments."
        return 1
    fi
    if [ -e "$FILE" ]; then
        echo "The file $FILE already exists"
        return 2
    fi
    echo $2 > $FILE

    openDocument $(mapFileToClient $FILE)
}

function search! {
    PATTERN=$1

#     if ! [ "$PATTERN" ]; then
#         echo "Error: No pattern given."
#         return 1
#     fi

    LOCATION=$2

    if ! [ "$LOCATION" ]; then
        LOCATION="."
    fi

    LOCATION=$(mapFileToClient $LOCATION)

    for LOC in $*; do
        if [[ "$LOC" == "$1" ]]; then
            continue;
        fi
        if [[ "$LOC" == "$2" ]]; then
            continue;
        fi
        LOCATION="$LOCATION;$(mapFileToClient $LOC)"
    done

    qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" true
}

function dsearch! {
    PATTERN=$1

    if ! [ "$PATTERN" ]; then
        echo "Error: No pattern given."
        return 1
    fi

    LOCATION=$2

    if ! [ "$LOCATION" ]; then
        LOCATION="."
    fi

    LOCATION=$(mapFileToClient $LOCATION)

    for LOC in $*; do
        if [[ "$LOC" == "$1" ]]; then
            continue;
        fi
        if [[ "$LOC" == "$2" ]]; then
            continue;
        fi
        LOCATION="$LOCATION;$(mapFileToClient $LOC)"
    done

    qdbus $KDEV_DBUS_ID /org/kdevelop/GrepViewPlugin org.kdevelop.GrepViewPlugin.startSearch "$PATTERN" "$LOCATION" false
}

##### SSH DBUS FORWARDING --------------------------------------------------------------------------------------------------------------------

DBUS_SOCKET_TRANSFORMER=$KDEV_BASEDIR/kdev_dbus_socket_transformer

# We need this, to make sure that our forwarding-loops won't get out of control
# This configures the shell to kill background jobs when it is terminated
shopt -s huponexit

export DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH=/tmp/dbus-forwarded-$USER-$APPLICATION_HOST

export DBUS_FORWARDING_TCP_LOCAL_PORT=9000
export DBUS_FORWARDING_TCP_MAX_LOCAL_PORT=10000
export DBUS_ABSTRACT_SOCKET_TARGET_INDEX=1
export DBUS_ABSTRACT_SOCKET_MAX_TARGET_INDEX=1000

function getPortFromSSHCommand {
    # The port is given to ssh exclusively in the format "-p PORT"
    # This regular expression extracts the "4821" from "ssh -q bla1 -p 4821 bla2"
    local ARGS=$@
    local RET=$(echo "$@" | sed "s/.*-p \+\([0-9]*\).*/\1/")
    if [[ "$ARGS" == "$RET" ]]; then
        # There was no match
        echo ""
    else
        echo ":$RET"
    fi
}

function getLoginFromSSHCommand {
    # The login name can be given to ssh in the format "-l NAME"
    # This regular expression extracts the "NAME" from "ssh -q bla1 -l NAME bla2"
    local ARGS=$@
    local RET=$(echo "$ARGS" | sed "s/.*-l \+\([a-z,A-Z,_,0-9]*\).*/\1/")
    if [[ "$RET" == "$ARGS"  ||  "$RET" == "" ]]; then
        # There was no match
        echo ""
    else
        echo "$RET@"
    fi
}

function getHostFromSSHCommand {
    # This regular expression extracts the "bla2" from "echo "ssh -q bla1 -p 4821 bla2"
    # Specifically, it finds the first argument which is not preceded by a "-x" parameter kind specification.

    local CLEANED=""
    local NEWCLEANED="$@"

    while [[ "$NEWCLEANED" != "$CLEANED" ]]; do
        CLEANED="$NEWCLEANED"
    # This expression removes one "-x ARG" parameter
        NEWCLEANED="$(echo $CLEANED | sed "s/\(.*\)\(-[a-z,A-Z] \+[a-z,0-9]*\)\ \(.*\)/\1\3/")"
    done

    # After cleaning, the result should only consist of the host-name followed by an optional command.
    # Select the host-name, by extracting the forst column.
    echo $CLEANED | cut --delimiter=" " -f 1
}

function getSSHForwardOptionsFromCommand {

    HOST="$(getLoginFromSSHCommand "$@")$(getHostFromSSHCommand "$@")$(getPortFromSSHCommand "$@")"

    if [ "$KDEV_SSH_FORWARD_CHAIN" ]; then
        # We are already forwarding, so we deal with a chain of multiple ssh commands.
        # We still record it, although it's not sure if we can use it somehow.
        echo "KDEV_SSH_FORWARD_CHAIN=\"$KDEV_SSH_FORWARD_CHAIN,$HOST\"";
    else
        echo "KDEV_SSH_FORWARD_CHAIN=$HOST"
    fi
}

function getDBusAbstractSocketSuffix {
    # From something like DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-wYmSkVH7FE,guid=b214dad39e0292a4299778d64d761a5b
    # extract the /tmp/dbus-wYmSkVH7FE
    echo $DBUS_SESSION_BUS_ADDRESS | sed 's/unix\:abstract\=.*\(,guid\=.*\)/\1/'
}

function keepForwardingDBusToTCPSocket {
    while ! $KDEV_BASEDIR/kdev_dbus_socket_transformer $DBUS_FORWARDING_TCP_LOCAL_PORT --bind-only; do
        if (($DBUS_FORWARDING_TCP_LOCAL_PORT<$DBUS_FORWARDING_TCP_MAX_LOCAL_PORT)); then
            export DBUS_FORWARDING_TCP_LOCAL_PORT=$(($DBUS_FORWARDING_TCP_LOCAL_PORT+1))
#             echo "Increased local port to " $DBUS_FORWARDING_TCP_LOCAL_PORT;
        else
            echo "Failed to allocate a local TCP port";
            return 1;
        fi
    done

    $KDEV_BASEDIR/kdev_dbus_socket_transformer $DBUS_FORWARDING_TCP_LOCAL_PORT&
    return 0;
}

function keepForwardingDBusFromTCPSocket {

    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
        if ((${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}<${DBUS_ABSTRACT_SOCKET_MAX_TARGET_INDEX})); then
            export DBUS_ABSTRACT_SOCKET_TARGET_INDEX=$(($DBUS_ABSTRACT_SOCKET_TARGET_INDEX+1))
        else
            echo "Failed to allocate a local path for the abstract dbus socket";
            return 1;
        fi
    done

    local PATH=${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}
    export DBUS_SESSION_BUS_ADDRESS=unix:abstract=$PATH${DBUS_SOCKET_SUFFIX}
    $KDEV_BASEDIR/kdev_dbus_socket_transformer $FORWARD_DBUS_FROM_PORT $PATH&
}

function ssh! {
    keepForwardingDBusToTCPSocket # Start the dbus forwarding subprocess
    DBUS_FORWARDING_TCP_TARGET_PORT=$((5000+($RANDOM%50000)))

    ssh $@ -t -R localhost:$DBUS_FORWARDING_TCP_TARGET_PORT:localhost:$DBUS_FORWARDING_TCP_LOCAL_PORT \
         " APPLICATION=$APPLICATION \
           KDEV_BASEDIR=$KDEV_BASEDIR \
           KDEV_DBUS_ID=$KDEV_DBUS_ID \
           FORWARD_DBUS_FROM_PORT=$DBUS_FORWARDING_TCP_TARGET_PORT \
           APPLICATION_HOST=$APPLICATION_HOST \
           KDEV_WORKING_DIR=$KDEV_WORKING_DIR \
           KDEV_SHELL_ENVIRONMENT_ID=$KDEV_SHELL_ENVIRONMENT_ID \
           DBUS_SOCKET_SUFFIX=$(getDBusAbstractSocketSuffix) \
           $(getSSHForwardOptionsFromCommand "$@") \
              bash --init-file \
                        \$(if [ -e \"$KDEV_BASEDIR/kdevplatform_shell_environment.sh\" ]; \
                                then echo \"$KDEV_BASEDIR/kdevplatform_shell_environment.sh\"; \
                           elif [ -e \"$(which kdevplatform_shell_environment.sh)\" ]; then
                                echo \"$(which kdevplatform_shell_environment.sh)\"; \
                           else \
                                echo \"~/.kdevplatform_shell_environment.sh\"; \
                           fi) \
                   -i"



    if [ "$FORWARD_DBUS_FROM_PORT" ]; then
        # We created the 2nd subprocess
        kill %2 # Stop the dbus forwarding subprocess
    else
        # We created the 1st subprocess
        kill %1 # Stop the dbus forwarding subprocess
    fi
}

# A version of ssh! that preserves the current working directory
function ssw! {
    KDEV_WORKING_DIR=$(pwd)
    ssh! $@
}

function env! {
    FILES="$(executeInAppSync "ls $(getSessionDir)/*.sh" "")"
    for FILE in $FILES; do
        FILE=$(basename $FILE)
        ID=${FILE%.sh} # This ugly construct strips away the .sh suffix
        if [[ "$ID" == "$KDEV_SHELL_ENVIRONMENT_ID" ]]; then
            echo "$ID   [current]"
        else
            echo "$ID"
        fi
    done
}

function editenv! {
    local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID
    if [ "$1" ]; then
        ENV_ID=$1
    fi
    # If the environment-file doesn't exist yet, create it
    executeInAppSync "if ! [ -e $(getCurrentShellEnvPath $ENV_ID) ]; then touch $(getCurrentShellEnvPath $ENV_ID); fi" ""
    # Open it
    openDocument "$(getCurrentShellEnvPath $ENV_ID)"
}

function setenv! {
    if [ "$1" ]; then
        KDEV_SHELL_ENVIRONMENT_ID=$1
    fi

    # Execute the contents of the shell-environment
    # note: keep compatible with FreeBSD: https://bugs.kde.org/show_bug.cgi?id=311186
    local TEMP=$(mktemp /tmp/$USER-XXXXXXXX)
    RESULT=$(executeInAppSync "cat \"$(getCurrentShellEnvPath)\"" "")
    echo "$RESULT" >| $TEMP
    if ! [ "$RESULT" ]; then
        # If the environment shell file doesn't exist, create it
        executeInAppSync "if ! [ -e $(getCurrentShellEnvPath) ]; then touch $(getCurrentShellEnvPath); fi" ""
    fi
    source $TEMP
    rm -f $TEMP
}

function showenv! {
    local ENV_ID=$KDEV_SHELL_ENVIRONMENT_ID
    if [ "$1" ]; then
        ENV_ID=$1
    fi

    echo "Environment $ENV_ID:"

    # Execute the contents of the shell-environment
    echo $(executeInAppSync "cat \"$(getCurrentShellEnvPath $ENV_ID)\"" "")
}

if [ "$FORWARD_DBUS_FROM_PORT" ]; then
    # Start the target-side dbus forwarding, transforming from the ssh pipe to the abstract unix domain socket
    export DBUS_SESSION_BUS_ADDRESS=unix:abstract=${DBUS_ABSTRACT_SOCKET_TARGET_BASE_PATH}-${DBUS_ABSTRACT_SOCKET_TARGET_INDEX}${DBUS_SOCKET_SUFFIX}
    keepForwardingDBusFromTCPSocket
fi

setenv!

##### INITIALIZATION --------------------------------------------------------------------------------------------------------------------

# Mark that this session is attached, by prepending a '!' character
PS1="!$PS1"

echo "You are controlling the $APPLICATION session '$(getSessionName)'. Type help! for more information."

if [ "$KDEV_WORKING_DIR" ]; then
    cd $KDEV_WORKING_DIR
fi