1 /*
2  * registry.c --
3  *
4  *	Implements the C level procedures handling the registry
5  *
6  *
7  * Copyright (c) 1996-1999 Andreas Kupries (andreas_kupries@users.sourceforge.net)
8  * All rights reserved.
9  *
10  * Permission is hereby granted, without written agreement and without
11  * license or royalty fees, to use, copy, modify, and distribute this
12  * software and its documentation for any purpose, provided that the
13  * above copyright notice and the following two paragraphs appear in
14  * all copies of this software.
15  *
16  * IN NO EVENT SHALL I LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
17  * INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
18  * SOFTWARE AND ITS DOCUMENTATION, EVEN IF I HAVE BEEN ADVISED OF THE
19  * POSSIBILITY OF SUCH DAMAGE.
20  *
21  * I SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND
24  * I HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
25  * ENHANCEMENTS, OR MODIFICATIONS.
26  *
27  * CVS: $Id: registry.c,v 1.58 2009/05/07 05:30:35 andreas_kupries Exp $
28  */
29 
30 #include "transformInt.h"
31 
32 /*
33  * Code used to associate the registry with an interpreter.
34  */
35 
36 #define ASSOC "binTrf"
37 
38 #ifdef TRF_DEBUG
39 int n = 0;
40 #endif
41 
42 /*
43  * Possible values for 'flags' field in control structure.
44  */
45 #define CHANNEL_ASYNC		(1<<0) /* non-blocking mode */
46 
47 /*
48  * Number of milliseconds to wait before firing an event to flush
49  * out information waiting in buffers (fileevent support).
50  *
51  * Relevant for only Tcl 8.0 and beyond.
52  */
53 
54 #define TRF_DELAY (5)
55 
56 /*
57  * Structures used by an attached transformation procedure
58  *
59  * => Information stored for a single direction of the channel.
60  * => Information required by a result buffer.
61  * => Information stored for the complete channel.
62  */
63 
64 typedef struct _DirectionInfo_ {
65   Trf_ControlBlock   control; /* control block of transformation */
66   Trf_Vectors*       vectors; /* vectors used during the transformation */
67 } DirectionInfo;
68 
69 
70 /*
71  * Definition of the structure containing the information about the
72  * internal input buffer.
73  */
74 
75 typedef struct _SeekState_ SeekState;
76 
77 typedef struct _ResultBuffer_ {
78   unsigned char* buf;       /* Reference to the buffer area */
79   int            allocated; /* Allocated size of the buffer area */
80   int            used;      /* Number of bytes in the buffer, <= allocated */
81 
82   SeekState*    seekState;
83 } ResultBuffer;
84 
85 
86 typedef struct _SeekConfig_ {
87 
88   int          overideAllowed; /* Boolean flag. If set the user may overide the
89 				* standard policy with his own choice */
90   Trf_SeekInformation natural; /* Natural seek policy, copied from the
91 				* transform definition */
92   Trf_SeekInformation  chosen;  /* Seek policy chosen from natural policy
93 				 * and the underlying channels; */
94   int identity;                 /* Flag, set if 'identity' was forced by the
95 				 * user. */
96 } SeekConfig;
97 
98 
99 struct _SeekState_ {
100   /* -- Integrity conditions --
101    *
102    * BufStartLoc == BufEndLoc	implies 	ResultLength(&result) == 0.
103    * BufStartLoc == BufEndLoc	implies		UpLoc == BufStart.
104    *
105    * UP_CONVERT (DownLoc - AheadOffset) == BufEndLoc
106    *
107    * UpXLoc % seekState.used.numBytesTransform == 0
108    * <=> Transform may seek only in multiples of its input tuples.
109    *
110    * (DownLoc - AheadOffset) % seekState.used.numBytesDown == 0
111    * <=> Downstream channel operates in multiples of the transformation
112    *     output tuples, except for possible offsets because of read ahead.
113    *
114    * UP_CONVERT (DownZero) == 0
115    *
116    * -- Integrity conditions --
117    */
118 
119   Trf_SeekInformation    used;  /* Seek policy currently in effect, might
120 				 * be chosen by user */
121   int                 allowed;  /* Flag. Set for seekable transforms. Derived
122 				 * from the contents of 'used'. */
123 
124   int upLoc;         /* Current location of file pointer in the
125 		      * transformed stream. */
126   int upBufStartLoc; /* Same as above, for start of read buffer (result) */
127   int upBufEndLoc;   /* See above, for the character after the end of the
128 		      * buffer. */
129   int downLoc;       /* Current location of the file pointer in the channel
130 		      * downstream. */
131   int downZero;      /* location downstream equivalent to UpLoc == 0 */
132   int aheadOffset;   /* #Bytes DownLoc is after the down location of
133 		      * BufEnd. Values > 0 indicate incomplete data in the
134 		      * transform buffer itself. */
135   int changed;       /* Flag, set if seeking occured with 'identity' set */
136 };
137 
138 
139 /** XXX change definition for 8.2, at compile time */
140 
141 typedef struct _TrfTransformationInstance_ {
142 #ifdef USE_TCL_STUBS
143   int patchVariant; /* See transformInt.h, Trf_Registry */
144 #endif
145 
146   /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com> */
147 
148   Tcl_Channel self;   /* Our own channel handle */
149   Tcl_Channel parent; /* The channel we are stacked upon. Relevant
150 		       * only for values PATCH_ORIG and PATCH_832 of
151 		       * 'patchVariant', see above. */
152 
153   int readIsFlushed; /* flag to note wether in.flushProc was called or not */
154 
155   /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com> */
156 
157   int flags;         /* currently CHANNEL_ASYNC or zero */
158   int watchMask;     /* current TrfWatch mask */
159 
160   int mode;          /* mode of parent channel,
161 		      * OR'ed combination of
162 		      * TCL_READABLE, TCL_WRITABLE */
163 
164   /* Tcl_Transformation standard; data required for all transformation
165    * instances.
166    */
167   DirectionInfo      in;   /* information for transformation of read data */
168   DirectionInfo      out;  /* information for transformation of written data */
169   ClientData         clientData; /* copy from entry->trfType->clientData */
170 
171   /*
172    * internal result buffer used during transformations of incoming data.
173    * Stores results waiting for retrieval too, i.e. state information
174    * carried from call to call.
175    */
176 
177   ResultBuffer result;
178 
179   /* Number of bytes written during a down transformation.
180    */
181 
182   int lastWritten;
183 
184   /* Number of bytes stored during an up transformation
185    */
186 
187   int lastStored;
188 
189 
190   /* Timer for automatic push out of information sitting in various channel
191    * buffers. Used by the fileevent support. See 'ChannelHandler'.
192    */
193 
194   Tcl_TimerToken timer;
195 
196   /* Information about the chosen and used seek policy and wether the user
197    * is allowed to change it. Runtime configuration.
198    */
199 
200   SeekConfig seekCfg;
201 
202   /* More seek information, runtime state.
203    */
204 
205   SeekState seekState;
206 
207 #ifdef TRF_STREAM_DEBUG
208   char*         name;       /* Name of transformation command */
209   unsigned long inCounter;  /* Number of bytes read from below */
210   unsigned long outCounter; /* Number of bytes stored in 'result' */
211 #endif
212 
213 } TrfTransformationInstance;
214 
215 #ifdef TRF_STREAM_DEBUG
216 #define STREAM_IN(trans,blen,buf) {int i; for (i=0;i<(blen);i++,(trans)->inCounter++) {printf ("%p:%s:in_\t%d\t%02x\n", (trans), (trans)->name, (trans)->inCounter, 0xff & ((buf) [i]));}}
217 #define STREAM_OUT(trans,blen,buf) {int i; for (i=0;i<(blen);i++,(trans)->outCounter++) {printf ("%p:%s:out\t%d\t%02x\n", (trans), (trans)->name, (trans)->outCounter, 0xff & ((buf) [i]));}}
218 #else
219 #define STREAM_IN(t,bl,b)
220 #define STREAM_OUT(t,bl,b)
221 #endif
222 
223 
224 #define INCREMENT (512)
225 #define READ_CHUNK_SIZE 4096
226 
227 
228 #define TRF_UP_CONVERT(trans,k) \
229      (((k) / trans->seekState.used.numBytesDown) * trans->seekState.used.numBytesTransform)
230 
231 #define TRF_DOWN_CONVERT(trans,k) \
232      (((k) / trans->seekState.used.numBytesTransform) * trans->seekState.used.numBytesDown)
233 
234 #define TRF_IS_UNSEEKABLE(si) \
235      (((si).numBytesTransform == 0) || ((si).numBytesDown == 0))
236 
237 #define TRF_SET_UNSEEKABLE(si) \
238      {(si).numBytesTransform = 0 ; (si).numBytesDown = 0;}
239 
240 
241 
242 /*
243  * forward declarations of all internally used procedures.
244  */
245 
246 static Tcl_ChannelType*
247 AllocChannelType _ANSI_ARGS_ ((int* sizePtr));
248 
249 static Tcl_ChannelType*
250 InitializeChannelType _ANSI_ARGS_ ((CONST char* name, int patchVariant));
251 
252 
253 static int
254 TrfUnregister _ANSI_ARGS_ ((Tcl_Interp*       interp,
255                             Trf_RegistryEntry* entry));
256 
257 static void
258 TrfDeleteRegistry _ANSI_ARGS_ ((ClientData clientData, Tcl_Interp *interp));
259 
260 static int
261 TrfExecuteObjCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp* interp,
262 			      int objc, struct Tcl_Obj* CONST objv []));
263 
264 static void
265 TrfDeleteCmd _ANSI_ARGS_((ClientData clientData));
266 
267 #if 0
268 static int
269 TrfInfoObjCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp* interp,
270 			   int objc, struct Tcl_Obj* CONST objv []));
271 #endif
272 /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
273  */
274 static int
275 TrfBlock _ANSI_ARGS_ ((ClientData instanceData, int mode));
276 
277 static int
278 TrfClose _ANSI_ARGS_ ((ClientData instanceData, Tcl_Interp* interp));
279 
280 static int
281 TrfInput _ANSI_ARGS_ ((ClientData instanceData,
282 		       char* buf, int toRead,
283 		       int*       errorCodePtr));
284 
285 static int
286 TrfOutput _ANSI_ARGS_ ((ClientData instanceData,
287 			CONST84 char* buf, int toWrite,
288 			int*        errorCodePtr));
289 
290 static int
291 TrfSeek _ANSI_ARGS_ ((ClientData instanceData, long offset,
292 		      int mode, int* errorCodePtr));
293 static void
294 TrfWatch _ANSI_ARGS_ ((ClientData instanceData, int mask));
295 
296 static int
297 TrfGetFile _ANSI_ARGS_ ((ClientData instanceData, int direction,
298 			 ClientData* handlePtr));
299 
300 static int
301 TrfGetOption _ANSI_ARGS_ ((ClientData instanceData, Tcl_Interp* interp,
302 			   CONST84 char* optionName, Tcl_DString* dsPtr));
303 
304 static int
305 TrfSetOption _ANSI_ARGS_((ClientData instanceData, Tcl_Interp* interp,
306 			  CONST char* optionName, CONST char* value));
307 #ifdef USE_TCL_STUBS
308 static int
309 TrfNotify _ANSI_ARGS_((ClientData instanceData, int interestMask));
310 #endif
311 
312 static int
313 TransformImmediate _ANSI_ARGS_ ((Tcl_Interp* interp, Trf_RegistryEntry* entry,
314 				 Tcl_Channel source, Tcl_Channel destination,
315 				 struct Tcl_Obj* CONST in,
316 				 Trf_Options optInfo));
317 
318 static int
319 AttachTransform _ANSI_ARGS_ ((Trf_RegistryEntry* entry,
320 			      Trf_BaseOptions*   baseOpt,
321 			      Trf_Options        optInfo,
322 			      Tcl_Interp*        interp));
323 
324 static int
325 PutDestination _ANSI_ARGS_ ((ClientData clientData,
326                              unsigned char* outString, int outLen,
327                              Tcl_Interp* interp));
328 
329 static int
330 PutDestinationImm _ANSI_ARGS_ ((ClientData clientData,
331 				unsigned char* outString, int outLen,
332 				Tcl_Interp* interp));
333 static int
334 PutTrans _ANSI_ARGS_ ((ClientData clientData,
335 		       unsigned char* outString, int outLen,
336 		       Tcl_Interp* interp));
337 
338 static int
339 PutInterpResult _ANSI_ARGS_ ((ClientData clientData,
340 			      unsigned char* outString, int outLen,
341 			      Tcl_Interp* interp));
342 /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
343  */
344 static void
345 ChannelHandler _ANSI_ARGS_ ((ClientData clientData, int mask));
346 
347 static void
348 ChannelHandlerTimer _ANSI_ARGS_ ((ClientData clientData));
349 
350 #ifdef USE_TCL_STUBS
351 static Tcl_Channel
352 DownChannel _ANSI_ARGS_ ((TrfTransformationInstance* ctrl));
353 
354 static int
355 DownSeek _ANSI_ARGS_ ((TrfTransformationInstance* ctrl, int offset, int mode));
356 
357 static int
358 DownRead _ANSI_ARGS_ ((TrfTransformationInstance* ctrl,
359 		       char* buf, int toRead));
360 static int
361 DownWrite _ANSI_ARGS_ ((TrfTransformationInstance* ctrl,
362 		       char* buf, int toWrite));
363 static int
364 DownSOpt _ANSI_ARGS_ ((Tcl_Interp* interp,
365 		       TrfTransformationInstance* ctrl,
366 		       CONST char* optionName, CONST char* value));
367 static int
368 DownGOpt _ANSI_ARGS_ ((Tcl_Interp* interp,
369 		       TrfTransformationInstance* ctrl,
370 		       CONST84 char* optionName, Tcl_DString* dsPtr));
371 
372 #define DOWNC(trans)             (DownChannel (trans))
373 #define TELL(trans)              (SEEK (trans, 0, SEEK_CUR))
374 #define SEEK(trans,off,mode)     (DownSeek  ((trans), (off), (mode)))
375 #define READ(trans,buf,toRead)   (DownRead  ((trans), (buf), (toRead)))
376 #define WRITE(trans,buf,toWrite) (DownWrite ((trans), (buf), (toWrite)))
377 #define SETOPT(i,trans,opt,val)  (DownSOpt  ((i), (trans), (opt), (val)))
378 #define GETOPT(i,trans,opt,ds)   (DownGOpt  ((i), (trans), (opt), (ds)))
379 #else
380 #define DOWNC(trans)             ((trans)->parent)
381 #define TELL(trans)              (SEEK (trans, 0, SEEK_CUR))
382 #define SEEK(trans,off,mode)     (Tcl_Seek  ((trans)->parent, (off), (mode)))
383 #define READ(trans,buf,toRead)   (Tcl_Read  ((trans)->parent, (buf), (toRead)))
384 #define WRITE(trans,buf,toWrite) (Tcl_Write ((trans)->parent, (buf), (toWrite)))
385 #define SETOPT(i,trans,opt,val)  (Tcl_SetChannelOption ((i), (trans)->parent, (opt), (val)))
386 #define GETOPT(i,trans,opt,ds)   (Tcl_GetChannelOption ((i), (trans)->parent, (opt), (ds)))
387 #endif
388 
389 /* Convenience macro for allocation
390  * of new transformation instances.
391  */
392 
393 #define NEW_TRANSFORM \
394 (TrfTransformationInstance*) ckalloc (sizeof (TrfTransformationInstance));
395 
396 /* Procedures to handle the internal timer.
397  */
398 
399 static void
400 TimerKill _ANSI_ARGS_ ((TrfTransformationInstance* trans));
401 
402 static void
403 TimerSetup _ANSI_ARGS_ ((TrfTransformationInstance* trans));
404 
405 static void
406 ChannelHandlerKS _ANSI_ARGS_ ((TrfTransformationInstance* trans, int mask));
407 
408 
409 
410 /* Procedures to handle the internal read buffer.
411  */
412 
413 static void             ResultClear  _ANSI_ARGS_ ((ResultBuffer* r));
414 static void             ResultInit   _ANSI_ARGS_ ((ResultBuffer* r));
415 static int              ResultLength _ANSI_ARGS_ ((ResultBuffer* r));
416 static int              ResultCopy   _ANSI_ARGS_ ((ResultBuffer* r,
417 			    unsigned char* buf, int toRead));
418 static void             ResultDiscardAtStart _ANSI_ARGS_ ((ResultBuffer* r,
419 							   int n));
420 static void             ResultAdd    _ANSI_ARGS_ ((ResultBuffer* r,
421                             unsigned char* buf, int toWrite));
422 
423 /*
424  * Procedures to handle seeking information.
425  */
426 
427 static void
428 SeekCalculatePolicies _ANSI_ARGS_ ((TrfTransformationInstance* trans));
429 
430 static void
431 SeekInitialize _ANSI_ARGS_ ((TrfTransformationInstance* trans));
432 
433 static void
434 SeekClearBuffer _ANSI_ARGS_ ((TrfTransformationInstance* trans, int which));
435 
436 static void
437 SeekSynchronize _ANSI_ARGS_ ((TrfTransformationInstance* trans,
438 			      Tcl_Channel parent));
439 
440 static Tcl_Obj*
441 SeekStateGet _ANSI_ARGS_ ((Tcl_Interp* interp, SeekState* state));
442 
443 static Tcl_Obj*
444 SeekConfigGet _ANSI_ARGS_ ((Tcl_Interp* interp, SeekConfig* cfg));
445 
446 static void
447 SeekPolicyGet _ANSI_ARGS_ ((TrfTransformationInstance* trans,
448 			    char*                      policy));
449 
450 #ifdef TRF_DEBUG
451 static void
452 SeekDump _ANSI_ARGS_ ((TrfTransformationInstance* trans, CONST char* place));
453 
454 #define SEEK_DUMP(str) SeekDump (trans, #str)
455 #else
456 #define SEEK_DUMP(str)
457 #endif
458 
459 /*
460  *------------------------------------------------------*
461  *
462  *	TrfGetRegistry --
463  *
464  *	------------------------------------------------*
465  *	Accessor to the interpreter associated registry
466  *	of transformations.
467  *	------------------------------------------------*
468  *
469  *	Sideeffects:
470  *		Allocates and initializes the hashtable
471  *		during the first call and associates it
472  *		with the specified interpreter.
473  *
474  *	Result:
475  *		The internal registry of transformations.
476  *
477  *------------------------------------------------------*
478  */
479 
480 Trf_Registry*
TrfGetRegistry(interp)481 TrfGetRegistry (interp)
482 Tcl_Interp* interp;
483 {
484   Trf_Registry* registry;
485 
486   START (TrfGetRegistry);
487 
488   registry = TrfPeekForRegistry (interp);
489 
490   if (registry == (Trf_Registry*) NULL) {
491     registry           = (Trf_Registry*)  ckalloc (sizeof (Trf_Registry));
492     registry->registry = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable));
493 
494     Tcl_InitHashTable (registry->registry, TCL_STRING_KEYS);
495 
496     Tcl_SetAssocData (interp, ASSOC, TrfDeleteRegistry,
497 		      (ClientData) registry);
498   }
499 
500   DONE (TrfGetRegistry);
501   return registry;
502 }
503 
504 /*
505  *------------------------------------------------------*
506  *
507  *	TrfPeekForRegistry --
508  *
509  *	------------------------------------------------*
510  *	Accessor to the interpreter associated registry
511  *	of transformations. Does not create the registry
512  *	(in contrast to 'TrfGetRegistry').
513  *	------------------------------------------------*
514  *
515  *	Sideeffects:
516  *		None.
517  *
518  *	Result:
519  *		The internal registry of transformations.
520  *
521  *------------------------------------------------------*
522  */
523 
524 Trf_Registry*
TrfPeekForRegistry(interp)525 TrfPeekForRegistry (interp)
526 Tcl_Interp* interp;
527 {
528   Tcl_InterpDeleteProc* proc;
529 
530   START (TrfPeekForRegistry);
531 
532   proc = TrfDeleteRegistry;
533 
534   DONE (TrfPeekForRegistry);
535   return (Trf_Registry*) Tcl_GetAssocData (interp, ASSOC, &proc);
536 }
537 
538 /*
539  *------------------------------------------------------*
540  *
541  *	Trf_Register --
542  *
543  *	------------------------------------------------*
544  *	Announce a transformation to the registry associated
545  *	with the specified interpreter.
546  *	------------------------------------------------*
547  *
548  *	Sideeffects:
549  *		May create the registry. Allocates and
550  *		initializes the structure describing
551  *		the announced transformation.
552  *
553  *	Result:
554  *		A standard TCL error code.
555  *
556  *------------------------------------------------------*
557  */
558 
559 int
Trf_Register(interp,type)560 Trf_Register (interp, type)
561 Tcl_Interp*               interp;
562 CONST Trf_TypeDefinition* type;
563 {
564   Trf_Registry*      registry;
565   Trf_RegistryEntry* entry;
566   Tcl_HashEntry*     hPtr;
567   int                new;
568 
569   START (Trf_Register);
570   PRINT ("(%p, \"%s\")\n", type, type->name); FL;
571 
572   registry = TrfGetRegistry (interp);
573 
574   /*
575    * Already defined ?
576    */
577 
578   hPtr = Tcl_FindHashEntry (registry->registry, (char*) type->name);
579 
580   if (hPtr != (Tcl_HashEntry*) NULL) {
581     PRINT ("Already defined!\n"); FL;
582     DONE (Trf_Register);
583     return TCL_ERROR;
584   }
585 
586   /*
587    * Check validity of given structure
588    */
589 
590 #define IMPLY(a,b) ((! (a)) || (b))
591 
592   /* assert (type->options); */
593   assert (IMPLY(type->options != NULL, type->options->createProc != NULL));
594   assert (IMPLY(type->options != NULL, type->options->deleteProc != NULL));
595   assert (IMPLY(type->options != NULL, type->options->checkProc  != NULL));
596   assert (IMPLY(type->options != NULL,
597 		(type->options->setProc   != NULL) ||
598 		(type->options->setObjProc != NULL)));
599   assert (IMPLY(type->options != NULL, type->options->queryProc  != NULL));
600 
601   assert (type->encoder.createProc);
602   assert (type->encoder.deleteProc);
603   assert ((type->encoder.convertProc != NULL) ||
604 	  (type->encoder.convertBufProc != NULL));
605   assert (type->encoder.flushProc);
606   assert (type->encoder.clearProc);
607 
608   assert (type->decoder.createProc);
609   assert (type->decoder.deleteProc);
610   assert ((type->decoder.convertProc != NULL) ||
611 	  (type->decoder.convertBufProc != NULL));
612   assert (type->decoder.flushProc);
613   assert (type->decoder.clearProc);
614 
615   /*
616    * Generate command to execute transformations immediately or to generate
617    * filters.
618    */
619 
620   entry          = (Trf_RegistryEntry*) ckalloc (sizeof (Trf_RegistryEntry));
621   entry->registry   = registry;
622 
623   entry->trfType    = (Trf_TypeDefinition*) type;
624   entry->interp     = interp;
625 #ifndef USE_TCL_STUBS
626   entry->transType  = InitializeChannelType (type->name, -1);
627 #else
628   entry->transType  = InitializeChannelType (type->name,
629 					     registry->patchVariant);
630 #endif
631   entry->trfCommand = Tcl_CreateObjCommand (interp, (char*) type->name,
632 					    TrfExecuteObjCmd,
633 					    (ClientData) entry, TrfDeleteCmd);
634 
635   /*
636    * Add entry to internal registry.
637    */
638 
639   hPtr = Tcl_CreateHashEntry (registry->registry, (char*) type->name, &new);
640   Tcl_SetHashValue (hPtr, entry);
641 
642   DONE (Trf_Register);
643   return TCL_OK;
644 }
645 
646 /*
647  *------------------------------------------------------*
648  *
649  *	Trf_Unregister --
650  *
651  *	------------------------------------------------*
652  *	Removes the transformation from the registry
653  *	------------------------------------------------*
654  *
655  *	Sideeffects:
656  *		Releases the memory allocated in 'Trf_Register'.
657  *
658  *	Result:
659  *		A standard TCL error code.
660  *
661  *------------------------------------------------------*
662  */
663 
664 static int
TrfUnregister(interp,entry)665 TrfUnregister (interp, entry)
666 Tcl_Interp*        interp;
667 Trf_RegistryEntry* entry;
668 {
669   Trf_Registry*  registry;
670   Tcl_HashEntry* hPtr;
671 
672   START (Trf_Unregister);
673 
674   registry  = TrfGetRegistry    (interp);
675   hPtr      = Tcl_FindHashEntry (registry->registry,
676 				 (char*) entry->trfType->name);
677 
678   ckfree ((char*) entry->transType);
679   ckfree ((char*) entry);
680 
681   Tcl_DeleteHashEntry (hPtr);
682 
683   DONE (Trf_Unregister);
684   return TCL_OK;
685 }
686 
687 /*
688  *------------------------------------------------------*
689  *
690  *	TrfDeleteRegistry --
691  *
692  *	------------------------------------------------*
693  *	Trap handler. Called by the Tcl core during
694  *	interpreter destruction. Destroys the registry
695  *	of transformations.
696  *	------------------------------------------------*
697  *
698  *	Sideeffects:
699  *		Releases the memory allocated in 'TrfGetRegistry'.
700  *
701  *	Result:
702  *		None.
703  *
704  *------------------------------------------------------*
705  */
706 
707 static void
TrfDeleteRegistry(clientData,interp)708 TrfDeleteRegistry (clientData, interp)
709 ClientData  clientData;
710 Tcl_Interp* interp;
711 {
712   Trf_Registry* registry = (Trf_Registry*) clientData;
713 
714   START (TrfDeleteRegistry);
715 
716   /*
717    * The commands are already deleted, therefore the hashtable is empty here.
718    */
719 
720   Tcl_DeleteHashTable (registry->registry);
721   ckfree ((char*) registry);
722 
723   DONE (TrfDeleteRegistry);
724 }
725 
726 /* (readable) shortcuts for calling the option processing vectors.
727  */
728 
729 #define CLT  (entry->trfType->clientData)
730 #define OPT  (entry->trfType->options)
731 
732 #define CREATE_OPTINFO         (OPT ? (*OPT->createProc) (CLT) : NULL)
733 #define DELETE_OPTINFO         if (optInfo) (*OPT->deleteProc) (optInfo, CLT)
734 #define CHECK_OPTINFO(baseOpt) (optInfo ? (*OPT->checkProc) (optInfo, interp, &baseOpt, CLT) : TCL_OK)
735 #define SET_OPTION(opt,optval) (optInfo ? (*OPT->setProc) (optInfo, interp, opt, optval, CLT) : TCL_ERROR)
736 
737 #define SET_OPTION_OBJ(opt,optval) (optInfo ? (*OPT->setObjProc) (optInfo, interp, opt, optval, CLT) : TCL_ERROR)
738 
739 #define ENCODE_REQUEST(entry,optInfo) (optInfo ? (*OPT->queryProc) (optInfo, CLT) : 1)
740 
741 /*
742  *------------------------------------------------------*
743  *
744  *	TrfExecuteObjCmd --
745  *
746  *	------------------------------------------------*
747  *	Implementation procedure for all transformations.
748  *	Equivalent to 'TrfExecuteCmd', but using the new
749  *	Object interfaces.
750  *	------------------------------------------------*
751  *
752  *	Sideeffects:
753  *		See 'TrfExecuteCmd'.
754  *
755  *	Result:
756  *		A standard TCL error code.
757  *
758  *------------------------------------------------------*
759  */
760 
761 static int
TrfExecuteObjCmd(clientData,interp,objc,objv)762 TrfExecuteObjCmd (clientData, interp, objc, objv)
763      ClientData              clientData;
764      Tcl_Interp*             interp;
765      int                     objc;
766      struct Tcl_Obj* CONST * objv;
767 {
768   /* (readable) shortcuts for calling the option processing vectors.
769    * as defined in 'TrfExecuteCmd'.
770    */
771 
772   int                res, len;
773   /*  Tcl_Channel        source, destination;*/
774   /*  int                src_mode, dst_mode;*/
775   const char*        cmd;
776   const char*        option;
777   struct Tcl_Obj*    optarg;
778   Trf_RegistryEntry* entry;
779   Trf_Options        optInfo;
780   Trf_BaseOptions    baseOpt;
781   int                mode;
782   int                wrong_mod2;
783   int                wrong_number;
784 
785   START (TrfExecuteObjCmd);
786 #ifdef TRF_DEBUG
787   {
788     int i;
789     for (i = 0; i < objc; i++) {
790       PRINT ("Argument [%03d] = \"%s\"\n",
791 	     i, Tcl_GetStringFromObj (objv [i], NULL)); FL;
792     }
793   }
794 #endif
795 
796   baseOpt.attach      = (Tcl_Channel) NULL;
797   baseOpt.attach_mode = 0;
798   baseOpt.source      = (Tcl_Channel) NULL;
799   baseOpt.destination = (Tcl_Channel) NULL;
800   baseOpt.policy      = (Tcl_Obj*)    NULL;
801 
802   entry = (Trf_RegistryEntry*) clientData;
803   cmd   = Tcl_GetStringFromObj (objv [0], NULL);
804 
805   objc --;
806   objv ++;
807 
808   optInfo = CREATE_OPTINFO;
809 
810   PRINT ("Processing options...\n"); FL; IN;
811 
812   while ((objc > 0) && (*Tcl_GetStringFromObj (objv [0], NULL) == '-')) {
813     /*
814      * Process options, as long as they are found
815      */
816 
817     option = Tcl_GetStringFromObj (objv [0], NULL);
818 
819     if (0 == strcmp (option, "--")) {
820       /* end of option list */
821       objc--, objv++;
822       break;
823     }
824 
825     wrong_number = (objc < 2); /* option, but without argument */
826 
827     optarg = objv [1];
828 
829     objc -= 2;
830     objv += 2;
831 
832     len = strlen (option);
833 
834     if (len < 2)
835       goto unknown_option;
836 
837     switch (option [1])
838       {
839       case 'a':
840 	if (0 != strncmp (option, "-attach", len))
841 	  goto check_for_trans_option;
842 
843 	if (wrong_number) {
844 	  Tcl_AppendResult (interp, cmd, ": wrong # args, option \"", option, "\" requires an argument", (char*) NULL);
845 	  OT;
846 	  goto cleanup_after_error;
847 	}
848 
849 	baseOpt.attach = Tcl_GetChannel (interp,
850 					 Tcl_GetStringFromObj (optarg, NULL),
851 					 &baseOpt.attach_mode);
852 	if (baseOpt.attach == (Tcl_Channel) NULL) {
853 	  OT;
854 	  goto cleanup_after_error;
855 	}
856 	break;
857 
858       case 'i':
859 	if (0 != strncmp (option, "-in", len))
860 	  goto check_for_trans_option;
861 
862 	if (wrong_number) {
863 	  Tcl_AppendResult (interp, cmd, ": wrong # args, option \"", option, "\" requires an argument", (char*) NULL);
864 	  OT;
865 	  goto cleanup_after_error;
866 	}
867 
868 	baseOpt.source = Tcl_GetChannel (interp,
869 					 Tcl_GetStringFromObj (optarg, NULL),
870 					 &mode);
871 	if (baseOpt.source == (Tcl_Channel) NULL)
872 	  goto cleanup_after_error;
873 
874 	if (! (mode & TCL_READABLE)) {
875 	  Tcl_AppendResult (interp, cmd,
876 			    ": source-channel not readable",
877 			    (char*) NULL);
878 	  OT;
879 	  goto cleanup_after_error;
880 	}
881 	break;
882 
883       case 'o':
884 	if (0 != strncmp (option, "-out", len))
885 	  goto check_for_trans_option;
886 
887 	if (wrong_number) {
888 	  Tcl_AppendResult (interp, cmd, ": wrong # args, option \"", option, "\" requires an argument", (char*) NULL);
889 	  OT;
890 	  goto cleanup_after_error;
891 	}
892 
893 	baseOpt.destination = Tcl_GetChannel (interp,
894 					      Tcl_GetStringFromObj (optarg,
895 								    NULL),
896 					      &mode);
897 
898 	if (baseOpt.destination == (Tcl_Channel) NULL) {
899 	  OT;
900 	  goto cleanup_after_error;
901 	}
902 
903 	if (! (mode & TCL_WRITABLE)) {
904 	  Tcl_AppendResult (interp, cmd,
905 			    ": destination-channel not writable",
906 			    (char*) NULL);
907 	  OT;
908 	  goto cleanup_after_error;
909 	}
910 	break;
911 
912       case 's':
913 	if (0 != strncmp (option, "-seekpolicy", len))
914 	  goto check_for_trans_option;
915 
916 	if (wrong_number) {
917 	  Tcl_AppendResult (interp, cmd, ": wrong # args, option \"", option, "\" requires an argument", (char*) NULL);
918 	  OT;
919 	  goto cleanup_after_error;
920 	}
921 
922 	baseOpt.policy = optarg;
923 	Tcl_IncrRefCount (optarg);
924 	break;
925 
926       default:
927       check_for_trans_option:
928 	if (wrong_number) {
929 	  Tcl_AppendResult (interp, cmd, ": wrong # args, all options require an argument", (char*) NULL);
930 	  OT;
931 	  goto cleanup_after_error;
932 	}
933 
934 	if ((*OPT->setObjProc) == NULL) {
935 	  res = SET_OPTION     (option, Tcl_GetStringFromObj (optarg, NULL));
936 	} else {
937 	  res = SET_OPTION_OBJ (option, optarg);
938 	}
939 
940 	if (res != TCL_OK) {
941 	  OT;
942 	  goto cleanup_after_error;
943 	}
944 	break;
945       } /* switch option */
946   } /* while options */
947 
948   OT;
949 
950   /*
951    * Check argument restrictions, insert defaults if necessary,
952    * execute the required operation.
953    */
954 
955   if ((baseOpt.attach != (Tcl_Channel) NULL) &&
956       ((baseOpt.source      != (Tcl_Channel) NULL) ||
957        (baseOpt.destination != (Tcl_Channel) NULL))) {
958     Tcl_AppendResult (interp, cmd,
959 	      ": inconsistent options, -in/-out not allowed with -attach",
960 		      (char*) NULL);
961 
962     PRINT ("Inconsistent options\n"); FL;
963     goto cleanup_after_error;
964   }
965 
966   if ((baseOpt.attach == (Tcl_Channel) NULL) &&
967       baseOpt.policy !=  (Tcl_Obj*) NULL) {
968 
969     Tcl_AppendResult (interp, cmd,
970 		      ": inconsistent options, -seekpolicy ",
971 		      "not allowed without -attach",
972 		      (char*) NULL);
973 
974     PRINT ("Inconsistent options\n"); FL;
975     goto cleanup_after_error;
976   }
977 
978   if ((baseOpt.source == (Tcl_Channel) NULL) &&
979       (baseOpt.attach == (Tcl_Channel) NULL))
980     wrong_mod2 = 0;
981   else
982     wrong_mod2 = 1;
983 
984   if (wrong_mod2 == (objc % 2)) {
985       Tcl_AppendResult (interp, cmd, ": wrong # args", (char*) NULL);
986       PRINT ("Wrong # args\n"); FL;
987       goto cleanup_after_error;
988   }
989 
990   res = CHECK_OPTINFO (baseOpt);
991   if (res != TCL_OK) {
992     DELETE_OPTINFO;
993 
994     PRINT ("Options contain errors\n"); FL;
995     DONE (TrfExecuteObjCmd);
996     return TCL_ERROR;
997   }
998 
999   if (baseOpt.attach == (Tcl_Channel) NULL) /* TRF_IMMEDIATE */ {
1000     /*
1001      * Immediate execution of transformation requested.
1002      */
1003 
1004     res = TransformImmediate (interp, entry,
1005 			      baseOpt.source, baseOpt.destination,
1006 			      objv [0], optInfo);
1007 
1008   } else /* TRF_ATTACH */ {
1009     /*
1010      * User requested attachment of transformation procedure to a channel.
1011      * In case of a stub-aware interpreter use that to check for the
1012      * existence of the necessary patches ! Bail out if not.
1013      */
1014 
1015 #ifdef USE_TCL_STUBS
1016     if (Tcl_StackChannel == NULL) {
1017       Tcl_AppendResult (interp, cmd, ": this feature (-attach) is not ",
1018 			"available as the required patch to the core ",
1019 			"was not applied", (char*) NULL);
1020       DELETE_OPTINFO;
1021 
1022       PRINT ("-attach not available\n"); FL;
1023       DONE (TrfExecuteObjCmd);
1024       return TCL_ERROR;
1025     }
1026 #endif
1027 
1028     res = AttachTransform (entry, &baseOpt, optInfo, interp);
1029 
1030     if (baseOpt.policy != (Tcl_Obj*) NULL) {
1031       Tcl_DecrRefCount (baseOpt.policy);
1032       baseOpt.policy = (Tcl_Obj*) NULL;
1033     }
1034   }
1035 
1036   DELETE_OPTINFO;
1037   DONE (TrfExecuteObjCmd);
1038   return res;
1039 
1040 
1041 unknown_option:
1042   PRINT ("Unknown option \"%s\"\n", option); FL; OT;
1043 
1044   Tcl_AppendResult (interp, cmd, ": unknown option '", option, "', should be '-attach/in/out' or '-seekpolicy'",
1045 		    (char*) NULL);
1046   /* fall through to cleanup */
1047 
1048 cleanup_after_error:
1049   DELETE_OPTINFO;
1050   DONE (TrfExecuteObjCmd);
1051   return TCL_ERROR;
1052 }
1053 
1054 /*
1055  *------------------------------------------------------*
1056  *
1057  *	TrfDeleteCmd --
1058  *
1059  *	------------------------------------------------*
1060  *	Trap handler. Called by the Tcl core during
1061  *	destruction of the command for invocation of a
1062  *	transformation.
1063  *	------------------------------------------------*
1064  *
1065  *	Sideeffects:
1066  *		Removes the transformation from the registry.
1067  *
1068  *	Result:
1069  *		None.
1070  *
1071  *------------------------------------------------------*
1072  */
1073 
1074 static void
TrfDeleteCmd(clientData)1075 TrfDeleteCmd (clientData)
1076 ClientData clientData;
1077 {
1078   Trf_RegistryEntry* entry;
1079 
1080   START (TrfDeleteCmd);
1081 
1082   entry = (Trf_RegistryEntry*) clientData;
1083 
1084   TrfUnregister (entry->interp, entry);
1085   DONE (TrfDeleteCmd);
1086 }
1087 
1088 /*
1089  *----------------------------------------------------------------------
1090  *
1091  * TrfInfoObjCmd --
1092  *
1093  *	This procedure is invoked to process the "trfinfo" Tcl command.
1094  *	See the user documentation for details on what it does.
1095  *
1096  * Results:
1097  *	A standard Tcl result.
1098  *
1099  * Side effects:
1100  *	None.
1101  *
1102  *----------------------------------------------------------------------
1103  */
1104 #if 0
1105 static int
1106 TrfInfoObjCmd (notUsed, interp, objc, objv)
1107      ClientData              notUsed;	/* Not used. */
1108      Tcl_Interp*             interp;	/* Current interpreter. */
1109      int                     objc;
1110      struct Tcl_Obj* CONST * objv;
1111 {
1112   /*
1113    * trfinfo <channel>
1114    */
1115 
1116   static char* subcmd [] = {
1117     "seekstate", "seekcfg", NULL
1118   };
1119   enum subcmd {
1120     TRFINFO_SEEKSTATE, TRFINFO_SEEKCFG
1121   };
1122 
1123   Tcl_Channel                chan;
1124   int                        mode, pindex;
1125   char*                      chanName;
1126   TrfTransformationInstance* trans;
1127 
1128 
1129   if ((objc < 2) || (objc > 3)) {
1130     Tcl_AppendResult (interp,
1131 		      "wrong # args: should be \"trfinfo cmd channel\"",
1132 		      (char*) NULL);
1133     return TCL_ERROR;
1134   }
1135 
1136   chanName = Tcl_GetStringFromObj (objv [2], NULL);
1137   chan     = Tcl_GetChannel (interp, chanName, &mode);
1138 
1139   if (chan == (Tcl_Channel) NULL) {
1140     return TCL_ERROR;
1141   }
1142 
1143   if (Tcl_GetChannelType (chan)->seekProc != TrfSeek) {
1144     /* No trf transformation, info not applicable.
1145      */
1146 
1147     Tcl_AppendResult (interp,
1148 		      "channel \"", chanName,
1149 		      "\" is no transformation from trf",
1150 		      (char*) NULL);
1151     return TCL_ERROR;
1152   }
1153 
1154   /* Peek into the instance structure and return the requested
1155    * information.
1156    */
1157 
1158   if (Tcl_GetIndexFromObj(interp, objv [1], subcmd, "subcommand", 0,
1159 			  &pindex) != TCL_OK) {
1160     return TCL_ERROR;
1161   }
1162 
1163   trans = (TrfTransformationInstance*) Tcl_GetChannelInstanceData (chan);
1164 
1165   switch (pindex) {
1166   case TRFINFO_SEEKSTATE:
1167     {
1168       Tcl_Obj* state = SeekStateGet (interp, &trans->seekState);
1169 
1170       if (state == NULL)
1171 	return TCL_ERROR;
1172 
1173       Tcl_SetObjResult (interp, state);
1174       return TCL_OK;
1175     }
1176     break;
1177 
1178   case TRFINFO_SEEKCFG:
1179     {
1180       Tcl_Obj* cfg = SeekConfigGet (interp, &trans->seekCfg);
1181 
1182       if (cfg == NULL)
1183 	return TCL_ERROR;
1184 
1185       Tcl_SetObjResult (interp, cfg);
1186       return TCL_OK;
1187     }
1188     break;
1189 
1190   default:
1191     /* impossible */
1192     return TCL_ERROR;
1193   }
1194 
1195   /* We should not come to this place */
1196   return TCL_ERROR;
1197 }
1198 #endif
1199 
1200 /*
1201  *------------------------------------------------------*
1202  *
1203  *	TrfInit_Info --
1204  *
1205  *	------------------------------------------------*
1206  *	Register the 'info' command.
1207  *	------------------------------------------------*
1208  *
1209  *	Sideeffects:
1210  *		As of 'Tcl_CreateObjCommand'.
1211  *
1212  *	Result:
1213  *		A standard Tcl error code.
1214  *
1215  *------------------------------------------------------*
1216  */
1217 
1218 int
TrfInit_Info(interp)1219 TrfInit_Info (interp)
1220 Tcl_Interp* interp;
1221 {
1222 #if 0
1223   Tcl_CreateObjCommand (interp, "trfinfo", TrfInfoObjCmd,
1224 			(ClientData) NULL,
1225 			(Tcl_CmdDeleteProc *) NULL);
1226 #endif
1227   return TCL_OK;
1228 }
1229 
1230 /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
1231  */
1232 /*
1233  *------------------------------------------------------*
1234  *
1235  *	TrfBlock --
1236  *
1237  *	------------------------------------------------*
1238  *	Trap handler. Called by the generic IO system
1239  *	during option processing to change the blocking
1240  *	mode of the channel.
1241  *	------------------------------------------------*
1242  *
1243  *	Sideeffects:
1244  *		Forwards the request to the underlying
1245  *		channel.
1246  *
1247  *	Result:
1248  *		0 if successful, errno when failed.
1249  *
1250  *------------------------------------------------------*
1251  */
1252 
1253 static int
TrfBlock(instanceData,mode)1254 TrfBlock (instanceData, mode)
1255 ClientData  instanceData;
1256 int mode;
1257 {
1258   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
1259   char                   block [2] = {0,0};
1260   Tcl_Channel            parent;
1261 
1262   START (TrfBlock);
1263   PRINT ("Mode = %d\n", mode); FL;
1264 
1265   parent = DOWNC (trans);
1266 
1267   if (mode == TCL_MODE_NONBLOCKING) {
1268     trans->flags |= CHANNEL_ASYNC;
1269     block [0] = '0';
1270   } else {
1271     trans->flags &= ~(CHANNEL_ASYNC);
1272     block [0] = '1';
1273   }
1274 
1275 #ifndef USE_TCL_STUBS
1276   Tcl_SetChannelOption (NULL, parent, "-blocking", block);
1277 #else
1278   if ((trans->patchVariant == PATCH_ORIG) ||
1279       (trans->patchVariant == PATCH_82)) {
1280     /*
1281      * Both old-style patch and first integrated version of the patch
1282      * require the transformation to pass the blocking mode to the
1283      * channel downstream. The newest implementation (PATCH_832)
1284      * handles this in the core.
1285      */
1286 
1287     Tcl_SetChannelOption (NULL, parent, "-blocking", block);
1288   }
1289 #endif
1290 
1291   DONE (TrfBlock);
1292   return 0;
1293 }
1294 
1295 /*
1296  *------------------------------------------------------*
1297  *
1298  *	TrfClose --
1299  *
1300  *	------------------------------------------------*
1301  *	Trap handler. Called by the generic IO system
1302  *	during destruction of the transformation channel.
1303  *	------------------------------------------------*
1304  *
1305  *	Sideeffects:
1306  *		Releases the memory allocated in
1307  *		'AttachTransform'.
1308  *
1309  *	Result:
1310  *		None.
1311  *
1312  *------------------------------------------------------*
1313  */
1314 
1315 static int
TrfClose(instanceData,interp)1316 TrfClose (instanceData, interp)
1317 ClientData  instanceData;
1318 Tcl_Interp* interp;
1319 {
1320   /*
1321    * The parent channel will be removed automatically
1322    * (if necessary and/or desired).
1323    */
1324 
1325   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
1326   Tcl_Channel               parent;
1327 
1328   START (TrfClose);
1329 
1330 #ifndef USE_TCL_STUBS
1331   if ((trans  == (TrfTransformationInstance*) NULL) ||
1332       (interp == (Tcl_Interp*) NULL)) {
1333     /* Hack, prevent 8.0 from crashing upon exit if channels
1334      * with transformations were left open during exit
1335      *
1336      * Suggested by Mikhail Teterin <mi@aldan.algebra.com> 25.11.1999.
1337      */
1338 
1339     DONE (TrfClose);
1340     return TCL_OK;
1341   }
1342 #endif
1343 
1344   parent = DOWNC (trans);
1345 
1346   /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
1347    * Remove event handler to underlying channel, this could
1348    * be because we are closing for real, or being "unstacked".
1349    */
1350 
1351 #ifndef USE_TCL_STUBS
1352   Tcl_DeleteChannelHandler (parent, ChannelHandler, (ClientData) trans);
1353 #else
1354   if ((trans->patchVariant == PATCH_ORIG) ||
1355       (trans->patchVariant == PATCH_82)) {
1356     Tcl_DeleteChannelHandler (parent, ChannelHandler, (ClientData) trans);
1357   }
1358   /*
1359    * PATCH_832 doesn't use channelhandlers for communication of events
1360    * between the channels of stack anymore.
1361    */
1362 #endif
1363 
1364   TimerKill (trans);
1365 
1366   /*
1367    * Flush data waiting in transformation buffers to output.
1368    * Flush input too, maybe there are side effects other
1369    * parts do rely on (-> message digests).
1370    */
1371 
1372   if (trans->mode & TCL_WRITABLE) {
1373     PRINT ("out.flushproc\n"); FL;
1374 
1375     trans->out.vectors->flushProc (trans->out.control,
1376 				   (Tcl_Interp*) NULL,
1377 				   trans->clientData);
1378   }
1379 
1380   if (trans->mode & TCL_READABLE) {
1381     if (!trans->readIsFlushed) {
1382       PRINT ("in_.flushproc\n"); FL;
1383 
1384       trans->readIsFlushed = 1;
1385       trans->in.vectors->flushProc (trans->in.control,
1386 				    (Tcl_Interp*) NULL,
1387 				    trans->clientData);
1388     }
1389   }
1390 
1391   if (trans->mode & TCL_WRITABLE) {
1392     PRINT ("out.deleteproc\n"); FL;
1393     trans->out.vectors->deleteProc (trans->out.control, trans->clientData);
1394   }
1395 
1396   if (trans->mode & TCL_READABLE) {
1397     PRINT ("in_.deleteproc\n"); FL;
1398     trans->in.vectors->deleteProc  (trans->in.control,  trans->clientData);
1399   }
1400 
1401   ResultClear (&trans->result);
1402 
1403   /*
1404    * Complement to NEW_TRANSFORM in AttachChannel.
1405    * [Bug 2788106].
1406    */
1407   ckfree((void *)trans);
1408 
1409   DONE (TrfClose);
1410   return TCL_OK;
1411 }
1412 
1413 /*
1414  *------------------------------------------------------*
1415  *
1416  *	TrfInput --
1417  *
1418  *	------------------------------------------------*
1419  *	Called by the generic IO system to convert read data.
1420  *	------------------------------------------------*
1421  *
1422  *	Sideeffects:
1423  *		As defined by the conversion.
1424  *
1425  *	Result:
1426  *		A transformed buffer.
1427  *
1428  *------------------------------------------------------*
1429  */
1430 
1431 static int
TrfInput(instanceData,buf,toRead,errorCodePtr)1432 TrfInput (instanceData, buf, toRead, errorCodePtr)
1433 ClientData instanceData;
1434 char*      buf;
1435 int        toRead;
1436 int*       errorCodePtr;
1437 {
1438   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
1439   int       gotBytes, read, i, res, copied, maxRead;
1440   Tcl_Channel parent;
1441 
1442   START (TrfInput);
1443   PRINT ("trans = %p, toRead = %d\n", trans, toRead); FL;
1444 
1445   parent = DOWNC (trans);
1446 
1447   /* should assert (trans->mode & TCL_READABLE) */
1448 
1449   gotBytes = 0;
1450 
1451   SEEK_DUMP (TrfInput; Start);
1452 
1453   while (toRead > 0) {
1454     /* Loop until the request is satisfied
1455      * (or no data available from below, possibly EOF).
1456      */
1457 
1458     SEEK_DUMP (TrfInput; Loop_);
1459 
1460     /* The position may be inside the buffer, and not at its start.
1461      * Remove the superfluous data now. There was no need to do it
1462      * earlier, as intervening seeks and writes could have discarded
1463      * the buffer completely, seeked back to an earlier point in it, etc.
1464      * We can be sure that the location is not behind its end!
1465      * And for an empty buffer location and buffer start are identical,
1466      * bypassing this code. See integrity constraints listed in the
1467      * description of Trf_TransformationInstance.
1468      */
1469 
1470     if (trans->seekState.upLoc > trans->seekState.upBufStartLoc) {
1471       ResultDiscardAtStart (&trans->result,
1472 		    trans->seekState.upLoc - trans->seekState.upBufStartLoc);
1473     }
1474 
1475     /* Assertion: UpLoc == UpBufStartLoc now. */
1476 
1477     SEEK_DUMP (TrfInput; Disc<);
1478 
1479     copied    = ResultCopy (&trans->result, (unsigned char*) buf, toRead);
1480     toRead   -= copied;
1481     buf      += copied;
1482     gotBytes += copied;
1483     trans->seekState.upLoc += copied;
1484 
1485     SEEK_DUMP (TrfInput; Copy<);
1486 
1487     if (toRead == 0) {
1488       PRINT ("Got %d, satisfied from result buffer\n", gotBytes); FL;
1489       DONE  (TrfInput);
1490       return gotBytes;
1491     }
1492 
1493     /* The buffer is exhausted, but the caller wants even more. We now have
1494      * to go to the underlying channel, get more bytes and then transform
1495      * them for delivery. We may not get that we want (full EOF or temporary
1496      * out of data). This part has to manipulate the various seek locations
1497      * in a more complicated way to keep everything in sync.
1498      */
1499 
1500     /* Assertion:    UpLoc == UpBufEndLoc now (and == UpBufStartLoc).
1501      * Additionally: UP_CONVERT (DownLoc - AheadOffset) == BufEndLoc
1502      */
1503 
1504     /*
1505      * Length (trans->result) == 0, toRead > 0 here  Use 'buf'! as target
1506      * to store the intermediary information read from the parent channel.
1507      *
1508      * Ask the transform how much data it allows us to read from
1509      * the underlying channel. This feature allows the transform to
1510      * signal EOF upstream although there is none downstream. Useful
1511      * to control an unbounded 'fcopy' for example, either through counting
1512      * bytes, or by pattern matching.
1513      */
1514 
1515     if (trans->in.vectors->maxReadProc == (Trf_QueryMaxRead*) NULL)
1516       maxRead = -1;
1517     else
1518       maxRead = trans->in.vectors->maxReadProc (trans->in.control,
1519 						trans->clientData);
1520 
1521     if (maxRead >= 0) {
1522       if (maxRead < toRead) {
1523 	toRead = maxRead;
1524       }
1525     } /* else: 'maxRead < 0' == Accept the current value of toRead */
1526 
1527     if (toRead <= 0) {
1528       PRINT ("Got %d, constrained by script\n", gotBytes); FL;
1529       DONE  (TrfInput);
1530       return gotBytes;
1531     }
1532 
1533     PRINT ("Read from parent %p\n", parent);
1534     IN; IN;
1535 
1536     read = READ (trans, buf, toRead);
1537 
1538     OT; OT;
1539     PRINT  ("................\n");
1540     /*PRTSTR ("Retrieved = {%d, \"%s\"}\n", read, buf);*/
1541 
1542     PRINT ("Retrieved = %d {\n", read);
1543     DUMP  (read, buf);
1544     PRINT ("}\n");
1545     STREAM_IN (trans, read, buf);
1546 
1547     if (read < 0) {
1548       /* Report errors to caller.
1549        * The state of the seek system is unchanged!
1550        */
1551 
1552       if ((Tcl_GetErrno () == EAGAIN) && (gotBytes > 0)) {
1553 	  /* EAGAIN is a special situation.  If we had some data
1554 	   * before we report that instead of the request to re-try.
1555 	   */
1556 
1557 	  PRINT ("Got %d, read < 0, <EAGAIN>\n", gotBytes);
1558 	  FL; DONE (TrfInput);
1559 	  return gotBytes;
1560       }
1561 
1562       *errorCodePtr = Tcl_GetErrno ();
1563 
1564       PRINT ("Got %d, read < 0, report error %d\n", gotBytes, *errorCodePtr);
1565       FL; DONE (TrfInput);
1566       return -1;
1567     }
1568 
1569     if (read == 0) {
1570       /* Check wether we hit on EOF in 'parent' or
1571        * not. If not differentiate between blocking and
1572        * non-blocking modes. In non-blocking mode we ran
1573        * temporarily out of data. Signal this to the caller
1574        * via EWOULDBLOCK and error return (-1). In the other
1575        * cases we simply return what we got and let the
1576        * caller wait for more. On the other hand, if we got
1577        * an EOF we have to convert and flush all waiting
1578        * partial data.
1579        */
1580 
1581       /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
1582        */
1583       if (! Tcl_Eof (parent)) {
1584 	/* The state of the seek system is unchanged! */
1585 
1586 	if (gotBytes == 0 && trans->flags & CHANNEL_ASYNC) {
1587 	  *errorCodePtr = EWOULDBLOCK;
1588 
1589 	  PRINT ("Got %d, report EWOULDBLOCK\n", gotBytes); FL;
1590 	  DONE (TrfInput);
1591 	  return -1;
1592 	} else {
1593 	  PRINT ("(Got = %d || not async)\n", gotBytes); FL;
1594 	  DONE (TrfInput);
1595 	  return gotBytes;
1596 	}
1597       } else {
1598 	PRINT ("EOF in downstream channel\n"); FL;
1599 	if (trans->readIsFlushed) {
1600 	  /* The state of the seek system is unchanged! */
1601 	  /* already flushed, nothing to do anymore */
1602 	  PRINT ("Got %d, !read flushed\n", gotBytes); FL;
1603 	  DONE (TrfInput);
1604 	  return gotBytes;
1605 	}
1606 
1607 	/* Now this is a bit different. The partial data waiting is converted
1608 	 * and returned. So the 'AheadOffset' changes despite the location
1609 	 * downstream not changing at all. It is now the negative of its
1610 	 * additive inverse modulo 'numBytesDown':
1611 	 *	 -((-k)%n) == -((n-1)-k) == k+1-n.
1612 	 */
1613 
1614 	PRINT ("in_.flushproc\n"); FL;
1615 
1616 	trans->readIsFlushed = 1;
1617 	trans->lastStored    = 0;
1618 
1619 	res = trans->in.vectors->flushProc (trans->in.control,
1620 					    (Tcl_Interp*) NULL,
1621 					    trans->clientData);
1622 	if (trans->seekState.allowed &&
1623 	    trans->seekState.used.numBytesDown > 1) {
1624 	  trans->seekState.aheadOffset += -trans->seekState.used.numBytesDown;
1625 	}
1626 
1627 	SEEK_DUMP (TrfInput; AhdC<);
1628 
1629 	if (ResultLength (&trans->result) == 0) {
1630 	  /* we had nothing to flush */
1631 	  PRINT ("Got %d, read flushed / no result\n", gotBytes); FL;
1632 	  DONE (TrfInput);
1633 	  return gotBytes;
1634 	}
1635 	continue; /* at: while (toRead > 0) */
1636       }
1637     } /* read == 0 */
1638 
1639     /* Transform the read chunk, which was not empty.
1640      * The transformation processes 'read + aheadOffset' bytes.
1641      * So UP_CONVERT (read+ahead) == #bytes produced == ResultLength!
1642      * And  (read+ahead) % #down == #bytes now waiting == new ahead.
1643      */
1644 
1645     SEEK_DUMP (TrfInput; Read<);
1646     trans->lastStored = 0;
1647 
1648     if (trans->in.vectors->convertBufProc){
1649       PRINT ("in_.convertbufproc\n"); FL;
1650 
1651       res = trans->in.vectors->convertBufProc (trans->in.control,
1652 					       (unsigned char*) buf, read,
1653 					       (Tcl_Interp*) NULL,
1654 					       trans->clientData);
1655     } else {
1656       PRINT ("in_.convertproc\n"); FL;
1657 
1658       res = TCL_OK;
1659       for (i=0; i < read; i++) {
1660 	res = trans->in.vectors->convertProc (trans->in.control, buf [i],
1661 					      (Tcl_Interp*) NULL,
1662 					      trans->clientData);
1663 	if (res != TCL_OK) {
1664 	  break;
1665 	}
1666       }
1667     }
1668 
1669     if (res != TCL_OK) {
1670       *errorCodePtr = EINVAL;
1671       PRINT ("Got %d, report error in transform (EINVAL)\n", gotBytes); FL;
1672       DONE (TrfInput);
1673       return -1;
1674     }
1675 
1676     /* Assert: UP_CONVERT (read+ahead) == ResultLength! */
1677 
1678     trans->seekState.downLoc += read;
1679 
1680     if (trans->seekState.allowed) {
1681       trans->seekState.aheadOffset += (read % trans->seekState.used.numBytesDown);
1682       trans->seekState.aheadOffset %= trans->seekState.used.numBytesDown;
1683     }
1684 
1685   } /* while toRead > 0 */
1686 
1687   SEEK_DUMP (TrfInput; Loop<);
1688 
1689   PRINT ("Got %d, after loop\n", gotBytes); FL;
1690   DONE (TrfInput);
1691   return gotBytes;
1692 }
1693 
1694 /*
1695  *------------------------------------------------------*
1696  *
1697  *	TrfOutput --
1698  *
1699  *	------------------------------------------------*
1700  *	Called by the generic IO system to convert data
1701  *	waiting to be written.
1702  *	------------------------------------------------*
1703  *
1704  *	Sideeffects:
1705  *		As defined by the transformation.
1706  *
1707  *	Result:
1708  *		A transformed buffer.
1709  *
1710  *------------------------------------------------------*
1711  */
1712 
1713 static int
TrfOutput(instanceData,buf,toWrite,errorCodePtr)1714 TrfOutput (instanceData, buf, toWrite, errorCodePtr)
1715 ClientData instanceData;
1716 CONST84 char*      buf;
1717 int        toWrite;
1718 int*       errorCodePtr;
1719 {
1720   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
1721   int i, res;
1722   Tcl_Channel parent;
1723 
1724   START (TrfOutput);
1725 
1726   parent = DOWNC (trans);
1727 
1728   /* should assert (trans->mode & TCL_WRITABLE) */
1729 
1730   /*
1731    * transformation results are automatically written to
1732    * the parent channel ('PutDestination' was configured
1733    * as write procedure in 'AttachTransform').
1734    */
1735 
1736   if (toWrite == 0) {
1737     /* Nothing came in to write, ignore the call
1738      */
1739 
1740     PRINT ("Nothing to write\n"); FL; DONE (TrfOutput);
1741     return 0;
1742   }
1743 
1744   SEEK_DUMP (TrfOutput; Start);
1745 
1746   /* toWrite / seekState.used.numBytesTransform = #tuples converted.
1747    * toWrite % seekState.used.numBytesTransform = #Bytes waiting in the transform.
1748    */
1749 
1750   SeekSynchronize (trans, parent);
1751 
1752   SEEK_DUMP (TrfOutput; Syncd);
1753 
1754   trans->lastWritten = 0;
1755 
1756   if (trans->out.vectors->convertBufProc){
1757     PRINT ("out.convertbufproc\n"); FL;
1758 
1759     res = trans->out.vectors->convertBufProc (trans->out.control,
1760 					      (unsigned char*) buf, toWrite,
1761 					      (Tcl_Interp*) NULL,
1762 					      trans->clientData);
1763   } else {
1764     PRINT ("out.convertproc\n"); FL;
1765 
1766     res = TCL_OK;
1767     for (i=0; i < toWrite; i++) {
1768       res = trans->out.vectors->convertProc (trans->out.control, buf [i],
1769 					     (Tcl_Interp*) NULL,
1770 					     trans->clientData);
1771       if (res != TCL_OK) {
1772 	break;
1773       }
1774     }
1775   }
1776 
1777   if (res != TCL_OK) {
1778     *errorCodePtr = EINVAL;
1779     PRINT ("error EINVAL\n"); FL; DONE (TrfInput);
1780     return -1;
1781   }
1782 
1783   /* Update seek state to new location
1784    * Assert: lastWritten == TRF_DOWN_CONVERT (trans, toWrite)
1785    */
1786 
1787   trans->seekState.upLoc        += toWrite;
1788   trans->seekState.upBufStartLoc = trans->seekState.upLoc;
1789   trans->seekState.upBufEndLoc   = trans->seekState.upLoc;
1790   trans->seekState.downLoc      += trans->lastWritten;
1791   trans->lastWritten       = 0;
1792 
1793   SEEK_DUMP (TrfOutput; Done_);
1794 
1795   /* In the last statement above the integer division automatically
1796    * strips off the #bytes waiting in the transform.
1797    */
1798 
1799   PRINT ("Written: %d\n", toWrite); FL; DONE (TrfOutput);
1800   return toWrite;
1801 }
1802 
1803 /*
1804  *------------------------------------------------------*
1805  *
1806  *	TrfSeek --
1807  *
1808  *	------------------------------------------------*
1809  *	This procedure is called by the generic IO level
1810  *	to move the access point in a channel.
1811  *	------------------------------------------------*
1812  *
1813  *	Sideeffects:
1814  *		Moves the location at which the channel
1815  *		will be accessed in future operations.
1816  *		Flushes all transformation buffers, then
1817  *		forwards it to the underlying channel.
1818  *
1819  *	Result:
1820  *		-1 if failed, the new position if
1821  *		successful. An output argument contains
1822  *		the POSIX error code if an error
1823  *		occurred, or zero.
1824  *
1825  *------------------------------------------------------*
1826  */
1827 
1828 static int
TrfSeek(instanceData,offset,mode,errorCodePtr)1829 TrfSeek (instanceData, offset, mode, errorCodePtr)
1830 ClientData instanceData;	/* The channel to manipulate */
1831 long       offset;		/* Size of movement. */
1832 int        mode;		/* How to move */
1833 int*       errorCodePtr;	/* Location of error flag. */
1834 {
1835   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
1836   int         result;
1837   int         newLoc;
1838 
1839   START (TrfSeek);
1840   PRINT ("(Mode = %d, Offset = %ld)\n", mode, offset); FL;
1841 
1842   /*
1843    * Several things to look at before deciding what to do.
1844    * Is it a tell request ?
1845    * Is the channel unseekable ?
1846    * If not, are we in pass-down mode ?
1847    * If not, check buffer boundaries, etc. before discarding buffers, etc.
1848    */
1849 
1850   if ((offset == 0) && (mode == SEEK_CUR)) {
1851     /* Tell location.
1852      */
1853 
1854     PRINT ("[Tell], Location = %d\n", trans->seekState.upLoc); FL;
1855     DONE (TrfSeek);
1856     return trans->seekState.upLoc;
1857   }
1858 
1859   if (!trans->seekState.allowed) {
1860     *errorCodePtr = EINVAL;
1861 
1862     PRINT ("[Unseekable]\n"); FL; DONE (TrfSeek);
1863     return -1;
1864   }
1865 
1866   /* Assert: seekState.allowed, numBytesDown > 0, numBytesTransform > 0 */
1867 
1868   if (trans->seekCfg.identity) {
1869     /* Pass down mode. Pass request and record the change. This is used after
1870      * restoration of constrained seek to force the usage of a new zero-point.
1871      */
1872 
1873     PRINT ("[Passing down]\n"); FL;
1874 
1875     SeekClearBuffer (trans, TCL_WRITABLE | TCL_READABLE);
1876 
1877     trans->seekState.changed = 1;
1878 
1879     result = SEEK (trans, offset, mode);
1880     *errorCodePtr = (result == -1) ? Tcl_GetErrno () : 0;
1881 
1882     SEEK_DUMP (TrfSeek; Pass<);
1883     DONE (TrfSeek);
1884     return result;
1885   }
1886 
1887   /* Constrained seeking, as specified by the transformation.
1888    */
1889 
1890   if (mode == SEEK_SET) {
1891     /* Convert and handle absolute from start as relative to current
1892      * location.
1893      */
1894 
1895     PRINT ("[Seek from start] => Seek relative\n"); FL;
1896     result = TrfSeek (trans, offset - trans->seekState.upLoc, SEEK_CUR,
1897 		      errorCodePtr);
1898     DONE (TrfSeek);
1899     return result;
1900   }
1901 
1902   if (mode == SEEK_END) {
1903     /* Can't do that right now! TODO */
1904     *errorCodePtr = EINVAL;
1905 
1906     PRINT ("[Seek from end not available]"); FL; DONE (TrfSeek);
1907     return -1;
1908   }
1909 
1910   /* Seeking relative to the current location.
1911    */
1912 
1913   newLoc = trans->seekState.upLoc + offset;
1914 
1915   if (newLoc % trans->seekState.used.numBytesTransform) {
1916     /* Seek allowed only to locations which are multiples of the input.
1917      */
1918 
1919     *errorCodePtr = EINVAL;
1920 
1921     PRINT ("Seek constrained to multiples of input tuples\n"); FL;
1922     DONE (TrfSeek);
1923     return -1;
1924   }
1925 
1926   if (newLoc < 0) {
1927     *errorCodePtr = EINVAL;
1928 
1929     PRINT ("[Seek relative], cannot seek before start of stream\n"); FL;
1930     DONE (TrfSeek);
1931     return -1;
1932   }
1933 
1934   if ((newLoc < trans->seekState.upBufStartLoc) ||
1935       (trans->seekState.upBufEndLoc <= newLoc)) {
1936     /* We are seeking out of the read buffer.
1937      * Discard it, adjust our position and seek the channel below to the
1938      * equivalent position.
1939      */
1940 
1941     int offsetDown, newDownLoc;
1942 
1943     PRINT ("[Seek relative], beyond read buffer\n"); FL;
1944 
1945     newDownLoc = trans->seekState.downZero + TRF_DOWN_CONVERT (trans, newLoc);
1946     offsetDown = newDownLoc - trans->seekState.downLoc;
1947 
1948     SeekClearBuffer (trans, TCL_WRITABLE | TCL_READABLE);
1949 
1950     if (offsetDown != 0) {
1951       result = SEEK (trans, offsetDown, SEEK_CUR);
1952       *errorCodePtr = (result == -1) ? Tcl_GetErrno () : 0;
1953     }
1954 
1955     trans->seekState.downLoc      += offsetDown;
1956     trans->seekState.upLoc         = newLoc;
1957     trans->seekState.upBufStartLoc = newLoc;
1958     trans->seekState.upBufEndLoc   = newLoc;
1959 
1960     SEEK_DUMP (TrfSeek; NoBuf);
1961     DONE (TrfSeek);
1962     return newLoc;
1963   }
1964 
1965   /* We are still inside the buffer, adjust the position
1966    * and clear out incomplete data waiting in the write
1967    * buffers, they are now invalid.
1968    */
1969 
1970   SeekClearBuffer (trans, TCL_WRITABLE);
1971   trans->seekState.upLoc = newLoc;
1972 
1973   SEEK_DUMP (TrfSeek; Base_);
1974   DONE (TrfSeek);
1975   return newLoc;
1976 }
1977 
1978 /*
1979  *------------------------------------------------------*
1980  *
1981  *	TrfWatch --
1982  *
1983  *	------------------------------------------------*
1984  *	Initialize the notifier to watch Tcl_Files from
1985  *	this channel.
1986  *	------------------------------------------------*
1987  *
1988  *	Sideeffects:
1989  *		Sets up the notifier so that a future
1990  *		event on the channel will be seen by Tcl.
1991  *
1992  *	Result:
1993  *		None.
1994  *
1995  *------------------------------------------------------*
1996  */
1997 	/* ARGSUSED */
1998 static void
TrfWatch(instanceData,mask)1999 TrfWatch (instanceData, mask)
2000 ClientData instanceData;	/* Channel to watch */
2001 int        mask;		/* Events of interest */
2002 {
2003   /*
2004    * 08/01/2000 - Completely rewritten to support as many versions of
2005    * the core and their different implementation s of stacked channels.
2006    */
2007   /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
2008    * Added the comments.  */
2009   /* The caller expressed interest in events occuring for this
2010    * channel. Instead of forwarding the call to the underlying
2011    * channel we now express our interest in events on that
2012    * channel. This will ripple through all stacked channels to
2013    * the bottom-most real one actually able to generate events
2014    * (files, sockets, pipes, ...). The improvement beyond the
2015    * simple forwarding is that the generated events will ripple
2016    * back up to us, until they reach the channel the user
2017    * expressed his interest in (via fileevent). This way the
2018    * low-level events are propagated upward to the place where
2019    * the real event script resides, something which does not
2020    * happen in the simple forwarding model. It loses these events.
2021    */
2022 
2023   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
2024 
2025   START (TrfWatch);
2026 
2027 #ifndef USE_TCL_STUBS
2028   /* 8.0.x. Original patch. */
2029 
2030   if (mask == trans->watchMask) {
2031     /* No changes in the expressed interest, skip this call.
2032      */
2033     DONE (TrfWatch);
2034     return;
2035   }
2036 
2037   ChannelHandlerKS (trans, mask);
2038 #else
2039   /* 8.1. and up */
2040 
2041   if ((trans->patchVariant == PATCH_ORIG) ||
2042       (trans->patchVariant == PATCH_82)) {
2043 
2044     if (mask == trans->watchMask) {
2045       /* No changes in the expressed interest, skip this call.
2046        */
2047       DONE (TrfWatch);
2048       return;
2049     }
2050 
2051     ChannelHandlerKS (trans, mask);
2052 
2053   } else if (trans->patchVariant == PATCH_832) {
2054     /* 8.3.2 and up */
2055 
2056     Tcl_DriverWatchProc* watchProc;
2057     Tcl_Channel          parent;
2058 
2059     trans->watchMask = mask;
2060 
2061     /* No channel handlers any more. We will be notified automatically
2062      * about events on the channel below via a call to our
2063      * 'TransformNotifyProc'. But we have to pass the interest down now.
2064      * We are allowed to add additional 'interest' to the mask if we want
2065      * to. But this transformation has no such interest. It just passes
2066      * the request down, unchanged.
2067      */
2068 
2069     parent    = DOWNC (trans);
2070     watchProc = Tcl_ChannelWatchProc (Tcl_GetChannelType (parent));
2071 
2072     (*watchProc) (Tcl_GetChannelInstanceData(parent), mask);
2073 
2074   } else {
2075     Tcl_Panic ("Illegal value for 'patchVariant'");
2076   }
2077 #endif
2078 
2079   /*
2080    * Management of the internal timer.
2081    */
2082 
2083   if (!(mask & TCL_READABLE) || (ResultLength(&trans->result) == 0)) {
2084     /* A pending timer may exist, but either is there no (more)
2085      * interest in the events it generates or nothing is available
2086      * for reading. Remove it, if existing.
2087      */
2088 
2089     TimerKill (trans);
2090   } else {
2091     /* There might be no pending timer, but there is interest in
2092      * readable events and we actually have data waiting, so
2093      * generate a timer to flush that if it does not exist.
2094      */
2095 
2096     TimerSetup (trans);
2097   }
2098 
2099   DONE (TrfWatch);
2100 }
2101 
2102 /*
2103  *------------------------------------------------------*
2104  *
2105  *	TrfGetFile --
2106  *
2107  *	------------------------------------------------*
2108  *	Called from Tcl_GetChannelHandle to retrieve
2109  *	OS specific file handle from inside this channel.
2110  *	------------------------------------------------*
2111  *
2112  *	Sideeffects:
2113  *		None.
2114  *
2115  *	Result:
2116  *		The appropriate Tcl_File or NULL if not
2117  *		present.
2118  *
2119  *------------------------------------------------------*
2120  */
2121 
2122 static int
TrfGetFile(instanceData,direction,handlePtr)2123 TrfGetFile (instanceData, direction, handlePtr)
2124 ClientData  instanceData;	/* Channel to query */
2125 int         direction;		/* Direction of interest */
2126 ClientData* handlePtr;		/* Place to store the handle into */
2127 {
2128   /*
2129    * return handle belonging to parent channel
2130    */
2131 
2132   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
2133   Tcl_Channel parent;
2134 
2135   START (TrfGetFile);
2136 
2137   parent = DOWNC (trans);
2138 
2139   DONE (TrfGetFile);
2140   return Tcl_GetChannelHandle (parent, direction, handlePtr);
2141 }
2142 
2143 /*
2144  *------------------------------------------------------*
2145  *
2146  *	TrfSetOption --
2147  *
2148  *	------------------------------------------------*
2149  *	Called by the generic layer to handle the reconfi-
2150  *	guration of channel specific options. Unknown
2151  *	options are passed downstream.
2152  *	------------------------------------------------*
2153  *
2154  *	Sideeffects:
2155  *		As defined by the channel downstream.
2156  *
2157  *	Result:
2158  *		A standard TCL error code.
2159  *
2160  *------------------------------------------------------*
2161  */
2162 
2163 static int
TrfSetOption(instanceData,interp,optionName,value)2164 TrfSetOption (instanceData, interp, optionName, value)
2165      ClientData  instanceData;
2166      Tcl_Interp* interp;
2167      CONST char* optionName;
2168      CONST char* value;
2169 {
2170   /* Recognized options:
2171    *
2172    * -seekpolicy	Accepted values: unseekable, identity, {}
2173    */
2174 
2175   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
2176 
2177   START (TrfSetOption);
2178 
2179   if (0 == strcmp (optionName, "-seekpolicy")) {
2180     /* The seekpolicy is about to be changed. Make sure that we got a valid
2181      * value and that it really changes the used policy. Failing the first
2182      * test causes an error, failing the second causes the system to silently
2183      * ignore this request. Reconfiguration will fail for a non-overidable
2184      * policy too.
2185      */
2186 
2187     if (!trans->seekCfg.overideAllowed) {
2188       Tcl_SetErrno (EINVAL);
2189       Tcl_AppendResult (interp, "It is not allowed to overide ",
2190 			"the seek policy used by this channel.", NULL);
2191       DONE (TrfSetOption);
2192       return TCL_ERROR;
2193     }
2194 
2195     if (0 == strcmp (value, "unseekable")) {
2196       if (!trans->seekState.allowed) {
2197 	/* Ignore the request if the channel already uses this policy.
2198 	 */
2199 	DONE (TrfSetOption);
2200 	return TCL_OK;
2201       }
2202 
2203       TRF_SET_UNSEEKABLE (trans->seekState.used);
2204       trans->seekState.allowed = 0;
2205       trans->seekCfg.identity  = 0;
2206 
2207       /* Changed is not touched! We might have been forced to identity
2208        * before, and have to remember this for any restoration.
2209        */
2210 
2211     } else if (0 == strcmp (value, "identity")) {
2212 
2213       if (trans->seekState.allowed &&
2214 	  (trans->seekState.used.numBytesTransform == 1) &&
2215 	  (trans->seekState.used.numBytesDown == 1)) {
2216 
2217 	/* Ignore the request if the channel already uses this policy.
2218 	 */
2219 	DONE (TrfSetOption);
2220 	return TCL_OK;
2221       }
2222 
2223       trans->seekState.used.numBytesTransform = 1;
2224       trans->seekState.used.numBytesDown      = 1;
2225       trans->seekState.allowed                = 1;
2226       trans->seekCfg.identity                 = 1;
2227       trans->seekState.changed                = 0;
2228 
2229     } else if (0 == strcmp (value, "")) {
2230       if ((trans->seekState.used.numBytesTransform ==
2231 	   trans->seekCfg.chosen.numBytesTransform) &&
2232 	  (trans->seekState.used.numBytesDown ==
2233 	   trans->seekCfg.chosen.numBytesDown)) {
2234 	/* Ignore the request if the channel already uses hios chosen policy.
2235 	 */
2236 	DONE (TrfSetOption);
2237 	return TCL_OK;
2238       }
2239 
2240       trans->seekState.used.numBytesTransform =
2241 	trans->seekCfg.chosen.numBytesTransform;
2242 
2243       trans->seekState.used.numBytesDown =
2244 	trans->seekCfg.chosen.numBytesDown;
2245 
2246       trans->seekState.allowed = !TRF_IS_UNSEEKABLE (trans->seekState.used);
2247 
2248       if (trans->seekState.changed) {
2249 	/* Define new base location. Resync up and down to get the
2250 	 * proper location without read-ahead. Reinitialize the
2251 	 * upper location.
2252 	 */
2253 
2254 	Tcl_Channel parent = DOWNC (trans);
2255 	SeekSynchronize (trans, parent);
2256 	trans->seekState.downLoc     = TELL (trans);
2257 
2258 #ifdef USE_TCL_STUBS
2259 	if (trans->patchVariant == PATCH_832) {
2260 	  trans->seekState.downLoc  -= Tcl_ChannelBuffered (parent);
2261 	}
2262 #endif
2263 	trans->seekState.downZero    = trans->seekState.downLoc;
2264 	trans->seekState.aheadOffset = 0;
2265 
2266 	trans->seekState.upLoc         = 0;
2267 	trans->seekState.upBufStartLoc = 0;
2268 	trans->seekState.upBufEndLoc   = ResultLength (&trans->result);
2269       }
2270 
2271       trans->seekCfg.identity  = 0;
2272       trans->seekState.changed = 0;
2273 
2274     } else {
2275       Tcl_SetErrno (EINVAL);
2276       Tcl_AppendResult (interp, "Invalid value \"", value,
2277 			"\", must be one of 'unseekable', 'identity' or ''.",
2278 			NULL);
2279       DONE (TrfSetOption);
2280       return TCL_ERROR;
2281     }
2282 
2283   } else {
2284     int res;
2285     res = SETOPT (interp, trans, optionName, value);
2286     DONE (TrfSetOption);
2287     return res;
2288   }
2289 
2290   DONE (TrfSetOption);
2291   return TCL_OK;
2292 }
2293 
2294 /*
2295  *------------------------------------------------------*
2296  *
2297  *	TrfGetOption --
2298  *
2299  *	------------------------------------------------*
2300  *	Called by generic layer to handle requests for
2301  *	the values of channel specific options. As this
2302  *	channel type does not have such, it simply passes
2303  *	all requests downstream.
2304  *	------------------------------------------------*
2305  *
2306  *	Sideeffects:
2307  *		Adds characters to the DString refered by
2308  *		'dsPtr'.
2309  *
2310  *	Result:
2311  *		A standard TCL error code.
2312  *
2313  *------------------------------------------------------*
2314  */
2315 
2316 static int
TrfGetOption(instanceData,interp,optionName,dsPtr)2317 TrfGetOption (instanceData, interp, optionName, dsPtr)
2318      ClientData    instanceData;
2319      Tcl_Interp*   interp;
2320      CONST84 char* optionName;
2321      Tcl_DString*  dsPtr;
2322 {
2323   /* Recognized options:
2324    *
2325    * -seekcfg
2326    * -seekstate
2327    * -seekpolicy
2328    */
2329 
2330   TrfTransformationInstance* trans = (TrfTransformationInstance*) instanceData;
2331 
2332   if (optionName == (char*) NULL) {
2333     /* A list of options and their values was requested,
2334      */
2335 
2336     Tcl_Obj* tmp;
2337     char policy [20];
2338 
2339     SeekPolicyGet (trans, policy);
2340     Tcl_DStringAppendElement (dsPtr, "-seekpolicy");
2341     Tcl_DStringAppendElement (dsPtr, policy);
2342 
2343     Tcl_DStringAppendElement (dsPtr, "-seekcfg");
2344     tmp = SeekConfigGet (interp, &trans->seekCfg);
2345     Tcl_DStringAppendElement (dsPtr, Tcl_GetStringFromObj (tmp, NULL));
2346     Tcl_DecrRefCount (tmp);
2347 
2348     Tcl_DStringAppendElement (dsPtr, "-seekstate");
2349     tmp = SeekStateGet (interp, &trans->seekState);
2350     Tcl_DStringAppendElement (dsPtr, Tcl_GetStringFromObj (tmp, NULL));
2351     Tcl_DecrRefCount (tmp);
2352 
2353     /* Pass the request down to all channels below so that we may a complete
2354      * state.
2355      */
2356 
2357     return GETOPT (interp, trans, optionName, dsPtr);
2358 
2359   } else if (0 == strcmp (optionName, "-seekpolicy")) {
2360     /* Deduce the policy in effect, use chosen/used
2361      * policy and identity to do this. Use a helper
2362      * procedure to allow easy reuse in the code above.
2363      */
2364 
2365     char policy [20];
2366 
2367     SeekPolicyGet (trans, policy);
2368     Tcl_DStringAppend (dsPtr, policy, -1);
2369     return TCL_OK;
2370 
2371   } else if (0 == strcmp (optionName, "-seekcfg")) {
2372     Tcl_Obj* tmp;
2373 
2374     tmp = SeekConfigGet (interp, &trans->seekCfg);
2375     Tcl_DStringAppend (dsPtr, Tcl_GetStringFromObj (tmp, NULL), -1);
2376     Tcl_DecrRefCount (tmp);
2377 
2378     return TCL_OK;
2379   } else if (0 == strcmp (optionName, "-seekstate")) {
2380     Tcl_Obj* tmp;
2381 
2382     tmp = SeekStateGet (interp, &trans->seekState);
2383     Tcl_DStringAppend (dsPtr, Tcl_GetStringFromObj (tmp, NULL), -1);
2384     Tcl_DecrRefCount (tmp);
2385 
2386     return TCL_OK;
2387   } else {
2388     /* Unknown option. Pass it down to the channels below, maybe one
2389      * of them is able to handle this request.
2390      */
2391 
2392     return GETOPT (interp, trans, optionName, dsPtr);
2393 #if 0
2394     Tcl_SetErrno (EINVAL);
2395     return Tcl_BadChannelOption (interp, optionName, "seekcfg seekstate");
2396 #endif
2397   }
2398 }
2399 
2400 #ifdef USE_TCL_STUBS
2401 /*
2402  *------------------------------------------------------*
2403  *
2404  *	TrfNotify --
2405  *
2406  *	------------------------------------------------*
2407  *	Called by the generic layer of 8.3.2 and higher
2408  *	to handle events coming from below. We simply pass
2409  *	them upward.
2410  *	------------------------------------------------*
2411  *
2412  *	Sideeffects:
2413  *		None.
2414  *
2415  *	Result:
2416  *		The unchanged interest mask.
2417  *
2418  *------------------------------------------------------*
2419  */
2420 static int
TrfNotify(instanceData,interestMask)2421 TrfNotify (instanceData, interestMask)
2422      ClientData instanceData;
2423      int        interestMask;
2424 {
2425   /*
2426    * An event occured in the underlying channel.  This transformation
2427    * doesn't process such events thus returns the incoming mask
2428    * unchanged.
2429    *
2430    * We do delete an existing timer. It was not fired, yet we are
2431    * here, so the channel below generated such an event and we don't
2432    * have to. The renewal of the interest after the execution of
2433    * channel handlers will eventually cause us to recreate the timer
2434    * (in TrfWatch).
2435    */
2436 
2437   TimerKill ((TrfTransformationInstance*) instanceData);
2438   return interestMask;
2439 }
2440 #endif
2441 
2442 /*
2443  *------------------------------------------------------*
2444  *
2445  *	TransformImmediate --
2446  *
2447  *	------------------------------------------------*
2448  *	Read from source, apply the specified transformation
2449  *	and write the result to destination.
2450  *	------------------------------------------------*
2451  *
2452  *	Sideeffects:
2453  *		The access points of source and destination
2454  *		change, data is added to destination too.
2455  *
2456  *	Result:
2457  *		A standard Tcl error code.
2458  *
2459  *------------------------------------------------------* */
2460 
2461 static int
TransformImmediate(interp,entry,source,destination,in,optInfo)2462 TransformImmediate (interp, entry, source, destination, in, optInfo)
2463 Tcl_Interp*        interp;
2464 Trf_RegistryEntry* entry;
2465 Tcl_Channel        source;
2466 Tcl_Channel        destination;
2467 struct Tcl_Obj* CONST in;
2468 Trf_Options        optInfo;
2469 {
2470   Trf_Vectors*     v;
2471   Trf_ControlBlock control;
2472   int              res = TCL_OK;
2473 
2474   ResultBuffer r;
2475 
2476   START (TransformImmediate);
2477 
2478   if (ENCODE_REQUEST (entry, optInfo)) {
2479     v = &(entry->trfType->encoder);
2480   } else {
2481     v = &(entry->trfType->decoder);
2482   }
2483 
2484   /* Take care of output (channel vs. interpreter result area).
2485    */
2486 
2487   if (destination == (Tcl_Channel) NULL) {
2488     ResultInit (&r);
2489 
2490     PRINT ("___.createproc\n"); FL;
2491     control = v->createProc ((ClientData) &r, PutInterpResult,
2492 			     optInfo, interp,
2493 			     entry->trfType->clientData);
2494   } else {
2495     PRINT ("___.createproc\n"); FL;
2496     control = v->createProc ((ClientData) destination, PutDestinationImm,
2497 			     optInfo, interp,
2498 			     entry->trfType->clientData);
2499   }
2500 
2501   if (control == (Trf_ControlBlock) NULL) {
2502     DONE (TransformImmediate);
2503     return TCL_ERROR;
2504   }
2505 
2506 
2507   /* Now differentiate between immediate value and channel as input.
2508    */
2509 
2510   if (source == (Tcl_Channel) NULL) {
2511     /* Immediate value.
2512      * -- VERSION DEPENDENT CODE --
2513      */
2514     int            length;
2515     unsigned char* buf;
2516 
2517     buf = GET_DATA (in, &length);
2518     if (v->convertBufProc) {
2519       /* play it safe, use a copy, avoid clobbering the input. */
2520       unsigned char* tmp;
2521 
2522       tmp = (unsigned char*) ckalloc (length);
2523       memcpy (tmp, buf, length);
2524 
2525       PRINT ("___.convertbufproc\n"); FL;
2526 
2527       res = v->convertBufProc (control, tmp, length, interp,
2528 			       entry->trfType->clientData);
2529       ckfree ((char*) tmp);
2530     } else {
2531       unsigned int i, c;
2532 
2533       PRINT ("___.convertproc\n"); FL;
2534 
2535       for (i=0; i < ((unsigned int) length); i++) {
2536 	c = buf [i];
2537 	res = v->convertProc (control, c, interp,
2538 			      entry->trfType->clientData);
2539 
2540 	if (res != TCL_OK)
2541 	  break;
2542       }
2543     }
2544 
2545     if (res == TCL_OK) {
2546       PRINT ("___.flushproc\n"); FL;
2547 
2548       res = v->flushProc (control, interp, entry->trfType->clientData);
2549     }
2550   } else {
2551     /* Read from channel.
2552      */
2553 
2554     unsigned char* buf;
2555     int            actuallyRead;
2556 
2557     buf = (unsigned char*) ckalloc (READ_CHUNK_SIZE);
2558 
2559     while (1) {
2560       if (Tcl_Eof (source))
2561 	break;
2562 
2563       actuallyRead = Tcl_Read (source, (char*) buf, READ_CHUNK_SIZE);
2564 
2565       if (actuallyRead <= 0)
2566 	break;
2567 
2568       if (v->convertBufProc) {
2569 	PRINT ("___.convertbufproc\n"); FL;
2570 
2571 	res = v->convertBufProc (control, buf, actuallyRead, interp,
2572 				 entry->trfType->clientData);
2573       } else {
2574 	unsigned int i, c;
2575 
2576 	PRINT ("___.convertproc\n"); FL;
2577 
2578 	for (i=0; i < ((unsigned int) actuallyRead); i++) {
2579 	  c = buf [i];
2580 	  res = v->convertProc (control, c, interp,
2581 				entry->trfType->clientData);
2582 
2583 	  if (res != TCL_OK)
2584 	    break;
2585 	}
2586       }
2587 
2588       if (res != TCL_OK)
2589 	break;
2590     }
2591 
2592     ckfree ((char*) buf);
2593 
2594     if (res == TCL_OK)
2595       res = v->flushProc (control, interp, entry->trfType->clientData);
2596   }
2597 
2598   PRINT ("___.deleteproc\n"); FL;
2599   v->deleteProc (control, entry->trfType->clientData);
2600 
2601 
2602   if (destination == (Tcl_Channel) NULL) {
2603     /* Now write into interpreter result area.
2604      */
2605 
2606     if (res == TCL_OK) {
2607       Tcl_ResetResult (interp);
2608 
2609       if (r.buf != NULL) {
2610 	Tcl_Obj* o = NEW_DATA (r);
2611 	Tcl_IncrRefCount (o);
2612 	Tcl_SetObjResult (interp, o);
2613 	Tcl_DecrRefCount (o);
2614       }
2615     }
2616     ResultClear (&r);
2617   }
2618 
2619   DONE (TransformImmediate);
2620   return res;
2621 }
2622 
2623 /*
2624  *------------------------------------------------------*
2625  *
2626  *	AttachTransform --
2627  *
2628  *	------------------------------------------------*
2629  *	Create an instance of a transformation and
2630  *	associate as filter it with the specified channel.
2631  *	------------------------------------------------*
2632  *
2633  *	Sideeffects:
2634  *		Allocates memory, changes the internal
2635  *		state of the channel.
2636  *
2637  *	Result:
2638  *		A standard Tcl error code.
2639  *
2640  *------------------------------------------------------*
2641  */
2642 
2643 static int
AttachTransform(entry,baseOpt,optInfo,interp)2644 AttachTransform (entry, baseOpt, optInfo, interp)
2645 Trf_RegistryEntry* entry;
2646 Trf_BaseOptions*   baseOpt;
2647 Trf_Options        optInfo;
2648 Tcl_Interp*        interp;
2649 {
2650   TrfTransformationInstance* trans;
2651 
2652   trans = NEW_TRANSFORM;
2653 
2654   START (AttachTransform);
2655 
2656 #ifdef TRF_STREAM_DEBUG
2657   trans->inCounter  = 0;
2658   trans->outCounter = 0;
2659   trans->name       = (char*) entry->trfType->name;
2660 #endif
2661 #ifdef USE_TCL_STUBS
2662   trans->patchVariant = entry->registry->patchVariant;
2663 #endif
2664 
2665   /* trans->standard.typePtr = entry->transType; */
2666   trans->clientData       = entry->trfType->clientData;
2667 
2668   if (trans->patchVariant == PATCH_832) {
2669     trans->parent = Tcl_GetTopChannel (baseOpt->attach);
2670   } else {
2671     trans->parent = baseOpt->attach;
2672   }
2673 
2674   trans->readIsFlushed    = 0;
2675 
2676   /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
2677    */
2678   trans->flags            = 0;
2679   trans->watchMask        = 0;
2680 
2681   /* 03/28/2000 Added by DNew@Invisible.Net because Purify says so. */
2682   trans->lastStored       = 0;
2683 
2684   trans->mode             = Tcl_GetChannelMode (baseOpt->attach);
2685   trans->timer            = (Tcl_TimerToken) NULL;
2686 
2687   if (ENCODE_REQUEST (entry, optInfo)) {
2688     /* ENCODE on write
2689      * DECODE on read
2690      */
2691 
2692     trans->out.vectors = ((trans->mode & TCL_WRITABLE) ?
2693 			  &entry->trfType->encoder     :
2694 			  NULL);
2695     trans->in.vectors  = ((trans->mode & TCL_READABLE) ?
2696 			  &entry->trfType->decoder     :
2697 			  NULL);
2698 
2699   } else /* mode == DECODE */ {
2700     /* DECODE on write
2701      * ENCODE on read
2702      */
2703 
2704     trans->out.vectors = ((trans->mode & TCL_WRITABLE) ?
2705 			  &entry->trfType->decoder     :
2706 			  NULL);
2707     trans->in.vectors  = ((trans->mode & TCL_READABLE) ?
2708 			  &entry->trfType->encoder     :
2709 			  NULL);
2710   }
2711 
2712   /* 'PutDestination' is ok for write, only read
2713    * requires 'PutTrans' and its internal buffer.
2714    */
2715 
2716   if (trans->mode & TCL_WRITABLE) {
2717     PRINT ("out.createproc\n"); FL;
2718 
2719     trans->out.control = trans->out.vectors->createProc ((ClientData) trans,
2720 							 PutDestination,
2721 							 optInfo, interp,
2722 							 trans->clientData);
2723 
2724     if (trans->out.control == (Trf_ControlBlock) NULL) {
2725       ckfree ((char*) trans);
2726       DONE (AttachTransform);
2727       return TCL_ERROR;
2728     }
2729   }
2730 
2731   if (trans->mode & TCL_READABLE) {
2732     PRINT ("in_.createproc\n"); FL;
2733 
2734     trans->in.control  = trans->in.vectors->createProc  ((ClientData) trans,
2735 							 PutTrans,
2736 							 optInfo, interp,
2737 							 trans->clientData);
2738 
2739     if (trans->in.control == (Trf_ControlBlock) NULL) {
2740       ckfree ((char*) trans);
2741       DONE (AttachTransform);
2742       return TCL_ERROR;
2743     }
2744   }
2745 
2746   ResultInit (&trans->result);
2747   trans->result.seekState = &trans->seekState;
2748 
2749   /*
2750    * Build channel from converter definition and stack it upon the one we
2751    * shall attach to.
2752    */
2753 
2754   /* Discard information dangerous for the integrated patch.
2755    * (This makes sure that we don't miss any place using this pointer
2756    * without generating a crash (instead of some silent failure, like
2757    * thrashing far away memory)).
2758    */
2759 
2760 #ifndef USE_TCL_STUBS
2761   trans->self   = Tcl_StackChannel (interp, entry->transType,
2762 				    (ClientData) trans, trans->mode,
2763 				    trans->parent);
2764 #else
2765   if ((trans->patchVariant == PATCH_ORIG) ||
2766       (trans->patchVariant == PATCH_832)) {
2767 
2768     trans->self = Tcl_StackChannel (interp, entry->transType,
2769 				    (ClientData) trans, trans->mode,
2770 				    trans->parent);
2771 
2772   } else if (trans->patchVariant == PATCH_82) {
2773     trans->parent = NULL;
2774     trans->self   = baseOpt->attach;
2775 
2776     Tcl_StackChannel (interp, entry->transType,
2777 		      (ClientData) trans, trans->mode,
2778 		      trans->self);
2779   } else {
2780     Tcl_Panic ("Illegal value for 'patchVariant'");
2781   }
2782 #endif
2783 
2784   if (trans->self == (Tcl_Channel) NULL) {
2785     ckfree ((char*) trans);
2786     Tcl_AppendResult (interp, "internal error in Tcl_StackChannel",
2787 		      (char*) NULL);
2788     DONE (AttachTransform);
2789     return TCL_ERROR;
2790   }
2791 
2792   /* Initialize the seek subsystem.
2793    */
2794 
2795   PRINTLN ("Initialize Seeking");
2796   PRINTLN ("Copy configuration");
2797 
2798   trans->seekCfg.natural.numBytesTransform =
2799     entry->trfType->naturalSeek.numBytesTransform;
2800 
2801   trans->seekCfg.natural.numBytesDown      =
2802     entry->trfType->naturalSeek.numBytesDown;
2803 
2804   if (optInfo && (*OPT->seekQueryProc != (Trf_SeekQueryOptions*) NULL)) {
2805     PRINTLN ("Query seekQueryProc");
2806     (*OPT->seekQueryProc) (interp, optInfo, &trans->seekCfg.natural, CLT);
2807   }
2808 
2809   PRINTLN ("Determine Policy");
2810   SeekCalculatePolicies (trans);
2811 
2812   PRINTLN ("    Initialize");
2813   SeekInitialize        (trans);
2814 
2815   /* Check for options overiding the policy. If they do despite being not
2816    * allowed to do so we have to remove the transformation and break it down.
2817    * We do this by calling 'Unstack', which does all the necessary things for
2818    * us.
2819    */
2820 
2821   PRINTLN ("    Policy options ?");
2822   if (baseOpt->policy != (Tcl_Obj*) NULL) {
2823     if (TCL_OK != TrfSetOption ((ClientData) trans, interp, "-seekpolicy",
2824 				Tcl_GetStringFromObj (baseOpt->policy,
2825 						      NULL))) {
2826 
2827       /* An error prevented setting a policy. Save the resulting error
2828        * message across the necessary unstacking of the now faulty
2829        * transformation.
2830        */
2831 
2832 #if GT81
2833       Tcl_SavedResult ciSave;
2834 
2835       Tcl_SaveResult     (interp, &ciSave);
2836       Tcl_UnstackChannel (interp, trans->self);
2837       Tcl_RestoreResult  (interp, &ciSave);
2838 #else
2839       Tcl_UnstackChannel (interp, trans->self);
2840 #endif
2841       DONE (AttachTransform);
2842       return TCL_ERROR;
2843     }
2844   }
2845 
2846   /*  Tcl_RegisterChannel (interp, new); */
2847   Tcl_AppendResult (interp, Tcl_GetChannelName (trans->self),
2848 		    (char*) NULL);
2849   DONE (AttachTransform);
2850   return TCL_OK;
2851 }
2852 
2853 /*
2854  *------------------------------------------------------*
2855  *
2856  *	PutDestination --
2857  *
2858  *	------------------------------------------------*
2859  *	Handler used by a transformation to write its results.
2860  *	------------------------------------------------*
2861  *
2862  *	Sideeffects:
2863  *		Writes to the channel.
2864  *
2865  *	Result:
2866  *		A standard Tcl error code.
2867  *
2868  *------------------------------------------------------*
2869  */
2870 
2871 static int
PutDestination(clientData,outString,outLen,interp)2872 PutDestination (clientData, outString, outLen, interp)
2873 ClientData     clientData;
2874 unsigned char* outString;
2875 int            outLen;
2876 Tcl_Interp*    interp;
2877 {
2878   TrfTransformationInstance* trans = (TrfTransformationInstance*) clientData;
2879   int         res;
2880   Tcl_Channel parent;
2881 
2882   START  (PutDestination);
2883   /*PRTSTR ("Data = {%d, \"%s\"}\n", outLen, outString);*/
2884   PRINT ("Data = %d {\n", outLen);
2885   DUMP  (outLen, outString);
2886   PRINT ("}\n");
2887 
2888   parent = DOWNC (trans);
2889 
2890   trans->lastWritten += outLen;
2891 
2892   res = WRITE (trans, (char*) outString, outLen);
2893 
2894   if (res < 0) {
2895     if (interp) {
2896       Tcl_AppendResult (interp, "error writing \"",
2897 			Tcl_GetChannelName (parent),
2898 			"\": ", Tcl_PosixError (interp),
2899 			(char*) NULL);
2900     }
2901     PRINT ("ERROR /written = %d, errno = %d, (%d) %s\n",
2902 	   res, Tcl_GetErrno (), EACCES, strerror (Tcl_GetErrno ()));
2903     DONE (PutDestination);
2904     return TCL_ERROR;
2905   }
2906 
2907   DONE (PutDestination);
2908   return TCL_OK;
2909 }
2910 
2911 /*
2912  *------------------------------------------------------*
2913  *
2914  *	PutDestinationImm --
2915  *
2916  *	------------------------------------------------*
2917  *	Handler used during an immediate transformation
2918  *	to write its results into the -out channel.
2919  *	------------------------------------------------*
2920  *
2921  *	Sideeffects:
2922  *		Writes to the channel.
2923  *
2924  *	Result:
2925  *		A standard Tcl error code.
2926  *
2927  *------------------------------------------------------*
2928  */
2929 
2930 static int
PutDestinationImm(clientData,outString,outLen,interp)2931 PutDestinationImm (clientData, outString, outLen, interp)
2932 ClientData     clientData;
2933 unsigned char* outString;
2934 int            outLen;
2935 Tcl_Interp*    interp;
2936 {
2937   int         res;
2938   Tcl_Channel destination = (Tcl_Channel) clientData;
2939 
2940   START  (PutDestinationImm);
2941   /*PRTSTR ("Data = {%d, \"%s\"}\n", outLen, outString);*/
2942   PRINT ("Data = %d {\n", outLen);
2943   DUMP  (outLen, outString);
2944   PRINT ("}\n");
2945 
2946   res = Tcl_Write (destination, (char*) outString, outLen);
2947 
2948   if (res < 0) {
2949     if (interp) {
2950       Tcl_AppendResult (interp, "error writing \"",
2951 			Tcl_GetChannelName (destination),
2952 			"\": ", Tcl_PosixError (interp),
2953 			(char*) NULL);
2954     }
2955     DONE (PutDestinationImm);
2956     return TCL_ERROR;
2957   }
2958 
2959   DONE (PutDestinationImm);
2960   return TCL_OK;
2961 }
2962 
2963 /*
2964  *------------------------------------------------------*
2965  *
2966  *	PutTrans --
2967  *
2968  *	------------------------------------------------*
2969  *	Handler used by a transformation to write its
2970  *	results (to be read later). Used by transformations
2971  *	acting as filter.
2972  *	------------------------------------------------*
2973  *
2974  *	Sideeffects:
2975  *		May allocate memory.
2976  *
2977  *	Result:
2978  *		A standard Tcl error code.
2979  *
2980  *------------------------------------------------------*
2981  */
2982 
2983 static int
PutTrans(clientData,outString,outLen,interp)2984 PutTrans (clientData, outString, outLen, interp)
2985 ClientData     clientData;
2986 unsigned char* outString;
2987 int            outLen;
2988 Tcl_Interp*    interp;
2989 {
2990   TrfTransformationInstance* trans = (TrfTransformationInstance*) clientData;
2991 
2992   START  (PutTrans);
2993   /*PRTSTR ("Data = {%d, \"%s\"}\n", outLen, outString);*/
2994   PRINT ("Data = %d {\n", outLen);
2995   DUMP  (outLen, outString);
2996   PRINT ("}\n");
2997   STREAM_OUT (trans, outLen, outString);
2998 
2999   trans->lastStored += outLen;
3000 
3001   ResultAdd (&trans->result, outString, outLen);
3002 
3003   DONE (PutTrans);
3004   return TCL_OK;
3005 }
3006 
3007 /*
3008  *------------------------------------------------------*
3009  *
3010  *	PutInterpResult --
3011  *
3012  *	------------------------------------------------*
3013  *	Handler used by a transformation to write its
3014  *	results into the interpreter result area.
3015  *	------------------------------------------------*
3016  *
3017  *	Sideeffects:
3018  *		changes the contents of the interpreter
3019  *		result area.
3020  *
3021  *	Result:
3022  *		A standard Tcl error code.
3023  *
3024  *------------------------------------------------------*
3025  */
3026 
3027 static int
PutInterpResult(clientData,outString,outLen,interp)3028 PutInterpResult (clientData, outString, outLen, interp)
3029 ClientData     clientData;
3030 unsigned char* outString;
3031 int            outLen;
3032 Tcl_Interp*    interp;
3033 {
3034   ResultBuffer* r = (ResultBuffer*) clientData;
3035 
3036   START  (PutInterpResult);
3037   /*PRTSTR ("Data = {%d, \"%s\"}\n", outLen, outString);*/
3038   PRINT ("Data = %d {\n", outLen);
3039   DUMP  (outLen, outString);
3040   PRINT ("}\n");
3041 
3042   ResultAdd (r, outString, outLen);
3043 
3044   DONE (PutInterpResult);
3045   return TCL_OK;
3046 }
3047 
3048 /* 04/13/1999 Fileevent patch from Matt Newman <matt@novadigm.com>
3049  */
3050 /*
3051  *------------------------------------------------------*
3052  *
3053  *	ChannelHandler --
3054  *
3055  *	------------------------------------------------*
3056  *	Handler called by Tcl as a result of
3057  *	Tcl_CreateChannelHandler - to inform us of activity
3058  *	on the underlying channel.
3059  *	------------------------------------------------*
3060  *
3061  *	Sideeffects:
3062  *		May generate subsequent calls to
3063  *		Tcl_NotifyChannel.
3064  *
3065  *	Result:
3066  *		None.
3067  *
3068  *------------------------------------------------------*
3069  */
3070 
3071 static void
ChannelHandler(clientData,mask)3072 ChannelHandler (clientData, mask)
3073 ClientData     clientData;
3074 int            mask;
3075 {
3076   /*
3077    * An event occured in the underlying channel. Forward it to
3078    * ourself. This will either execute an attached event script
3079    * (fileevent) or an intermediate handler like this one propagating
3080    * the event further upward.
3081    *
3082    * This procedure is called only for the original and the 8.2
3083    * patch. The 8.2.3 patch uses a new vector in the driver to get and
3084    * handle events coming from below.
3085    */
3086 
3087   TrfTransformationInstance* trans = (TrfTransformationInstance*) clientData;
3088 
3089 #ifndef USE_TCL_STUBS
3090   /*
3091    * Core 8.0.x. Forward the event to ourselves.
3092    */
3093 
3094   Tcl_NotifyChannel (trans->self, mask);
3095 #else
3096   /*
3097    * Check for the correct variants first. Forwarding the event is not
3098    * required for the 8.2 patch. For that variant the core,
3099    * i.e. Tcl_NotifyChannel loops over all channels in the stack by
3100    * itself.
3101    */
3102 
3103   if (trans->patchVariant == PATCH_832) {
3104     Tcl_Panic ("Illegal value for 'patchVariant' in ChannelHandler");
3105   }
3106   if (trans->patchVariant == PATCH_ORIG) {
3107     Tcl_NotifyChannel (trans->self, mask);
3108   }
3109 #endif
3110 
3111   /*
3112    * Check the I/O-Buffers of this channel for waiting information.
3113    * Setup a timer generating an artificial event for us if we have
3114    * such. We could call Tcl_NotifyChannel directly, but this would
3115    * starve other event sources, so a timer is used to prevent that.
3116    */
3117 
3118   TimerKill (trans);
3119 
3120   /* Check for waiting data, flush it out with a timer.
3121    */
3122 
3123 #ifndef USE_TCL_STUBS
3124   if ((mask & TCL_READABLE) && ((ResultLength (&trans->result) > 0) ||
3125 				(Tcl_InputBuffered (trans->self) > 0))) {
3126     TimerSetup (trans);
3127   }
3128 #else
3129   if (trans->patchVariant != PATCH_ORIG) {
3130     if ((mask & TCL_READABLE) && (ResultLength (&trans->result) > 0)) {
3131       TimerSetup (trans);
3132     }
3133   } else {
3134     if ((mask & TCL_READABLE) && ((ResultLength (&trans->result) > 0) ||
3135 				  (Tcl_InputBuffered (trans->self) > 0))) {
3136       TimerSetup (trans);
3137     }
3138   }
3139 #endif
3140 }
3141 
3142 /*
3143  *------------------------------------------------------*
3144  *
3145  *	ChannelHandlerTimer --
3146  *
3147  *	------------------------------------------------*
3148  *	Called by the notifier (-> timer) to flush out
3149  *	information waiting in channel buffers.
3150  *	------------------------------------------------*
3151  *
3152  *	Sideeffects:
3153  *		As of 'ChannelHandler'.
3154  *
3155  *	Result:
3156  *		None.
3157  *
3158  *------------------------------------------------------*
3159  */
3160 
3161 static void
ChannelHandlerTimer(clientData)3162 ChannelHandlerTimer (clientData)
3163 ClientData clientData; /* Transformation to query */
3164 {
3165   TrfTransformationInstance* trans = (TrfTransformationInstance*) clientData;
3166 
3167   trans->timer = (Tcl_TimerToken) NULL;
3168 
3169 #ifndef USE_TCL_STUBS
3170   /* 8.0.x.
3171    * Use the channel handler itself to do the necessary actions
3172    */
3173 
3174   ChannelHandler (clientData, trans->watchMask);
3175 #else
3176   if ((trans->patchVariant == PATCH_82) ||
3177       (trans->patchVariant == PATCH_832)) {
3178     /*
3179      * Use the standard notification mechanism to invoke all channel
3180      * handlers.
3181      */
3182     Tcl_NotifyChannel (trans->self, TCL_READABLE);
3183   } else {
3184     /* PATCH_ORIG, seee 8.0.x
3185      */
3186 
3187     ChannelHandler (clientData, trans->watchMask);
3188   }
3189 #endif
3190 }
3191 
3192 #ifdef USE_TCL_STUBS
3193 /*
3194  *------------------------------------------------------*
3195  *
3196  *	DownSOpt --
3197  *
3198  *	Helper procedure. Writes an option to the downstream channel.
3199  *
3200  *	Sideeffects:
3201  *		As of Tcl_SetChannelOption
3202  *
3203  *	Result:
3204  *		A standard tcl error code.
3205  *
3206  *------------------------------------------------------*
3207  */
3208 
3209 static int
DownSOpt(interp,ctrl,optionName,value)3210 DownSOpt (interp, ctrl, optionName, value)
3211      Tcl_Interp*                interp;
3212      TrfTransformationInstance* ctrl;
3213      CONST char*                optionName;
3214      CONST char*                value;
3215 {
3216   Tcl_Channel parent = DOWNC (ctrl);
3217 
3218   if (ctrl->patchVariant == PATCH_832) {
3219     /*
3220      * The newly written patch forces direct use of the driver.
3221      */
3222 
3223     Tcl_DriverSetOptionProc *setOptionProc =
3224       Tcl_ChannelSetOptionProc (Tcl_GetChannelType (parent));
3225 
3226     if (setOptionProc != NULL) {
3227       return (*setOptionProc) (Tcl_GetChannelInstanceData (parent),
3228 			       interp, optionName, value);
3229     } else {
3230       return TCL_ERROR;
3231     }
3232 
3233   } else {
3234     return Tcl_SetChannelOption (interp, parent, optionName, value);
3235   }
3236 }
3237 
3238 /*
3239  *------------------------------------------------------*
3240  *
3241  *	DownGOpt --
3242  *
3243  *	Helper procedure. Reads options from the downstream channel.
3244  *
3245  *	Sideeffects:
3246  *		As of Tcl_GetChannelOption
3247  *
3248  *	Result:
3249  *		A standard tcl error code.
3250  *
3251  *------------------------------------------------------*
3252  */
3253 
3254 static int
DownGOpt(interp,ctrl,optionName,dsPtr)3255 DownGOpt (interp, ctrl, optionName, dsPtr)
3256      Tcl_Interp*                interp;
3257      TrfTransformationInstance* ctrl;
3258      CONST84 char*              optionName;
3259      Tcl_DString*               dsPtr;
3260 {
3261   Tcl_Channel parent = DOWNC (ctrl);
3262 
3263   if (ctrl->patchVariant == PATCH_832) {
3264     /*
3265      * The newly written patch forces direct use of the driver.
3266      */
3267 
3268     Tcl_DriverGetOptionProc *getOptionProc =
3269       Tcl_ChannelGetOptionProc (Tcl_GetChannelType (parent));
3270 
3271     if (getOptionProc != NULL) {
3272 	return (*getOptionProc) (Tcl_GetChannelInstanceData (parent),
3273 				 interp, optionName, dsPtr);
3274     }
3275 
3276     /*
3277      * Downstream channel has no driver to get options. Fall back on
3278      * some default behaviour. A query for all options is ok. A
3279      * request for a specific unknown option OTOH has to fail.
3280      */
3281 
3282     if (optionName == (char*) NULL) {
3283       return TCL_OK;
3284     } else {
3285       return TCL_ERROR;
3286     }
3287   } else {
3288     return Tcl_GetChannelOption (interp, parent, optionName, dsPtr);
3289   }
3290 }
3291 
3292 /*
3293  *------------------------------------------------------*
3294  *
3295  *	DownWrite --
3296  *
3297  *	Helper procedure. Writes to the downstream channel.
3298  *
3299  *	Sideeffects:
3300  *		As of TclWrite / Tcl_WriteRaw
3301  *
3302  *	Result:
3303  *		The number of bytes written.
3304  *
3305  *------------------------------------------------------*
3306  */
3307 
3308 static int
DownWrite(ctrl,buf,toWrite)3309 DownWrite (ctrl, buf, toWrite)
3310      TrfTransformationInstance* ctrl;
3311      char*                      buf;
3312      int                        toWrite;
3313 {
3314   Tcl_Channel parent = DOWNC (ctrl);
3315 
3316   if (ctrl->patchVariant == PATCH_832) {
3317     /*
3318      * The newly written patch forces use of the new raw-API.
3319      */
3320 
3321     PRINT ("WriteRaw %p %s\n", parent, Tcl_GetChannelType (parent)->typeName);
3322     return Tcl_WriteRaw (parent, buf, toWrite);
3323   } else {
3324     return Tcl_Write (parent, buf, toWrite);
3325   }
3326   return TCL_OK;
3327 }
3328 
3329 /*
3330  *------------------------------------------------------*
3331  *
3332  *	DownRead --
3333  *
3334  *	Helper procedure. Reads from the downstream channel.
3335  *
3336  *	Sideeffects:
3337  *		As of TclRead / Tcl_ReadRaw
3338  *
3339  *	Result:
3340  *		The number of bytes read.
3341  *
3342  *------------------------------------------------------*
3343  */
3344 
3345 static int
DownRead(ctrl,buf,toRead)3346 DownRead (ctrl, buf, toRead)
3347      TrfTransformationInstance* ctrl;
3348      char*                      buf;
3349      int                        toRead;
3350 {
3351   Tcl_Channel parent = DOWNC (ctrl);
3352 
3353   if (ctrl->patchVariant == PATCH_832) {
3354     /*
3355      * The newly written patch forces use of the new raw-API.
3356      */
3357 
3358     return Tcl_ReadRaw (parent, buf, toRead);
3359   } else {
3360     return Tcl_Read (parent, buf, toRead);
3361   }
3362   return TCL_OK;
3363 }
3364 
3365 /*
3366  *------------------------------------------------------*
3367  *
3368  *	DownSeek --
3369  *
3370  *	Helper procedure. Asks the downstream channel
3371  *	to seek, or for its current location.
3372  *
3373  *	Sideeffects:
3374  *		None.
3375  *
3376  *	Result:
3377  *		The location in the downstream channel
3378  *
3379  *------------------------------------------------------*
3380  */
3381 
3382 static int
DownSeek(ctrl,offset,mode)3383 DownSeek (ctrl, offset, mode)
3384     TrfTransformationInstance* ctrl;
3385     int                        offset;
3386     int                        mode;
3387 {
3388   Tcl_Channel parent = DOWNC (ctrl);
3389 
3390   if (ctrl->patchVariant == PATCH_832) {
3391     /*
3392      * The newly rewritten patch forces the transformation into
3393      * directly using the seek-proc of the downstream driver. Tcl_Seek
3394      * would compensate for the stack and cause and infinite recursion
3395      * blowing the stack.
3396      */
3397 #if TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 4
3398     const
3399 #endif
3400     Tcl_ChannelType*    parentType     = Tcl_GetChannelType  (parent);
3401     Tcl_DriverSeekProc* parentSeekProc = Tcl_ChannelSeekProc (parentType);
3402     int                 errorCode;
3403 
3404     if (parentSeekProc == (Tcl_DriverSeekProc*) NULL) {
3405       return -1;
3406     }
3407 
3408     return (*parentSeekProc) (Tcl_GetChannelInstanceData (parent),
3409 			      offset, mode, &errorCode);
3410   }
3411 
3412   /*
3413    * (ctrl->patchVariant == PATCH_ORIG)
3414    * (ctrl->patchVariant == PATCH_82)
3415    *
3416    * Both the original patch for stacked channels and rewritten
3417    * implementation for 8.2. have the same simple semantics for
3418    * getting at the location of the downstream channel.
3419    *
3420    * Just use the standard 'Tcl_Seek'.
3421    */
3422 
3423     return (int) Tcl_Seek (parent, offset, mode);
3424 }
3425 
3426 /*
3427  *------------------------------------------------------*
3428  *
3429  *	DownChannel --
3430  *
3431  *	Helper procedure. Finds the downstream channel.
3432  *
3433  *	Sideeffects:
3434  *		May modify 'self'.
3435  *
3436  *	Result:
3437  *		None.
3438  *
3439  *------------------------------------------------------*
3440  */
3441 
3442 static Tcl_Channel
DownChannel(ctrl)3443 DownChannel (ctrl)
3444     TrfTransformationInstance* ctrl;
3445 {
3446   Tcl_Channel self;
3447   Tcl_Channel next;
3448 
3449   if ((ctrl->patchVariant == PATCH_ORIG) ||
3450       (ctrl->patchVariant == PATCH_832)) {
3451     /*
3452      * Both the original patch for stacked channels and rewritten
3453      * implementation for 8.3.2. have simple semantics for getting at
3454      * the parent of a channel.
3455      */
3456 
3457     return ctrl->parent;
3458   }
3459 
3460   /*
3461    * The first rewrite of the stacked channel patch initially included
3462    * in 8.2. requires that a transformation searches it's channel in
3463    * the whole stack. Only for the versions of the core using this
3464    * implementation, 8.2 till 8.3.1, the comments below apply.
3465    */
3466 
3467   /* The reason for the existence of this procedure is
3468    * the fact that stacking a transform over another
3469    * transform will leave our internal pointer unchanged,
3470    * and thus pointing to the new transform, and not the
3471    * Channel structure containing the saved state of this
3472    * transform. This is the price to pay for leaving
3473    * Tcl_Channel references intact. The only other solution
3474    * is an extension of Tcl_ChannelType with another driver
3475    * procedure to notify a Channel about the (un)stacking.
3476    *
3477    * It walks the chain of Channel structures until it
3478    * finds the one pointing having 'ctrl' as instanceData
3479    * and then returns the superceding channel to that.
3480    */
3481 
3482   self = ctrl->self;
3483 
3484   while ((ClientData) ctrl != Tcl_GetChannelInstanceData (self)) {
3485     next = Tcl_GetStackedChannel (self);
3486     if (next == (Tcl_Channel) NULL) {
3487       /* 09/24/1999 Unstacking bug, found by Matt Newman <matt@sensus.org>.
3488        *
3489        * We were unable to find the channel structure for this
3490        * transformation in the chain of stacked channel. This
3491        * means that we are currently in the process of unstacking
3492        * it *and* there were some bytes waiting which are now
3493        * flushed. In this situation the pointer to the channel
3494        * itself already refers to the parent channel we have to
3495        * write the bytes into, so we return that.
3496        */
3497       return ctrl->self;
3498     }
3499     self = next;
3500   }
3501 
3502   return Tcl_GetStackedChannel (self);
3503 }
3504 #endif
3505 
3506 /*
3507  *------------------------------------------------------*
3508  *
3509  *	ResultClear --
3510  *
3511  *	Deallocates any memory allocated by 'ResultAdd'.
3512  *
3513  *	Sideeffects:
3514  *		See above.
3515  *
3516  *	Result:
3517  *		None.
3518  *
3519  *------------------------------------------------------*
3520  */
3521 
3522 static void
ResultClear(r)3523 ResultClear (r)
3524      ResultBuffer* r; /* Reference to the buffer to clear out */
3525 {
3526   r->used = 0;
3527 
3528   if (r->allocated) {
3529     ckfree ((char*) r->buf);
3530     r->buf       = (unsigned char*) NULL;
3531     r->allocated = 0;
3532   }
3533 
3534   if (r->seekState != (SeekState*) NULL) {
3535     r->seekState->upBufStartLoc  = r->seekState->upLoc;
3536     r->seekState->upBufEndLoc    = r->seekState->upLoc;
3537   }
3538 }
3539 
3540 /*
3541  *------------------------------------------------------*
3542  *
3543  *	ResultInit --
3544  *
3545  *	Initializes the specified buffer structure. The
3546  *	structure will contain valid information for an
3547  *	emtpy buffer.
3548  *
3549  *	Sideeffects:
3550  *		See above.
3551  *
3552  *	Result:
3553  *		None.
3554  *
3555  *------------------------------------------------------*
3556  */
3557 
3558 static void
ResultInit(r)3559 ResultInit (r)
3560     ResultBuffer* r; /* Reference to the structure to initialize */
3561 {
3562     r->used      = 0;
3563     r->allocated = 0;
3564     r->buf       = (unsigned char*) NULL;
3565     r->seekState = (SeekState*) NULL;
3566 }
3567 
3568 /*
3569  *------------------------------------------------------*
3570  *
3571  *	ResultLength --
3572  *
3573  *	Returns the number of bytes stored in the buffer.
3574  *
3575  *	Sideeffects:
3576  *		None.
3577  *
3578  *	Result:
3579  *		An integer, see above too.
3580  *
3581  *------------------------------------------------------*
3582  */
3583 
3584 static int
ResultLength(r)3585 ResultLength (r)
3586     ResultBuffer* r; /* The structure to query */
3587 {
3588     return r->used;
3589 }
3590 
3591 /*
3592  *------------------------------------------------------*
3593  *
3594  *	ResultCopy --
3595  *
3596  *	Copies the requested number of bytes from the
3597  *	buffer into the specified array and removes them
3598  *	from the buffer afterward. Copies less if there
3599  *	is not enough data in the buffer.
3600  *
3601  *	Sideeffects:
3602  *		See above.
3603  *
3604  *	Result:
3605  *		The number of actually copied bytes,
3606  *		possibly less than 'toRead'.
3607  *
3608  *------------------------------------------------------*
3609  */
3610 
3611 static int
ResultCopy(r,buf,toRead)3612 ResultCopy (r, buf, toRead)
3613      ResultBuffer*  r;      /* The buffer to read from */
3614      unsigned char* buf;    /* The buffer to copy into */
3615      int            toRead; /* Number of requested bytes */
3616 {
3617   int copied;
3618 
3619   START (ResultCopy);
3620   PRINT ("request = %d, have = %d\n", toRead, r->used); FL;
3621 
3622   if (r->used == 0) {
3623     /* Nothing to copy in the case of an empty buffer.
3624      */
3625 
3626     copied = 0;
3627     goto done;
3628   }
3629 
3630   if (r->used == toRead) {
3631     /* We have just enough. Copy everything to the caller.
3632      */
3633 
3634     memcpy ((VOID*) buf, (VOID*) r->buf, toRead);
3635     r->used = 0;
3636 
3637     copied = toRead;
3638     goto done;
3639   }
3640 
3641   if (r->used > toRead) {
3642     /* The internal buffer contains more than requested.
3643      * Copy the requested subset to the caller, and shift
3644      * the remaining bytes down.
3645      */
3646 
3647     memcpy  ((VOID*) buf,    (VOID*) r->buf,            toRead);
3648     memmove ((VOID*) r->buf, (VOID*) (r->buf + toRead), r->used - toRead);
3649 
3650     r->used -= toRead;
3651 
3652     copied = toRead;
3653     goto done;
3654   }
3655 
3656   /* There is not enough in the buffer to satisfy the caller, so
3657    * take everything.
3658    */
3659 
3660   memcpy ((VOID*) buf, (VOID*) r->buf, r->used);
3661   toRead  = r->used;
3662   r->used = 0;
3663   copied  = toRead;
3664 
3665   /* -- common postwork code ------- */
3666 
3667 done:
3668   if ((copied > 0) &&
3669       (r->seekState != (SeekState*) NULL)) {
3670     r->seekState->upBufStartLoc += copied;
3671   }
3672 
3673   DONE (ResultCopy);
3674   return copied;
3675 }
3676 
3677 /*
3678  *------------------------------------------------------*
3679  *
3680  *	ResultDiscardAtStart --
3681  *
3682  *	Removes the n bytes at the beginning of the buffer
3683  *	from it. Clears the buffer if n is greater than
3684  *	its length.
3685  *
3686  *	Sideeffects:
3687  *		See above.
3688  *
3689  *	Result:
3690  *		None.
3691  *
3692  *------------------------------------------------------*
3693  */
3694 
3695 static void
ResultDiscardAtStart(r,n)3696 ResultDiscardAtStart (r, n)
3697      ResultBuffer*  r; /* The buffer to manipulate  */
3698      int            n; /* Number of bytes to remove */
3699 {
3700   START (ResultDiscardAtStart);
3701   PRINT ("n = %d, have = %d\n", n, r->used); FL;
3702 
3703   if (r->used == 0) {
3704     /* Nothing to remove in the case of an empty buffer.
3705      */
3706 
3707     DONE (ResultDiscardAtStart);
3708     return;
3709   }
3710 
3711   if (n > r->used) {
3712     ResultClear (r);
3713     DONE (ResultDiscardAtStart);
3714     return;
3715   }
3716 
3717   /* Shift remaining information down */
3718 
3719   memmove ((VOID*) r->buf, (VOID*) (r->buf + n), r->used - n);
3720   r->used -= n;
3721 
3722   if (r->seekState != (SeekState*) NULL) {
3723     r->seekState->upBufStartLoc += n;
3724   }
3725 
3726   DONE (ResultCopy);
3727 }
3728 
3729 /*
3730  *------------------------------------------------------*
3731  *
3732  *	ResultAdd --
3733  *
3734  *	Adds the bytes in the specified array to the
3735  *	buffer, by appending it.
3736  *
3737  *	Sideeffects:
3738  *		See above.
3739  *
3740  *	Result:
3741  *		None.
3742  *
3743  *------------------------------------------------------*
3744  */
3745 
3746 static void
ResultAdd(r,buf,toWrite)3747 ResultAdd (r, buf, toWrite)
3748     ResultBuffer*  r;       /* The buffer to extend */
3749     unsigned char* buf;     /* The buffer to read from */
3750     int            toWrite; /* The number of bytes in 'buf' */
3751 {
3752   START (ResultAdd);
3753   PRINT ("have %d, adding %d\n", r->used, toWrite); FL;
3754 
3755   if ((r->used + toWrite + 1) > r->allocated) {
3756     /* Extension of the internal buffer is required.
3757      */
3758 
3759     if (r->allocated == 0) {
3760       r->allocated = toWrite + INCREMENT;
3761       r->buf       = (unsigned char*) ckalloc (r->allocated);
3762     } else {
3763       r->allocated += toWrite + INCREMENT;
3764       r->buf        = (unsigned char*) ckrealloc((char*) r->buf,
3765 						   r->allocated);
3766     }
3767   }
3768 
3769   /* now copy data */
3770   memcpy (r->buf + r->used, buf, toWrite);
3771   r->used += toWrite;
3772 
3773   if (r->seekState != (SeekState*) NULL) {
3774     r->seekState->upBufEndLoc += toWrite;
3775   }
3776 
3777   DONE (ResultAdd);
3778 }
3779 
3780 /*
3781  *------------------------------------------------------*
3782  *
3783  *	SeekCalculatePolicies --
3784  *
3785  *	Computes standard and used policy from the natural
3786  *	policy of the transformation, all transformations
3787  *	below and its base channel.
3788  *
3789  *	Sideeffects:
3790  *		See above.
3791  *
3792  *	Result:
3793  *		None.
3794  *
3795  *------------------------------------------------------*
3796  */
3797 
3798 static void
SeekCalculatePolicies(trans)3799 SeekCalculatePolicies (trans)
3800      TrfTransformationInstance* trans;
3801 {
3802   /* Define seek related runtime configuration.
3803    * seekCfg.overideAllowed, seekCfg.chosen, seekState.used
3804    *
3805    * i.   some transformation below unseekable ? not-overidable unseekable
3806    * ii.  base channel unseekable ?              see above
3807    * iii. naturally unseekable ?                 overidable unseekable.
3808    *
3809    * WARNING: For 8.0 and 8.1 we will always return 'unseekable'. Due to a
3810    * missing 'Tcl_GetStackedChannel' we are unable to go down through the
3811    * stack of transformations.
3812    */
3813 
3814 #ifndef USE_TCL_STUBS
3815   START (SeekCalculatePolicies);
3816   PRINTLN ("8.0., no Tcl_GetStackedChannel, unseekable, no overide");
3817 
3818   TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3819   trans->seekCfg.overideAllowed = 0;
3820 
3821 #else
3822   Tcl_Channel self = trans->self;
3823   Tcl_Channel next;
3824 
3825   int stopped = 0;
3826 
3827   START (SeekCalculatePolicies);
3828 
3829   if (trans->patchVariant == PATCH_ORIG) {
3830     PRINTLN ("8.1., no Tcl_GetStackedChannel, unseekable, no overide");
3831 
3832     TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3833     trans->seekCfg.overideAllowed = 0;
3834     goto done;
3835   }
3836 
3837   /* 8.2 or higher */
3838 
3839   while (self != (Tcl_Channel) NULL) {
3840     PRINT ("Check %p\n", self); FL;
3841 
3842 #if GT81
3843     next = Tcl_GetStackedChannel (self);
3844 #else
3845     /* In case of 8.1 and higher we can use the (integrated or patched)
3846      * 'Tcl_GetStackedChannel' to find the next transform in a general
3847      * way. Else we have to check the type of 'next' itself before trying
3848      * to peek into its structure. If it is no Trf transform we cannot go
3849      * deeper into the stack. But that is not necessary, as the result of
3850      * 'unseekable' will not change anymore.
3851      */
3852 
3853     if (Tcl_GetChannelType (self)->seekProc != TrfSeek) {
3854       PRINTLN ("Can't go further down, unseekable, disallow overide");
3855 
3856       TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3857       trans->seekCfg.overideAllowed = 0;
3858       stopped = 1;
3859       break;
3860     }
3861 
3862     next = ((TrfTransformationInstance*)
3863 		   Tcl_GetChannelInstanceData (self))->parent;
3864 #endif
3865 
3866     if (next == (Tcl_Channel) NULL) {
3867       /* self points to base channel (ii).
3868        */
3869 
3870       if (Tcl_GetChannelType (self)->seekProc == (Tcl_DriverSeekProc*) NULL) {
3871 	/* Base is unseekable.
3872 	 */
3873 
3874 	PRINTLN ("Base is unseekable");
3875 
3876 	TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3877 	trans->seekCfg.overideAllowed = 0;
3878 	stopped = 1;
3879 	break;
3880       }
3881     } else {
3882       /* 'next' points to a transformation.
3883        */
3884 
3885       Tcl_Channel nextAfter;
3886 
3887 #if GT81
3888       nextAfter = Tcl_GetStackedChannel (next);
3889 #else
3890       nextAfter = ((TrfTransformationInstance*)
3891 		   Tcl_GetChannelInstanceData (next))->parent;
3892 #endif
3893 
3894       if (nextAfter != (Tcl_Channel) NULL) {
3895 	/* next points to a transformation below the top (i).
3896 	 * Assume unseekable for a non-trf transformation, else peek directly
3897 	 * into the relevant structure
3898 	 */
3899 
3900 	if (Tcl_GetChannelType (next)->seekProc != TrfSeek) {
3901 	  PRINTLN ("Unknown type of transform, unseekable, no overide");
3902 
3903 	  TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3904 	  trans->seekCfg.overideAllowed = 0;
3905 	  stopped = 1;
3906 	} else {
3907 	  TrfTransformationInstance* down =
3908 	    (TrfTransformationInstance*) Tcl_GetChannelInstanceData (next);
3909 
3910 	  if (!down->seekState.allowed) {
3911 	    PRINTLN ("Trf transform, unseekable");
3912 
3913 	    TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3914 	    trans->seekCfg.overideAllowed = 0;
3915 	    stopped = 1;
3916 	  }
3917 	}
3918       } else {
3919 	/* Next points to the base channel */
3920 	/* assert (0); */
3921       }
3922     }
3923 
3924     self = next;
3925   }
3926 
3927   PRINTLN ("Looping done");
3928 
3929   if (!stopped) {
3930     PRINTLN ("Search went through, check natural policy");
3931 
3932     if (TRF_IS_UNSEEKABLE (trans->seekCfg.natural)) {
3933       /* Naturally unseekable (iii)
3934        */
3935 
3936       PRINTLN ("Naturally unseekable");
3937 
3938       TRF_SET_UNSEEKABLE (trans->seekCfg.chosen);
3939       trans->seekCfg.overideAllowed = 1;
3940     } else {
3941       /* Take the natural ratio.
3942        */
3943 
3944       PRINTLN ("naturally seekable");
3945 
3946       trans->seekCfg.chosen.numBytesTransform =
3947 	trans->seekCfg.natural.numBytesTransform;
3948 
3949       trans->seekCfg.chosen.numBytesDown      =
3950 	trans->seekCfg.natural.numBytesDown;
3951 
3952       trans->seekCfg.overideAllowed = 1;
3953     }
3954   }
3955 #endif
3956 
3957   PRINTLN ("Copy ratio chosen :- used");
3958 
3959 #ifdef USE_TCL_STUBS
3960 done:
3961 #endif
3962   trans->seekState.used.numBytesTransform =
3963     trans->seekCfg.chosen.numBytesTransform;
3964 
3965   trans->seekState.used.numBytesDown      =
3966     trans->seekCfg.chosen.numBytesDown;
3967 
3968   trans->seekState.allowed                =
3969     !TRF_IS_UNSEEKABLE (trans->seekState.used);
3970 
3971   DONE (SeekCalculatePolicies);
3972 }
3973 
3974 /*
3975  *------------------------------------------------------*
3976  *
3977  *	SeekInitialize --
3978  *
3979  *	Initialize the runtime state of the seek mechanisms
3980  *
3981  *	Sideeffects:
3982  *		See above.
3983  *
3984  *	Result:
3985  *		None.
3986  *
3987  *------------------------------------------------------*
3988  */
3989 
3990 static void
SeekInitialize(trans)3991 SeekInitialize (trans)
3992      TrfTransformationInstance* trans;
3993 {
3994   trans->seekState.upLoc         = 0;
3995   trans->seekState.upBufStartLoc = 0;
3996   trans->seekState.upBufEndLoc   = 0;
3997 
3998   if (trans->seekState.allowed) {
3999     trans->seekState.downLoc     = TELL (trans);
4000 #ifdef USE_TCL_STUBS
4001     if (trans->patchVariant == PATCH_832) {
4002       trans->seekState.downLoc  -= Tcl_ChannelBuffered (DOWNC (trans));
4003     }
4004 #endif
4005     trans->seekState.downZero    = trans->seekState.downLoc;
4006     trans->seekState.aheadOffset = 0;
4007   } else {
4008     trans->seekState.downLoc     = 0;
4009     trans->seekState.downZero    = 0;
4010     trans->seekState.aheadOffset = 0;
4011   }
4012 
4013   trans->seekCfg.identity   = 0;
4014   trans->seekState.changed  = 0;
4015 
4016   SEEK_DUMP (Seek Initialized);
4017 }
4018 
4019 /*
4020  *------------------------------------------------------*
4021  *
4022  *	SeekClearBuffer --
4023  *
4024  *	Clear read / write buffers of the transformation,
4025  *	as specified by the second argument.
4026  *
4027  *	Sideeffects:
4028  *		See above.
4029  *
4030  *	Result:
4031  *		None.
4032  *
4033  *------------------------------------------------------*
4034  */
4035 
4036 static void
SeekClearBuffer(trans,which)4037 SeekClearBuffer (trans, which)
4038      TrfTransformationInstance* trans;
4039      int                        which;
4040 {
4041   /*
4042    * Discard everything in the input and output buffers, both
4043    * in the transformation and in the generic layer of Trf.
4044    */
4045 
4046   if (trans->mode & which & TCL_WRITABLE) {
4047     PRINT ("out.clearproc\n"); FL;
4048 
4049     trans->out.vectors->clearProc (trans->out.control, trans->clientData);
4050   }
4051 
4052   if (trans->mode & which & TCL_READABLE) {
4053     PRINT ("in.clearproc\n"); FL;
4054 
4055     trans->in.vectors->clearProc  (trans->in.control, trans->clientData);
4056     trans->readIsFlushed = 0;
4057     ResultClear (&trans->result);
4058   }
4059 }
4060 
4061 /*
4062  *------------------------------------------------------*
4063  *
4064  *	SeekSynchronize --
4065  *
4066  *	Discard an existing read buffer and annulate the
4067  *	read ahead in the downstream channel.
4068  *
4069  *	Sideeffects:
4070  *		See above.
4071  *
4072  *	Result:
4073  *		None.
4074  *
4075  *------------------------------------------------------*
4076  */
4077 
4078 static void
SeekSynchronize(trans,parent)4079 SeekSynchronize (trans, parent)
4080      TrfTransformationInstance* trans;
4081      Tcl_Channel                parent;
4082 {
4083   int offsetDown;
4084 
4085   if (!trans->seekState.allowed) {
4086     /* No synchronisation required for an unseekable transform */
4087     return;
4088   }
4089 
4090   if ((trans->seekState.upLoc == trans->seekState.upBufEndLoc) &&
4091       (trans->seekState.aheadOffset == 0)) {
4092     /* Up and down locations are in sync, nothing to do. */
4093     return;
4094   }
4095 
4096   PRINT ("in.clearproc\n"); FL;
4097 
4098   trans->in.vectors->clearProc  (trans->in.control, trans->clientData);
4099   trans->readIsFlushed = 0;
4100 
4101   offsetDown  = TRF_DOWN_CONVERT (trans,
4102 				  trans->seekState.upLoc - trans->seekState.upBufEndLoc);
4103   offsetDown -= trans->seekState.aheadOffset; /* !! */
4104 
4105   ResultClear (&trans->result);
4106 
4107   if (offsetDown != 0) {
4108     SEEK (trans, offsetDown, SEEK_CUR);
4109   }
4110 
4111   trans->seekState.downLoc += offsetDown;
4112 }
4113 
4114 /*
4115  *------------------------------------------------------*
4116  *
4117  *	SeekPolicyGet --
4118  *
4119  *	Compute the currently used policy and store its
4120  *	name into the character buffer.
4121  *
4122  *	Sideeffects:
4123  *		See above.
4124  *
4125  *	Result:
4126  *		None.
4127  *
4128  *------------------------------------------------------*
4129  */
4130 
4131 static void
SeekPolicyGet(trans,policy)4132 SeekPolicyGet (trans, policy)
4133      TrfTransformationInstance* trans;
4134      char*                      policy;
4135 {
4136   if (trans->seekCfg.identity) {
4137     /* identity forced */
4138 
4139     strcpy (policy, "identity");
4140     return;
4141   }
4142 
4143   if (!trans->seekState.allowed &&
4144       ((trans->seekState.used.numBytesTransform !=
4145 	trans->seekCfg.chosen.numBytesTransform) ||
4146        (trans->seekState.used.numBytesDown !=
4147 	trans->seekCfg.chosen.numBytesDown))) {
4148     /* unseekable forced */
4149 
4150     strcpy (policy, "unseekable");
4151     return;
4152   }
4153 
4154   /* chosen policy in effect */
4155 
4156   strcpy (policy, "");
4157   return;
4158 }
4159 
4160 /*
4161  *------------------------------------------------------*
4162  *
4163  *	SeekConfigGet --
4164  *
4165  *	Generates a list containing the current configuration
4166  *	of the seek system in a readable manner.
4167  *
4168  *	Sideeffects:
4169  *		See above.
4170  *
4171  *	Result:
4172  *		An Tcl_Obj, or NULL.
4173  *
4174  *------------------------------------------------------*
4175  */
4176 
4177 static Tcl_Obj*
SeekConfigGet(interp,cfg)4178 SeekConfigGet (interp, cfg)
4179      Tcl_Interp* interp;
4180      SeekConfig* cfg;
4181 {
4182   int      res;
4183   Tcl_Obj* list = (Tcl_Obj*) NULL;
4184   Tcl_Obj* sub1 = (Tcl_Obj*) NULL;
4185   Tcl_Obj* sub2 = (Tcl_Obj*) NULL;
4186 
4187   list = Tcl_NewListObj (0, NULL);
4188 
4189   if (list == (Tcl_Obj*) NULL) {
4190     goto error;
4191   }
4192 
4193   LIST_ADDSTR (error, list, "ratioNatural");
4194   sub1 = Tcl_NewListObj (0, NULL);
4195 
4196   if (sub1 == (Tcl_Obj*) NULL) {
4197     goto error;
4198   }
4199 
4200   LIST_ADDINT (error, sub1, cfg->natural.numBytesTransform);
4201   LIST_ADDINT (error, sub1, cfg->natural.numBytesDown);
4202   LIST_ADDOBJ (error, list, sub1);
4203 
4204 
4205   LIST_ADDSTR (error, list, "ratioChosen");
4206   sub2 = Tcl_NewListObj (0, NULL);
4207 
4208   if (sub2 == (Tcl_Obj*) NULL) {
4209     goto error;
4210   }
4211 
4212   LIST_ADDINT (error, sub2, cfg->chosen.numBytesTransform);
4213   LIST_ADDINT (error, sub2, cfg->chosen.numBytesDown);
4214   LIST_ADDOBJ (error, list, sub2);
4215 
4216   LIST_ADDSTR (error, list, "overideAllowed");
4217   LIST_ADDINT (error, list, cfg->overideAllowed);
4218 
4219   LIST_ADDSTR (error, list, "identityForced");
4220   LIST_ADDINT (error, list, cfg->identity);
4221 
4222   return list;
4223 
4224 error:
4225   /* Cleanup any remnants of errors above */
4226 
4227   if (list != (Tcl_Obj*) NULL) {
4228     Tcl_DecrRefCount (list);
4229   }
4230 
4231   if (sub1 != (Tcl_Obj*) NULL) {
4232     Tcl_DecrRefCount (sub1);
4233   }
4234 
4235   if (sub2 != (Tcl_Obj*) NULL) {
4236     Tcl_DecrRefCount (sub2);
4237   }
4238 
4239   return NULL;
4240 }
4241 
4242 /*
4243  *------------------------------------------------------*
4244  *
4245  *	SeekStateGet --
4246  *
4247  *	Generates a list containing the current state of
4248  *	the seek system in a readable manner.
4249  *
4250  *	Sideeffects:
4251  *		See above.
4252  *
4253  *	Result:
4254  *		An Tcl_Obj, or NULL.
4255  *
4256  *------------------------------------------------------*
4257  */
4258 
4259 static Tcl_Obj*
SeekStateGet(interp,state)4260 SeekStateGet (interp, state)
4261      Tcl_Interp* interp;
4262      SeekState* state;
4263 {
4264   int      res;
4265   Tcl_Obj* list = (Tcl_Obj*) NULL;
4266   Tcl_Obj* sub  = (Tcl_Obj*) NULL;
4267 
4268   list = Tcl_NewListObj (0, NULL);
4269 
4270   if (list == (Tcl_Obj*) NULL) {
4271     goto error;
4272   }
4273 
4274   LIST_ADDSTR (error, list, "seekable");
4275   LIST_ADDINT (error, list, state->allowed);
4276 
4277   LIST_ADDSTR (error, list, "ratio");
4278 
4279   sub  = Tcl_NewListObj (0, NULL);
4280   if (sub == (Tcl_Obj*) NULL) {
4281     goto error;
4282   }
4283 
4284   LIST_ADDINT (error, sub, state->used.numBytesTransform);
4285   LIST_ADDINT (error, sub, state->used.numBytesDown);
4286   LIST_ADDOBJ (error, list, sub);
4287 
4288   LIST_ADDSTR (error, list, "up");
4289   LIST_ADDINT (error, list, state->upLoc);
4290 
4291   LIST_ADDSTR (error, list, "upBufStart");
4292   LIST_ADDINT (error, list, state->upBufStartLoc);
4293 
4294   LIST_ADDSTR (error, list, "upBufEnd");
4295   LIST_ADDINT (error, list, state->upBufEndLoc);
4296 
4297   LIST_ADDSTR (error, list, "down");
4298   LIST_ADDINT (error, list, state->downLoc);
4299 
4300   LIST_ADDSTR (error, list, "downBase");
4301   LIST_ADDINT (error, list, state->downZero);
4302 
4303   LIST_ADDSTR (error, list, "downAhead");
4304   LIST_ADDINT (error, list, state->aheadOffset);
4305 
4306   LIST_ADDSTR (error, list, "changed");
4307   LIST_ADDINT (error, list, state->changed);
4308 
4309   return list;
4310 
4311 error:
4312   /* Cleanup any remnants of errors above */
4313 
4314   if (list != (Tcl_Obj*) NULL) {
4315     Tcl_DecrRefCount (list);
4316   }
4317 
4318   if (sub != (Tcl_Obj*) NULL) {
4319     Tcl_DecrRefCount (sub);
4320   }
4321 
4322   return NULL;
4323 }
4324 
4325 #ifdef TRF_DEBUG
4326 /*
4327  *------------------------------------------------------*
4328  *
4329  *	PrintString --
4330  *
4331  *	Defined only in debug mode, enforces correct
4332  *	printing of strings by adding a \0 after its value.
4333  *
4334  *	Sideeffects:
4335  *		See above.
4336  *
4337  *	Result:
4338  *		None.
4339  *
4340  *------------------------------------------------------*
4341  */
4342 
4343 void
PrintString(fmt,len,bytes)4344 PrintString (fmt,len,bytes)
4345      char* fmt;
4346      int   len;
4347      char* bytes;
4348 {
4349   char* tmp = (char*) ckalloc (len+1);
4350   memcpy (tmp, bytes, len);
4351   tmp [len] = '\0';
4352 
4353   PRINT (fmt, len, tmp);
4354 
4355   ckfree (tmp);
4356 }
4357 
4358 /*
4359  *------------------------------------------------------*
4360  *
4361  *	DumpString --
4362  *
4363  *	Defined only in debug mode, dumps information
4364  *	in hex blocks
4365  *
4366  *	Sideeffects:
4367  *		See above.
4368  *
4369  *	Result:
4370  *		None.
4371  *
4372  *------------------------------------------------------*
4373  */
4374 
4375 void
DumpString(n,len,bytes)4376 DumpString (n,len,bytes)
4377      int   n;
4378      int   len;
4379      char* bytes;
4380 {
4381   int i, c;
4382 
4383   for (i=0, c=0; i < len; i++, c++) {
4384     if (c == 0) {
4385       BLNKS;
4386     }
4387 
4388     printf (" %02x", (0xff & bytes [i]));
4389 
4390     if (c == 16) {
4391       c = -1;
4392       printf ("\n");
4393     }
4394   }
4395 
4396   if (c != 0) {
4397     printf ("\n");
4398   }
4399 }
4400 
4401 /*
4402  *------------------------------------------------------*
4403  *
4404  *	SeekDump --
4405  *
4406  *	Defined only in debug mode, dumps the complete
4407  *	state of all seek variables.
4408  *
4409  *	Sideeffects:
4410  *		See above.
4411  *
4412  *	Result:
4413  *		None.
4414  *
4415  *------------------------------------------------------*
4416  */
4417 
4418 static void
SeekDump(trans,place)4419 SeekDump (trans, place)
4420      TrfTransformationInstance* trans;
4421      CONST char*                place;
4422 {
4423   int         loc;
4424   Tcl_Channel parent = DOWNC (trans);
4425 
4426   loc = TELL (trans);
4427 
4428 #if 0
4429   PRINT ("SeekDump (%s) {\n", place); FL; IN;
4430 
4431   PRINT ("ratio up:down    %d : %d\n",
4432 	 trans->seekState.used.numBytesTransform,
4433 	 trans->seekState.used.numBytesDown); FL;
4434   PRINT ("seekable         %d\n",
4435 	 trans->seekState.allowed); FL;
4436   PRINT ("up               %d [%d .. %d]\n",
4437 	 trans->seekState.upLoc,
4438 	 trans->seekState.upBufStartLoc,
4439 	 trans->seekState.upBufEndLoc); FL;
4440   PRINT ("down             %d [%d] | %d\n",
4441 	 trans->seekState.downLoc,
4442 	 trans->seekState.aheadOffset,
4443 	 loc); FL;
4444   PRINT ("base             %d\n",
4445 	 trans->seekState.downZero); FL;
4446   PRINT ("identity force   %d\n",
4447 	 trans->seekCfg.identity); FL;
4448   PRINT ("seek while ident %d\n",
4449 	 trans->seekState.changed); FL;
4450   PRINT ("read buffer      %d\n",
4451 	 ResultLength (&trans->result)); FL;
4452 
4453   OT ; PRINT ("}\n"); FL;
4454 #else
4455   PRINT ("SkDmp (%s) ", place); FL;
4456 
4457 #if 0
4458   NPRINT ("(%2d : %2d) | ",
4459 	  trans->seekCfg.natural.numBytesTransform,
4460 	  trans->seekCfg.natural.numBytesDown); FL;
4461   NPRINT ("(%2d : %2d) | ",
4462 	  trans->seekCfg.chosen.numBytesTransform,
4463 	  trans->seekCfg.chosen.numBytesDown); FL;
4464 #endif
4465   NPRINT ("%2d:%2d /%1d |r %5d |u %5d [%5d..%5d] |d %5d [%2d] %5d | %5d | %1d %1d",
4466 	  trans->seekState.used.numBytesTransform,
4467 	  trans->seekState.used.numBytesDown,
4468 	  trans->seekState.allowed,
4469 	  ResultLength (&trans->result),
4470 	  trans->seekState.upLoc,
4471 	  trans->seekState.upBufStartLoc,
4472 	  trans->seekState.upBufEndLoc,
4473 	  trans->seekState.downLoc,
4474 	  trans->seekState.aheadOffset,
4475 	  loc,
4476 	  trans->seekState.downZero,
4477 	  trans->seekCfg.identity,
4478 	  trans->seekState.changed
4479 	  ); FL;
4480 
4481   NPRINT ("\n"); FL;
4482 #endif
4483 }
4484 #endif
4485 
4486 /*
4487  *------------------------------------------------------*
4488  *
4489  *	AllocChannelType --
4490  *
4491  *	Allocates a new ChannelType structure.
4492  *
4493  *
4494  *	Sideeffects:
4495  *		See above.
4496  *
4497  *	Result:
4498  *		A reference to the new structure.
4499  *
4500  *------------------------------------------------------*
4501  */
4502 
4503 static Tcl_ChannelType*
AllocChannelType(sizePtr)4504 AllocChannelType (sizePtr)
4505      int* sizePtr;
4506 {
4507   /*
4508    * Allocation of a new channeltype structure is not easy, because of
4509    * the various version of the core and subsequent changes to the
4510    * structure. The main challenge is to allocate enough memory for
4511    * modern versions even if this extension is compiled against one
4512    * of the older variants!
4513    *
4514    * (1) Versions before stubs (8.0.x) are simple, because they are
4515    *     supported only if the extension is compiled against exactly
4516    *     that version of the core.
4517    *
4518    * (2) With stubs we just determine the difference between the older
4519    *     and modern variant and overallocate accordingly if compiled
4520    *     against an older variant.
4521    */
4522 
4523   int size = sizeof(Tcl_ChannelType); /* Base size */
4524 
4525 #ifdef USE_TCL_STUBS
4526   /*
4527    * Size of a procedure pointer. We assume that all procedure
4528    * pointers are of the same size, regardless of exact type
4529    * (arguments and return values).
4530    *
4531    * 8.1.   First version containing close2proc. Baseline.
4532    * 8.3.2  Three additional vectors. Moved blockMode, new flush- and
4533    *        handlerProc's.
4534    * 8.4+   wide seek, and thread action.
4535    *
4536    * => Compilation against earlier version has to overallocate five
4537    *    procedure pointers.
4538    */
4539 
4540 #if !(GT832)
4541   size += 5 * procPtrSize;
4542 #endif
4543 #endif
4544 
4545   if (sizePtr != (int*) NULL) {
4546     *sizePtr = size;
4547   }
4548   return (Tcl_ChannelType*) ckalloc (size);
4549 }
4550 
4551 /*
4552  *------------------------------------------------------*
4553  *
4554  *	InitializeChannelType --
4555  *
4556  *	Initializes a new ChannelType structure.
4557  *
4558  *
4559  *	Sideeffects:
4560  *		See above.
4561  *
4562  *	Result:
4563  *		None.
4564  *
4565  *------------------------------------------------------*
4566  */
4567 
4568 static Tcl_ChannelType*
InitializeChannelType(name,patchVariant)4569 InitializeChannelType (name, patchVariant)
4570      CONST char*      name;
4571      int              patchVariant;
4572 {
4573   Tcl_ChannelType* tct;
4574   int              size;
4575 
4576   /*
4577    * Initialization of a new channeltype structure is not easy,
4578    * because of the various version of the core and subsequent changes
4579    * to the structure. The main problem is if compiled against an
4580    * older version how to access the elements of the structure not
4581    * known in that version. It is made a bit easier because the
4582    * allocation routine returns the allocated size. This allows us to
4583    * clear out the entire structure. So we just have to deal with the
4584    * elements to set and not the ones left alone.
4585    */
4586 
4587   tct           = AllocChannelType (&size);
4588   tct->typeName = (char*) name;
4589 
4590   memset ((VOID*) tct, '\0', size);
4591 
4592   /*
4593    * Common elements of the structure (no changes in location or name)
4594    */
4595 
4596   tct->closeProc        = TrfClose;
4597   tct->inputProc        = TrfInput;
4598   tct->outputProc       = TrfOutput;
4599   tct->seekProc         = TrfSeek;
4600   tct->setOptionProc    = TrfSetOption;
4601   tct->getOptionProc    = TrfGetOption;
4602   tct->watchProc        = TrfWatch;
4603   tct->getHandleProc    = TrfGetFile;
4604 
4605   /*
4606    * No need to handle close2Proc. Already cleared with the 'memset'
4607    * above.
4608    */
4609 
4610   /*
4611    * blockModeProc is a twister. For 8.0.x we can access it
4612    * immediately. For the higher versions we have to make some
4613    * runtime-choices, and their implementation depends on the version
4614    * we compile against.
4615    */
4616 
4617 #ifndef USE_TCL_STUBS
4618   /* 8.0.x */
4619   tct->blockModeProc    = TrfBlock;
4620 #else
4621 #if GT832
4622   /* 8.3.2. and higher. Direct access to all elements possible. Use
4623    *'patchVariant' information to select the values to use.
4624    */
4625 
4626   if ((patchVariant == PATCH_ORIG) ||
4627       (patchVariant == PATCH_82)) {
4628     /* The 'version' element of 8.3.2 is in the the place of the
4629      * blockModeProc. For the original patch in 8.1.x and the firstly
4630      * included (8.2) we have to set our blockModeProc into this
4631      * place.
4632      */
4633     tct->version = (Tcl_ChannelTypeVersion) TrfBlock;
4634   } else /* patchVariant == PATCH_832 */ {
4635     /* For the 8.3.2 core we present ourselves as a version 2
4636      * driver. This means a speciial value in version (ex
4637      * blockModeProc), blockModeProc in a different place and of
4638      * course usage of the handlerProc.
4639      */
4640 
4641     tct->version       = TCL_CHANNEL_VERSION_2;
4642     tct->blockModeProc = TrfBlock;
4643     tct->handlerProc   = TrfNotify;
4644   }
4645 #else
4646   /* Same as above, but as we are compiling against an older core we
4647    * have to create some definitions for the new elements as the compiler
4648    * does not know them by name.
4649    */
4650 
4651   if ((patchVariant == PATCH_ORIG) ||
4652       (patchVariant == PATCH_82)) {
4653     /* The 'version' element of 8.3.2 is in the the place of the
4654      * blockModeProc. For the original patch in 8.1.x and the firstly
4655      * included (8.2) we have to set our blockModeProc into this
4656      * place.
4657      */
4658     tct->blockModeProc = TrfBlock;
4659   } else /* patchVariant == PATCH_832 */ {
4660     /* For the 8.3.2 core we present ourselves as a version 2
4661      * driver. This means a special value in version (ex
4662      * blockModeProc), blockModeProc in a different place and of
4663      * course usage of the handlerProc.
4664      */
4665 
4666 #define TRF_CHANNEL_VERSION_2	((TrfChannelTypeVersion) 0x2)
4667 
4668 #define BMP (*((Tcl_DriverBlockModeProc**) (&(tct->close2Proc) + 1)))
4669 #define HP  (*((TrfDriverHandlerProc**)    (&(tct->close2Proc) + 3)))
4670 
4671     typedef struct TrfChannelTypeVersion_* TrfChannelTypeVersion;
4672     typedef int	(TrfDriverHandlerProc) _ANSI_ARGS_((ClientData instanceData,
4673 						    int interestMask));
4674 
4675     tct->blockModeProc = (Tcl_DriverBlockModeProc*) TRF_CHANNEL_VERSION_2;
4676 
4677     BMP = TrfBlock;
4678     HP  = TrfNotify;
4679 
4680 #undef BMP
4681 #undef HP
4682 #undef TRF_CHANNEL_VERSION_2
4683   }
4684 #endif
4685 #endif
4686 
4687   return tct;
4688 }
4689 
4690 /*
4691  *------------------------------------------------------*
4692  *
4693  *	TimerKill --
4694  *
4695  *	Timer management. Removes the internal timer
4696  *	if it exists.
4697  *
4698  *	Sideeffects:
4699  *		See above.
4700  *
4701  *	Result:
4702  *		None.
4703  *
4704  *------------------------------------------------------*
4705  */
4706 
4707 static void
TimerKill(trans)4708 TimerKill (trans)
4709      TrfTransformationInstance* trans;
4710 {
4711   if (trans->timer != (Tcl_TimerToken) NULL) {
4712     /* Delete an existing flush-out timer,
4713      * prevent it from firing on removed channel.
4714      */
4715 
4716     Tcl_DeleteTimerHandler (trans->timer);
4717     trans->timer = (Tcl_TimerToken) NULL;
4718 
4719     PRINT ("Timer deleted ..."); FL;
4720   }
4721 }
4722 
4723 /*
4724  *------------------------------------------------------*
4725  *
4726  *	TimerSetup --
4727  *
4728  *	Timer management. Creates the internal timer
4729  *	if it does not exist.
4730  *
4731  *	Sideeffects:
4732  *		See above.
4733  *
4734  *	Result:
4735  *		None.
4736  *
4737  *------------------------------------------------------*
4738  */
4739 
4740 static void
TimerSetup(trans)4741 TimerSetup (trans)
4742      TrfTransformationInstance* trans;
4743 {
4744   if (trans->timer == (Tcl_TimerToken) NULL) {
4745     trans->timer = Tcl_CreateTimerHandler (TRF_DELAY, ChannelHandlerTimer,
4746 					   (ClientData) trans);
4747   }
4748 }
4749 
4750 /*
4751  *------------------------------------------------------*
4752  *
4753  *	ChannelHandlerKS --
4754  *
4755  *	Management of channel handlers. Deletes/Recreates
4756  *	as required by the specified mask.
4757  *
4758  *	Sideeffects:
4759  *		See above.
4760  *
4761  *	Result:
4762  *		None.
4763  *
4764  *------------------------------------------------------*
4765  */
4766 
4767 static void
ChannelHandlerKS(trans,mask)4768 ChannelHandlerKS (trans, mask)
4769      TrfTransformationInstance* trans;
4770      int                        mask;
4771 {
4772   /*
4773    * This procedure is called only for the original and the 8.2
4774    * patch. The new 8.2.3 patch does not use channel handlers but a
4775    * separate NotifyHandler in the driver.
4776    */
4777 
4778   Tcl_Channel parent = DOWNC (trans);
4779 
4780   if (trans->watchMask) {
4781     /*
4782      * Remove event handler to underlying channel, this could
4783      * be because we are closing for real, or being "unstacked".
4784      */
4785 
4786     Tcl_DeleteChannelHandler (parent, ChannelHandler,
4787 			      (ClientData) trans);
4788   }
4789 
4790   trans->watchMask = mask;
4791 
4792   if (trans->watchMask) {
4793     /*
4794      * Setup active monitor for events on underlying Channel
4795      */
4796 
4797     Tcl_CreateChannelHandler (parent, trans->watchMask,
4798 			      ChannelHandler, (ClientData) trans);
4799   }
4800 }
4801