diff --git a/include/extern.h b/include/extern.h index d4f1e549d..63c6800fc 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1976,6 +1976,7 @@ extern int win32_cr_helper(char, struct CRctxt *, void *, int); extern int win32_cr_gettrace(int, char *, int); extern int *win32_cr_shellexecute(const char *); # endif +extern boolean contains_directory(const char *); #endif /* WIN32 */ #endif /* MICRO || WIN32 */ diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index a0b8387e9..14273dd61 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -1339,7 +1339,7 @@ guilflags = $(lflags) -subsystem:windows # basic subsystem specific libraries, less the C Run-Time baselibs = kernel32.lib $(optlibs) $(winsocklibs) advapi32.lib gdi32.lib \ - ole32.lib Shell32.lib dbghelp.lib + ole32.lib Shell32.lib UserEnv.lib dbghelp.lib winlibs = $(baselibs) user32.lib comdlg32.lib winspool.lib # for Windows applications that use the C Run-Time libraries diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index 45282b446..04b4668ac 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -19,9 +19,7 @@ #endif static void nhusage(void); -static char *get_executable_path(void); static void early_options(int argc, char **argv); -char *translate_path_variables(const char *, char *); char *exename(void); boolean fakeconsole(void); void freefakeconsole(void); @@ -98,17 +96,7 @@ static struct stat hbuf; extern char orgdir[]; -int get_known_folder_path(const KNOWNFOLDERID * folder_id, - char * path, size_t path_size); -void create_directory(const char * path); -int build_known_folder_path(const KNOWNFOLDERID * folder_id, - char * path, size_t path_size, boolean versioned); -void build_environment_path(const char * env_str, const char * folder, - char * path, size_t path_size); -boolean folder_file_exists(const char * folder, const char * file_name); -boolean test_portable_config(const char *executable_path, - char *portable_device_path, size_t portable_device_path_size); -void set_default_prefix_locations(const char *programPath); +extern void set_default_prefix_locations(const char *programPath); void copy_sysconf_content(void); void copy_config_content(void); void copy_hack_content(void); @@ -681,224 +669,6 @@ nhusage(void) #undef ADD_USAGE } -DISABLE_WARNING_UNREACHABLE_CODE - -int -get_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, - size_t path_size) -{ - PWSTR wide_path; - if (FAILED(SHGetKnownFolderPath(folder_id, 0, NULL, &wide_path))) { - error("Unable to get known folder path"); - return FALSE; - } - - size_t converted; - errno_t err; - - err = wcstombs_s(&converted, path, path_size, wide_path, _TRUNCATE); - - CoTaskMemFree(wide_path); - - if (err == STRUNCATE || err == EILSEQ) { - // silently handle this problem - return FALSE; - } else if (err != 0) { - error( - "Failed folder (%lu) path string conversion, unexpected err = %d", - folder_id->Data1, err); - return FALSE; - } - - return TRUE; -} - -void -create_directory(const char *path) -{ - BOOL dres = CreateDirectoryA(path, NULL); - - if (!dres) { - DWORD dw = GetLastError(); - - if (dw != ERROR_ALREADY_EXISTS) - error("Unable to create directory '%s'", path); - } -} - -RESTORE_WARNING_UNREACHABLE_CODE - -int -build_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, - size_t path_size, boolean versioned) -{ - if (!get_known_folder_path(folder_id, path, path_size)) - return FALSE; - - strcat(path, "\\NetHack\\"); - create_directory(path); - if (versioned) { - Sprintf(eos(path), "%d.%d\\", VERSION_MAJOR, VERSION_MINOR); - create_directory(path); - } - return TRUE; -} - -void -build_environment_path(const char *env_str, const char *folder, char *path, - size_t path_size) -{ - path[0] = '\0'; - - const char *root_path = nh_getenv(env_str); - - if (root_path == NULL) - return; - - strcpy_s(path, path_size, root_path); - - char *colon = strchr(path, ';'); - if (colon != NULL) - path[0] = '\0'; - - if (strlen(path) == 0) - return; - - append_slash(path); - - if (folder != NULL) { - strcat_s(path, path_size, folder); - strcat_s(path, path_size, "\\"); - } -} - -boolean -folder_file_exists(const char *folder, const char *file_name) -{ - char path[MAX_PATH]; - - if (folder[0] == '\0') - return FALSE; - - strcpy(path, folder); - strcat(path, file_name); - return file_exists(path); -} - -boolean -test_portable_config(const char *executable_path, char *portable_device_path, - size_t portable_device_path_size) -{ - int lth = 0; - const char *sysconf = "sysconf"; - char tmppath[MAX_PATH]; - boolean retval = FALSE, - save_initoptions_noterminate = iflags.initoptions_noterminate; - - if (portable_device_path - && folder_file_exists(executable_path, "sysconf")) { - /* - There is a sysconf file (not just sysconf.template) present in - the exe path, which is not the way NetHack is initially - distributed, so assume it means that the admin/installer wants to - override something, perhaps set up for a fully-portable - configuration that leaves no traces behind elsewhere on this - computer's hard drive - delve into that... - */ - - *portable_device_path = '\0'; - lth = sizeof tmppath - strlen(sysconf); - (void) strncpy(tmppath, executable_path, lth - 1); - tmppath[lth - 1] = '\0'; - (void) strcat(tmppath, sysconf); - - iflags.initoptions_noterminate = 1; - /* assure_syscf_file(); */ - config_error_init(TRUE, tmppath, FALSE); - /* ... and _must_ parse correctly. */ - if (read_config_file(tmppath, set_in_sysconf) - && sysopt.portable_device_paths) - retval = TRUE; - (void) config_error_done(); - iflags.initoptions_noterminate = save_initoptions_noterminate; - sysopt_release(); /* the real sysconf processing comes later */ - } - if (retval) { - lth = strlen(executable_path); - if (lth <= (int) portable_device_path_size - 1) - Strcpy(portable_device_path, executable_path); - else - retval = FALSE; - } - return retval; -} - -static char portable_device_path[MAX_PATH]; - -const char * -get_portable_device(void) -{ - return (const char *) portable_device_path; -} - -void -set_default_prefix_locations(const char *programPath UNUSED) -{ - static char executable_path[MAX_PATH]; - static char profile_path[MAX_PATH]; - static char versioned_profile_path[MAX_PATH]; - static char versioned_user_data_path[MAX_PATH]; - static char versioned_global_data_path[MAX_PATH]; - /* static char versioninfo[20] UNUSED; */ - - strcpy(executable_path, get_executable_path()); - append_slash(executable_path); - - if (test_portable_config(executable_path, portable_device_path, - sizeof portable_device_path)) { - gf.fqn_prefix[SYSCONFPREFIX] = executable_path; - gf.fqn_prefix[CONFIGPREFIX] = portable_device_path; - gf.fqn_prefix[HACKPREFIX] = portable_device_path; - gf.fqn_prefix[SAVEPREFIX] = portable_device_path; - gf.fqn_prefix[LEVELPREFIX] = portable_device_path; - gf.fqn_prefix[BONESPREFIX] = portable_device_path; - gf.fqn_prefix[SCOREPREFIX] = portable_device_path; - gf.fqn_prefix[LOCKPREFIX] = portable_device_path; - gf.fqn_prefix[TROUBLEPREFIX] = portable_device_path; - gf.fqn_prefix[DATAPREFIX] = executable_path; - } else { - if (!build_known_folder_path(&FOLDERID_Profile, profile_path, - sizeof(profile_path), FALSE)) - strcpy(profile_path, executable_path); - - if (!build_known_folder_path(&FOLDERID_Profile, - versioned_profile_path, - sizeof(profile_path), TRUE)) - strcpy(versioned_profile_path, executable_path); - - if (!build_known_folder_path(&FOLDERID_LocalAppData, - versioned_user_data_path, - sizeof(versioned_user_data_path), TRUE)) - strcpy(versioned_user_data_path, executable_path); - - if (!build_known_folder_path( - &FOLDERID_ProgramData, versioned_global_data_path, - sizeof(versioned_global_data_path), TRUE)) - strcpy(versioned_global_data_path, executable_path); - - gf.fqn_prefix[SYSCONFPREFIX] = versioned_global_data_path; - gf.fqn_prefix[CONFIGPREFIX] = profile_path; - gf.fqn_prefix[HACKPREFIX] = versioned_profile_path; - gf.fqn_prefix[SAVEPREFIX] = versioned_user_data_path; - gf.fqn_prefix[LEVELPREFIX] = versioned_user_data_path; - gf.fqn_prefix[BONESPREFIX] = versioned_global_data_path; - gf.fqn_prefix[SCOREPREFIX] = versioned_global_data_path; - gf.fqn_prefix[LOCKPREFIX] = versioned_global_data_path; - gf.fqn_prefix[TROUBLEPREFIX] = versioned_profile_path; - gf.fqn_prefix[DATAPREFIX] = executable_path; - } -} - /* copy file if destination does not exist */ void copy_file(const char *dst_folder, const char *dst_name, @@ -1080,7 +850,9 @@ authorize_explore_mode(void) return TRUE; /* no restrictions on explore mode */ } +#ifndef PATH_SEPARATOR #define PATH_SEPARATOR '\\' +#endif #if defined(WIN32) && !defined(WIN32CON) static char exenamebuf[PATHLEN]; @@ -1147,96 +919,6 @@ void freefakeconsole(void) } #endif -static boolean path_buffer_set = FALSE; -static char path_buffer[MAX_PATH]; - -char * -get_executable_path(void) -{ - -#ifdef UNICODE - { - TCHAR wbuf[BUFSZ]; - GetModuleFileName((HANDLE) 0, wbuf, BUFSZ); - WideCharToMultiByte(CP_ACP, 0, wbuf, -1, path_buffer, sizeof(path_buffer), NULL, NULL); - } -#else - DWORD length = GetModuleFileName((HANDLE) 0, path_buffer, MAX_PATH); - if (length == ERROR_INSUFFICIENT_BUFFER) error("Unable to get module name"); - path_buffer[length] = '\0'; -#endif - - char * seperator = strrchr(path_buffer, PATH_SEPARATOR); - if (seperator) - *seperator = '\0'; - - path_buffer_set = TRUE; - return path_buffer; -} - -char * -windows_exepath(void) -{ - char *p = (char *) 0; - - if (path_buffer_set) - p = path_buffer; - return p; -} - -char * -translate_path_variables(const char *str, char *buf) -{ - const char *src; - char evar[BUFSZ], *dest, *envp, *eptr = (char *) 0; - boolean in_evar; - size_t ccount, ecount, destcount, slen = str ? strlen(str) : 0; - - if (!slen || !buf) { - if (buf) - *buf = '\0'; - return buf; - } - - dest = buf; - src = str; - in_evar = FALSE; - destcount = ecount = 0; - for (ccount = 0; ccount < slen && destcount < (BUFSZ - 1) && - ecount < (BUFSZ - 1); ++ccount, ++src) { - if (*src == '%') { - if (in_evar) { - *eptr = '\0'; - envp = nh_getenv(evar); - if (envp) { - size_t elen = strlen(envp); - - if ((elen + destcount) < (size_t) (BUFSZ - 1)) { - Strcpy(dest, envp); - dest += elen; - destcount += elen; - } - } - } else { - eptr = evar; - ecount = 0; - } - in_evar = !in_evar; - continue; - } - if (in_evar) { - *eptr++ = *src; - ecount++; - } else { - *dest++ = *src; - destcount++; - } - } - *dest = '\0'; - return buf; -} - - /*ARGSUSED*/ void windows_raw_print(const char *str) @@ -1453,18 +1135,6 @@ gotlock: } #endif /* PC_LOCKING */ -boolean -file_exists(const char *path) -{ - struct stat sb; - - /* Just see if it's there */ - if (stat(path, &sb)) { - return FALSE; - } - return TRUE; -} - RESTORE_WARNING_UNREACHABLE_CODE /* diff --git a/sys/windows/windsys.c b/sys/windows/windsys.c index 24dd6ed60..ea425ce26 100644 --- a/sys/windows/windsys.c +++ b/sys/windows/windsys.c @@ -13,6 +13,7 @@ #include "win10.h" #include "winos.h" +#include #define NEED_VARARGS #include "hack.h" @@ -27,6 +28,7 @@ #ifdef WIN32 #include +#include /* * The following WIN32 API routines are used in this file. @@ -40,6 +42,8 @@ * */ +static char portable_device_path[MAX_PATH]; + /* runtime cursor display control switch */ boolean win32_cursorblink; @@ -68,7 +72,17 @@ unsigned long sys_random_seed(void); static int max_filename(void); #endif - +int get_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size); +void create_directory(const char *path); +int build_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size, boolean versioned); +void build_environment_path(const char *env_str, const char *folder, + char *path, size_t path_size); +boolean folder_file_exists(const char *folder, const char *file_name); +boolean test_portable_config(const char *executable_path, + char *portable_device_path, + size_t portable_device_path_size); /* The function pointer nt_kbhit contains a kbhit() equivalent * which varies depending on which window port is active. * For the tty port it is tty_kbhit() [from consoletty.c] @@ -491,6 +505,20 @@ get_port_id(char *buf) extern void free_winmain_stuff(void); #endif +/* return TRUE if s contains a directory, not just a filespec */ +boolean +contains_directory(const char *s) +{ + int i, slen = strlen(s); + const char *cp = s; + + for (i = 0; i < slen; ++i) { + if (*cp == '\\' || *cp == '/' || *cp == ':') + return TRUE; + } + return FALSE; +} + void nethack_exit(int code) { @@ -697,25 +725,6 @@ windows_early_options(const char *window_opt) return 0; } -/* - * Add a backslash to any name not ending in /, \ or : There must - * be room for the \ - */ -void -append_slash(char *name) -{ - char *ptr; - - if (!*name) - return; - ptr = name + (strlen(name) - 1); - if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { - *++ptr = '\\'; - *++ptr = '\0'; - } - return; -} - #include /* Windows Crypto Next Gen (CNG) */ #ifndef STATUS_SUCCESS @@ -786,6 +795,363 @@ nt_assert_failed(const char *expression, const char *filepath, int line) expression, filename, line); } +boolean +get_user_home_folder(char *homebuf, size_t sz) +{ + static char szHomeDirBuf[MAX_PATH] = { 0 }; + // We need a process with query permission set + HANDLE hToken = 0; + DWORD result = + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); + DWORD BufSize = MAX_PATH; + + result = GetUserProfileDirectoryA(hToken, szHomeDirBuf, &BufSize); + // Close handle opened via OpenProcessToken + CloseHandle(hToken); + + return (result != 0); +} +static char *get_executable_path(void); + +static boolean path_buffer_set = FALSE; +static char path_buffer[MAX_PATH]; + +char * +get_executable_path(void) +{ +#ifdef UNICODE + { + TCHAR wbuf[BUFSZ]; + GetModuleFileName((HANDLE) 0, wbuf, BUFSZ); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, path_buffer, + sizeof(path_buffer), NULL, NULL); + } +#else + DWORD length = GetModuleFileName((HANDLE) 0, path_buffer, MAX_PATH); + if (length == ERROR_INSUFFICIENT_BUFFER) + error("Unable to get module name"); + path_buffer[length] = '\0'; +#endif + + char *seperator = strrchr(path_buffer, PATH_SEPARATOR); + if (seperator) + *seperator = '\0'; + + path_buffer_set = TRUE; + return path_buffer; +} + +char * +windows_exepath(void) +{ + char *p = (char *) 0; + + if (path_buffer_set) + p = path_buffer; + return p; +} + +char * +translate_path_variables(const char *str, char *buf) +{ + const char *src; + char evar[BUFSZ], *dest, *envp, *eptr = (char *) 0; + boolean in_evar; + size_t ccount, ecount, destcount, slen = str ? strlen(str) : 0; + + if (!slen || !buf) { + if (buf) + *buf = '\0'; + return buf; + } + + dest = buf; + src = str; + in_evar = FALSE; + destcount = ecount = 0; + for (ccount = 0; + ccount < slen && destcount < (BUFSZ - 1) && ecount < (BUFSZ - 1); + ++ccount, ++src) { + if (*src == '%') { + if (in_evar) { + *eptr = '\0'; + envp = nh_getenv(evar); + if (envp) { + size_t elen = strlen(envp); + + if ((elen + destcount) < (size_t) (BUFSZ - 1)) { + Strcpy(dest, envp); + dest += elen; + destcount += elen; + } + } + } else { + eptr = evar; + ecount = 0; + } + in_evar = !in_evar; + continue; + } + if (in_evar) { + *eptr++ = *src; + ecount++; + } else { + *dest++ = *src; + destcount++; + } + } + *dest = '\0'; + return buf; +} + +DISABLE_WARNING_UNREACHABLE_CODE + +int +get_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size) +{ + PWSTR wide_path; + if (FAILED(SHGetKnownFolderPath(folder_id, 0, NULL, &wide_path))) { + error("Unable to get known folder path"); + return FALSE; + } + + size_t converted; + errno_t err; + + err = wcstombs_s(&converted, path, path_size, wide_path, _TRUNCATE); + + CoTaskMemFree(wide_path); + + if (err == STRUNCATE || err == EILSEQ) { + // silently handle this problem + return FALSE; + } else if (err != 0) { + error( + "Failed folder (%lu) path string conversion, unexpected err = %d", + folder_id->Data1, err); + return FALSE; + } + + return TRUE; +} + +void +create_directory(const char *path) +{ + BOOL dres = CreateDirectoryA(path, NULL); + + if (!dres) { + DWORD dw = GetLastError(); + + if (dw != ERROR_ALREADY_EXISTS) + error("Unable to create directory '%s'", path); + } +} + +RESTORE_WARNING_UNREACHABLE_CODE + +int +build_known_folder_path(const KNOWNFOLDERID *folder_id, char *path, + size_t path_size, boolean versioned) +{ + if (!get_known_folder_path(folder_id, path, path_size)) + return FALSE; + + strcat(path, "\\NetHack\\"); + create_directory(path); + if (versioned) { + Sprintf(eos(path), "%d.%d\\", VERSION_MAJOR, VERSION_MINOR); + create_directory(path); + } + return TRUE; +} + +void +build_environment_path(const char *env_str, const char *folder, char *path, + size_t path_size) +{ + path[0] = '\0'; + + const char *root_path = nh_getenv(env_str); + + if (root_path == NULL) + return; + + strcpy_s(path, path_size, root_path); + + char *colon = strchr(path, ';'); + if (colon != NULL) + path[0] = '\0'; + + if (strlen(path) == 0) + return; + + append_slash(path); + + if (folder != NULL) { + strcat_s(path, path_size, folder); + strcat_s(path, path_size, "\\"); + } +} + +boolean +folder_file_exists(const char *folder, const char *file_name) +{ + char path[MAX_PATH]; + + if (folder[0] == '\0') + return FALSE; + + strcpy(path, folder); + strcat(path, file_name); + return file_exists(path); +} + +boolean +file_exists(const char *path) +{ + struct stat sb; + + /* Just see if it's there */ + if (stat(path, &sb)) { + return FALSE; + } + return TRUE; +} + +void +set_default_prefix_locations(const char *programPath UNUSED) +{ + static char executable_path[MAX_PATH]; + static char profile_path[MAX_PATH]; + static char versioned_profile_path[MAX_PATH]; + static char versioned_user_data_path[MAX_PATH]; + static char versioned_global_data_path[MAX_PATH]; + /* static char versioninfo[20] UNUSED; */ + + strcpy(executable_path, get_executable_path()); + append_slash(executable_path); + + if (test_portable_config(executable_path, portable_device_path, + sizeof portable_device_path)) { + gf.fqn_prefix[SYSCONFPREFIX] = executable_path; + gf.fqn_prefix[CONFIGPREFIX] = portable_device_path; + gf.fqn_prefix[HACKPREFIX] = portable_device_path; + gf.fqn_prefix[SAVEPREFIX] = portable_device_path; + gf.fqn_prefix[LEVELPREFIX] = portable_device_path; + gf.fqn_prefix[BONESPREFIX] = portable_device_path; + gf.fqn_prefix[SCOREPREFIX] = portable_device_path; + gf.fqn_prefix[LOCKPREFIX] = portable_device_path; + gf.fqn_prefix[TROUBLEPREFIX] = portable_device_path; + gf.fqn_prefix[DATAPREFIX] = executable_path; + } else { + if (!build_known_folder_path(&FOLDERID_Profile, profile_path, + sizeof(profile_path), FALSE)) + strcpy(profile_path, executable_path); + + if (!build_known_folder_path(&FOLDERID_Profile, + versioned_profile_path, + sizeof(profile_path), TRUE)) + strcpy(versioned_profile_path, executable_path); + + if (!build_known_folder_path(&FOLDERID_LocalAppData, + versioned_user_data_path, + sizeof(versioned_user_data_path), TRUE)) + strcpy(versioned_user_data_path, executable_path); + + if (!build_known_folder_path( + &FOLDERID_ProgramData, versioned_global_data_path, + sizeof(versioned_global_data_path), TRUE)) + strcpy(versioned_global_data_path, executable_path); + + gf.fqn_prefix[SYSCONFPREFIX] = versioned_global_data_path; + gf.fqn_prefix[CONFIGPREFIX] = profile_path; + gf.fqn_prefix[HACKPREFIX] = versioned_profile_path; + gf.fqn_prefix[SAVEPREFIX] = versioned_user_data_path; + gf.fqn_prefix[LEVELPREFIX] = versioned_user_data_path; + gf.fqn_prefix[BONESPREFIX] = versioned_global_data_path; + gf.fqn_prefix[SCOREPREFIX] = versioned_global_data_path; + gf.fqn_prefix[LOCKPREFIX] = versioned_global_data_path; + gf.fqn_prefix[TROUBLEPREFIX] = versioned_profile_path; + gf.fqn_prefix[DATAPREFIX] = executable_path; + } +} + +/* + * Add a backslash to any name not ending in /, \ or : There must + * be room for the \ + */ +void +append_slash(char *name) +{ + char *ptr; + + if (!*name) + return; + ptr = name + (strlen(name) - 1); + if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { + *++ptr = '\\'; + *++ptr = '\0'; + } + return; +} + +void set_default_prefix_locations(const char *programPath); +boolean +test_portable_config(const char *executable_path, char *portable_device_path, + size_t portable_device_path_size) +{ + int lth = 0; + const char *sysconf = "sysconf"; + char tmppath[MAX_PATH]; + boolean retval = FALSE, + save_initoptions_noterminate = iflags.initoptions_noterminate; + + if (portable_device_path + && folder_file_exists(executable_path, "sysconf")) { + /* + There is a sysconf file (not just sysconf.template) present in + the exe path, which is not the way NetHack is initially + distributed, so assume it means that the admin/installer wants to + override something, perhaps set up for a fully-portable + configuration that leaves no traces behind elsewhere on this + computer's hard drive - delve into that... + */ + + *portable_device_path = '\0'; + lth = sizeof tmppath - strlen(sysconf); + (void) strncpy(tmppath, executable_path, lth - 1); + tmppath[lth - 1] = '\0'; + (void) strcat(tmppath, sysconf); + + iflags.initoptions_noterminate = 1; + /* assure_syscf_file(); */ + config_error_init(TRUE, tmppath, FALSE); + /* ... and _must_ parse correctly. */ + if (read_config_file(tmppath, set_in_sysconf) + && sysopt.portable_device_paths) + retval = TRUE; + (void) config_error_done(); + iflags.initoptions_noterminate = save_initoptions_noterminate; + sysopt_release(); /* the real sysconf processing comes later */ + } + if (retval) { + lth = strlen(executable_path); + if (lth <= (int) portable_device_path_size - 1) + Strcpy(portable_device_path, executable_path); + else + retval = FALSE; + } + return retval; +} + +const char * +get_portable_device(void) +{ + return (const char *) portable_device_path; +} + /* Windows helpers for CRASHREPORT etc */ #ifdef CRASHREPORT struct CRctxt { diff --git a/sys/windows/winos.h b/sys/windows/winos.h index 132b153e0..aed062035 100644 --- a/sys/windows/winos.h +++ b/sys/windows/winos.h @@ -7,6 +7,10 @@ #include "win32api.h" +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR '\\' +#endif + extern const WCHAR cp437[256]; WCHAR *