Skip to content

Commit

Permalink
Work on get_self_exe_path for windows
Browse files Browse the repository at this point in the history
  • Loading branch information
tkittel committed Nov 4, 2024
1 parent e680d7f commit 5ce691e
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 8 deletions.
13 changes: 8 additions & 5 deletions ncrystal_core/include/NCrystal/internal/NCFileUtils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ namespace NCRYSTAL_NAMESPACE {
std::string basename(const std::string& path);
//The extension of the filename (if any):
std::string getfileext(const std::string& path);

//Normalise by analysing and reencoding:
std::string normalise(const std::string& path);

Expand All @@ -57,10 +56,10 @@ namespace NCRYSTAL_NAMESPACE {
//Windows, unless path already contains forward slashes):
std::string path_join(const std::string&, const std::string&);

// tryRealPath: Wrap posix "realpath" function in tryRealPath(..). On any
// sort of error (including the error of not being unix), returns an empty
// string. Hopefully this function should work for 99.9% of usecases on
// linux/osx/bsd, and fail gracefully in the rest.
// tryRealPath: Wrap posix "realpath" or windows GetFullPathNameW function. On
// any sort of error (including being un an unsupported platform), it returns
// an empty string. Hopefully this function should work most of the time, and
// fail gracefully in the rest.
std::string tryRealPath( const std::string& path );

//Read entire file into a string while protecting against someone mistakenly
Expand All @@ -70,6 +69,10 @@ namespace NCRYSTAL_NAMESPACE {
//DataLoadError.
Optional<std::string> readEntireFileToString( const std::string& path );

//Use in applications (from main) needing to determine their own
//paths. Returns empty string if unable.
std::string determine_exe_self_path( int argc, char** argv );

}

#endif
101 changes: 98 additions & 3 deletions ncrystal_core/src/NCFileUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@

namespace NC = NCrystal;

#if defined(__unix__) || (defined (__APPLE__) && defined (__MACH__))
#ifdef NCRYSTAL_USE_WINDOWS_FILEUTILS
std::string NC::tryRealPath( const std::string& path )
{
return WinFileUtils::get_absolute_path(path);
}
#elif defined(__unix__) || (defined (__APPLE__) && defined (__MACH__))
#include <stdlib.h>
#include <limits.h>
std::string NC::tryRealPath( const std::string& fn ) {
Expand All @@ -41,8 +46,11 @@ std::string NC::tryRealPath( const std::string& fn ) {
return std::string(res);
}
#else
//Windows or whatever... just give up. [FIXME: To NCFileUtilsWin.cc + add NCFileUtilsUnix.cc]
std::string NC::tryRealPath( const std::string& ) { return {}; }
//Give up:
std::string NC::tryRealPath( const std::string& path )
{
return {};
}
#endif


Expand Down Expand Up @@ -354,3 +362,90 @@ std::string NC::ncgetcwd() {
NCRYSTAL_THROW(CalcError,"Could not determine current working directory");
}
#endif

#if defined(__unix__) && !defined(__APPLE__)
namespace NCRYSTAL_NAMESPACE {
namespace {
std::string try_get_self_exe_path_from_proc(const char * procpath)
{
//Read a link like /proc/self/exe
char buf[65536+1];//PATH_MAX is unreliable so we use huge buffer
auto len = ::readlink(procpath, buf, sizeof(buf)-1 );
if ( len > 0 && std::size_t(len+1) < std::size_t(sizeof(buf)) ) {
buf[len] = '\0';//readlink does not add terminating null char
return { buf };
} else {
return {};//error
}
}
}
}
#endif

#ifdef __APPLE__
#include <mach-o/dyld.h>
namespace NCRYSTAL_NAMESPACE {
namespace {
std::string get_self_exe_path_apple()
{
//First find required buffer size:
uint32_t bufsize = 0;
char fakebuf[4];
auto status = _NSGetExecutablePath(&fakebuf, &bufsize);
if ( status != -1 )
return {};//unexpected result, buffer should NOT be large enough
std::string res;
res.resize( bufsize, 0 );
status = _NSGetExecutablePath(&fakebuf, &bufsize);
if ( status == 0 && res2.find('\0') > res2.size() )
return res;
else
return {};//error
}
}
}
#endif

std::string NC::determine_exe_self_path( int argc, char** argv )
{
//First try some platform specific methods not relying on argv0:
#ifdef NCRYSTAL_USE_WINDOWS_FILEUTILS
{
auto res = WinFileUtils::get_self_exe_path_windows();
if (!res.empty())
return res;
}
#endif
#ifdef __APPLE__
{
auto res = get_self_exe_path_apple();
if (!res.empty())
return res;
}
#endif
#if defined(__unix__) && !defined(__APPLE__)
{
//Should always work on linux:
auto res = try_get_self_exe_path_from_proc("/proc/self/exe");
if (!res.empty())
return res;
}
{
//Might occasionally work on FreeBSD etc.
auto res = try_get_self_exe_path_from_proc("/proc/curproc/file");
if (!res.empty())
return res;
}
#endif
//Fall back to argv0:
if ( !(argc > 0) )
return {};//not available (rare, but allowed by C standard);
std::string argv0( argv[0] );
if ( NC::path_is_absolute( argv0 ) )
return argv0;
std::string guess = NC::path_join( ncgetcwd(), argv0 );
if ( NC::file_exists(guess) )
return guess;
//Give up:
return {};
}
42 changes: 42 additions & 0 deletions ncrystal_core/src/NCFileUtilsWin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,39 @@ namespace NCRYSTAL_NAMESPACE {
return winimpl_wstr2str( std::move(wpath) );
}

std::wstring path2wpath( const std::string& path )
{
if ( contains( path, '/' ) ) {
std::string pfix = path;
for ( auto& c : pfix )
if ( c == '/' )
c = '\\';
return winimpl_str2wstr( pfix );
} else {
return winimpl_str2wstr( path );
}
}

std::string get_absolute_path(std::string path)
{
if ( path.empty() )
return {};
auto wpath = path2wpath( path );
auto len_with_null_term = GetFullPathNameW( &wpath[0],
0, nullptr, nullptr );
if ( len_with_null_term <= 1 )
return {};
std::string wres;
wres.resize(len-1,0);
auto len = GetFullPathNameW( &wpath[0],
len_with_null_term, &wres[0],
nullptr );
if ( len != wres.size() )
return {};//failed

return winimpl_wstr2str( wres );
}

VectS ncglob_impl( const std::string& pattern_utf8 )
{
std::wstring wpattern;
Expand Down Expand Up @@ -214,6 +247,15 @@ namespace NCRYSTAL_NAMESPACE {
return result;
}

std::string get_self_exe_path_windows()
{
std::wstring wpath;
wpath.resize(MAX_PATH);
auto nsize = GetModuleFileNameW(nullptr, &wpath[0], MAX_PATH);
nc_assert_always(nsize<=wpath.size());
wpath.resize(nsize);
return winimpl_wstr2str( std::move(wpath) );
}
}
}
#endif
2 changes: 2 additions & 0 deletions ncrystal_core/src/NCFileUtilsWin.hh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace NCRYSTAL_NAMESPACE {
std::ios_base::openmode mode = std::ios_base::in);
VectS ncglob_impl(const std::string&);
std::string get_current_working_dir();
std::string get_self_exe_path_windows();
std::string get_absolute_path(std::string path)
}
}

Expand Down
49 changes: 49 additions & 0 deletions tests/src/app_selfpath/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
////////////////////////////////////////////////////////////////////////////////
// //
// This file is part of NCrystal (see https://mctools.github.io/ncrystal/) //
// //
// Copyright 2015-2024 NCrystal developers //
// //
// Licensed under the Apache License, Version 2.0 (the "License"); //
// you may not use this file except in compliance with the License. //
// You may obtain a copy of the License at //
// //
// http://www.apache.org/licenses/LICENSE-2.0 //
// //
// Unless required by applicable law or agreed to in writing, software //
// distributed under the License is distributed on an "AS IS" BASIS, //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
// See the License for the specific language governing permissions and //
// limitations under the License. //
// //
////////////////////////////////////////////////////////////////////////////////

#include "NCrystal/internal/NCFileUtils.hh"
#include "NCrystal/internal/NCMath.hh"//isOneOf
#include <iostream>
namespace NC=NCrystal;

int main( int argc, char ** argv )
{
auto self_path = NC::determine_exe_self_path( argc, argv );
std::cout<<"determine_exe_self_path: "<<self_path<<std::endl;
if (!NC::file_exists(self_path)) {
std::cout<<"Error: did not exist!"<<std::endl;
return 1;
}

if (!NC::path_is_absolute(self_path)) {
std::cout<<"Error: was not absolute!"<<std::endl;
return 1;
}

nc_assert_always( NC::isOneOf( NC::basename(self_path),
"selfpath",
"selfpath.exe") );
nc_assert_always( NC::basename(NC::dirname(self_path)) == "bin" );
//tryRealPath
//std::string normalise(const std::string& path);
std::cout<<"All looks OK"<<std::endl;
return 0;
}

0 comments on commit 5ce691e

Please sign in to comment.