#include "path.h"
#include <unistd.h>
#include <sys/stat.h>
#ifndef APPNAME
#define APPNAME "jigsaw"
#endif
#ifndef SUBDIR
#define SUBDIR "games/" APPNAME
#endif
#ifndef PREFIX
#define PREFIX "/usr"
#endif
#ifndef PATHENV_DATA
#define PATHENV_DATA "JIGSAW_DATA"
#endif
#ifndef PATHENV_SAVE
#define PATHENV_SAVE "JIGSAW_SAVE"
#endif
const char* filename(const char *path) {
const char *r = path;
for(const char *c = r; *c; ++c) if (*c == '/' || *c == '\\') r = c+1;
return r;
}
const char* fileext(const char *path) {
const char* r = path = filename(path), *c = r;
for(;*c; ++c) if (*c == '.') r = c;
return r == path ? c : r;
}
char* filebase(const char *path) {
const char* c0 = filename(path);
const char* c1 = fileext(c0);
char* r = (char*)alloc(c1 - c0 + 1);
return strncpy(r, c0, c1 - c0);
}
char* filedir(const char *path) {
const char* c = filename(path);
while(c > path && (*(c-1) == '/' || *(c-1) == '\\')) --c;
char* r = (char*)alloc(c - path + 1);
return strncpy(r, path, c - path);
}
time_t filetime(const char *path) {
struct stat st = {};
return stat(path, &st) ? 0 : st.st_mtime;
}
int makeDirectoryRecursive(const char *path) {
if (directoryExists(path)) return 1;
char *parent = filedir(path);
if (*parent) makeDirectoryRecursive(parent);
free(parent);
return makeDirectory(path);
}
PathList* mergePaths(PathList a, PathList b) {
int cnt = 0;
size_t size = ASIZEOF(PathList);
for(int i = 0, j = 0; i < a.cnt || j < b.cnt; ++cnt) {
int r = i < a.cnt && j < b.cnt ? strcmp(filename(a.l[i]), filename(b.l[i]))
: i < a.cnt ? -1 : 1;
if (r < 0) { size += strlen(a.l[i++]) + 1; }
else { size += strlen(b.l[j++]) + 1; i += !r; }
}
size += cnt*ASIZEOF(char*);
PathList *pl = alloc(size);
char **l = (char**)(pl+1);
char *c = (char*)(l+cnt);
pl->l = (StrList)l;
pl->cnt = cnt;
for(int i = 0, j = 0; i < a.cnt || j < b.cnt; ++cnt) {
int r = i < a.cnt && j < b.cnt ? strcmp(filename(a.l[i]), filename(b.l[i]))
: i < a.cnt ? -1 : 1;
if (r < 0) { *l++ = strcpy(c, a.l[i++]); }
else { *l++ = strcpy(c, b.l[j++]); i += !r; }
c += strlen(c) + 1;
}
return pl;
}
PathList* readDir(const char *path, const char *ext) {
Directory d = openDirectory(path);
if (!d) return alloc(sizeof(PathList));
int cnt = directoryGetCount(d);
size_t size = ASIZEOF(PathList) + cnt*ASIZEOF(char*);
for(int i = 0; i < cnt; ++i) {
const char *p = directoryGetFull(d, i);
if (!fileExists(p) || strcmp(fileext(p), ext)) continue;
size += strlen(p) + 1;
}
PathList *pl = alloc(size);
char **l = (char**)(pl+1);
char *c = (char*)(l+cnt);
pl->l = (StrList)l;
for(int i = 0; i < cnt; ++i) {
const char *p = directoryGetFull(d, i);
if (!fileExists(p) || strcmp(fileext(p), ext)) continue;
l[pl->cnt++] = strcpy(c, p);
c += strlen(c) + 1;
}
closeDirectory(d);
return pl;
}
PathList* readDirs(PathList paths, const char *ext) {
PathList *a = alloc(sizeof(PathList));
for(int i = 0; i < paths.cnt; ++i) {
PathList *b = readDir(paths.l[i], ext);
PathList *c = mergePaths(*a, *b);
free(a); free(b); a = c;
}
return a;
}
char* findFile(PathList paths, const char *name) {
for(int i = paths.cnt-1; i >= 0; --i) {
char *fn = strprintf("%s/%s", paths.l[i], name);
if (fileExists(fn)) return fn;
free(fn);
}
return NULL;
}
PathList imagePaths() {
static PathList pl;
if (pl.l) return pl;
const char *sys = PREFIX "/local/share:" PREFIX "/share";
const char *xdd = getenv("XDG_DATA_DIRS");
if (!xdd) xdd = "";
const char *home = getenv("HOME");
char *paths = home && *home ? strprintf("%s/.local/share:%s:%s", home, xdd, sys)
: strprintf("%s:%s", xdd, sys);
int cnt = 3; // :tail + wd + ud
for(char* c = paths; *c; ++c) if (*c == ':') ++cnt;
char **l = (char**)alloc(cnt*ASIZEOF(*l));
pl.l = (StrList)l;
while(pl.cnt < cnt) {
char* c = strrchr(paths, ':');
if (c) *c++ = 0; else c = paths;
if (*c) l[pl.cnt++] = strprintf("%s/%s", c, SUBDIR);
if (c == paths) break;
}
const char *wd = getcwd(NULL, 0);
const char *ud = getenv(PATHENV_DATA);
if (wd && *wd) l[pl.cnt++] = strprintf("%s/data/images", wd);
if (ud && *ud) l[pl.cnt++] = strprintf("%s", ud);
for(int i = 0; i < pl.cnt; ++i) INF("images directory: %s", l[i]);
return pl;
}
const char* savesPath() {
static char *path;
if (path) return path;
if (!path) {
const char *p = getenv(PATHENV_SAVE);
if (p && *p) path = strprintf("%s", p);
}
if (!path) {
const char *p = getenv("HOME");
if (p && *p) path = strprintf("%s/.local/share/%s/saves", p, SUBDIR);
}
if (!path) {
const char *p = getcwd(NULL, 0);
if (p && *p) path = strprintf("%s/data/saves", p);
}
if (!path)
path = strprintf("./data/saves");
INF("saves directory: %s", path);
if (!makeDirectoryRecursive(path))
WRN("cannot create saves directory: %s", path);
return path;
}
const char* thumbsPath() {
static char *path;
if (!path) {
path = strprintf("%s/thumbs", savesPath());
INF("thumbs directory: %s", path);
if (!makeDirectoryRecursive(path))
WRN("cannot create thumbs directory: %s", path);
}
return path;
}
const char* settingsFile() {
static char *path;
if (!path) {
path = strprintf("%s/settings.ini", savesPath());
INF("settings file: %s", path);
}
return path;
}
char* findImage(const char *name) {
char* fn = strprintf("%s.png", name);
char* r = findFile(imagePaths(), fn);
free(fn);
return r;
}
PathList* imageFiles() { return readDirs(imagePaths(), ".png"); }
PathList* saveFiles() { return readDir(savesPath(), ".puzzle"); }