Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libtksh/tcl/tclIOCmd.c
1810 views
1
/*
2
* tclIOCmd.c --
3
*
4
* Contains the definitions of most of the Tcl commands relating to IO.
5
*
6
* Copyright (c) 1995-1996 Sun Microsystems, Inc.
7
*
8
* See the file "license.terms" for information on usage and redistribution
9
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10
*
11
* SCCS: @(#) tclIOCmd.c 1.100 96/11/06 16:41:52
12
*/
13
14
#include "tclInt.h"
15
#include "tclPort.h"
16
17
/*
18
* Return at most this number of bytes in one call to Tcl_Read:
19
*/
20
21
#define TCL_READ_CHUNK_SIZE 4096
22
23
/*
24
* Callback structure for accept callback in a TCP server.
25
*/
26
27
typedef struct AcceptCallback {
28
char *script; /* Script to invoke. */
29
Tcl_Interp *interp; /* Interpreter in which to run it. */
30
} AcceptCallback;
31
32
/*
33
* Static functions for this file:
34
*/
35
36
static void AcceptCallbackProc _ANSI_ARGS_((ClientData callbackData,
37
Tcl_Channel chan, char *address, int port));
38
static void RegisterTcpServerInterpCleanup _ANSI_ARGS_((Tcl_Interp *interp,
39
AcceptCallback *acceptCallbackPtr));
40
static void TcpAcceptCallbacksDeleteProc _ANSI_ARGS_((
41
ClientData clientData, Tcl_Interp *interp));
42
static void TcpServerCloseProc _ANSI_ARGS_((ClientData callbackData));
43
static void UnregisterTcpServerInterpCleanupProc _ANSI_ARGS_((
44
Tcl_Interp *interp, AcceptCallback *acceptCallbackPtr));
45
46
/*
47
*----------------------------------------------------------------------
48
*
49
* Tcl_PutsCmd --
50
*
51
* This procedure is invoked to process the "puts" Tcl command.
52
* See the user documentation for details on what it does.
53
*
54
* Results:
55
* A standard Tcl result.
56
*
57
* Side effects:
58
* Produces output on a channel.
59
*
60
*----------------------------------------------------------------------
61
*/
62
63
/* ARGSUSED */
64
int
65
Tcl_PutsCmd(clientData, interp, argc, argv)
66
ClientData clientData; /* Not used. */
67
Tcl_Interp *interp; /* Current interpreter. */
68
int argc; /* Number of arguments. */
69
char **argv; /* Argument strings. */
70
{
71
Tcl_Channel chan; /* The channel to puts on. */
72
int i; /* Counter. */
73
int newline; /* Add a newline at end? */
74
char *channelId; /* Name of channel for puts. */
75
int result; /* Result of puts operation. */
76
int mode; /* Mode in which channel is opened. */
77
78
i = 1;
79
newline = 1;
80
if ((argc >= 2) && (strcmp(argv[1], "-nonewline") == 0)) {
81
newline = 0;
82
i++;
83
}
84
if ((i < (argc-3)) || (i >= argc)) {
85
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
86
" ?-nonewline? ?channelId? string\"", (char *) NULL);
87
return TCL_ERROR;
88
}
89
90
/*
91
* The code below provides backwards compatibility with an old
92
* form of the command that is no longer recommended or documented.
93
*/
94
95
if (i == (argc-3)) {
96
if (strncmp(argv[i+2], "nonewline", strlen(argv[i+2])) != 0) {
97
Tcl_AppendResult(interp, "bad argument \"", argv[i+2],
98
"\": should be \"nonewline\"", (char *) NULL);
99
return TCL_ERROR;
100
}
101
newline = 0;
102
}
103
if (i == (argc-1)) {
104
channelId = "stdout";
105
} else {
106
channelId = argv[i];
107
i++;
108
}
109
chan = Tcl_GetChannel(interp, channelId, &mode);
110
if (chan == (Tcl_Channel) NULL) {
111
return TCL_ERROR;
112
}
113
if ((mode & TCL_WRITABLE) == 0) {
114
Tcl_AppendResult(interp, "channel \"", channelId,
115
"\" wasn't opened for writing", (char *) NULL);
116
return TCL_ERROR;
117
}
118
119
result = Tcl_Write(chan, argv[i], -1);
120
if (result < 0) {
121
goto error;
122
}
123
if (newline != 0) {
124
result = Tcl_Write(chan, "\n", 1);
125
if (result < 0) {
126
goto error;
127
}
128
}
129
return TCL_OK;
130
error:
131
Tcl_AppendResult(interp, "error writing \"", Tcl_GetChannelName(chan),
132
"\": ", Tcl_PosixError(interp), (char *) NULL);
133
return TCL_ERROR;
134
}
135
136
/*
137
*----------------------------------------------------------------------
138
*
139
* Tcl_FlushCmd --
140
*
141
* This procedure is called to process the Tcl "flush" command.
142
* See the user documentation for details on what it does.
143
*
144
* Results:
145
* A standard Tcl result.
146
*
147
* Side effects:
148
* May cause output to appear on the specified channel.
149
*
150
*----------------------------------------------------------------------
151
*/
152
153
/* ARGSUSED */
154
int
155
Tcl_FlushCmd(clientData, interp, argc, argv)
156
ClientData clientData; /* Not used. */
157
Tcl_Interp *interp; /* Current interpreter. */
158
int argc; /* Number of arguments. */
159
char **argv; /* Argument strings. */
160
{
161
Tcl_Channel chan; /* The channel to flush on. */
162
int result; /* Result of call to channel
163
* level function. */
164
int mode; /* Mode in which channel is opened. */
165
166
if (argc != 2) {
167
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
168
" channelId\"", (char *) NULL);
169
return TCL_ERROR;
170
}
171
chan = Tcl_GetChannel(interp, argv[1], &mode);
172
if (chan == (Tcl_Channel) NULL) {
173
return TCL_ERROR;
174
}
175
if ((mode & TCL_WRITABLE) == 0) {
176
Tcl_AppendResult(interp, "channel \"", argv[1],
177
"\" wasn't opened for writing", (char *) NULL);
178
return TCL_ERROR;
179
}
180
181
result = Tcl_Flush(chan);
182
if (result != TCL_OK) {
183
Tcl_AppendResult(interp, "error flushing \"", Tcl_GetChannelName(chan),
184
"\": ", Tcl_PosixError(interp), (char *) NULL);
185
}
186
return result;
187
}
188
189
/*
190
*----------------------------------------------------------------------
191
*
192
* Tcl_GetsCmd --
193
*
194
* This procedure is called to process the Tcl "gets" command.
195
* See the user documentation for details on what it does.
196
*
197
* Results:
198
* A standard Tcl result.
199
*
200
* Side effects:
201
* May consume input from channel.
202
*
203
*----------------------------------------------------------------------
204
*/
205
206
/* ARGSUSED */
207
int
208
Tcl_GetsCmd(clientData, interp, argc, argv)
209
ClientData clientData; /* Not used. */
210
Tcl_Interp *interp; /* Current interpreter. */
211
int argc; /* Number of arguments. */
212
char **argv; /* Argument strings. */
213
{
214
Tcl_Channel chan; /* The channel to read from. */
215
char *varName; /* Assign to this variable? */
216
char buf[128]; /* Buffer to store string
217
* representation of how long
218
* a line was read. */
219
Tcl_DString ds; /* Dynamic string to hold the
220
* buffer for the line just read. */
221
int lineLen; /* Length of line just read. */
222
int mode; /* Mode in which channel is opened. */
223
224
if ((argc != 2) && (argc != 3)) {
225
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
226
" channelId ?varName?\"", (char *) NULL);
227
return TCL_ERROR;
228
}
229
chan = Tcl_GetChannel(interp, argv[1], &mode);
230
if (chan == (Tcl_Channel) NULL) {
231
return TCL_ERROR;
232
}
233
if ((mode & TCL_READABLE) == 0) {
234
Tcl_AppendResult(interp, "channel \"", argv[1],
235
"\" wasn't opened for reading", (char *) NULL);
236
return TCL_ERROR;
237
}
238
239
if (argc != 3) {
240
varName = (char *) NULL;
241
} else {
242
varName = argv[2];
243
}
244
Tcl_DStringInit(&ds);
245
lineLen = Tcl_Gets(chan, &ds);
246
if (lineLen < 0) {
247
if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
248
Tcl_DStringFree(&ds);
249
Tcl_AppendResult(interp, "error reading \"",
250
Tcl_GetChannelName(chan), "\": ", Tcl_PosixError(interp),
251
(char *) NULL);
252
return TCL_ERROR;
253
}
254
lineLen = -1;
255
}
256
if (varName == (char *) NULL) {
257
Tcl_DStringResult(interp, &ds);
258
} else {
259
if (Tcl_SetVar(interp, varName, Tcl_DStringValue(&ds),
260
TCL_LEAVE_ERR_MSG) == NULL) {
261
Tcl_DStringFree(&ds);
262
return TCL_ERROR;
263
}
264
Tcl_ResetResult(interp);
265
sprintf(buf, "%d", lineLen);
266
Tcl_AppendResult(interp, buf, (char *) NULL);
267
}
268
Tcl_DStringFree(&ds);
269
270
return TCL_OK;
271
}
272
273
/*
274
*----------------------------------------------------------------------
275
*
276
* Tcl_ReadCmd --
277
*
278
* This procedure is invoked to process the Tcl "read" command.
279
* See the user documentation for details on what it does.
280
*
281
* Results:
282
* A standard Tcl result.
283
*
284
* Side effects:
285
* May consume input from channel.
286
*
287
*----------------------------------------------------------------------
288
*/
289
290
/* ARGSUSED */
291
int
292
Tcl_ReadCmd(clientData, interp, argc, argv)
293
ClientData clientData; /* Not used. */
294
Tcl_Interp *interp; /* Current interpreter. */
295
int argc; /* Number of arguments. */
296
char **argv; /* Argument strings. */
297
{
298
Tcl_Channel chan; /* The channel to read from. */
299
int newline, i; /* Discard newline at end? */
300
int toRead; /* How many bytes to read? */
301
int toReadNow; /* How many bytes to attempt to
302
* read in the current iteration? */
303
int charactersRead; /* How many characters were read? */
304
int charactersReadNow; /* How many characters were read
305
* in this iteration? */
306
int mode; /* Mode in which channel is opened. */
307
Tcl_DString ds; /* Used to accumulate the data
308
* read by Tcl_Read. */
309
int bufSize; /* Channel buffer size; used to decide
310
* in what chunk sizes to read from
311
* the channel. */
312
313
if ((argc != 2) && (argc != 3)) {
314
argerror:
315
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
316
" channelId ?numBytes?\" or \"", argv[0],
317
" ?-nonewline? channelId\"", (char *) NULL);
318
return TCL_ERROR;
319
}
320
i = 1;
321
newline = 0;
322
if (strcmp(argv[i], "-nonewline") == 0) {
323
newline = 1;
324
i++;
325
}
326
327
if (i == argc) {
328
goto argerror;
329
}
330
331
chan = Tcl_GetChannel(interp, argv[i], &mode);
332
if (chan == (Tcl_Channel) NULL) {
333
return TCL_ERROR;
334
}
335
if ((mode & TCL_READABLE) == 0) {
336
Tcl_AppendResult(interp, "channel \"", argv[i],
337
"\" wasn't opened for reading", (char *) NULL);
338
return TCL_ERROR;
339
}
340
341
i++; /* Consumed channel name. */
342
343
/*
344
* Compute how many bytes to read, and see whether the final
345
* newline should be dropped.
346
*/
347
348
toRead = INT_MAX;
349
if (i < argc) {
350
if (isdigit((unsigned char) (argv[i][0]))) {
351
if (Tcl_GetInt(interp, argv[i], &toRead) != TCL_OK) {
352
return TCL_ERROR;
353
}
354
} else if (strcmp(argv[i], "nonewline") == 0) {
355
newline = 1;
356
} else {
357
Tcl_AppendResult(interp, "bad argument \"", argv[i],
358
"\": should be \"nonewline\"", (char *) NULL);
359
return TCL_ERROR;
360
}
361
}
362
363
bufSize = Tcl_GetChannelBufferSize(chan);
364
Tcl_DStringInit(&ds);
365
for (charactersRead = 0; charactersRead < toRead; ) {
366
toReadNow = toRead - charactersRead;
367
if (toReadNow > bufSize) {
368
toReadNow = bufSize;
369
}
370
Tcl_DStringSetLength(&ds, charactersRead + toReadNow);
371
charactersReadNow =
372
Tcl_Read(chan, Tcl_DStringValue(&ds) + charactersRead, toReadNow);
373
if (charactersReadNow < 0) {
374
Tcl_DStringFree(&ds);
375
Tcl_AppendResult(interp, "error reading \"",
376
Tcl_GetChannelName(chan), "\": ",
377
Tcl_PosixError(interp), (char *) NULL);
378
return TCL_ERROR;
379
}
380
381
/*
382
* If we had a short read it means that we have either EOF
383
* or BLOCKED on the channel, so break out.
384
*/
385
386
charactersRead += charactersReadNow;
387
if (charactersReadNow < toReadNow) {
388
break; /* Out of "for" loop. */
389
}
390
}
391
392
/*
393
* Tcl_Read does not put a NULL at the end of the string, so we must
394
* do it here.
395
*/
396
397
Tcl_DStringSetLength(&ds, charactersRead);
398
Tcl_DStringResult(interp, &ds);
399
Tcl_DStringFree(&ds);
400
401
/*
402
* If requested, remove the last newline in the channel if at EOF.
403
*/
404
405
if ((charactersRead > 0) && (newline) &&
406
(interp->result[charactersRead-1] == '\n')) {
407
interp->result[charactersRead-1] = '\0';
408
}
409
return TCL_OK;
410
}
411
412
/*
413
*----------------------------------------------------------------------
414
*
415
* TclUnsupported0Cmd --
416
*
417
* This procedure is invoked to process the Tcl "unsupported0" command.
418
* See the user documentation for details on what it does.
419
*
420
* Results:
421
* A standard Tcl result.
422
*
423
* Side effects:
424
* May copy a chunk from one channel to another.
425
*
426
*----------------------------------------------------------------------
427
*/
428
429
int
430
TclUnsupported0Cmd(clientData, interp, argc, argv)
431
ClientData clientData; /* Not used. */
432
Tcl_Interp *interp; /* Interpreter in which both channels
433
* are defined. */
434
int argc; /* How many arguments? */
435
char **argv; /* The argument strings. */
436
{
437
Tcl_Channel inChan, outChan;
438
int requested;
439
char *bufPtr;
440
int actuallyRead, actuallyWritten, totalRead, toReadNow, mode;
441
442
/*
443
* Assume we want to copy the entire channel.
444
*/
445
446
requested = INT_MAX;
447
448
if ((argc < 3) || (argc > 4)) {
449
Tcl_AppendResult(interp, "wrong # args: should be \"",
450
argv[0], " inChanId outChanId ?chunkSize?\"", (char *) NULL);
451
return TCL_ERROR;
452
}
453
inChan = Tcl_GetChannel(interp, argv[1], &mode);
454
if (inChan == (Tcl_Channel) NULL) {
455
return TCL_ERROR;
456
}
457
if ((mode & TCL_READABLE) == 0) {
458
Tcl_AppendResult(interp, "channel \"", argv[1],
459
"\" wasn't opened for reading", (char *) NULL);
460
return TCL_ERROR;
461
}
462
outChan = Tcl_GetChannel(interp, argv[2], &mode);
463
if (outChan == (Tcl_Channel) NULL) {
464
return TCL_ERROR;
465
}
466
if ((mode & TCL_WRITABLE) == 0) {
467
Tcl_AppendResult(interp, "channel \"", argv[2],
468
"\" wasn't opened for writing", (char *) NULL);
469
return TCL_ERROR;
470
}
471
472
if (argc == 4) {
473
if (Tcl_GetInt(interp, argv[3], &requested) != TCL_OK) {
474
return TCL_ERROR;
475
}
476
if (requested < 0) {
477
requested = INT_MAX;
478
}
479
}
480
481
bufPtr = ckalloc((unsigned) TCL_READ_CHUNK_SIZE);
482
for (totalRead = 0;
483
requested > 0;
484
totalRead += actuallyRead, requested -= actuallyRead) {
485
toReadNow = requested;
486
if (toReadNow > TCL_READ_CHUNK_SIZE) {
487
toReadNow = TCL_READ_CHUNK_SIZE;
488
}
489
actuallyRead = Tcl_Read(inChan, bufPtr, toReadNow);
490
if (actuallyRead < 0) {
491
ckfree(bufPtr);
492
Tcl_AppendResult(interp, argv[0], ": ", Tcl_GetChannelName(inChan),
493
Tcl_PosixError(interp), (char *) NULL);
494
return TCL_ERROR;
495
}
496
if (actuallyRead == 0) {
497
ckfree(bufPtr);
498
sprintf(interp->result, "%d", totalRead);
499
return TCL_OK;
500
}
501
actuallyWritten = Tcl_Write(outChan, bufPtr, actuallyRead);
502
if (actuallyWritten < 0) {
503
ckfree(bufPtr);
504
Tcl_AppendResult(interp, argv[0], ": ", Tcl_GetChannelName(outChan),
505
Tcl_PosixError(interp), (char *) NULL);
506
return TCL_ERROR;
507
}
508
}
509
ckfree(bufPtr);
510
511
sprintf(interp->result, "%d", totalRead);
512
return TCL_OK;
513
}
514
515
/*
516
*----------------------------------------------------------------------
517
*
518
* Tcl_SeekCmd --
519
*
520
* This procedure is invoked to process the Tcl "seek" command. See
521
* the user documentation for details on what it does.
522
*
523
* Results:
524
* A standard Tcl result.
525
*
526
* Side effects:
527
* Moves the position of the access point on the specified channel.
528
* May flush queued output.
529
*
530
*----------------------------------------------------------------------
531
*/
532
533
/* ARGSUSED */
534
int
535
Tcl_SeekCmd(clientData, interp, argc, argv)
536
ClientData clientData; /* Not used. */
537
Tcl_Interp *interp; /* Current interpreter. */
538
int argc; /* Number of arguments. */
539
char **argv; /* Argument strings. */
540
{
541
Tcl_Channel chan; /* The channel to tell on. */
542
int offset, mode; /* Where to seek? */
543
int result; /* Of calling Tcl_Seek. */
544
545
if ((argc != 3) && (argc != 4)) {
546
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
547
" channelId offset ?origin?\"", (char *) NULL);
548
return TCL_ERROR;
549
}
550
chan = Tcl_GetChannel(interp, argv[1], NULL);
551
if (chan == (Tcl_Channel) NULL) {
552
return TCL_ERROR;
553
}
554
if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) {
555
return TCL_ERROR;
556
}
557
mode = SEEK_SET;
558
if (argc == 4) {
559
size_t length;
560
int c;
561
562
length = strlen(argv[3]);
563
c = argv[3][0];
564
if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) {
565
mode = SEEK_SET;
566
} else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) {
567
mode = SEEK_CUR;
568
} else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) {
569
mode = SEEK_END;
570
} else {
571
Tcl_AppendResult(interp, "bad origin \"", argv[3],
572
"\": should be start, current, or end", (char *) NULL);
573
return TCL_ERROR;
574
}
575
}
576
577
result = Tcl_Seek(chan, offset, mode);
578
if (result == -1) {
579
Tcl_AppendResult(interp, "error during seek on \"",
580
Tcl_GetChannelName(chan), "\": ",
581
Tcl_PosixError(interp), (char *) NULL);
582
return TCL_ERROR;
583
}
584
return TCL_OK;
585
}
586
587
/*
588
*----------------------------------------------------------------------
589
*
590
* Tcl_TellCmd --
591
*
592
* This procedure is invoked to process the Tcl "tell" command.
593
* See the user documentation for details on what it does.
594
*
595
* Results:
596
* A standard Tcl result.
597
*
598
* Side effects:
599
* None.
600
*
601
*----------------------------------------------------------------------
602
*/
603
604
/* ARGSUSED */
605
int
606
Tcl_TellCmd(clientData, interp, argc, argv)
607
ClientData clientData; /* Not used. */
608
Tcl_Interp *interp; /* Current interpreter. */
609
int argc; /* Number of arguments. */
610
char **argv; /* Argument strings. */
611
{
612
Tcl_Channel chan; /* The channel to tell on. */
613
614
if (argc != 2) {
615
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
616
" channelId\"", (char *) NULL);
617
return TCL_ERROR;
618
}
619
/*
620
* Try to find a channel with the right name and permissions in
621
* the IO channel table of this interpreter.
622
*/
623
624
chan = Tcl_GetChannel(interp, argv[1], NULL);
625
if (chan == (Tcl_Channel) NULL) {
626
return TCL_ERROR;
627
}
628
sprintf(interp->result, "%d", Tcl_Tell(chan));
629
630
return TCL_OK;
631
}
632
633
/*
634
*----------------------------------------------------------------------
635
*
636
* Tcl_CloseCmd --
637
*
638
* This procedure is invoked to process the Tcl "close" command.
639
* See the user documentation for details on what it does.
640
*
641
* Results:
642
* A standard Tcl result.
643
*
644
* Side effects:
645
* May discard queued input; may flush queued output.
646
*
647
*----------------------------------------------------------------------
648
*/
649
650
/* ARGSUSED */
651
int
652
Tcl_CloseCmd(clientData, interp, argc, argv)
653
ClientData clientData; /* Not used. */
654
Tcl_Interp *interp; /* Current interpreter. */
655
int argc; /* Number of arguments. */
656
char **argv; /* Argument strings. */
657
{
658
Tcl_Channel chan; /* The channel to close. */
659
int len; /* Length of error output. */
660
661
if (argc != 2) {
662
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
663
" channelId\"", (char *) NULL);
664
return TCL_ERROR;
665
}
666
chan = Tcl_GetChannel(interp, argv[1], NULL);
667
if (chan == (Tcl_Channel) NULL) {
668
return TCL_ERROR;
669
}
670
if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
671
672
/*
673
* If there is an error message and it ends with a newline, remove
674
* the newline. This is done for command pipeline channels where the
675
* error output from the subprocesses is stored in interp->result.
676
*
677
* NOTE: This is likely to not have any effect on regular error
678
* messages produced by drivers during the closing of a channel,
679
* because the Tcl convention is that such error messages do not
680
* have a terminating newline.
681
*/
682
683
len = strlen(interp->result);
684
if ((len > 0) && (interp->result[len - 1] == '\n')) {
685
interp->result[len - 1] = '\0';
686
}
687
688
return TCL_ERROR;
689
}
690
return TCL_OK;
691
}
692
693
/*
694
*----------------------------------------------------------------------
695
*
696
* Tcl_FconfigureCmd --
697
*
698
* This procedure is invoked to process the Tcl "fconfigure" command.
699
* See the user documentation for details on what it does.
700
*
701
* Results:
702
* A standard Tcl result.
703
*
704
* Side effects:
705
* May modify the behavior of an IO channel.
706
*
707
*----------------------------------------------------------------------
708
*/
709
710
/* ARGSUSED */
711
int
712
Tcl_FconfigureCmd(clientData, interp, argc, argv)
713
ClientData clientData; /* Not used. */
714
Tcl_Interp *interp; /* Current interpreter. */
715
int argc; /* Number of arguments. */
716
char **argv; /* Argument strings. */
717
{
718
Tcl_Channel chan; /* The channel to set a mode on. */
719
int result; /* Of Tcl_Set/GetChannelOption. */
720
int i; /* Iterate over arg-value pairs. */
721
Tcl_DString ds; /* DString to hold result of
722
* calling Tcl_GetChannelOption. */
723
724
if ((argc < 2) || (((argc % 2) == 1) && (argc != 3))) {
725
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
726
" channelId ?optionName? ?value? ?optionName value?...\"",
727
(char *) NULL);
728
return TCL_ERROR;
729
}
730
chan = Tcl_GetChannel(interp, argv[1], NULL);
731
if (chan == (Tcl_Channel) NULL) {
732
return TCL_ERROR;
733
}
734
if (argc == 2) {
735
Tcl_DStringInit(&ds);
736
if (Tcl_GetChannelOption(chan, (char *) NULL, &ds) != TCL_OK) {
737
Tcl_AppendResult(interp, "option retrieval failed",
738
(char *) NULL);
739
return TCL_ERROR;
740
}
741
Tcl_DStringResult(interp, &ds);
742
Tcl_DStringFree(&ds);
743
return TCL_OK;
744
}
745
if (argc == 3) {
746
Tcl_DStringInit(&ds);
747
if (Tcl_GetChannelOption(chan, argv[2], &ds) != TCL_OK) {
748
Tcl_DStringFree(&ds);
749
Tcl_AppendResult(interp, "bad option \"", argv[2],
750
"\": must be -blocking, -buffering, -buffersize, ",
751
"-eofchar, -translation, ",
752
"or a channel type specific option", (char *) NULL);
753
return TCL_ERROR;
754
}
755
Tcl_DStringResult(interp, &ds);
756
Tcl_DStringFree(&ds);
757
return TCL_OK;
758
}
759
for (i = 3; i < argc; i += 2) {
760
result = Tcl_SetChannelOption(interp, chan, argv[i-1], argv[i]);
761
if (result != TCL_OK) {
762
return result;
763
}
764
}
765
return TCL_OK;
766
}
767
768
/*
769
*----------------------------------------------------------------------
770
*
771
* Tcl_EofCmd --
772
*
773
* This procedure is invoked to process the Tcl "eof" command.
774
* See the user documentation for details on what it does.
775
*
776
* Results:
777
* A standard Tcl result.
778
*
779
* Side effects:
780
* Sets interp->result to "0" or "1" depending on whether the
781
* specified channel has an EOF condition.
782
*
783
*----------------------------------------------------------------------
784
*/
785
786
/* ARGSUSED */
787
int
788
Tcl_EofCmd(unused, interp, argc, argv)
789
ClientData unused; /* Not used. */
790
Tcl_Interp *interp; /* Current interpreter. */
791
int argc; /* Number of arguments. */
792
char **argv; /* Argument strings. */
793
{
794
Tcl_Channel chan; /* The channel to query for EOF. */
795
int mode; /* Mode in which channel is opened. */
796
797
if (argc != 2) {
798
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
799
" channelId\"", (char *) NULL);
800
return TCL_ERROR;
801
}
802
chan = Tcl_GetChannel(interp, argv[1], &mode);
803
if (chan == (Tcl_Channel) NULL) {
804
return TCL_ERROR;
805
}
806
sprintf(interp->result, "%d", Tcl_Eof(chan) ? 1 : 0);
807
return TCL_OK;
808
}
809
810
#if 0
811
/*
812
*----------------------------------------------------------------------
813
*
814
* Tcl_ExecCmd --
815
*
816
* This procedure is invoked to process the "exec" Tcl command.
817
* See the user documentation for details on what it does.
818
*
819
* Results:
820
* A standard Tcl result.
821
*
822
* Side effects:
823
* See the user documentation.
824
*
825
*----------------------------------------------------------------------
826
*/
827
828
/* ARGSUSED */
829
int
830
Tcl_ExecCmd(dummy, interp, argc, argv)
831
ClientData dummy; /* Not used. */
832
Tcl_Interp *interp; /* Current interpreter. */
833
int argc; /* Number of arguments. */
834
char **argv; /* Argument strings. */
835
{
836
#ifdef MAC_TCL
837
Tcl_AppendResult(interp, "exec not implemented under Mac OS",
838
(char *)NULL);
839
return TCL_ERROR;
840
#else /* !MAC_TCL */
841
int keepNewline, firstWord, background, length, result;
842
Tcl_Channel chan;
843
Tcl_DString ds;
844
int readSoFar, readNow, bufSize;
845
846
/*
847
* Check for a leading "-keepnewline" argument.
848
*/
849
850
keepNewline = 0;
851
for (firstWord = 1; (firstWord < argc) && (argv[firstWord][0] == '-');
852
firstWord++) {
853
if (strcmp(argv[firstWord], "-keepnewline") == 0) {
854
keepNewline = 1;
855
} else if (strcmp(argv[firstWord], "--") == 0) {
856
firstWord++;
857
break;
858
} else {
859
Tcl_AppendResult(interp, "bad switch \"", argv[firstWord],
860
"\": must be -keepnewline or --", (char *) NULL);
861
return TCL_ERROR;
862
}
863
}
864
865
if (argc <= firstWord) {
866
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
867
" ?switches? arg ?arg ...?\"", (char *) NULL);
868
return TCL_ERROR;
869
}
870
871
/*
872
* See if the command is to be run in background.
873
*/
874
875
background = 0;
876
if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) {
877
argc--;
878
argv[argc] = NULL;
879
background = 1;
880
}
881
882
chan = Tcl_OpenCommandChannel(interp, argc-firstWord,
883
argv+firstWord,
884
(background ? 0 : TCL_STDOUT | TCL_STDERR));
885
886
if (chan == (Tcl_Channel) NULL) {
887
return TCL_ERROR;
888
}
889
890
if (background) {
891
892
/*
893
* Get the list of PIDs from the pipeline into interp->result and
894
* detach the PIDs (instead of waiting for them).
895
*/
896
897
TclGetAndDetachPids(interp, chan);
898
899
if (Tcl_Close(interp, chan) != TCL_OK) {
900
return TCL_ERROR;
901
}
902
return TCL_OK;
903
}
904
905
if (Tcl_GetChannelFile(chan, TCL_READABLE) != NULL) {
906
#define EXEC_BUFFER_SIZE 4096
907
908
Tcl_DStringInit(&ds);
909
readSoFar = 0; bufSize = 0;
910
while (1) {
911
bufSize += EXEC_BUFFER_SIZE;
912
Tcl_DStringSetLength(&ds, bufSize);
913
readNow = Tcl_Read(chan, Tcl_DStringValue(&ds) + readSoFar,
914
EXEC_BUFFER_SIZE);
915
if (readNow < 0) {
916
Tcl_DStringFree(&ds);
917
Tcl_AppendResult(interp,
918
"error reading output from command: ",
919
Tcl_PosixError(interp), (char *) NULL);
920
return TCL_ERROR;
921
}
922
readSoFar += readNow;
923
if (readNow < EXEC_BUFFER_SIZE) {
924
break; /* Out of "while (1)" loop. */
925
}
926
}
927
Tcl_DStringSetLength(&ds, readSoFar);
928
Tcl_DStringResult(interp, &ds);
929
Tcl_DStringFree(&ds);
930
}
931
932
result = Tcl_Close(interp, chan);
933
934
/*
935
* If the last character of interp->result is a newline, then remove
936
* the newline character (the newline would just confuse things).
937
* Special hack: must replace the old terminating null character
938
* as a signal to Tcl_AppendResult et al. that we've mucked with
939
* the string.
940
*/
941
942
length = strlen(interp->result);
943
if (!keepNewline && (length > 0) &&
944
(interp->result[length-1] == '\n')) {
945
interp->result[length-1] = '\0';
946
interp->result[length] = 'x';
947
}
948
949
return result;
950
#endif /* !MAC_TCL */
951
}
952
#endif
953
954
/*
955
*----------------------------------------------------------------------
956
*
957
* Tcl_FblockedCmd --
958
*
959
* This procedure is invoked to process the Tcl "fblocked" command.
960
* See the user documentation for details on what it does.
961
*
962
* Results:
963
* A standard Tcl result.
964
*
965
* Side effects:
966
* Sets interp->result to "0" or "1" depending on whether the
967
* a preceding input operation on the channel would have blocked.
968
*
969
*----------------------------------------------------------------------
970
*/
971
972
/* ARGSUSED */
973
int
974
Tcl_FblockedCmd(unused, interp, argc, argv)
975
ClientData unused; /* Not used. */
976
Tcl_Interp *interp; /* Current interpreter. */
977
int argc; /* Number of arguments. */
978
char **argv; /* Argument strings. */
979
{
980
Tcl_Channel chan; /* The channel to query for blocked. */
981
int mode; /* Mode in which channel was opened. */
982
983
if (argc != 2) {
984
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
985
" channelId\"", (char *) NULL);
986
return TCL_ERROR;
987
}
988
chan = Tcl_GetChannel(interp, argv[1], &mode);
989
if (chan == (Tcl_Channel) NULL) {
990
return TCL_ERROR;
991
}
992
if ((mode & TCL_READABLE) == 0) {
993
Tcl_AppendResult(interp, "channel \"", argv[1],
994
"\" wasn't opened for reading", (char *) NULL);
995
return TCL_ERROR;
996
}
997
998
sprintf(interp->result, "%d", Tcl_InputBlocked(chan) ? 1 : 0);
999
return TCL_OK;
1000
}
1001
1002
/*
1003
*----------------------------------------------------------------------
1004
*
1005
* Tcl_OpenCmd --
1006
*
1007
* This procedure is invoked to process the "open" Tcl command.
1008
* See the user documentation for details on what it does.
1009
*
1010
* Results:
1011
* A standard Tcl result.
1012
*
1013
* Side effects:
1014
* See the user documentation.
1015
*
1016
*----------------------------------------------------------------------
1017
*/
1018
1019
/* ARGSUSED */
1020
int
1021
Tcl_OpenCmd(notUsed, interp, argc, argv)
1022
ClientData notUsed; /* Not used. */
1023
Tcl_Interp *interp; /* Current interpreter. */
1024
int argc; /* Number of arguments. */
1025
char **argv; /* Argument strings. */
1026
{
1027
int pipeline, prot;
1028
char *modeString;
1029
Tcl_Channel chan;
1030
1031
if ((argc < 2) || (argc > 4)) {
1032
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1033
" fileName ?access? ?permissions?\"", (char *) NULL);
1034
return TCL_ERROR;
1035
}
1036
prot = 0666;
1037
if (argc == 2) {
1038
modeString = "r";
1039
} else {
1040
modeString = argv[2];
1041
if (argc == 4) {
1042
if (Tcl_GetInt(interp, argv[3], &prot) != TCL_OK) {
1043
return TCL_ERROR;
1044
}
1045
}
1046
}
1047
1048
pipeline = 0;
1049
if (argv[1][0] == '|') {
1050
pipeline = 1;
1051
}
1052
1053
/*
1054
* Open the file or create a process pipeline.
1055
*/
1056
1057
if (!pipeline) {
1058
chan = Tcl_OpenFileChannel(interp, argv[1], modeString, prot);
1059
} else {
1060
int mode, seekFlag, cmdArgc;
1061
char **cmdArgv;
1062
1063
if (Tcl_TclSplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) {
1064
return TCL_ERROR;
1065
}
1066
1067
mode = TclGetOpenMode(interp, modeString, &seekFlag);
1068
if (mode == -1) {
1069
chan = NULL;
1070
} else {
1071
int flags = TCL_STDERR | TCL_ENFORCE_MODE;
1072
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1073
case O_RDONLY:
1074
flags |= TCL_STDOUT;
1075
break;
1076
case O_WRONLY:
1077
flags |= TCL_STDIN;
1078
break;
1079
case O_RDWR:
1080
flags |= (TCL_STDIN | TCL_STDOUT);
1081
break;
1082
default:
1083
panic("Tcl_OpenCmd: invalid mode value");
1084
break;
1085
}
1086
chan = Tcl_OpenCommandChannel(interp, cmdArgc, cmdArgv, flags);
1087
}
1088
ckfree((char *) cmdArgv);
1089
}
1090
if (chan == (Tcl_Channel) NULL) {
1091
return TCL_ERROR;
1092
}
1093
Tcl_RegisterChannel(interp, chan);
1094
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
1095
return TCL_OK;
1096
}
1097
1098
#if 0
1099
/*
1100
*----------------------------------------------------------------------
1101
*
1102
* TcpAcceptCallbacksDeleteProc --
1103
*
1104
* Assocdata cleanup routine called when an interpreter is being
1105
* deleted to set the interp field of all the accept callback records
1106
* registered with the interpreter to NULL. This will prevent the
1107
* interpreter from being used in the future to eval accept scripts.
1108
*
1109
* Results:
1110
* None.
1111
*
1112
* Side effects:
1113
* Deallocates memory and sets the interp field of all the accept
1114
* callback records to NULL to prevent this interpreter from being
1115
* used subsequently to eval accept scripts.
1116
*
1117
*----------------------------------------------------------------------
1118
*/
1119
1120
/* ARGSUSED */
1121
static void
1122
TcpAcceptCallbacksDeleteProc(clientData, interp)
1123
ClientData clientData; /* Data which was passed when the assocdata
1124
* was registered. */
1125
Tcl_Interp *interp; /* Interpreter being deleted - not used. */
1126
{
1127
Tcl_HashTable *hTblPtr;
1128
Tcl_HashEntry *hPtr;
1129
Tcl_HashSearch hSearch;
1130
AcceptCallback *acceptCallbackPtr;
1131
1132
hTblPtr = (Tcl_HashTable *) clientData;
1133
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
1134
hPtr != (Tcl_HashEntry *) NULL;
1135
hPtr = Tcl_NextHashEntry(&hSearch)) {
1136
acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr);
1137
acceptCallbackPtr->interp = (Tcl_Interp *) NULL;
1138
}
1139
Tcl_DeleteHashTable(hTblPtr);
1140
ckfree((char *) hTblPtr);
1141
}
1142
1143
/*
1144
*----------------------------------------------------------------------
1145
*
1146
* RegisterTcpServerInterpCleanup --
1147
*
1148
* Registers an accept callback record to have its interp
1149
* field set to NULL when the interpreter is deleted.
1150
*
1151
* Results:
1152
* None.
1153
*
1154
* Side effects:
1155
* When, in the future, the interpreter is deleted, the interp
1156
* field of the accept callback data structure will be set to
1157
* NULL. This will prevent attempts to eval the accept script
1158
* in a deleted interpreter.
1159
*
1160
*----------------------------------------------------------------------
1161
*/
1162
1163
static void
1164
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr)
1165
Tcl_Interp *interp; /* Interpreter for which we want to be
1166
* informed of deletion. */
1167
AcceptCallback *acceptCallbackPtr;
1168
/* The accept callback record whose
1169
* interp field we want set to NULL when
1170
* the interpreter is deleted. */
1171
{
1172
Tcl_HashTable *hTblPtr; /* Hash table for accept callback
1173
* records to smash when the interpreter
1174
* will be deleted. */
1175
Tcl_HashEntry *hPtr; /* Entry for this record. */
1176
int new; /* Is the entry new? */
1177
1178
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
1179
"tclTCPAcceptCallbacks",
1180
NULL);
1181
if (hTblPtr == (Tcl_HashTable *) NULL) {
1182
hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
1183
Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
1184
(void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
1185
TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr);
1186
}
1187
hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new);
1188
if (!new) {
1189
panic("RegisterTcpServerCleanup: damaged accept record table");
1190
}
1191
Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr);
1192
}
1193
1194
/*
1195
*----------------------------------------------------------------------
1196
*
1197
* UnregisterTcpServerInterpCleanupProc --
1198
*
1199
* Unregister a previously registered accept callback record. The
1200
* interp field of this record will no longer be set to NULL in
1201
* the future when the interpreter is deleted.
1202
*
1203
* Results:
1204
* None.
1205
*
1206
* Side effects:
1207
* Prevents the interp field of the accept callback record from
1208
* being set to NULL in the future when the interpreter is deleted.
1209
*
1210
*----------------------------------------------------------------------
1211
*/
1212
1213
static void
1214
UnregisterTcpServerInterpCleanupProc(interp, acceptCallbackPtr)
1215
Tcl_Interp *interp; /* Interpreter in which the accept callback
1216
* record was registered. */
1217
AcceptCallback *acceptCallbackPtr;
1218
/* The record for which to delete the
1219
* registration. */
1220
{
1221
Tcl_HashTable *hTblPtr;
1222
Tcl_HashEntry *hPtr;
1223
1224
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
1225
"tclTCPAcceptCallbacks", NULL);
1226
if (hTblPtr == (Tcl_HashTable *) NULL) {
1227
return;
1228
}
1229
hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
1230
if (hPtr == (Tcl_HashEntry *) NULL) {
1231
return;
1232
}
1233
Tcl_DeleteHashEntry(hPtr);
1234
}
1235
1236
/*
1237
*----------------------------------------------------------------------
1238
*
1239
* AcceptCallbackProc --
1240
*
1241
* This callback is invoked by the TCP channel driver when it
1242
* accepts a new connection from a client on a server socket.
1243
*
1244
* Results:
1245
* None.
1246
*
1247
* Side effects:
1248
* Whatever the script does.
1249
*
1250
*----------------------------------------------------------------------
1251
*/
1252
1253
static void
1254
AcceptCallbackProc(callbackData, chan, address, port)
1255
ClientData callbackData; /* The data stored when the callback
1256
* was created in the call to
1257
* Tcl_OpenTcpServer. */
1258
Tcl_Channel chan; /* Channel for the newly accepted
1259
* connection. */
1260
char *address; /* Address of client that was
1261
* accepted. */
1262
int port; /* Port of client that was accepted. */
1263
{
1264
AcceptCallback *acceptCallbackPtr;
1265
Tcl_Interp *interp;
1266
char *script;
1267
char portBuf[10];
1268
int result;
1269
1270
acceptCallbackPtr = (AcceptCallback *) callbackData;
1271
1272
/*
1273
* Check if the callback is still valid; the interpreter may have gone
1274
* away, this is signalled by setting the interp field of the callback
1275
* data to NULL.
1276
*/
1277
1278
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
1279
1280
script = acceptCallbackPtr->script;
1281
interp = acceptCallbackPtr->interp;
1282
1283
Tcl_Preserve((ClientData) script);
1284
Tcl_Preserve((ClientData) interp);
1285
1286
sprintf(portBuf, "%d", port);
1287
Tcl_RegisterChannel(interp, chan);
1288
result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
1289
" ", address, " ", portBuf, (char *) NULL);
1290
if (result != TCL_OK) {
1291
Tcl_BackgroundError(interp);
1292
Tcl_UnregisterChannel(interp, chan);
1293
}
1294
Tcl_Release((ClientData) interp);
1295
Tcl_Release((ClientData) script);
1296
} else {
1297
1298
/*
1299
* The interpreter has been deleted, so there is no useful
1300
* way to utilize the client socket - just close it.
1301
*/
1302
1303
Tcl_Close((Tcl_Interp *) NULL, chan);
1304
}
1305
}
1306
1307
/*
1308
*----------------------------------------------------------------------
1309
*
1310
* TcpServerCloseProc --
1311
*
1312
* This callback is called when the TCP server channel for which it
1313
* was registered is being closed. It informs the interpreter in
1314
* which the accept script is evaluated (if that interpreter still
1315
* exists) that this channel no longer needs to be informed if the
1316
* interpreter is deleted.
1317
*
1318
* Results:
1319
* None.
1320
*
1321
* Side effects:
1322
* In the future, if the interpreter is deleted this channel will
1323
* no longer be informed.
1324
*
1325
*----------------------------------------------------------------------
1326
*/
1327
1328
static void
1329
TcpServerCloseProc(callbackData)
1330
ClientData callbackData; /* The data passed in the call to
1331
* Tcl_CreateCloseHandler. */
1332
{
1333
AcceptCallback *acceptCallbackPtr;
1334
/* The actual data. */
1335
1336
acceptCallbackPtr = (AcceptCallback *) callbackData;
1337
if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
1338
UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
1339
acceptCallbackPtr);
1340
}
1341
Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC);
1342
ckfree((char *) acceptCallbackPtr);
1343
}
1344
1345
/*
1346
*----------------------------------------------------------------------
1347
*
1348
* Tcl_SocketCmd --
1349
*
1350
* This procedure is invoked to process the "socket" Tcl command.
1351
* See the user documentation for details on what it does.
1352
*
1353
* Results:
1354
* A standard Tcl result.
1355
*
1356
* Side effects:
1357
* Creates a socket based channel.
1358
*
1359
*----------------------------------------------------------------------
1360
*/
1361
1362
int
1363
Tcl_SocketCmd(notUsed, interp, argc, argv)
1364
ClientData notUsed; /* Not used. */
1365
Tcl_Interp *interp; /* Current interpreter. */
1366
int argc; /* Number of arguments. */
1367
char **argv; /* Argument strings. */
1368
{
1369
int a, server, port;
1370
char *arg, *copyScript, *host, *script;
1371
char *myaddr = NULL;
1372
int myport = 0;
1373
int async = 0;
1374
Tcl_Channel chan;
1375
AcceptCallback *acceptCallbackPtr;
1376
1377
server = 0;
1378
script = NULL;
1379
1380
if (TclHasSockets(interp) != TCL_OK) {
1381
return TCL_ERROR;
1382
}
1383
1384
for (a = 1; a < argc; a++) {
1385
arg = argv[a];
1386
if (arg[0] == '-') {
1387
if (strcmp(arg, "-server") == 0) {
1388
if (async == 1) {
1389
Tcl_AppendResult(interp,
1390
"cannot set -async option for server sockets",
1391
(char *) NULL);
1392
return TCL_ERROR;
1393
}
1394
server = 1;
1395
a++;
1396
if (a >= argc) {
1397
Tcl_AppendResult(interp,
1398
"no argument given for -server option",
1399
(char *) NULL);
1400
return TCL_ERROR;
1401
}
1402
script = argv[a];
1403
} else if (strcmp(arg, "-myaddr") == 0) {
1404
a++;
1405
if (a >= argc) {
1406
Tcl_AppendResult(interp,
1407
"no argument given for -myaddr option",
1408
(char *) NULL);
1409
return TCL_ERROR;
1410
}
1411
myaddr = argv[a];
1412
} else if (strcmp(arg, "-myport") == 0) {
1413
a++;
1414
if (a >= argc) {
1415
Tcl_AppendResult(interp,
1416
"no argument given for -myport option",
1417
(char *) NULL);
1418
return TCL_ERROR;
1419
}
1420
if (TclSockGetPort(interp, argv[a], "tcp", &myport)
1421
!= TCL_OK) {
1422
return TCL_ERROR;
1423
}
1424
} else if (strcmp(arg, "-async") == 0) {
1425
if (server == 1) {
1426
Tcl_AppendResult(interp,
1427
"cannot set -async option for server sockets",
1428
(char *) NULL);
1429
return TCL_ERROR;
1430
}
1431
async = 1;
1432
} else {
1433
Tcl_AppendResult(interp, "bad option \"", arg,
1434
"\", must be -async, -myaddr, -myport, or -server",
1435
(char *) NULL);
1436
return TCL_ERROR;
1437
}
1438
} else {
1439
break;
1440
}
1441
}
1442
if (server) {
1443
host = myaddr; /* NULL implies INADDR_ANY */
1444
if (myport != 0) {
1445
Tcl_AppendResult(interp, "Option -myport is not valid for servers",
1446
NULL);
1447
return TCL_ERROR;
1448
}
1449
} else if (a < argc) {
1450
host = argv[a];
1451
a++;
1452
} else {
1453
wrongNumArgs:
1454
Tcl_AppendResult(interp, "wrong # args: should be either:\n",
1455
argv[0],
1456
" ?-myaddr addr? ?-myport myport? ?-async? host port\n",
1457
argv[0],
1458
" -server command ?-myaddr addr? port",
1459
(char *) NULL);
1460
return TCL_ERROR;
1461
}
1462
1463
if (a == argc-1) {
1464
if (TclSockGetPort(interp, argv[a], "tcp", &port) != TCL_OK) {
1465
return TCL_ERROR;
1466
}
1467
} else {
1468
goto wrongNumArgs;
1469
}
1470
1471
if (server) {
1472
acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned)
1473
sizeof(AcceptCallback));
1474
copyScript = ckalloc((unsigned) strlen(script) + 1);
1475
strcpy(copyScript, script);
1476
acceptCallbackPtr->script = copyScript;
1477
acceptCallbackPtr->interp = interp;
1478
chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
1479
(ClientData) acceptCallbackPtr);
1480
if (chan == (Tcl_Channel) NULL) {
1481
ckfree(copyScript);
1482
ckfree((char *) acceptCallbackPtr);
1483
return TCL_ERROR;
1484
}
1485
1486
/*
1487
* Register with the interpreter to let us know when the
1488
* interpreter is deleted (by having the callback set the
1489
* acceptCallbackPtr->interp field to NULL). This is to
1490
* avoid trying to eval the script in a deleted interpreter.
1491
*/
1492
1493
RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
1494
1495
/*
1496
* Register a close callback. This callback will inform the
1497
* interpreter (if it still exists) that this channel does not
1498
* need to be informed when the interpreter is deleted.
1499
*/
1500
1501
Tcl_CreateCloseHandler(chan, TcpServerCloseProc,
1502
(ClientData) acceptCallbackPtr);
1503
} else {
1504
chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
1505
if (chan == (Tcl_Channel) NULL) {
1506
return TCL_ERROR;
1507
}
1508
}
1509
Tcl_RegisterChannel(interp, chan);
1510
Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
1511
1512
return TCL_OK;
1513
}
1514
#endif
1515
1516