tclIOGT.cGo to the documentation of this file.00001 /* 00002 * tclIOGT.c -- 00003 * 00004 * Implements a generic transformation exposing the underlying API at the 00005 * script level. Contributed by Andreas Kupries. 00006 * 00007 * Copyright (c) 2000 Ajuba Solutions 00008 * Copyright (c) 1999-2000 Andreas Kupries (a.kupries@westend.com) 00009 * 00010 * See the file "license.terms" for information on usage and redistribution of 00011 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * CVS: $Id: tclIOGT.c,v 1.20 2007/12/13 15:23:18 dgp Exp $ 00014 */ 00015 00016 #include "tclInt.h" 00017 #include "tclIO.h" 00018 00019 /* 00020 * Forward declarations of internal procedures. First the driver procedures of 00021 * the transformation. 00022 */ 00023 00024 static int TransformBlockModeProc(ClientData instanceData, 00025 int mode); 00026 static int TransformCloseProc(ClientData instanceData, 00027 Tcl_Interp *interp); 00028 static int TransformInputProc(ClientData instanceData, char *buf, 00029 int toRead, int *errorCodePtr); 00030 static int TransformOutputProc(ClientData instanceData, 00031 const char *buf, int toWrite, int *errorCodePtr); 00032 static int TransformSeekProc(ClientData instanceData, long offset, 00033 int mode, int *errorCodePtr); 00034 static int TransformSetOptionProc(ClientData instanceData, 00035 Tcl_Interp *interp, const char *optionName, 00036 const char *value); 00037 static int TransformGetOptionProc(ClientData instanceData, 00038 Tcl_Interp *interp, const char *optionName, 00039 Tcl_DString *dsPtr); 00040 static void TransformWatchProc(ClientData instanceData, int mask); 00041 static int TransformGetFileHandleProc(ClientData instanceData, 00042 int direction, ClientData *handlePtr); 00043 static int TransformNotifyProc(ClientData instanceData, int mask); 00044 static Tcl_WideInt TransformWideSeekProc(ClientData instanceData, 00045 Tcl_WideInt offset, int mode, int *errorCodePtr); 00046 00047 /* 00048 * Forward declarations of internal procedures. Secondly the procedures for 00049 * handling and generating fileeevents. 00050 */ 00051 00052 static void TransformChannelHandlerTimer(ClientData clientData); 00053 00054 /* 00055 * Forward declarations of internal procedures. Third, helper procedures 00056 * encapsulating essential tasks. 00057 */ 00058 00059 typedef struct TransformChannelData TransformChannelData; 00060 00061 static int ExecuteCallback(TransformChannelData *ctrl, 00062 Tcl_Interp *interp, unsigned char *op, 00063 unsigned char *buf, int bufLen, int transmit, 00064 int preserve); 00065 00066 /* 00067 * Action codes to give to 'ExecuteCallback' (argument 'transmit'), telling 00068 * the procedure what to do with the result of the script it calls. 00069 */ 00070 00071 #define TRANSMIT_DONT 0 /* No transfer to do. */ 00072 #define TRANSMIT_DOWN 1 /* Transfer to the underlying channel. */ 00073 #define TRANSMIT_SELF 2 /* Transfer into our channel. */ 00074 #define TRANSMIT_IBUF 3 /* Transfer to internal input buffer. */ 00075 #define TRANSMIT_NUM 4 /* Transfer number to 'maxRead'. */ 00076 00077 /* 00078 * Codes for 'preserve' of 'ExecuteCallback'. 00079 */ 00080 00081 #define P_PRESERVE 1 00082 #define P_NO_PRESERVE 0 00083 00084 /* 00085 * Strings for the action codes delivered to the script implementing a 00086 * transformation. Argument 'op' of 'ExecuteCallback'. 00087 */ 00088 00089 #define A_CREATE_WRITE (UCHARP("create/write")) 00090 #define A_DELETE_WRITE (UCHARP("delete/write")) 00091 #define A_FLUSH_WRITE (UCHARP("flush/write")) 00092 #define A_WRITE (UCHARP("write")) 00093 00094 #define A_CREATE_READ (UCHARP("create/read")) 00095 #define A_DELETE_READ (UCHARP("delete/read")) 00096 #define A_FLUSH_READ (UCHARP("flush/read")) 00097 #define A_READ (UCHARP("read")) 00098 00099 #define A_QUERY_MAXREAD (UCHARP("query/maxRead")) 00100 #define A_CLEAR_READ (UCHARP("clear/read")) 00101 00102 /* 00103 * Management of a simple buffer. 00104 */ 00105 00106 typedef struct ResultBuffer ResultBuffer; 00107 00108 static inline void ResultClear(ResultBuffer *r); 00109 static inline void ResultInit(ResultBuffer *r); 00110 static inline int ResultEmpty(ResultBuffer *r); 00111 static inline int ResultCopy(ResultBuffer *r, unsigned char *buf, 00112 size_t toRead); 00113 static inline void ResultAdd(ResultBuffer *r, unsigned char *buf, 00114 size_t toWrite); 00115 00116 /* 00117 * This structure describes the channel type structure for Tcl-based 00118 * transformations. 00119 */ 00120 00121 static Tcl_ChannelType transformChannelType = { 00122 "transform", /* Type name. */ 00123 TCL_CHANNEL_VERSION_5, /* v5 channel */ 00124 TransformCloseProc, /* Close proc. */ 00125 TransformInputProc, /* Input proc. */ 00126 TransformOutputProc, /* Output proc. */ 00127 TransformSeekProc, /* Seek proc. */ 00128 TransformSetOptionProc, /* Set option proc. */ 00129 TransformGetOptionProc, /* Get option proc. */ 00130 TransformWatchProc, /* Initialize notifier. */ 00131 TransformGetFileHandleProc, /* Get OS handles out of channel. */ 00132 NULL, /* close2proc */ 00133 TransformBlockModeProc, /* Set blocking/nonblocking mode.*/ 00134 NULL, /* Flush proc. */ 00135 TransformNotifyProc, /* Handling of events bubbling up. */ 00136 TransformWideSeekProc, /* Wide seek proc. */ 00137 NULL, /* Thread action. */ 00138 NULL, /* Truncate. */ 00139 }; 00140 00141 /* 00142 * Possible values for 'flags' field in control structure, see below. 00143 */ 00144 00145 #define CHANNEL_ASYNC (1<<0) /* Non-blocking mode. */ 00146 00147 /* 00148 * Definition of the structure containing the information about the internal 00149 * input buffer. 00150 */ 00151 00152 struct ResultBuffer { 00153 unsigned char *buf; /* Reference to the buffer area. */ 00154 size_t allocated; /* Allocated size of the buffer area. */ 00155 size_t used; /* Number of bytes in the buffer, no more than 00156 * number allocated. */ 00157 }; 00158 00159 /* 00160 * Additional bytes to allocate during buffer expansion. 00161 */ 00162 00163 #define INCREMENT 512 00164 00165 /* 00166 * Number of milliseconds to wait before firing an event to flush out 00167 * information waiting in buffers (fileevent support). 00168 */ 00169 00170 #define FLUSH_DELAY 5 00171 00172 /* 00173 * Convenience macro to make some casts easier to use. 00174 */ 00175 00176 #define UCHARP(x) ((unsigned char *) (x)) 00177 00178 /* 00179 * Definition of a structure used by all transformations generated here to 00180 * maintain their local state. 00181 */ 00182 00183 struct TransformChannelData { 00184 /* 00185 * General section. Data to integrate the transformation into the channel 00186 * system. 00187 */ 00188 00189 Tcl_Channel self; /* Our own Channel handle. */ 00190 int readIsFlushed; /* Flag to note whether in.flushProc was 00191 * called or not. */ 00192 int flags; /* Currently CHANNEL_ASYNC or zero. */ 00193 int watchMask; /* Current watch/event/interest mask. */ 00194 int mode; /* Mode of parent channel, OR'ed combination 00195 * of TCL_READABLE, TCL_WRITABLE. */ 00196 Tcl_TimerToken timer; /* Timer for automatic flushing of information 00197 * sitting in an internal buffer. Required for 00198 * full fileevent support. */ 00199 00200 /* 00201 * Transformation specific data. 00202 */ 00203 00204 int maxRead; /* Maximum allowed number of bytes to read, as 00205 * given to us by the Tcl script implementing 00206 * the transformation. */ 00207 Tcl_Interp *interp; /* Reference to the interpreter which created 00208 * the transformation. Used to execute the 00209 * code below. */ 00210 Tcl_Obj *command; /* Tcl code to execute for a buffer */ 00211 ResultBuffer result; /* Internal buffer used to store the result of 00212 * a transformation of incoming data. Also 00213 * serves as buffer of all data not yet 00214 * consumed by the reader. */ 00215 }; 00216 00217 /* 00218 *---------------------------------------------------------------------- 00219 * 00220 * TclChannelTransform -- 00221 * 00222 * Implements the Tcl "testchannel transform" debugging command. This is 00223 * part of the testing environment. This sets up a tcl script (cmdObjPtr) 00224 * to be used as a transform on the channel. 00225 * 00226 * Results: 00227 * A standard Tcl result. 00228 * 00229 * Side effects: 00230 * None. 00231 * 00232 *---------------------------------------------------------------------- 00233 */ 00234 00235 /* ARGSUSED */ 00236 int 00237 TclChannelTransform( 00238 Tcl_Interp *interp, /* Interpreter for result. */ 00239 Tcl_Channel chan, /* Channel to transform. */ 00240 Tcl_Obj *cmdObjPtr) /* Script to use for transform. */ 00241 { 00242 Channel *chanPtr; /* The actual channel. */ 00243 ChannelState *statePtr; /* State info for channel. */ 00244 int mode; /* Read/write mode of the channel. */ 00245 TransformChannelData *dataPtr; 00246 Tcl_DString ds; 00247 00248 if (chan == NULL) { 00249 return TCL_ERROR; 00250 } 00251 00252 chanPtr = (Channel *) chan; 00253 statePtr = chanPtr->state; 00254 chanPtr = statePtr->topChanPtr; 00255 chan = (Tcl_Channel) chanPtr; 00256 mode = (statePtr->flags & (TCL_READABLE|TCL_WRITABLE)); 00257 00258 /* 00259 * Now initialize the transformation state and stack it upon the specified 00260 * channel. One of the necessary things to do is to retrieve the blocking 00261 * regime of the underlying channel and to use the same for us too. 00262 */ 00263 00264 dataPtr = (TransformChannelData *) ckalloc(sizeof(TransformChannelData)); 00265 00266 Tcl_DStringInit(&ds); 00267 Tcl_GetChannelOption(interp, chan, "-blocking", &ds); 00268 dataPtr->readIsFlushed = 0; 00269 dataPtr->flags = 0; 00270 if (ds.string[0] == '0') { 00271 dataPtr->flags |= CHANNEL_ASYNC; 00272 } 00273 Tcl_DStringFree(&ds); 00274 00275 dataPtr->self = chan; 00276 dataPtr->watchMask = 0; 00277 dataPtr->mode = mode; 00278 dataPtr->timer = NULL; 00279 dataPtr->maxRead = 4096; /* Initial value not relevant. */ 00280 dataPtr->interp = interp; 00281 dataPtr->command = cmdObjPtr; 00282 Tcl_IncrRefCount(dataPtr->command); 00283 00284 ResultInit(&dataPtr->result); 00285 00286 dataPtr->self = Tcl_StackChannel(interp, &transformChannelType, dataPtr, 00287 mode, chan); 00288 if (dataPtr->self == NULL) { 00289 Tcl_AppendResult(interp, "\nfailed to stack channel \"", 00290 Tcl_GetChannelName(chan), "\"", NULL); 00291 Tcl_DecrRefCount(dataPtr->command); 00292 ResultClear(&dataPtr->result); 00293 ckfree((char *) dataPtr); 00294 return TCL_ERROR; 00295 } 00296 00297 /* 00298 * At last initialize the transformation at the script level. 00299 */ 00300 00301 if ((dataPtr->mode & TCL_WRITABLE) && ExecuteCallback(dataPtr, NULL, 00302 A_CREATE_WRITE, NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE) != TCL_OK){ 00303 Tcl_UnstackChannel(interp, chan); 00304 return TCL_ERROR; 00305 } 00306 00307 if ((dataPtr->mode & TCL_READABLE) && ExecuteCallback(dataPtr, NULL, 00308 A_CREATE_READ, NULL, 0, TRANSMIT_DONT, P_NO_PRESERVE) != TCL_OK) { 00309 ExecuteCallback(dataPtr, NULL, A_DELETE_WRITE, NULL, 0, TRANSMIT_DONT, 00310 P_NO_PRESERVE); 00311 Tcl_UnstackChannel(interp, chan); 00312 return TCL_ERROR; 00313 } 00314 00315 return TCL_OK; 00316 } 00317 00318 /* 00319 *---------------------------------------------------------------------- 00320 * 00321 * ExecuteCallback -- 00322 * 00323 * Executes the defined callback for buffer and operation. 00324 * 00325 * Side effects: 00326 * As of the executed tcl script. 00327 * 00328 * Result: 00329 * A standard TCL error code. In case of an error a message is left in 00330 * the result area of the specified interpreter. 00331 * 00332 *---------------------------------------------------------------------- 00333 */ 00334 00335 static int 00336 ExecuteCallback( 00337 TransformChannelData *dataPtr, 00338 /* Transformation with the callback. */ 00339 Tcl_Interp *interp, /* Current interpreter, possibly NULL. */ 00340 unsigned char *op, /* Operation invoking the callback. */ 00341 unsigned char *buf, /* Buffer to give to the script. */ 00342 int bufLen, /* And its length. */ 00343 int transmit, /* Flag, determines whether the result of the 00344 * callback is sent to the underlying channel 00345 * or not. */ 00346 int preserve) /* Flag. If true the procedure will preserve 00347 * the result state of all accessed 00348 * interpreters. */ 00349 { 00350 Tcl_Obj *resObj; /* See below, switch (transmit). */ 00351 int resLen; 00352 unsigned char *resBuf; 00353 Tcl_InterpState state = NULL; 00354 int res = TCL_OK; 00355 Tcl_Obj *command = Tcl_DuplicateObj(dataPtr->command); 00356 00357 /* 00358 * Step 1, create the complete command to execute. Do this by appending 00359 * operation and buffer to operate upon to a copy of the callback 00360 * definition. We *cannot* create a list containing 3 objects and then use 00361 * 'Tcl_EvalObjv', because the command may contain additional prefixed 00362 * arguments. Feather's curried commands would come in handy here. 00363 */ 00364 00365 if (preserve == P_PRESERVE) { 00366 state = Tcl_SaveInterpState(dataPtr->interp, res); 00367 } 00368 00369 Tcl_IncrRefCount(command); 00370 res = Tcl_ListObjAppendElement(dataPtr->interp, command, 00371 Tcl_NewStringObj((char *) op, -1)); 00372 if (res != TCL_OK) { 00373 goto cleanup; 00374 } 00375 00376 /* 00377 * Use a byte-array to prevent the misinterpretation of binary data coming 00378 * through as UTF while at the tcl level. 00379 */ 00380 00381 res = Tcl_ListObjAppendElement(dataPtr->interp, command, 00382 Tcl_NewByteArrayObj(buf, bufLen)); 00383 if (res != TCL_OK) { 00384 goto cleanup; 00385 } 00386 00387 /* 00388 * Step 2, execute the command at the global level of the interpreter used 00389 * to create the transformation. Destroy the command afterward. If an 00390 * error occured and the current interpreter is defined and not equal to 00391 * the interpreter for the callback, then copy the error message into 00392 * current interpreter. Don't copy if in preservation mode. 00393 */ 00394 00395 res = Tcl_EvalObjEx(dataPtr->interp, command, TCL_EVAL_GLOBAL); 00396 Tcl_DecrRefCount(command); 00397 command = NULL; 00398 00399 if ((res != TCL_OK) && (interp != NULL) && (dataPtr->interp != interp) 00400 && (preserve == P_NO_PRESERVE)) { 00401 Tcl_SetObjResult(interp, Tcl_GetObjResult(dataPtr->interp)); 00402 return res; 00403 } 00404 00405 /* 00406 * Step 3, transmit a possible conversion result to the underlying 00407 * channel, or ourselves. 00408 */ 00409 00410 switch (transmit) { 00411 case TRANSMIT_DONT: 00412 /* nothing to do */ 00413 break; 00414 00415 case TRANSMIT_DOWN: 00416 resObj = Tcl_GetObjResult(dataPtr->interp); 00417 resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen); 00418 Tcl_WriteRaw(Tcl_GetStackedChannel(dataPtr->self), (char *) resBuf, 00419 resLen); 00420 break; 00421 00422 case TRANSMIT_SELF: 00423 resObj = Tcl_GetObjResult(dataPtr->interp); 00424 resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen); 00425 Tcl_WriteRaw(dataPtr->self, (char *) resBuf, resLen); 00426 break; 00427 00428 case TRANSMIT_IBUF: 00429 resObj = Tcl_GetObjResult(dataPtr->interp); 00430 resBuf = Tcl_GetByteArrayFromObj(resObj, &resLen); 00431 ResultAdd(&dataPtr->result, resBuf, resLen); 00432 break; 00433 00434 case TRANSMIT_NUM: 00435 /* 00436 * Interpret result as integer number. 00437 */ 00438 00439 resObj = Tcl_GetObjResult(dataPtr->interp); 00440 TclGetIntFromObj(dataPtr->interp, resObj, &dataPtr->maxRead); 00441 break; 00442 } 00443 00444 Tcl_ResetResult(dataPtr->interp); 00445 if (preserve == P_PRESERVE) { 00446 (void) Tcl_RestoreInterpState(dataPtr->interp, state); 00447 } 00448 return res; 00449 00450 cleanup: 00451 if (preserve == P_PRESERVE) { 00452 (void) Tcl_RestoreInterpState(dataPtr->interp, state); 00453 } 00454 if (command != NULL) { 00455 Tcl_DecrRefCount(command); 00456 } 00457 return res; 00458 } 00459 00460 /* 00461 *---------------------------------------------------------------------- 00462 * 00463 * TransformBlockModeProc -- 00464 * 00465 * Trap handler. Called by the generic IO system during option processing 00466 * to change the blocking mode of the channel. 00467 * 00468 * Side effects: 00469 * Forwards the request to the underlying channel. 00470 * 00471 * Result: 00472 * 0 if successful, errno when failed. 00473 * 00474 *---------------------------------------------------------------------- 00475 */ 00476 00477 static int 00478 TransformBlockModeProc( 00479 ClientData instanceData, /* State of transformation. */ 00480 int mode) /* New blocking mode. */ 00481 { 00482 TransformChannelData *dataPtr = instanceData; 00483 00484 if (mode == TCL_MODE_NONBLOCKING) { 00485 dataPtr->flags |= CHANNEL_ASYNC; 00486 } else { 00487 dataPtr->flags &= ~CHANNEL_ASYNC; 00488 } 00489 return 0; 00490 } 00491 00492 /* 00493 *---------------------------------------------------------------------- 00494 * 00495 * TransformCloseProc -- 00496 * 00497 * Trap handler. Called by the generic IO system during destruction of 00498 * the transformation channel. 00499 * 00500 * Side effects: 00501 * Releases the memory allocated in 'Tcl_TransformObjCmd'. 00502 * 00503 * Result: 00504 * None. 00505 * 00506 *---------------------------------------------------------------------- 00507 */ 00508 00509 static int 00510 TransformCloseProc( 00511 ClientData instanceData, 00512 Tcl_Interp *interp) 00513 { 00514 TransformChannelData *dataPtr = instanceData; 00515 00516 /* 00517 * Important: In this procedure 'dataPtr->self' already points to the 00518 * underlying channel. 00519 * 00520 * There is no need to cancel an existing channel handler, this is already 00521 * done. Either by 'Tcl_UnstackChannel' or by the general cleanup in 00522 * 'Tcl_Close'. 00523 * 00524 * But we have to cancel an active timer to prevent it from firing on the 00525 * removed channel. 00526 */ 00527 00528 if (dataPtr->timer != NULL) { 00529 Tcl_DeleteTimerHandler(dataPtr->timer); 00530 dataPtr->timer = NULL; 00531 } 00532 00533 /* 00534 * Now flush data waiting in internal buffers to output and input. The 00535 * input must be done despite the fact that there is no real receiver for 00536 * it anymore. But the scripts might have sideeffects other parts of the 00537 * system rely on (f.e. signaling the close to interested parties). 00538 */ 00539 00540 if (dataPtr->mode & TCL_WRITABLE) { 00541 ExecuteCallback(dataPtr, interp, A_FLUSH_WRITE, NULL, 0, 00542 TRANSMIT_DOWN, P_PRESERVE); 00543 } 00544 00545 if ((dataPtr->mode & TCL_READABLE) && !dataPtr->readIsFlushed) { 00546 dataPtr->readIsFlushed = 1; 00547 ExecuteCallback(dataPtr, interp, A_FLUSH_READ, NULL, 0, TRANSMIT_IBUF, 00548 P_PRESERVE); 00549 } 00550 00551 if (dataPtr->mode & TCL_WRITABLE) { 00552 ExecuteCallback(dataPtr, interp, A_DELETE_WRITE, NULL, 0, 00553 TRANSMIT_DONT, P_PRESERVE); 00554 } 00555 if (dataPtr->mode & TCL_READABLE) { 00556 ExecuteCallback(dataPtr, interp, A_DELETE_READ, NULL, 0, 00557 TRANSMIT_DONT, P_PRESERVE); 00558 } 00559 00560 /* 00561 * General cleanup. 00562 */ 00563 00564 ResultClear(&dataPtr->result); 00565 Tcl_DecrRefCount(dataPtr->command); 00566 ckfree((char *) dataPtr); 00567 return TCL_OK; 00568 } 00569 00570 /* 00571 *---------------------------------------------------------------------- 00572 * 00573 * TransformInputProc -- 00574 * 00575 * Called by the generic IO system to convert read data. 00576 * 00577 * Side effects: 00578 * As defined by the conversion. 00579 * 00580 * Result: 00581 * A transformed buffer. 00582 * 00583 *---------------------------------------------------------------------- 00584 */ 00585 00586 static int 00587 TransformInputProc( 00588 ClientData instanceData, 00589 char *buf, 00590 int toRead, 00591 int *errorCodePtr) 00592 { 00593 TransformChannelData *dataPtr = instanceData; 00594 int gotBytes, read, copied; 00595 Tcl_Channel downChan; 00596 00597 /* 00598 * Should assert(dataPtr->mode & TCL_READABLE); 00599 */ 00600 00601 if (toRead == 0) { 00602 /* 00603 * Catch a no-op. 00604 */ 00605 return 0; 00606 } 00607 00608 gotBytes = 0; 00609 downChan = Tcl_GetStackedChannel(dataPtr->self); 00610 00611 while (toRead > 0) { 00612 /* 00613 * Loop until the request is satisfied (or no data is available from 00614 * below, possibly EOF). 00615 */ 00616 00617 copied = ResultCopy(&dataPtr->result, UCHARP(buf), toRead); 00618 toRead -= copied; 00619 buf += copied; 00620 gotBytes += copied; 00621 00622 if (toRead == 0) { 00623 /* 00624 * The request was completely satisfied from our buffers. We can 00625 * break out of the loop and return to the caller. 00626 */ 00627 00628 return gotBytes; 00629 } 00630 00631 /* 00632 * Length (dataPtr->result) == 0, toRead > 0 here. Use the incoming 00633 * 'buf'! as target to store the intermediary information read from 00634 * the underlying channel. 00635 * 00636 * Ask the tcl level how much data it allows us to read from the 00637 * underlying channel. This feature allows the transform to signal EOF 00638 * upstream although there is none downstream. Useful to control an 00639 * unbounded 'fcopy', either through counting bytes, or by pattern 00640 * matching. 00641 */ 00642 00643 ExecuteCallback(dataPtr, NULL, A_QUERY_MAXREAD, NULL, 0, 00644 TRANSMIT_NUM /* -> maxRead */, P_PRESERVE); 00645 00646 if (dataPtr->maxRead >= 0) { 00647 if (dataPtr->maxRead < toRead) { 00648 toRead = dataPtr->maxRead; 00649 } 00650 } /* else: 'maxRead < 0' == Accept the current value of toRead. */ 00651 if (toRead <= 0) { 00652 return gotBytes; 00653 } 00654 00655 /* 00656 * Get bytes from the underlying channel. 00657 */ 00658 00659 read = Tcl_ReadRaw(downChan, buf, toRead); 00660 if (read < 0) { 00661 /* 00662 * Report errors to caller. EAGAIN is a special situation. If we 00663 * had some data before we report that instead of the request to 00664 * re-try. 00665 */ 00666 00667 if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) { 00668 return gotBytes; 00669 } 00670 00671 *errorCodePtr = Tcl_GetErrno(); 00672 return -1; 00673 } else if (read == 0) { 00674 /* 00675 * Check wether we hit on EOF in the underlying channel or not. If 00676 * not differentiate between blocking and non-blocking modes. In 00677 * non-blocking mode we ran temporarily out of data. Signal this 00678 * to the caller via EWOULDBLOCK and error return (-1). In the 00679 * other cases we simply return what we got and let the caller 00680 * wait for more. On the other hand, if we got an EOF we have to 00681 * convert and flush all waiting partial data. 00682 */ 00683 00684 if (!Tcl_Eof(downChan)) { 00685 if ((gotBytes == 0) && (dataPtr->flags & CHANNEL_ASYNC)) { 00686 *errorCodePtr = EWOULDBLOCK; 00687 return -1; 00688 } 00689 return gotBytes; 00690 } 00691 00692 if (dataPtr->readIsFlushed) { 00693 /* 00694 * Already flushed, nothing to do anymore. 00695 */ 00696 00697 return gotBytes; 00698 } 00699 00700 dataPtr->readIsFlushed = 1; 00701 ExecuteCallback(dataPtr, NULL, A_FLUSH_READ, NULL, 0, 00702 TRANSMIT_IBUF, P_PRESERVE); 00703 00704 if (ResultEmpty(&dataPtr->result)) { 00705 /* 00706 * We had nothing to flush. 00707 */ 00708 00709 return gotBytes; 00710 } 00711 00712 continue; /* at: while (toRead > 0) */ 00713 } /* read == 0 */ 00714 00715 /* 00716 * Transform the read chunk and add the result to our read buffer 00717 * (dataPtr->result). 00718 */ 00719 00720 if (ExecuteCallback(dataPtr, NULL, A_READ, UCHARP(buf), read, 00721 TRANSMIT_IBUF, P_PRESERVE) != TCL_OK) { 00722 *errorCodePtr = EINVAL; 00723 return -1; 00724 } 00725 } /* while toRead > 0 */ 00726 00727 return gotBytes; 00728 } 00729 00730 /* 00731 *---------------------------------------------------------------------- 00732 * 00733 * TransformOutputProc -- 00734 * 00735 * Called by the generic IO system to convert data waiting to be written. 00736 * 00737 * Side effects: 00738 * As defined by the transformation. 00739 * 00740 * Result: 00741 * A transformed buffer. 00742 * 00743 *---------------------------------------------------------------------- 00744 */ 00745 00746 static int 00747 TransformOutputProc( 00748 ClientData instanceData, 00749 const char *buf, 00750 int toWrite, 00751 int *errorCodePtr) 00752 { 00753 TransformChannelData *dataPtr = instanceData; 00754 00755 /* 00756 * Should assert(dataPtr->mode & TCL_WRITABLE); 00757 */ 00758 00759 if (toWrite == 0) { 00760 /* 00761 * Catch a no-op. 00762 */ 00763 00764 return 0; 00765 } 00766 00767 if (ExecuteCallback(dataPtr, NULL, A_WRITE, UCHARP(buf), toWrite, 00768 TRANSMIT_DOWN, P_NO_PRESERVE) != TCL_OK) { 00769 *errorCodePtr = EINVAL; 00770 return -1; 00771 } 00772 00773 return toWrite; 00774 } 00775 00776 /* 00777 *---------------------------------------------------------------------- 00778 * 00779 * TransformSeekProc -- 00780 * 00781 * This procedure is called by the generic IO level to move the access 00782 * point in a channel. 00783 * 00784 * Side effects: 00785 * Moves the location at which the channel will be accessed in future 00786 * operations. Flushes all transformation buffers, then forwards it to 00787 * the underlying channel. 00788 * 00789 * Result: 00790 * -1 if failed, the new position if successful. An output argument 00791 * contains the POSIX error code if an error occurred, or zero. 00792 * 00793 *---------------------------------------------------------------------- 00794 */ 00795 00796 static int 00797 TransformSeekProc( 00798 ClientData instanceData, /* The channel to manipulate. */ 00799 long offset, /* Size of movement. */ 00800 int mode, /* How to move. */ 00801 int *errorCodePtr) /* Location of error flag. */ 00802 { 00803 TransformChannelData *dataPtr = instanceData; 00804 Tcl_Channel parent = Tcl_GetStackedChannel(dataPtr->self); 00805 Tcl_ChannelType *parentType = Tcl_GetChannelType(parent); 00806 Tcl_DriverSeekProc *parentSeekProc = Tcl_ChannelSeekProc(parentType); 00807 00808 if ((offset == 0) && (mode == SEEK_CUR)) { 00809 /* 00810 * This is no seek but a request to tell the caller the current 00811 * location. Simply pass the request down. 00812 */ 00813 00814 return parentSeekProc(Tcl_GetChannelInstanceData(parent), offset, 00815 mode, errorCodePtr); 00816 } 00817 00818 /* 00819 * It is a real request to change the position. Flush all data waiting for 00820 * output and discard everything in the input buffers. Then pass the 00821 * request down, unchanged. 00822 */ 00823 00824 if (dataPtr->mode & TCL_WRITABLE) { 00825 ExecuteCallback(dataPtr, NULL, A_FLUSH_WRITE, NULL, 0, TRANSMIT_DOWN, 00826 P_NO_PRESERVE); 00827 } 00828 00829 if (dataPtr->mode & TCL_READABLE) { 00830 ExecuteCallback(dataPtr, NULL, A_CLEAR_READ, NULL, 0, TRANSMIT_DONT, 00831 P_NO_PRESERVE); 00832 ResultClear(&dataPtr->result); 00833 dataPtr->readIsFlushed = 0; 00834 } 00835 00836 return parentSeekProc(Tcl_GetChannelInstanceData(parent), offset, mode, 00837 errorCodePtr); 00838 } 00839 00840 /* 00841 *---------------------------------------------------------------------- 00842 * 00843 * TransformWideSeekProc -- 00844 * 00845 * This procedure is called by the generic IO level to move the access 00846 * point in a channel, with a (potentially) 64-bit offset. 00847 * 00848 * Side effects: 00849 * Moves the location at which the channel will be accessed in future 00850 * operations. Flushes all transformation buffers, then forwards it to 00851 * the underlying channel. 00852 * 00853 * Result: 00854 * -1 if failed, the new position if successful. An output argument 00855 * contains the POSIX error code if an error occurred, or zero. 00856 * 00857 *---------------------------------------------------------------------- 00858 */ 00859 00860 static Tcl_WideInt 00861 TransformWideSeekProc( 00862 ClientData instanceData, /* The channel to manipulate. */ 00863 Tcl_WideInt offset, /* Size of movement. */ 00864 int mode, /* How to move. */ 00865 int *errorCodePtr) /* Location of error flag. */ 00866 { 00867 TransformChannelData *dataPtr = instanceData; 00868 Tcl_Channel parent = Tcl_GetStackedChannel(dataPtr->self); 00869 Tcl_ChannelType *parentType = Tcl_GetChannelType(parent); 00870 Tcl_DriverSeekProc *parentSeekProc = Tcl_ChannelSeekProc(parentType); 00871 Tcl_DriverWideSeekProc *parentWideSeekProc = 00872 Tcl_ChannelWideSeekProc(parentType); 00873 ClientData parentData = Tcl_GetChannelInstanceData(parent); 00874 00875 if ((offset == Tcl_LongAsWide(0)) && (mode == SEEK_CUR)) { 00876 /* 00877 * This is no seek but a request to tell the caller the current 00878 * location. Simply pass the request down. 00879 */ 00880 00881 if (parentWideSeekProc != NULL) { 00882 return parentWideSeekProc(parentData, offset, mode, errorCodePtr); 00883 } 00884 00885 return Tcl_LongAsWide(parentSeekProc(parentData, 0, mode, 00886 errorCodePtr)); 00887 } 00888 00889 /* 00890 * It is a real request to change the position. Flush all data waiting for 00891 * output and discard everything in the input buffers. Then pass the 00892 * request down, unchanged. 00893 */ 00894 00895 if (dataPtr->mode & TCL_WRITABLE) { 00896 ExecuteCallback(dataPtr, NULL, A_FLUSH_WRITE, NULL, 0, TRANSMIT_DOWN, 00897 P_NO_PRESERVE); 00898 } 00899 00900 if (dataPtr->mode & TCL_READABLE) { 00901 ExecuteCallback(dataPtr, NULL, A_CLEAR_READ, NULL, 0, TRANSMIT_DONT, 00902 P_NO_PRESERVE); 00903 ResultClear(&dataPtr->result); 00904 dataPtr->readIsFlushed = 0; 00905 } 00906 00907 /* 00908 * If we have a wide seek capability, we should stick with that. 00909 */ 00910 00911 if (parentWideSeekProc != NULL) { 00912 return parentWideSeekProc(parentData, offset, mode, errorCodePtr); 00913 } 00914 00915 /* 00916 * We're transferring to narrow seeks at this point; this is a bit complex 00917 * because we have to check whether the seek is possible first (i.e. 00918 * whether we are losing information in truncating the bits of the 00919 * offset). Luckily, there's a defined error for what happens when trying 00920 * to go out of the representable range. 00921 */ 00922 00923 if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) { 00924 *errorCodePtr = EOVERFLOW; 00925 return Tcl_LongAsWide(-1); 00926 } 00927 00928 return Tcl_LongAsWide(parentSeekProc(parentData, Tcl_WideAsLong(offset), 00929 mode, errorCodePtr)); 00930 } 00931 00932 /* 00933 *---------------------------------------------------------------------- 00934 * 00935 * TransformSetOptionProc -- 00936 * 00937 * Called by generic layer to handle the reconfiguration of channel 00938 * specific options. As this channel type does not have such, it simply 00939 * passes all requests downstream. 00940 * 00941 * Side effects: 00942 * As defined by the channel downstream. 00943 * 00944 * Result: 00945 * A standard TCL error code. 00946 * 00947 *---------------------------------------------------------------------- 00948 */ 00949 00950 static int 00951 TransformSetOptionProc( 00952 ClientData instanceData, 00953 Tcl_Interp *interp, 00954 const char *optionName, 00955 const char *value) 00956 { 00957 TransformChannelData *dataPtr = instanceData; 00958 Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self); 00959 Tcl_DriverSetOptionProc *setOptionProc; 00960 00961 setOptionProc = Tcl_ChannelSetOptionProc(Tcl_GetChannelType(downChan)); 00962 if (setOptionProc == NULL) { 00963 return TCL_ERROR; 00964 } 00965 00966 return setOptionProc(Tcl_GetChannelInstanceData(downChan), interp, 00967 optionName, value); 00968 } 00969 00970 /* 00971 *---------------------------------------------------------------------- 00972 * 00973 * TransformGetOptionProc -- 00974 * 00975 * Called by generic layer to handle requests for the values of channel 00976 * specific options. As this channel type does not have such, it simply 00977 * passes all requests downstream. 00978 * 00979 * Side effects: 00980 * As defined by the channel downstream. 00981 * 00982 * Result: 00983 * A standard TCL error code. 00984 * 00985 *---------------------------------------------------------------------- 00986 */ 00987 00988 static int 00989 TransformGetOptionProc( 00990 ClientData instanceData, 00991 Tcl_Interp *interp, 00992 const char *optionName, 00993 Tcl_DString *dsPtr) 00994 { 00995 TransformChannelData *dataPtr = instanceData; 00996 Tcl_Channel downChan = Tcl_GetStackedChannel(dataPtr->self); 00997 Tcl_DriverGetOptionProc *getOptionProc; 00998 00999 getOptionProc = Tcl_ChannelGetOptionProc(Tcl_GetChannelType(downChan)); 01000 if (getOptionProc != NULL) { 01001 return getOptionProc(Tcl_GetChannelInstanceData(downChan), interp, 01002 optionName, dsPtr); 01003 } else if (optionName == NULL) { 01004 /* 01005 * Request is query for all options, this is ok. 01006 */ 01007 01008 return TCL_OK; 01009 } 01010 01011 /* 01012 * Request for a specific option has to fail, since we don't have any. 01013 */ 01014 01015 return TCL_ERROR; 01016 } 01017 01018 /* 01019 *---------------------------------------------------------------------- 01020 * 01021 * TransformWatchProc -- 01022 * 01023 * Initialize the notifier to watch for events from this channel. 01024 * 01025 * Side effects: 01026 * Sets up the notifier so that a future event on the channel will be 01027 * seen by Tcl. 01028 * 01029 * Result: 01030 * None. 01031 * 01032 *---------------------------------------------------------------------- 01033 */ 01034 01035 /* ARGSUSED */ 01036 static void 01037 TransformWatchProc( 01038 ClientData instanceData, /* Channel to watch. */ 01039 int mask) /* Events of interest. */ 01040 { 01041 TransformChannelData *dataPtr = instanceData; 01042 Tcl_Channel downChan; 01043 01044 /* 01045 * The caller expressed interest in events occuring for this channel. We 01046 * are forwarding the call to the underlying channel now. 01047 */ 01048 01049 dataPtr->watchMask = mask; 01050 01051 /* 01052 * No channel handlers any more. We will be notified automatically about 01053 * events on the channel below via a call to our 'TransformNotifyProc'. 01054 * But we have to pass the interest down now. We are allowed to add 01055 * additional 'interest' to the mask if we want to. But this 01056 * transformation has no such interest. It just passes the request down, 01057 * unchanged. 01058 */ 01059 01060 downChan = Tcl_GetStackedChannel(dataPtr->self); 01061 01062 Tcl_GetChannelType(downChan)->watchProc( 01063 Tcl_GetChannelInstanceData(downChan), mask); 01064 01065 /* 01066 * Management of the internal timer. 01067 */ 01068 01069 if ((dataPtr->timer != NULL) && 01070 (!(mask & TCL_READABLE) || ResultEmpty(&dataPtr->result))) { 01071 /* 01072 * A pending timer exists, but either is there no (more) interest in 01073 * the events it generates or nothing is available for reading, so 01074 * remove it. 01075 */ 01076 01077 Tcl_DeleteTimerHandler(dataPtr->timer); 01078 dataPtr->timer = NULL; 01079 } 01080 01081 if ((dataPtr->timer == NULL) && (mask & TCL_READABLE) 01082 && !ResultEmpty(&dataPtr->result)) { 01083 /* 01084 * There is no pending timer, but there is interest in readable events 01085 * and we actually have data waiting, so generate a timer to flush 01086 * that. 01087 */ 01088 01089 dataPtr->timer = Tcl_CreateTimerHandler(FLUSH_DELAY, 01090 TransformChannelHandlerTimer, dataPtr); 01091 } 01092 } 01093 01094 /* 01095 *---------------------------------------------------------------------- 01096 * 01097 * TransformGetFileHandleProc -- 01098 * 01099 * Called from Tcl_GetChannelHandle to retrieve OS specific file handle 01100 * from inside this channel. 01101 * 01102 * Side effects: 01103 * None. 01104 * 01105 * Result: 01106 * The appropriate Tcl_File or NULL if not present. 01107 * 01108 *---------------------------------------------------------------------- 01109 */ 01110 01111 static int 01112 TransformGetFileHandleProc( 01113 ClientData instanceData, /* Channel to query. */ 01114 int direction, /* Direction of interest. */ 01115 ClientData *handlePtr) /* Place to store the handle into. */ 01116 { 01117 TransformChannelData *dataPtr = instanceData; 01118 01119 /* 01120 * Return the handle belonging to parent channel. IOW, pass the request 01121 * down and the result up. 01122 */ 01123 01124 return Tcl_GetChannelHandle(Tcl_GetStackedChannel(dataPtr->self), 01125 direction, handlePtr); 01126 } 01127 01128 /* 01129 *---------------------------------------------------------------------- 01130 * 01131 * TransformNotifyProc -- 01132 * 01133 * Handler called by Tcl to inform us of activity on the underlying 01134 * channel. 01135 * 01136 * Side effects: 01137 * May process the incoming event by itself. 01138 * 01139 * Result: 01140 * None. 01141 * 01142 *---------------------------------------------------------------------- 01143 */ 01144 01145 static int 01146 TransformNotifyProc( 01147 ClientData clientData, /* The state of the notified 01148 * transformation. */ 01149 int mask) /* The mask of occuring events. */ 01150 { 01151 TransformChannelData *dataPtr = clientData; 01152 01153 /* 01154 * An event occured in the underlying channel. This transformation doesn't 01155 * process such events thus returns the incoming mask unchanged. 01156 */ 01157 01158 if (dataPtr->timer != NULL) { 01159 /* 01160 * Delete an existing timer. It was not fired, yet we are here, so the 01161 * channel below generated such an event and we don't have to. The 01162 * renewal of the interest after the execution of channel handlers 01163 * will eventually cause us to recreate the timer (in 01164 * TransformWatchProc). 01165 */ 01166 01167 Tcl_DeleteTimerHandler(dataPtr->timer); 01168 dataPtr->timer = NULL; 01169 } 01170 return mask; 01171 } 01172 01173 /* 01174 *---------------------------------------------------------------------- 01175 * 01176 * TransformChannelHandlerTimer -- 01177 * 01178 * Called by the notifier (-> timer) to flush out information waiting in 01179 * the input buffer. 01180 * 01181 * Side effects: 01182 * As of 'Tcl_NotifyChannel'. 01183 * 01184 * Result: 01185 * None. 01186 * 01187 *---------------------------------------------------------------------- 01188 */ 01189 01190 static void 01191 TransformChannelHandlerTimer( 01192 ClientData clientData) /* Transformation to query. */ 01193 { 01194 TransformChannelData *dataPtr = clientData; 01195 01196 dataPtr->timer = NULL; 01197 if (!(dataPtr->watchMask&TCL_READABLE) || ResultEmpty(&dataPtr->result)) { 01198 /* 01199 * The timer fired, but either is there no (more) interest in the 01200 * events it generates or nothing is available for reading, so ignore 01201 * it and don't recreate it. 01202 */ 01203 01204 return; 01205 } 01206 Tcl_NotifyChannel(dataPtr->self, TCL_READABLE); 01207 } 01208 01209 /* 01210 *---------------------------------------------------------------------- 01211 * 01212 * ResultClear -- 01213 * 01214 * Deallocates any memory allocated by 'ResultAdd'. 01215 * 01216 * Side effects: 01217 * See above. 01218 * 01219 * Result: 01220 * None. 01221 * 01222 *---------------------------------------------------------------------- 01223 */ 01224 01225 static inline void 01226 ResultClear( 01227 ResultBuffer *r) /* Reference to the buffer to clear out. */ 01228 { 01229 r->used = 0; 01230 01231 if (r->allocated) { 01232 ckfree((char *) r->buf); 01233 r->buf = NULL; 01234 r->allocated = 0; 01235 } 01236 } 01237 01238 /* 01239 *---------------------------------------------------------------------- 01240 * 01241 * ResultInit -- 01242 * 01243 * Initializes the specified buffer structure. The structure will contain 01244 * valid information for an emtpy buffer. 01245 * 01246 * Side effects: 01247 * See above. 01248 * 01249 * Result: 01250 * None. 01251 * 01252 *---------------------------------------------------------------------- 01253 */ 01254 01255 static inline void 01256 ResultInit( 01257 ResultBuffer *r) /* Reference to the structure to 01258 * initialize. */ 01259 { 01260 r->used = 0; 01261 r->allocated = 0; 01262 r->buf = NULL; 01263 } 01264 01265 /* 01266 *---------------------------------------------------------------------- 01267 * 01268 * ResultEmpty -- 01269 * 01270 * Returns whether the number of bytes stored in the buffer is zero. 01271 * 01272 * Side effects: 01273 * None. 01274 * 01275 * Result: 01276 * A boolean. 01277 * 01278 *---------------------------------------------------------------------- 01279 */ 01280 01281 static inline int 01282 ResultEmpty( 01283 ResultBuffer *r) /* The structure to query. */ 01284 { 01285 return r->used == 0; 01286 } 01287 01288 /* 01289 *---------------------------------------------------------------------- 01290 * 01291 * ResultCopy -- 01292 * 01293 * Copies the requested number of bytes from the buffer into the 01294 * specified array and removes them from the buffer afterward. Copies 01295 * less if there is not enough data in the buffer. 01296 * 01297 * Side effects: 01298 * See above. 01299 * 01300 * Result: 01301 * The number of actually copied bytes, possibly less than 'toRead'. 01302 * 01303 *---------------------------------------------------------------------- 01304 */ 01305 01306 static inline int 01307 ResultCopy( 01308 ResultBuffer *r, /* The buffer to read from. */ 01309 unsigned char *buf, /* The buffer to copy into. */ 01310 size_t toRead) /* Number of requested bytes. */ 01311 { 01312 if (r->used == 0) { 01313 /* 01314 * Nothing to copy in the case of an empty buffer. 01315 */ 01316 01317 return 0; 01318 } else if (r->used == toRead) { 01319 /* 01320 * We have just enough. Copy everything to the caller. 01321 */ 01322 01323 memcpy(buf, r->buf, toRead); 01324 r->used = 0; 01325 } else if (r->used > toRead) { 01326 /* 01327 * The internal buffer contains more than requested. Copy the 01328 * requested subset to the caller, and shift the remaining bytes down. 01329 */ 01330 01331 memcpy(buf, r->buf, toRead); 01332 memmove(r->buf, r->buf + toRead, r->used - toRead); 01333 r->used -= toRead; 01334 } else { 01335 /* 01336 * There is not enough in the buffer to satisfy the caller, so take 01337 * everything. 01338 */ 01339 01340 memcpy(buf, r->buf, r->used); 01341 toRead = r->used; 01342 r->used = 0; 01343 } 01344 return toRead; 01345 } 01346 01347 /* 01348 *---------------------------------------------------------------------- 01349 * 01350 * ResultAdd -- 01351 * 01352 * Adds the bytes in the specified array to the buffer, by appending it. 01353 * 01354 * Side effects: 01355 * See above. 01356 * 01357 * Result: 01358 * None. 01359 * 01360 *---------------------------------------------------------------------- 01361 */ 01362 01363 static inline void 01364 ResultAdd( 01365 ResultBuffer *r, /* The buffer to extend. */ 01366 unsigned char *buf, /* The buffer to read from. */ 01367 size_t toWrite) /* The number of bytes in 'buf'. */ 01368 { 01369 if (r->used + toWrite > r->allocated) { 01370 /* 01371 * Extension of the internal buffer is required. 01372 */ 01373 01374 if (r->allocated == 0) { 01375 r->allocated = toWrite + INCREMENT; 01376 r->buf = UCHARP(ckalloc(r->allocated)); 01377 } else { 01378 r->allocated += toWrite + INCREMENT; 01379 r->buf = UCHARP(ckrealloc((char *) r->buf, r->allocated)); 01380 } 01381 } 01382 01383 /* 01384 * Now we may copy the data. 01385 */ 01386 01387 memcpy(r->buf + r->used, buf, toWrite); 01388 r->used += toWrite; 01389 } 01390 01391 /* 01392 * Local Variables: 01393 * mode: c 01394 * c-basic-offset: 4 01395 * fill-column: 78 01396 * End: 01397 */
Generated on Wed Mar 12 12:18:18 2008 by 1.5.1 |