2013-03-22 08:42:09 +04:00
|
|
|
/**
|
|
|
|
* WinPR: Windows Portable Runtime
|
|
|
|
* Path Functions
|
|
|
|
*
|
|
|
|
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
2016-12-01 00:47:06 +03:00
|
|
|
* Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2022-02-16 12:08:00 +03:00
|
|
|
#include <winpr/config.h>
|
2013-03-22 08:42:09 +04:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include <winpr/crt.h>
|
2016-08-10 12:06:34 +03:00
|
|
|
#include <winpr/platform.h>
|
2015-06-03 12:47:40 +03:00
|
|
|
#include <winpr/file.h>
|
2013-03-22 08:42:09 +04:00
|
|
|
#include <winpr/tchar.h>
|
|
|
|
#include <winpr/environment.h>
|
|
|
|
|
|
|
|
#include <winpr/path.h>
|
2023-03-24 09:54:18 +03:00
|
|
|
#include <winpr/wlog.h>
|
|
|
|
|
|
|
|
#include "../log.h"
|
|
|
|
#define TAG WINPR_TAG("path.shell")
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2016-08-05 13:05:08 +03:00
|
|
|
#if defined(__IOS__)
|
|
|
|
#include "shell_ios.h"
|
|
|
|
#endif
|
|
|
|
|
2015-06-02 10:02:29 +03:00
|
|
|
#if defined(WIN32)
|
2019-01-09 11:36:28 +03:00
|
|
|
#include <shlobj.h>
|
2016-06-13 20:19:28 +03:00
|
|
|
#else
|
|
|
|
#include <errno.h>
|
2016-12-01 00:47:06 +03:00
|
|
|
#include <dirent.h>
|
2015-06-02 10:02:29 +03:00
|
|
|
#endif
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
static char* GetPath_XDG_CONFIG_HOME(void);
|
|
|
|
static char* GetPath_XDG_RUNTIME_DIR(void);
|
2015-06-02 10:02:29 +03:00
|
|
|
|
2013-03-22 08:42:09 +04:00
|
|
|
/**
|
|
|
|
* SHGetKnownFolderPath function:
|
|
|
|
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* XDG Base Directory Specification:
|
|
|
|
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
|
|
|
*/
|
|
|
|
|
2021-05-25 20:48:50 +03:00
|
|
|
char* GetEnvAlloc(LPCSTR lpName)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
2021-05-25 20:48:50 +03:00
|
|
|
DWORD nSize;
|
|
|
|
DWORD nStatus;
|
2013-03-22 08:42:09 +04:00
|
|
|
char* env = NULL;
|
|
|
|
|
2021-05-25 20:48:50 +03:00
|
|
|
nSize = GetEnvironmentVariableX(lpName, NULL, 0);
|
|
|
|
|
|
|
|
if (nSize > 0)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
2021-05-25 20:48:50 +03:00
|
|
|
env = malloc(nSize);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-03-23 19:25:23 +03:00
|
|
|
if (!env)
|
|
|
|
return NULL;
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2021-05-25 20:48:50 +03:00
|
|
|
nStatus = GetEnvironmentVariableX(lpName, env, nSize);
|
|
|
|
|
|
|
|
if (nStatus != (nSize - 1))
|
2017-03-03 14:37:27 +03:00
|
|
|
{
|
|
|
|
free(env);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-22 08:42:09 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return env;
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
static char* GetPath_HOME(void)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
|
|
|
char* path = NULL;
|
|
|
|
#ifdef _WIN32
|
|
|
|
path = GetEnvAlloc("UserProfile");
|
2016-08-05 13:05:08 +03:00
|
|
|
#elif defined(__IOS__)
|
|
|
|
path = ios_get_home();
|
2013-03-22 08:42:09 +04:00
|
|
|
#else
|
|
|
|
path = GetEnvAlloc("HOME");
|
|
|
|
#endif
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
static char* GetPath_TEMP(void)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
|
|
|
char* path = NULL;
|
|
|
|
#ifdef _WIN32
|
|
|
|
path = GetEnvAlloc("TEMP");
|
2016-08-05 13:05:08 +03:00
|
|
|
#elif defined(__IOS__)
|
2017-02-20 16:28:33 +03:00
|
|
|
path = ios_get_temp();
|
2013-03-22 08:42:09 +04:00
|
|
|
#else
|
|
|
|
path = GetEnvAlloc("TMPDIR");
|
|
|
|
|
|
|
|
if (!path)
|
|
|
|
path = _strdup("/tmp");
|
|
|
|
|
2016-08-05 13:05:08 +03:00
|
|
|
#endif
|
2013-03-22 08:42:09 +04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
static char* GetPath_XDG_DATA_HOME(void)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
|
|
|
char* path = NULL;
|
2017-10-07 00:55:34 +03:00
|
|
|
#if defined(WIN32) || defined(__IOS__)
|
2015-06-02 10:02:29 +03:00
|
|
|
path = GetPath_XDG_CONFIG_HOME();
|
|
|
|
#else
|
2018-11-14 16:59:58 +03:00
|
|
|
size_t size;
|
2015-06-02 10:02:29 +03:00
|
|
|
char* home = NULL;
|
2013-03-22 08:42:09 +04:00
|
|
|
/**
|
2019-11-06 17:24:51 +03:00
|
|
|
* There is a single base directory relative to which user-specific data files should be
|
|
|
|
* written. This directory is defined by the environment variable $XDG_DATA_HOME.
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* $XDG_DATA_HOME defines the base directory relative to which user specific data files should
|
|
|
|
* be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to
|
|
|
|
* $HOME/.local/share should be used.
|
2013-03-22 08:42:09 +04:00
|
|
|
*/
|
|
|
|
path = GetEnvAlloc("XDG_DATA_HOME");
|
|
|
|
|
|
|
|
if (path)
|
|
|
|
return path;
|
|
|
|
|
|
|
|
home = GetPath_HOME();
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-03-25 19:37:46 +03:00
|
|
|
if (!home)
|
|
|
|
return NULL;
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
size = strlen(home) + strlen("/.local/share") + 1;
|
2019-11-06 17:24:51 +03:00
|
|
|
path = (char*)malloc(size);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-03-25 19:37:46 +03:00
|
|
|
if (!path)
|
|
|
|
{
|
|
|
|
free(home);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
sprintf_s(path, size, "%s%s", home, "/.local/share");
|
2013-03-22 08:42:09 +04:00
|
|
|
free(home);
|
2015-06-02 10:02:29 +03:00
|
|
|
#endif
|
2013-03-22 08:42:09 +04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
static char* GetPath_XDG_CONFIG_HOME(void)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
|
|
|
char* path = NULL;
|
2016-02-06 00:28:45 +03:00
|
|
|
#if defined(WIN32) && !defined(_UWP)
|
2015-06-02 10:02:29 +03:00
|
|
|
path = calloc(MAX_PATH, sizeof(char));
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-02 10:02:29 +03:00
|
|
|
if (!path)
|
|
|
|
return NULL;
|
|
|
|
|
2016-02-06 00:28:45 +03:00
|
|
|
if (FAILED(SHGetFolderPathA(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
|
2015-06-02 10:02:29 +03:00
|
|
|
{
|
|
|
|
free(path);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-08-05 13:05:08 +03:00
|
|
|
|
|
|
|
#elif defined(__IOS__)
|
2017-10-07 00:55:34 +03:00
|
|
|
path = ios_get_data();
|
2015-06-02 10:02:29 +03:00
|
|
|
#else
|
2018-11-14 16:59:58 +03:00
|
|
|
size_t size;
|
2015-06-02 10:02:29 +03:00
|
|
|
char* home = NULL;
|
2013-03-22 08:42:09 +04:00
|
|
|
/**
|
2019-11-06 17:24:51 +03:00
|
|
|
* There is a single base directory relative to which user-specific configuration files should
|
|
|
|
* be written. This directory is defined by the environment variable $XDG_CONFIG_HOME.
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration
|
|
|
|
* files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to
|
|
|
|
* $HOME/.config should be used.
|
2013-03-22 08:42:09 +04:00
|
|
|
*/
|
|
|
|
path = GetEnvAlloc("XDG_CONFIG_HOME");
|
|
|
|
|
|
|
|
if (path)
|
|
|
|
return path;
|
|
|
|
|
|
|
|
home = GetPath_HOME();
|
|
|
|
|
2013-04-24 02:17:01 +04:00
|
|
|
if (!home)
|
|
|
|
home = GetPath_TEMP();
|
|
|
|
|
2015-03-25 19:37:46 +03:00
|
|
|
if (!home)
|
|
|
|
return NULL;
|
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
size = strlen(home) + strlen("/.config") + 1;
|
2019-11-06 17:24:51 +03:00
|
|
|
path = (char*)malloc(size);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-03-23 19:25:23 +03:00
|
|
|
if (!path)
|
|
|
|
{
|
|
|
|
free(home);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
sprintf_s(path, size, "%s%s", home, "/.config");
|
2013-03-22 08:42:09 +04:00
|
|
|
free(home);
|
2015-06-02 10:02:29 +03:00
|
|
|
#endif
|
2013-03-22 08:42:09 +04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
static char* GetPath_XDG_CACHE_HOME(void)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
|
|
|
char* path = NULL;
|
|
|
|
char* home = NULL;
|
2015-06-02 10:02:29 +03:00
|
|
|
#if defined(WIN32)
|
|
|
|
home = GetPath_XDG_RUNTIME_DIR();
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-02 11:10:13 +03:00
|
|
|
if (home)
|
|
|
|
{
|
|
|
|
path = GetCombinedPath(home, "cache");
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2021-05-31 12:42:03 +03:00
|
|
|
if (!winpr_PathFileExists(path))
|
2015-06-02 11:10:13 +03:00
|
|
|
if (!CreateDirectoryA(path, NULL))
|
|
|
|
path = NULL;
|
|
|
|
}
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-02 10:02:29 +03:00
|
|
|
free(home);
|
2017-10-07 00:55:34 +03:00
|
|
|
#elif defined(__IOS__)
|
|
|
|
path = ios_get_cache();
|
2015-06-02 10:02:29 +03:00
|
|
|
#else
|
2018-11-14 16:59:58 +03:00
|
|
|
size_t size;
|
2013-03-22 08:42:09 +04:00
|
|
|
/**
|
2019-11-06 17:24:51 +03:00
|
|
|
* There is a single base directory relative to which user-specific non-essential (cached) data
|
|
|
|
* should be written. This directory is defined by the environment variable $XDG_CACHE_HOME.
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data
|
|
|
|
* files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to
|
|
|
|
* $HOME/.cache should be used.
|
2013-03-22 08:42:09 +04:00
|
|
|
*/
|
|
|
|
path = GetEnvAlloc("XDG_CACHE_HOME");
|
|
|
|
|
|
|
|
if (path)
|
|
|
|
return path;
|
|
|
|
|
|
|
|
home = GetPath_HOME();
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-04-03 17:21:01 +03:00
|
|
|
if (!home)
|
|
|
|
return NULL;
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
size = strlen(home) + strlen("/.cache") + 1;
|
2019-11-06 17:24:51 +03:00
|
|
|
path = (char*)malloc(size);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-04-03 17:21:01 +03:00
|
|
|
if (!path)
|
|
|
|
{
|
|
|
|
free(home);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
sprintf_s(path, size, "%s%s", home, "/.cache");
|
2013-03-22 08:42:09 +04:00
|
|
|
free(home);
|
2015-06-02 10:02:29 +03:00
|
|
|
#endif
|
2013-03-22 08:42:09 +04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2015-06-02 10:45:46 +03:00
|
|
|
char* GetPath_XDG_RUNTIME_DIR(void)
|
2013-03-22 08:42:09 +04:00
|
|
|
{
|
|
|
|
char* path = NULL;
|
2016-02-06 00:28:45 +03:00
|
|
|
#if defined(WIN32) && !defined(_UWP)
|
2015-06-02 10:02:29 +03:00
|
|
|
path = calloc(MAX_PATH, sizeof(char));
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-02 10:02:29 +03:00
|
|
|
if (!path)
|
|
|
|
return NULL;
|
2013-03-22 08:42:09 +04:00
|
|
|
|
2019-11-06 17:24:51 +03:00
|
|
|
if (FAILED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
|
2015-06-02 10:02:29 +03:00
|
|
|
{
|
|
|
|
free(path);
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-02 10:02:29 +03:00
|
|
|
#else
|
2013-03-22 08:42:09 +04:00
|
|
|
/**
|
2019-11-06 17:24:51 +03:00
|
|
|
* There is a single base directory relative to which user-specific runtime files and other file
|
|
|
|
* objects should be placed. This directory is defined by the environment variable
|
|
|
|
* $XDG_RUNTIME_DIR.
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential
|
|
|
|
* runtime files and other file objects (such as sockets, named pipes, ...) should be stored.
|
|
|
|
* The directory MUST be owned by the user, and he MUST be the only one having read and write
|
|
|
|
* access to it. Its Unix access mode MUST be 0700.
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* The lifetime of the directory MUST be bound to the user being logged in. It MUST be created
|
|
|
|
* when the user first logs in and if the user fully logs out the directory MUST be removed. If
|
|
|
|
* the user logs in more than once he should get pointed to the same directory, and it is
|
|
|
|
* mandatory that the directory continues to exist from his first login to his last logout on
|
|
|
|
* the system, and not removed in between. Files in the directory MUST not survive reboot or a
|
2013-03-22 08:42:09 +04:00
|
|
|
* full logout/login cycle.
|
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* The directory MUST be on a local file system and not shared with any other system. The
|
|
|
|
* directory MUST by fully-featured by the standards of the operating system. More specifically,
|
|
|
|
* on Unix-like operating systems AF_UNIX sockets, symbolic links, hard links, proper
|
|
|
|
* permissions, file locking, sparse files, memory mapping, file change notifications, a
|
|
|
|
* reliable hard link count must be supported, and no restrictions on the file name character
|
|
|
|
* set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To
|
|
|
|
* ensure that your files are not removed, they should have their access time timestamp modified
|
|
|
|
* at least once every 6 hours of monotonic time or the 'sticky' bit should be set on the file.
|
2013-03-22 08:42:09 +04:00
|
|
|
*
|
2019-11-06 17:24:51 +03:00
|
|
|
* If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with
|
|
|
|
* similar capabilities and print a warning message. Applications should use this directory for
|
|
|
|
* communication and synchronization purposes and should not place larger files in it, since it
|
|
|
|
* might reside in runtime memory and cannot necessarily be swapped out to disk.
|
2013-03-22 08:42:09 +04:00
|
|
|
*/
|
|
|
|
path = GetEnvAlloc("XDG_RUNTIME_DIR");
|
2015-06-02 10:02:29 +03:00
|
|
|
#endif
|
2013-03-22 08:42:09 +04:00
|
|
|
|
|
|
|
if (path)
|
|
|
|
return path;
|
|
|
|
|
|
|
|
path = GetPath_TEMP();
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* GetKnownPath(int id)
|
|
|
|
{
|
|
|
|
char* path = NULL;
|
|
|
|
|
|
|
|
switch (id)
|
|
|
|
{
|
|
|
|
case KNOWN_PATH_HOME:
|
|
|
|
path = GetPath_HOME();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KNOWN_PATH_TEMP:
|
|
|
|
path = GetPath_TEMP();
|
|
|
|
break;
|
|
|
|
|
2013-03-23 00:03:42 +04:00
|
|
|
case KNOWN_PATH_XDG_DATA_HOME:
|
2013-03-22 08:42:09 +04:00
|
|
|
path = GetPath_XDG_DATA_HOME();
|
|
|
|
break;
|
|
|
|
|
2013-03-23 00:03:42 +04:00
|
|
|
case KNOWN_PATH_XDG_CONFIG_HOME:
|
2013-03-22 08:42:09 +04:00
|
|
|
path = GetPath_XDG_CONFIG_HOME();
|
|
|
|
break;
|
|
|
|
|
2013-03-23 00:03:42 +04:00
|
|
|
case KNOWN_PATH_XDG_CACHE_HOME:
|
2013-03-22 08:42:09 +04:00
|
|
|
path = GetPath_XDG_CACHE_HOME();
|
|
|
|
break;
|
|
|
|
|
2013-03-23 00:03:42 +04:00
|
|
|
case KNOWN_PATH_XDG_RUNTIME_DIR:
|
2013-03-22 08:42:09 +04:00
|
|
|
path = GetPath_XDG_RUNTIME_DIR();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
path = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-03-24 09:54:18 +03:00
|
|
|
if (!path)
|
|
|
|
WLog_WARN(TAG, "Path %s is %p", GetKnownPathIdString(id), path);
|
2013-03-22 08:42:09 +04:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2014-04-16 18:53:41 +04:00
|
|
|
char* GetKnownSubPath(int id, const char* path)
|
2013-03-22 23:52:43 +04:00
|
|
|
{
|
|
|
|
char* subPath;
|
|
|
|
char* knownPath;
|
|
|
|
knownPath = GetKnownPath(id);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-03-23 19:25:23 +03:00
|
|
|
if (!knownPath)
|
|
|
|
return NULL;
|
2013-03-22 23:52:43 +04:00
|
|
|
|
2016-08-05 13:05:08 +03:00
|
|
|
subPath = GetCombinedPath(knownPath, path);
|
2013-03-22 23:52:43 +04:00
|
|
|
free(knownPath);
|
|
|
|
return subPath;
|
|
|
|
}
|
|
|
|
|
2014-07-18 05:15:22 +04:00
|
|
|
char* GetEnvironmentPath(char* name)
|
|
|
|
{
|
2014-09-12 02:36:29 +04:00
|
|
|
char* env = NULL;
|
2014-07-18 05:15:22 +04:00
|
|
|
DWORD nSize;
|
2021-05-25 20:48:50 +03:00
|
|
|
DWORD nStatus;
|
|
|
|
nSize = GetEnvironmentVariableX(name, NULL, 0);
|
2014-07-18 05:15:22 +04:00
|
|
|
|
|
|
|
if (nSize)
|
|
|
|
{
|
2019-11-06 17:24:51 +03:00
|
|
|
env = (LPSTR)malloc(nSize);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-04-03 17:21:01 +03:00
|
|
|
if (!env)
|
|
|
|
return NULL;
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2021-05-25 20:48:50 +03:00
|
|
|
nStatus = GetEnvironmentVariableX(name, env, nSize);
|
|
|
|
|
|
|
|
if (nStatus != (nSize - 1))
|
2017-02-20 16:28:33 +03:00
|
|
|
{
|
|
|
|
free(env);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-07-18 05:15:22 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return env;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* GetEnvironmentSubPath(char* name, const char* path)
|
|
|
|
{
|
|
|
|
char* env;
|
|
|
|
char* subpath;
|
|
|
|
env = GetEnvironmentPath(name);
|
|
|
|
|
|
|
|
if (!env)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
subpath = GetCombinedPath(env, path);
|
|
|
|
free(env);
|
|
|
|
return subpath;
|
|
|
|
}
|
|
|
|
|
2013-11-10 08:38:22 +04:00
|
|
|
char* GetCombinedPath(const char* basePath, const char* subPath)
|
2013-03-22 23:52:43 +04:00
|
|
|
{
|
2021-09-17 09:52:19 +03:00
|
|
|
size_t length;
|
2013-03-22 23:52:43 +04:00
|
|
|
HRESULT status;
|
|
|
|
char* path = NULL;
|
2021-09-17 09:52:19 +03:00
|
|
|
char* subPathCpy = NULL;
|
2021-09-21 10:56:56 +03:00
|
|
|
size_t basePathLength = 0;
|
|
|
|
size_t subPathLength = 0;
|
2013-03-22 23:52:43 +04:00
|
|
|
|
2013-08-29 12:46:44 +04:00
|
|
|
if (basePath)
|
2021-09-17 09:52:19 +03:00
|
|
|
basePathLength = strlen(basePath);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2013-08-29 12:46:44 +04:00
|
|
|
if (subPath)
|
2021-09-17 09:52:19 +03:00
|
|
|
subPathLength = strlen(subPath);
|
2013-03-22 23:52:43 +04:00
|
|
|
|
|
|
|
length = basePathLength + subPathLength + 1;
|
2021-09-17 09:52:19 +03:00
|
|
|
path = (char*)calloc(1, length + 1);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2013-08-29 17:30:22 +04:00
|
|
|
if (!path)
|
2021-09-17 09:52:19 +03:00
|
|
|
goto fail;
|
2013-03-22 23:52:43 +04:00
|
|
|
|
2013-08-30 16:19:50 +04:00
|
|
|
if (basePath)
|
|
|
|
CopyMemory(path, basePath, basePathLength);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-22 19:52:13 +03:00
|
|
|
if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
|
2021-09-17 09:52:19 +03:00
|
|
|
goto fail;
|
2013-03-22 23:52:43 +04:00
|
|
|
|
|
|
|
if (!subPath)
|
|
|
|
return path;
|
|
|
|
|
2013-08-29 12:46:44 +04:00
|
|
|
subPathCpy = _strdup(subPath);
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-17 23:08:02 +03:00
|
|
|
if (!subPathCpy)
|
2021-09-17 09:52:19 +03:00
|
|
|
goto fail;
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2015-06-22 19:52:13 +03:00
|
|
|
if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
|
2021-09-17 09:52:19 +03:00
|
|
|
goto fail;
|
2013-03-22 23:52:43 +04:00
|
|
|
|
2013-08-29 12:46:44 +04:00
|
|
|
status = NativePathCchAppendA(path, length + 1, subPathCpy);
|
2021-09-17 09:52:19 +03:00
|
|
|
if (FAILED(status))
|
|
|
|
goto fail;
|
|
|
|
|
2013-08-29 12:46:44 +04:00
|
|
|
free(subPathCpy);
|
2021-09-17 09:52:19 +03:00
|
|
|
return path;
|
2013-03-22 23:52:43 +04:00
|
|
|
|
2021-09-17 09:52:19 +03:00
|
|
|
fail:
|
|
|
|
free(path);
|
|
|
|
free(subPathCpy);
|
|
|
|
return NULL;
|
2013-03-22 23:52:43 +04:00
|
|
|
}
|
|
|
|
|
2015-06-03 12:47:40 +03:00
|
|
|
BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
|
|
|
|
{
|
2016-12-02 21:18:55 +03:00
|
|
|
#if defined(_UWP)
|
|
|
|
return FALSE;
|
|
|
|
#elif defined(_WIN32)
|
2016-06-13 20:19:28 +03:00
|
|
|
return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS);
|
|
|
|
#else
|
|
|
|
const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
|
|
|
|
char* dup;
|
|
|
|
char* p;
|
2017-07-12 21:47:08 +03:00
|
|
|
BOOL result = TRUE;
|
2016-06-13 20:19:28 +03:00
|
|
|
/* we only operate on a non-null, absolute path */
|
2019-01-18 15:13:41 +03:00
|
|
|
#if defined(__OS2__)
|
|
|
|
|
|
|
|
if (!path)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2016-06-13 20:19:28 +03:00
|
|
|
if (!path || *path != delim)
|
2015-06-03 12:47:40 +03:00
|
|
|
return FALSE;
|
|
|
|
|
2019-01-18 15:13:41 +03:00
|
|
|
#endif
|
|
|
|
|
2016-06-13 20:19:28 +03:00
|
|
|
if (!(dup = _strdup(path)))
|
|
|
|
return FALSE;
|
2015-06-03 12:47:40 +03:00
|
|
|
|
2019-01-18 15:13:41 +03:00
|
|
|
#ifdef __OS2__
|
|
|
|
p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
|
|
|
|
|
|
|
|
while (p)
|
|
|
|
#else
|
2016-06-13 20:19:28 +03:00
|
|
|
for (p = dup; p;)
|
2019-01-18 15:13:41 +03:00
|
|
|
#endif
|
2019-11-06 17:24:51 +03:00
|
|
|
{
|
|
|
|
if ((p = strchr(p + 1, delim)))
|
|
|
|
*p = '\0';
|
2015-06-03 12:47:40 +03:00
|
|
|
|
2016-06-13 20:19:28 +03:00
|
|
|
if (mkdir(dup, 0777) != 0)
|
|
|
|
if (errno != EEXIST)
|
2017-07-12 21:47:08 +03:00
|
|
|
{
|
|
|
|
result = FALSE;
|
2015-06-03 12:47:40 +03:00
|
|
|
break;
|
2017-07-12 21:47:08 +03:00
|
|
|
}
|
2016-08-05 13:05:08 +03:00
|
|
|
|
2016-06-13 20:19:28 +03:00
|
|
|
if (p)
|
|
|
|
*p = delim;
|
2015-06-03 12:47:40 +03:00
|
|
|
}
|
|
|
|
|
2016-06-13 20:19:28 +03:00
|
|
|
free(dup);
|
2017-07-12 21:47:08 +03:00
|
|
|
return (result);
|
2016-06-13 20:19:28 +03:00
|
|
|
#endif
|
2015-06-03 12:47:40 +03:00
|
|
|
}
|
|
|
|
|
2021-01-27 17:03:29 +03:00
|
|
|
BOOL PathMakePathW(LPCWSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
|
|
|
|
{
|
|
|
|
#if defined(_UWP)
|
|
|
|
return FALSE;
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
return (SHCreateDirectoryExW(NULL, path, lpAttributes) == ERROR_SUCCESS);
|
|
|
|
#else
|
2022-11-15 12:52:34 +03:00
|
|
|
const WCHAR wdelim = PathGetSeparatorW(PATH_STYLE_NATIVE);
|
|
|
|
const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
|
2021-01-27 17:03:29 +03:00
|
|
|
char* dup;
|
|
|
|
char* p;
|
|
|
|
BOOL result = TRUE;
|
|
|
|
/* we only operate on a non-null, absolute path */
|
|
|
|
#if defined(__OS2__)
|
|
|
|
|
|
|
|
if (!path)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2022-11-15 12:52:34 +03:00
|
|
|
if (!path || *path != wdelim)
|
2021-01-27 17:03:29 +03:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
dup = ConvertWCharToUtf8Alloc(path, NULL);
|
|
|
|
if (!dup)
|
2021-01-27 17:03:29 +03:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
#ifdef __OS2__
|
2022-11-15 12:52:34 +03:00
|
|
|
p = (strlen(dup) > 3) && (dup[1] == ':') && (dup[2] == delim)) ? &dup[3] : dup;
|
2021-01-27 17:03:29 +03:00
|
|
|
|
|
|
|
while (p)
|
|
|
|
#else
|
|
|
|
for (p = dup; p;)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
if ((p = strchr(p + 1, delim)))
|
|
|
|
*p = '\0';
|
|
|
|
|
|
|
|
if (mkdir(dup, 0777) != 0)
|
|
|
|
if (errno != EEXIST)
|
|
|
|
{
|
|
|
|
result = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p)
|
|
|
|
*p = delim;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(dup);
|
|
|
|
return (result);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-03-25 19:20:51 +03:00
|
|
|
#if !defined(_WIN32) || defined(_UWP)
|
|
|
|
|
2019-09-24 16:44:34 +03:00
|
|
|
BOOL PathIsRelativeA(LPCSTR pszPath)
|
|
|
|
{
|
|
|
|
if (!pszPath)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return pszPath[0] != '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL PathIsRelativeW(LPCWSTR pszPath)
|
|
|
|
{
|
|
|
|
LPSTR lpFileNameA = NULL;
|
2022-10-28 09:09:27 +03:00
|
|
|
BOOL ret = FALSE;
|
2019-09-24 16:44:34 +03:00
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
if (!pszPath)
|
|
|
|
goto fail;
|
2019-09-24 16:44:34 +03:00
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
|
|
|
|
if (!lpFileNameA)
|
|
|
|
goto fail;
|
2019-09-24 16:44:34 +03:00
|
|
|
ret = PathIsRelativeA(lpFileNameA);
|
2022-10-28 09:09:27 +03:00
|
|
|
fail:
|
2019-09-24 16:44:34 +03:00
|
|
|
free(lpFileNameA);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-03-22 08:42:09 +04:00
|
|
|
BOOL PathFileExistsA(LPCSTR pszPath)
|
|
|
|
{
|
|
|
|
struct stat stat_info;
|
|
|
|
|
|
|
|
if (stat(pszPath, &stat_info) != 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL PathFileExistsW(LPCWSTR pszPath)
|
|
|
|
{
|
2016-12-01 00:47:06 +03:00
|
|
|
LPSTR lpFileNameA = NULL;
|
2022-10-28 09:09:27 +03:00
|
|
|
BOOL ret = FALSE;
|
2016-12-01 00:47:06 +03:00
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
if (!pszPath)
|
|
|
|
goto fail;
|
|
|
|
lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
|
|
|
|
if (!lpFileNameA)
|
|
|
|
goto fail;
|
2016-12-01 00:47:06 +03:00
|
|
|
|
2021-05-31 12:42:03 +03:00
|
|
|
ret = winpr_PathFileExists(lpFileNameA);
|
2022-10-28 09:09:27 +03:00
|
|
|
fail:
|
2018-08-24 11:39:48 +03:00
|
|
|
free(lpFileNameA);
|
2016-12-01 00:47:06 +03:00
|
|
|
return ret;
|
2013-03-22 08:42:09 +04:00
|
|
|
}
|
2016-03-25 19:20:51 +03:00
|
|
|
|
2016-12-01 00:47:06 +03:00
|
|
|
BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
|
|
|
|
{
|
2018-08-24 11:39:48 +03:00
|
|
|
struct dirent* dp;
|
2016-12-01 00:47:06 +03:00
|
|
|
int empty = 1;
|
2018-08-24 11:39:48 +03:00
|
|
|
DIR* dir = opendir(pszPath);
|
2016-12-01 00:47:06 +03:00
|
|
|
|
2017-03-18 00:05:21 +03:00
|
|
|
if (dir == NULL) /* Not a directory or doesn't exist */
|
2016-12-01 00:47:06 +03:00
|
|
|
return 1;
|
|
|
|
|
2018-08-24 11:39:48 +03:00
|
|
|
while ((dp = readdir(dir)) != NULL)
|
|
|
|
{
|
2016-12-01 00:47:06 +03:00
|
|
|
if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
|
2019-11-06 17:24:51 +03:00
|
|
|
continue; /* Skip . and .. */
|
2016-12-01 00:47:06 +03:00
|
|
|
|
|
|
|
empty = 0;
|
|
|
|
break;
|
|
|
|
}
|
2018-08-24 11:39:48 +03:00
|
|
|
|
2016-12-01 00:47:06 +03:00
|
|
|
closedir(dir);
|
|
|
|
return empty;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
|
|
|
|
{
|
|
|
|
LPSTR lpFileNameA = NULL;
|
2022-10-28 09:09:27 +03:00
|
|
|
BOOL ret = FALSE;
|
|
|
|
if (!pszPath)
|
|
|
|
goto fail;
|
|
|
|
lpFileNameA = ConvertWCharToUtf8Alloc(pszPath, NULL);
|
|
|
|
if (!lpFileNameA)
|
|
|
|
goto fail;
|
2016-12-01 00:47:06 +03:00
|
|
|
ret = PathIsDirectoryEmptyA(lpFileNameA);
|
2022-10-28 09:09:27 +03:00
|
|
|
fail:
|
2018-08-24 11:39:48 +03:00
|
|
|
free(lpFileNameA);
|
2016-12-01 00:47:06 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-30 17:58:36 +03:00
|
|
|
#else
|
|
|
|
|
2021-04-28 16:00:24 +03:00
|
|
|
#ifdef _MSC_VER
|
2016-03-30 17:58:36 +03:00
|
|
|
#pragma comment(lib, "shlwapi.lib")
|
|
|
|
#endif
|
|
|
|
|
2016-03-14 16:08:48 +03:00
|
|
|
#endif
|
2021-05-25 20:27:13 +03:00
|
|
|
|
|
|
|
BOOL winpr_MoveFile(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
|
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
|
|
|
return MoveFileA(lpExistingFileName, lpNewFileName);
|
|
|
|
#else
|
|
|
|
BOOL result = FALSE;
|
|
|
|
LPWSTR lpExistingFileNameW = NULL;
|
|
|
|
LPWSTR lpNewFileNameW = NULL;
|
|
|
|
|
|
|
|
if (!lpExistingFileName || !lpNewFileName)
|
|
|
|
return FALSE;
|
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL);
|
|
|
|
if (!lpExistingFileNameW)
|
2021-05-25 20:27:13 +03:00
|
|
|
goto cleanup;
|
2022-10-28 09:09:27 +03:00
|
|
|
lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL);
|
|
|
|
if (!lpNewFileNameW)
|
2021-05-25 20:27:13 +03:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
result = MoveFileW(lpExistingFileNameW, lpNewFileNameW);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
free(lpExistingFileNameW);
|
|
|
|
free(lpNewFileNameW);
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-10-25 14:45:44 +03:00
|
|
|
BOOL winpr_MoveFileEx(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
|
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
|
|
|
return MoveFileExA(lpExistingFileName, lpNewFileName, dwFlags);
|
|
|
|
#else
|
|
|
|
BOOL result = FALSE;
|
|
|
|
LPWSTR lpExistingFileNameW = NULL;
|
|
|
|
LPWSTR lpNewFileNameW = NULL;
|
|
|
|
|
|
|
|
if (!lpExistingFileName || !lpNewFileName)
|
|
|
|
return FALSE;
|
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
lpExistingFileNameW = ConvertUtf8ToWCharAlloc(lpExistingFileName, NULL);
|
|
|
|
if (!lpExistingFileNameW)
|
2022-10-25 14:45:44 +03:00
|
|
|
goto cleanup;
|
2022-10-28 09:09:27 +03:00
|
|
|
lpNewFileNameW = ConvertUtf8ToWCharAlloc(lpNewFileName, NULL);
|
|
|
|
if (!lpNewFileNameW)
|
2022-10-25 14:45:44 +03:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
result = MoveFileExW(lpExistingFileNameW, lpNewFileNameW, dwFlags);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
free(lpExistingFileNameW);
|
|
|
|
free(lpNewFileNameW);
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-05-25 20:27:13 +03:00
|
|
|
BOOL winpr_DeleteFile(const char* lpFileName)
|
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
|
|
|
return DeleteFileA(lpFileName);
|
|
|
|
#else
|
|
|
|
LPWSTR lpFileNameW = NULL;
|
|
|
|
BOOL result = FALSE;
|
|
|
|
|
|
|
|
if (lpFileName)
|
|
|
|
{
|
2022-10-28 09:09:27 +03:00
|
|
|
lpFileNameW = ConvertUtf8ToWCharAlloc(lpFileName, NULL);
|
|
|
|
if (!lpFileNameW)
|
2021-05-25 20:27:13 +03:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = DeleteFileW(lpFileNameW);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
free(lpFileNameW);
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL winpr_RemoveDirectory(LPCSTR lpPathName)
|
|
|
|
{
|
|
|
|
#ifndef _WIN32
|
|
|
|
return RemoveDirectoryA(lpPathName);
|
|
|
|
#else
|
|
|
|
LPWSTR lpPathNameW = NULL;
|
|
|
|
BOOL result = FALSE;
|
|
|
|
|
|
|
|
if (lpPathName)
|
|
|
|
{
|
2022-10-28 09:09:27 +03:00
|
|
|
lpPathNameW = ConvertUtf8ToWCharAlloc(lpPathName, NULL);
|
|
|
|
if (!lpPathNameW)
|
2021-05-25 20:27:13 +03:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = RemoveDirectoryW(lpPathNameW);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
free(lpPathNameW);
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL winpr_PathFileExists(const char* pszPath)
|
|
|
|
{
|
2022-10-28 09:09:27 +03:00
|
|
|
if (!pszPath)
|
|
|
|
return FALSE;
|
2021-05-25 20:27:13 +03:00
|
|
|
#ifndef _WIN32
|
|
|
|
return PathFileExistsA(pszPath);
|
|
|
|
#else
|
2022-10-28 09:09:27 +03:00
|
|
|
WCHAR* pathW = ConvertUtf8ToWCharAlloc(pszPath, NULL);
|
2021-05-25 20:27:13 +03:00
|
|
|
BOOL result = FALSE;
|
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
if (!pathW)
|
2021-05-25 20:27:13 +03:00
|
|
|
return FALSE;
|
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
result = PathFileExistsW(pathW);
|
|
|
|
free(pathW);
|
2021-05-25 20:27:13 +03:00
|
|
|
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL winpr_PathMakePath(const char* path, LPSECURITY_ATTRIBUTES lpAttributes)
|
|
|
|
{
|
2022-10-28 09:09:27 +03:00
|
|
|
if (!path)
|
|
|
|
return FALSE;
|
2021-05-25 20:27:13 +03:00
|
|
|
#ifndef _WIN32
|
|
|
|
return PathMakePathA(path, lpAttributes);
|
|
|
|
#else
|
2022-10-28 09:09:27 +03:00
|
|
|
WCHAR* pathW = ConvertUtf8ToWCharAlloc(path, NULL);
|
2021-05-25 20:27:13 +03:00
|
|
|
BOOL result = FALSE;
|
|
|
|
|
2022-10-28 09:09:27 +03:00
|
|
|
if (!pathW)
|
2021-05-25 20:27:13 +03:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
result = SHCreateDirectoryExW(NULL, pathW, lpAttributes) == ERROR_SUCCESS;
|
|
|
|
free(pathW);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
#endif
|
|
|
|
}
|