fix nsv_array exists to return true for empty nsvs
/*
* The contents of this file are subject to the AOLserver Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://aolserver.com/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is AOLserver Code and related documentation
* distributed by AOL.
*
* The Initial Developer of the Original Code is America Online,
* Inc. Portions created by AOL are Copyright (C) 1999 America Online,
* Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License (the "GPL"), in which case the
* provisions of GPL are applicable instead of those above. If you wish
* to allow use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* License, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the License or the GPL.
*/
/*
* tclvar.c --
*
* Support for the old ns_var and new nsv_* commands.
*/
#include "nsd.h"
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/tclvar.c,v 1.15 2003/04/25 19:35:46 mpagenva Exp $, compiled: " __DATE__ " " __TIME__;
/*
* The following structure defines a collection of arrays.
* Only the arrays within a given bucket share a lock,
* allowing for more concurency in nsv.
*/
typedef struct Bucket {
Ns_Mutex lock;
Tcl_HashTable arrays;
} Bucket;
/*
* The following structure maintains the context for each variable
* array.
*/
typedef struct Array {
Bucket *bucketPtr; /* Array bucket. */
Tcl_HashEntry *entryPtr; /* Entry in bucket array table. */
Tcl_HashTable vars; /* Table of variables. */
} Array;
/*
* Forward declarations for coommands and routines defined in this file.
*/
static void SetVar(Array *, Tcl_Obj *key, Tcl_Obj *value);
static void UpdateVar(Tcl_HashEntry *hPtr, Tcl_Obj *obj);
static void FlushArray(Array *arrayPtr);
static Array *LockArray(void *arg, Tcl_Interp *interp, Tcl_Obj *array,
int create);
#define UnlockArray(arrayPtr) \
Ns_MutexUnlock(&((arrayPtr)->bucketPtr->lock));
/*
*----------------------------------------------------------------------
*
* NsTclNsvCreateBuckets --
*
* Create a new array of buckets for a server.
*
* Results:
* Pointer to bucket array.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
struct Bucket *
NsTclCreateBuckets(char *server, int n)
{
char buf[NS_THREAD_NAMESIZE];
Bucket *buckets;
buckets = ns_malloc(sizeof(Bucket) * n);
while (--n >= 0) {
sprintf(buf, "nsv:%d", n);
Tcl_InitHashTable(&buckets[n].arrays, TCL_STRING_KEYS);
Ns_MutexInit(&buckets[n].lock);
Ns_MutexSetName2(&buckets[n].lock, buf, server);
}
return buckets;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvGetObjCmd --
*
* Implements nsv_get.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvGetObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Tcl_HashEntry *hPtr;
Array *arrayPtr;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "array key");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[1], 0);
if (arrayPtr == NULL) {
return TCL_ERROR;
}
hPtr = Tcl_FindHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2]));
if (hPtr != NULL) {
Tcl_SetStringObj(Tcl_GetObjResult(interp), Tcl_GetHashValue(hPtr), -1);
}
UnlockArray(arrayPtr);
if (hPtr == NULL) {
Tcl_AppendResult(interp, "no such key: ", Tcl_GetString(objv[2]), NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvExistsObjCmd --
*
* Implements nsv_exists.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvExistsObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Array *arrayPtr;
int exists;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "array key");
return TCL_ERROR;
}
exists = 0;
arrayPtr = LockArray(arg, NULL, objv[1], 0);
if (arrayPtr != NULL) {
if (Tcl_FindHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2])) != NULL) {
exists = 1;
}
UnlockArray(arrayPtr);
}
Tcl_SetBooleanObj(Tcl_GetObjResult(interp), exists);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvSetObjCmd --
*
* Implelments nsv_set.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvSetObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Array *arrayPtr;
if (objc == 3) {
return NsTclNsvGetObjCmd(arg, interp, objc, objv);
} else if (objc != 4) {
Tcl_WrongNumArgs(interp, 1, objv, "array key ?value?");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[1], 1);
SetVar(arrayPtr, objv[2], objv[3]);
UnlockArray(arrayPtr);
Tcl_SetObjResult(interp, objv[3]);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvIncrObjCmd --
*
* Implements nsv_incr as an obj command.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvIncrObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Array *arrayPtr;
int count, current, result, new;
char *value;
Tcl_HashEntry *hPtr;
if (objc != 3 && objc != 4) {
Tcl_WrongNumArgs(interp, 1, objv, "array key ?count?");
return TCL_ERROR;
}
if (objc == 3) {
count = 1;
} else if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[1], 1);
hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2]), &new);
if (new) {
current = 0;
result = TCL_OK;
} else {
value = Tcl_GetHashValue(hPtr);
result = Tcl_GetInt(interp, value, ¤t);
}
if (result == TCL_OK) {
Tcl_Obj *obj = Tcl_GetObjResult(interp);
current += count;
Tcl_SetIntObj(obj, current);
UpdateVar(hPtr, obj);
}
UnlockArray(arrayPtr);
return result;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvLappendObjCmd --
*
* Implements nsv_lappend command.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvLappendObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Array *arrayPtr;
int i, new;
Tcl_HashEntry *hPtr;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 1, objv, "array key string ?string ...?");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[1], 1);
hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2]), &new);
if (new) {
Tcl_SetListObj(Tcl_GetObjResult(interp), objc-3, objv+3);
} else {
Tcl_SetResult(interp, Tcl_GetHashValue(hPtr), TCL_VOLATILE);
for (i = 3; i < objc; ++i) {
Tcl_AppendElement(interp, Tcl_GetString(objv[i]));
}
}
UpdateVar(hPtr, Tcl_GetObjResult(interp));
UnlockArray(arrayPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvAppendObjCmd --
*
* Implements nsv_append command.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvAppendObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Array *arrayPtr;
int i, new;
Tcl_HashEntry *hPtr;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 1, objv, "array key string ?string ...?");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[1], 1);
hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2]), &new);
if (!new) {
Tcl_SetResult(interp, Tcl_GetHashValue(hPtr), TCL_VOLATILE);
}
for (i = 3; i < objc; ++i) {
Tcl_AppendResult(interp, Tcl_GetString(objv[i]), NULL);
}
UpdateVar(hPtr, Tcl_GetObjResult(interp));
UnlockArray(arrayPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvArrayObjCmd --
*
* Implements nsv_array as an obj command.
*
* Results:
* Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvArrayObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Array *arrayPtr;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
char *pattern, *key;
int i, lobjc, size;
Tcl_Obj *result, **lobjv;
static CONST char *opts[] = {
"set", "reset", "get", "names", "size", "exists", NULL
};
enum ISubCmdIdx {
CSetIdx, CResetIdx, CGetIdx, CNamesIdx, CSizeIdx, CExistsIdx
} opt;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ...");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,
(int *) &opt) != TCL_OK) {
return TCL_ERROR;
}
result = Tcl_GetObjResult(interp);
switch (opt) {
case CSetIdx:
case CResetIdx:
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "array valueList");
return TCL_ERROR;
}
if (Tcl_ListObjGetElements(interp, objv[3], &lobjc,
&lobjv) != TCL_OK) {
return TCL_ERROR;
}
if (lobjc & 1) {
Tcl_AppendResult(interp, "invalid list: ",
Tcl_GetString(objv[3]), NULL);
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[2], 1);
if (opt == CResetIdx) {
FlushArray(arrayPtr);
}
for (i = 0; i < lobjc; i += 2) {
SetVar(arrayPtr, lobjv[i], lobjv[i+1]);
}
UnlockArray(arrayPtr);
break;
case CSizeIdx:
case CExistsIdx:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "array");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, NULL, objv[2], 0);
if (arrayPtr == NULL) {
size = 0;
} else {
size = (opt == CSizeIdx) ? arrayPtr->vars.numEntries : 1;
UnlockArray(arrayPtr);
}
if (opt == CExistsIdx) {
Tcl_SetBooleanObj(result, size);
} else {
Tcl_SetIntObj(result, size);
}
break;
case CGetIdx:
case CNamesIdx:
if (objc != 3 && objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "array ?pattern?");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, NULL, objv[2], 0);
if (arrayPtr != NULL) {
pattern = (objc > 3) ? Tcl_GetString(objv[3]) : NULL;
hPtr = Tcl_FirstHashEntry(&arrayPtr->vars, &search);
while (hPtr != NULL) {
key = Tcl_GetHashKey(&arrayPtr->vars, hPtr);
if (pattern == NULL || Tcl_StringMatch(key, pattern)) {
Tcl_AppendElement(interp, key);
if (opt == CGetIdx) {
Tcl_AppendElement(interp, Tcl_GetHashValue(hPtr));
}
}
hPtr = Tcl_NextHashEntry(&search);
}
UnlockArray(arrayPtr);
}
break;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvUnsetObjCmd --
*
* Implements nsv_unset as an obj command.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvUnsetObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
Tcl_HashEntry *hPtr = NULL;
Array *arrayPtr = NULL;
if (objc != 2 && objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "array ?key?");
return TCL_ERROR;
}
arrayPtr = LockArray(arg, interp, objv[1], 0);
if (arrayPtr == NULL) {
return TCL_ERROR;
}
if (objc == 2) {
Tcl_DeleteHashEntry(arrayPtr->entryPtr);
} else {
hPtr = Tcl_FindHashEntry(&arrayPtr->vars, Tcl_GetString(objv[2]));
if (hPtr != NULL) {
ns_free(Tcl_GetHashValue(hPtr));
Tcl_DeleteHashEntry(hPtr);
}
}
UnlockArray(arrayPtr);
if (objc == 2) {
FlushArray(arrayPtr);
Tcl_DeleteHashTable(&arrayPtr->vars);
ns_free(arrayPtr);
} else if (hPtr == NULL) {
Tcl_AppendResult(interp, "no such key: ", Tcl_GetString(objv[2]), NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsTclNsvNamesObjCmd --
*
* Implements nsv_names as an obj command.
*
* Results:
* Tcl result.
*
* Side effects:
* See docs.
*
*----------------------------------------------------------------------
*/
int
NsTclNsvNamesObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj **objv)
{
NsInterp *itPtr = arg;
NsServer *servPtr = itPtr->servPtr;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Tcl_Obj *result;
Bucket *bucketPtr;
char *pattern, *key;
int i;
if (objc != 1 && objc !=2) {
Tcl_WrongNumArgs(interp, 1, objv, "?pattern?");
return TCL_ERROR;
}
pattern = objc < 2 ? NULL : Tcl_GetString(objv[1]);
/*
* Walk the bucket list for each array.
*/
result = Tcl_GetObjResult(interp);
for (i = 0; i < servPtr->nsv.nbuckets; i++) {
bucketPtr = &servPtr->nsv.buckets[i];
Ns_MutexLock(&bucketPtr->lock);
hPtr = Tcl_FirstHashEntry(&bucketPtr->arrays, &search);
while (hPtr != NULL) {
key = Tcl_GetHashKey(&bucketPtr->arrays, hPtr);
if (pattern == NULL || Tcl_StringMatch(key, pattern)) {
Tcl_ListObjAppendElement(NULL, result,
Tcl_NewStringObj(key, -1));
}
hPtr = Tcl_NextHashEntry(&search);
}
Ns_MutexUnlock(&bucketPtr->lock);
}
return TCL_OK;
}
/*
*----------------------------------------------------------------
*
* LockArray --
*
* Find (or create) the Array structure for an array and
* lock it. Array structure must be later unlocked with
* UnlockArray.
*
* Results:
* TCL_OK or TCL_ERROR if no such array.
*
* Side effects;
* Sets *arrayPtrPtr with Array pointer or leave error in
* given Tcl_Interp.
*
*----------------------------------------------------------------
*/
static Array *
LockArray(void *arg, Tcl_Interp *interp, Tcl_Obj *arrayObj, int create)
{
NsInterp *itPtr = arg;
Bucket *bucketPtr;
Tcl_HashEntry *hPtr;
Array *arrayPtr;
char *array;
register char *p;
register unsigned int result;
register int i;
int new;
array = Tcl_GetString(arrayObj);
p = array;
result = 0;
while (1) {
i = *p;
p++;
if (i == 0) {
break;
}
result += (result<<3) + i;
}
i = result % itPtr->servPtr->nsv.nbuckets;
bucketPtr = &itPtr->servPtr->nsv.buckets[i];
Ns_MutexLock(&bucketPtr->lock);
if (create) {
hPtr = Tcl_CreateHashEntry(&bucketPtr->arrays, array, &new);
if (!new) {
arrayPtr = Tcl_GetHashValue(hPtr);
} else {
arrayPtr = ns_malloc(sizeof(Array));
arrayPtr->bucketPtr = bucketPtr;
arrayPtr->entryPtr = hPtr;
Tcl_InitHashTable(&arrayPtr->vars, TCL_STRING_KEYS);
Tcl_SetHashValue(hPtr, arrayPtr);
}
} else {
hPtr = Tcl_FindHashEntry(&bucketPtr->arrays, array);
if (hPtr == NULL) {
Ns_MutexUnlock(&bucketPtr->lock);
if (interp != NULL) {
Tcl_AppendResult(interp, "no such array: ", array, NULL);
}
return NULL;
}
arrayPtr = Tcl_GetHashValue(hPtr);
}
return arrayPtr;
}
/*
*----------------------------------------------------------------
*
* UpdateVar --
*
* Update a variable entry.
*
* Results:
* None.
*
* Side effects;
* New value is set.
*
*----------------------------------------------------------------
*/
static void
UpdateVar(Tcl_HashEntry *hPtr, Tcl_Obj *obj)
{
char *str, *old, *new;
int len;
str = Tcl_GetStringFromObj(obj, &len);
old = Tcl_GetHashValue(hPtr);
new = ns_realloc(old, (size_t)(len+1));
memcpy(new, str, (size_t)(len+1));
Tcl_SetHashValue(hPtr, new);
}
/*
*----------------------------------------------------------------
*
* SetVar --
*
* Set (or reset) an array entry.
*
* Results:
* None.
*
* Side effects;
* New entry is created and updated.
*
*----------------------------------------------------------------
*/
static void
SetVar(Array *arrayPtr, Tcl_Obj *key, Tcl_Obj *value)
{
Tcl_HashEntry *hPtr;
int new;
hPtr = Tcl_CreateHashEntry(&arrayPtr->vars, Tcl_GetString(key), &new);
UpdateVar(hPtr, value);
}
/*
*----------------------------------------------------------------
*
* FlushArray --
*
* Unset all keys in an array.
*
* Results:
* None.
*
* Side effects;
* New entry is created and updated.
*
*----------------------------------------------------------------
*/
static void
FlushArray(Array *arrayPtr)
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
hPtr = Tcl_FirstHashEntry(&arrayPtr->vars, &search);
while (hPtr != NULL) {
ns_free(Tcl_GetHashValue(hPtr));
Tcl_DeleteHashEntry(hPtr);
hPtr = Tcl_NextHashEntry(&search);
}
}
/*
*----------------------------------------------------------------------
*
* NsTclVarObjCmd --
*
* Implements ns_var (deprecated)
*
* Results:
* Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
NsTclVarObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv)
{
NsInterp *itPtr = arg;
NsServer *servPtr;
Tcl_HashTable *tablePtr;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
int new, code;
char *var = NULL, *val = NULL;
static CONST char *opts[] = {
"exists", "get", "list", "set", "unset", NULL
};
enum {
VExistsIdx, VGetIdx, VListIdx, VSetIdx, VUnsetIdx
} opt;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?args?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0,
(int *) &opt) != TCL_OK) {
return TCL_ERROR;
}
servPtr = itPtr->servPtr;
tablePtr = &servPtr->var.table;
code = TCL_OK;
if (objc > 2) {
var = Tcl_GetString(objv[2]);
}
Ns_MutexLock(&servPtr->var.lock);
switch (opt) {
case VExistsIdx:
case VGetIdx:
case VUnsetIdx:
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "var");
code = TCL_ERROR;
} else {
hPtr = Tcl_FindHashEntry(tablePtr, var);
if (opt == VExistsIdx) {
Tcl_SetBooleanObj(Tcl_GetObjResult(interp), hPtr ? 1 : 0);
} else if (hPtr == NULL) {
Tcl_AppendResult(interp, "no such variable \"", var,
"\"", NULL);
code = TCL_ERROR;
} else if (opt == VGetIdx) {
Tcl_SetResult(interp, Tcl_GetHashValue(hPtr), TCL_VOLATILE);
} else {
ns_free(Tcl_GetHashValue(hPtr));
Tcl_DeleteHashEntry(hPtr);
}
}
break;
case VSetIdx:
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "var value");
code = TCL_ERROR;
} else {
hPtr = Tcl_CreateHashEntry(tablePtr, var, &new);
if (!new) {
ns_free(Tcl_GetHashValue(hPtr));
}
val = Tcl_GetString(objv[3]);
Tcl_SetHashValue(hPtr, ns_strdup(val));
Tcl_SetResult(interp, val, TCL_VOLATILE);
}
break;
case VListIdx:
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
while (hPtr != NULL) {
Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr));
hPtr = Tcl_NextHashEntry(&search);
}
break;
}
Ns_MutexUnlock(&servPtr->var.lock);
return code;
}
|
Back to SourceForge.net Powered by ViewCVS 1.0-dev |