Question
How do you extract the base path from pathname in C?
Are there any functions built into the C language or the C-Runtime Library to extract the base path from a pathname in C?
I'm asking basically the opposite of this question.
NOTE: I prefer a cross-platform solution, but I'm working in Windows so if there is a Windows API call that does this, I'd still like to know.
Examples
Input | Output
---------------------------------
C:\path\to\file -> C:\path\to\
C:\path\to\file.c -> C:\path\to\
C:\file -> C:\
.\file -> .\
.\ -> .\
\ -> \
References
Extract file name from full path in C using MSVS2005
On Windows there is _splitpath.
Example
#include <Windows.h>
#include <tchar.h>
// Use your own error codes here
#define SUCCESS 0L
#define FAILURE_NULL_ARGUMENT 1L
#define FAILURE_API_CALL 2L
#define FAILURE_INSUFFICIENT_BUFFER 3L
DWORD GetBasePathFromPathName( LPCTSTR szPathName,
LPTSTR szBasePath,
DWORD dwBasePathSize )
{
TCHAR szDrive[_MAX_DRIVE] = { 0 };
TCHAR szDir[_MAX_DIR] = { 0 };
TCHAR szFname[_MAX_FNAME] = { 0 };
TCHAR szExt[_MAX_EXT] = { 0 };
size_t PathLength;
DWORD dwReturnCode;
// Parameter validation
if( szPathName == NULL || szBasePath == NULL )
{
return FAILURE_NULL_ARGUMENT;
}
// Split the path into it's components
dwReturnCode = _tsplitpath_s( szPathName, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFname, _MAX_FNAME, szExt, _MAX_EXT );
if( dwReturnCode != 0 )
{
_ftprintf( stderr, TEXT("Error splitting path. _tsplitpath_s returned %d.\n"), dwReturnCode );
return FAILURE_API_CALL;
}
// Check that the provided buffer is large enough to store the results and a terminal null character
PathLength = _tcslen( szDrive ) + _tcslen( szDir );
if( ( PathLength + sizeof( TCHAR ) ) > dwBasePathSize )
{
_ftprintf( stderr, TEXT("Insufficient buffer. Required %d. Provided: %d\n"), PathLength, dwBasePathSize );
return FAILURE_INSUFFICIENT_BUFFER;
}
// Copy the szDrive and szDir into the provide buffer to form the basepath
if( ( dwReturnCode = _tcscpy_s( szBasePath, dwBasePathSize, szDrive ) ) != 0 )
{
_ftprintf( stderr, TEXT("Error copying string. _tcscpy_s returned %d\n"), dwReturnCode );
return FAILURE_API_CALL;
}
if( ( dwReturnCode = _tcscat_s( szBasePath, dwBasePathSize, szDir ) ) != 0 )
{
_ftprintf( stderr, TEXT("Error copying string. _tcscat_s returned %d\n"), dwReturnCode );
return FAILURE_API_CALL;
}
return SUCCESS;
}
Are there any functions built into the C language or C-Runtime to extract the base path from a pathname in C?
No there are not. Rules for path names are platform specific and so the standard does not cover them.
In Windows you can use the API call "PathRemoveFileSpec" http://msdn.microsoft.com/en-us/library/bb773748(v=vs.85).aspx
Cross platform solutions will not really be possible due to variations in file systems bewtween different OS's.
Just loop from back to forward until you meet the first \
WinAPI (shlwapi) PathRemoveFileSpec should do all of that with the exception of .\file which would come back as .
There is no standard C99 function for doing this. POSIX has dirname(), but that won't help you much on Windows. It shouldn't be too hard for you to implement your own function, though; just search through the string, looking for the last occurrence of the directory separator, and discard anything after it.
I think the best solution on Windows is to use _splitpath as was suggested, to use something like basename on Linux (more on that here).
That said, since someone has already suggested implementing my own (and since I had already done it while I was waiting for an answer), here is what I came up with. It is not cross-platform and it does not check for /valid/ paths or expand short or relative path names.
// Retrieves the pathpath from a pathname.
//
// Returns: SUCCESS if the basepath is present and successfully copied to the p_base_path buffer
// FAILURE_NULL_ARGUMENT if any arguments are NULL
// FAILURE_INVALID_ARGUMENTS if either buffer size is less than 1
// FAILURE_BUFFER_TOO_SMALL if the p_basepath buffer is too small
// FAILURE_INVALID_PATH if the p_pathname doesn't have a path (e.g. C:, calc.exe, ?qwa)
// FAILURE_API_CALL if there is an error from the underlying API calls
int get_base_path_from_pathname( const char* const p_pathname,
size_t pathname_size,
char* const p_basepath,
size_t basepath_size );
int get_base_path_from_pathname( const char* const p_pathname,
size_t pathname_size,
char* const p_basepath,
size_t basepath_size )
{
char* p_end_of_path;
size_t path_length;
int return_code;
// Parameter Validation
if( p_pathname == NULL || p_basepath == NULL ) { return FAILURE_NULL_ARGUMENT; }
if( pathname_size < 1 || basepath_size < 1 ) { return FAILURE_INVALID_ARGUMENTS; }
// Returns a pointer to the last occurrence of \ in p_pathname or NULL if it is not found
p_end_of_path = strrchr( p_pathname, '\\' );
if( p_end_of_path == NULL )
{
// There is no path part
return FAILURE_INVALID_PATH;
}
else
{
path_length = (size_t)( p_end_of_path - p_pathname + 1 );
// Do some sanity checks on the length
if( path_length < 1 ) { return FAILURE_INVALID_PATH; }
if( ( path_length + 1 ) > basepath_size ) { return FAILURE_BUFFER_TOO_SMALL; }
// Copy the base path into the out variable
if( strncpy( p_basepath, p_pathname, path_length ) != 0 ) { return FAILURE_API_CALL; }
p_basepath[path_length] = '\0';
}
return SUCCESS;
}
Before str is the full path and file name, after str is just the path:
char dir_ch = '\\'; // set dir_ch according to platform
char str[] = "C:\\path\\to\\file.c";
char *pch = &str[strlen(str)-1];
while(*pch != dir_ch) pch--;
pch++;
*pch = '\0';
Related
Question
How do you extract the base path from pathname in C?
Are there any functions built into the C language or the C-Runtime Library to extract the base path from a pathname in C?
I'm asking basically the opposite of this question.
NOTE: I prefer a cross-platform solution, but I'm working in Windows so if there is a Windows API call that does this, I'd still like to know.
Examples
Input | Output
---------------------------------
C:\path\to\file -> C:\path\to\
C:\path\to\file.c -> C:\path\to\
C:\file -> C:\
.\file -> .\
.\ -> .\
\ -> \
References
Extract file name from full path in C using MSVS2005
On Windows there is _splitpath.
Example
#include <Windows.h>
#include <tchar.h>
// Use your own error codes here
#define SUCCESS 0L
#define FAILURE_NULL_ARGUMENT 1L
#define FAILURE_API_CALL 2L
#define FAILURE_INSUFFICIENT_BUFFER 3L
DWORD GetBasePathFromPathName( LPCTSTR szPathName,
LPTSTR szBasePath,
DWORD dwBasePathSize )
{
TCHAR szDrive[_MAX_DRIVE] = { 0 };
TCHAR szDir[_MAX_DIR] = { 0 };
TCHAR szFname[_MAX_FNAME] = { 0 };
TCHAR szExt[_MAX_EXT] = { 0 };
size_t PathLength;
DWORD dwReturnCode;
// Parameter validation
if( szPathName == NULL || szBasePath == NULL )
{
return FAILURE_NULL_ARGUMENT;
}
// Split the path into it's components
dwReturnCode = _tsplitpath_s( szPathName, szDrive, _MAX_DRIVE, szDir, _MAX_DIR, szFname, _MAX_FNAME, szExt, _MAX_EXT );
if( dwReturnCode != 0 )
{
_ftprintf( stderr, TEXT("Error splitting path. _tsplitpath_s returned %d.\n"), dwReturnCode );
return FAILURE_API_CALL;
}
// Check that the provided buffer is large enough to store the results and a terminal null character
PathLength = _tcslen( szDrive ) + _tcslen( szDir );
if( ( PathLength + sizeof( TCHAR ) ) > dwBasePathSize )
{
_ftprintf( stderr, TEXT("Insufficient buffer. Required %d. Provided: %d\n"), PathLength, dwBasePathSize );
return FAILURE_INSUFFICIENT_BUFFER;
}
// Copy the szDrive and szDir into the provide buffer to form the basepath
if( ( dwReturnCode = _tcscpy_s( szBasePath, dwBasePathSize, szDrive ) ) != 0 )
{
_ftprintf( stderr, TEXT("Error copying string. _tcscpy_s returned %d\n"), dwReturnCode );
return FAILURE_API_CALL;
}
if( ( dwReturnCode = _tcscat_s( szBasePath, dwBasePathSize, szDir ) ) != 0 )
{
_ftprintf( stderr, TEXT("Error copying string. _tcscat_s returned %d\n"), dwReturnCode );
return FAILURE_API_CALL;
}
return SUCCESS;
}
Are there any functions built into the C language or C-Runtime to extract the base path from a pathname in C?
No there are not. Rules for path names are platform specific and so the standard does not cover them.
In Windows you can use the API call "PathRemoveFileSpec" http://msdn.microsoft.com/en-us/library/bb773748(v=vs.85).aspx
Cross platform solutions will not really be possible due to variations in file systems bewtween different OS's.
Just loop from back to forward until you meet the first \
WinAPI (shlwapi) PathRemoveFileSpec should do all of that with the exception of .\file which would come back as .
There is no standard C99 function for doing this. POSIX has dirname(), but that won't help you much on Windows. It shouldn't be too hard for you to implement your own function, though; just search through the string, looking for the last occurrence of the directory separator, and discard anything after it.
I think the best solution on Windows is to use _splitpath as was suggested, to use something like basename on Linux (more on that here).
That said, since someone has already suggested implementing my own (and since I had already done it while I was waiting for an answer), here is what I came up with. It is not cross-platform and it does not check for /valid/ paths or expand short or relative path names.
// Retrieves the pathpath from a pathname.
//
// Returns: SUCCESS if the basepath is present and successfully copied to the p_base_path buffer
// FAILURE_NULL_ARGUMENT if any arguments are NULL
// FAILURE_INVALID_ARGUMENTS if either buffer size is less than 1
// FAILURE_BUFFER_TOO_SMALL if the p_basepath buffer is too small
// FAILURE_INVALID_PATH if the p_pathname doesn't have a path (e.g. C:, calc.exe, ?qwa)
// FAILURE_API_CALL if there is an error from the underlying API calls
int get_base_path_from_pathname( const char* const p_pathname,
size_t pathname_size,
char* const p_basepath,
size_t basepath_size );
int get_base_path_from_pathname( const char* const p_pathname,
size_t pathname_size,
char* const p_basepath,
size_t basepath_size )
{
char* p_end_of_path;
size_t path_length;
int return_code;
// Parameter Validation
if( p_pathname == NULL || p_basepath == NULL ) { return FAILURE_NULL_ARGUMENT; }
if( pathname_size < 1 || basepath_size < 1 ) { return FAILURE_INVALID_ARGUMENTS; }
// Returns a pointer to the last occurrence of \ in p_pathname or NULL if it is not found
p_end_of_path = strrchr( p_pathname, '\\' );
if( p_end_of_path == NULL )
{
// There is no path part
return FAILURE_INVALID_PATH;
}
else
{
path_length = (size_t)( p_end_of_path - p_pathname + 1 );
// Do some sanity checks on the length
if( path_length < 1 ) { return FAILURE_INVALID_PATH; }
if( ( path_length + 1 ) > basepath_size ) { return FAILURE_BUFFER_TOO_SMALL; }
// Copy the base path into the out variable
if( strncpy( p_basepath, p_pathname, path_length ) != 0 ) { return FAILURE_API_CALL; }
p_basepath[path_length] = '\0';
}
return SUCCESS;
}
Before str is the full path and file name, after str is just the path:
char dir_ch = '\\'; // set dir_ch according to platform
char str[] = "C:\\path\\to\\file.c";
char *pch = &str[strlen(str)-1];
while(*pch != dir_ch) pch--;
pch++;
*pch = '\0';
I'm writing a logger library in C and am currently trying to get better backtrace output by using addr2line. In order to do so, I need to be able to get the path of the current executable. At the moment I am just concerned about linux, but will be shooting for Mac OS support as well.
For linux support I'm trying to use readlink() and /proc/self/exe to resolve the current executable's path:
static char** getPrettyBacktrace( void* addresses[], int array_size ) {
// Used to return the strings generated from the addresses
char** backtrace_strings = (char**)malloc( sizeof( char ) * array_size );
for( int i = 0; i < array_size; i ++ ) {
backtrace_strings[i] = (char*)malloc( sizeof( char ) * 255 );
}
// Will hold the command to be used
char* command_string = (char*)malloc( 255 );
char* exe_path = (char*)malloc( 255 );
// Used to check if an error occured while setting up command
bool error = false;
// Check if we are running on Mac OS or not, and select appropriate command
char* command;
#ifdef __APPLE__
// Check if 'gaddr2line' function is available, if not exit
if( !system( "which gaddr2line > /dev/null 2>&1" ) ) {
command = "gaddr2line -Cfspe";
// TODO: get path for mac with 'proc_pidpath'
} else {
writeLog( SIMPLOG_LOGGER, "Function 'gaddr2line' unavailable. Defaulting to standard backtrace. Please install package 'binutils' for better stacktrace output." );
error = true;
}
#else
// Check if 'addr2line' function is available, if not exit
if( !system( "which addr2line > /dev/null 2>&1" ) ) {
command = "addr2line -Cfspe";
if( readlink( "/proc/self/exe", exe_path, sizeof( exe_path ) ) < 0 ) {
writeLog( SIMPLOG_LOGGER, "Unable to get execution path. Defaulting to standard backtrace." );
error = true;
}
} else {
writeLog( SIMPLOG_LOGGER, "Function 'addr2line' unavailable. Defaulting to standard backtrace. Please install package 'binutils' for better stacktrace output." );
error = true;
}
#endif
// If an error occured, exit now
if( error ) {
free( backtrace_strings );
free( command_string );
free( exe_path );
return NULL;
}
for( int i = 0; i < array_size; i++ ) {
// Compose the complete command to execute
sprintf( command_string, "%s %s %X", command, exe_path, addresses[i] );
// Execute the command
FILE* line = popen( command_string, "r" );
// Get the size of the command output
int line_size = fseek( line, 0, SEEK_END );
// Read the output into the return string
fgets( backtrace_strings[i] , line_size, line );
// Close the command pipe
pclose( line );
}
return backtrace_strings;
}
The path being returned by readlink() is: /home/nax��?. the first part is correct: /home/na, but everything after that is pure gibberish.
Why am I unable to get the current execution path in this way?
char* exe_path = (char*)malloc( 255 );
// ...
readlink( "/proc/self/exe", exe_path, sizeof( exe_path ) )
exe_path is a pointer, so it's size will be equal to sizeof(char*) (4 or 8), not 255.
change exe_path to char[255] or change the call to sizeof
btw, readlink does not append the NULL byte, so you should do something like this:
len = readlink( "/proc/self/exe", exe_path, sizeof( exe_path ) )
exe_path[len] = 0;
I'm trying to write a program that will delete a set of files/folders that are matching a specific naming pattern (wild cards) based on their dates using the windows API
...
SHFILEOPSTRUCT shFileOpStruct = {
.hwnd = NULL,
.wFunc = processByDate->op,
.pTo = NULL,
.fFlags = FOF_NOCONFIRMATION | FOF_SILENT
};
buildReferenceDate( &refTime, processByDate->nDays );
hFind = FindFirstFile( processByDate->srcFileName, &findFileData );
errorCode = GetLastError();
while ( errorCode == ERROR_SUCCESS ) {
LONG res = CompareFileTime( &refTime, &findFileData.ftCreationTime );
if ( (processByDate->nDays ^ res) > 0 ) {
sprintf( strrchr(processByDate->srcFileName, '\\') + 1, "%s%c",
findFileData.cFileName, '\0');
shFileOpStruct.pFrom = processByDate->srcFileName;
fprintf( stdout, "\n%s\n", shFileOpStruct.pFrom);
fprintf( stdout, "\n0x%x\n", SHFileOperation( &shFileOpStruct ));
}
FindNextFile( hFind, &findFileData );
errorCode = GetLastError();
}
if ( errorCode != ERROR_NO_MORE_FILES )
displayError ( stdout, errorCode );
...
Only the first matching file is deleted, because FindNextFile terminates with "The handle is invalid." apparently SHFileOperation somehow invalidates the file handle (or at least so I suppose). The only solution I can think of is to save the name of the matching files/folders and delete them one by one. Is there any other simpler solution?
Thanks
FindNextFile( hFind, &findFileData );
errorCode = GetLastError();
That's wrong. Only call GetLastError() when you get a FALSE return from FindNextFile(). Fix:
if (!FindNextFile( hFind, &findFileData ) {
errorCode = GetLastError();
}
The thread's last error code is not set when a function succeeds. Instead of calling GetLastError, you have to check the return value of FindNextFile.
If the function succeeds, the return value is nonzero and the lpFindFileData parameter contains information about the next file or directory found.
If the function fails, the return value is zero and the contents of lpFindFileData are indeterminate.
FindNextFile function
The loop should look like this:
HANDLE handle(FindFirstFile(...));
if (handle != INVALID_HANDLE_VALUE)
{
do
{
// filter files here
}
while (FindNextFile(handle, ...));
FindClose(handle);
}
I'm trying to get the version of Mac OS X programmatically in C. After searching for a while I tried this code:
#include <CoreServices/CoreServices.h>
int GetOS()
{
SInt32 majorVersion,minorVersion,bugFixVersion;
Gestalt(gestaltSystemVersionMajor, &majorVersion);
Gestalt(gestaltSystemVersionMinor, &minorVersion);
Gestalt(gestaltSystemVersionBugFix, &bugFixVersion);
printf("Running on Mac OS X %d.%d.%d\n",majorVersion,minorVersion,bugFixVersion);
return 0;
}
XCode returns an LD error:
Undefined symbols for architecture x86_64:
"_Gestalt", referenced from:
_GetOS in main.o
What am I missing? How do you do this?
I found also this snippet
[[NSProcessInfo processInfo] operatingSystemVersionString]
But I have no idea how to write that in C.
Did you pass the appropriate framework to GCC in order to enable CoreServices?
% gcc -framework CoreServices -o getos main.c
The code below should work in the foreseeable future for figuring out the current version of Mac Os X.
/* McUsr put this together, and into public domain,
without any guarrantees about anything,
but the statement that it works for me.
*/
#if 1 == 1
#define TESTING
#endif
#include <sys/param.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct osver {
int minor;
int sub;
} ;
typedef struct osver osxver ;
void macosx_ver(char *darwinversion, osxver *osxversion ) ;
char *osversionString(void) ;
#ifdef TESTING
int main( int argc, char *argv[] )
{
osxver foundver;
char *osverstr= NULL ;
osverstr=osversionString() ;
macosx_ver(osverstr, &foundver ) ;
printf("Mac os x version = 10.%d.%d\n",foundver.minor,foundver.sub );
free(osverstr);
return 0;
}
#endif
char *osversionString(void) {
int mib[2];
size_t len;
char *kernelVersion=NULL;
mib[0] = CTL_KERN;
mib[1] = KERN_OSRELEASE;
if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0 ) {
fprintf(stderr,"%s: Error during sysctl probe call!\n",__PRETTY_FUNCTION__ );
fflush(stdout);
exit(4) ;
}
kernelVersion = malloc(len );
if (kernelVersion == NULL ) {
fprintf(stderr,"%s: Error during malloc!\n",__PRETTY_FUNCTION__ );
fflush(stdout);
exit(4) ;
}
if (sysctl(mib, 2, kernelVersion, &len, NULL, 0) < 0 ) {
fprintf(stderr,"%s: Error during sysctl get verstring call!\n",__PRETTY_FUNCTION__ );
fflush(stdout);
exit(4) ;
}
return kernelVersion ;
}
void macosx_ver(char *darwinversion, osxver *osxversion ) {
/*
From the book Mac Os X and IOS Internals:
In version 10.1.1, Darwin (the core OS) was renumbered from v1.4.1 to 5.1,
and since then has followed the OS X numbers consistently by being four
numbers ahead of the minor version, and aligning its own minor with the
sub-version.
*/
char firstelm[2]= {0,0},secElm[2]={0,0};
if (strlen(darwinversion) < 5 ) {
fprintf(stderr,"%s: %s Can't possibly be a version string. Exiting\n",__PRETTY_FUNCTION__,darwinversion);
fflush(stdout);
exit(2);
}
char *s=darwinversion,*t=firstelm,*curdot=strchr(darwinversion,'.' );
while ( s != curdot )
*t++ = *s++;
t=secElm ;
curdot=strchr(++s,'.' );
while ( s != curdot )
*t++ = *s++;
int maj=0, min=0;
maj= (int)strtol(firstelm, (char **)NULL, 10);
if ( maj == 0 && errno == EINVAL ) {
fprintf(stderr,"%s Error during conversion of version string\n",__PRETTY_FUNCTION__);
fflush(stdout);
exit(4);
}
min=(int)strtol(secElm, (char **)NULL, 10);
if ( min == 0 && errno == EINVAL ) {
fprintf(stderr,"%s: Error during conversion of version string\n",__PRETTY_FUNCTION__);
fflush(stdout);
exit(4);
}
osxversion->minor=maj-4;
osxversion->sub=min;
}
Here is one with "less work", good enough for home projects (statically allocated buffers, ignoring errors). Works for me in OS X 10.11.1.
#include <stdio.h>
/*!
#brief Returns one component of the OS version
#param component 1=major, 2=minor, 3=bugfix
*/
int GetOSVersionComponent(int component) {
char cmd[64] ;
sprintf(
cmd,
"sw_vers -productVersion | awk -F '.' '{print $%d}'",
component
) ;
FILE* stdoutFile = popen(cmd, "r") ;
int answer = 0 ;
if (stdoutFile) {
char buff[16] ;
char *stdout = fgets(buff, sizeof(buff), stdoutFile) ;
pclose(stdoutFile) ;
sscanf(stdout, "%d", &answer) ;
}
return answer ;
}
int main(int argc, const char * argv[]) {
printf(
"Your OS version is: %d.%d.%d\n",
GetOSVersionComponent(1),
GetOSVersionComponent(2),
GetOSVersionComponent(3)
) ;
return 0 ;
}
Using the hint from #uchuugaka in the comment on the answer by #McUsr, I wrote a function that seems to work. I'm not saying it's better than any other answer.
/*
* Structure for MacOS version number
*/
typedef struct macos_version_str
{
ushort major;
ushort minor;
ushort point;
} macos_type;
/****************************************************************************
*
* Determine the MacOS version.
*
* Parameters:
* version_struct: (pointer to) macos_version structure to be filled in.
*
* Return value:
* 0: no error.
*
****************************************************************************/
static int get_macos_version ( macos_type *version_struct )
{
char os_temp [20] = "";
char *os_temp_ptr = os_temp;
size_t os_temp_len = sizeof(os_temp);
size_t os_temp_left = 0;
int rslt = 0;
version_struct->major = 0;
version_struct->minor = 0;
version_struct->point = 0;
rslt = sysctlbyname ( "kern.osproductversion", os_temp, &os_temp_len, NULL, 0 );
if ( rslt != 0 )
{
fprintf ( stderr,
"sysctlbyname() returned %d error (%d): %s",
rslt, errno, strerror(errno));
return ( rslt );
}
os_temp_left = os_temp_len; /* length of string returned */
int temp = atoi ( os_temp_ptr );
version_struct->major = temp;
version_struct->major = atoi ( os_temp_ptr );
while ( os_temp_left > 0 && *os_temp_ptr != '.' )
{
os_temp_left--;
os_temp_ptr++;
}
os_temp_left--;
os_temp_ptr++;
version_struct->minor = atoi ( os_temp_ptr );
while ( os_temp_left > 0 && *os_temp_ptr != '.' )
{
os_temp_left--;
os_temp_ptr++;
}
os_temp_left--;
os_temp_ptr++;
version_struct->point = atoi ( os_temp_ptr );
fprintf ( stderr, "Calculated OS Version: %d.%d.%d", version_struct->major, version_struct->minor, version_struct->point );
if ( version_struct->major == 0 ||
version_struct->minor == 0 ||
version_struct->point == 0 )
{
fprintf ( stderr, "Unable to parse MacOS version string %s", os_temp );
return ( -2 );
}
return 0;
}
If for whatever reason you want to avoid the Gestalt API (which still works fine, but is deprecated), the macosx_deployment_target.c in cctools contains a code snippet that uses the CTL_KERN + KERN_OSRELEASE sysctl(), similar to other answers here.
Here's a small program adapted from that code and taking macOS 11 and newer (tested and verified with up to macOS 12.6, which was at time of updating this post the latest stable release) into account:
#include <stdio.h>
#include <sys/sysctl.h>
int main()
{
char osversion[32];
size_t osversion_len = sizeof(osversion) - 1;
int osversion_name[] = { CTL_KERN, KERN_OSRELEASE };
if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) == -1) {
printf("sysctl() failed\n");
return 1;
}
uint32_t major, minor;
if (sscanf(osversion, "%u.%u", &major, &minor) != 2) {
printf("sscanf() failed\n");
return 1;
}
if (major >= 20) {
major -= 9;
// macOS 11 and newer
printf("%u.%u\n", major, minor);
} else {
major -= 4;
// macOS 10.1.1 and newer
printf("10.%u.%u\n", major, minor);
}
return 0;
}
RegOpenKeyEx()
I want to printf("Success") or printf("Failure") depending on if the function fails or succeeds
How would I do such a conditional while keeping it neat and legible?
I am wanting to stay away from this :
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,"HUGE LONG KYEY STRUCTURE HERE",0,KEY_SET_VALUE) != 0 )
{
//CODE
}
Are you asking how to check why it failed?
"A nonzero error code defined in Winerror.h indicates failure. To get a generic description of the error, call FormatMessage with the FORMAT_MESSAGE_FROM_SYSTEM flag set."
So.. ERROR_SUCCESS if succeeded, error code if it fails.
After checking the unedited question, I think the only thing you can do is to break up the statement into multiple statements like this:
const char* regKey = "BIG_STRING......";
DWORD errorCode = RegOpenKeyEx(...);
if(ERROR_SUCCESS == errorCode)
{
//Rest of the code
}
else
{
//Error handling
}
You can use newlines, or name things or both,
LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"HUGE LONG KYEY STRUCTURE HERE",
0,KEY_SET_VALUE);
if(result != 0 ) {
...
or perhaps
LPCTSTR keypath = "HUGE LONG KYEY STRUCTURE HERE";
if( RegOpenKeyEx(HKEY_LOCAL_MACHINE,keypath,0,KEY_SET_VALUE) != 0 ) {
...
You could instead use the ternary operator:
bool success = (RegKeyOpenEx(...) == ERROR_SUCCESS);
printf("%s\n", success ? "Success" : "Failure");
It does not return ERROR_SUCCESS when it does not succeed. Is that what you mean?
for multiple checks of error codes use:
LONG errors = 0;
errors += abs( RegKeyOpenEx(HKEY_LOCAL_MACHINE,"HUGE LONG KYEY STRUCTURE HERE",0,KEY_SET_VALUE) );
errors += abs( RegKeyOpenEx(HKEY_LOCAL_MACHINE,"HUGE LONG KYEY STRUCTURE HERE",0,KEY_SET_VALUE) );
errors += abs( RegKeyOpenEx(HKEY_LOCAL_MACHINE,"HUGE LONG KYEY STRUCTURE HERE",0,KEY_SET_VALUE) );
errors += abs( RegKeyOpenEx(HKEY_LOCAL_MACHINE,"HUGE LONG KYEY STRUCTURE HERE",0,KEY_SET_VALUE) );
errors += abs( RegKeyOpenEx(HKEY_LOCAL_MACHINE,"HUGE LONG KYEY STRUCTURE HERE",0,KEY_SET_VALUE) );
if( errors > 0 )
{
print( "OMG It went wrong!\n" );
}
else
{
print( "Hero!\n" );
}