tags:

views:

1863

answers:

3

I'm writing a simple program to browse the local network and pass on filenames to mplayer using "system". However, sometimes filenames contain spaces or quotes. Obviously I could write my own function to escape those, but I'm not sure exactly what characters do or do not need escaping.

Is there a function available in the CRT or somewhere in the linux headers to safely escape a string to pass to the command line ?

+2  A: 

While i don't know a function that does this, you can surround each of your arguments with '...', and replace any ' in the original argument by '"'"' . like system("mplayer 'foo'\"'\"' bar'"); will give a single argument to mplayer which is foo' bar and which is allowed to contain strange things like " or \n . Note the escaping before " above (\") is only to make it valid C++.

You should consider using a function that accepts the arguments each separate, thus avoiding such issues. Wikipedia has a good article on this about the famous fork-and-exec pattern. http://en.wikipedia.org/wiki/Fork-exec

Johannes Schaub - litb
+5  A: 

There isn't a single solution that works everywhere because different shells have different ideas of what special characters are and how they are interpreted. For bash, you could probably get away with surrounding the entire filename in single quotes after replacing every single quote in the file name with '"'"' (the first single quote stops the sequence, the "'" appends the literal single quote to the string, the final single quote starts the quoted sequence again). A better solution would be to find a way to call the program without using system, such as by using fork with one of the exec functions so there is no shell interpolation.

Robert Gamble
youre right. it's not only '' but '"'"' :p
Johannes Schaub - litb
It is possible to create a safe solution for a specific shell but I answered the question asked, acknowledged it wasn't the best way to go about it, and provided a safe alternative. I don't think this deserves a down vote.
Robert Gamble
+7  A: 

Other answers include this fork and exec solution, but I claim that this is the only right way to do it.

Escaping shell arguments is prone to bugs and a waste of time, just as trying to escape SQL parameters is a silly idea when safer and more efficient parameter binding APIs exist.

Here is a sample function:

void play(const char *path)
{
    /* Fork, then exec */
    pid = fork();

    if( pid < 0 ) { 
        /* This is an error! */
        return;
    }   

    if( pid == 0 ) { 
        /* This is the child */
        freopen( "/dev/null", "r", stdin );
        freopen( "/dev/null", "w", stdout );
        freopen( "/dev/null", "w", stderr );

        execlp( "mplayer", "mplayer", path, (char *)0 );
        /* This is also an error! */
        return;
    }
}
Zan Lynx
you are indeed right it's the only right way. but nevertheless, the question was how you do escaping in c++ for the shell. so we answered it first, and then showed how to do it right.
Johannes Schaub - litb
As I mentioned, fork/exec is the better way to do it but it *is* possible to safely handle this for a given shell which I included in my answer since that was the question asked. I've had to do this where fork/exec wasn't an option so it isn't always a waste of time.
Robert Gamble
Everyone had already covered the quoting so I decided to give detail on fork/exec and express my opinion at the same time. No offense intended to anyone.
Zan Lynx