mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 16:10:53 +00:00
453 lines
11 KiB
C
453 lines
11 KiB
C
/*
|
|
bi_inputline.c
|
|
|
|
CSQC string hashes builtins
|
|
|
|
Copyright (C) 2002 Robin Redeker <elmex@x-paste.de>
|
|
|
|
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
|
|
|
|
*/
|
|
static const char rcsid[] =
|
|
"$Id$";
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include "QF/console.h"
|
|
#include "QF/draw.h"
|
|
#include "QF/progs.h"
|
|
#include "QF/zone.h"
|
|
|
|
#define MAX_SH_VALUES 16
|
|
|
|
/*
|
|
DESIGN NOTE (by elmex):
|
|
|
|
This file contains code for QuakeC hashes.
|
|
The hases are designed to have around 10 sub-values for
|
|
one key.
|
|
Hashes are stored as hash-ids in the QC-environment.
|
|
|
|
The key->value pairs in the hashes are stored in the
|
|
order they are set. So its easy to make array-hashes.
|
|
The function which gets the Lenght of a hash is specialy
|
|
made for array purposes.
|
|
|
|
TODO: Check the FIXME's below in the code.
|
|
(please taniwha ;)
|
|
(about memory leaks)
|
|
*/
|
|
|
|
// a hash element
|
|
typedef struct {
|
|
char * key;
|
|
char * values[MAX_SH_VALUES];
|
|
} str_hash_elem;
|
|
|
|
// a structure of a hash
|
|
typedef struct {
|
|
str_hash_elem **elements;
|
|
int cnt_elements;
|
|
} str_hash;
|
|
|
|
// a list structure of hashes
|
|
typedef struct {
|
|
str_hash **hashes;
|
|
int cnt_hashes;
|
|
} strh_resources_t;
|
|
|
|
/*
|
|
bi_StringHash_Create
|
|
|
|
Creates a hash structure and gives back the hash-id to the
|
|
QC-environment
|
|
*/
|
|
static void
|
|
bi_StringHash_Create (progs_t *pr)
|
|
{
|
|
strh_resources_t *res = PR_Resources_Find (pr, "StringHash");
|
|
int i;
|
|
int hash_id=-1;
|
|
|
|
// check if there is a empty hash in the array
|
|
for(i = 0; i < res->cnt_hashes; i++) {
|
|
if(res->hashes[i]->cnt_elements == 0) {
|
|
// we found a empty already allocated hash
|
|
hash_id = i;
|
|
break;
|
|
}
|
|
}
|
|
if(hash_id == -1) {
|
|
/* allocate a new hash struct, if
|
|
there is no old one */
|
|
if(res->hashes == NULL) { // allocate a new list of hashes
|
|
res->hashes = (str_hash **) malloc(
|
|
sizeof(str_hash*) * (res->cnt_hashes + 1));
|
|
} else { // reallocate the list of hashes
|
|
res->hashes = (str_hash **) realloc(res->hashes,
|
|
sizeof(str_hash*) * (res->cnt_hashes + 1));
|
|
}
|
|
hash_id = res->cnt_hashes;
|
|
|
|
res->hashes[hash_id] =
|
|
(str_hash*) malloc(sizeof(str_hash));
|
|
// dont forge to clean the hash
|
|
memset(res->hashes[hash_id],0,sizeof(str_hash));
|
|
|
|
res->cnt_hashes++; // increase cnt of allocated hashes
|
|
}
|
|
G_INT (pr, OFS_RETURN) = hash_id;
|
|
}
|
|
|
|
/*
|
|
bi_StringHash_Destroy
|
|
|
|
Destroys a hash
|
|
*/
|
|
static void
|
|
bi_StringHash_Destroy (progs_t *pr)
|
|
{
|
|
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
|
int hash_id = G_INT (pr, OFS_PARM0);
|
|
str_hash *sh = NULL;
|
|
int i,d;
|
|
|
|
if(hash_id >= res->cnt_hashes || hash_id < 0) {
|
|
G_INT (pr, OFS_RETURN) = 0;
|
|
return;
|
|
}
|
|
sh = res->hashes[hash_id];
|
|
|
|
/* we dont really destroy the allocated hash,
|
|
but we free the elements of the hash
|
|
and mark it for reuseing */
|
|
|
|
for(i = 0; i < sh->cnt_elements; i++) {
|
|
if(sh->elements[i] != NULL) {
|
|
/* they should never be NULL,
|
|
buy, who knows? */
|
|
PR_Error(pr, "NULL hash-element found -> not supposed!");
|
|
} else {
|
|
for(d=0;d<MAX_SH_VALUES;d++) {
|
|
// free key
|
|
free(sh->elements[i]->key);
|
|
// free values
|
|
if(sh->elements[i]->values[d] != NULL) {
|
|
free(sh->elements[i]->values[d]);
|
|
}
|
|
}
|
|
free(sh->elements[i]); // free str_hash_elem structs
|
|
}
|
|
/*
|
|
FIXME: HELP: taniwha??? do i have to FREE
|
|
the strings ??
|
|
*/
|
|
}
|
|
free(sh->elements); // free the list pointer
|
|
sh->elements = NULL;
|
|
sh->cnt_elements = 0;
|
|
G_INT (pr, OFS_RETURN) = 1;
|
|
}
|
|
|
|
|
|
/*
|
|
bi_StringHash_Set
|
|
|
|
Sets a the key-value (with a special id) in a hash
|
|
to a value.
|
|
If a non existing key is given a element if generated for it.
|
|
FIXME: look if this functions does mem-leak
|
|
*/
|
|
static void
|
|
bi_StringHash_Set (progs_t *pr)
|
|
{
|
|
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
|
int hash_id = G_INT (pr, OFS_PARM0);
|
|
const char *key = G_STRING (pr, OFS_PARM1);
|
|
const char *val = G_STRING (pr, OFS_PARM2);
|
|
int val_id = G_INT (pr, OFS_PARM3);
|
|
str_hash *sh = NULL;
|
|
int i,found_fl=0;
|
|
|
|
// validate the hash ID
|
|
if(res->hashes == NULL ||
|
|
(hash_id >= res->cnt_hashes || hash_id < 0) ||
|
|
(val_id < 0 || val_id >= MAX_SH_VALUES))
|
|
{
|
|
G_INT (pr, OFS_RETURN) = 0;
|
|
return;
|
|
}
|
|
sh = res->hashes[hash_id];
|
|
|
|
// first search for existing key
|
|
for(i = 0; i < sh->cnt_elements; i++) {
|
|
if(strcmp(sh->elements[i]->key, key) == 0) {
|
|
// found already a element with that key
|
|
if(sh->elements[i]->values[val_id] == NULL) { // empty val
|
|
// strdup() because strings can dissappear
|
|
sh->elements[i]->values[val_id] = strdup(val);
|
|
} else {
|
|
// when using strdup(), we have to free the stuff properly
|
|
free(sh->elements[i]->values[val_id]);
|
|
sh->elements[i]->values[val_id] = strdup(val);
|
|
}
|
|
found_fl = 1;
|
|
}
|
|
}
|
|
if(!found_fl) { // add a new element
|
|
if(sh->elements == NULL) { // alloc new elements list pointer
|
|
sh->elements = (str_hash_elem**) malloc(sizeof(str_hash_elem*));
|
|
sh->cnt_elements = 0; // 0 because usage as index here
|
|
} else {
|
|
sh->elements = (str_hash_elem**) realloc(sh->elements,
|
|
sizeof(str_hash_elem*) * (sh->cnt_elements+1));
|
|
}
|
|
sh->elements[sh->cnt_elements] = malloc(sizeof(str_hash_elem));
|
|
memset(sh->elements[sh->cnt_elements],0,sizeof(str_hash_elem));
|
|
|
|
sh->elements[sh->cnt_elements]->key = strdup(key);
|
|
sh->elements[sh->cnt_elements]->values[val_id] = strdup(val);
|
|
|
|
sh->cnt_elements++;
|
|
}
|
|
G_INT (pr, OFS_RETURN) = 1;
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
bi_StringHash_SetIdx
|
|
|
|
Sets a the key-value (with a special id) in a hash
|
|
to a value. This function works by index of the element.
|
|
A element in hash is NOT generated automatically.
|
|
FIXME: look if this functions does mem-leak
|
|
*/
|
|
static void
|
|
bi_StringHash_SetIdx (progs_t *pr)
|
|
{
|
|
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
|
int hash_id = G_INT (pr, OFS_PARM0);
|
|
int idx = G_INT (pr, OFS_PARM1);
|
|
const char *val = G_STRING (pr, OFS_PARM2);
|
|
int val_id = G_INT (pr, OFS_PARM3);
|
|
str_hash *sh = NULL;
|
|
|
|
// validate the hash ID
|
|
if(res->hashes == NULL ||
|
|
(hash_id >= res->cnt_hashes || hash_id < 0) ||
|
|
(val_id < 0 || val_id >= MAX_SH_VALUES))
|
|
{
|
|
G_INT (pr, OFS_RETURN) = 0;
|
|
return;
|
|
}
|
|
sh = res->hashes[hash_id];
|
|
|
|
if(idx < 0 || idx >= sh->cnt_elements || sh->elements[idx] == NULL) {
|
|
if(sh->elements[idx] == NULL)
|
|
PR_Error(pr, "NULL hash-element found -> not supposed!");
|
|
|
|
G_INT (pr, OFS_RETURN) = 0;
|
|
return;
|
|
}
|
|
|
|
if(sh->elements[idx]->values[val_id] != NULL) {
|
|
free(sh->elements[idx]->values[val_id]);
|
|
}
|
|
sh->elements[idx]->values[val_id] = strdup(val);
|
|
|
|
|
|
G_INT (pr, OFS_RETURN) = 1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
bi_StringHash_Get
|
|
|
|
Gets the value of a key and its id in a hash
|
|
*/
|
|
static void
|
|
bi_StringHash_Get (progs_t *pr)
|
|
{
|
|
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
|
int hash_id = G_INT (pr, OFS_PARM0);
|
|
const char *key = G_STRING (pr, OFS_PARM1);
|
|
int val_id = G_INT (pr, OFS_PARM2);
|
|
str_hash *sh = NULL;
|
|
int i,found_fl=0;
|
|
const char *retstr = NULL;
|
|
|
|
// validate the hash ID
|
|
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 ||
|
|
val_id >= MAX_SH_VALUES)
|
|
{
|
|
retstr = "";
|
|
RETURN_STRING(pr, retstr);
|
|
return;
|
|
}
|
|
sh = res->hashes[hash_id];
|
|
|
|
// first search for existing key
|
|
for(i = 0; i < sh->cnt_elements; i++) {
|
|
if(strcmp(sh->elements[i]->key, key) == 0) {
|
|
if(sh->elements[i]->values[val_id] != NULL) {
|
|
retstr = sh->elements[i]->values[val_id];
|
|
} else {
|
|
retstr = "";
|
|
}
|
|
found_fl = 1;
|
|
}
|
|
}
|
|
if(!found_fl) {
|
|
retstr = "";
|
|
}
|
|
RETURN_STRING(pr, retstr);
|
|
}
|
|
|
|
/*
|
|
bi_StringHash_Length
|
|
|
|
Gets the count of the elements in a hash
|
|
*/
|
|
static void
|
|
bi_StringHash_Length (progs_t *pr)
|
|
{
|
|
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
|
int hash_id = G_INT (pr, OFS_PARM0);
|
|
str_hash *sh = NULL;
|
|
|
|
// validate the hash ID
|
|
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) {
|
|
G_INT (pr, OFS_RETURN) = 0;
|
|
return;
|
|
}
|
|
sh = res->hashes[hash_id];
|
|
|
|
G_INT (pr, OFS_RETURN) = sh->cnt_elements;
|
|
}
|
|
|
|
/*
|
|
bi_StringHash_GetIdx
|
|
|
|
Gets a hash elment by its index
|
|
special: if the val_id is -1 the key of the element will
|
|
be returned
|
|
*/
|
|
static void
|
|
bi_StringHash_GetIdx (progs_t *pr)
|
|
{
|
|
strh_resources_t* res = PR_Resources_Find (pr, "StringHash");
|
|
int hash_id = G_INT (pr, OFS_PARM0);
|
|
int idx = G_INT (pr, OFS_PARM1);
|
|
int val_id = G_INT (pr, OFS_PARM2);
|
|
str_hash *sh = NULL;
|
|
const char *retstr = NULL;
|
|
|
|
// validate the hash ID
|
|
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0) {
|
|
retstr = NULL;
|
|
}
|
|
sh = res->hashes[hash_id];
|
|
|
|
if(idx < 0 || idx >= sh->cnt_elements ||
|
|
(val_id < -1 || val_id >= MAX_SH_VALUES))
|
|
{
|
|
retstr = NULL;
|
|
} else {
|
|
if(val_id == -1) {
|
|
retstr = sh->elements[idx]->key;
|
|
} else {
|
|
retstr = sh->elements[idx]->values[val_id];
|
|
}
|
|
}
|
|
if(retstr == NULL) { retstr = ""; }
|
|
|
|
RETURN_STRING(pr, retstr);
|
|
}
|
|
|
|
/*
|
|
bi_strh_clear
|
|
|
|
Free the dynamic allocated memory
|
|
XXX: taniwha: i dont know what to free
|
|
exactly, could you validate this code?
|
|
*/
|
|
static void
|
|
bi_strh_clear (progs_t *pr, void *data)
|
|
{
|
|
strh_resources_t *res = (strh_resources_t *)data;
|
|
int i,d;
|
|
|
|
for (i = 0; i < res->cnt_hashes; i++) {
|
|
if (res->hashes[i]) {
|
|
for(d = 0; d < res->hashes[i]->cnt_elements; d++) {
|
|
free(res->hashes[i]->elements[d]);
|
|
}
|
|
free(res->hashes[i]->elements);
|
|
free(res->hashes[i]);
|
|
res->hashes[i] = 0;
|
|
}
|
|
}
|
|
free (res->hashes);
|
|
res->hashes = 0;
|
|
res->cnt_hashes = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
StringHash_Progs_Init
|
|
|
|
Inits the Progs-system with StringHash resources and
|
|
functions.
|
|
*/
|
|
void
|
|
StringHash_Progs_Init (progs_t *pr)
|
|
{
|
|
strh_resources_t *res = malloc (sizeof (strh_resources_t));
|
|
res->cnt_hashes = 0;
|
|
res->hashes = NULL;
|
|
|
|
PR_Resources_Register (pr, "StringHash", res, bi_strh_clear);
|
|
PR_AddBuiltin (pr, "StringHash_Create", bi_StringHash_Create, -1);
|
|
PR_AddBuiltin (pr, "StringHash_Destroy", bi_StringHash_Destroy, -1);
|
|
PR_AddBuiltin (pr, "StringHash_Set", bi_StringHash_Set, -1);
|
|
PR_AddBuiltin (pr, "StringHash_Get", bi_StringHash_Get, -1);
|
|
PR_AddBuiltin (pr, "StringHash_Length", bi_StringHash_Length, -1);
|
|
PR_AddBuiltin (pr, "StringHash_SetIdx", bi_StringHash_SetIdx, -1);
|
|
PR_AddBuiltin (pr, "StringHash_GetIdx", bi_StringHash_GetIdx, -1);
|
|
}
|
|
|
|
/*
|
|
XXX NOTE by elmex:
|
|
A file, decripted like this is what
|
|
i want to see everywhere in qf-cvs =)
|
|
No excuse for undocumented code and design without
|
|
a reason for it.
|
|
We/I want to know why something was designed how it is.
|
|
*/
|