Blame projects/jigsaw/path.c

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