1 // Creates and registers Tcl's stdout & stderr channels for e93
2 // (currently using John Ousterhout's Tcl)
3 // Copyright (C) 2000 Core Technologies.
4
5 // This file is part of e93.
6 //
7 // e93 is free software; you can redistribute it and/or modify
8 // it under the terms of the e93 LICENSE AGREEMENT.
9 //
10 // e93 is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // e93 LICENSE AGREEMENT for more details.
14 //
15 // You should have received a copy of the e93 LICENSE AGREEMENT
16 // along with e93; see the file "LICENSE.TXT".
17
18 // This code was contributed by Michael Manning
19
20 // This could use some more work to support some of the things Tcl might
21 // want to do with its channels. Like blocking and setting watches on
22 // them.
23
24 #include "includes.h"
25
26 static EDITOR_BUFFER_HANDLE
27 stdoutBufferHandle,
28 stderrBufferHandle;
29
CommonBlockModeProc(ClientData instanceData,int mode)30 static int CommonBlockModeProc(ClientData instanceData,int mode)
31 // Set blocking or non-blocking mode
32 {
33 return(-1); // can't set blocking, return non-zero
34 }
35
CommonWatchProc(ClientData instanceData,int mask)36 static void CommonWatchProc(ClientData instanceData,int mask)
37 // Called by the notifier to set up to watch for events on this
38 // channel.
39 {
40 }
41
CommonGetHandleProc(ClientData instanceData,int direction,ClientData * handlePtr)42 static int CommonGetHandleProc(ClientData instanceData,int direction,ClientData *handlePtr)
43 // Called from Tcl_GetChannelHandle to retrieve OS handles from
44 // inside a command console line based channel.
45 {
46 *handlePtr=NULL;
47
48 return(TCL_ERROR);
49 }
50
CommonCloseProc(ClientData instanceData,Tcl_Interp * interp)51 static int CommonCloseProc(ClientData instanceData,Tcl_Interp *interp)
52 // Closes a console based IO channel.
53 {
54 return(TCL_OK); // nothing for us to do, but this proc must exist
55 }
56
StdoutOutputProc(ClientData clientData,const char * buf,int charsToWrite,int * errorCode)57 static int StdoutOutputProc(ClientData clientData,const char *buf,int charsToWrite,int *errorCode)
58 // Writes the given output on the IO channel. Returns count of how
59 // many characters were actually written, and an error indication.
60 {
61 int
62 charsWritten;
63 EDITOR_BUFFER
64 *buffer;
65
66 buffer=EditorGetBufferFromHandle(&stdoutBufferHandle);
67 if(buffer&&!BufferBusy(buffer)) // get the output buffer if there is one, make sure it is not otherwise busy
68 {
69 EditorInsert(buffer,(UINT8 *)buf,charsToWrite);
70 charsWritten=charsToWrite; // tell tcl how many characters we wrote
71 }
72 else
73 {
74 charsWritten=-1;
75 }
76 return(charsWritten);
77 }
78
StderrOutputProc(ClientData clientData,const char * buf,int charsToWrite,int * errorCode)79 static int StderrOutputProc(ClientData clientData,const char *buf,int charsToWrite,int *errorCode)
80 // Writes the given output on the IO channel. Returns count of how
81 // many characters were actually written, and an error indication.
82 {
83 int
84 charsWritten;
85 EDITOR_BUFFER
86 *buffer;
87
88 buffer=EditorGetBufferFromHandle(&stderrBufferHandle);
89 if(buffer&&!BufferBusy(buffer)) // get the output buffer if there is one, make sure it is not otherwise busy
90 {
91 EditorInsert(buffer,(UINT8 *)buf,charsToWrite);
92 charsWritten=charsToWrite; // tell tcl how many characters we wrote
93 }
94 else
95 {
96 charsWritten=-1;
97 }
98 return(charsWritten);
99 }
100
101 // Anyone who knows me knows I loathe the use of #ifdef and friends
102 // However, until there's a better way to handle this....
103
104 #if TCL_MAJOR_VERSION == 8 && (TCL_MINOR_VERSION > 3 || (TCL_MINOR_VERSION == 3 && TCL_RELEASE_SERIAL > 1))
105
106 static Tcl_ChannelType
107 stdoutChannelType=
108 {
109 (char *)"window", // Type name
110 TCL_CHANNEL_VERSION_2, // channel version
111 CommonCloseProc, // Close proc
112 NULL, // Input proc (we don't do input)
113 StdoutOutputProc, // Output proc
114 NULL, // Seek proc
115 NULL, // Set option proc
116 NULL, // Get option proc
117 CommonWatchProc, // Set up notifier to watch the channel
118 CommonGetHandleProc, // Get an OS handle from channel
119 NULL, // Close2 proc
120 CommonBlockModeProc, // Set blocking or non-blocking mode
121 NULL, // flush proc
122 NULL, // handler proc
123 };
124
125 static Tcl_ChannelType
126 stderrChannelType=
127 {
128 (char *)"window", // Type name
129 TCL_CHANNEL_VERSION_2, // channel version
130 CommonCloseProc, // Close proc
131 NULL, // Input proc (we don't do input)
132 StderrOutputProc, // Output proc
133 NULL, // Seek proc
134 NULL, // Set option proc
135 NULL, // Get option proc
136 CommonWatchProc, // Set up notifier to watch the channel
137 CommonGetHandleProc, // Get an OS handle from channel
138 NULL, // Close2 proc
139 CommonBlockModeProc, // Set blocking or non-blocking mode
140 NULL, // flush proc
141 NULL, // handler proc
142 };
143
144 #else // old way
145
146 static Tcl_ChannelType
147 stdoutChannelType=
148 {
149 "window", // Type name
150 CommonBlockModeProc, // Set blocking or non-blocking mode
151 CommonCloseProc, // Close proc
152 NULL, // Input proc (we don't do input)
153 StdoutOutputProc, // Output proc
154 NULL, // Seek proc
155 NULL, // Set option proc
156 NULL, // Get option proc
157 CommonWatchProc, // Set up notifier to watch the channel
158 CommonGetHandleProc, // Get an OS handle from channel
159 NULL, // Close2 proc
160 };
161
162 static Tcl_ChannelType
163 stderrChannelType=
164 {
165 "window", // Type name
166 CommonBlockModeProc, // Set blocking or non-blocking mode
167 CommonCloseProc, // Close proc
168 NULL, // Input proc (we don't do input)
169 StderrOutputProc, // Output proc
170 NULL, // Seek proc
171 NULL, // Set option proc
172 NULL, // Get option proc
173 CommonWatchProc, // Set up notifier to watch the channel
174 CommonGetHandleProc, // Get an OS handle from channel
175 NULL, // Close2 proc
176 };
177
178 #endif
179
180
GetTclStdoutBuffer(Tcl_Interp * localInterpreter)181 EDITOR_BUFFER *GetTclStdoutBuffer(Tcl_Interp *localInterpreter)
182 // set the buffer being used for stdout
183 {
184 return(EditorGetBufferFromHandle(&stdoutBufferHandle));
185 }
186
SetTclStdoutBuffer(Tcl_Interp * localInterpreter,EDITOR_BUFFER * newBuffer)187 void SetTclStdoutBuffer(Tcl_Interp *localInterpreter,EDITOR_BUFFER *newBuffer)
188 // set the buffer to be used for stdout
189 {
190 Tcl_Channel
191 channel;
192
193 Tcl_Eval(localInterpreter,"close stdout"); // if a stdout has been opened, close it ignore any error
194 channel=Tcl_CreateChannel(&stdoutChannelType,"stdout",NULL,TCL_WRITABLE);
195 Tcl_RegisterChannel(localInterpreter,channel);
196 Tcl_SetChannelOption(localInterpreter,channel,"-buffering","none"); // don't buffer stderr, write as soon as you get data (Tcl convention)
197 Tcl_SetChannelOption(localInterpreter,channel,"-translation","lf"); // we only want to see linefeeds in our output
198 Tcl_ResetResult(localInterpreter); // do not leave error messages hanging around
199
200 EditorReleaseBufferHandle(&stdoutBufferHandle); // release our grip on any buffer we held
201 if(newBuffer)
202 {
203 EditorGrabBufferHandle(&stdoutBufferHandle,newBuffer); // hang onto this one
204 }
205 }
206
GetTclStderrBuffer(Tcl_Interp * localInterpreter)207 EDITOR_BUFFER *GetTclStderrBuffer(Tcl_Interp *localInterpreter)
208 // set the buffer to be used for stderr
209 {
210 return(EditorGetBufferFromHandle(&stderrBufferHandle));
211 }
212
SetTclStderrBuffer(Tcl_Interp * localInterpreter,EDITOR_BUFFER * newBuffer)213 void SetTclStderrBuffer(Tcl_Interp *localInterpreter,EDITOR_BUFFER *newBuffer)
214 // set the buffer to be used for stderr
215 {
216 Tcl_Channel
217 channel;
218
219 Tcl_Eval(localInterpreter,"close stderr"); // if a stderr has been opened, close it ignore any error
220 channel=Tcl_CreateChannel(&stderrChannelType,"stderr",NULL,TCL_WRITABLE);
221 Tcl_RegisterChannel(localInterpreter,channel);
222 Tcl_SetChannelOption(localInterpreter,channel,"-buffering","none"); // don't buffer stderr, write as soon as you get data (Tcl convention)
223 Tcl_SetChannelOption(localInterpreter,channel,"-translation","lf"); // we only want to see linefeeds in our output
224 Tcl_ResetResult(localInterpreter); // do not leave error messages hanging around
225
226 EditorReleaseBufferHandle(&stderrBufferHandle); // release our grip on any buffer we held
227 if(newBuffer)
228 {
229 EditorGrabBufferHandle(&stderrBufferHandle,newBuffer); // hang onto this one
230 }
231 }
232
UnInitChannels(Tcl_Interp * localInterpreter)233 void UnInitChannels(Tcl_Interp *localInterpreter)
234 // undo what InitChannels did
235 {
236 // SetTclStderrBuffer(localInterpreter,NULL);
237 // SetTclStdoutBuffer(localInterpreter,NULL);
238 }
239
InitChannels(Tcl_Interp * localInterpreter)240 bool InitChannels(Tcl_Interp *localInterpreter)
241 {
242 EditorInitBufferHandle(&stdoutBufferHandle);
243 EditorInitBufferHandle(&stderrBufferHandle);
244
245 // start with no channels (this will mess up stderr for the editor)
246 // SetTclStdoutBuffer(localInterpreter,NULL);
247 // SetTclStderrBuffer(localInterpreter,NULL);
248
249 return(true);
250 }
251