Redid the most of the file loading/saving. Now uses $HOME/.avp/ and
$AVP_DIR to look for files.
This commit is contained in:
parent
95b8b49b76
commit
080430b3bd
40 changed files with 1400 additions and 1579 deletions
575
src/files.c
Normal file
575
src/files.c
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue