views:

254

answers:

4

I've tried the following short example to find out about a bug in a bigger program I am working on. It looks like QFile doesn't support unix (or the shell's) notation for the home directory:

#include <QFile>
#include <QDebug>

int main()
{
        QFile f("~/.vimrc");
        if (f.open(QIODevice::ReadOnly))
        {
                qDebug() << f.readAll();
                f.close();
        }
        else
        {
                qDebug() << f.error();
        }
}

As soon as I replace the "~" with my real home directory path, it works. Is there an easy workaround - some setting to enable? Or do I have to go the "ugly" way and ask QDir for the home directory of the current user and prepend that manually to each path?

Addendum: It's clear that usually the shell performs the tilde expansion so programs would never see that. Still it is so convenient in unix shells that I hoped the Qt implementation for file access would have that expansion included.

+3  A: 

It has nothing to do with not supporting UNIX; the expansion of tildes to the user's home directory is a substitution performed by the shell, so yes, you will have to manually replace them.

Michael Aaron Safyan
Yes, I know it's a shell expansion. But it is so convenient that I hoped that the unix Qt implementation would support and expand on the tilde.
hurikhan77
A: 

Take a look at the C library function glob, which will do tilde expansion (and possibly wildcard expansion and various other functions too).

Andrew McGregor
I suppose that will involve excaping other special globbing chars then which I don't want to fiddle around with. And I have to handle multiple entries in the returned list somehow because glob will not return a single string (though a list containing probably only one string).
hurikhan77
That depends on your OS; for example, OS X has a GLOB_LIMIT flag, that will let you ask for only one result. For what may be an even more useful function, try `wordexp`, which lets you do most of what the shell does in constructing commands.
Andrew McGregor
+6  A: 

Why not just create a helper function, something like (untested):

QString morphFile (QString s) {
    if (s.startsWith ("~/"))
        s.replace (0, 1, QDir::homePath());
    return s;
}
:
QFile f(morphFile ("~/.vimrc"));

A more complete solution may be (again untested since I don't have Qt locally, but a competent coder should be able to get this going):

QString morphFile (QString fspec) {
    // Leave strings not starting with tilde.

    if (!fspec.startsWith ("~"))
        return fspec;

    // Special case for current user.

    if (fspec.startsWith ("~/")) {
        fspec.replace (0, 1, QDir::homePath());
        return fspec;
    }

    // General case for any user. Get user name and length of it.

    QString name (fspec);
    name.replace (0, 1, "");
    int len = name.indexOf ('/');
    if (len == -1)
        len = name.length()
    else
        len--;
    name = name.left (idx);

    // Find that user in the passwd file, replace with home directory
    //   if found, then return it.

    struct passwd *pwent = getpwnam (name.toAscii().constData());
    if (pwent != NULL)
        fspec.replace (0, len+1, pwent->pw_dir);

    return fspec;
}
paxdiablo
While I like the idea it will become cumbersome when handling cases like "~user" etc...
hurikhan77
Your code probably shouldn't be attempting to fiddle around in another user's directory anyway :-) But, yes, if you want the full expansions, you'll probably need to go out to the shell to get them. I was only answering the specific case in your question, that of `~`. Still, if you need the entire gamut, it's _still_ best to isolate the code to this single helper function. If it's just `~/`, use what I've given. If it's `~<anything-else>`, find another way within the function, such as running a subshell to retrieve the value of it. I can't see a less cumbersome way of doing what you want.
paxdiablo
In other words, even if Qt provided this functionality, it would probably be doing it the way I'm suggesting (although possibly a little more cross-platform).
paxdiablo
Well, you could do a pwent lookup or something, I'm sure. As a quickfix I am going with QString("%1/.vimrc").arg(QDir::homePath()). I will investigate a more elegant solution later. Maybe someone posts it. ;-)
hurikhan77
@paxdiablo: While ~user can be used to define the home of 'user', that is not as widely used as plain ~ to refer to your own home directory. Anyway you can detect the difference by looking for ~ as the first character and then checking whether the second character is / or not. Dealing with ~user would get a lot messier... but you can at least detect it and complain :)
David Rodríguez - dribeas
Okay, I've added some (untested) code which should come close to handling other users as well. Keep in mind it's not thread-safe, you ll need to use the `_r` versions if you want that. In fact, it may not even compile but it's a good start :-) Feel free to edit once you get it working (or let me know the problems and I'll fix them myself). Cheers.
paxdiablo
@paxdiablo: If the user puts `~foo/bar.baz` in a configuration file (or enters it in a dialog; I've had to deal with that in the past) then it's not the programmer's place to say "don't fiddle around in other people's home directories". Leave saying that to the OS, or maybe the other user will actually have granted permission on that file. This sort of thing happens when code hits the ground.
Donal Fellows
@Donal, I'm not sure if you noticed the smiley at the end of that sentence. But you raise a good point, which is hopefully now addressed in the updated answer.
paxdiablo
The question is not about if one should or should not poke around in other users directories. In this case I need it for sharing some printing templates between users with one user able to keep and edit in his home dir. However, this won't be a permanent solution, it will be replaced later.
hurikhan77
+2  A: 

Please submit a suggestion to the Qt bugtracker.

http://bugreports.qt.nokia.com/

guruz