1

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)

2 Answers2

4

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");
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

Having the username in usern, you can construct the string any way you want:

std::string dir = "/home/" + usern + "/.program/"

mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

To get the username without promting the user, you can use getlogin_r()

Ramon
  • 1,169
  • 11
  • 25