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)
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");
}