Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

birchy

macrumors newbie
Original poster
Aug 6, 2010
4
0
Canada
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!
 
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.
 
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)?
 
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)?

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.
 
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
 
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...
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.