1 | /* |
---|
2 | * tclIOCmd.c -- |
---|
3 | * |
---|
4 | * Contains the definitions of most of the Tcl commands relating to IO. |
---|
5 | * |
---|
6 | * Copyright (c) 1995-1997 Sun Microsystems, Inc. |
---|
7 | * |
---|
8 | * See the file "license.terms" for information on usage and redistribution of |
---|
9 | * this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
10 | * |
---|
11 | * RCS: @(#) $Id: tclIOCmd.c,v 1.51 2007/12/13 15:23:18 dgp Exp $ |
---|
12 | */ |
---|
13 | |
---|
14 | #include "tclInt.h" |
---|
15 | |
---|
16 | /* |
---|
17 | * Callback structure for accept callback in a TCP server. |
---|
18 | */ |
---|
19 | |
---|
20 | typedef struct AcceptCallback { |
---|
21 | char *script; /* Script to invoke. */ |
---|
22 | Tcl_Interp *interp; /* Interpreter in which to run it. */ |
---|
23 | } AcceptCallback; |
---|
24 | |
---|
25 | /* |
---|
26 | * Thread local storage used to maintain a per-thread stdout channel obj. |
---|
27 | * It must be per-thread because of std channel limitations. |
---|
28 | */ |
---|
29 | |
---|
30 | typedef struct ThreadSpecificData { |
---|
31 | int initialized; /* Set to 1 when the module is initialized. */ |
---|
32 | Tcl_Obj *stdoutObjPtr; /* Cached stdout channel Tcl_Obj */ |
---|
33 | } ThreadSpecificData; |
---|
34 | |
---|
35 | static Tcl_ThreadDataKey dataKey; |
---|
36 | |
---|
37 | /* |
---|
38 | * Static functions for this file: |
---|
39 | */ |
---|
40 | |
---|
41 | static void FinalizeIOCmdTSD(ClientData clientData); |
---|
42 | static void AcceptCallbackProc(ClientData callbackData, |
---|
43 | Tcl_Channel chan, char *address, int port); |
---|
44 | static int ChanPendingObjCmd(ClientData unused, |
---|
45 | Tcl_Interp *interp, int objc, |
---|
46 | Tcl_Obj *const objv[]); |
---|
47 | static int ChanTruncateObjCmd(ClientData dummy, |
---|
48 | Tcl_Interp *interp, int objc, |
---|
49 | Tcl_Obj *const objv[]); |
---|
50 | static void RegisterTcpServerInterpCleanup(Tcl_Interp *interp, |
---|
51 | AcceptCallback *acceptCallbackPtr); |
---|
52 | static void TcpAcceptCallbacksDeleteProc(ClientData clientData, |
---|
53 | Tcl_Interp *interp); |
---|
54 | static void TcpServerCloseProc(ClientData callbackData); |
---|
55 | static void UnregisterTcpServerInterpCleanupProc( |
---|
56 | Tcl_Interp *interp, |
---|
57 | AcceptCallback *acceptCallbackPtr); |
---|
58 | |
---|
59 | /* |
---|
60 | *---------------------------------------------------------------------- |
---|
61 | * |
---|
62 | * FinalizeIOCmdTSD -- |
---|
63 | * |
---|
64 | * Release the storage associated with the per-thread cache. |
---|
65 | * |
---|
66 | * Results: |
---|
67 | * None. |
---|
68 | * |
---|
69 | * Side effects: |
---|
70 | * None. |
---|
71 | * |
---|
72 | *---------------------------------------------------------------------- |
---|
73 | */ |
---|
74 | |
---|
75 | static void |
---|
76 | FinalizeIOCmdTSD( |
---|
77 | ClientData clientData) /* Not used. */ |
---|
78 | { |
---|
79 | ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); |
---|
80 | |
---|
81 | if (tsdPtr->stdoutObjPtr != NULL) { |
---|
82 | Tcl_DecrRefCount(tsdPtr->stdoutObjPtr); |
---|
83 | tsdPtr->stdoutObjPtr = NULL; |
---|
84 | } |
---|
85 | tsdPtr->initialized = 0; |
---|
86 | } |
---|
87 | |
---|
88 | /* |
---|
89 | *---------------------------------------------------------------------- |
---|
90 | * |
---|
91 | * Tcl_PutsObjCmd -- |
---|
92 | * |
---|
93 | * This function is invoked to process the "puts" Tcl command. See the |
---|
94 | * user documentation for details on what it does. |
---|
95 | * |
---|
96 | * Results: |
---|
97 | * A standard Tcl result. |
---|
98 | * |
---|
99 | * Side effects: |
---|
100 | * Produces output on a channel. |
---|
101 | * |
---|
102 | *---------------------------------------------------------------------- |
---|
103 | */ |
---|
104 | |
---|
105 | /* ARGSUSED */ |
---|
106 | int |
---|
107 | Tcl_PutsObjCmd( |
---|
108 | ClientData dummy, /* Not used. */ |
---|
109 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
110 | int objc, /* Number of arguments. */ |
---|
111 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
112 | { |
---|
113 | Tcl_Channel chan; /* The channel to puts on. */ |
---|
114 | Tcl_Obj *string; /* String to write. */ |
---|
115 | Tcl_Obj *chanObjPtr = NULL; /* channel object. */ |
---|
116 | int newline; /* Add a newline at end? */ |
---|
117 | int result; /* Result of puts operation. */ |
---|
118 | int mode; /* Mode in which channel is opened. */ |
---|
119 | ThreadSpecificData *tsdPtr; |
---|
120 | |
---|
121 | switch (objc) { |
---|
122 | case 2: /* [puts $x] */ |
---|
123 | string = objv[1]; |
---|
124 | newline = 1; |
---|
125 | break; |
---|
126 | |
---|
127 | case 3: /* [puts -nonewline $x] or [puts $chan $x] */ |
---|
128 | if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) { |
---|
129 | newline = 0; |
---|
130 | } else { |
---|
131 | newline = 1; |
---|
132 | chanObjPtr = objv[1]; |
---|
133 | } |
---|
134 | string = objv[2]; |
---|
135 | break; |
---|
136 | |
---|
137 | case 4: /* [puts -nonewline $chan $x] or [puts $chan $x nonewline] */ |
---|
138 | if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) { |
---|
139 | chanObjPtr = objv[2]; |
---|
140 | string = objv[3]; |
---|
141 | } else { |
---|
142 | /* |
---|
143 | * The code below provides backwards compatibility with an old |
---|
144 | * form of the command that is no longer recommended or |
---|
145 | * documented. |
---|
146 | */ |
---|
147 | |
---|
148 | char *arg; |
---|
149 | int length; |
---|
150 | |
---|
151 | arg = TclGetStringFromObj(objv[3], &length); |
---|
152 | if ((length != 9) |
---|
153 | || (strncmp(arg, "nonewline", (size_t) length) != 0)) { |
---|
154 | Tcl_AppendResult(interp, "bad argument \"", arg, |
---|
155 | "\": should be \"nonewline\"", NULL); |
---|
156 | return TCL_ERROR; |
---|
157 | } |
---|
158 | chanObjPtr = objv[1]; |
---|
159 | string = objv[2]; |
---|
160 | } |
---|
161 | newline = 0; |
---|
162 | break; |
---|
163 | |
---|
164 | default: |
---|
165 | /* [puts] or [puts some bad number of arguments...] */ |
---|
166 | Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string"); |
---|
167 | return TCL_ERROR; |
---|
168 | } |
---|
169 | |
---|
170 | if (chanObjPtr == NULL) { |
---|
171 | tsdPtr = TCL_TSD_INIT(&dataKey); |
---|
172 | |
---|
173 | if (!tsdPtr->initialized) { |
---|
174 | tsdPtr->initialized = 1; |
---|
175 | TclNewLiteralStringObj(tsdPtr->stdoutObjPtr, "stdout"); |
---|
176 | Tcl_IncrRefCount(tsdPtr->stdoutObjPtr); |
---|
177 | Tcl_CreateThreadExitHandler(FinalizeIOCmdTSD, NULL); |
---|
178 | } |
---|
179 | chanObjPtr = tsdPtr->stdoutObjPtr; |
---|
180 | } |
---|
181 | if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { |
---|
182 | return TCL_ERROR; |
---|
183 | } |
---|
184 | if ((mode & TCL_WRITABLE) == 0) { |
---|
185 | Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr), |
---|
186 | "\" wasn't opened for writing", NULL); |
---|
187 | return TCL_ERROR; |
---|
188 | } |
---|
189 | |
---|
190 | result = Tcl_WriteObj(chan, string); |
---|
191 | if (result < 0) { |
---|
192 | goto error; |
---|
193 | } |
---|
194 | if (newline != 0) { |
---|
195 | result = Tcl_WriteChars(chan, "\n", 1); |
---|
196 | if (result < 0) { |
---|
197 | goto error; |
---|
198 | } |
---|
199 | } |
---|
200 | return TCL_OK; |
---|
201 | |
---|
202 | /* |
---|
203 | * TIP #219. |
---|
204 | * Capture error messages put by the driver into the bypass area and put |
---|
205 | * them into the regular interpreter result. Fall back to the regular |
---|
206 | * message if nothing was found in the bypass. |
---|
207 | */ |
---|
208 | |
---|
209 | error: |
---|
210 | if (!TclChanCaughtErrorBypass(interp, chan)) { |
---|
211 | Tcl_AppendResult(interp, "error writing \"", |
---|
212 | TclGetString(chanObjPtr), "\": ", |
---|
213 | Tcl_PosixError(interp), NULL); |
---|
214 | } |
---|
215 | return TCL_ERROR; |
---|
216 | } |
---|
217 | |
---|
218 | /* |
---|
219 | *---------------------------------------------------------------------- |
---|
220 | * |
---|
221 | * Tcl_FlushObjCmd -- |
---|
222 | * |
---|
223 | * This function is called to process the Tcl "flush" command. See the |
---|
224 | * user documentation for details on what it does. |
---|
225 | * |
---|
226 | * Results: |
---|
227 | * A standard Tcl result. |
---|
228 | * |
---|
229 | * Side effects: |
---|
230 | * May cause output to appear on the specified channel. |
---|
231 | * |
---|
232 | *---------------------------------------------------------------------- |
---|
233 | */ |
---|
234 | |
---|
235 | /* ARGSUSED */ |
---|
236 | int |
---|
237 | Tcl_FlushObjCmd( |
---|
238 | ClientData dummy, /* Not used. */ |
---|
239 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
240 | int objc, /* Number of arguments. */ |
---|
241 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
242 | { |
---|
243 | Tcl_Obj *chanObjPtr; |
---|
244 | Tcl_Channel chan; /* The channel to flush on. */ |
---|
245 | int mode; |
---|
246 | |
---|
247 | if (objc != 2) { |
---|
248 | Tcl_WrongNumArgs(interp, 1, objv, "channelId"); |
---|
249 | return TCL_ERROR; |
---|
250 | } |
---|
251 | chanObjPtr = objv[1]; |
---|
252 | if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { |
---|
253 | return TCL_ERROR; |
---|
254 | } |
---|
255 | if ((mode & TCL_WRITABLE) == 0) { |
---|
256 | Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr), |
---|
257 | "\" wasn't opened for writing", NULL); |
---|
258 | return TCL_ERROR; |
---|
259 | } |
---|
260 | |
---|
261 | if (Tcl_Flush(chan) != TCL_OK) { |
---|
262 | /* |
---|
263 | * TIP #219. |
---|
264 | * Capture error messages put by the driver into the bypass area and |
---|
265 | * put them into the regular interpreter result. Fall back to the |
---|
266 | * regular message if nothing was found in the bypass. |
---|
267 | */ |
---|
268 | |
---|
269 | if (!TclChanCaughtErrorBypass(interp, chan)) { |
---|
270 | Tcl_AppendResult(interp, "error flushing \"", |
---|
271 | TclGetString(chanObjPtr), "\": ", |
---|
272 | Tcl_PosixError(interp), NULL); |
---|
273 | } |
---|
274 | return TCL_ERROR; |
---|
275 | } |
---|
276 | return TCL_OK; |
---|
277 | } |
---|
278 | |
---|
279 | /* |
---|
280 | *---------------------------------------------------------------------- |
---|
281 | * |
---|
282 | * Tcl_GetsObjCmd -- |
---|
283 | * |
---|
284 | * This function is called to process the Tcl "gets" command. See the |
---|
285 | * user documentation for details on what it does. |
---|
286 | * |
---|
287 | * Results: |
---|
288 | * A standard Tcl result. |
---|
289 | * |
---|
290 | * Side effects: |
---|
291 | * May consume input from channel. |
---|
292 | * |
---|
293 | *---------------------------------------------------------------------- |
---|
294 | */ |
---|
295 | |
---|
296 | /* ARGSUSED */ |
---|
297 | int |
---|
298 | Tcl_GetsObjCmd( |
---|
299 | ClientData dummy, /* Not used. */ |
---|
300 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
301 | int objc, /* Number of arguments. */ |
---|
302 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
303 | { |
---|
304 | Tcl_Channel chan; /* The channel to read from. */ |
---|
305 | int lineLen; /* Length of line just read. */ |
---|
306 | int mode; /* Mode in which channel is opened. */ |
---|
307 | Tcl_Obj *linePtr, *chanObjPtr; |
---|
308 | |
---|
309 | if ((objc != 2) && (objc != 3)) { |
---|
310 | Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?"); |
---|
311 | return TCL_ERROR; |
---|
312 | } |
---|
313 | chanObjPtr = objv[1]; |
---|
314 | if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { |
---|
315 | return TCL_ERROR; |
---|
316 | } |
---|
317 | if ((mode & TCL_READABLE) == 0) { |
---|
318 | Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr), |
---|
319 | "\" wasn't opened for reading", NULL); |
---|
320 | return TCL_ERROR; |
---|
321 | } |
---|
322 | |
---|
323 | linePtr = Tcl_NewObj(); |
---|
324 | lineLen = Tcl_GetsObj(chan, linePtr); |
---|
325 | if (lineLen < 0) { |
---|
326 | if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) { |
---|
327 | Tcl_DecrRefCount(linePtr); |
---|
328 | |
---|
329 | /* |
---|
330 | * TIP #219. Capture error messages put by the driver into the |
---|
331 | * bypass area and put them into the regular interpreter result. |
---|
332 | * Fall back to the regular message if nothing was found in the |
---|
333 | * bypass. |
---|
334 | */ |
---|
335 | |
---|
336 | if (!TclChanCaughtErrorBypass(interp, chan)) { |
---|
337 | Tcl_ResetResult(interp); |
---|
338 | Tcl_AppendResult(interp, "error reading \"", |
---|
339 | TclGetString(chanObjPtr), "\": ", |
---|
340 | Tcl_PosixError(interp), NULL); |
---|
341 | } |
---|
342 | return TCL_ERROR; |
---|
343 | } |
---|
344 | lineLen = -1; |
---|
345 | } |
---|
346 | if (objc == 3) { |
---|
347 | if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr, |
---|
348 | TCL_LEAVE_ERR_MSG) == NULL) { |
---|
349 | return TCL_ERROR; |
---|
350 | } |
---|
351 | Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen)); |
---|
352 | return TCL_OK; |
---|
353 | } else { |
---|
354 | Tcl_SetObjResult(interp, linePtr); |
---|
355 | } |
---|
356 | return TCL_OK; |
---|
357 | } |
---|
358 | |
---|
359 | /* |
---|
360 | *---------------------------------------------------------------------- |
---|
361 | * |
---|
362 | * Tcl_ReadObjCmd -- |
---|
363 | * |
---|
364 | * This function is invoked to process the Tcl "read" command. See the |
---|
365 | * user documentation for details on what it does. |
---|
366 | * |
---|
367 | * Results: |
---|
368 | * A standard Tcl result. |
---|
369 | * |
---|
370 | * Side effects: |
---|
371 | * May consume input from channel. |
---|
372 | * |
---|
373 | *---------------------------------------------------------------------- |
---|
374 | */ |
---|
375 | |
---|
376 | /* ARGSUSED */ |
---|
377 | int |
---|
378 | Tcl_ReadObjCmd( |
---|
379 | ClientData dummy, /* Not used. */ |
---|
380 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
381 | int objc, /* Number of arguments. */ |
---|
382 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
383 | { |
---|
384 | Tcl_Channel chan; /* The channel to read from. */ |
---|
385 | int newline, i; /* Discard newline at end? */ |
---|
386 | int toRead; /* How many bytes to read? */ |
---|
387 | int charactersRead; /* How many characters were read? */ |
---|
388 | int mode; /* Mode in which channel is opened. */ |
---|
389 | Tcl_Obj *resultPtr, *chanObjPtr; |
---|
390 | |
---|
391 | if ((objc != 2) && (objc != 3)) { |
---|
392 | Interp *iPtr; |
---|
393 | |
---|
394 | argerror: |
---|
395 | iPtr = (Interp *) interp; |
---|
396 | Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numChars?"); |
---|
397 | |
---|
398 | /* |
---|
399 | * Do not append directly; that makes ensembles using this command as |
---|
400 | * a subcommand produce the wrong message. |
---|
401 | */ |
---|
402 | |
---|
403 | iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS; |
---|
404 | Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? channelId"); |
---|
405 | iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS; |
---|
406 | return TCL_ERROR; |
---|
407 | } |
---|
408 | |
---|
409 | i = 1; |
---|
410 | newline = 0; |
---|
411 | if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) { |
---|
412 | newline = 1; |
---|
413 | i++; |
---|
414 | } |
---|
415 | |
---|
416 | if (i == objc) { |
---|
417 | goto argerror; |
---|
418 | } |
---|
419 | |
---|
420 | chanObjPtr = objv[i]; |
---|
421 | if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) { |
---|
422 | return TCL_ERROR; |
---|
423 | } |
---|
424 | if ((mode & TCL_READABLE) == 0) { |
---|
425 | Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr), |
---|
426 | "\" wasn't opened for reading", NULL); |
---|
427 | return TCL_ERROR; |
---|
428 | } |
---|
429 | i++; /* Consumed channel name. */ |
---|
430 | |
---|
431 | /* |
---|
432 | * Compute how many bytes to read, and see whether the final newline |
---|
433 | * should be dropped. |
---|
434 | */ |
---|
435 | |
---|
436 | toRead = -1; |
---|
437 | if (i < objc) { |
---|
438 | char *arg; |
---|
439 | |
---|
440 | arg = TclGetString(objv[i]); |
---|
441 | if (isdigit(UCHAR(arg[0]))) { /* INTL: digit */ |
---|
442 | if (TclGetIntFromObj(interp, objv[i], &toRead) != TCL_OK) { |
---|
443 | return TCL_ERROR; |
---|
444 | } |
---|
445 | } else if (strcmp(arg, "nonewline") == 0) { |
---|
446 | newline = 1; |
---|
447 | } else { |
---|
448 | Tcl_AppendResult(interp, "bad argument \"", arg, |
---|
449 | "\": should be \"nonewline\"", NULL); |
---|
450 | return TCL_ERROR; |
---|
451 | } |
---|
452 | } |
---|
453 | |
---|
454 | resultPtr = Tcl_NewObj(); |
---|
455 | Tcl_IncrRefCount(resultPtr); |
---|
456 | charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0); |
---|
457 | if (charactersRead < 0) { |
---|
458 | /* |
---|
459 | * TIP #219. |
---|
460 | * Capture error messages put by the driver into the bypass area and |
---|
461 | * put them into the regular interpreter result. Fall back to the |
---|
462 | * regular message if nothing was found in the bypass. |
---|
463 | */ |
---|
464 | |
---|
465 | if (!TclChanCaughtErrorBypass(interp, chan)) { |
---|
466 | Tcl_ResetResult(interp); |
---|
467 | Tcl_AppendResult(interp, "error reading \"", |
---|
468 | TclGetString(chanObjPtr), "\": ", |
---|
469 | Tcl_PosixError(interp), NULL); |
---|
470 | } |
---|
471 | Tcl_DecrRefCount(resultPtr); |
---|
472 | return TCL_ERROR; |
---|
473 | } |
---|
474 | |
---|
475 | /* |
---|
476 | * If requested, remove the last newline in the channel if at EOF. |
---|
477 | */ |
---|
478 | |
---|
479 | if ((charactersRead > 0) && (newline != 0)) { |
---|
480 | char *result; |
---|
481 | int length; |
---|
482 | |
---|
483 | result = TclGetStringFromObj(resultPtr, &length); |
---|
484 | if (result[length - 1] == '\n') { |
---|
485 | Tcl_SetObjLength(resultPtr, length - 1); |
---|
486 | } |
---|
487 | } |
---|
488 | Tcl_SetObjResult(interp, resultPtr); |
---|
489 | Tcl_DecrRefCount(resultPtr); |
---|
490 | return TCL_OK; |
---|
491 | } |
---|
492 | |
---|
493 | /* |
---|
494 | *---------------------------------------------------------------------- |
---|
495 | * |
---|
496 | * Tcl_SeekObjCmd -- |
---|
497 | * |
---|
498 | * This function is invoked to process the Tcl "seek" command. See the |
---|
499 | * user documentation for details on what it does. |
---|
500 | * |
---|
501 | * Results: |
---|
502 | * A standard Tcl result. |
---|
503 | * |
---|
504 | * Side effects: |
---|
505 | * Moves the position of the access point on the specified channel. May |
---|
506 | * flush queued output. |
---|
507 | * |
---|
508 | *---------------------------------------------------------------------- |
---|
509 | */ |
---|
510 | |
---|
511 | /* ARGSUSED */ |
---|
512 | int |
---|
513 | Tcl_SeekObjCmd( |
---|
514 | ClientData clientData, /* Not used. */ |
---|
515 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
516 | int objc, /* Number of arguments. */ |
---|
517 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
518 | { |
---|
519 | Tcl_Channel chan; /* The channel to tell on. */ |
---|
520 | Tcl_WideInt offset; /* Where to seek? */ |
---|
521 | int mode; /* How to seek? */ |
---|
522 | Tcl_WideInt result; /* Of calling Tcl_Seek. */ |
---|
523 | int optionIndex; |
---|
524 | static const char *originOptions[] = { |
---|
525 | "start", "current", "end", NULL |
---|
526 | }; |
---|
527 | static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END}; |
---|
528 | |
---|
529 | if ((objc != 3) && (objc != 4)) { |
---|
530 | Tcl_WrongNumArgs(interp, 1, objv, "channelId offset ?origin?"); |
---|
531 | return TCL_ERROR; |
---|
532 | } |
---|
533 | if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { |
---|
534 | return TCL_ERROR; |
---|
535 | } |
---|
536 | if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) { |
---|
537 | return TCL_ERROR; |
---|
538 | } |
---|
539 | mode = SEEK_SET; |
---|
540 | if (objc == 4) { |
---|
541 | if (Tcl_GetIndexFromObj(interp, objv[3], originOptions, "origin", 0, |
---|
542 | &optionIndex) != TCL_OK) { |
---|
543 | return TCL_ERROR; |
---|
544 | } |
---|
545 | mode = modeArray[optionIndex]; |
---|
546 | } |
---|
547 | |
---|
548 | result = Tcl_Seek(chan, offset, mode); |
---|
549 | if (result == Tcl_LongAsWide(-1)) { |
---|
550 | /* |
---|
551 | * TIP #219. |
---|
552 | * Capture error messages put by the driver into the bypass area and |
---|
553 | * put them into the regular interpreter result. Fall back to the |
---|
554 | * regular message if nothing was found in the bypass. |
---|
555 | */ |
---|
556 | if (!TclChanCaughtErrorBypass(interp, chan)) { |
---|
557 | Tcl_AppendResult(interp, "error during seek on \"", |
---|
558 | TclGetString(objv[1]), "\": ", |
---|
559 | Tcl_PosixError(interp), NULL); |
---|
560 | } |
---|
561 | return TCL_ERROR; |
---|
562 | } |
---|
563 | return TCL_OK; |
---|
564 | } |
---|
565 | |
---|
566 | /* |
---|
567 | *---------------------------------------------------------------------- |
---|
568 | * |
---|
569 | * Tcl_TellObjCmd -- |
---|
570 | * |
---|
571 | * This function is invoked to process the Tcl "tell" command. See the |
---|
572 | * user documentation for details on what it does. |
---|
573 | * |
---|
574 | * Results: |
---|
575 | * A standard Tcl result. |
---|
576 | * |
---|
577 | * Side effects: |
---|
578 | * None. |
---|
579 | * |
---|
580 | *---------------------------------------------------------------------- |
---|
581 | */ |
---|
582 | |
---|
583 | /* ARGSUSED */ |
---|
584 | int |
---|
585 | Tcl_TellObjCmd( |
---|
586 | ClientData clientData, /* Not used. */ |
---|
587 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
588 | int objc, /* Number of arguments. */ |
---|
589 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
590 | { |
---|
591 | Tcl_Channel chan; /* The channel to tell on. */ |
---|
592 | Tcl_WideInt newLoc; |
---|
593 | |
---|
594 | if (objc != 2) { |
---|
595 | Tcl_WrongNumArgs(interp, 1, objv, "channelId"); |
---|
596 | return TCL_ERROR; |
---|
597 | } |
---|
598 | |
---|
599 | /* |
---|
600 | * Try to find a channel with the right name and permissions in the IO |
---|
601 | * channel table of this interpreter. |
---|
602 | */ |
---|
603 | |
---|
604 | if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { |
---|
605 | return TCL_ERROR; |
---|
606 | } |
---|
607 | |
---|
608 | newLoc = Tcl_Tell(chan); |
---|
609 | |
---|
610 | /* |
---|
611 | * TIP #219. |
---|
612 | * Capture error messages put by the driver into the bypass area and put |
---|
613 | * them into the regular interpreter result. |
---|
614 | */ |
---|
615 | |
---|
616 | if (TclChanCaughtErrorBypass(interp, chan)) { |
---|
617 | return TCL_ERROR; |
---|
618 | } |
---|
619 | |
---|
620 | Tcl_SetObjResult(interp, Tcl_NewWideIntObj(newLoc)); |
---|
621 | return TCL_OK; |
---|
622 | } |
---|
623 | |
---|
624 | /* |
---|
625 | *---------------------------------------------------------------------- |
---|
626 | * |
---|
627 | * Tcl_CloseObjCmd -- |
---|
628 | * |
---|
629 | * This function is invoked to process the Tcl "close" command. See the |
---|
630 | * user documentation for details on what it does. |
---|
631 | * |
---|
632 | * Results: |
---|
633 | * A standard Tcl result. |
---|
634 | * |
---|
635 | * Side effects: |
---|
636 | * May discard queued input; may flush queued output. |
---|
637 | * |
---|
638 | *---------------------------------------------------------------------- |
---|
639 | */ |
---|
640 | |
---|
641 | /* ARGSUSED */ |
---|
642 | int |
---|
643 | Tcl_CloseObjCmd( |
---|
644 | ClientData clientData, /* Not used. */ |
---|
645 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
646 | int objc, /* Number of arguments. */ |
---|
647 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
648 | { |
---|
649 | Tcl_Channel chan; /* The channel to close. */ |
---|
650 | |
---|
651 | if (objc != 2) { |
---|
652 | Tcl_WrongNumArgs(interp, 1, objv, "channelId"); |
---|
653 | return TCL_ERROR; |
---|
654 | } |
---|
655 | |
---|
656 | if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { |
---|
657 | return TCL_ERROR; |
---|
658 | } |
---|
659 | |
---|
660 | if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) { |
---|
661 | /* |
---|
662 | * If there is an error message and it ends with a newline, remove the |
---|
663 | * newline. This is done for command pipeline channels where the error |
---|
664 | * output from the subprocesses is stored in interp's result. |
---|
665 | * |
---|
666 | * NOTE: This is likely to not have any effect on regular error |
---|
667 | * messages produced by drivers during the closing of a channel, |
---|
668 | * because the Tcl convention is that such error messages do not have |
---|
669 | * a terminating newline. |
---|
670 | */ |
---|
671 | |
---|
672 | Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); |
---|
673 | char *string; |
---|
674 | int len; |
---|
675 | |
---|
676 | if (Tcl_IsShared(resultPtr)) { |
---|
677 | resultPtr = Tcl_DuplicateObj(resultPtr); |
---|
678 | Tcl_SetObjResult(interp, resultPtr); |
---|
679 | } |
---|
680 | string = TclGetStringFromObj(resultPtr, &len); |
---|
681 | if ((len > 0) && (string[len - 1] == '\n')) { |
---|
682 | Tcl_SetObjLength(resultPtr, len - 1); |
---|
683 | } |
---|
684 | return TCL_ERROR; |
---|
685 | } |
---|
686 | |
---|
687 | return TCL_OK; |
---|
688 | } |
---|
689 | |
---|
690 | /* |
---|
691 | *---------------------------------------------------------------------- |
---|
692 | * |
---|
693 | * Tcl_FconfigureObjCmd -- |
---|
694 | * |
---|
695 | * This function is invoked to process the Tcl "fconfigure" command. See |
---|
696 | * the user documentation for details on what it does. |
---|
697 | * |
---|
698 | * Results: |
---|
699 | * A standard Tcl result. |
---|
700 | * |
---|
701 | * Side effects: |
---|
702 | * May modify the behavior of an IO channel. |
---|
703 | * |
---|
704 | *---------------------------------------------------------------------- |
---|
705 | */ |
---|
706 | |
---|
707 | /* ARGSUSED */ |
---|
708 | int |
---|
709 | Tcl_FconfigureObjCmd( |
---|
710 | ClientData clientData, /* Not used. */ |
---|
711 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
712 | int objc, /* Number of arguments. */ |
---|
713 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
714 | { |
---|
715 | char *optionName, *valueName; |
---|
716 | Tcl_Channel chan; /* The channel to set a mode on. */ |
---|
717 | int i; /* Iterate over arg-value pairs. */ |
---|
718 | |
---|
719 | if ((objc < 2) || (((objc % 2) == 1) && (objc != 3))) { |
---|
720 | Tcl_WrongNumArgs(interp, 1, objv, |
---|
721 | "channelId ?optionName? ?value? ?optionName value?..."); |
---|
722 | return TCL_ERROR; |
---|
723 | } |
---|
724 | |
---|
725 | if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { |
---|
726 | return TCL_ERROR; |
---|
727 | } |
---|
728 | |
---|
729 | if (objc == 2) { |
---|
730 | Tcl_DString ds; /* DString to hold result of calling |
---|
731 | * Tcl_GetChannelOption. */ |
---|
732 | |
---|
733 | Tcl_DStringInit(&ds); |
---|
734 | if (Tcl_GetChannelOption(interp, chan, NULL, &ds) != TCL_OK) { |
---|
735 | Tcl_DStringFree(&ds); |
---|
736 | return TCL_ERROR; |
---|
737 | } |
---|
738 | Tcl_DStringResult(interp, &ds); |
---|
739 | return TCL_OK; |
---|
740 | } else if (objc == 3) { |
---|
741 | Tcl_DString ds; /* DString to hold result of calling |
---|
742 | * Tcl_GetChannelOption. */ |
---|
743 | |
---|
744 | Tcl_DStringInit(&ds); |
---|
745 | optionName = TclGetString(objv[2]); |
---|
746 | if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) { |
---|
747 | Tcl_DStringFree(&ds); |
---|
748 | return TCL_ERROR; |
---|
749 | } |
---|
750 | Tcl_DStringResult(interp, &ds); |
---|
751 | return TCL_OK; |
---|
752 | } |
---|
753 | |
---|
754 | for (i = 3; i < objc; i += 2) { |
---|
755 | optionName = TclGetString(objv[i-1]); |
---|
756 | valueName = TclGetString(objv[i]); |
---|
757 | if (Tcl_SetChannelOption(interp, chan, optionName, valueName) |
---|
758 | != TCL_OK) { |
---|
759 | return TCL_ERROR; |
---|
760 | } |
---|
761 | } |
---|
762 | |
---|
763 | return TCL_OK; |
---|
764 | } |
---|
765 | |
---|
766 | /* |
---|
767 | *--------------------------------------------------------------------------- |
---|
768 | * |
---|
769 | * Tcl_EofObjCmd -- |
---|
770 | * |
---|
771 | * This function is invoked to process the Tcl "eof" command. See the |
---|
772 | * user documentation for details on what it does. |
---|
773 | * |
---|
774 | * Results: |
---|
775 | * A standard Tcl result. |
---|
776 | * |
---|
777 | * Side effects: |
---|
778 | * Sets interp's result to boolean true or false depending on whether the |
---|
779 | * specified channel has an EOF condition. |
---|
780 | * |
---|
781 | *--------------------------------------------------------------------------- |
---|
782 | */ |
---|
783 | |
---|
784 | /* ARGSUSED */ |
---|
785 | int |
---|
786 | Tcl_EofObjCmd( |
---|
787 | ClientData unused, /* Not used. */ |
---|
788 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
789 | int objc, /* Number of arguments. */ |
---|
790 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
791 | { |
---|
792 | Tcl_Channel chan; |
---|
793 | |
---|
794 | if (objc != 2) { |
---|
795 | Tcl_WrongNumArgs(interp, 1, objv, "channelId"); |
---|
796 | return TCL_ERROR; |
---|
797 | } |
---|
798 | |
---|
799 | if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { |
---|
800 | return TCL_ERROR; |
---|
801 | } |
---|
802 | |
---|
803 | Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_Eof(chan))); |
---|
804 | return TCL_OK; |
---|
805 | } |
---|
806 | |
---|
807 | /* |
---|
808 | *---------------------------------------------------------------------- |
---|
809 | * |
---|
810 | * Tcl_ExecObjCmd -- |
---|
811 | * |
---|
812 | * This function is invoked to process the "exec" Tcl command. See the |
---|
813 | * user documentation for details on what it does. |
---|
814 | * |
---|
815 | * Results: |
---|
816 | * A standard Tcl result. |
---|
817 | * |
---|
818 | * Side effects: |
---|
819 | * See the user documentation. |
---|
820 | * |
---|
821 | *---------------------------------------------------------------------- |
---|
822 | */ |
---|
823 | |
---|
824 | /* ARGSUSED */ |
---|
825 | int |
---|
826 | Tcl_ExecObjCmd( |
---|
827 | ClientData dummy, /* Not used. */ |
---|
828 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
829 | int objc, /* Number of arguments. */ |
---|
830 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
831 | { |
---|
832 | /* |
---|
833 | * This function generates an argv array for the string arguments. It |
---|
834 | * starts out with stack-allocated space but uses dynamically-allocated |
---|
835 | * storage if needed. |
---|
836 | */ |
---|
837 | |
---|
838 | Tcl_Obj *resultPtr; |
---|
839 | const char **argv; |
---|
840 | char *string; |
---|
841 | Tcl_Channel chan; |
---|
842 | int argc, background, i, index, keepNewline, result, skip, length; |
---|
843 | int ignoreStderr; |
---|
844 | static const char *options[] = { |
---|
845 | "-ignorestderr", "-keepnewline", "--", NULL |
---|
846 | }; |
---|
847 | enum options { |
---|
848 | EXEC_IGNORESTDERR, EXEC_KEEPNEWLINE, EXEC_LAST |
---|
849 | }; |
---|
850 | |
---|
851 | /* |
---|
852 | * Check for any leading option arguments. |
---|
853 | */ |
---|
854 | |
---|
855 | keepNewline = 0; |
---|
856 | ignoreStderr = 0; |
---|
857 | for (skip = 1; skip < objc; skip++) { |
---|
858 | string = TclGetString(objv[skip]); |
---|
859 | if (string[0] != '-') { |
---|
860 | break; |
---|
861 | } |
---|
862 | if (Tcl_GetIndexFromObj(interp, objv[skip], options, "switch", |
---|
863 | TCL_EXACT, &index) != TCL_OK) { |
---|
864 | return TCL_ERROR; |
---|
865 | } |
---|
866 | if (index == EXEC_KEEPNEWLINE) { |
---|
867 | keepNewline = 1; |
---|
868 | } else if (index == EXEC_IGNORESTDERR) { |
---|
869 | ignoreStderr = 1; |
---|
870 | } else { |
---|
871 | skip++; |
---|
872 | break; |
---|
873 | } |
---|
874 | } |
---|
875 | if (objc <= skip) { |
---|
876 | Tcl_WrongNumArgs(interp, 1, objv, "?switches? arg ?arg ...?"); |
---|
877 | return TCL_ERROR; |
---|
878 | } |
---|
879 | |
---|
880 | /* |
---|
881 | * See if the command is to be run in background. |
---|
882 | */ |
---|
883 | |
---|
884 | background = 0; |
---|
885 | string = TclGetString(objv[objc - 1]); |
---|
886 | if ((string[0] == '&') && (string[1] == '\0')) { |
---|
887 | objc--; |
---|
888 | background = 1; |
---|
889 | } |
---|
890 | |
---|
891 | /* |
---|
892 | * Create the string argument array "argv". Make sure argv is large enough |
---|
893 | * to hold the argc arguments plus 1 extra for the zero end-of-argv word. |
---|
894 | */ |
---|
895 | |
---|
896 | argc = objc - skip; |
---|
897 | argv = (const char **) |
---|
898 | TclStackAlloc(interp, (unsigned)(argc + 1) * sizeof(char *)); |
---|
899 | |
---|
900 | /* |
---|
901 | * Copy the string conversions of each (post option) object into the |
---|
902 | * argument vector. |
---|
903 | */ |
---|
904 | |
---|
905 | for (i = 0; i < argc; i++) { |
---|
906 | argv[i] = TclGetString(objv[i + skip]); |
---|
907 | } |
---|
908 | argv[argc] = NULL; |
---|
909 | chan = Tcl_OpenCommandChannel(interp, argc, argv, (background ? 0 : |
---|
910 | (ignoreStderr ? TCL_STDOUT : TCL_STDOUT|TCL_STDERR))); |
---|
911 | |
---|
912 | /* |
---|
913 | * Free the argv array. |
---|
914 | */ |
---|
915 | |
---|
916 | TclStackFree(interp, (void *)argv); |
---|
917 | |
---|
918 | if (chan == NULL) { |
---|
919 | return TCL_ERROR; |
---|
920 | } |
---|
921 | |
---|
922 | if (background) { |
---|
923 | /* |
---|
924 | * Store the list of PIDs from the pipeline in interp's result and |
---|
925 | * detach the PIDs (instead of waiting for them). |
---|
926 | */ |
---|
927 | |
---|
928 | TclGetAndDetachPids(interp, chan); |
---|
929 | if (Tcl_Close(interp, chan) != TCL_OK) { |
---|
930 | return TCL_ERROR; |
---|
931 | } |
---|
932 | return TCL_OK; |
---|
933 | } |
---|
934 | |
---|
935 | resultPtr = Tcl_NewObj(); |
---|
936 | if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) { |
---|
937 | if (Tcl_ReadChars(chan, resultPtr, -1, 0) < 0) { |
---|
938 | /* |
---|
939 | * TIP #219. |
---|
940 | * Capture error messages put by the driver into the bypass area |
---|
941 | * and put them into the regular interpreter result. Fall back to |
---|
942 | * the regular message if nothing was found in the bypass. |
---|
943 | */ |
---|
944 | |
---|
945 | if (!TclChanCaughtErrorBypass(interp, chan)) { |
---|
946 | Tcl_ResetResult(interp); |
---|
947 | Tcl_AppendResult(interp, "error reading output from command: ", |
---|
948 | Tcl_PosixError(interp), NULL); |
---|
949 | Tcl_DecrRefCount(resultPtr); |
---|
950 | } |
---|
951 | return TCL_ERROR; |
---|
952 | } |
---|
953 | } |
---|
954 | |
---|
955 | /* |
---|
956 | * If the process produced anything on stderr, it will have been returned |
---|
957 | * in the interpreter result. It needs to be appended to the result |
---|
958 | * string. |
---|
959 | */ |
---|
960 | |
---|
961 | result = Tcl_Close(interp, chan); |
---|
962 | Tcl_AppendObjToObj(resultPtr, Tcl_GetObjResult(interp)); |
---|
963 | |
---|
964 | /* |
---|
965 | * If the last character of the result is a newline, then remove the |
---|
966 | * newline character. |
---|
967 | */ |
---|
968 | |
---|
969 | if (keepNewline == 0) { |
---|
970 | string = TclGetStringFromObj(resultPtr, &length); |
---|
971 | if ((length > 0) && (string[length - 1] == '\n')) { |
---|
972 | Tcl_SetObjLength(resultPtr, length - 1); |
---|
973 | } |
---|
974 | } |
---|
975 | Tcl_SetObjResult(interp, resultPtr); |
---|
976 | |
---|
977 | return result; |
---|
978 | } |
---|
979 | |
---|
980 | /* |
---|
981 | *--------------------------------------------------------------------------- |
---|
982 | * |
---|
983 | * Tcl_FblockedObjCmd -- |
---|
984 | * |
---|
985 | * This function is invoked to process the Tcl "fblocked" command. See |
---|
986 | * the user documentation for details on what it does. |
---|
987 | * |
---|
988 | * Results: |
---|
989 | * A standard Tcl result. |
---|
990 | * |
---|
991 | * Side effects: |
---|
992 | * Sets interp's result to boolean true or false depending on whether the |
---|
993 | * preceeding input operation on the channel would have blocked. |
---|
994 | * |
---|
995 | *--------------------------------------------------------------------------- |
---|
996 | */ |
---|
997 | |
---|
998 | /* ARGSUSED */ |
---|
999 | int |
---|
1000 | Tcl_FblockedObjCmd( |
---|
1001 | ClientData unused, /* Not used. */ |
---|
1002 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1003 | int objc, /* Number of arguments. */ |
---|
1004 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
1005 | { |
---|
1006 | Tcl_Channel chan; |
---|
1007 | int mode; |
---|
1008 | |
---|
1009 | if (objc != 2) { |
---|
1010 | Tcl_WrongNumArgs(interp, 1, objv, "channelId"); |
---|
1011 | return TCL_ERROR; |
---|
1012 | } |
---|
1013 | |
---|
1014 | if (TclGetChannelFromObj(interp, objv[1], &chan, &mode, 0) != TCL_OK) { |
---|
1015 | return TCL_ERROR; |
---|
1016 | } |
---|
1017 | if ((mode & TCL_READABLE) == 0) { |
---|
1018 | Tcl_AppendResult(interp, "channel \"", TclGetString(objv[1]), |
---|
1019 | "\" wasn't opened for reading", NULL); |
---|
1020 | return TCL_ERROR; |
---|
1021 | } |
---|
1022 | |
---|
1023 | Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_InputBlocked(chan))); |
---|
1024 | return TCL_OK; |
---|
1025 | } |
---|
1026 | |
---|
1027 | /* |
---|
1028 | *---------------------------------------------------------------------- |
---|
1029 | * |
---|
1030 | * Tcl_OpenObjCmd -- |
---|
1031 | * |
---|
1032 | * This function is invoked to process the "open" Tcl command. See the |
---|
1033 | * user documentation for details on what it does. |
---|
1034 | * |
---|
1035 | * Results: |
---|
1036 | * A standard Tcl result. |
---|
1037 | * |
---|
1038 | * Side effects: |
---|
1039 | * See the user documentation. |
---|
1040 | * |
---|
1041 | *---------------------------------------------------------------------- |
---|
1042 | */ |
---|
1043 | |
---|
1044 | /* ARGSUSED */ |
---|
1045 | int |
---|
1046 | Tcl_OpenObjCmd( |
---|
1047 | ClientData notUsed, /* Not used. */ |
---|
1048 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1049 | int objc, /* Number of arguments. */ |
---|
1050 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
1051 | { |
---|
1052 | int pipeline, prot; |
---|
1053 | const char *modeString, *what; |
---|
1054 | Tcl_Channel chan; |
---|
1055 | |
---|
1056 | if ((objc < 2) || (objc > 4)) { |
---|
1057 | Tcl_WrongNumArgs(interp, 1, objv, "fileName ?access? ?permissions?"); |
---|
1058 | return TCL_ERROR; |
---|
1059 | } |
---|
1060 | prot = 0666; |
---|
1061 | if (objc == 2) { |
---|
1062 | modeString = "r"; |
---|
1063 | } else { |
---|
1064 | modeString = TclGetString(objv[2]); |
---|
1065 | if (objc == 4) { |
---|
1066 | char *permString = TclGetString(objv[3]); |
---|
1067 | int code = TCL_ERROR; |
---|
1068 | int scanned = TclParseAllWhiteSpace(permString, -1); |
---|
1069 | |
---|
1070 | /* Support legacy octal numbers */ |
---|
1071 | if ((permString[scanned] == '0') |
---|
1072 | && (permString[scanned+1] >= '0') |
---|
1073 | && (permString[scanned+1] <= '7')) { |
---|
1074 | |
---|
1075 | Tcl_Obj *permObj; |
---|
1076 | |
---|
1077 | TclNewLiteralStringObj(permObj, "0o"); |
---|
1078 | Tcl_AppendToObj(permObj, permString+scanned+1, -1); |
---|
1079 | code = TclGetIntFromObj(NULL, permObj, &prot); |
---|
1080 | Tcl_DecrRefCount(permObj); |
---|
1081 | } |
---|
1082 | |
---|
1083 | if ((code == TCL_ERROR) |
---|
1084 | && TclGetIntFromObj(interp, objv[3], &prot) != TCL_OK) { |
---|
1085 | return TCL_ERROR; |
---|
1086 | } |
---|
1087 | } |
---|
1088 | } |
---|
1089 | |
---|
1090 | pipeline = 0; |
---|
1091 | what = TclGetString(objv[1]); |
---|
1092 | if (what[0] == '|') { |
---|
1093 | pipeline = 1; |
---|
1094 | } |
---|
1095 | |
---|
1096 | /* |
---|
1097 | * Open the file or create a process pipeline. |
---|
1098 | */ |
---|
1099 | |
---|
1100 | if (!pipeline) { |
---|
1101 | chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot); |
---|
1102 | } else { |
---|
1103 | int mode, seekFlag, cmdObjc, binary; |
---|
1104 | const char **cmdArgv; |
---|
1105 | |
---|
1106 | if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) { |
---|
1107 | return TCL_ERROR; |
---|
1108 | } |
---|
1109 | |
---|
1110 | mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary); |
---|
1111 | if (mode == -1) { |
---|
1112 | chan = NULL; |
---|
1113 | } else { |
---|
1114 | int flags = TCL_STDERR | TCL_ENFORCE_MODE; |
---|
1115 | |
---|
1116 | switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) { |
---|
1117 | case O_RDONLY: |
---|
1118 | flags |= TCL_STDOUT; |
---|
1119 | break; |
---|
1120 | case O_WRONLY: |
---|
1121 | flags |= TCL_STDIN; |
---|
1122 | break; |
---|
1123 | case O_RDWR: |
---|
1124 | flags |= (TCL_STDIN | TCL_STDOUT); |
---|
1125 | break; |
---|
1126 | default: |
---|
1127 | Tcl_Panic("Tcl_OpenCmd: invalid mode value"); |
---|
1128 | break; |
---|
1129 | } |
---|
1130 | chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags); |
---|
1131 | if (binary) { |
---|
1132 | Tcl_SetChannelOption(interp, chan, "-translation", "binary"); |
---|
1133 | } |
---|
1134 | } |
---|
1135 | ckfree((char *) cmdArgv); |
---|
1136 | } |
---|
1137 | if (chan == NULL) { |
---|
1138 | return TCL_ERROR; |
---|
1139 | } |
---|
1140 | Tcl_RegisterChannel(interp, chan); |
---|
1141 | Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL); |
---|
1142 | return TCL_OK; |
---|
1143 | } |
---|
1144 | |
---|
1145 | /* |
---|
1146 | *---------------------------------------------------------------------- |
---|
1147 | * |
---|
1148 | * TcpAcceptCallbacksDeleteProc -- |
---|
1149 | * |
---|
1150 | * Assocdata cleanup routine called when an interpreter is being deleted |
---|
1151 | * to set the interp field of all the accept callback records registered |
---|
1152 | * with the interpreter to NULL. This will prevent the interpreter from |
---|
1153 | * being used in the future to eval accept scripts. |
---|
1154 | * |
---|
1155 | * Results: |
---|
1156 | * None. |
---|
1157 | * |
---|
1158 | * Side effects: |
---|
1159 | * Deallocates memory and sets the interp field of all the accept |
---|
1160 | * callback records to NULL to prevent this interpreter from being used |
---|
1161 | * subsequently to eval accept scripts. |
---|
1162 | * |
---|
1163 | *---------------------------------------------------------------------- |
---|
1164 | */ |
---|
1165 | |
---|
1166 | /* ARGSUSED */ |
---|
1167 | static void |
---|
1168 | TcpAcceptCallbacksDeleteProc( |
---|
1169 | ClientData clientData, /* Data which was passed when the assocdata |
---|
1170 | * was registered. */ |
---|
1171 | Tcl_Interp *interp) /* Interpreter being deleted - not used. */ |
---|
1172 | { |
---|
1173 | Tcl_HashTable *hTblPtr = clientData; |
---|
1174 | Tcl_HashEntry *hPtr; |
---|
1175 | Tcl_HashSearch hSearch; |
---|
1176 | |
---|
1177 | for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); |
---|
1178 | hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { |
---|
1179 | AcceptCallback *acceptCallbackPtr = Tcl_GetHashValue(hPtr); |
---|
1180 | |
---|
1181 | acceptCallbackPtr->interp = NULL; |
---|
1182 | } |
---|
1183 | Tcl_DeleteHashTable(hTblPtr); |
---|
1184 | ckfree((char *) hTblPtr); |
---|
1185 | } |
---|
1186 | |
---|
1187 | /* |
---|
1188 | *---------------------------------------------------------------------- |
---|
1189 | * |
---|
1190 | * RegisterTcpServerInterpCleanup -- |
---|
1191 | * |
---|
1192 | * Registers an accept callback record to have its interp field set to |
---|
1193 | * NULL when the interpreter is deleted. |
---|
1194 | * |
---|
1195 | * Results: |
---|
1196 | * None. |
---|
1197 | * |
---|
1198 | * Side effects: |
---|
1199 | * When, in the future, the interpreter is deleted, the interp field of |
---|
1200 | * the accept callback data structure will be set to NULL. This will |
---|
1201 | * prevent attempts to eval the accept script in a deleted interpreter. |
---|
1202 | * |
---|
1203 | *---------------------------------------------------------------------- |
---|
1204 | */ |
---|
1205 | |
---|
1206 | static void |
---|
1207 | RegisterTcpServerInterpCleanup( |
---|
1208 | Tcl_Interp *interp, /* Interpreter for which we want to be |
---|
1209 | * informed of deletion. */ |
---|
1210 | AcceptCallback *acceptCallbackPtr) |
---|
1211 | /* The accept callback record whose interp |
---|
1212 | * field we want set to NULL when the |
---|
1213 | * interpreter is deleted. */ |
---|
1214 | { |
---|
1215 | Tcl_HashTable *hTblPtr; /* Hash table for accept callback records to |
---|
1216 | * smash when the interpreter will be |
---|
1217 | * deleted. */ |
---|
1218 | Tcl_HashEntry *hPtr; /* Entry for this record. */ |
---|
1219 | int isNew; /* Is the entry new? */ |
---|
1220 | |
---|
1221 | hTblPtr = (Tcl_HashTable *) |
---|
1222 | Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL); |
---|
1223 | |
---|
1224 | if (hTblPtr == NULL) { |
---|
1225 | hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable)); |
---|
1226 | Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS); |
---|
1227 | (void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks", |
---|
1228 | TcpAcceptCallbacksDeleteProc, hTblPtr); |
---|
1229 | } |
---|
1230 | |
---|
1231 | hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &isNew); |
---|
1232 | if (!isNew) { |
---|
1233 | Tcl_Panic("RegisterTcpServerCleanup: damaged accept record table"); |
---|
1234 | } |
---|
1235 | Tcl_SetHashValue(hPtr, acceptCallbackPtr); |
---|
1236 | } |
---|
1237 | |
---|
1238 | /* |
---|
1239 | *---------------------------------------------------------------------- |
---|
1240 | * |
---|
1241 | * UnregisterTcpServerInterpCleanupProc -- |
---|
1242 | * |
---|
1243 | * Unregister a previously registered accept callback record. The interp |
---|
1244 | * field of this record will no longer be set to NULL in the future when |
---|
1245 | * the interpreter is deleted. |
---|
1246 | * |
---|
1247 | * Results: |
---|
1248 | * None. |
---|
1249 | * |
---|
1250 | * Side effects: |
---|
1251 | * Prevents the interp field of the accept callback record from being set |
---|
1252 | * to NULL in the future when the interpreter is deleted. |
---|
1253 | * |
---|
1254 | *---------------------------------------------------------------------- |
---|
1255 | */ |
---|
1256 | |
---|
1257 | static void |
---|
1258 | UnregisterTcpServerInterpCleanupProc( |
---|
1259 | Tcl_Interp *interp, /* Interpreter in which the accept callback |
---|
1260 | * record was registered. */ |
---|
1261 | AcceptCallback *acceptCallbackPtr) |
---|
1262 | /* The record for which to delete the |
---|
1263 | * registration. */ |
---|
1264 | { |
---|
1265 | Tcl_HashTable *hTblPtr; |
---|
1266 | Tcl_HashEntry *hPtr; |
---|
1267 | |
---|
1268 | hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, |
---|
1269 | "tclTCPAcceptCallbacks", NULL); |
---|
1270 | if (hTblPtr == NULL) { |
---|
1271 | return; |
---|
1272 | } |
---|
1273 | |
---|
1274 | hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr); |
---|
1275 | if (hPtr != NULL) { |
---|
1276 | Tcl_DeleteHashEntry(hPtr); |
---|
1277 | } |
---|
1278 | } |
---|
1279 | |
---|
1280 | /* |
---|
1281 | *---------------------------------------------------------------------- |
---|
1282 | * |
---|
1283 | * AcceptCallbackProc -- |
---|
1284 | * |
---|
1285 | * This callback is invoked by the TCP channel driver when it accepts a |
---|
1286 | * new connection from a client on a server socket. |
---|
1287 | * |
---|
1288 | * Results: |
---|
1289 | * None. |
---|
1290 | * |
---|
1291 | * Side effects: |
---|
1292 | * Whatever the script does. |
---|
1293 | * |
---|
1294 | *---------------------------------------------------------------------- |
---|
1295 | */ |
---|
1296 | |
---|
1297 | static void |
---|
1298 | AcceptCallbackProc( |
---|
1299 | ClientData callbackData, /* The data stored when the callback was |
---|
1300 | * created in the call to |
---|
1301 | * Tcl_OpenTcpServer. */ |
---|
1302 | Tcl_Channel chan, /* Channel for the newly accepted |
---|
1303 | * connection. */ |
---|
1304 | char *address, /* Address of client that was accepted. */ |
---|
1305 | int port) /* Port of client that was accepted. */ |
---|
1306 | { |
---|
1307 | AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData; |
---|
1308 | |
---|
1309 | /* |
---|
1310 | * Check if the callback is still valid; the interpreter may have gone |
---|
1311 | * away, this is signalled by setting the interp field of the callback |
---|
1312 | * data to NULL. |
---|
1313 | */ |
---|
1314 | |
---|
1315 | if (acceptCallbackPtr->interp != NULL) { |
---|
1316 | char portBuf[TCL_INTEGER_SPACE]; |
---|
1317 | char *script = acceptCallbackPtr->script; |
---|
1318 | Tcl_Interp *interp = acceptCallbackPtr->interp; |
---|
1319 | int result; |
---|
1320 | |
---|
1321 | Tcl_Preserve(script); |
---|
1322 | Tcl_Preserve(interp); |
---|
1323 | |
---|
1324 | TclFormatInt(portBuf, port); |
---|
1325 | Tcl_RegisterChannel(interp, chan); |
---|
1326 | |
---|
1327 | /* |
---|
1328 | * Artificially bump the refcount to protect the channel from being |
---|
1329 | * deleted while the script is being evaluated. |
---|
1330 | */ |
---|
1331 | |
---|
1332 | Tcl_RegisterChannel(NULL, chan); |
---|
1333 | |
---|
1334 | result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan), |
---|
1335 | " ", address, " ", portBuf, NULL); |
---|
1336 | if (result != TCL_OK) { |
---|
1337 | TclBackgroundException(interp, result); |
---|
1338 | Tcl_UnregisterChannel(interp, chan); |
---|
1339 | } |
---|
1340 | |
---|
1341 | /* |
---|
1342 | * Decrement the artificially bumped refcount. After this it is not |
---|
1343 | * safe anymore to use "chan", because it may now be deleted. |
---|
1344 | */ |
---|
1345 | |
---|
1346 | Tcl_UnregisterChannel(NULL, chan); |
---|
1347 | |
---|
1348 | Tcl_Release(interp); |
---|
1349 | Tcl_Release(script); |
---|
1350 | } else { |
---|
1351 | /* |
---|
1352 | * The interpreter has been deleted, so there is no useful way to |
---|
1353 | * utilize the client socket - just close it. |
---|
1354 | */ |
---|
1355 | |
---|
1356 | Tcl_Close(NULL, chan); |
---|
1357 | } |
---|
1358 | } |
---|
1359 | |
---|
1360 | /* |
---|
1361 | *---------------------------------------------------------------------- |
---|
1362 | * |
---|
1363 | * TcpServerCloseProc -- |
---|
1364 | * |
---|
1365 | * This callback is called when the TCP server channel for which it was |
---|
1366 | * registered is being closed. It informs the interpreter in which the |
---|
1367 | * accept script is evaluated (if that interpreter still exists) that |
---|
1368 | * this channel no longer needs to be informed if the interpreter is |
---|
1369 | * deleted. |
---|
1370 | * |
---|
1371 | * Results: |
---|
1372 | * None. |
---|
1373 | * |
---|
1374 | * Side effects: |
---|
1375 | * In the future, if the interpreter is deleted this channel will no |
---|
1376 | * longer be informed. |
---|
1377 | * |
---|
1378 | *---------------------------------------------------------------------- |
---|
1379 | */ |
---|
1380 | |
---|
1381 | static void |
---|
1382 | TcpServerCloseProc( |
---|
1383 | ClientData callbackData) /* The data passed in the call to |
---|
1384 | * Tcl_CreateCloseHandler. */ |
---|
1385 | { |
---|
1386 | AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData; |
---|
1387 | /* The actual data. */ |
---|
1388 | |
---|
1389 | if (acceptCallbackPtr->interp != NULL) { |
---|
1390 | UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp, |
---|
1391 | acceptCallbackPtr); |
---|
1392 | } |
---|
1393 | Tcl_EventuallyFree(acceptCallbackPtr->script, TCL_DYNAMIC); |
---|
1394 | ckfree((char *) acceptCallbackPtr); |
---|
1395 | } |
---|
1396 | |
---|
1397 | /* |
---|
1398 | *---------------------------------------------------------------------- |
---|
1399 | * |
---|
1400 | * Tcl_SocketObjCmd -- |
---|
1401 | * |
---|
1402 | * This function is invoked to process the "socket" Tcl command. See the |
---|
1403 | * user documentation for details on what it does. |
---|
1404 | * |
---|
1405 | * Results: |
---|
1406 | * A standard Tcl result. |
---|
1407 | * |
---|
1408 | * Side effects: |
---|
1409 | * Creates a socket based channel. |
---|
1410 | * |
---|
1411 | *---------------------------------------------------------------------- |
---|
1412 | */ |
---|
1413 | |
---|
1414 | int |
---|
1415 | Tcl_SocketObjCmd( |
---|
1416 | ClientData notUsed, /* Not used. */ |
---|
1417 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1418 | int objc, /* Number of arguments. */ |
---|
1419 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
1420 | { |
---|
1421 | static const char *socketOptions[] = { |
---|
1422 | "-async", "-myaddr", "-myport","-server", NULL |
---|
1423 | }; |
---|
1424 | enum socketOptions { |
---|
1425 | SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER |
---|
1426 | }; |
---|
1427 | int optionIndex, a, server = 0, port, myport = 0, async = 0; |
---|
1428 | char *host, *script = NULL, *myaddr = NULL; |
---|
1429 | Tcl_Channel chan; |
---|
1430 | |
---|
1431 | if (TclpHasSockets(interp) != TCL_OK) { |
---|
1432 | return TCL_ERROR; |
---|
1433 | } |
---|
1434 | |
---|
1435 | for (a = 1; a < objc; a++) { |
---|
1436 | const char *arg = Tcl_GetString(objv[a]); |
---|
1437 | |
---|
1438 | if (arg[0] != '-') { |
---|
1439 | break; |
---|
1440 | } |
---|
1441 | if (Tcl_GetIndexFromObj(interp, objv[a], socketOptions, "option", |
---|
1442 | TCL_EXACT, &optionIndex) != TCL_OK) { |
---|
1443 | return TCL_ERROR; |
---|
1444 | } |
---|
1445 | switch ((enum socketOptions) optionIndex) { |
---|
1446 | case SKT_ASYNC: |
---|
1447 | if (server == 1) { |
---|
1448 | Tcl_AppendResult(interp, |
---|
1449 | "cannot set -async option for server sockets", NULL); |
---|
1450 | return TCL_ERROR; |
---|
1451 | } |
---|
1452 | async = 1; |
---|
1453 | break; |
---|
1454 | case SKT_MYADDR: |
---|
1455 | a++; |
---|
1456 | if (a >= objc) { |
---|
1457 | Tcl_AppendResult(interp, |
---|
1458 | "no argument given for -myaddr option", NULL); |
---|
1459 | return TCL_ERROR; |
---|
1460 | } |
---|
1461 | myaddr = TclGetString(objv[a]); |
---|
1462 | break; |
---|
1463 | case SKT_MYPORT: { |
---|
1464 | char *myPortName; |
---|
1465 | |
---|
1466 | a++; |
---|
1467 | if (a >= objc) { |
---|
1468 | Tcl_AppendResult(interp, |
---|
1469 | "no argument given for -myport option", NULL); |
---|
1470 | return TCL_ERROR; |
---|
1471 | } |
---|
1472 | myPortName = TclGetString(objv[a]); |
---|
1473 | if (TclSockGetPort(interp, myPortName, "tcp", &myport) != TCL_OK) { |
---|
1474 | return TCL_ERROR; |
---|
1475 | } |
---|
1476 | break; |
---|
1477 | } |
---|
1478 | case SKT_SERVER: |
---|
1479 | if (async == 1) { |
---|
1480 | Tcl_AppendResult(interp, |
---|
1481 | "cannot set -async option for server sockets", NULL); |
---|
1482 | return TCL_ERROR; |
---|
1483 | } |
---|
1484 | server = 1; |
---|
1485 | a++; |
---|
1486 | if (a >= objc) { |
---|
1487 | Tcl_AppendResult(interp, |
---|
1488 | "no argument given for -server option", NULL); |
---|
1489 | return TCL_ERROR; |
---|
1490 | } |
---|
1491 | script = TclGetString(objv[a]); |
---|
1492 | break; |
---|
1493 | default: |
---|
1494 | Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions"); |
---|
1495 | } |
---|
1496 | } |
---|
1497 | if (server) { |
---|
1498 | host = myaddr; /* NULL implies INADDR_ANY */ |
---|
1499 | if (myport != 0) { |
---|
1500 | Tcl_AppendResult(interp, "option -myport is not valid for servers", |
---|
1501 | NULL); |
---|
1502 | return TCL_ERROR; |
---|
1503 | } |
---|
1504 | } else if (a < objc) { |
---|
1505 | host = TclGetString(objv[a]); |
---|
1506 | a++; |
---|
1507 | } else { |
---|
1508 | Interp *iPtr; |
---|
1509 | |
---|
1510 | wrongNumArgs: |
---|
1511 | iPtr = (Interp *) interp; |
---|
1512 | Tcl_WrongNumArgs(interp, 1, objv, |
---|
1513 | "?-myaddr addr? ?-myport myport? ?-async? host port"); |
---|
1514 | iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS; |
---|
1515 | Tcl_WrongNumArgs(interp, 1, objv, |
---|
1516 | "-server command ?-myaddr addr? port"); |
---|
1517 | iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS; |
---|
1518 | return TCL_ERROR; |
---|
1519 | } |
---|
1520 | |
---|
1521 | if (a == objc-1) { |
---|
1522 | if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp", |
---|
1523 | &port) != TCL_OK) { |
---|
1524 | return TCL_ERROR; |
---|
1525 | } |
---|
1526 | } else { |
---|
1527 | goto wrongNumArgs; |
---|
1528 | } |
---|
1529 | |
---|
1530 | if (server) { |
---|
1531 | AcceptCallback *acceptCallbackPtr = (AcceptCallback *) |
---|
1532 | ckalloc((unsigned) sizeof(AcceptCallback)); |
---|
1533 | unsigned len = strlen(script) + 1; |
---|
1534 | char *copyScript = ckalloc(len); |
---|
1535 | |
---|
1536 | memcpy(copyScript, script, len); |
---|
1537 | acceptCallbackPtr->script = copyScript; |
---|
1538 | acceptCallbackPtr->interp = interp; |
---|
1539 | chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc, |
---|
1540 | acceptCallbackPtr); |
---|
1541 | if (chan == NULL) { |
---|
1542 | ckfree(copyScript); |
---|
1543 | ckfree((char *) acceptCallbackPtr); |
---|
1544 | return TCL_ERROR; |
---|
1545 | } |
---|
1546 | |
---|
1547 | /* |
---|
1548 | * Register with the interpreter to let us know when the interpreter |
---|
1549 | * is deleted (by having the callback set the interp field of the |
---|
1550 | * acceptCallbackPtr's structure to NULL). This is to avoid trying to |
---|
1551 | * eval the script in a deleted interpreter. |
---|
1552 | */ |
---|
1553 | |
---|
1554 | RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr); |
---|
1555 | |
---|
1556 | /* |
---|
1557 | * Register a close callback. This callback will inform the |
---|
1558 | * interpreter (if it still exists) that this channel does not need to |
---|
1559 | * be informed when the interpreter is deleted. |
---|
1560 | */ |
---|
1561 | |
---|
1562 | Tcl_CreateCloseHandler(chan, TcpServerCloseProc, acceptCallbackPtr); |
---|
1563 | } else { |
---|
1564 | chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async); |
---|
1565 | if (chan == NULL) { |
---|
1566 | return TCL_ERROR; |
---|
1567 | } |
---|
1568 | } |
---|
1569 | Tcl_RegisterChannel(interp, chan); |
---|
1570 | Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL); |
---|
1571 | |
---|
1572 | return TCL_OK; |
---|
1573 | } |
---|
1574 | |
---|
1575 | /* |
---|
1576 | *---------------------------------------------------------------------- |
---|
1577 | * |
---|
1578 | * Tcl_FcopyObjCmd -- |
---|
1579 | * |
---|
1580 | * This function is invoked to process the "fcopy" Tcl command. See the |
---|
1581 | * user documentation for details on what it does. |
---|
1582 | * |
---|
1583 | * Results: |
---|
1584 | * A standard Tcl result. |
---|
1585 | * |
---|
1586 | * Side effects: |
---|
1587 | * Moves data between two channels and possibly sets up a background copy |
---|
1588 | * handler. |
---|
1589 | * |
---|
1590 | *---------------------------------------------------------------------- |
---|
1591 | */ |
---|
1592 | |
---|
1593 | int |
---|
1594 | Tcl_FcopyObjCmd( |
---|
1595 | ClientData dummy, /* Not used. */ |
---|
1596 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1597 | int objc, /* Number of arguments. */ |
---|
1598 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
1599 | { |
---|
1600 | Tcl_Channel inChan, outChan; |
---|
1601 | int mode, i, toRead, index; |
---|
1602 | Tcl_Obj *cmdPtr; |
---|
1603 | static const char* switches[] = { "-size", "-command", NULL }; |
---|
1604 | enum { FcopySize, FcopyCommand }; |
---|
1605 | |
---|
1606 | if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) { |
---|
1607 | Tcl_WrongNumArgs(interp, 1, objv, |
---|
1608 | "input output ?-size size? ?-command callback?"); |
---|
1609 | return TCL_ERROR; |
---|
1610 | } |
---|
1611 | |
---|
1612 | /* |
---|
1613 | * Parse the channel arguments and verify that they are readable or |
---|
1614 | * writable, as appropriate. |
---|
1615 | */ |
---|
1616 | |
---|
1617 | if (TclGetChannelFromObj(interp, objv[1], &inChan, &mode, 0) != TCL_OK) { |
---|
1618 | return TCL_ERROR; |
---|
1619 | } |
---|
1620 | if ((mode & TCL_READABLE) == 0) { |
---|
1621 | Tcl_AppendResult(interp, "channel \"", TclGetString(objv[1]), |
---|
1622 | "\" wasn't opened for reading", NULL); |
---|
1623 | return TCL_ERROR; |
---|
1624 | } |
---|
1625 | if (TclGetChannelFromObj(interp, objv[2], &outChan, &mode, 0) != TCL_OK) { |
---|
1626 | return TCL_ERROR; |
---|
1627 | } |
---|
1628 | if ((mode & TCL_WRITABLE) == 0) { |
---|
1629 | Tcl_AppendResult(interp, "channel \"", TclGetString(objv[2]), |
---|
1630 | "\" wasn't opened for writing", NULL); |
---|
1631 | return TCL_ERROR; |
---|
1632 | } |
---|
1633 | |
---|
1634 | toRead = -1; |
---|
1635 | cmdPtr = NULL; |
---|
1636 | for (i = 3; i < objc; i += 2) { |
---|
1637 | if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0, |
---|
1638 | &index) != TCL_OK) { |
---|
1639 | return TCL_ERROR; |
---|
1640 | } |
---|
1641 | switch (index) { |
---|
1642 | case FcopySize: |
---|
1643 | if (TclGetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) { |
---|
1644 | return TCL_ERROR; |
---|
1645 | } |
---|
1646 | break; |
---|
1647 | case FcopyCommand: |
---|
1648 | cmdPtr = objv[i+1]; |
---|
1649 | break; |
---|
1650 | } |
---|
1651 | } |
---|
1652 | |
---|
1653 | return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr); |
---|
1654 | } |
---|
1655 | |
---|
1656 | /* |
---|
1657 | *--------------------------------------------------------------------------- |
---|
1658 | * |
---|
1659 | * ChanPendingObjCmd -- |
---|
1660 | * |
---|
1661 | * This function is invoked to process the Tcl "chan pending" command |
---|
1662 | * (TIP #287). See the user documentation for details on what it does. |
---|
1663 | * |
---|
1664 | * Results: |
---|
1665 | * A standard Tcl result. |
---|
1666 | * |
---|
1667 | * Side effects: |
---|
1668 | * Sets interp's result to the number of bytes of buffered input or |
---|
1669 | * output (depending on whether the first argument is "input" or |
---|
1670 | * "output"), or -1 if the channel wasn't opened for that mode. |
---|
1671 | * |
---|
1672 | *--------------------------------------------------------------------------- |
---|
1673 | */ |
---|
1674 | |
---|
1675 | /* ARGSUSED */ |
---|
1676 | static int |
---|
1677 | ChanPendingObjCmd( |
---|
1678 | ClientData unused, /* Not used. */ |
---|
1679 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1680 | int objc, /* Number of arguments. */ |
---|
1681 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
1682 | { |
---|
1683 | Tcl_Channel chan; |
---|
1684 | int index, mode; |
---|
1685 | static const char *options[] = {"input", "output", NULL}; |
---|
1686 | enum options {PENDING_INPUT, PENDING_OUTPUT}; |
---|
1687 | |
---|
1688 | if (objc != 3) { |
---|
1689 | Tcl_WrongNumArgs(interp, 1, objv, "mode channelId"); |
---|
1690 | return TCL_ERROR; |
---|
1691 | } |
---|
1692 | |
---|
1693 | if (Tcl_GetIndexFromObj(interp, objv[1], options, "mode", 0, |
---|
1694 | &index) != TCL_OK) { |
---|
1695 | return TCL_ERROR; |
---|
1696 | } |
---|
1697 | |
---|
1698 | if (TclGetChannelFromObj(interp, objv[2], &chan, &mode, 0) != TCL_OK) { |
---|
1699 | return TCL_ERROR; |
---|
1700 | } |
---|
1701 | |
---|
1702 | switch ((enum options) index) { |
---|
1703 | case PENDING_INPUT: |
---|
1704 | if ((mode & TCL_READABLE) == 0) { |
---|
1705 | Tcl_SetObjResult(interp, Tcl_NewIntObj(-1)); |
---|
1706 | } else { |
---|
1707 | Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_InputBuffered(chan))); |
---|
1708 | } |
---|
1709 | break; |
---|
1710 | case PENDING_OUTPUT: |
---|
1711 | if ((mode & TCL_WRITABLE) == 0) { |
---|
1712 | Tcl_SetObjResult(interp, Tcl_NewIntObj(-1)); |
---|
1713 | } else { |
---|
1714 | Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_OutputBuffered(chan))); |
---|
1715 | } |
---|
1716 | break; |
---|
1717 | } |
---|
1718 | return TCL_OK; |
---|
1719 | } |
---|
1720 | |
---|
1721 | /* |
---|
1722 | *---------------------------------------------------------------------- |
---|
1723 | * |
---|
1724 | * ChanTruncateObjCmd -- |
---|
1725 | * |
---|
1726 | * This function is invoked to process the "chan truncate" Tcl command. |
---|
1727 | * See the user documentation for details on what it does. |
---|
1728 | * |
---|
1729 | * Results: |
---|
1730 | * A standard Tcl result. |
---|
1731 | * |
---|
1732 | * Side effects: |
---|
1733 | * Truncates a channel (or rather a file underlying a channel). |
---|
1734 | * |
---|
1735 | *---------------------------------------------------------------------- |
---|
1736 | */ |
---|
1737 | |
---|
1738 | static int |
---|
1739 | ChanTruncateObjCmd( |
---|
1740 | ClientData dummy, /* Not used. */ |
---|
1741 | Tcl_Interp *interp, /* Current interpreter. */ |
---|
1742 | int objc, /* Number of arguments. */ |
---|
1743 | Tcl_Obj *const objv[]) /* Argument objects. */ |
---|
1744 | { |
---|
1745 | Tcl_Channel chan; |
---|
1746 | Tcl_WideInt length; |
---|
1747 | |
---|
1748 | if ((objc < 2) || (objc > 3)) { |
---|
1749 | Tcl_WrongNumArgs(interp, 1, objv, "channelId ?length?"); |
---|
1750 | return TCL_ERROR; |
---|
1751 | } |
---|
1752 | if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) { |
---|
1753 | return TCL_ERROR; |
---|
1754 | } |
---|
1755 | |
---|
1756 | if (objc == 3) { |
---|
1757 | /* |
---|
1758 | * User is supplying an explicit length. |
---|
1759 | */ |
---|
1760 | |
---|
1761 | if (Tcl_GetWideIntFromObj(interp, objv[2], &length) != TCL_OK) { |
---|
1762 | return TCL_ERROR; |
---|
1763 | } |
---|
1764 | if (length < 0) { |
---|
1765 | Tcl_AppendResult(interp, |
---|
1766 | "cannot truncate to negative length of file", NULL); |
---|
1767 | return TCL_ERROR; |
---|
1768 | } |
---|
1769 | } else { |
---|
1770 | /* |
---|
1771 | * User wants to truncate to the current file position. |
---|
1772 | */ |
---|
1773 | |
---|
1774 | length = Tcl_Tell(chan); |
---|
1775 | if (length == Tcl_WideAsLong(-1)) { |
---|
1776 | Tcl_AppendResult(interp, |
---|
1777 | "could not determine current location in \"", |
---|
1778 | TclGetString(objv[1]), "\": ", |
---|
1779 | Tcl_PosixError(interp), NULL); |
---|
1780 | return TCL_ERROR; |
---|
1781 | } |
---|
1782 | } |
---|
1783 | |
---|
1784 | if (Tcl_TruncateChannel(chan, length) != TCL_OK) { |
---|
1785 | Tcl_AppendResult(interp, "error during truncate on \"", |
---|
1786 | TclGetString(objv[1]), "\": ", |
---|
1787 | Tcl_PosixError(interp), NULL); |
---|
1788 | return TCL_ERROR; |
---|
1789 | } |
---|
1790 | |
---|
1791 | return TCL_OK; |
---|
1792 | } |
---|
1793 | |
---|
1794 | /* |
---|
1795 | *---------------------------------------------------------------------- |
---|
1796 | * |
---|
1797 | * TclInitChanCmd -- |
---|
1798 | * |
---|
1799 | * This function is invoked to create the "chan" Tcl command. See the |
---|
1800 | * user documentation for details on what it does. |
---|
1801 | * |
---|
1802 | * Results: |
---|
1803 | * A Tcl command handle. |
---|
1804 | * |
---|
1805 | * Side effects: |
---|
1806 | * None (since nothing is byte-compiled). |
---|
1807 | * |
---|
1808 | *---------------------------------------------------------------------- |
---|
1809 | */ |
---|
1810 | |
---|
1811 | Tcl_Command |
---|
1812 | TclInitChanCmd( |
---|
1813 | Tcl_Interp *interp) |
---|
1814 | { |
---|
1815 | /* |
---|
1816 | * Most commands are plugged directly together, but some are done via |
---|
1817 | * alias-like rewriting; [chan configure] is this way for security reasons |
---|
1818 | * (want overwriting of [fconfigure] to control that nicely), and [chan |
---|
1819 | * names] because the functionality isn't available as a separate command |
---|
1820 | * function at the moment. |
---|
1821 | */ |
---|
1822 | static const EnsembleImplMap initMap[] = { |
---|
1823 | {"blocked", Tcl_FblockedObjCmd}, |
---|
1824 | {"close", Tcl_CloseObjCmd}, |
---|
1825 | {"copy", Tcl_FcopyObjCmd}, |
---|
1826 | {"create", TclChanCreateObjCmd}, /* TIP #219 */ |
---|
1827 | {"eof", Tcl_EofObjCmd}, |
---|
1828 | {"event", Tcl_FileEventObjCmd}, |
---|
1829 | {"flush", Tcl_FlushObjCmd}, |
---|
1830 | {"gets", Tcl_GetsObjCmd}, |
---|
1831 | {"pending", ChanPendingObjCmd}, /* TIP #287 */ |
---|
1832 | {"postevent", TclChanPostEventObjCmd}, /* TIP #219 */ |
---|
1833 | {"puts", Tcl_PutsObjCmd}, |
---|
1834 | {"read", Tcl_ReadObjCmd}, |
---|
1835 | {"seek", Tcl_SeekObjCmd}, |
---|
1836 | {"tell", Tcl_TellObjCmd}, |
---|
1837 | {"truncate", ChanTruncateObjCmd}, /* TIP #208 */ |
---|
1838 | {NULL} |
---|
1839 | }; |
---|
1840 | static const char *extras[] = { |
---|
1841 | "configure", "::fconfigure", |
---|
1842 | "names", "::file channels", |
---|
1843 | NULL |
---|
1844 | }; |
---|
1845 | Tcl_Command ensemble; |
---|
1846 | Tcl_Obj *mapObj; |
---|
1847 | int i; |
---|
1848 | |
---|
1849 | ensemble = TclMakeEnsemble(interp, "chan", initMap); |
---|
1850 | Tcl_GetEnsembleMappingDict(NULL, ensemble, &mapObj); |
---|
1851 | for (i=0 ; extras[i] ; i+=2) { |
---|
1852 | /* |
---|
1853 | * Can assume that reference counts are all incremented. |
---|
1854 | */ |
---|
1855 | |
---|
1856 | Tcl_DictObjPut(NULL, mapObj, Tcl_NewStringObj(extras[i], -1), |
---|
1857 | Tcl_NewStringObj(extras[i+1], -1)); |
---|
1858 | } |
---|
1859 | Tcl_SetEnsembleMappingDict(interp, ensemble, mapObj); |
---|
1860 | return ensemble; |
---|
1861 | } |
---|
1862 | |
---|
1863 | /* |
---|
1864 | * Local Variables: |
---|
1865 | * mode: c |
---|
1866 | * c-basic-offset: 4 |
---|
1867 | * fill-column: 78 |
---|
1868 | * End: |
---|
1869 | */ |
---|