Blame winpr/libwinpr/path/shell.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * WinPR: Windows Portable Runtime
Packit 1fb8d4
 * Path Functions
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit 1fb8d4
 * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <stdio.h>
Packit 1fb8d4
#include <stdlib.h>
Packit 1fb8d4
#include <string.h>
Packit 1fb8d4
#include <sys/stat.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/crt.h>
Packit 1fb8d4
#include <winpr/platform.h>
Packit 1fb8d4
#include <winpr/heap.h>
Packit 1fb8d4
#include <winpr/file.h>
Packit 1fb8d4
#include <winpr/tchar.h>
Packit 1fb8d4
#include <winpr/environment.h>
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/path.h>
Packit 1fb8d4
Packit 1fb8d4
#if defined(__IOS__)
Packit 1fb8d4
#include "shell_ios.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#if defined(WIN32)
Packit 1fb8d4
#include <Shlobj.h>
Packit 1fb8d4
#else
Packit 1fb8d4
#include <errno.h>
Packit 1fb8d4
#include <dirent.h>
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
static char* GetPath_XDG_CONFIG_HOME(void);
Packit 1fb8d4
static char* GetPath_XDG_RUNTIME_DIR(void);
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * SHGetKnownFolderPath function:
Packit 1fb8d4
 * http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188/
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
/**
Packit 1fb8d4
 * XDG Base Directory Specification:
Packit 1fb8d4
 * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
static char* GetEnvAlloc(LPCSTR lpName)
Packit 1fb8d4
{
Packit 1fb8d4
	DWORD length;
Packit 1fb8d4
	char* env = NULL;
Packit 1fb8d4
	length = GetEnvironmentVariableA(lpName, NULL, 0);
Packit 1fb8d4
Packit 1fb8d4
	if (length > 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		env = malloc(length);
Packit 1fb8d4
Packit 1fb8d4
		if (!env)
Packit 1fb8d4
			return NULL;
Packit 1fb8d4
Packit 1fb8d4
		if (GetEnvironmentVariableA(lpName, env, length) != length - 1)
Packit 1fb8d4
		{
Packit 1fb8d4
			free(env);
Packit 1fb8d4
			return NULL;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return env;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static char* GetPath_HOME(void)
Packit 1fb8d4
{
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
	path = GetEnvAlloc("UserProfile");
Packit 1fb8d4
#elif defined(__IOS__)
Packit 1fb8d4
	path = ios_get_home();
Packit 1fb8d4
#else
Packit 1fb8d4
	path = GetEnvAlloc("HOME");
Packit 1fb8d4
#endif
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static char* GetPath_TEMP(void)
Packit 1fb8d4
{
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
	path = GetEnvAlloc("TEMP");
Packit 1fb8d4
#elif defined(__IOS__)
Packit 1fb8d4
	path = ios_get_temp();
Packit 1fb8d4
#else
Packit 1fb8d4
	path = GetEnvAlloc("TMPDIR");
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
		path = _strdup("/tmp");
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static char* GetPath_XDG_DATA_HOME(void)
Packit 1fb8d4
{
Packit 1fb8d4
	size_t size;
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
#if defined(WIN32) || defined(__IOS__)
Packit 1fb8d4
	path = GetPath_XDG_CONFIG_HOME();
Packit 1fb8d4
#else
Packit 1fb8d4
	char* home = NULL;
Packit 1fb8d4
	/**
Packit 1fb8d4
	 * There is a single base directory relative to which user-specific data files should be written.
Packit 1fb8d4
	 * This directory is defined by the environment variable $XDG_DATA_HOME.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
Packit 1fb8d4
	 * If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
Packit 1fb8d4
	 */
Packit 1fb8d4
	path = GetEnvAlloc("XDG_DATA_HOME");
Packit 1fb8d4
Packit 1fb8d4
	if (path)
Packit 1fb8d4
		return path;
Packit 1fb8d4
Packit 1fb8d4
	home = GetPath_HOME();
Packit 1fb8d4
Packit 1fb8d4
	if (!home)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	size = strlen(home) + strlen("/.local/share") + 1;
Packit 1fb8d4
	path = (char*) malloc(size);
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(home);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	sprintf_s(path, size, "%s%s", home, "/.local/share");
Packit 1fb8d4
	free(home);
Packit 1fb8d4
#endif
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static char* GetPath_XDG_CONFIG_HOME(void)
Packit 1fb8d4
{
Packit 1fb8d4
	size_t size;
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
#if defined(WIN32) && !defined(_UWP)
Packit 1fb8d4
	path = calloc(MAX_PATH, sizeof(char));
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (FAILED(SHGetFolderPathA(0, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, path)))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(path);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#elif defined(__IOS__)
Packit 1fb8d4
	path = ios_get_data();
Packit 1fb8d4
#else
Packit 1fb8d4
	char* home = NULL;
Packit 1fb8d4
	/**
Packit 1fb8d4
	 * There is a single base directory relative to which user-specific configuration files should be written.
Packit 1fb8d4
	 * This directory is defined by the environment variable $XDG_CONFIG_HOME.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored.
Packit 1fb8d4
	 * If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
Packit 1fb8d4
	 */
Packit 1fb8d4
	path = GetEnvAlloc("XDG_CONFIG_HOME");
Packit 1fb8d4
Packit 1fb8d4
	if (path)
Packit 1fb8d4
		return path;
Packit 1fb8d4
Packit 1fb8d4
	home = GetPath_HOME();
Packit 1fb8d4
Packit 1fb8d4
	if (!home)
Packit 1fb8d4
		home = GetPath_TEMP();
Packit 1fb8d4
Packit 1fb8d4
	if (!home)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	size = strlen(home) + strlen("/.config") + 1;
Packit 1fb8d4
	path = (char*) malloc(size);
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(home);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	sprintf_s(path, size, "%s%s", home, "/.config");
Packit 1fb8d4
	free(home);
Packit 1fb8d4
#endif
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static char* GetPath_XDG_CACHE_HOME(void)
Packit 1fb8d4
{
Packit 1fb8d4
	size_t size;
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
	char* home = NULL;
Packit 1fb8d4
#if defined(WIN32)
Packit 1fb8d4
	home = GetPath_XDG_RUNTIME_DIR();
Packit 1fb8d4
Packit 1fb8d4
	if (home)
Packit 1fb8d4
	{
Packit 1fb8d4
		path = GetCombinedPath(home, "cache");
Packit 1fb8d4
Packit 1fb8d4
		if (!PathFileExistsA(path))
Packit 1fb8d4
			if (!CreateDirectoryA(path, NULL))
Packit 1fb8d4
				path = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	free(home);
Packit 1fb8d4
#elif defined(__IOS__)
Packit 1fb8d4
	path = ios_get_cache();
Packit 1fb8d4
#else
Packit 1fb8d4
	/**
Packit 1fb8d4
	 * There is a single base directory relative to which user-specific non-essential (cached) data should be written.
Packit 1fb8d4
	 * This directory is defined by the environment variable $XDG_CACHE_HOME.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * $XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored.
Packit 1fb8d4
	 * If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache should be used.
Packit 1fb8d4
	 */
Packit 1fb8d4
	path = GetEnvAlloc("XDG_CACHE_HOME");
Packit 1fb8d4
Packit 1fb8d4
	if (path)
Packit 1fb8d4
		return path;
Packit 1fb8d4
Packit 1fb8d4
	home = GetPath_HOME();
Packit 1fb8d4
Packit 1fb8d4
	if (!home)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	size = strlen(home) + strlen("/.cache") + 1;
Packit 1fb8d4
	path = (char*) malloc(size);
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(home);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	sprintf_s(path, size, "%s%s", home, "/.cache");
Packit 1fb8d4
	free(home);
Packit 1fb8d4
#endif
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
char* GetPath_XDG_RUNTIME_DIR(void)
Packit 1fb8d4
{
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
#if defined(WIN32) && !defined(_UWP)
Packit 1fb8d4
	path = calloc(MAX_PATH, sizeof(char));
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (FAILED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
Packit 1fb8d4
	                            path)))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(path);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#else
Packit 1fb8d4
	/**
Packit 1fb8d4
	 * There is a single base directory relative to which user-specific runtime files and other file objects should be placed.
Packit 1fb8d4
	 * This directory is defined by the environment variable $XDG_RUNTIME_DIR.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * $XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and other
Packit 1fb8d4
	 * file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the user,
Packit 1fb8d4
	 * and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user first
Packit 1fb8d4
	 * logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than once he should
Packit 1fb8d4
	 * get pointed to the same directory, and it is mandatory that the directory continues to exist from his first login
Packit 1fb8d4
	 * to his last logout on the system, and not removed in between. Files in the directory MUST not survive reboot or a
Packit 1fb8d4
	 * full logout/login cycle.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * The directory MUST be on a local file system and not shared with any other system. The directory MUST by fully-featured
Packit 1fb8d4
	 * by the standards of the operating system. More specifically, on Unix-like operating systems AF_UNIX sockets,
Packit 1fb8d4
	 * symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping, file change notifications,
Packit 1fb8d4
	 * a reliable hard link count must be supported, and no restrictions on the file name character set should be imposed.
Packit 1fb8d4
	 * Files in this directory MAY be subjected to periodic clean-up. To ensure that your files are not removed, they should
Packit 1fb8d4
	 * have their access time timestamp modified at least once every 6 hours of monotonic time or the 'sticky' bit should be
Packit 1fb8d4
	 * set on the file.
Packit 1fb8d4
	 *
Packit 1fb8d4
	 * If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with similar capabilities and
Packit 1fb8d4
	 * print a warning message. Applications should use this directory for communication and synchronization purposes and
Packit 1fb8d4
	 * should not place larger files in it, since it might reside in runtime memory and cannot necessarily be swapped out to disk.
Packit 1fb8d4
	 */
Packit 1fb8d4
	path = GetEnvAlloc("XDG_RUNTIME_DIR");
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
	if (path)
Packit 1fb8d4
		return path;
Packit 1fb8d4
Packit 1fb8d4
	path = GetPath_TEMP();
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
char* GetKnownPath(int id)
Packit 1fb8d4
{
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
Packit 1fb8d4
	switch (id)
Packit 1fb8d4
	{
Packit 1fb8d4
		case KNOWN_PATH_HOME:
Packit 1fb8d4
			path = GetPath_HOME();
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case KNOWN_PATH_TEMP:
Packit 1fb8d4
			path = GetPath_TEMP();
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case KNOWN_PATH_XDG_DATA_HOME:
Packit 1fb8d4
			path = GetPath_XDG_DATA_HOME();
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case KNOWN_PATH_XDG_CONFIG_HOME:
Packit 1fb8d4
			path = GetPath_XDG_CONFIG_HOME();
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case KNOWN_PATH_XDG_CACHE_HOME:
Packit 1fb8d4
			path = GetPath_XDG_CACHE_HOME();
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		case KNOWN_PATH_XDG_RUNTIME_DIR:
Packit 1fb8d4
			path = GetPath_XDG_RUNTIME_DIR();
Packit 1fb8d4
			break;
Packit 1fb8d4
Packit 1fb8d4
		default:
Packit 1fb8d4
			path = NULL;
Packit 1fb8d4
			break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
char* GetKnownSubPath(int id, const char* path)
Packit 1fb8d4
{
Packit 1fb8d4
	char* subPath;
Packit 1fb8d4
	char* knownPath;
Packit 1fb8d4
	knownPath = GetKnownPath(id);
Packit 1fb8d4
Packit 1fb8d4
	if (!knownPath)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	subPath = GetCombinedPath(knownPath, path);
Packit 1fb8d4
	free(knownPath);
Packit 1fb8d4
	return subPath;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
char* GetEnvironmentPath(char* name)
Packit 1fb8d4
{
Packit 1fb8d4
	char* env = NULL;
Packit 1fb8d4
	DWORD nSize;
Packit 1fb8d4
	nSize = GetEnvironmentVariableA(name, NULL, 0);
Packit 1fb8d4
Packit 1fb8d4
	if (nSize)
Packit 1fb8d4
	{
Packit 1fb8d4
		env = (LPSTR) malloc(nSize);
Packit 1fb8d4
Packit 1fb8d4
		if (!env)
Packit 1fb8d4
			return NULL;
Packit 1fb8d4
Packit 1fb8d4
		if (GetEnvironmentVariableA(name, env, nSize) != nSize - 1)
Packit 1fb8d4
		{
Packit 1fb8d4
			free(env);
Packit 1fb8d4
			return NULL;
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return env;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
char* GetEnvironmentSubPath(char* name, const char* path)
Packit 1fb8d4
{
Packit 1fb8d4
	char* env;
Packit 1fb8d4
	char* subpath;
Packit 1fb8d4
	env = GetEnvironmentPath(name);
Packit 1fb8d4
Packit 1fb8d4
	if (!env)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	subpath = GetCombinedPath(env, path);
Packit 1fb8d4
	free(env);
Packit 1fb8d4
	return subpath;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
char* GetCombinedPath(const char* basePath, const char* subPath)
Packit 1fb8d4
{
Packit 1fb8d4
	int length;
Packit 1fb8d4
	HRESULT status;
Packit 1fb8d4
	char* path = NULL;
Packit 1fb8d4
	char* subPathCpy;
Packit 1fb8d4
	int basePathLength = 0;
Packit 1fb8d4
	int subPathLength = 0;
Packit 1fb8d4
Packit 1fb8d4
	if (basePath)
Packit 1fb8d4
		basePathLength = (int) strlen(basePath);
Packit 1fb8d4
Packit 1fb8d4
	if (subPath)
Packit 1fb8d4
		subPathLength = (int) strlen(subPath);
Packit 1fb8d4
Packit 1fb8d4
	length = basePathLength + subPathLength + 1;
Packit 1fb8d4
	path = (char*) malloc(length + 1);
Packit 1fb8d4
Packit 1fb8d4
	if (!path)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (basePath)
Packit 1fb8d4
		CopyMemory(path, basePath, basePathLength);
Packit 1fb8d4
Packit 1fb8d4
	path[basePathLength] = '\0';
Packit 1fb8d4
Packit 1fb8d4
	if (FAILED(PathCchConvertStyleA(path, basePathLength, PATH_STYLE_NATIVE)))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(path);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (!subPath)
Packit 1fb8d4
		return path;
Packit 1fb8d4
Packit 1fb8d4
	subPathCpy = _strdup(subPath);
Packit 1fb8d4
Packit 1fb8d4
	if (!subPathCpy)
Packit 1fb8d4
	{
Packit 1fb8d4
		free(path);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	if (FAILED(PathCchConvertStyleA(subPathCpy, subPathLength, PATH_STYLE_NATIVE)))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(path);
Packit 1fb8d4
		free(subPathCpy);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	status = NativePathCchAppendA(path, length + 1, subPathCpy);
Packit 1fb8d4
	free(subPathCpy);
Packit 1fb8d4
Packit 1fb8d4
	if (FAILED(status))
Packit 1fb8d4
	{
Packit 1fb8d4
		free(path);
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
		return path;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes)
Packit 1fb8d4
{
Packit 1fb8d4
#if defined(_UWP)
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
#elif defined(_WIN32)
Packit 1fb8d4
	return (SHCreateDirectoryExA(NULL, path, lpAttributes) == ERROR_SUCCESS);
Packit 1fb8d4
#else
Packit 1fb8d4
	const char delim = PathGetSeparatorA(PATH_STYLE_NATIVE);
Packit 1fb8d4
	char* dup;
Packit 1fb8d4
	char* p;
Packit 1fb8d4
	BOOL result = TRUE;
Packit 1fb8d4
Packit 1fb8d4
	/* we only operate on a non-null, absolute path */
Packit 1fb8d4
	if (!path || *path != delim)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!(dup = _strdup(path)))
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	for (p = dup; p;)
Packit 1fb8d4
	{
Packit 1fb8d4
		if ((p = strchr(p + 1, delim)))
Packit 1fb8d4
			* p = '\0';
Packit 1fb8d4
Packit 1fb8d4
		if (mkdir(dup, 0777) != 0)
Packit 1fb8d4
			if (errno != EEXIST)
Packit 1fb8d4
			{
Packit 1fb8d4
				result = FALSE;
Packit 1fb8d4
				break;
Packit 1fb8d4
			}
Packit 1fb8d4
Packit 1fb8d4
		if (p)
Packit 1fb8d4
			*p = delim;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	free(dup);
Packit 1fb8d4
	return (result);
Packit 1fb8d4
#endif
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#if !defined(_WIN32) || defined(_UWP)
Packit 1fb8d4
Packit 1fb8d4
BOOL PathFileExistsA(LPCSTR pszPath)
Packit 1fb8d4
{
Packit 1fb8d4
	struct stat stat_info;
Packit 1fb8d4
Packit 1fb8d4
	if (stat(pszPath, &stat_info) != 0)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL PathFileExistsW(LPCWSTR pszPath)
Packit 1fb8d4
{
Packit 1fb8d4
	LPSTR lpFileNameA = NULL;
Packit 1fb8d4
	BOOL ret;
Packit 1fb8d4
Packit 1fb8d4
	if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	ret = PathFileExistsA(lpFileNameA);
Packit 1fb8d4
	free(lpFileNameA);
Packit 1fb8d4
	return ret;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL PathIsDirectoryEmptyA(LPCSTR pszPath)
Packit 1fb8d4
{
Packit 1fb8d4
	struct dirent* dp;
Packit 1fb8d4
	int empty = 1;
Packit 1fb8d4
	DIR* dir = opendir(pszPath);
Packit 1fb8d4
Packit 1fb8d4
	if (dir == NULL) /* Not a directory or doesn't exist */
Packit 1fb8d4
		return 1;
Packit 1fb8d4
Packit 1fb8d4
	while ((dp = readdir(dir)) != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
		if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
Packit 1fb8d4
			continue;    /* Skip . and .. */
Packit 1fb8d4
Packit 1fb8d4
		empty = 0;
Packit 1fb8d4
		break;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	closedir(dir);
Packit 1fb8d4
	return empty;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath)
Packit 1fb8d4
{
Packit 1fb8d4
	LPSTR lpFileNameA = NULL;
Packit 1fb8d4
	BOOL ret;
Packit 1fb8d4
Packit 1fb8d4
	if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
	ret = PathIsDirectoryEmptyA(lpFileNameA);
Packit 1fb8d4
	free(lpFileNameA);
Packit 1fb8d4
	return ret;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
Packit 1fb8d4
#else
Packit 1fb8d4
Packit 1fb8d4
#ifdef _WIN32
Packit 1fb8d4
#pragma comment(lib, "shlwapi.lib")
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#endif