Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't QFile read from the "~" directory?

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.

like image 874
hurikhan77 Avatar asked May 12 '10 06:05

hurikhan77


2 Answers

You can just create a helper function to do this for you, something like:

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

A more complete solution, allowing for home directories of other users as well, may be:

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

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

    // Special case for current user.

    if ((fspec == "~") || (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, "");           // Remove leading '~'.
    int len = name.indexOf('/');      // Get name (up to first '/').
    len = (len == -1)
        ? name.length()
        : len - 1;
    name = name.left(idx);

    // Find that user in the password file, replace with home
    // directory if found, then return it. You can also add a
    // Windows-specific variant if needed.

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

    return fspec;
}

Just one thing to keep in mind, the current solution is not portable to Windows (as per the comments in the code). I suspect this is okay for the immediate question since .vimrc indicates that's not the platform you're running on (it's _vimrc on Windows).

Tailoring the solution to that platform is possible, and indeed shows that the helper-function solution is a good fit since you'll only have to change one piece of code to add that.

like image 119
paxdiablo Avatar answered Sep 27 '22 22:09

paxdiablo


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.

like image 42
Michael Aaron Safyan Avatar answered Sep 27 '22 21:09

Michael Aaron Safyan