| top | | - proc uriassist—Implement the Tcl command uriassist. returns TCL_OK or TCL_ERROR.
| - input N—Number of parameters.
| - input P—Parameters.
| - io intr—The command result or an error message.
|
|
|
<Protocol task>
<Close close task>
static int uriassist(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]) {
ProtocolTask *(*activeProtocol) = Tcl_GetThreadData(&activeProtocolKey,sizeof(ProtocolTask*));
int N0 = N; Obj *P0 = P; int rc,index;
static chars subcommand[] = {
"scanname","protocol","channel","puts","log","closeclose",
0
};
enum {
o_scanname,o_protocol,o_channel,o_puts,o_log,o_closeclose
};
if (N<=1) {
Tcl_WrongNumArgs(intr,1,P0,"subcommand ..."); return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(intr,P[1],(CONST char**)subcommand,"subcommand",0,&index)!=TCL_OK) {
return TCL_ERROR;
}
N -= 2; P += 2;
switch (index) {
case o_scanname:
if (N==2) {
Obj name; chars dot = "";
int n; bytes block = Tcl_GetByteArrayFromObj(P[0],&n);
int offset,returnoffset; bool jumped = false,*hit;
if (Tcl_GetIntFromObj(intr,P[1],&offset)!=TCL_OK) return TCL_ERROR;
name = incr(Tcl_NewObj()); hit = nheap(n,bool); zero(n,bool,hit);
returnoffset = offset;
for (rc=TCL_OK; rc==TCL_OK && offset>0;) {
if ((block[offset]&0xC0)==0xC0) {
if (!jumped) returnoffset = offset+2;
offset = ((block[offset]<<8)+block[offset+1])&0x3FFF;
if (offset>=n) rc = rprintf(intr,"%!name offset outside of block",TCL_ERROR);
jumped = true;
}else if (hit[offset]) {
rc = rprintf(intr,"%!name offset loops in block",TCL_ERROR);
}else if (block[offset]+offset>n) {
rc = rprintf(intr,"%!name string beyond end of block",TCL_ERROR);
}else if (block[offset]==0) {
offset += 1;
if (!jumped) returnoffset = offset;
break;
}else {
Tcl_AppendToObj(name,dot,-1); dot = ".";
Tcl_AppendToObj(name,(chars)(&block[offset+1]),block[offset]);
offset += block[offset]+1;
if (!jumped) returnoffset = offset;
}
}
if (rc==TCL_OK) {
Obj E[2]; E[0] = name; E[1] = Tcl_NewIntObj(returnoffset);
Tcl_SetObjResult(intr,Tcl_NewListObj(2,E));
}
decr(name); dispose(hit);
return rc;
}else {
Tcl_WrongNumArgs(intr,2,P0,"block offset"); return TCL_ERROR;
}
case o_protocol:
if (N>=2 && N%2==0) {
int mood; Tcl_Channel channel = Tcl_GetChannel(intr,Tcl_GetString(P[0]),&mood);
int nv = N-2; Obj *pv = P+1;
ProtocolTask T = {intr,channel,0,0,0,0,0,TCL_OK,0};
if (!channel) return TCL_ERROR;
if (!(mood&TCL_READABLE)) return rprintf(intr,"%!channel not readable",TCL_ERROR);
if (!(mood&TCL_WRITABLE)) return rprintf(intr,"%!channel not writable",TCL_ERROR);
if (Tcl_ListObjGetElements(intr,P[N-1],&T.ns,&T.ps)!=TCL_OK) return TCL_ERROR;
if (T.ns%3!=0) return rprintf(intr,"%!switch length is not a multiple of three",TCL_ERROR);
if (asynchronous(&T)) {
ProtocolTask *t;
if (Tcl_SetChannelOption(intr,channel,"-blocking","0")!=TCL_OK) return TCL_ERROR;
t = heap(ProtocolTask); *t = T;
t->scripts = incr(P[N-1]);
t->label = nheap(4,char); strcpy(t->label,"GET");
Tcl_Preserve(intr);
Tcl_CreateChannelHandler(channel,TCL_READABLE|TCL_EXCEPTION,protocolTask,t);
Tcl_ResetResult(intr);
return TCL_OK;
}else {
if (Tcl_SetChannelOption(intr,channel,"-blocking","1")!=TCL_OK) return TCL_ERROR;
for (; nv>0; nv-=2,pv+=2) {
if (!Tcl_ObjSetVar2(intr,pv[0],0,pv[1],TCL_LEAVE_ERR_MSG)) return TCL_ERROR;
}
T.label = nheap(4,char); strcpy(T.label,"GET");
protocolTask((ClientData)(&T),TCL_READABLE);
if (T.rc==TCL_OK) {
Obj output = variable(&T,"output");
if (output) Tcl_SetObjResult(intr,output);
else Tcl_ResetResult(intr);
}
return T.rc;
}
}else {
Tcl_WrongNumArgs(intr,2,P0,"channel [var val]... switch"); return TCL_ERROR;
}
case o_channel:
if (N==1 || N==2) {
if (!(*activeProtocol)) {
return rprintf(intr,"not in an active protocol block",TCL_ERROR);
}else {
chars newlabel = N==2 ? Tcl_GetString(P[1]) : 0;
int mood; Tcl_Channel channel = Tcl_GetChannel(intr,Tcl_GetString(P[0]),&mood);
if (!channel) return TCL_ERROR;
if (!(mood&TCL_READABLE)) return rprintf(intr,"%!channel not readable",TCL_ERROR);
if (asynchronous(0)) {
if (Tcl_SetChannelOption(intr,channel,"-blocking","0")!=TCL_OK) return TCL_ERROR;
Tcl_DeleteChannelHandler((*activeProtocol)->channel,protocolTask,(*activeProtocol));
Tcl_CreateChannelHandler(channel,TCL_READABLE|TCL_EXCEPTION,protocolTask,(*activeProtocol));
}else {
if (Tcl_SetChannelOption(intr,channel,"-blocking","1")!=TCL_OK) return TCL_ERROR;
}
if (newlabel) {
dispose((*activeProtocol)->label);
(*activeProtocol)->label = nheap(1+strlen(newlabel),char);
strcpy((*activeProtocol)->label,newlabel);
}
(*activeProtocol)->channel = channel;
Tcl_ResetResult(intr);
return TCL_OK;
}
}else {
Tcl_WrongNumArgs(intr,2,P0,"channel [label]"); return TCL_ERROR;
}
case o_puts:
if (N==2) {
int mood; Tcl_Channel channel = Tcl_GetChannel(intr,Tcl_GetString(P[0]),&mood);
Obj line = P[1];
if (!channel) return TCL_ERROR;
if (!(mood&TCL_WRITABLE)) return rprintf(intr,"%!channel not writable",TCL_ERROR);
cprintf(channel,"%{y}s\n",line);
if (logging(0)) cprintf(chstderr,"PUT %{y}s\n",line);
Tcl_ResetResult(intr);
return TCL_OK;
}else {
Tcl_WrongNumArgs(intr,2,P0,"channel line"); return TCL_ERROR;
}
case o_log:
if (N==1) {
if (logging(0)) cprintf(chstderr,"%{y}s\n",P[0]);
Tcl_ResetResult(intr);
return TCL_OK;
}else {
Tcl_WrongNumArgs(intr,2,P0,"line"); return TCL_ERROR;
}
case o_closeclose:
if (N==2) {
Tcl_Channel channel1 = Tcl_GetChannel(intr,Tcl_GetString(P[0]),0);
Tcl_Channel channel2 = Tcl_GetChannel(intr,Tcl_GetString(P[1]),0);
ClosecloseTask *t;
if (!channel1 || !channel2) return TCL_ERROR;
t = heap(ClosecloseTask);
t->intr = intr; t->channel = channel2;
Tcl_Preserve(intr);
Tcl_CreateCloseHandler(channel1,closecloseTask,t);
Tcl_ResetResult(intr);
return TCL_OK;
}else {
Tcl_WrongNumArgs(intr,2,P0,"channel1 channel2"); return TCL_ERROR;
}
}
return rc;
}
| ^ | | | | | | section top | |
static bool channelIsBlocking(Intr intr,Tcl_Channel channel) {
return blocking;
}
| ^ | | | | section top | | 103. Protocol task :: -
- uriassist protocol
-
Drive the ASCII-Telnet protocol used by many of the access
methods. The protocol maintains a collection of variables as the current
state. Some of the variables have special meaning:
-
- line
- The last input line read in.
- state
- The state name.
- async
- Asynchronous response script.
- logging
- If true, log the transaction to stderr.
- output
- If defined, the output of the protocol.
If defined and the async script is not empty,
this is list appended to the script before evaluation.
As full lines become available, the state-glob and line-glob
patterns are compared to the state name and gotten line.
If the channel is at end of file, "!" is prepended to the state name,
and the gotten line is
set to "EOF". On I/O error, "!" is prepended to the state name,
and the gotten line is set to "ERR error-message".
On the
first matching pair, the state variables are defined
and the script is evaluated.
If no pair matches, it is an error.
Any changes to the variables are saved to the next evaluation of the protocol scripts.
The final state name is "!QUIT!"; when the state variable is set to this,
the protocol evaluator terminates.
If async is not empty, the output (if defined) and error message
(if an error occurred)
are list appended and the script evaluated.
Channel blocking is set based on whether there
is an async script.
typedef struct {
Intr intr; Tcl_Channel channel;
Obj scripts; int ns; Obj *ps;
Obj line;
Obj vars;
int rc; chars label;
} ProtocolTask;
static Tcl_ThreadDataKey activeProtocolKey;
static chars logging(ProtocolTask *t) {
ProtocolTask *(*activeProtocol) = Tcl_GetThreadData(&activeProtocolKey,sizeof(ProtocolTask*));
Obj obj; int l;
if (!t) t = (*activeProtocol);
if (!t) return 0;
obj = Tcl_GetVar2Ex(t->intr,"logging",0,0);
return obj && Tcl_GetBooleanFromObj(0,obj,&l)==TCL_OK && l ? t->label : 0;
}
static bool asynchronous(ProtocolTask *t) {
ProtocolTask *(*activeProtocol) = Tcl_GetThreadData(&activeProtocolKey,sizeof(ProtocolTask*));
Obj obj; int n;
if (!t) t = (*activeProtocol);
if (!t) return 0;
obj = Tcl_GetVar2Ex(t->intr,"async",0,0);
if (!obj) return 0;
Tcl_GetStringFromObj(obj,&n);
return n>0;
}
static Obj variable(ProtocolTask *t,chars var) {
ProtocolTask *(*activeProtocol) = Tcl_GetThreadData(&activeProtocolKey,sizeof(ProtocolTask*));
Obj obj;
if (!t) t = (*activeProtocol);
if (!t) return 0;
obj = Tcl_GetVar2Ex(t->intr,var,0,TCL_LEAVE_ERR_MSG);
return obj;
}
static void protocolTask(ClientData clientData,int mask) {
ProtocolTask *(*activeProtocol) = Tcl_GetThreadData(&activeProtocolKey,sizeof(ProtocolTask*));
ProtocolTask *t = (ProtocolTask*)clientData;
Tcl_SavedResult safe;
chars state,line; int i; chars label;
bool async = asynchronous(t);
if (async && Tcl_InterpDeleted(t->intr)) goto quit;
while (t->rc==TCL_OK) {
int nv; Obj *pv;
if (mask&TCL_READABLE) {
if (!t->line) t->line = incr(Tcl_NewObj());
switch (cgets(t->channel,t->line)) {
eof: case EOF: {
Obj state = variable(t,"state");
state = state ? oprintf("!%{y}s",state) : incr(Tcl_NewStringObj("!",1));
Tcl_SetVar2Ex(t->intr,"state",0,state,0); decr(state);
Tcl_SetStringObj(t->line,"EOF",3);
} break;
err: case ERR: {
Obj state = variable(t,"state");
state = state ? oprintf("!%{y}s",state) : incr(Tcl_NewStringObj("!",1));
Tcl_SetVar2Ex(t->intr,"state",0,state,0); decr(state);
decr(t->line); t->line = oprintf("ERR %s",Tcl_PosixError(t->intr));
} break;
case BLK:
return;
}
}else if (Tcl_Eof(t->channel)) {
goto eof;
}else {
goto err;
}
if (async) Tcl_SaveResult(t->intr,&safe);
t->rc = Tcl_ListObjGetElements(t->intr,variable(t,"state"),&nv,&pv);
state = t->rc==TCL_OK && nv>0 ? Tcl_GetString(*pv) : "";
line = Tcl_GetString(t->line);
if ((label=logging(t))) cprintf(chstderr,"%s <%s>%s\n",label,state,line);
for (i=0; i<t->ns && t->rc==TCL_OK; i+= 3) {
if (Tcl_StringMatch(state,Tcl_GetString(t->ps[i+0])) && Tcl_StringMatch(line,Tcl_GetString(t->ps[i+1]))) {
ProtocolTask *oactiveProtocol = (*activeProtocol); Obj state;
(*activeProtocol) = t;
if (async) {
Obj E[4],script; int rc; int nr; Obj *pr;
E[0] = Tcl_NewStringObj("::wyrm::wif::protocol",-1);
E[1] = t->line;
E[2] = t->vars;
E[3] = t->ps[i+2];
script = incr(Tcl_NewListObj(4,E));
decr(t->vars); t->vars = 0; decr(t->line); t->line = 0;
Tcl_EvalObjEx(t->intr,t->ps[i+2],0);
decr(script);
t->rc = Tcl_ListObjGetElements(t->intr,Tcl_GetObjResult(t->intr),&nr,&pr);
if (t->rc==TCL_OK && nr<2)
t->rc = rprintf(t->intr,"%!bad ::wyrm::wif::protocol result",TCL_ERROR);
if (t->rc==TCL_OK) {
if (Tcl_GetIntFromObj(0,pr[0],&t->rc)!=TCL_OK) {
t->rc = rprintf(t->intr,"%!bad ::wyrm::wif::protocol result",TCL_ERROR);
}else if (t->rc!=TCL_OK) {
Obj temp = incr(pr[1]); Tcl_SetObjResult(t->intr,temp); decr(temp);
}else {
t->vars = incr(Tcl_NewListObj(nr-2,pr+2));
}
}
}else {
Tcl_SetVar2Ex(t->intr,"line",0,t->line,0); decr(t->line); t->line = 0;
t->rc = Tcl_EvalObjEx(t->intr,t->ps[i+2],0);
}
(*activeProtocol) = oactiveProtocol;
state = variable(t,"state");
if (t && streq(Tcl_GetString(state),"!QUIT!")) t->rc = TCL_BREAK;
break;
}
}
if (i>=t->ns) {
t->rc = rprintf(t->intr,"%!no state was selected: %s",TCL_ERROR,state);
}
decr(t->line); t->line = 0;
}
if (async) {
{
Obj output = variable(t,"output");
Obj callback = variable(t,"async");
bool shared = Tcl_IsShared(callback) && (output || t->rc==TCL_ERROR);
Obj async = shared ? incr(Tcl_DuplicateObj(callback)) : callback;
if (output) Tcl_ListObjAppendElement(0,async,output);
if (t->rc==TCL_ERROR) Tcl_ListObjAppendElement(0,async,Tcl_GetObjResult(t->intr));
Tcl_EvalObjEx(t->intr,async,0);
Tcl_RestoreResult(t->intr,&safe);
if (shared) decr(async);
}
quit:
Tcl_DeleteChannelHandler(t->channel,protocolTask,clientData);
decr(t->scripts); decr(t->vars); dispose(t->label);
Tcl_Release(t->intr);
dispose(t);
Tcl_ResetResult(t->intr);
}else {
if (t->rc==TCL_BREAK) t->rc = TCL_OK;
if (t->rc==TCL_OK) {
Obj output = variable(t,"output");
if (output) Tcl_SetObjResult(t->intr,output); else Tcl_ResetResult(t->intr);
}
}
}
| ^ | | | | | | section top | |
typedef struct {
Intr intr; Tcl_Channel channel;
} ClosecloseTask;
static void closecloseTask(ClientData clientData) {
ClosecloseTask *t = (ClosecloseTask*)clientData;
if (!Tcl_InterpDeleted(t->intr)) {
Tcl_UnregisterChannel(t->intr,t->channel);
}
Tcl_Release(t->intr);
dispose(t);
}
| ^ | | | | | | section top | |
static int uriassist(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]);
| ^ | | |
| |