How can I change my current working directory in C++ in a platform-agnostic way?
I found the direct.h header file, which is Windows compatible, and the unistd.h, which is UNIX/POSIX compatible.
The chdir function works on both POSIX (manpage) and Windows (called _chdir there but an alias chdir exists).
Both implementations return zero on success and -1 on error. As you can see in the manpage, more distinguished errno values are possible in the POSIX variant, but that shouldn't really make a difference for most use cases.
Now, with C++17 is possible to use std::filesystem::current_path:
#include <filesystem>
int main() {
auto path = std::filesystem::current_path(); //getting path
std::filesystem::current_path(path); //setting path
}
For C++, boost::filesystem::current_path (setter and getter prototypes).
A file system library based on Boost.Filesystem will be added to the standard.
This cross-platform sample code for changing the working directory using POSIX chdir and MS _chdir as recommend in this answer. Likewise for determining the current working directory, the analogous getcwd and _getcwd are used.
These platform differences are hidden behind the macros cd and cwd.
As per the documentation, chdir's signature is int chdir(const char *path) where path is absolute or relative. chdir will return 0 on success. getcwd is slightly more complicated because it needs (in one variant) a buffer to store the fetched path in as seen in char *getcwd(char *buf, size_t size). It returns NULL on failure and a pointer to the same passed buffer on success. The code sample makes use of this returned char pointer directly.
The sample is based on #MarcD's but corrects a memory leak. Additionally, I strove for concision, no dependencies, and only basic failure/error checking as well as ensuring it works on multiple (common) platforms.
I tested it on OSX 10.11.6, Centos7, and Win10. For OSX & Centos, I used g++ changedir.cpp -o changedir to build and ran as ./changedir <path>.
On Win10, I built with cl.exe changedir.cpp /EHsc /nologo.
MVP solution
$ cat changedir.cpp
#ifdef _WIN32
#include <direct.h>
// MSDN recommends against using getcwd & chdir names
#define cwd _getcwd
#define cd _chdir
#else
#include "unistd.h"
#define cwd getcwd
#define cd chdir
#endif
#include <iostream>
char buf[4096]; // never know how much is needed
int main(int argc , char** argv) {
if (argc > 1) {
std::cout << "CWD: " << cwd(buf, sizeof buf) << std::endl;
// Change working directory and test for success
if (0 == cd(argv[1])) {
std::cout << "CWD changed to: " << cwd(buf, sizeof buf) << std::endl;
}
} else {
std::cout << "No directory provided" << std::endl;
}
return 0;
}
OSX Listing:
$ g++ changedir.c -o changedir
$ ./changedir testing
CWD: /Users/Phil
CWD changed to: /Users/Phil/testing
Centos Listing:
$ g++ changedir.c -o changedir
$ ./changedir
No directory provided
$ ./changedir does_not_exist
CWD: /home/phil
$ ./changedir Music
CWD: /home/phil
CWD changed to: /home/phil/Music
$ ./changedir /
CWD: /home/phil
CWD changed to: /
Win10 Listing
cl.exe changedir.cpp /EHsc /nologo
changedir.cpp
c:\Users\Phil> changedir.exe test
CWD: c:\Users\Phil
CWD changed to: c:\Users\Phil\test
Note: OSX uses clang and Centos gnu gcc behind g++.
Does chdir() do what you want? It works under both POSIX and Windows.
You want chdir(2). If you are trying to have your program change the working directory of your shell - you can't. There are plenty of answers on SO already addressing that problem.
Did you mean C or C++? They are completely different languages.
In C, the standard that defines the language doesn't cover directories. Many platforms that support directories have a chdir function that takes a char* or const char* argument, but even where it exists the header where it's declared is not standard. There may also be subtleties as to what the argument means (e.g. Windows has per-drive directories).
In C++, googling leads to chdir and _chdir, and suggests that Boost doesn't have an interface to chdir. But I won't comment any further since I don't know C++.
Nice cross-platform way to change current directory in C++ was suggested long time ago by #pepper_chico. This solution uses boost::filesystem::current_path().
To get the current working directory use:
namespace fs = boost::filesystem;
fs::path cur_working_dir(fs::current_path());
To set the current working directory use:
namespace fs = boost::filesystem;
fs::current_path(fs::system_complete( fs::path( "new_working_directory_path" ) ));
Bellow is the self-contained helper functions:
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include <string>
namespace fs = boost::filesystem;
fs::path get_cwd_pth()
{
return fs::current_path();
}
std::string get_cwd()
{
return get_cwd_pth().c_str();
}
void set_cwd(const fs::path& new_wd)
{
fs::current_path(fs::system_complete( new_wd));
}
void set_cwd(const std::string& new_wd)
{
set_cwd( fs::path( new_wd));
}
Here is my complete code-example on how to set/get current working directory:
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"
#include <iostream>
namespace fs = boost::filesystem;
int main( int argc, char* argv[] )
{
fs::path full_path;
if ( argc > 1 )
{
full_path = fs::system_complete( fs::path( argv[1] ) );
}
else
{
std::cout << "Usage: tcd [path]" << std::endl;
}
if ( !fs::exists( full_path ) )
{
std::cout << "Not found: " << full_path.c_str() << std::endl;
return 1;
}
if ( !fs::is_directory( full_path ))
{
std::cout << "Provided path is not a directory: " << full_path.c_str() << std::endl;
return 1;
}
std::cout << "Old current working directory: " << boost::filesystem::current_path().c_str() << std::endl;
fs::current_path(full_path);
std::cout << "New current working directory: " << boost::filesystem::current_path().c_str() << std::endl;
return 0;
}
If boost installed on your system you can use the following command to compile this sample:
g++ -o tcd app.cpp -lboost_filesystem -lboost_system
Can't believe no one has claimed the bounty on this one yet!!!
Here is a cross platform implementation that gets and changes the current working directory using C++. All it takes is a little macro magic, to read the value of argv[0], and to define a few small functions.
Here is the code to change directories to the location of the executable file that is running currently. It can easily be adapted to change the current working directory to any directory you want.
Code :
#ifdef _WIN32
#include "direct.h"
#define PATH_SEP '\\'
#define GETCWD _getcwd
#define CHDIR _chdir
#else
#include "unistd.h"
#define PATH_SEP '/'
#define GETCWD getcwd
#define CHDIR chdir
#endif
#include <cstring>
#include <string>
#include <iostream>
using std::cout;
using std::endl;
using std::string;
string GetExecutableDirectory(const char* argv0) {
string path = argv0;
int path_directory_index = path.find_last_of(PATH_SEP);
return path.substr(0 , path_directory_index + 1);
}
bool ChangeDirectory(const char* dir) {return CHDIR(dir) == 0;}
string GetCurrentWorkingDirectory() {
const int BUFSIZE = 4096;
char buf[BUFSIZE];
memset(buf , 0 , BUFSIZE);
GETCWD(buf , BUFSIZE - 1);
return buf;
}
int main(int argc , char** argv) {
cout << endl << "Current working directory was : " << GetCurrentWorkingDirectory() << endl;
cout << "Changing directory..." << endl;
string exedir = GetExecutableDirectory(argv[0]);
ChangeDirectory(exedir.c_str());
cout << "Current working directory is now : " << GetCurrentWorkingDirectory() << endl;
return 0;
}
Output :
c:\Windows>c:\ctwoplus\progcode\test\CWD\cwd.exe
Current working directory was : c:\Windows
Changing directory...
Current working directory is now : c:\ctwoplus\progcode\test\CWD
c:\Windows>
Related
I'm an inexperienced Linux programmer and am trying to learn to use readlink() based on this question and answer.
My call to readlink() returns -1 and sets errno to 2 (ENOENT).
The code:
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <iostream>
#include <algorithm>
#include <cstdio>
int main(int argc, char* argv[])
{
char szTmp[100];
snprintf(szTmp, 100, "proc/%d/exe", getpid());
std::cout << "szTmp is " << szTmp << std::endl;
char executingFolder[500];
errno = 0;
int bytes = std::min(readlink(szTmp, executingFolder, 500), (ssize_t)499);
if (bytes > 0)
{
executingFolder[bytes] = '\0';
}
std::cout << "bytes is " << bytes << std::endl;
std::cout << "errno is " << errno;
if (ENOENT == errno)
{
std::cout << " ENOENT";
}
std::cout << std::endl;
std::cout << "Executing folder is \"" << executingFolder << "\"" << std::endl;
return 0;
}
The output:
(An example from one iteration since pid changes)
szTmp is proc/22272/exe
bytes is -1
errno is 2 ENOENT
Executing folder is ""
Things I have tried:
After compilation: sudo ./a.out (thinking that directory access was restricted because of lack of permission). Result: unchanged behavior from ./a.out
SIGINT the program during execution, and verified that /proc/<pid>/exe exists. Result: it consistently exists for each run of the program.
Verified that the value of the target link is well within 499 chars.
Can someone please help identify the problem? Having read the readlink man page and online descriptions, and the noted StackOverflow article, I am still unclear what is wrong.
Thank you.
proc/1234/exe is a relative path.
I think you want /proc/%d/exe, which is an absolute path, and correctly refers to the /proc directory.
Secondly, because readlink() will truncate the result in case the buffer is too small, you should consider the case where the return value is == bufsiz to be an error, as truncation may have happened. You can't know.
Also, "Executing folder" is not what /proc/<pid>/exe gives you. /proc/<pid>/exe is a symlink to the currently running executable (file), not a directory.
proc/22272/exe is a relative path name. It resolves to the file exe, in the directory 22272, in the directory proc, in your current directory. Unless your current directory is /, that's unlikely to exist.
You want an absolute path name, starting with /, in this case /proc/22272/exe.
Change this:
snprintf(szTmp, 100, "proc/%d/exe", getpid());
to this:
snprintf(szTmp, 100, "/proc/%d/exe", getpid());
But before you fix your program, you might try this:
( cd / ; ~/a.out )
(assuming a.out is in your home directory).
This question already has answers here:
Get path of executable
(25 answers)
Closed 4 years ago.
I want to get the full path of the current process.
I use _getcwd to get the current working directory. But it not includes file name.
How can I get file name like: filename.exe?
argv[0] of your main function is your filename.
A simple code snippet:
#include<stdio.h>
int main(int argc, char** argv)
{
//access argv[0] here
}
If you cannot access/change code in main(), you can do something like this:
std::string executable_name()
{
#if defined(PLATFORM_POSIX) || defined(__linux__) //check defines for your setup
std::string sp;
std::ifstream("/proc/self/comm") >> sp;
return sp;
#elif defined(_WIN32)
char buf[MAX_PATH];
GetModuleFileNameA(nullptr, buf, MAX_PATH);
return buf;
#else
static_assert(false, "unrecognized platform");
#endif
}
On windows you can use:
TCHAR szExeFileName[MAX_PATH];
GetModuleFileName(NULL, szExeFileName, MAX_PATH);
szExeFileName will contain full path + executable name
[edit]
For more portable solution use argv[0] or some other platform specific code. You can find such aproach here: https://github.com/mirror/boost/blob/master/libs/log/src/process_name.cpp.
On Linux, the filename of your binary is the destination of a symlink at /proc/self/exe. You can use the readlink system call to find the destination of a symlink.
Note that this tells you the actual location on disk where the binary is stored, not simply the command the user used to start your program.
Here's a cross-platform way using boost (https://www.boost.org/)
#include <iostream>
#include <boost/dll.hpp>
int main( int argc, char **argv ) {
std::cout << "hello world, this is [" << boost::dll::program_location().filename().string() << "]" << std::endl;
std::cout << "or [" << boost::dll::program_location().string() << "] if you're not into the whole brevity thing." << std::endl;
return 0;
}
compiled via
g++ -o hello_world hello_world.cpp -lboost_filesystem -lboost_system -ldl
results in the output
hello world, this is [hello_world]
or [/home/gjvc/tmp/hello_world] if you're not into the whole brevity thing.
As others have mentioned, the name of your executable is contained in argv[0]. If you need that, you could:
cout << argv[0] << endl;
If you need the name of a source file of the executable, C++ has a predefined macro you can use:
cout << __FILE__ << endl;
Go to here and scroll to "Predefined macro names"
You can use program_invocation_name from errno.h
https://linux.die.net/man/3/program_invocation_short_name
In Linux (POSIX?) there is an enviroment variable called _ that contains the current process.
$ echo $_
echo
In C++
#include <stdlib.h> /* getenv */
#include<iostream>
int main(){
std::cout << getenv("_") << '\n';
return 0;
}
compile
$ c++ a.cpp -o a.out
$ ./a.out
prints ./a.out (or whatever is the executed line, including path).
This has certain advantages over the other approaches, it can be read globally (not passing argv[0]) and doesn't need file handling.
You can usually get the executable file name from argv[0]:
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Running: %s\n", argv[0]);
return 0;
}
Indeed, there are ways for an application to execl() another application (or another similar function) and override this argument. It still is unconventional for the system to change it for that sort of application.
Hey this is more of a question, i want to know if it is possible to modify code through GUI asking because i was asked to see if i could create a GUI where the user can change certain attributes. i.e an exmaple is below
start %= -(status)
> lexeme[elementV]
> -(lexeme[elementF])
> +(inboundGroup);
Above is part of my code which is Boost SPIRIT which parses Strings so for example would it be possible to change the + to a * or - etc
+ = One
- = optional
* = multiple
Do you think it would be possible to change that through a GUI i think it could be just not sure on how to do it?
Any help i will be very grateful
Thanks Shamari
Everything is possible in programming ;-)
For dynamic modification of a program during execution, there are several solutions :
Use a dynamic language like LUA
Use a plugin system with dynamic loading
Since you require C++ and Boost Spirit, I think the best solution is to generate a plugin on the fly and load it afterwards.
Your program will generate code, compile it into a shared library (.so) and then load and execute it. (Some people will find that dirty. It's insecure also. But it's simple and it works.)
Here is an exemple for linux : plugin.h :
#ifndef PLUGIN_H__
#define PLUGIN_H__
#ifdef __cplusplus
extern "C" {
#endif
int process();
typedef int (*plugin_process_fn_ptr)();
#ifdef __cplusplus
}
#endif
#endif // PLUGIN_H__
Note that we must use extern C or else, C++ name mangling will make it difficult to import symbols.
plugin.cpp :
#include "plugin.h"
#include <iostream>
using namespace std;
int process()
{
int return_value = 0;
#include "plugin_content.inc.cpp"
return return_value;
}
Note that I use a hack here, the code will be included from another file, "plugin_content.inc.cpp". The code from user will be put inside.
a script to build the plugin, "build_plugin.sh" :
#! /bin/sh
g++ -c -Wall -fPIC plugin.cpp -o plugin.o
gcc -shared -o libplugin.so plugin.o
Now the calling program, main.cpp :
#include <iostream>
#include <fstream> // to open files
#include <dlfcn.h> // C lib to load dynamic libs
#include "plugin.h"
using namespace std;
// load the plugin and call the process() function fom it
static int process_via_plugin()
{
int return_value = -1;
void *lib_handle(NULL);
char *error(NULL);
char *plugin_lib = "./libplugin.so";
lib_handle = dlopen(plugin_lib, RTLD_LAZY);
if (!lib_handle)
{
cerr << "Error loading lib " << plugin_lib << " : " << dlerror() << endl;
exit(1);
}
char *plugin_fn = "process";
plugin_process_fn_ptr fn = (plugin_process_fn_ptr)dlsym(lib_handle, plugin_fn);
error = dlerror();
if (error)
{
cerr << "Error finding lib " << plugin_fn << " : " << error << endl;
exit(1);
}
// call the function loaded from lib
return_value = (*fn)();
dlclose(lib_handle);
lib_handle = NULL; // useless but for good habits ^^
return return_value;
}
// build or rebuild the plugin,
// we must call it when we change the plugin code code
static int build_plugin(string code)
{
{
char *plugin_code_file = "plugin_content.inc.cpp";
ofstream plugin_code(plugin_code_file, ios::out);
plugin_code << code << endl;
}
system("build_plugin.sh");
return 0;
}
// our program
int main(int argc, char *argv[])
{
cout << "Hello World !" << endl;
string code = ""
"cout << \"Hello from plugin !\" << endl;"
"";
// build a first version of the plugin and call it
build_plugin(code);
process_via_plugin();
// now we modify the code (use a GUI here)
code = ""
"cout << \"Hello from plugin, updated !\" << endl;"
"";
// rebuild the plugin and call it again
build_plugin(code);
process_via_plugin();
// do it again as much as you want.
return 0;
}
Now, build your program :
g++ -Wall -rdynamic -ldl main.cpp
and execute it :
a.out
and you get :
Hello World !
Hello from plugin !
Hello from plugin, updated !
The code I give you is very basic. For example, we should check if the compilation of the plugin is successful and report errors to the user. Now it's up to you to add more stuff.
Hey everyone. I need to write a POSIX program to search through an entire file system for a specified file starting at the top directory. I've got some code which isn't done at all, but when I run it, and check to see if a particular file is a directory, it's saying this file which is not at all a directory is a directory and is trying to move into it, causing an error. I'm not sure how I can tell it that this type of file isn't a directory.
Here's my code. I know it's not perfect and I could probably do some things differently in the way of getting the directory names and passing them into the function. Either way, I'm pretty sure I have to do this recursively.
The file in question is /dev/dri/card0 and I'm running this from a Debian virtual machine.
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <locale.h>
#include <langinfo.h>
#include <fcntl.h>
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
void SearchDirectory(string file_Name, string directory){
string new_Directory = directory;
DIR *dirp;
dirp = opendir(directory.c_str());
struct dirent *dptr;
struct stat statStruct;
while(dptr = readdir(dirp)){
stat(dptr->d_name, &statStruct);
if( S_ISDIR(statStruct.st_mode) ){
string check = dptr->d_name;
if ( check.compare(".") == 0 || check.compare("..") == 0 ){
continue;
}
else{
cout << dptr->d_name << " is is a directory" << endl;
new_Directory.append("/");
new_Directory.append(dptr->d_name);
SearchDirectory(file_Name, new_Directory);
}
}
else if( S_ISREG(statStruct.st_mode)){
string check = dptr->d_name;
if( check.compare(file_Name) == 0){
cout << "Found " << file_Name << " in " << directory << "/" << endl;
}
}
}
}
int main(int argc, char *argv[]){
if(argc < 2 || argc > 2){
cerr << "This program will find the specified file." << endl;
cerr << "Usage: mysearch <filename>" << endl;
return 1;
}
string file_Name = argv[1];
SearchDirectory(file_Name, "/");
return 0;
}
POSIX.2 requires a working "find" command.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <filename>", argv[0]);
}
execlp("find", "find", "/", "-name", argv[1], "-print", (char *)NULL);
exit(EXIT_FAILURE);
}
->d_name returns just the name of the file, not the path to the file. You need to stat (not yet constructed) new_Directory instead of dptr->d_name.
You also have a problem if a directory contains more than one subdirectories. Your construction of new_Directory is incorrect for each subdirectory after the first.
You never closedir your directory handle, so you run out of resources. You should also consider loading the entire directory into an array before recursing to avoid running out of handles.
void SearchDirectory(string directory, string target_File_Name){
DIR *dirp = opendir(directory.c_str());
if (!dirp) {
perror(("opendir " + directory).c_str());
return;
}
struct dirent *dptr;
while(dptr = readdir(dirp)){
string file_Name = dptr->d_name;
string file_Path = directory + "/" + file_Name;
struct stat statStruct;
stat(file_Path.c_str(), &statStruct);
if( S_ISDIR(statStruct.st_mode) ){
if ( file_Name.compare(".") == 0 || file_Name.compare("..") == 0 ){
continue;
}
SearchDirectory(file_Path, target_File_Name);
}
else if( S_ISREG(statStruct.st_mode)){
if( file_Name.compare(target_File_Name) == 0){
cout << file_Path << endl;
}
}
}
closedir(dirp);
}
Update: Added second problem.
Update: Added third problem.
Update: Added code.
Not for the benefit of the OP, who writes "The point is to come up with a way to do it myself," but rather for the benefit of posterity, here is a way to use Boost.Filesystem:
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
// sample usage: find_file("/home", ".profile");
void find_file( const fs::path& dirPath, const std::string& fileName) {
fs::recursive_directory_iterator end;
for(fs::recursive_directory_iterator it(dirPath); it != end; ++it) {
if(it->leaf() == fileName)
std::cout << it->path() << "\n";
if(fs::is_symlink(it->symlink_status()))
it.no_push();
}
}
Use fork, execv and the Unix implemented /usr/bin/find process and redirect its output for your result area?
I'm not sure if it's POSIX or not but the nftw library function is widely available on UNIX (HP-UX, AIX, Linux).
Your problem is "search a tree for a match"
BFS and DFS are the canonical basic algorithms. Give them a start node and go.
You will get into trouble if you follow symlinks; so test for them and don't follow them.
You should be able to map each point in the *FS algorithms to a directory operation.
Since C++ is an option, why not use something like Boost.Filesystem? The Boost.Filesystem two-minute tutorial gives an example of how to implement your search using directory iterators.
I've got a pretty basic console program here, to determine if a folder or file exists or not using stat:
#include <iostream>
#include <sys/stat.h>
using namespace std;
int main() {
char path[] = "myfolder/";
struct stat status;
if(stat(path,&status)==0) { cout << "Folder found." << endl; }
else { cout << "Can't find folder." << endl; } //Doesn't exist
cin.get();
return 0;
}
I have also tried the access version:
#include <iostream>
#include <io.h>
using namespace std;
int main() {
char path[] = "myfolder/";
if(access(path,0)==0) { cout << "Folder found." << endl; }
else { cout << "Can't find folder." << endl; } //Doesn't exist
cin.get();
return 0;
}
Neither of them find my folder (which is right there in the same directory as the program). These worked on my last compiler (the default one with DevCpp). I switched to CodeBlocks and am compiling with Gnu GCC now, if that helps. I'm sure it's a quick fix - can someone help out?
(Obviously I'm a noob at this so if you need any other information I've left out please let me know).
UPDATE
The problem was with the base directory. The updated, working program is as follows:
#include <iostream>
#include <sys/stat.h>
using namespace std;
int main() {
cout << "Current directory: " << system("cd") << endl;
char path[] = "./bin/Release/myfolder";
struct stat status;
if(stat(path,&status)==0) { cout << "Directory found." << endl; }
else { cout << "Can't find directory." << endl; } //Doesn't exist
cin.get();
return 0;
}
ANOTHER UPDATE
Turns out that a trailing backslash on the path is big trouble.
Right before your stat call, insert the code:
system("pwd"); // for UNIXy systems
system("cd"); // for Windowsy systems
(or equivalent) to check your current directory. I think you'll find it's not what you think.
Alternatively, run the executable from the command line where you know what directory you're in. IDEs will frequently run your executable from a directory you may not expect.
Or, use the full path name so that it doesn't matter which directory you're in.
For what it's worth, your first code segment works perfectly (gcc under Ubuntu 10):
pax$ ls my*
ls: cannot access my*: No such file or directory
pax$ ./qq
Cannot find folder.
pax$ mkdir myfolder
pax$ ll -d my*
drwxr-xr-x 2 pax pax 4096 2010-12-14 09:33 myfolder/
pax$ ./qq
Folder found.
Are you sure that the current directory of your running program is what you expect it to be? Try changing path to an absolute pathname to see if that helps.
Check your PWD when you running your program. This problem is not caused by compiler. You DevCpp may set a working directory for your program automatically.
You can find out why stat() failed (which is a C function, not C++, by the way), by checking errno:
#include <cerrno>
...
if (stat(path,&status) != 0)
{
std::cout << "stat() failed:" << std::strerror(errno) << endl;
}