How to tell where the application is being ran in C++ - c++

I'm using wget along side my app to download a jar and run it. Problem is with my current setup wget.exe would have to be kept in a folder in the app data and that really isn't smart i.e. how would the file get there to begin with?
So how would one find the directory the app is being run in no matter where it is being ran from?

for windows:
std::string calculateRunPath()
{
const unsigned int size = 500;
char buf[size] = {0};
HMODULE hModule = GetModuleHandle(NULL);
GetModuleFileName(hModule,buf, sizeof(buf));
std::string path(buf);
size_t pos = path.find_last_of('\\');
return path.substr(0, pos);
}
for Linux:
std::string calculateRunPath()
{
const unsigned int size = 500;
char path[size + 1] = {0};
size_t len = readlink("/proc/self/exe", path, size);
path[len] = 0;
char* p = strrchr(path, '/');
if(p)
*(p + 1) = 0;
else
path[0] = 0;
return std::string(path);
}

Some boost filesystem goodness should work too, something like...
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <iostream>
int main()
{
std::cout << boost::filesystem::current_path().string() << std::endl;
return 0;
}

You have to read the PWD environment variable

Related

Get temp path with file name

I want to get path to file like this > %ENV%/%FILE_NAME%.docx
But c++ doesn't make sense at all and nothing works..
I would use std::string but it's not compatible so I tried multiple ways of converting it to char[] or char* but none of them works and I'm also pretty sure this is unsafe..
My code so far (I know it's the worst code ever..)
char* appendCharToCharArray(char* array, char a)
{
size_t len = strlen(array);
char* ret = new char[len + 2];
strcpy(ret, array);
ret[len] = a;
ret[len + 1] = '\0';
return ret;
}
const char* getBaseName(std::string path)
{
std::string base_filename = path.substr(path.find_last_of("/\\") + 1);
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
return file_without_extension.c_str();
}
int main()
{
char szExeFileName[MAX_PATH];
GetModuleFileName(NULL, szExeFileName, MAX_PATH);
const char* file_name = getBaseName(std::string(szExeFileName));
char* new_file = getenv("temp");
new_file = appendCharToCharArray(new_file, '\\');
for (int i=0;i<sizeof(file_name)/sizeof(file_name[0]);i++)
{
new_file = appendCharToCharArray(new_file, file_name[i]);
}
new_file = appendCharToCharArray(new_file, '.');
new_file = appendCharToCharArray(new_file, 'd');
new_file = appendCharToCharArray(new_file, 'o');
new_file = appendCharToCharArray(new_file, 'c');
new_file = appendCharToCharArray(new_file, 'x');
std::cout << new_file << std::endl;
}
Using appendCharToCharArray() is just horribly inefficient in general, and also you are leaking lots of memory with the way you are using it. Just use std::string instead. And yes, you can use std::string in this code, it is perfectly "compatible" if you use it correctly.
getBaseName() is returning a char* pointer to the data of a local std::string variable that goes out of scope when the function exits, thus a dangling pointer is returned. Again, use std::string instead.
And, you should use the Win32 GetTempPath/2() function instead of getenv("temp").
Try something more like this:
#include <iostream>
#include <string>
std::string getBaseName(const std::string &path)
{
std::string base_filename = path.substr(path.find_last_of("/\\") + 1);
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
return file_without_extension;
}
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
std::string new_file = std::string(szTempFolder) + getBaseName(szExeFileName) + ".docx";
std::cout << new_file << std::endl;
}
Online Demo
That being said, the Win32 Shell API has functions for manipulating path strings, eg:
#include <iostream>
#include <string>
#include <windows.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
char new_file[MAX_PATH] = {};
PathCombineA(new_file, szTempFolder, PathFindFileNameA(szExeFileName));
PathRenameExtensionA(new_file, ".docx");
std::cout << new_file << std::endl;
}
Or, if you are using C++17 or later, consider using std::filesystem::path instead, eg:
#include <iostream>
#include <filesystem>
#include <windows.h>
namespace fs = std::filesystem;
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
fs::path new_file = fs::path(szTempFolder) / fs::path(szExeFileName).stem();
new_file += ".docx";
// alternatively:
// fs::path new_file = fs::path(szTempFolder) / fs::path(szExeFileName).filename();
// new_file.replace_extension(".docx");
std::cout << new_file << std::endl;
}
Online Demo

GetEnvironmentVariableW and GetEnvironmentStringsW returns different values

I'm developing an UTF-8 API which should transparently work independent of the OS.
For that I'm replacing getenv by GetEnvironmentVariableW and the char** env passed to main by GetEnvironmentStringsW.
To make sure everything is correct I iterate over the env array and check each value against GetEnvironmentVariableW to make sure they match.
This works everywhere but with MinGW/MSYS where it fails for the env variable temp where getenv = C:\msys64\tmp and GetEnvironmentStringsW returns C:\Users\appveyor\AppData\Local\Temp\1
I verified the value using c:\msys64\usr\bin\env MSYSTEM=MINGW32 c:\msys64\usr\bin\bash -l -c "echo $temp" (similar how the build/test is run) and there it returns C:\Users\appveyor\AppData\Local\Temp\1.
So it seems GetEnvironmentVariableW returns a different/wrong value.
How is this possible? Can I avoid this?
Code of the test is:
for(char** e = env; *e != 0; e++)
{
const char* key_begin = *e;
const char* key_end = strchr(key_begin, '=');
std::string key = std::string(key_begin, key_end);
std::string value = key_end + 1;
const char* env_value = boost::nowide::getenv(key.c_str());
TEST_EQ(env_value, value);
}
where the getenv function is (reduced without error checking etc):
char* getenv(const char* key)
{
static const size_t buf_size = 64;
wchar_t buf[buf_size];
GetEnvironmentVariableW(widen(key), buf, buf_size);
return narrow(buf);
}
Edit: I was able to reproduce this with the following minimal code:
#include <iostream>
#include <windows.h>
#include <string>
int main()
{
const size_t buf_size = 4000;
char buf[buf_size];
GetEnvironmentVariable("temp", buf, buf_size);
std::cout << buf << std::endl;
return 0;
}
When compiled (e.g. in MSVC) and run with temp=Foobar ./a.out in e.g. Git bash on Windows it prints C:\Users\alex\AppData\Local\Temp not Foobar. In GetEnvironmentStrings the value is correct

how to make 10 copies of initial file, if first file is as-1.txt second should be as-2.txt and so on

Loop isn't making 10 copies and i have no idea how to change file names
#include "iostream"
#include "fstream"
#include "windows.h"
using namespace std;
void main()
{
char str[200];
ifstream myfile("as-1.txt");
if (!myfile)
{
cerr << "file not opening";
exit(1);
}
for (int i = 0; i < 10; i++)
{
ofstream myfile2("as-2.txt");
while (!myfile.eof())
{
myfile.getline(str, 200);
myfile2 << str << endl;
}
}
system("pause");
}
Solution using plain C API from <cstdio>. Easily customizable.
const char* file_name_format = "as-%d.txt"; //Change that if you need different name pattern
const char* original_file_name = "as-1.txt"; //Original file
const size_t max_file_name = 255;
FILE* original_file = fopen(original_file_name, "r+");
if(!original_file)
//file not found, handle error
fseek(original_file, 0, SEEK_END); //(*)
long file_size = ftell(original_file);
fseek(original_file, 0, SEEK_SET);
char* original_content = (char*)malloc(file_size);
fread(original_content, file_size, 1, original_file);
fclose(original_file);
size_t copies_num = 10;
size_t first_copy_number = 2;
char file_name[max_file_name];
for(size_t n = first_copy_number; n < first_copy_number + copies_num; ++n)
{
snprintf(file_name, max_file_name, file_name_format, n);
FILE* file = fopen(file_name, "w");
fwrite(original_content, file_size, 1, file);
fclose(file);
}
free(original_content);
(*) As noted on this page, SEEK_END may not necessarily be supported (i.e. it is not a portable solution). However most POSIX-compliant systems (including the most popular Linux distros), Windows family and OSX support this without any problems.
Oh, and one more thing. This line
while (!myfile.eof())
is not quite correct. Read this question - it explains why you shouldn't write such code.
int main()
{
const int copies_of_file = 10;
for (int i = 1; i <= copies_of_file; ++i)
{
std::ostringstream name;
name << "filename as-" << i << ".txt";
std::ofstream ofile(name.str().c_str());
ofile.close();
}
return 0;
}
That will make 10 copies of a blank .txt file named "filename as-1.txt" "filename as-2.txt" etc.
Note also the use of int main: main always has a return of int, never void

How to get file suffix in c++?

I want to get the suffix(.txt,.png etc.) of a file that I know that exists in some folder.
I know that the file name(prefix) is unique in this folder.
The language is c++.
thanks
Assuming that "suffix" is the filename extension, you can do this:
char * getfilextension(char * fullfilename)
{
int size, index;
size = index = 0;
while(fullfilename[size] != '\0') {
if(fullfilename[size] == '.') {
index = size;
}
size ++;
}
if(size && index) {
return fullfilename + index;
}
return NULL;
}
It's C code, but I believe that can easily ported to C++(maybe no changes).
getfilextension("foo.png"); /* output -> .png */
I hope this help you.
UPDATE:
You will need scan all files of directory and compare each file without extension if is equal to your target.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
//.....
char * substr(char * string, int start, int end)
{
char * p = &string[start];
char * buf = malloc(strlen(p) + 1);
char * ptr = buf;
if(!buf) return NULL;
while(*p != '\0' && start < end) {
*ptr ++ = *p++;
start ++;
}
*ptr++ = '\0';
return buf;
}
char * getfilenamewithoutextension(char * fullfilename)
{
int i, size;
i = size = 0;
while(fullfilename[i] != '\0') {
if(fullfilename[i] == '.') {
size = i;
}
i ++;
}
return substr(fullfilename, 0, size);
}
char * getfilextension(char * fullfilename)
{
int size, index;
size = index = 0;
while(size ++, fullfilename[size]) {
if(fullfilename[size] == '.') {
index = size;
}
}
if(size && index) {
return fullfilename + index;
}
return NULL;
}
char*FILE_NAME;
int filefilter(const struct dirent * d)
{
return strcmp(getfilenamewithoutextension((char*)d->d_name), FILE_NAME) == 0;
}
and then:
void foo(char * path, char * target) {
FILE_NAME = target;
struct dirent ** namelist;
size_t dirscount;
dirscount = scandir(path, &namelist, filefilter, alphasort);
if(dirscount > 0) {
int c;
for(c = 0; c < dirscount; c++) {
printf("Found %s filename,the extension is %s.\n", target, getfilextension(namelist[c]->d_name));
free(namelist[c]);
}
free(namelist);
} else {
printf("No files found on %s\n", path);
}
}
and main code:
int main(int argc, char * argv[])
{
foo(".", "a"); /* The .(dot) scan the current path */
}
For a directory with this files:
a.c a.c~ a.out
a.o makefile test.cs
The output is:
Found a filename,the extension is .c.
Found a filename,the extension is .c~.
Found a filename,the extension is .o.
Found a filename,the extension is .out.
Note: the scandir() function is part of GNU extensions/GNU library,if you don't have this function available on your compiler,tell me that I will write an alias for that or use this implementation(don't forget to read the license).
If you're using Windows, use PathFindExtension.
There's no standard functionality to list directory contents in c++. So if you know allowed extensions in your app you can iterate through and find if file exists.
Other options are use OS specific API or use something like Boost. You can also use "ls | grep *filename" or "dir" command dump and parse the output.

Get parent directory from file in C++

I need to get parent directory from file in C++:
For example:
Input:
D:\Devs\Test\sprite.png
Output:
D:\Devs\Test\ [or D:\Devs\Test]
I can do this with a function:
char *str = "D:\\Devs\\Test\\sprite.png";
for(int i = strlen(str) - 1; i>0; --i)
{
if( str[i] == '\\' )
{
str[i] = '\0';
break;
}
}
But, I just want to know there is exist a built-in function.
I use VC++ 2003.
Thanks in advance.
If you're using std::string instead of a C-style char array, you can use string::find_last_of and string::substr in the following manner:
std::string str = "D:\\Devs\\Test\\sprite.png";
str = str.substr(0, str.find_last_of("/\\"));
Now, with C++17 is possible to use std::filesystem::path::parent_path:
#include <filesystem>
namespace fs = std::filesystem;
int main() {
fs::path p = "D:\\Devs\\Test\\sprite.png";
std::cout << "parent of " << p << " is " << p.parent_path() << std::endl;
// parent of "D:\\Devs\\Test\\sprite.png" is "D:\\Devs\\Test"
std::string as_string = p.parent_path().string();
return 0;
}
Heavy duty and cross platform way would be to use boost::filesystem::parent_path(). But obviously this adds overhead you may not desire.
Alternatively you could make use of cstring's strrchr function something like this:
include <cstring>
char * lastSlash = strrchr( str, '\\');
if ( *lastSlash != '\n') *(lastSlash +1) = '\n';
Editing a const string is undefined behavior, so declare something like below:
char str[] = "D:\\Devs\\Test\\sprite.png";
You can use below 1 liner to get your desired result:
*(strrchr(str, '\\') + 1) = 0; // put extra NULL check before if path can have 0 '\' also
On POSIX-compliant systems (*nix) there is a commonly available function for this dirname(3). On windows there is _splitpath.
The _splitpath function breaks a path
into its four components.
void _splitpath(
const char *path,
char *drive,
char *dir,
char *fname,
char *ext
);
So the result (it's what I think you are looking for) would be in dir.
Here's an example:
int main()
{
char *path = "c:\\that\\rainy\\day";
char dir[256];
char drive[8];
errno_t rc;
rc = _splitpath_s(
path, /* the path */
drive, /* drive */
8, /* drive buffer size */
dir, /* dir buffer */
256, /* dir buffer size */
NULL, /* filename */
0, /* filename size */
NULL, /* extension */
0 /* extension size */
);
if (rc != 0) {
cerr << GetLastError();
exit (EXIT_FAILURE);
}
cout << drive << dir << endl;
return EXIT_SUCCESS;
}
On Windows platforms, you can use
PathRemoveFileSpec or PathCchRemoveFileSpec
to achieve this.
However for portability I'd go with the other approaches that are suggested here.
You can use dirname to get the parent directory
Check this link for more info
Raghu