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