#define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "files.h" static char *local_dir; static char *global_dir; /* Sets the local and global directories used by the other functions. Local = ~/.dir, where config and user-installed files are kept. Global = installdir, where installed data is stored. */ int SetGameDirectories(const char *local, const char *global) { struct stat buf; local_dir = strdup(local); global_dir = strdup(global); if (stat(local_dir, &buf) == -1) { printf("Creating local directory %s...\n", local_dir); mkdir(local_dir, S_IRWXU); } return 0; } #define DIR_SEPARATOR "/" static char *FixFilename(const char *filename, const char *prefix, int force) { char *f, *ptr; int flen; flen = strlen(filename) + strlen(prefix) + 2; f = (char *)malloc(flen); strcpy(f, prefix); strcat(f, DIR_SEPARATOR); strcat(f, filename); ptr = f; while (*ptr) { if ((*ptr == '/') || (*ptr == '\\') || (*ptr == ':')) { *ptr = DIR_SEPARATOR[0]; } else if (*ptr == '\r' || *ptr == '\n') { *ptr = 0; break; } else { if (force) *ptr = tolower(*ptr); } ptr++; } return f; } /* Open a file of type type, with mode mode. Mode can be: #define FILEMODE_READONLY 0x01 #define FILEMODE_WRITEONLY 0x02 #define FILEMODE_READWRITE 0x04 #define FILEMODE_APPEND 0x08 Type is (mode = ReadOnly): #define FILETYPE_PERM 0x08 // try the global dir only #define FILETYPE_OPTIONAL 0x10 // try the global dir first, then try the local dir #define FILETYPE_CONFIG 0x20 // try the local dir only Type is (mode = WriteOnly or ReadWrite): FILETYPE_PERM: error FILETYPE_OPTIONAL: error FILETYPE_CONFIG: try the local dir only */ FILE *OpenGameFile(const char *filename, int mode, int type) { char *rfilename; char *openmode; FILE *fp; if ((type != FILETYPE_CONFIG) && (mode != FILEMODE_READONLY)) return NULL; switch(mode) { case FILEMODE_READONLY: openmode = "rb"; break; case FILEMODE_WRITEONLY: openmode = "wb"; break; case FILEMODE_READWRITE: openmode = "w+"; break; case FILEMODE_APPEND: openmode = "ab"; break; default: return NULL; } if (type != FILETYPE_CONFIG) { rfilename = FixFilename(filename, global_dir, 0); fp = fopen(rfilename, openmode); free(rfilename); if (fp != NULL) { return fp; } rfilename = FixFilename(filename, global_dir, 1); fp = fopen(rfilename, openmode); free(rfilename); if (fp != NULL) { return fp; } } if (type != FILETYPE_PERM) { rfilename = FixFilename(filename, local_dir, 0); fp = fopen(rfilename, openmode); free(rfilename); if (fp != NULL) { return fp; } rfilename = FixFilename(filename, local_dir, 1); fp = fopen(rfilename, openmode); free(rfilename); return fp; } return NULL; } /* Close a fd returned from OpenGameFile Currently this just uses stdio, so CloseGameFile is redundant. */ int CloseGameFile(FILE *pfd) { return fclose(pfd); } /* Get the filesystem attributes of a file #define FILEATTR_DIRECTORY 0x0100 #define FILEATTR_READABLE 0x0200 #define FILEATTR_WRITABLE 0x0400 Error or can't access it: return value of 0 (What is the game going to do about it anyway?) */ static int GetFA(const char *filename) { struct stat buf; int attr; attr = 0; if (stat(filename, &buf) == 0) { if (S_ISDIR(buf.st_mode)) { attr |= FILEATTR_DIRECTORY; } if (access(filename, R_OK) == 0) { attr |= FILEATTR_READABLE; } if (access(filename, W_OK) == 0) { attr |= FILEATTR_WRITABLE; } } return attr; } static time_t GetTS(const char *filename) { struct stat buf; if (stat(filename, &buf) == 0) { return buf.st_mtime; } return 0; } int GetGameFileAttributes(const char *filename, int type) { struct stat buf; char *rfilename; int attr; attr = 0; if (type != FILETYPE_CONFIG) { rfilename = FixFilename(filename, global_dir, 0); if (stat(rfilename, &buf) == 0) { if (S_ISDIR(buf.st_mode)) { attr |= FILEATTR_DIRECTORY; } if (access(rfilename, R_OK) == 0) { attr |= FILEATTR_READABLE; } if (access(rfilename, W_OK) == 0) { attr |= FILEATTR_WRITABLE; } free(rfilename); return attr; } free(rfilename); rfilename = FixFilename(filename, global_dir, 1); if (stat(rfilename, &buf) == 0) { if (S_ISDIR(buf.st_mode)) { attr |= FILEATTR_DIRECTORY; } if (access(rfilename, R_OK) == 0) { attr |= FILEATTR_READABLE; } if (access(rfilename, W_OK) == 0) { attr |= FILEATTR_WRITABLE; } free(rfilename); return attr; } free(rfilename); } if (type != FILETYPE_PERM) { rfilename = FixFilename(filename, local_dir, 0); if (stat(rfilename, &buf) == 0) { if (S_ISDIR(buf.st_mode)) { attr |= FILEATTR_DIRECTORY; } if (access(rfilename, R_OK) == 0) { attr |= FILEATTR_READABLE; } if (access(rfilename, W_OK) == 0) { attr |= FILEATTR_WRITABLE; } free(rfilename); return attr; } free(rfilename); rfilename = FixFilename(filename, local_dir, 1); if (stat(rfilename, &buf) == 0) { if (S_ISDIR(buf.st_mode)) { attr |= FILEATTR_DIRECTORY; } if (access(rfilename, R_OK) == 0) { attr |= FILEATTR_READABLE; } if (access(rfilename, W_OK) == 0) { attr |= FILEATTR_WRITABLE; } free(rfilename); return attr; } free(rfilename); } return 0; } /* Delete a file: local dir only */ int DeleteGameFile(const char *filename) { char *rfilename; int ret; rfilename = FixFilename(filename, local_dir, 0); ret = unlink(rfilename); free(rfilename); if (ret == -1) { rfilename = FixFilename(filename, local_dir, 1); ret = unlink(rfilename); free(rfilename); } return ret; } /* Create a directory: local dir only TODO: maybe also mkdir parent directories, if they do not exist? */ int CreateGameDirectory(const char *dirname) { char *rfilename; int ret; rfilename = FixFilename(dirname, local_dir, 0); ret = mkdir(rfilename, S_IRWXU); free(rfilename); if (ret == -1) { rfilename = FixFilename(dirname, local_dir, 1); ret = mkdir(rfilename, S_IRWXU); free(rfilename); } return ret; } /* This struct is private. */ typedef struct GameDirectory { DIR *localdir; /* directory opened with opendir */ DIR *globaldir; char *localdirname; char *globaldirname; char *pat; /* pattern to match */ GameDirectoryFile tmp; /* Temp space */ } GameDirectory; /* "Open" a directory dirname, with type type Returns a pointer to a directory datatype Pattern is the pattern to match */ void *OpenGameDirectory(const char *dirname, const char *pattern, int type) { char *localdirname, *globaldirname; DIR *localdir, *globaldir; GameDirectory *gd; globaldir = NULL; globaldirname = NULL; if (type != FILETYPE_CONFIG) { globaldirname = FixFilename(dirname, global_dir, 0); globaldir = opendir(globaldirname); if (globaldir == NULL) { free(globaldirname); globaldirname = FixFilename(dirname, global_dir, 1); globaldir = opendir(globaldirname); if (globaldir == NULL) free(globaldirname); } } localdir = NULL; localdirname = NULL; if (type != FILETYPE_PERM) { localdirname = FixFilename(dirname, local_dir, 0); localdir = opendir(localdirname); if (localdir == NULL) { free(localdirname); localdirname = FixFilename(dirname, local_dir, 1); localdir = opendir(localdirname); if (localdir == NULL) free(localdirname); } } if (localdir == NULL && globaldir == NULL) return NULL; gd = (GameDirectory *)malloc(sizeof(GameDirectory)); gd->localdir = localdir; gd->globaldir = globaldir; gd->localdirname = localdirname; gd->globaldirname = globaldirname; gd->pat = strdup(pattern); return gd; } /* This struct is public. typedef struct GameDirectoryFile { char *filename; int attr; } GameDirectoryFile; */ /* Returns the next match of pattern with the contents of dir f is the current file */ GameDirectoryFile *ScanGameDirectory(void *dir) { char *ptr; struct dirent *file; GameDirectory *directory; directory = (GameDirectory *)dir; if (directory->globaldir) { while ((file = readdir(directory->globaldir)) != NULL) { if (fnmatch(directory->pat, file->d_name, FNM_PATHNAME) == 0) { ptr = FixFilename(file->d_name, directory->globaldirname, 0); directory->tmp.attr = GetFA(ptr); free(ptr); directory->tmp.filename = file->d_name; return &directory->tmp; } } closedir(directory->globaldir); free(directory->globaldirname); directory->globaldir = NULL; directory->globaldirname = NULL; } if (directory->localdir) { while ((file = readdir(directory->localdir)) != NULL) { if (fnmatch(directory->pat, file->d_name, FNM_PATHNAME) == 0) { ptr = FixFilename(file->d_name, directory->localdirname, 0); directory->tmp.attr = GetFA(ptr); directory->tmp.timestamp = GetTS(ptr); free(ptr); directory->tmp.filename = file->d_name; return &directory->tmp; } } closedir(directory->localdir); free(directory->localdirname); directory->localdir = NULL; directory->localdirname = NULL; } return NULL; } /* Close directory */ int CloseGameDirectory(void *dir) { GameDirectory *directory = (GameDirectory *)dir; if (directory) { free(directory->pat); if (directory->localdirname) free(directory->localdirname); if (directory->globaldirname) free(directory->globaldirname); if (directory->localdir) closedir(directory->localdir); if (directory->globaldir) closedir(directory->globaldir); return 0; } return -1; } #ifdef FILES_DRIVER int main(int argc, char *argv[]) { FILE *fp; char buf[64]; void *dir; SetGameDirectories("tmp1", "tmp2"); fp = OpenGameFile("tester", FILEMODE_WRITEONLY, FILETYPE_CONFIG); fputs("test\n", fp); CloseGameFile(fp); CreateGameDirectory("yaya"); CreateGameDirectory("tester2"); CreateGameDirectory("tester2/blah"); fp = OpenGameFile("tester", FILEMODE_READONLY, FILETYPE_OPTIONAL); printf("Read: %s", fgets(buf, 60, fp)); CloseGameFile(fp); fp = OpenGameFile("tester", FILEMODE_READONLY, FILETYPE_CONFIG); printf("Read: %s", fgets(buf, 60, fp)); CloseGameFile(fp); dir = OpenGameDirectory(".", "*", FILETYPE_OPTIONAL); if (dir != NULL) { GameDirectoryFile *gd; while ((gd = ScanGameDirectory(dir)) != NULL) { printf("Name: %s, Attr: %08X\n", gd->filename, gd->attr); } CloseGameDirectory(dir); } else { printf("Could not open the directory...\n"); } DeleteGameFile("tester"); return 0; } #endif