Programmatically Setting Terminal CWD

Discussion in 'Mac Programming' started by birchy, Aug 6, 2010.

  1. birchy macrumors newbie

    birchy

    Joined:
    Aug 6, 2010
    Location:
    Canada
    #1
    Hi there,

    I am trying to create a command line utility that allows me to change directories in a terminal efficiently. Basically I want to be able to programmatically set the current working directory in Terminal.app (using either c++ or objective-c++).

    The following code (together with provided output) shows an example of the problem I am experiencing:
    Code:
    #include <iostream>
    #include <string>
    using namespace std;
    
    #define COLOR_RED   "\E[0;31;48m"
    #define COLOR_BLACK "\E[0;30;48m"
    
    #define PERROR() fprintf(stderr,"  %sERROR:%s ", COLOR_RED, COLOR_BLACK)
    #define D_ERROR( e, msg, ... ) do{ PERROR(); fprintf( stderr, msg , ## __VA_ARGS__); fprintf(stderr,"\n"); exit(e); } while(0)
    //#define _SOL1
    
    string getCWD() {
        char result[FILENAME_MAX];
        if( !getcwd( result, sizeof(result) ) )
            D_ERROR( -1, "Could not get current working directory.\n" );
        return string( result, (strlen(result) > 0) ? strlen(result) : 0 );
    }
    
    int setCWD( const char *path ) {
    #ifdef _SOL1
        BOOL success;
        NSFileManager *fm = [NSFileManager defaultManager];
    
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSString *nsPath = [NSString stringWithCString:path encoding:[NSString defaultCStringEncoding]];
        success = [fm changeCurrentDirectoryPath: nsPath];
        [pool release];
    
        if( success == YES ) return 0;
    #else
        if( !chdir( path ) ) return 0;
    #endif
    
        return -1;
    }
    
    int main( int argc, char **argv ) {
        fprintf( stdout, "  Current Directory: %s\n", getCWD().c_str() );
    
        fprintf( stdout, "  Enter Desired CWD: " );
        string dir;
        getline( cin, dir );
        int e = setCWD( dir.c_str() );
        if( e == 0 ) {
            fprintf( stdout, "  Current Directory: %s\n", getCWD().c_str() );
            return 0;
        }
        fprintf( stderr, "Could not change cwd to %s.\n", dir.c_str() );
        return -1;
    }
    And this output:
    Code:
    birchy-mac: pwd
    /Users/birchy/Dev/cwd
    birchy-mac: ./cwd.out
      Current Directory: /Users/birchy/Dev/cwd
      Enter Desired CWD: /Users/
      Current Directory: /Users
    birchy-mac: pwd
    /Users/birchy/Dev/cwd
    birchy-mac: 
    It appears that the current working directory is being changed successfully within the scope of the application, however the current working directory of Terminal.app does not change (as is apparent by the output of pwd after execution of the application).

    I have tried both of the shown implementations within setCWD, (ie traditional UNIX way and the Cocoa way) yet both exhibit this same behaviour.

    Does anyone have any idea why this is happening?
    Does anyone know how I can solve this issue?

    Help would be much appreciated.
    Thanks in advance!
     
  2. mrbash macrumors 6502

    Joined:
    Aug 10, 2008
    #2
    Unfortunately you cannot do what you are trying to do because executing a program is essentially forking, you won't be able to modify the parent process's environment from the child.
    What you can do instead is to wrap your program in a script that will change the current directory based on some input from your program.

    e.g:
    Code:
    #!/bin/bash
    cd `/path/to/myprogram`
    
    note that I am not using the apostrophe, but the the other character below ~ on your keyboard.
     
  3. birchy thread starter macrumors newbie

    birchy

    Joined:
    Aug 6, 2010
    Location:
    Canada
    #3
    Yes, I could wrap it in a bash script.

    Actually, the original implementation of this utility was done in bash, however I was having issues supporting the script on multiple platforms, so I decided to take a shot at implementing the utility in c++.

    This being said, it is my intension to implement this entirely in c++/objective-c++.

    Anyone know of a workaround to this issue?

    Is it possible to capture the pid of the parent (Terminal.app) and then set the id of another process (Terminal.app)?
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    There is no workaround. No process can directly change the working directory of another process. If it could, it would be an exploitable security hole.

    The best you can do is to somehow trigger the execution of code in the target process, which then changes its own working directory.
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    I'd alias a command to "cd `dir_changalator`". Then when you run your alias, your program executes, and its output is passed to cd.

    -Lee
     
  6. asciimov macrumors newbie

    Joined:
    Jan 31, 2008
    #6
    Solution

    First lets get everybody on the same page:

    1) you need this script, save it as cwd in /usr/local/bin

    Code:
    #!/bin/bash
    printf "\tCurrent Directory: %s\n" $(pwd)
    printf "\tEnter Desired CWD: "
    read input_dir
    cd $input_dir
    printf "\tCurrent Directory: %s\n" $(pwd)
    
    2) set the permissions for the script, we need to be able to execute it, from the terminal type

    Code:
    chmod 711 cwd
    3) the shell forks executables, the command "source" runs the executable without forking, from the terminal type

    Code:
    source ./cwd
    -or-

    Code:
    . ./cwd
    4) #3 looks ugly you say, well then lets make an "alias"

    if /usr/local/bin is in your $PATH
    Code:
    alias cwd="source cwd"
    -or- if /usr/local/bin is not in your $PATH

    Code:
    alias cwd="source /usr/local/bin/cwd"
    Second, I don't like this program/script. 1) it doesn't have tab completions, which saves tons of key strokes 2) its an interactive script, which limits its usefulness 3) you can have your shell display the current directory before the prompt. 4) why do in C++ what you can do in a very simple script?

    Finally, I feel that this is probably somebodies homework but oh well. If you want a new exercise, figure out how to do this in an alias, not that I condone such use...
     

Share This Page