summaryrefslogtreecommitdiff
path: root/src/files.c
diff options
context:
space:
mode:
authorSteven Fuller <relnev@icculus.org>2001-12-18 05:08:52 +0000
committerPatryk Obara <dreamer.tan@gmail.com>2019-08-20 02:22:37 +0200
commit080430b3bda2bec05362119447d51b6c37f1cfc1 (patch)
treea4d2354ed94acb903f7f79a83e198398cb6eb757 /src/files.c
parent95b8b49b7602e4e2d3cd9e38273fa94451bec780 (diff)
Redid the most of the file loading/saving. Now uses $HOME/.avp/ and
$AVP_DIR to look for files.
Diffstat (limited to 'src/files.c')
-rw-r--r--src/files.c575
1 files changed, 575 insertions, 0 deletions
diff --git a/src/files.c b/src/files.c
new file mode 100644
index 0000000..042441b
--- /dev/null
+++ b/src/files.c
@@ -0,0 +1,575 @@
+#define _BSD_SOURCE
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+#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