1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#include "log.h"
#include "util.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int vl_mkdir_parents_open(int fd, char *path, mode_t nmode)
{
int last = 0;
int at = fd;
int temp;
char *ch = path;
char *compstart = path;
int ret = -1;
do {
if (*ch == '/' || (*ch == '\0' && (last = 1))) {
if (ch == path) continue; /* false alarm: we're trying to create / */
*ch = '\0';
if (mkdirat(at, compstart, nmode) < 0 && errno != EEXIST) {
vl_debug("%s: failed to create directory %s: %s", __func__, path, strerror(errno));
goto cleanup;
}
temp = openat(at, compstart, O_RDONLY);
if (temp < 0) {
vl_debug("%s: failed to open new directory %s: %s", __func__, path, strerror(errno));
goto cleanup;
}
close(at);
temp = at;
compstart = ch + 1;
if (!last) *ch = '/'; /* make an effort to unclobber path */
}
} while (*(ch++));
ret = 0;
cleanup:
if (at != fd) close(at);
return ret;
}
/* will clobber path */
int vl_mkdir_parents(int fd, char *path, mode_t nmode)
{
#if 0
int last = 0;
char *ch = path;
do {
if (*ch == '/' || (*ch == '\0' && (last = 1))) {
/* last is set to 1 iff *ch == '\0' */
*ch = '\0';
if (mkdirat(fd, path, mode) < 0 && errno != EEXIST) {
vl_debug("Failed to create directory %s: %s", path, strerror(errno));
return -1;
}
if (!last) *ch = '/';
}
} while (*(ch++));
#endif /* old implementation */
fd = vl_mkdir_parents_open(fd, path, nmode);
if (fd >= 0) {
close(fd);
return 0;
}
return -1;
}
/* def: home-relative default */
/* https://specifications.freedesktop.org/basedir/latest/ */
static int open_xdg_dir(vl_arena *scratch, const char *xdg_env, const char *def)
{
const char *xdg_dir = getenv(xdg_env);
int dfd;
if (xdg_dir && *xdg_dir == '/') {
char *xdg_dir_mod = vl_arena_strdup(scratch, xdg_dir);
/* must be an absolute path */
if ((dfd = vl_mkdir_parents_open(AT_FDCWD, xdg_dir_mod, 0700)) < 0) {
vl_warn("get_xdg_dir: Failed to create %s: %s", xdg_dir, strerror(errno));
return -1;
}
return dfd;
}
/* xdg dir is missing or not absolute (as required) */
const char *home = getenv("HOME");
/* TODO: could get passwd entry for home directory */
if (!home || *home != '/') {
vl_warn("Missing $HOME (or it is not an absolute path)! Cannot decide launcher data directory.");
return -1;
}
char *path = vl_arena_sprintf(scratch, "%s/%s", home, def);
if ((dfd = vl_mkdir_parents_open(AT_FDCWD, path, 0700)) < 0) {
vl_warn("%s: failed to create/open xdg dir %s: %s", __func__, path, strerror(errno));
return -1;
}
return 0;
}
int vl_get_data_dir(vl_arena *alloc, const char *application, char **opath)
{
/* TODO: not just linux */
int datadir = open_xdg_dir(alloc, "XDG_DATA_HOME", ".local/share");
}
|