#include "macros.h" #include "l2su.h" #include #include #include #include #include #include /* handcoded string functions * * NOTE: I am aware that this is inefficient but since these are used only a handful of times * during initialization, I don't think performance is really a huge deal here. * * remember that this is a Minecraft launcher, so the PC has to meet the minimum specs of that, * which are well above the minimum specs of this launcher */ char *l2_launcher_strapp(char *buf, const char *src) { size_t buflen = strlen(buf); size_t srclen = strlen(src); char *ret = realloc(buf, buflen + srclen + 1); if (!ret) return NULL; memcpy(ret + buflen, src, srclen); ret[buflen + srclen] = '\0'; /* realloc does not initialize like calloc does */ return ret; } char *l2_launcher_find_config_path(void) { /* check for $L2SU_CONFIG */ char *config = getenv(PROJECT_NAME_UPPER "_CONFIG"); if (config) { return strdup(config); } /* check for $XDG_CONFIG_HOME */ config = getenv("XDG_CONFIG_HOME"); if (config) { /* want to append '"/" PROJECT_NAME' to our string */ char *ret = strdup(config); if (!ret) return NULL; return l2_launcher_strapp(ret, "/" PROJECT_NAME); } /* check for $HOME/.config */ config = getenv("HOME"); if (config) { char *ret = strdup(config); if (!ret) return NULL; return l2_launcher_strapp(ret, "/.config/" PROJECT_NAME); } /* fail (do NOT attempt to find home directory from passwd */ return NULL; } char *l2_launcher_find_data_path(void) { /* check for $L2SU_DATA */ char *config = getenv(PROJECT_NAME_UPPER "_DATA"); if (config) { return strdup(config); } /* check for $XDG_DATA_HOME */ config = getenv("XDG_DATA_HOME"); if (config) { char *ret = strdup(config); if (!ret) return NULL; return l2_launcher_strapp(ret, "/" PROJECT_NAME); } /* check for $HOME/.local/share */ config = getenv("HOME"); if (config) { char *ret = strdup(config); if (!ret) return NULL; return l2_launcher_strapp(ret, "/.local/share/" PROJECT_NAME); } return NULL; } int l2_launcher_open_config(const char *path, int flags, mode_t mode) { int conffd = open(l2_state.paths.config, O_RDONLY | O_DIRECTORY); if (conffd < 0) return INSTANCE_ERRNO; int instfd = openat(conffd, path, flags, mode); int en = errno; /* back up errno because close can fail */ close(conffd); errno = en; return instfd; } /* NOTE: There's no portable (or otherwise - see open(2) BUGS) way to do this without race conditions. */ int l2_launcher_mkdir_parents(const char *path) { if (*path != '/') return -1; int reserrno = 0; char *pathbuf = strdup(path); char *pcurelem = pathbuf; if (!pathbuf) return -1; struct stat stbuf = { 0 }; do { /* strtok is off-limits because it smells bad */ *pcurelem = '/'; pcurelem = strchr(pcurelem + 1, '/'); if (pcurelem) { *pcurelem = '\0'; } /* now pathbuf contains our truncated path name which may or may not exist */ if (mkdir(pathbuf, 0755) < 0) { if (errno == EEXIST) { /* racy: stat the file and continue if it is a directory */ if (stat(pathbuf, &stbuf) < 0) { reserrno = errno; goto mdcleanup; } if (!S_ISDIR(stbuf.st_mode)) { reserrno = ENOTDIR; goto mdcleanup; } } else { reserrno = errno; goto mdcleanup; } } } while (pcurelem); mdcleanup: free(pathbuf); if (reserrno != 0) { errno = reserrno; return -1; } return 0; }