Add a set of "crudefile" file io functions. Reads or writes an

entire C string (including terminating nul).  Read the source for
more details :)
This commit is contained in:
Adam Olsen 2001-07-22 19:03:26 +00:00
parent 77694855e7
commit 2f223924f4
6 changed files with 521 additions and 2 deletions

41
qw/include/crudefile.h Normal file
View file

@ -0,0 +1,41 @@
/*
crudefile.h
(description)
Copyright (C) 2001 Adam Olsen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifndef _CRUDEFILE_H
#define _CRUDEFILE_H
void CF_Init ();
void CF_CloseAllFiles ();
int CF_Open (const char *path, const char *mode);
void CF_Close (int desc);
const char * CF_Read (int desc);
int CF_Write (int desc, const char *buf);
int CF_EOF (int desc);
int CF_Quota ();
#endif // _CRUDEFILE_H

View file

@ -74,8 +74,8 @@ else
syssv_SRC= sv_sys_unix.c
endif
server_SOURCES= sv_ccmds.c sv_console.c sv_cvar.c sv_ents.c sv_init.c \
sv_main.c sv_misc.c \
server_SOURCES= crudefile.c sv_ccmds.c sv_console.c sv_cvar.c \
sv_ents.c sv_init.c sv_main.c sv_misc.c \
sv_model.c sv_move.c sv_nchan.c sv_phys.c sv_pr_cmds.c \
sv_progs.c sv_send.c sv_user.c world.c $(syssv_SRC)

397
qw/source/crudefile.c Normal file
View file

@ -0,0 +1,397 @@
/*
sv_crudefile.c
(description)
Copyright (C) 2001 Adam Olsen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_CTYPE_H
# include <ctype.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include "compat.h"
#include "QF/cvar.h"
#include "QF/vfs.h"
#include "QF/zone.h"
#include "QF/sys.h"
#include "QF/console.h"
#include "crudefile.h"
int cf_maxsize; // max combined file size (eg quota)
int cf_cursize; // current combined file size
typedef struct cf_file_s {
VFile *file;
char *path;
char *buf;
int size;
int writtento;
char mode; // 'r' for read, 'w' for write
} cf_file_t;
cf_file_t * cf_filep;
int cf_filepcount; // elements in array
int cf_openfiles; // used elements
cvar_t *crudefile_quota;
#define CF_DIR "cf/"
#define CF_MAXFILES 100
#define CF_BUFFERSIZE 256
/*
CF_ValidDesc
Returns 1 if the file descriptor is valid.
*/
int
CF_ValidDesc (int desc)
{
if (desc >= 0 && desc < cf_filepcount && cf_filep[desc].file)
return 1;
return 0;
}
/*
CF_AlreadyOpen
Returns 1 if mode == 'r' and the file is already open for
writing, or if if mode == 'w' and the file's already open at all.
*/
int
CF_AlreadyOpen (const char * path, char mode)
{
int i;
for (i = 0; i < cf_filepcount; i++) {
if (!cf_filep[i].file)
continue;
if (mode == 'r' && cf_filep[i].mode == 'w' && strequal(path, cf_filep[i].path))
return 1;
if (mode == 'w' && strequal(path, cf_filep[i].path))
return 1;
}
return 0;
}
/*
CF_GetFileSize
Returns the size of the specified file
*/
int
CF_GetFileSize (const char *path)
{
struct stat buf;
if (!stat (path, &buf))
return 0;
return buf.st_size;
}
/*
CF_BuildQuota
Calculates the currently used space
*/
void
CF_BuildQuota ()
{
DIR *dir;
struct dirent *i;
char *path;
char *file;
path = Hunk_TempAlloc (strlen (com_gamedir) + 1 + strlen (CF_DIR) + 256 + 1);
if (!path)
return;
strcpy(path, com_gamedir);
strcpy(path + strlen(path), "/");
strcpy(path + strlen(path), CF_DIR);
dir = opendir (path);
if (!dir)
return;
file = path + strlen(path);
cf_cursize = 0;
while ((i = readdir(dir))) {
strcpy (file, i->d_name);
cf_cursize += CF_GetFileSize (path);
}
}
/*
CF_Init
Ye ol' Init function :)
*/
void
CF_Init ()
{
CF_BuildQuota();
crudefile_quota = Cvar_Get ("crudefile_quota", "-1", CVAR_ROM, NULL, "Maximum space available to the Crude File system, -1 to totally disable file writing");
cf_maxsize = crudefile_quota->int_val;
}
/*
CF_CloseAllFiles
Closes all open files, printing warnings if developer is on
*/
void
CF_CloseAllFiles ()
{
int i;
for (i = 0; i < cf_filepcount; i++)
if (cf_filep[i].file) {
Con_DPrintf ("Warning: closing Crude File %d left over from last map\n", i);
CF_Close(i);
}
}
/*
CF_Open
cfopen opens a file, either for reading or writing (not both).
returns a file descriptor >= 0 on success, < 0 on failure.
mode is either r or w.
*/
int
CF_Open (const char *path, const char *mode)
{
char *fullpath;
VFile *file;
int desc;
int i;
char *j;
int oldsize;
if (cf_openfiles >= CF_MAXFILES) {
return -1;
}
// check for paths with ..
if (strequal(path, "..")
|| !strncmp(path, "../", 3)
|| strstr(path, "/../")
|| (strlen(path) >= 3
&& strequal(path + strlen(path) - 3, "/.."))) {
return -1;
}
if (!(strequal(mode, "w") || strequal(mode, "r"))) {
return -1;
}
if (mode[0] == 'w' && cf_maxsize < 0) { // can't even delete if quota < 0
return -1;
}
fullpath = malloc(strlen(com_gamedir) + 1 + strlen(CF_DIR) + strlen(path) + 1);
if (!fullpath) {
return -1;
}
strcpy(fullpath, com_gamedir);
strcpy(fullpath + strlen(fullpath), "/");
strcpy(fullpath + strlen(fullpath), CF_DIR);
j = fullpath + strlen(fullpath);
for (i = 0; path[i]; i++, j++) // strcpy, but force lowercase
*j = tolower(path[i]);
*j = '\0';
if (CF_AlreadyOpen(fullpath, mode[0])) {
free (fullpath);
return -1;
}
if (mode[0] == 'w')
oldsize = CF_GetFileSize (fullpath);
else
oldsize = 0;
file = Qopen (fullpath, mode);
if (file) {
if (cf_openfiles >= cf_filepcount) {
cf_filepcount++;
cf_filep = realloc(cf_filep, sizeof(cf_file_t) * cf_filepcount);
if (!cf_filep) {
Sys_Error ("CF_Open: memory allocation error!");
}
cf_filep[cf_filepcount - 1].file = 0;
}
for (desc = 0; cf_filep[desc].file; desc++)
;
cf_filep[desc].path = fullpath;
cf_filep[desc].file = file;
cf_filep[desc].buf = 0;
cf_filep[desc].size = 0;
cf_filep[desc].writtento = 0;
cf_filep[desc].mode = mode[0];
cf_cursize -= oldsize;
cf_openfiles++;
return desc;
}
return -1;
}
/*
CF_Close
cfclose closes a file descriptor. returns nothing. to prevent
leakage, all open files are closed on map load, and warnings are
printed if developer is set to 1.
*/
void
CF_Close (int desc)
{
char *path;
if (!CF_ValidDesc(desc))
return;
if (cf_filep[desc].mode == 'w' && !cf_filep[desc].writtento)
unlink(cf_filep[desc].path);
path = cf_filep[desc].path;
Qclose (cf_filep[desc].file);
cf_filep[desc].file = 0;
free(cf_filep[desc].buf);
cf_openfiles--;
cf_cursize -= CF_GetFileSize (path);
free(path);
}
/*
CF_Read
cfread returns a single string read in from the file. an empty
string either means an empty string or eof, use cfeof to check.
*/
const char *
CF_Read (int desc)
{
int len = 0;
if (!CF_ValidDesc(desc) || cf_filep[desc].mode != 'r') {
return "";
}
do {
int foo;
if (cf_filep[desc].size <= len) {
char *t = realloc (cf_filep[desc].buf, cf_filep[desc].size + CF_BUFFERSIZE);
if (!t) {
Sys_Error ("CF_Read: memory allocation error!");
}
cf_filep[desc].buf = t;
cf_filep[desc].size += CF_BUFFERSIZE;
}
foo = Qgetc(cf_filep[desc].file);
if (foo == EOF)
foo = 0;
cf_filep[desc].buf[len] = (char) foo;
len++;
} while (cf_filep[desc].buf[len - 1]);
return cf_filep[desc].buf;
}
/*
CF_Write
cfwrite writes a string to the file, including a trailing nul,
returning the number of characters written. It returns 0 if
there was an error in writing, such as if the quota would have
been exceeded.
*/
int
CF_Write (int desc, const char *buf) // should be const char *, but Qwrite isn't...
{
int len;
if (!CF_ValidDesc(desc) || cf_filep[desc].mode != 'w' || cf_cursize >= cf_maxsize) {
return 0;
}
len = strlen(buf) + 1;
if (len > cf_maxsize - cf_cursize) {
return 0;
}
len = Qwrite(cf_filep[desc].file, buf, len);
if (len < 0)
len = 0;
cf_cursize += len;
cf_filep[desc].writtento = 1;
return len;
}
/*
CF_EOF
cfeof returns 1 if you're at the end of the file, 0 if not, and
-1 on a bad descriptor.
*/
int
CF_EOF (int desc)
{
if (!CF_ValidDesc(desc)) {
return -1;
}
return Qeof (cf_filep[desc].file);
}
/*
CF_Quota
cfquota returns the number of characters left in the quota, or
<= 0 if it's met or (somehow) exceeded.
*/
int CF_Quota()
{
return cf_maxsize - cf_cursize;
}

View file

@ -44,6 +44,7 @@
#include "server.h"
#include "sv_progs.h"
#include "world.h"
#include "crudefile.h"
server_t sv; // local server
@ -406,6 +407,9 @@ SV_SpawnServer (const char *server)
// serverflags are for cross level information (sigils)
*sv_globals.serverflags = svs.serverflags;
// close all CF files that progs didn't close from the last map
CF_CloseAllFiles ();
// run the frame start qc function to let progs check cvars
SV_ProgStartFrame ();

View file

@ -60,6 +60,7 @@
#include "pmove.h"
#include "server.h"
#include "sv_progs.h"
#include "crudefile.h"
quakeparms_t host_parms;
qboolean host_initialized; // true if into command execution
@ -1734,6 +1735,8 @@ SV_InitLocal (void)
Info_SetValueForStarKey (svs.info, "*qsg_version", QW_QSG_VERSION,
MAX_SERVERINFO_STRING);
CF_Init ();
// init fraglog stuff
svs.logsequence = 1;
svs.logtime = realtime;

View file

@ -43,6 +43,7 @@
#include "sv_pr_cmds.h"
#include "sv_progs.h"
#include "world.h"
#include "crudefile.h"
#define RETURN_EDICT(p, e) ((p)->pr_globals[OFS_RETURN].int_var = EDICT_TO_PROG(p, e))
#define RETURN_STRING(p, s) ((p)->pr_globals[OFS_RETURN].int_var = PR_SetString((p), s))
@ -1675,6 +1676,73 @@ PF_charcount (progs_t *pr)
}
/*
PF_cfopen
float(string path, string mode) cfopen
*/
void
PF_cfopen (progs_t *pr)
{
G_FLOAT (pr, OFS_RETURN) = CF_Open (G_STRING (pr, OFS_PARM0), G_STRING (pr, OFS_PARM1));
}
/*
PF_cfclose
void (float desc) cfclose
*/
void
PF_cfclose (progs_t *pr)
{
CF_Close ((int) G_FLOAT (pr, OFS_PARM0));
}
/*
PF_cfread
string (float desc) cfread
*/
void
PF_cfread (progs_t *pr)
{
RETURN_STRING (pr, CF_Read((int) G_FLOAT (pr, OFS_PARM0)));
}
/*
PF_cfwrite
float (float desc, string buf) cfwrite
*/
void
PF_cfwrite (progs_t *pr)
{
G_FLOAT (pr, OFS_RETURN) = CF_Write((int) G_FLOAT(pr, OFS_PARM0), G_STRING (pr, OFS_PARM1));
}
/*
PF_cfeof
float () cfeof
*/
void
PF_cfeof (progs_t *pr)
{
G_FLOAT (pr, OFS_RETURN) = CF_EOF ((int) G_FLOAT(pr, OFS_PARM0));
}
/*
PF_cfquota
float () cfquota
*/
void
PF_cfquota (progs_t *pr)
{
G_FLOAT (pr, OFS_RETURN) = CF_Quota();
}
void
PF_setinfokey (progs_t *pr)
{
@ -1855,6 +1923,12 @@ builtin_t sv_builtins[] = {
PF_strlen,
PF_charcount,
PF_setinfokey,
PF_cfopen,
PF_cfclose,
PF_cfread,
PF_cfwrite,
PF_cfeof,
PF_cfquota,
};
int sv_numbuiltins = sizeof (sv_builtins) / sizeof (sv_builtins[0]);