blob: a37460b19c50187aa7ac1253a9548506fb6bc092 [file] [log] [blame]
/*
* varchannel.c - simple channel implementation to write to var
* 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 <stdio.h>
#ifdef WIN32
#include <errno.h>
#else
#include <sys/errno.h>
#endif
#include <tcl.h>
#include "varchannel.h"
#include "webout.h" /* belongs to output module of websh */
/* ----------------------------------------------------------------------------
* close var channel
* ------------------------------------------------------------------------- */
int varchannelCloseProc(ClientData clientData, Tcl_Interp * interp)
{
/* printf("DBG varchannelCloseProc\n"); fflush(stdout); */
destroyVarChannel(clientData, interp);
/* nothign to do - var is managed by interp */
return 0;
}
/* ----------------------------------------------------------------------------
* input from var channel
* ------------------------------------------------------------------------- */
int varchannelInputProc(ClientData clientData,
char *buf, int bufSize, int *errorCodePtr)
{
char *str = NULL;
int strLen = 0;
int remains = 0;
VarChannel *varChannel = NULL;
int isNew = 0;
Tcl_Obj *var = NULL;
if ((clientData == NULL) || (buf == NULL))
return -1;
varChannel = (VarChannel *) clientData;
if (varChannel->varName == NULL)
return -1;
/* --------------------------------------------------------------------------
* get var
* ----------------------------------------------------------------------- */
var = Web_GetOrCreateGlobalVar(varChannel->interp,
varChannel->varName, &isNew);
if (var == NULL)
return -1;
if (isNew)
varChannel->readCursor = 0;
/* str = Tcl_GetStringFromObj(var,&strLen); */
str = (char *) Tcl_GetByteArrayFromObj(var, &strLen);
if (varChannel->readCursor < strLen) {
remains = strLen - varChannel->readCursor;
if (bufSize < remains) {
strncpy(buf, &(str[varChannel->readCursor]), bufSize);
varChannel->readCursor += bufSize;
if (isNew)
Tcl_DecrRefCount(var);
return bufSize;
}
else {
strncpy(buf, &(str[varChannel->readCursor]), remains);
varChannel->readCursor += remains;
if (isNew)
Tcl_DecrRefCount(var);
return remains;
}
}
if (isNew)
Tcl_DecrRefCount(var);
return 0;
}
/* ----------------------------------------------------------------------------
* output to var channel
* ------------------------------------------------------------------------- */
int varchannelOutputProc(ClientData clientData,
TCLCONST char *buf, int toWrite, int *errorCodePtr)
{
VarChannel *varChannel = NULL;
int res = -1;
int destLen = 0;
char *dest = NULL;
int bytesConv = 0;
Tcl_Obj *tmp = NULL;
int isNew = 0;
Tcl_Obj *var = NULL;
/* sanity */
if ((clientData == NULL) || (buf == NULL))
return res;
/* get interna */
varChannel = (VarChannel *) clientData;
if (varChannel->varName == NULL)
return -1;
/* --------------------------------------------------------------------------
* get var
* ----------------------------------------------------------------------- */
var = Web_GetOrCreateGlobalVar(varChannel->interp,
varChannel->varName, &isNew);
if (var == NULL)
return -1;
if (isNew) {
varChannel->readCursor = 0;
Tcl_DecrRefCount(var);
}
/* the conversion
*
* what we get from Tcl is the string in the encoding of this channel.
* Since we are writing to a variable, we need to back-translate
* the string to the encding currently in use by Tcl.
*/
/* convert to UTF */
destLen = (toWrite + 1) * TCL_UTF_MAX + 1;
dest = Tcl_Alloc(destLen);
if (dest == NULL)
return -1;
res = Tcl_ExternalToUtf(NULL, NULL, buf, toWrite, 0, NULL, dest, destLen,
NULL, &bytesConv, NULL);
if (res != TCL_OK) {
Tcl_Free(dest);
return -1;
}
/* now put UTF into Tcl_object */
tmp = Tcl_NewStringObj(dest, bytesConv);
if (tmp == NULL) {
Tcl_Free(dest);
return -1;
}
Tcl_IncrRefCount(tmp);
/* and append it to the global var */
var = Tcl_ObjSetVar2(varChannel->interp,
varChannel->varName,
NULL,
tmp,
TCL_GLOBAL_ONLY | TCL_APPEND_VALUE |
TCL_LEAVE_ERR_MSG);
/* cleanup */
Tcl_Free(dest);
Tcl_DecrRefCount(tmp);
if (var == NULL)
return -1;
return toWrite;
}
/* ----------------------------------------------------------------------------
* watch var channel
* ------------------------------------------------------------------------- */
void varchannelWatchProc(ClientData clientData, int mask)
{
}
/* ----------------------------------------------------------------------------
* getHandle for var channel
* ------------------------------------------------------------------------- */
int varchannelGetHandleProc(ClientData clientData, int direction,
ClientData * handlePtr)
{
/* "not implemented" */
return EINVAL;
}
/* ----------------------------------------------------------------------------
* varchannelType
* ------------------------------------------------------------------------- */
static Tcl_ChannelType varchannelType = {
"varchannel", /* Type name. */
NULL, /* Set blocking/nonblocking mode. */
varchannelCloseProc, /* Close proc. */
varchannelInputProc, /* Input proc. */
varchannelOutputProc, /* Output proc. */
NULL, /* Seek proc. */
NULL, /* Set option proc. */
NULL, /* Get option proc. */
varchannelWatchProc, /* Initialize notifier. */
varchannelGetHandleProc, /* Get OS handles out of channel. */
};
/* ----------------------------------------------------------------------------
* createVarChannel
* ------------------------------------------------------------------------- */
VarChannel *createVarChannel()
{
VarChannel *varChannel = NULL;
varChannel = WebAllocInternalData(VarChannel);
if (varChannel != NULL) {
varChannel->varName = NULL;
varChannel->interp = NULL;
varChannel->readCursor = 0;
}
return varChannel;
}
/* ----------------------------------------------------------------------------
* destroyVarChannel
* ------------------------------------------------------------------------- */
int destroyVarChannel(ClientData clientData, Tcl_Interp * interp)
{
VarChannel *varChannel = NULL;
/* printf("DBG - destroyVarChannel\n"); fflush(stdout); */
if (clientData != NULL) {
varChannel = (VarChannel *) clientData;
WebDecrRefCountIfNotNull(varChannel->varName);
Tcl_Free((char *) varChannel);
}
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* Web_GetChannelOrVarChannel - Tcl_GetChannel alike
* ------------------------------------------------------------------------- */
Tcl_Channel Web_GetChannelOrVarChannel(Tcl_Interp * interp,
char *name, int *mode)
{
Tcl_Channel channel = NULL;
if ((interp == NULL) || (name == NULL) || (mode == NULL))
return NULL;
if (name[0] == '#') {
/* get varchannel */
channel = Web_GetVarChannel(interp, name, mode);
}
else {
/* get normal channel */
channel = Tcl_GetChannel(interp, name, mode);
}
return channel;
}
/* ----------------------------------------------------------------------------
* Web_GetVarChannel - Tcl_GetChannel alike
* ------------------------------------------------------------------------- */
Tcl_Channel Web_GetVarChannel(Tcl_Interp * interp, char *name, int *mode)
{
Tcl_Obj *varNameObj = NULL;
Tcl_Obj *var = NULL;
VarChannel *varChannel = NULL;
Tcl_Channel channel = NULL;
int isNew = 0;
/* --------------------------------------------------------------------------
* expect two characters at least
* ----------------------------------------------------------------------- */
if (strlen(name) < 2)
return NULL;
/* --------------------------------------------------------------------------
* does channel already exist - good
* ----------------------------------------------------------------------- */
channel = Tcl_GetChannel(interp, name, mode);
if (channel != NULL)
return channel;
/* --------------------------------------------------------------------------
* unset Tcl_Result (we are not intetested, thank you)
* ----------------------------------------------------------------------- */
Tcl_ResetResult(interp);
/* --------------------------------------------------------------------------
* create VarChannel
* ----------------------------------------------------------------------- */
varChannel = createVarChannel();
if (varChannel == NULL)
return NULL;
/* --------------------------------------------------------------------------
* create name
* ----------------------------------------------------------------------- */
varNameObj = Tcl_NewStringObj(&name[1], -1);
Tcl_IncrRefCount(varNameObj);
varChannel->varName = varNameObj;
varChannel->interp = interp;
/* --------------------------------------------------------------------------
* create name
* ----------------------------------------------------------------------- */
var = Web_GetOrCreateGlobalVar(varChannel->interp,
varChannel->varName, &isNew);
if (isNew)
Tcl_DecrRefCount(var);
/* --------------------------------------------------------------------------
* create channel
* ----------------------------------------------------------------------- */
*mode = TCL_READABLE | TCL_WRITABLE;
channel = Tcl_CreateChannel(&varchannelType, name,
(ClientData) varChannel, *mode);
/* printf("DBG Web_GetVarChannel - gonna register '%s'\n",name); fflush(stdout); */
Tcl_RegisterChannel(interp, channel);
return channel;
}
/* ----------------------------------------------------------------------------
* Web_UnregisterVarChannel - Tcl_UnregisterChannel
* ------------------------------------------------------------------------- */
int Web_UnregisterVarChannel(Tcl_Interp * interp, char *name,
Tcl_Channel channel)
{
if ((interp == NULL) || (name == NULL))
return TCL_ERROR;
/* if name does not start with # we don't care */
if (name[0] != '#')
return TCL_OK;
if (channel == NULL) {
int mode = 0;
channel = Tcl_GetChannel(interp, name, &mode);
if (channel == NULL)
return TCL_ERROR;
}
return Tcl_UnregisterChannel(interp, channel);
}