Search code examples
c++c++11gccmkdirunistd.h

Use a variable inside a string


In my program, part of the resources needed is a directory to store data. As is custom, I decided to make this directory ~/.program/. In c++, the correct way to make this directory (on UNIX-based systems) is this code:

#include <sys/stat.h>
#include <unistd.h>
#include <iostream>

using namespace std;

void mkworkdir()
{
    if(stat("~/.program",&st) == 0)
    {
        cout << "Creating working directory..." << endl;
        mkdir("~/.program/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
        mkdir("~/.program/moredata", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    }

    else
    {
        cout << "Working directory found... continuing" << endl;
    }
}

int main()
{
    mkworkdir();
    return 0;
}

Now, the reliability of using the ~ in mkdir("~/.program/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) is questionable at the least, so what I actually want to do is prompt for the username, store that in a string (like string usern; cin >> usern;), and then do mkdir("/home/{$USERN}/.program/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) (like in shell). However, I have no idea how to get some equivalent of $USERN into a string, as in, I don't know how to get an expandable c++ construction into a string. What I mean with that is that I insert whatever "form" of variable would get expanded into the contents of that variable into the string.

I apologize if this question is confusing, I just can't seem to be able to explain well what exactly it is that I want.

Alternatively, and much more preferably, would it be possible to get the username without prompting for it? (and store this in a string, of course)


Solution

  • You could use:

    std::string usern;
    std::cin >> usern;
    std::string directory = "/home/" + usern + "/.program";
    mkdir(directory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    

    A better alternative, IMO, is to use the value of the environment variable HOME.

    char const* home = std::getenv("HOME");
    if ( home == nullptr )
    {
       // Deal with problem.
    }
    else
    {
       std::string directory = home + std::string("/.program");
       mkdir(directory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
    }
    

    FWIW, you can simplify your code by creating a function make_directory in your application's namespace where you can add the details of checking whether the directory exists, using the right flags, etc.

    namespace MyApp
    {
       bool directory_exists(std::string const& directory)
       {
          struct stat st;
    
          // This is simplistic check. It will be a problem
          // if the entry exists but is not a directory. Further
          // refinement is needed to deal with that case.
          return ( stat(directory.c_tr(), &st) == 0 );     
       }
    
       int make_directory(std::string const& directory)
       {
          if ( directory_exists(directory) )
          {
             return 0;
          }
          return mkdir(directory.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
       }
    }
    

    and then, you can just MyApp::make_directory in rest of your code.

    char const* home = std::getenv("HOME");
    if ( home == nullptr )
    {
       // Deal with problem.
    }
    else
    {
       std::string directory = home + std::string("/.program");
       MyApp::make_directory(directory);
       MyApp::make_directory(directory + "/moredata");
    }