How do I change the execution PATH of a process programatically? - c++

I am working on a mini-shell, and am trying to set the execution path dynamically.
I am using setvar() to set the PATH and when I check it with getvar() it reads the new PATH.
Here are the two things I tried to execute:
1. I change the PATH to /bin which I know contains most system functions, but no programs can work. I can also verify that the path has been set in the environment. I execute the commands using execvp()
2. I then used extern char** environ to get the environment and pass it into execvpe() but it still doesn't work.

I solved this by using istringstream to manually search the PATH and then use that to execute the process:
string dir;
string path = get_var("PATH");
istringstream search(path);
while(search.good()) {
getline(search, dir, ':');
if(dir != "") {
struct stat st;
if(dir[dir.length()] != '/') dir.append("/");
string file = dir + cmdArg[0];
//is file in dir?
if(stat(file.c_str(), &st) == 0) {
execvp(file.c_str(), cmdArg);
}
}
}

Related

Creating folders in C++

I have recently started working in C++ and came across this situation when I have to create a directory while executing my code. The code is working fine when I have to create a single folder but it fails when I have to create another folder withing this newly created folder.
Suppose, I am in C: and want to store my file in C:/A/B/ .The following piece of code using mkdir() works fine if I have to store my file in C:/A/ but fails when I am adding another folder B.
Following is my code snippet:
#include <sys/stat.h>
#include <string>
using namespace std;
int main()
{
string stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
if(status!=0)
{
//.....
}
else
{
//....
}
}
Can someone help me in creating this directory where I can have any number of folders inside the parent directory? (P.S:I have added the header files sys/stat.h,iostream and string)
This is how you do it in C++17:
#include <filesystem>
namespace fs = std::filesystem;
fs::create_directories("./a/b/c")
mkdir() creates only the last component of the specified path. In your example, it will create only B. If any of the parent directories do not exist (ie, if A does not exist), the function fails with ENOENT. You need to split up the path and call mkdir() for every intermediate directory in the path, ignoring EEXIST errors as you go.
status = mkdir("C:/A/", 0777);
if ((status < 0) && (errno != EEXIST)) ...
status = mkdir("C:/A/B/", 0777);
if ((status < 0) && (errno != EEXIST)) ...
If you don't want to handle this manually, use a wrapper that handles it for you, such as Boost's create_directories() function:
bool create_directories(const path& p);
bool create_directories(const path& p, system::error_code& ec);
Effects: Establishes the postcondition by calling create_directory() for any element of p that does not exist.
Postcondition: is_directory(p)
Returns: true if a new directory was created, otherwise false.
Throws: As specified in Error reporting.
Complexity: O(n+1)where n is the number of elements of p that do not exist.
You can call the following:
string stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
If
C:/A/ directory exists. If its not exists, then do the following:
string stringpath = "C:/A/";
int status = mkdir(stringpath.c_str(),0777);
stringpath = "C:/A/B/";
int status = mkdir(stringpath.c_str(),0777);
In C++11 you can use the experimental functios:
#include <experimental/filesystem>
...
std::stringstream bufH;
bufH << dirName << fName;
if (!std::experimental::filesystem::exists(bufH.str()))
{
std::experimental::filesystem::create_directories(bufH.str());
}
Try the octal flag 7777 like this to have all the rights necessary to create this folder.
int status = mkdir(stringpath.c_str(), 7777);
Or do a chmod in the A folder like that :
chmod -r 7777 *

/usr/bin/env: node: No such file or directory , c++

i am writing some functions on c++ for compiler less to css.
i installed nodejs, less.
i created a less file test.less
#color: red;
a{color:#color;}
when i run command on terminal:
lessc test.less test.css
it created a files css with name is test.css, but when i run this command via c++, it return a error. please help me. this is my c++ function:
std::string shell_exec( std::string cmd )
{
std::string result = "";
FILE* pipe = popen(cmd.c_str(), "r");
if (pipe == NULL)
{
return result;
}
char buffer[128];
while(!feof(pipe))
{
if(fgets(buffer, 128, pipe) != NULL)
{
result += buffer;
}
}
pclose(pipe);
return result;
}
shell_exec("lessc test.less test.css");
i got a error:
/usr/bin/env: node: No such file or directory
/usr/bin/node is existed.
================ UPDATE: Fixed==================
Thank you #Bass Jobsen , #Lightness Races in Orbit
i fixed by add absolute path to lessc and nodejs
shell_exec("/usr/bin/node /usr/bin/lessc test.less test.css");
From: https://unix.stackexchange.com/a/29620
The advantage of #!/usr/bin/env python is that it will use whatever
python executable appears first in the user's $PATH.
So you should add node to the $PATH of the user that runs your script, see: https://stackoverflow.com/a/13210246/1596547
Notice that i can not compile your code, but i can when using the following code:
int main()
{
std::string r = shell_exec("lessc test.less test.css");
}
Probably also use using namespace std and string instead of std:string.

Open a file with unicode path

I'm working under windows 7 with mingw. I have encountered some weird behaviour with unicode filenames. My program needs to be portable, and I'm using boost::filesystem (v 1.53) to handle the file paths.
This has all been going well, until I needed to open files with unicode filenames.
This is not about the content of the file, but the file's name.
I tried the following: For testing I made a folder named C:\UnicodeTest\вячеслав and I tried creating a file inside of it, by appending the file name test.txt to the boost wpath.
For some reason the creation of the file fails. I'm using boost's fstreams and when I try to open the file, the failbit of the stream is set.
Now the funny thing is, that when I append a foldername to the path instead, a call to create_directories() succeeds and creates the correct directory C:\UnicodeTest\вячеслав\folder.
I really don't understand why it won't work with a file. This is the code I use:
boost::filesystem::wpath path;
// find the folder to test
boost::filesystem::wpath dirPath = "C:\\UnicodeTest";
vector<boost::filesystem::wpath> files;
copy(boost::filesystem::directory_iterator(dirPath), boost::filesystem::directory_iterator(), back_inserter(files));
for(boost::filesystem::wpath &file : files)
{
if(boost::filesystem::is_directory(file))
{
path = file;
break;
}
}
// create a path for the folder
boost::filesystem::wpath folderPath = path / "folder";
// this works just fine
boost::filesystem::create_directories(folderPath);
// create a path for the file
boost::filesystem::wpath filePath = path / "test.txt";
boost::filesystem::ofstream stream;
// this fails
stream.open(filePath);
if(!stream)
{
cout << "failed to open file " << path << endl;
}
else
{
cout << "success" << endl;
}
If I understand the issue correctly, the issue of being unable to create a file directly within C:\UnicodeTest\вячеслав occurs when you do not create the folder directory, as illustrated below.
// create a path for the folder
//boost::filesystem::wpath folderPath = path / "folder";
// this works just fine
//boost::filesystem::create_directories(folderPath);
// create a path for the file
boost::filesystem::wpath filePath = path / "test.txt";
I was able to get this to work by making the filename a wchar_t string:
// create a path for the file
boost::filesystem::wpath filePath = path / L"test.txt";

stat() doesn't find a file in c++

on Linux 12.04
I have an executable file located in say:
/a/b/exe
and a config file on
/a/b/config
when doing:
cd /a/b/
./exe
everything's ok and the stat function finds the file config on /a/b/
HOWEVER,when running from root
/a/b/exe
the stat doesn't find the config file
any idea why?
it makes it impossible to run the binary using a script that isn't ran from the folder of the exe.
Edit
The call looks like this:
struct stat stFileInfo;
bool blnReturn;
int intStat;
// Attempt to get the file attributes
intStat = stat(strFilename.c_str(),&stFileInfo);
if(intStat == 0) {
// We were able to get the file attributes
// so the file obviously exists.
blnReturn = true;
} else {
// We were not able to get the file attributes.
// This may mean that we don't have permission to
// access the folder which contains this file. If you
// need to do that level of checking, lookup the
// return values of stat which will give you
// more details on why stat failed.
blnReturn = false;
}
In first case cd ..., run exe you change current working directory before executing the program, in second case you launch exe without changing current working directory, and I think in your program you use a relative path to open your config(for example ./config or just config) and it can't find it from current working directory. easiest workaround is to change working directory at start of your app:
int main(int argc, char** argv) {
std::string s( argv[0] ); // path to the program
std::string::size_type n = s.rfind( '/' );
if( n != std::string::npos ) {
std::system( ("cd " + s.substr(0, n)).c_str() );
}
// rest of your code
}

how to search the computer for files and folders

i need a way to search the computer for files like Windows Explorer. i want my program to search lets say hard drive c:. i need it to search C:\ for folders and files (just the ones you could see in c:\ then if the user clicks on a file on the list like the folder test (C:\test) it would search test and let the user see what files/folders are in it.
Since you mentioned windows, the most straight forward winapi way to do it is with FindFirstFile and FindNextFile functions.
edit: Here's an example that shows you how to enumerate all files/folders in a directory.
#include <Windows.h>
#include <iostream>
int main()
{
WIN32_FIND_DATA file;
HANDLE search_handle=FindFirstFile(L"C:\\*",&file);
if (search_handle)
{
do
{
std::wcout << file.cFileName << std::endl;
}while(FindNextFile(search_handle,&file));
FindClose(search_handle);
}
}
This will be OS dependent. The SO question
How can I get a list of files in a directory using C or C++?
handles this problem well. You can download DIRENT here.
Now that you have this, I'd recommend recursively searching for a file with a DFS/BFS algorithm. You can assume the whole directory structure is a tree where each file is a leaf node and each subdirectory is an internal node.
So all you have to do is,
Get the list of files/folders in a directory with a function such as:
void getFilesFolders(vector<string> & dir_list, const string & folder_name)
If it's a directory, go to 1 with the directory name
If it's a file, terminate if it's the file you're looking for, else move on to the next file.
boost::filesystem can be a cross-platform solution for that (check out for such functions in it).
You can use Directory class members to do this with C# or managed C++. See the following MSDN article:
http://support.microsoft.com/kb/307009
If you wish to use C++ with MFC you can use CFileFind
http://msdn.microsoft.com/en-us/library/f33e1618%28v=VS.80%29.aspx
You'll have to supply your own browse window to present the file system tree.
Or you can use one of the directory/file controls to do both for you.
#include <Windows.h>
#include <iostream>
int FindF(char* pDirectory)
{
char szFindPath[MAX_PATH] = {0};
strcpy(szFindPath, pDirectory);
strcat(szFindPath, "\\*");
WIN32_FIND_DATA file;
HANDLE search_handle=FindFirstFile(szFindPath,&file);
if (search_handle)
{
do
{
if(file.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
strcpy(szFindPath, pDirectory);
strcat(szFindPath, "\\");
strcat(szFindPath, file.cFileName);
FindF(szFindPath);
}
std::wcout << file.cFileName << std::endl;
}while(FindNextFile(search_handle,&file));
CloseHandle(search_handle);
}
}
There really is no need to use 3rd party library to accomplish this. This is a short, independent function which lists all files (with their paths) in a directory, including subdiretories' files. std::string folderName has to finish with \, and if you want to list all files on computer, just create a loop in calling function along with GetLogicalDriveStrings (It returns strings with \, so it couldn't be more convenient in this case).
void FindAllFiles(std::string folderName)
{
WIN32_FIND_DATA FileData;
std::string folderNameWithSt = folderName + "*";
HANDLE FirstFile = FindFirstFile(folderNameWithSt.c_str(), &FileData);
if (FirstFile != INVALID_HANDLE_VALUE) {
do {
if (strcmp(FileData.cFileName, ".") != 0 && strcmp(FileData.cFileName, "..") != 0)
{
if(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
std::string NewPath = folderName + FileData.cFileName;
NewPath = NewPath + "\\";
FindAllFiles(NewPath);
}
else
{
std::cout /*<< folderName*/ << FileData.cFileName << std::endl;
}
}
} while(FindNextFile(FirstFile, &FileData));
}
}
This is ASCII version, remember that files and folders can be named in Unicode