efl/src/lib/efl/interfaces/efl_vpath_core.c

366 lines
13 KiB
C

#include "config.h"
#include "Efl.h"
#ifdef HAVE_GETPWENT
# include <sys/types.h>
# include <pwd.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#define MY_CLASS EFL_VPATH_CORE_CLASS
typedef struct _Efl_Vpath_Core_Data Efl_Vpath_Core_Data;
struct _Efl_Vpath_Core_Data
{
Eina_Spinlock lock;
Eina_Hash *meta;
};
static Efl_Vpath_Core *vpath_core = NULL;
EOLIAN static Eo_Base *
_efl_vpath_core_eo_base_constructor(Eo *obj, Efl_Vpath_Core_Data *pd)
{
char buf[PATH_MAX], bufhome[PATH_MAX];
const char *home, *s;
if (vpath_core) return NULL;
obj = eo_constructor(eo_super(obj, MY_CLASS));
pd->meta = eina_hash_string_superfast_new
((Eina_Free_Cb)eina_stringshare_del);
eina_spinlock_new(&(pd->lock));
vpath_core = obj;
// $HOME / ~/ etc.
home = eina_environment_home_get();
if (!home)
{
/* Windows does not have getuid(), but home can't be NULL */
#ifdef HAVE_GETUID
uid_t uid = getuid();
struct stat st;
snprintf(bufhome, sizeof(bufhome), "/tmp/%i", (int)uid);
mkdir(bufhome, S_IRUSR | S_IWUSR | S_IXUSR);
if (stat(bufhome, &st) == 0) home = bufhome;
else
{
if (stat("/tmp", &st) == 0) home = "/tmp";
else home = "/";
}
#else
home = "/";
#endif
}
efl_vpath_core_meta_set(obj, "home", home);
// tmp dir - system wide
s = eina_environment_tmp_get();
efl_vpath_core_meta_set(obj, "tmp", s);
#define ENV_HOME_SET(_env, _dir, _meta) \
if (!(s = getenv(_env))) { \
snprintf(buf, sizeof(buf), "%s/"_dir, home); s = buf; \
} efl_vpath_core_meta_set(obj, _meta, s);
// $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.
ENV_HOME_SET("XDG_DATA_HOME", ".local/share", "data");
// $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.
ENV_HOME_SET("XDG_CONFIG_HOME", ".config", "config");
// $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.
ENV_HOME_SET("XDG_CACHE_HOME", ".cache", "cache");
// $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.
if (!(s = getenv("XDG_RUNTIME_DIR")))
{
#ifdef HAVE_GETUID
struct stat st;
// fallback - make ~/.run
snprintf(buf, sizeof(buf), "%s/.run", home);
mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR);
// if mkdir worked - use, otherwse use /tmp
if (stat(buf, &st) == 0) s = buf;
else
{
uid_t uid;
// use /tmp/.run-UID if ~/ dir cant be made
s = (char *)efl_vpath_core_meta_get(obj, "tmp");
uid = getuid();
snprintf(buf, sizeof(buf), "%s/.run-%i", s, (int)uid);
mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR);
// if ok - use it or fall back to /tmp
if (stat(buf, &st) == 0) s = buf;
else s = (char *)efl_vpath_core_meta_get(obj, "tmp");
}
#else
s = (char *)efl_vpath_core_meta_get(obj, "tmp");
#endif
}
efl_vpath_core_meta_set(obj, "run", s);
// https://www.freedesktop.org/wiki/Software/xdg-user-dirs/
// https://wiki.archlinux.org/index.php/Xdg_user_directories
// ^^^^ we don't handle:
// /etc/xdg/user-dirs.conf
// /etc/xdg/user-dirs.defaults
// (:config:)/user-dirs.conf
// (:config:)/user-dirs.defaults
// $XDG_DESKTOP_DIR="$HOME/Desktop"
ENV_HOME_SET("XDG_DESKTOP_DIR", "Desktop", "desktop");
// $XDG_DOCUMENTS_DIR="$HOME/Documents"
ENV_HOME_SET("XDG_DOCUMENTS_DIR", "Documents", "documents");
// $XDG_DOWNLOAD_DIR="$HOME/Downloads"
ENV_HOME_SET("XDG_DOWNLOAD_DIR", "Downloads", "downloads");
// $XDG_MUSIC_DIR="$HOME/Music"
ENV_HOME_SET("XDG_MUSIC_DIR", "Music", "music");
// $XDG_PICTURES_DIR="$HOME/Pictures"
ENV_HOME_SET("XDG_PICTURES_DIR", "Pictures", "pictures");
// $XDG_PUBLICSHARE_DIR="$HOME/Public"
ENV_HOME_SET("XDG_PUBLIC_DIR", "Public", "public");
// $XDG_TEMPLATES_DIR="$HOME/.Templates"
ENV_HOME_SET("XDG_TEMPLATES_DIR", ".Templates", "templates");
// $XDG_VIDEOS_DIR="$HOME/Videos"
ENV_HOME_SET("XDG_VIDEOS_DIR", "Videos", "videos");
// Add ~/Applications for user-installed apps
ENV_HOME_SET("E_APPS_DIR", "Applications", "apps");
// XXX: do the below ... later
//
// FHS FOR APP:
// app.dir = PREFIX
// app.bin = PREFIX/bin
// app.lib = PREFIX/lib
// app.data = PREFIX/share/APPNAME
// app.locale = PREFIX/share/locale
//
// XXX: figure out how to merge these with XDG/FHS?
// Tizen:
// App Dir Structure:
// bin Executable binary pathOwner: Read
// lib Library pathOwner: Read
// data Used to store private data of an application.
// res Used to read resource files that are delivered with the application package.
// shared Parent directory of the data, res, and trusted sub-directories. Files in this directory cannot be delivered with the application package.Owner: Read
// shared/data Used to share data with other applications.
// shared/res Used to share resources with other applications. The resource files are delivered with the application package.
// shared/trusted Used to share data with family of trusted applications. The family applications signed with the same certificate can access data in the shared/trusted directory.
//
// XXX: figure out how to merge these with XDG?
// Media/...vvv
// Images Used for Image data.Read and Write
// Sounds Used for Sound data.
// Videos Used for Video data.
// Cameras Used for Camera pictures.
// Downloads Used for Downloaded data.
// Music Used for Music data.
// Documents Used for Documents.
// Others Used for other types.
// System Ringtones Used for System default ringtones.Read
//
// $TZ_SYS_HOME=/home
// $TZ_SYS_DB=/var/db
// $TZ_SYS_CONFIG=/var/kdb
// $TZ_SYS_CONFIG_VOLATILE=/run/kdb
// $TZ_SYS_APP=/usr/apps
// $TZ_SYS_DESKTOP_APP=/usr/share/applications
//
// $TS_USER_DB=<user_homeid>/.tizen/db
// $TZ_USER_CONFIG=<user_home_dir>/.tizen/kdb
// $TZ_USER_APP=<user_home_dir>/.tizen/apps
// $TZ_USER_DESKTOP_APP=<user_home_dir>/.tizen/desktop
// $TZ_USER_DOCUMENTS=<user_home_dir>/Documents
// $TZ_USER_PICTURES=<user_home_dir>/Pictures
// $TZ_USER_VIDEOS=<user_home_dir>/Videos
// $TZ_USER_MUSIC=<user_home_dir>/Music
// $TZ_USER_DOWNLOADS=<user_home_dir>/Downloads
// $TZ_USER_PUBLIC=<user_home_dir>/Public
return obj;
}
EOLIAN static void
_efl_vpath_core_eo_base_destructor(Eo *obj, Efl_Vpath_Core_Data *pd)
{
eina_hash_free(pd->meta);
pd->meta = NULL;
eina_spinlock_free(&(pd->lock));
if (vpath_core == obj) vpath_core = NULL;
eo_destructor(eo_super(obj, MY_CLASS));
}
EOLIAN static Efl_Vpath_Core *
_efl_vpath_core_get(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED)
{
// no locks here as we expect efl to init this early in main "thread"
if (!vpath_core) vpath_core = eo_add(EFL_VPATH_CORE_CLASS, NULL);
return vpath_core;
}
EOLIAN static const char *
_efl_vpath_core_meta_get(Eo *obj EINA_UNUSED, Efl_Vpath_Core_Data *pd, const char *key)
{
const char *meta;
if (!key) return NULL;
eina_spinlock_take(&(pd->lock));
meta = eina_hash_find(pd->meta, key);
eina_spinlock_release(&(pd->lock));
return meta;
}
EOLIAN static void
_efl_vpath_core_meta_set(Eo *obj EINA_UNUSED, Efl_Vpath_Core_Data *pd, const char *key, const char *path)
{
if (!key) return;
eina_spinlock_take(&(pd->lock));
if (path) eina_hash_add(pd->meta, key, eina_stringshare_add(path));
else eina_hash_del(pd->meta, key, NULL);
eina_spinlock_release(&(pd->lock));
}
EOLIAN static Efl_Vpath_File *
_efl_vpath_core_efl_vpath_fetch(Eo *obj, Efl_Vpath_Core_Data *pd EINA_UNUSED, const char *path)
{
Efl_Vpath_File_Core *file;
file = eo_add(EFL_VPATH_FILE_CORE_CLASS, obj);
efl_vpath_file_path_set(file, path);
// XXX: implement parse of path then look up in hash if not just create
// object where path and result are the same and return that with
// path set and result set to resolved path - return obj handler calls
// "do" on object to get the result inside fetched or failed callback.
// if it's a url then we need a new classs that overrides the do and
// begins a fetch and on finish calls the event cb or when wait is called
if (path)
{
/* FIXME: not working for WIndows */
// /* <- full path
if (path[0] == '/')
{
efl_vpath_file_result_set(file, path);
return file;
}
// .*
if (path[0] == '.')
{
// .[/]* <- current dir relative
if ((path[1] == '/') || (path[1] == 0))
{
efl_vpath_file_result_set(file, path);
return file;
}
// ..[/]* <- parent dir relative
if ((path[1] == '.') && ((path[2] == '/') || (path[2] == 0)))
{
efl_vpath_file_result_set(file, path);
return file;
}
}
// ~* ...
if (path[0] == '~')
{
// ~/ <- home directory
if (path[1] == '/')
{
char buf[PATH_MAX];
const char *home = efl_vpath_core_meta_get(obj, "home");
if (home)
{
snprintf(buf, sizeof(buf), "%s%s", home, path + 1);
efl_vpath_file_result_set(file, buf);
return file;
}
}
#ifdef HAVE_GETPWENT
// ~username/ <- homedir of user "username"
else
{
const char *p;
struct passwd pwent, *pwent2 = NULL;
char *name, buf[PATH_MAX], pwbuf[8129];
for (p = path + 1; *p; p++)
{
if (*p =='/') break;
}
name = alloca(p - path);
strncpy(name, path + 1, p - path - 1);
name[p - path - 1] = 0;
if (!getpwnam_r(name, &pwent, pwbuf, sizeof(pwbuf), &pwent2))
{
if ((pwent2) && (pwent.pw_dir))
{
snprintf(buf, sizeof(buf), "%s%s", pwent.pw_dir, p);
efl_vpath_file_result_set(file, buf);
return file;
}
}
}
#endif /* HAVE_GETPWENT */
}
// (:xxx/* ... <- meta has table
if ((path[0] == '(') && (path[1] == ':'))
{
const char *p, *meta;
char *name, buf[PATH_MAX];
Eina_Bool found = EINA_FALSE;
for (p = path + 2; *p; p++)
{
if ((p[0] ==':') && (p[1] == ')') && (p[2] == '/'))
{
found = EINA_TRUE;
break;
}
}
if (found)
{
name = alloca(p - path);
strncpy(name, path + 2, p - path - 2);
name[p - path - 2] = 0;
eina_spinlock_take(&(pd->lock));
meta = eina_hash_find(pd->meta, name);
eina_spinlock_release(&(pd->lock));
if (meta)
{
snprintf(buf, sizeof(buf), "%s%s", meta, p + 2);
efl_vpath_file_result_set(file, buf);
return file;
}
}
}
// file:/// <- local file path uri
// file://localhost/ <- local file path uri
// file://hostname/ <- remove file path uri
// XXX: %c4,%17,%fe etc. are bytes escaped
// http://www.ietf.org/rfc/rfc2396.txt
// http://www.ietf.org/rfc/rfc1738.txt
// http://equinox-project.org/spec/file-uri-spec.txt
// http://en.wikipedia.org/wiki/File_URI_scheme
}
return file;
}
#include "interfaces/efl_vpath_core.eo.c"