Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ Basic use:

#include "cfgpath.h"

char cfgdir[MAX_PATH];
get_user_config_file(cfgdir, sizeof(cfgdir), "myapp");
cfgpathchar_t cfgdir[MAX_PATH];
get_user_config_file(cfgdir, sizeof(cfgdir)/sizeof(cfgdir[0]), CFGPATHTEXT("myapp"));
if (cfgdir[0] == 0) {
printf("Unable to find home directory.\n");
return 1;
}
printf("Saving configuration file to %s\n", cfgdir);
cfgpath__printf(CFGPATHTEXT("Saving configuration file to %s\n"), cfgdir);

To integrate it into your own project, just copy cfgpath.h. All the other
files are for testing to make sure it works correctly, so you don't need them
Expand Down
84 changes: 61 additions & 23 deletions cfgpath.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@
*
* Example use:
*
* char cfgdir[256];
* get_user_config_file(cfgdir, sizeof(cfgdir), "myapp");
* cfgpathchar_t cfgdir[256];
* get_user_config_file(cfgdir, sizeof(cfgdir)/sizeof(cfgdir[0]), CFGPATHTEXT("myapp"));
* if (cfgdir[0] == 0) {
* printf("Unable to find home directory.\n");
* return 1;
* }
* printf("Saving configuration file to %s\n", cfgdir);
* cfgpath__printf(CFGPATHTEXT("Saving configuration file to %s\n"), cfgdir);
*
* The CFGPATHTEXT macro will prefix strings with L if building for Win32 with
* UNICODE #defined. Similarly, cfgpath__printf(), cfgpath__strcat(), etc. will
* use the Win32 wchar_t functions, and cfgpathchar_t will resolve to the wchar_t
* data type. Otherwise, UTF-8 is assumed and the standard C library functions are
* used along with the char data type. For maximum portability between platforms
* and encodings, use these macros.
*
* A number of constants are also defined:
*
Expand All @@ -33,6 +40,18 @@
#ifndef CFGPATH_H_
#define CFGPATH_H_

#if defined(WIN32) && defined(UNICODE)
#define CFGPATHTEXT(q) L##q
#else
#define CFGPATHTEXT(q) q
#endif

#define cfgpath__strlen strlen
#define cfgpath__strcat strcat
#define cfgpath__strcpy strcpy
#define cfgpath__strcmp strcmp
#define cfgpath__printf printf

#ifdef _MSC_VER
#define inline __inline
#include <direct.h>
Expand All @@ -50,7 +69,22 @@
#include <shlobj.h>
/* MAX_PATH is defined by the Windows API */
#define PATH_SEPARATOR_CHAR '\\'
#define PATH_SEPARATOR_STRING "\\"
#define PATH_SEPARATOR_STRING CFGPATHTEXT("\\")
#ifdef UNICODE // will use wchar_t types in this instance
#define CFGPATH_WIN32_UNICODE
#define HAVE_CFGPATHCHAR_T
typedef wchar_t cfgpathchar_t;
#undef cfgpath__strlen
#undef cfgpath__strcat
#undef cfgpath__strcpy
#undef cfgpath__strcmp
#undef cfgpath__printf
#define cfgpath__strlen wcslen
#define cfgpath__strcat wcscat
#define cfgpath__strcpy wcscpy
#define cfgpath__strcmp wcscmp
#define cfgpath__printf wprintf
#endif // UNICODE
#elif defined(__APPLE__)
#include <CoreServices/CoreServices.h>
#include <sys/stat.h>
Expand All @@ -61,6 +95,10 @@
#error cfgpath.h functions have not been implemented for your platform! Please send patches.
#endif

#ifndef HAVE_CFGPATHCHAR_T
typedef char cfgpathchar_t;
#endif

/** Get an absolute path to a single configuration file, specific to this user.
*
* This function is useful for programs that need only a single configuration
Expand All @@ -86,7 +124,7 @@
* @post The file may or may not exist.
* @post The folder holding the file is created if needed.
*/
static inline void get_user_config_file(char *out, unsigned int maxlen, const char *appname)
static inline void get_user_config_file(cfgpathchar_t *out, unsigned int maxlen, const cfgpathchar_t *appname)
{
#ifdef __linux__
const char *out_orig = out;
Expand Down Expand Up @@ -137,14 +175,14 @@ static inline void get_user_config_file(char *out, unsigned int maxlen, const ch
return;
}
/* We don't try to create the AppData folder as it always exists already */
unsigned int appname_len = strlen(appname);
if (strlen(out) + 1 + appname_len + strlen(".ini") + 1 > maxlen) {
unsigned int appname_len = cfgpath__strlen(appname);
if (cfgpath__strlen(out) + 1 + appname_len + cfgpath__strlen(CFGPATHTEXT(".ini")) + 1 > maxlen) {
out[0] = 0;
return;
}
strcat(out, "\\");
strcat(out, appname);
strcat(out, ".ini");
cfgpath__strcat(out, PATH_SEPARATOR_STRING);
cfgpath__strcat(out, appname);
cfgpath__strcat(out, CFGPATHTEXT(".ini"));
#elif defined(__APPLE__)
FSRef ref;
FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &ref);
Expand Down Expand Up @@ -192,7 +230,7 @@ static inline void get_user_config_file(char *out, unsigned int maxlen, const ch
*
* @post The folder is created if needed.
*/
static inline void get_user_config_folder(char *out, unsigned int maxlen, const char *appname)
static inline void get_user_config_folder(cfgpathchar_t *out, unsigned int maxlen, const cfgpathchar_t *appname)
{
#ifdef __linux__
const char *out_orig = out;
Expand Down Expand Up @@ -246,16 +284,16 @@ static inline void get_user_config_folder(char *out, unsigned int maxlen, const
return;
}
/* We don't try to create the AppData folder as it always exists already */
unsigned int appname_len = strlen(appname);
if (strlen(out) + 1 + appname_len + 1 + 1 > maxlen) {
unsigned int appname_len = cfgpath__strlen(appname);
if (cfgpath__strlen(out) + 1 + appname_len + 1 + 1 > maxlen) {
out[0] = 0;
return;
}
strcat(out, "\\");
strcat(out, appname);
cfgpath__strcat(out, PATH_SEPARATOR_STRING);
cfgpath__strcat(out, appname);
/* Make the AppData\appname folder if it doesn't already exist */
mkdir(out);
strcat(out, "\\");
cfgpath__strcat(out, PATH_SEPARATOR_STRING);
#elif defined(__APPLE__)
FSRef ref;
FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &ref);
Expand Down Expand Up @@ -309,7 +347,7 @@ static inline void get_user_config_folder(char *out, unsigned int maxlen, const
*
* @post The folder is created if needed.
*/
static inline void get_user_data_folder(char *out, unsigned int maxlen, const char *appname)
static inline void get_user_data_folder(cfgpathchar_t *out, unsigned int maxlen, const cfgpathchar_t *appname)
{
#ifdef __linux__
const char *out_orig = out;
Expand Down Expand Up @@ -394,7 +432,7 @@ static inline void get_user_data_folder(char *out, unsigned int maxlen, const ch
*
* @post The folder is created if needed.
*/
static inline void get_user_cache_folder(char *out, unsigned int maxlen, const char *appname)
static inline void get_user_cache_folder(cfgpathchar_t *out, unsigned int maxlen, const cfgpathchar_t *appname)
{
#ifdef __linux__
const char *out_orig = out;
Expand Down Expand Up @@ -448,16 +486,16 @@ static inline void get_user_cache_folder(char *out, unsigned int maxlen, const c
return;
}
/* We don't try to create the AppData folder as it always exists already */
unsigned int appname_len = strlen(appname);
if (strlen(out) + 1 + appname_len + 1 + 1 > maxlen) {
unsigned int appname_len = cfgpath__strlen(appname);
if (cfgpath__strlen(out) + 1 + appname_len + 1 + 1 > maxlen) {
out[0] = 0;
return;
}
strcat(out, "\\");
strcat(out, appname);
cfgpath__strcat(out, PATH_SEPARATOR_STRING);
cfgpath__strcat(out, appname);
/* Make the AppData\appname folder if it doesn't already exist */
mkdir(out);
strcat(out, "\\");
cfgpath__strcat(out, PATH_SEPARATOR_STRING);
#elif defined(__APPLE__)
/* No distinction under OS X */
get_user_config_folder(out, maxlen, appname);
Expand Down
9 changes: 6 additions & 3 deletions test-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ char *test_getenv(const char *var)
int test_mkdir(const char *path, mode_t mode)
{
return 0;
// silence unused variable warnings
(void)path;
(void)mode;
}

#define TOSTRING_X(x) #x
#define TOSTRING(x) TOSTRING_X(x)
#define RUN_TEST(result, msg) \
TEST_FUNC(buffer, sizeof(buffer), "test-linux"); \
TEST_FUNC(buffer, sizeof(buffer)/sizeof(buffer[0]), "test-linux"); \
CHECK_RESULT(result, msg)

#define CHECK_RESULT(result, msg) \
Expand All @@ -57,9 +60,9 @@ int test_mkdir(const char *path, mode_t mode)
printf("PASS: " TOSTRING(TEST_FUNC) "() " msg "\n"); \
}

int main(int argc, char *argv[])
int main(void)
{
char buffer[256];
cfgpathchar_t buffer[MAX_PATH];

/*
* get_user_config_file()
Expand Down
105 changes: 105 additions & 0 deletions test-win-unicode.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* @file test-win-unicode.c
* @brief cfgpath.h test code for the Windows, with Unicode support.
*
* Copyright (C) 2013 Adam Nielsen <malvineous@shikadi.net>
*
* This code is placed in the public domain. You are free to use it for any
* purpose. If you add new platform support, please contribute a patch!
*/

#include <string.h>
#include <stdio.h>

#define SHGetFolderPath test_SHGetFolderPath
#define mkdir test_mkdir
int test_mkdir(void *path);
int test_SHGetFolderPath(void *hwndOwner, int nFolder, void *hToken, int dwFlags, void *_pszPath);
#undef __linux__
#ifndef WIN32
#define WIN32
#endif
#ifndef UNICODE // enable unicode before #include-ing cfgpath.h
#define UNICODE
#endif
#include "cfgpath.h"

const cfgpathchar_t *set_appdata;
const cfgpathchar_t *set_appdata_local;
int set_retval;

/* Fake implementation of SHGetFolderPath() that fails when and how we want */
int test_SHGetFolderPath(void *hwndOwner, int nFolder, void *hToken,
int dwFlags, void *_pszPath)
{
// silence unused variable warnings
(void)hwndOwner;
(void)hToken;
(void)dwFlags;

/* Hopefully trigger an error if the buffer is too small */
cfgpathchar_t *pszPath = (cfgpathchar_t*) _pszPath;
pszPath[MAX_PATH - 1] = 0;

switch (nFolder) {
case CSIDL_APPDATA:
if (set_appdata) {
cfgpath__strcpy(pszPath, set_appdata);
return set_retval;
}
break;
case CSIDL_LOCAL_APPDATA:
if (set_appdata_local) {
cfgpath__strcpy(pszPath, set_appdata_local);
return set_retval;
}
break;
}
return E_FAIL;
}

int test_mkdir(void *path)
{
return 0;
(void)path; // silence unused variable warning
}

int main(void)
{
cfgpathchar_t buffer[MAX_PATH];

// get_user_config_file
get_user_config_file(buffer, sizeof(buffer)/sizeof(buffer[0]), CFGPATHTEXT("myapp"));
if (buffer[0] == 0) {
printf("get_user_config_file() fail\n");
return 1;
}
cfgpath__printf(CFGPATHTEXT("get_user_config_file() returned '%s'!\n"), buffer);

// get_user_config_folder
get_user_config_folder(buffer, sizeof(buffer)/sizeof(buffer[0]), CFGPATHTEXT("myapp"));
if (buffer[0] == 0) {
printf("get_user_config_folder() fail\n");
return 1;
}
cfgpath__printf(CFGPATHTEXT("get_user_config_folder() returned '%s'!\n"), buffer);

// get_user_data_folder
get_user_data_folder(buffer, sizeof(buffer)/sizeof(buffer[0]), CFGPATHTEXT("myapp"));
if (buffer[0] == 0) {
printf("get_user_data_folder() fail\n");
return 1;
}
cfgpath__printf(CFGPATHTEXT("get_user_data_folder() returned '%s'!\n"), buffer);

// get_user_cache_folder
get_user_cache_folder(buffer, sizeof(buffer)/sizeof(buffer[0]), CFGPATHTEXT("myapp"));
if (buffer[0] == 0) {
printf("get_user_cache_folder() fail\n");
return 1;
}
cfgpath__printf(CFGPATHTEXT("get_user_cache_folder() returned '%s'!\n"), buffer);

printf("All tests passed for platform: Windows (Unicode).\n");
return 0;
}
23 changes: 17 additions & 6 deletions test-win.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @file test-linux.c
* @brief cfgpath.h test code for the Linux platform.
* @file test-win.c
* @brief cfgpath.h test code for the Windows platform.
*
* Copyright (C) 2013 Adam Nielsen <malvineous@shikadi.net>
*
Expand All @@ -13,10 +13,15 @@

#define SHGetFolderPath test_SHGetFolderPath
#define mkdir test_mkdir
int test_mkdir(const char *path);
int test_SHGetFolderPath(void *hwndOwner, int nFolder, void *hToken, int dwFlags, char *pszPath);
#undef __linux__
#ifndef WIN32
#define WIN32
#endif
#ifdef UNICODE
#undef UNICODE // try test-win-unicode.c if you want a unicode example
#endif
#include "cfgpath.h"

const char *set_appdata;
Expand All @@ -27,6 +32,11 @@ int set_retval;
int test_SHGetFolderPath(void *hwndOwner, int nFolder, void *hToken,
int dwFlags, char *pszPath)
{
// silence unused variable warnings
(void)hwndOwner;
(void)hToken;
(void)dwFlags;

/* Hopefully trigger an error if the buffer is too small */
pszPath[MAX_PATH - 1] = 0;

Expand All @@ -50,16 +60,17 @@ int test_SHGetFolderPath(void *hwndOwner, int nFolder, void *hToken,
int test_mkdir(const char *path)
{
return 0;
(void)path; // silence unused variable warning
}

#define TOSTRING_X(x) #x
#define TOSTRING(x) TOSTRING_X(x)
#define RUN_TEST(result, msg) \
TEST_FUNC(buffer, sizeof(buffer), "test-win"); \
TEST_FUNC(buffer, sizeof(buffer)/sizeof(buffer[0]), "test-win"); \
CHECK_RESULT(result, msg)

#define CHECK_RESULT(result, msg) \
if (strcmp(buffer, result) != 0) { \
if (cfgpath__strcmp(buffer, result) != 0) { \
printf("FAIL: %s:%d " TOSTRING(TEST_FUNC) "() " msg " Test failed, " \
"returning the wrong value.\n" \
"Expected: %s\nGot: %s\n", __FILE__, __LINE__, result, buffer); \
Expand All @@ -68,9 +79,9 @@ int test_mkdir(const char *path)
printf("PASS: " TOSTRING(TEST_FUNC) "() " msg "\n"); \
}

int main(int argc, char *argv[])
int main(void)
{
char buffer[256];
cfgpathchar_t buffer[MAX_PATH];

/*
* get_user_config_file()
Expand Down