blob: b5f417dc1be2025245e4f86daa3e612485567814 [file] [log] [blame]
/*
* paramlist.c -- key-value pairs, where value is a Tcl list Obj
* nca-073-9
*
* Copyright (c) 1996-2000 by Netcetera AG.
* Copyright (c) 2001 by Apache Software Foundation.
* All rights reserved.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* @(#) $Id$
*
*/
#include <tcl.h>
#include "webutl.h"
#include "paramlist.h"
#include "log.h"
#include "hashutl.h"
static TCLCONST char *paramsubcmd[] =
{ "-count", "-unset", "-set", "-lappend", "-names", NULL };
enum paramsubcmd
{ PARAM_COUNT, PARAM_UNSET, PARAM_SET, PARAM_LAPPEND, PARAM_NAMES };
/* the number of options in paramsubcmd */
#define PARAMSUBCMDLEN 5
/* ----------------------------------------------------------------------------
* paramGetIndexFromObj -- same as Tcl_GetIndexFromObj, but includes
* the values from static table paramsubcmd.
* ------------------------------------------------------------------------- */
int paramGetIndexFromObj(Tcl_Interp * interp, Tcl_Obj * obj, char **tablePtr,
char *msg, int flags, int *indexPtr)
{
char **allopts = NULL;
int i = 0, numprivateopts, po;
Tcl_Obj *objCopy = Tcl_DuplicateObj(obj);
Tcl_IncrRefCount(objCopy);
/* dynamically determine size of allopts */
while (tablePtr[i]) {i++;}
allopts = (char **) Tcl_Alloc((i+PARAMSUBCMDLEN+1) * sizeof(char *));
if (allopts == NULL) {
return TCL_ERROR;
}
i = 0;
while (tablePtr[i]) {
allopts[i] = tablePtr[i];
i++;
}
numprivateopts = i;
po = i;
i = 0;
while (paramsubcmd[i]) {
allopts[po] = (char *) paramsubcmd[i];
po++;
i++;
}
allopts[po] = NULL;
if (Tcl_GetIndexFromObj(interp, objCopy, (TCLCONST char **)allopts, msg, flags, indexPtr) ==
TCL_OK) {
if (*indexPtr < numprivateopts) {
Tcl_DecrRefCount(objCopy);
Tcl_Free((char *)allopts);
return TCL_OK;
}
}
Tcl_DecrRefCount(objCopy);
Tcl_Free((char*) allopts);
return TCL_ERROR;
}
/* ----------------------------------------------------------------------------
* paramListSet -- set value to new ListObj with just the given value
* ------------------------------------------------------------------------- */
int paramListSet(ParamList * hash, char *key, Tcl_Obj * value)
{
Tcl_Obj *existingValue;
Tcl_Obj *copy;
if ((hash == NULL) || (key == NULL) || (value == NULL))
return TCL_ERROR;
/* --------------------------------------------------------------------------
* remove existing
* ----------------------------------------------------------------------- */
if ((existingValue = (Tcl_Obj *) removeFromHashTable(hash, key)) != NULL)
Tcl_DecrRefCount(existingValue);
/* --------------------------------------------------------------------------
* force it into a list
* ----------------------------------------------------------------------- */
copy = Tcl_NewListObj(1, &value);
Tcl_IncrRefCount(copy);
return appendToHashTable(hash, key, (ClientData) copy);
}
/* ----------------------------------------------------------------------------
* paramListSetAsWhole -- set value to new ListObj with just the given value
* without listifying the element
* ------------------------------------------------------------------------- */
int __declspec(dllexport) paramListSetAsWhole(ParamList * hash, char *key, Tcl_Obj * value)
{
Tcl_Obj *existingValue = NULL;
if ((hash == NULL) || (key == NULL) || (value == NULL))
return TCL_ERROR;
/* --------------------------------------------------------------------------
* remove existing
* ----------------------------------------------------------------------- */
if ((existingValue = (Tcl_Obj *) removeFromHashTable(hash, key)) != NULL)
Tcl_DecrRefCount(existingValue);
Tcl_IncrRefCount(value);
return appendToHashTable(hash, key, (ClientData) value);
}
/* ----------------------------------------------------------------------------
* paramListAdd -- append value to existing ListObj under key
* ------------------------------------------------------------------------- */
int __declspec(dllexport) paramListAdd(ParamList * hash, char *key, Tcl_Obj * value)
{
Tcl_Obj *existing = NULL;
Tcl_Obj *new = NULL;
int iRes = -1;
if ((hash == NULL) || (key == NULL) || (value == NULL))
return TCL_ERROR;
/* --------------------------------------------------------------------------
* does key already exist ?
* ----------------------------------------------------------------------- */
if ((existing = (Tcl_Obj *) getFromHashTable(hash, key)) == NULL) {
/* ------------------------------------------------------------------------
* no, create new
* --------------------------------------------------------------------- */
return paramListSet(hash, key, value);
}
/* --------------------------------------------------------------------------
* yes, append
* ----------------------------------------------------------------------- */
if (Tcl_IsShared(existing)) {
if ((existing = (Tcl_Obj *) removeFromHashTable(hash, key)) == NULL)
return TCL_ERROR;
new = Tcl_DuplicateObj(existing);
Tcl_IncrRefCount(new);
Tcl_DecrRefCount(existing);
iRes = Tcl_ListObjAppendElement(NULL, new, value);
appendToHashTable(hash, key, (ClientData) new);
return iRes;
}
iRes = Tcl_ListObjAppendElement(NULL, existing, value);
return iRes;
}
/* ----------------------------------------------------------------------------
* paramListDel -- delete existing
* ------------------------------------------------------------------------- */
int paramListDel(ParamList * hash, char *key)
{
Tcl_Obj *tclo = NULL;
if ((hash == NULL) || (key == NULL))
return TCL_ERROR;
/* --------------------------------------------------------------------------
* remove existing
* ----------------------------------------------------------------------- */
if ((tclo = (Tcl_Obj *) removeFromHashTable(hash, key)) != NULL)
Tcl_DecrRefCount(tclo);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* paramListAsListObj -- append each entry of list to ListObj
* results in list with even number of elements of the form
* k1 v1 k1 v2 ... kj vn
* ------------------------------------------------------------------------- */
Tcl_Obj *paramListAsListObj(ParamList * hash)
{
HashTableIterator iterator;
Tcl_Obj *res = NULL;
Tcl_Obj *key = NULL;
Tcl_Obj *val = NULL;
Tcl_Obj *ele = NULL;
int valLen = 0;
int i;
if (hash == NULL)
return NULL;
assignIteratorToHashTable(hash, &iterator);
res = Tcl_NewObj();
if (res == NULL)
return NULL;
Tcl_IncrRefCount(res);
while (nextFromHashIterator(&iterator) != TCL_ERROR) {
key = Tcl_NewStringObj(keyOfCurrent(&iterator), -1);
if (key == NULL)
break;
Tcl_IncrRefCount(key);
val = (Tcl_Obj *) valueOfCurrent(&iterator);
if (val == NULL) {
Tcl_DecrRefCount(key);
break;
}
Tcl_IncrRefCount(val);
if (Tcl_ListObjLength(NULL, val, &valLen) == TCL_ERROR) {
Tcl_DecrRefCount(key);
Tcl_DecrRefCount(val);
break;
}
for (i = 0; i < valLen; i++) {
if (Tcl_ListObjAppendElement(NULL, res, key) == TCL_ERROR)
break;
if (Tcl_ListObjIndex(NULL, val, i, &ele) == TCL_ERROR)
break;
if (Tcl_ListObjAppendElement(NULL, res, ele) == TCL_ERROR)
break;
}
Tcl_DecrRefCount(key);
Tcl_DecrRefCount(val);
}
return res;
}
/* ----------------------------------------------------------------------------
* listObjAsParamList -- convert Tcl_ListObj to internal representation
* ------------------------------------------------------------------------- */
int listObjAsParamList(Tcl_Obj * list, ParamList * hash)
{
int listLen = 0;
int i;
Tcl_Obj *key = NULL;
Tcl_Obj *val = NULL;
if ((list == NULL) || (hash == NULL))
return TCL_ERROR;
/* --------------------------------------------------------------------------
* is even numbered ?
* ----------------------------------------------------------------------- */
if (Tcl_ListObjLength(NULL, list, &listLen) == TCL_ERROR)
return TCL_ERROR;
if ((listLen % 2) != 0) {
/* no. last is single key. append value */
val = Tcl_NewObj();
Tcl_ListObjAppendElement(NULL, list, val);
}
if (Tcl_ListObjLength(NULL, list, &listLen) == TCL_ERROR)
return TCL_ERROR;
for (i = 0; i < listLen; i += 2) {
key = NULL;
val = NULL;
Tcl_ListObjIndex(NULL, list, i, &key);
if (key == NULL)
return TCL_ERROR;
Tcl_ListObjIndex(NULL, list, i + 1, &val);
if (val == NULL)
return TCL_ERROR;
if (paramListAdd(hash, Tcl_GetString(key), val) == TCL_ERROR)
return TCL_ERROR;
}
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* paramListNamesAll -- create list of all existing keys matching pattern
* ------------------------------------------------------------------------- */
Tcl_Obj *paramListNamesAll(ParamList * hash)
{
HashTableIterator iterator;
char *key = NULL;
Tcl_Obj *res = NULL;
if (hash == NULL)
return NULL;
assignIteratorToHashTable(hash, &iterator);
res = Tcl_NewObj();
if (res == NULL)
return NULL;
Tcl_IncrRefCount(res);
while (nextFromHashIterator(&iterator) != TCL_ERROR) {
key = keyOfCurrent(&iterator);
if (key != NULL) {
if (Tcl_ListObjAppendElement(NULL, res, Tcl_NewStringObj(key, -1))
== TCL_ERROR)
break;
}
}
return res;
}
/* ----------------------------------------------------------------------------
* destroyParamList -- free the HashTable with and the values (Tcl_Obj)
* ------------------------------------------------------------------------- */
void destroyParamList(ParamList * hash)
{
HashTableIterator iterator;
Tcl_Obj *tclo = NULL;
if (hash != NULL) {
assignIteratorToHashTable(hash, &iterator);
while (nextFromHashIterator(&iterator) != TCL_ERROR) {
tclo = (Tcl_Obj *) valueOfCurrent(&iterator);
if (tclo != NULL) {
Tcl_DecrRefCount(tclo);
}
}
HashUtlDelFree(hash);
}
}
/* ----------------------------------------------------------------------------
* paramGet
* tries if it is a generic paramlist subcommand
* returns
* TCL_OK if command handled
* TCL_ERROR if there was an error
* TCL_CONTINUE if command is not handled
*
* ------------------------------------------------------------------------- */
int paramGet(ParamList * paramList,
Tcl_Interp * interp,
int objc, Tcl_Obj * CONST objv[], int hasPrivate)
{
char *arg = NULL;
int opt = 0;
/* --------------------------------------------------------------------------
* sanity
* ----------------------------------------------------------------------- */
WebAssertData(interp, paramList, "paramList/paramGet", TCL_ERROR);
/* if there are no args -> continue with private parsing */
if (objc < 2) {
if (hasPrivate)
return TCL_CONTINUE;
else {
WebAssertObjc(1, 1, "args ...");
}
}
/* see if there is a subcommand */
arg = Tcl_GetString(objv[1]);
if (arg[0] == '-') {
if (Tcl_GetIndexFromObj(interp, objv[1],
paramsubcmd, "subcommand", 0,
&opt) == TCL_ERROR) {
if (hasPrivate) {
/* we ignore the error here cause we might have private commands */
Tcl_ResetResult(interp);
return TCL_CONTINUE;
}
else {
return TCL_ERROR;
}
}
/* it's one of our's */
switch (opt) {
case PARAM_COUNT:
WebAssertObjc(objc != 3, 2, "key");
return paramListCountValue(interp, paramList, objv[2]);
case PARAM_UNSET:
WebAssertObjc(objc > 3, 2, "?key?");
if (objc == 3)
return paramListDel(paramList, Tcl_GetString(objv[2]));
else
emptyParamList(paramList);
return TCL_OK;
case PARAM_SET:
WebAssertObjc(objc < 3, 2, "key ?value ...?");
if (objc != 3) {
int i = 3;
paramListDel(paramList, Tcl_GetString(objv[2]));
while (i < objc)
paramListAdd(paramList, Tcl_GetString(objv[2]),
objv[i++]);
}
/* return value, just like tcl */
return paramListGetValueToResult(interp, paramList, objv[2],
NULL);
case PARAM_LAPPEND:{
int i = 3;
WebAssertObjc(objc < 4, 2, "key value ?value ...?");
/* we append stuff */
while (i < objc)
paramListAdd(paramList, Tcl_GetString(objv[2]),
objv[i++]);
/* return list, just like tcl */
return paramListGetValueToResult(interp, paramList, objv[2],
NULL);
}
case PARAM_NAMES:{
Tcl_Obj *obj = paramListNamesAll(paramList);
if (objc != 2) {
Tcl_WrongNumArgs(interp, 2, objv, NULL);
Tcl_DecrRefCount(obj);
return TCL_ERROR;
}
if (obj) {
Tcl_SetObjResult(interp, obj);
Tcl_DecrRefCount(obj);
}
else {
Tcl_ResetResult(interp);
}
return TCL_OK;
}
default:
return TCL_ERROR; /* should never be reached. */
}
}
else {
/* no subcommand -> return value */
WebAssertObjc(objc > 3, 1, "key ?default?");
if (objc == 3) {
return paramListGetValueToResult(interp, paramList, objv[1],
objv[2]);
} else {
return paramListGetValueToResult(interp, paramList, objv[1],
NULL);
}
}
/* we never get here */
/* return TCL_ERROR; */
}
int paramListGetValueToResult(Tcl_Interp * interp, ParamList * paramList,
Tcl_Obj * key, Tcl_Obj * defaultObj)
{
Tcl_Obj *resObj;
resObj = paramListGetObject(interp, paramList, key);
if (resObj)
Tcl_SetObjResult(interp, resObj);
else if (defaultObj)
Tcl_SetObjResult(interp, Tcl_DuplicateObj(defaultObj));
else
Tcl_ResetResult(interp);
return TCL_OK;
}
Tcl_Obj *paramListGetObject(Tcl_Interp * interp, ParamList * paramList,
Tcl_Obj * key)
{
return paramListGetObjectByString(interp, paramList, Tcl_GetString(key));
}
Tcl_Obj *paramListGetObjectByString(Tcl_Interp * interp,
ParamList * paramList, char *key)
{
Tcl_Obj *resObj;
resObj = (Tcl_Obj *) getFromHashTable((Tcl_HashTable *) paramList, key);
if (resObj) {
if (tclGetListLength(interp, resObj) == 1) {
/* if the value has only one element, we return that */
Tcl_Obj *firstEl = NULL;
if (Tcl_ListObjIndex(interp, resObj, 0, &firstEl) == TCL_ERROR)
return NULL;
else
return Tcl_DuplicateObj(firstEl);
}
else
return Tcl_DuplicateObj(resObj);
}
return NULL;
}
int paramListCountValue(Tcl_Interp * interp, ParamList * paramList,
Tcl_Obj * key)
{
Tcl_Obj *resObj;
int n = 0;
resObj =
(Tcl_Obj *) getFromHashTable((Tcl_HashTable *) paramList,
Tcl_GetString(key));
if (resObj)
n = tclGetListLength(interp, resObj);
Tcl_SetObjResult(interp, Tcl_NewIntObj(n));
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* emptyParamList -- delete values of param list (Tcl_Obj)
* ------------------------------------------------------------------------- */
void emptyParamList(ParamList * paramList)
{
/* HashTableIterator iterator; */
Tcl_Obj *tclo = NULL;
Tcl_HashTable *hash = paramList;
Tcl_HashSearch hs;
Tcl_HashEntry *he;
if (hash != NULL) {
while ((he = Tcl_FirstHashEntry(hash, &hs))) {
tclo = (Tcl_Obj *) Tcl_GetHashValue(he);
if (tclo != NULL)
Tcl_DecrRefCount(tclo);
Tcl_DeleteHashEntry(he);
}
}
}