summaryrefslogtreecommitdiffstats
path: root/lib/util.c
blob: 29423d10e00f527f4ce5d231d32b3567e1798101 (plain) (blame)
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");
}