OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [unix/] [tclUnixChan.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclUnixChan.c
3
 *
4
 *      Common channel driver for Unix channels based on files, command
5
 *      pipes and TCP sockets.
6
 *
7
 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8
 *
9
 * See the file "license.terms" for information on usage and redistribution
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
 *
12
 * RCS: @(#) $Id: tclUnixChan.c,v 1.1.1.1 2002-01-16 10:25:37 markom Exp $
13
 */
14
 
15
#include        "tclInt.h"      /* Internal definitions for Tcl. */
16
#include        "tclPort.h"     /* Portability features for Tcl. */
17
 
18
/*
19
 * sys/ioctl.h has already been included by tclPort.h.  Including termios.h
20
 * or termio.h causes a bunch of warning messages because some duplicate
21
 * (but not contradictory) #defines exist in termios.h and/or termio.h
22
 */
23
#undef NL0
24
#undef NL1
25
#undef CR0
26
#undef CR1
27
#undef CR2
28
#undef CR3
29
#undef TAB0
30
#undef TAB1
31
#undef TAB2
32
#undef XTABS
33
#undef BS0
34
#undef BS1
35
#undef FF0
36
#undef FF1
37
#undef ECHO
38
#undef NOFLSH
39
#undef TOSTOP
40
#undef FLUSHO
41
#undef PENDIN
42
 
43
#ifdef USE_TERMIOS
44
#   include <termios.h>
45
#else   /* !USE_TERMIOS */
46
#ifdef USE_TERMIO
47
#   include <termio.h>
48
#else   /* !USE_TERMIO */
49
#ifdef USE_SGTTY
50
#   include <sgtty.h>
51
#endif  /* USE_SGTTY */
52
#endif  /* !USE_TERMIO */
53
#endif  /* !USE_TERMIOS */
54
 
55
/*
56
 * The following structure is used to set or get the serial port
57
 * attributes in a platform-independant manner.
58
 */
59
 
60
typedef struct TtyAttrs {
61
    int baud;
62
    int parity;
63
    int data;
64
    int stop;
65
} TtyAttrs;
66
 
67
/*
68
 * This structure describes per-instance state of a file based channel.
69
 */
70
 
71
typedef struct FileState {
72
    Tcl_Channel channel;        /* Channel associated with this file. */
73
    int fd;                     /* File handle. */
74
    int validMask;              /* OR'ed combination of TCL_READABLE,
75
                                 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
76
                                 * which operations are valid on the file. */
77
    struct FileState *nextPtr;  /* Pointer to next file in list of all
78
                                 * file channels. */
79
} FileState;
80
 
81
/*
82
 * List of all file channels currently open.
83
 */
84
 
85
static FileState *firstFilePtr = NULL;
86
 
87
/*
88
 * This structure describes per-instance state of a tcp based channel.
89
 */
90
 
91
typedef struct TcpState {
92
    Tcl_Channel channel;        /* Channel associated with this file. */
93
    int fd;                     /* The socket itself. */
94
    int flags;                  /* ORed combination of the bitfields
95
                                 * defined below. */
96
    Tcl_TcpAcceptProc *acceptProc;
97
                                /* Proc to call on accept. */
98
    ClientData acceptProcData;  /* The data for the accept proc. */
99
} TcpState;
100
 
101
/*
102
 * These bits may be ORed together into the "flags" field of a TcpState
103
 * structure.
104
 */
105
 
106
#define TCP_ASYNC_SOCKET        (1<<0)  /* Asynchronous socket. */
107
#define TCP_ASYNC_CONNECT       (1<<1)  /* Async connect in progress. */
108
 
109
/*
110
 * The following defines the maximum length of the listen queue. This is
111
 * the number of outstanding yet-to-be-serviced requests for a connection
112
 * on a server socket, more than this number of outstanding requests and
113
 * the connection request will fail.
114
 */
115
 
116
#ifndef SOMAXCONN
117
#define SOMAXCONN       100
118
#endif
119
 
120
#if     (SOMAXCONN < 100)
121
#undef  SOMAXCONN
122
#define SOMAXCONN       100
123
#endif
124
 
125
/*
126
 * The following defines how much buffer space the kernel should maintain
127
 * for a socket.
128
 */
129
 
130
#define SOCKET_BUFSIZE  4096
131
 
132
/*
133
 * Static routines for this file:
134
 */
135
 
136
static TcpState *       CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
137
                            int port, char *host, int server,
138
                            char *myaddr, int myport, int async));
139
static int              CreateSocketAddress _ANSI_ARGS_(
140
                            (struct sockaddr_in *sockaddrPtr,
141
                            char *host, int port));
142
static int              FileBlockModeProc _ANSI_ARGS_((
143
                            ClientData instanceData, int mode));
144
static int              FileCloseProc _ANSI_ARGS_((ClientData instanceData,
145
                            Tcl_Interp *interp));
146
static int              FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
147
                            int direction, ClientData *handlePtr));
148
static int              FileInputProc _ANSI_ARGS_((ClientData instanceData,
149
                            char *buf, int toRead, int *errorCode));
150
static int              FileOutputProc _ANSI_ARGS_((
151
                            ClientData instanceData, char *buf, int toWrite,
152
                            int *errorCode));
153
static int              FileSeekProc _ANSI_ARGS_((ClientData instanceData,
154
                            long offset, int mode, int *errorCode));
155
static void             FileWatchProc _ANSI_ARGS_((ClientData instanceData,
156
                            int mask));
157
static void             TcpAccept _ANSI_ARGS_((ClientData data, int mask));
158
static int              TcpBlockModeProc _ANSI_ARGS_((ClientData data,
159
                            int mode));
160
static int              TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
161
                            Tcl_Interp *interp));
162
static int              TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
163
                            int direction, ClientData *handlePtr));
164
static int              TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
165
                            Tcl_Interp *interp, char *optionName,
166
                            Tcl_DString *dsPtr));
167
static int              TcpInputProc _ANSI_ARGS_((ClientData instanceData,
168
                            char *buf, int toRead,  int *errorCode));
169
static int              TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
170
                            char *buf, int toWrite, int *errorCode));
171
static void             TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
172
                            int mask));
173
static int              TtyParseMode _ANSI_ARGS_((Tcl_Interp *interp,
174
                            CONST char *mode, int *speedPtr, int *parityPtr,
175
                            int *dataPtr, int *stopPtr));
176
static void             TtyGetAttributes _ANSI_ARGS_((int fd,
177
                            TtyAttrs *ttyPtr));
178
static int              TtyGetOptionProc _ANSI_ARGS_((ClientData instanceData,
179
                            Tcl_Interp *interp, char *optionName,
180
                            Tcl_DString *dsPtr));
181
static void             TtyInit _ANSI_ARGS_((int fd));
182
static void             TtySetAttributes _ANSI_ARGS_((int fd,
183
                            TtyAttrs *ttyPtr));
184
static int              TtySetOptionProc _ANSI_ARGS_((ClientData instanceData,
185
                            Tcl_Interp *interp, char *optionName,
186
                            char *value));
187
static int              WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
188
                            int *errorCodePtr));
189
 
190
/*
191
 * This structure describes the channel type structure for file based IO:
192
 */
193
 
194
static Tcl_ChannelType fileChannelType = {
195
    "file",                             /* Type name. */
196
    FileBlockModeProc,                  /* Set blocking/nonblocking mode.*/
197
    FileCloseProc,                      /* Close proc. */
198
    FileInputProc,                      /* Input proc. */
199
    FileOutputProc,                     /* Output proc. */
200
    FileSeekProc,                       /* Seek proc. */
201
    NULL,                               /* Set option proc. */
202
    NULL,                               /* Get option proc. */
203
    FileWatchProc,                      /* Initialize notifier. */
204
    FileGetHandleProc,                  /* Get OS handles out of channel. */
205
};
206
 
207
/*
208
 * This structure describes the channel type structure for serial IO.
209
 * Note that this type is a subclass of the "file" type.
210
 */
211
 
212
static Tcl_ChannelType ttyChannelType = {
213
    "tty",                              /* Type name. */
214
    FileBlockModeProc,                  /* Set blocking/nonblocking mode.*/
215
    FileCloseProc,                      /* Close proc. */
216
    FileInputProc,                      /* Input proc. */
217
    FileOutputProc,                     /* Output proc. */
218
    NULL,                               /* Seek proc. */
219
    TtySetOptionProc,                   /* Set option proc. */
220
    TtyGetOptionProc,                   /* Get option proc. */
221
    FileWatchProc,                      /* Initialize notifier. */
222
    FileGetHandleProc,                  /* Get OS handles out of channel. */
223
};
224
 
225
/*
226
 * This structure describes the channel type structure for TCP socket
227
 * based IO:
228
 */
229
 
230
static Tcl_ChannelType tcpChannelType = {
231
    "tcp",                              /* Type name. */
232
    TcpBlockModeProc,                   /* Set blocking/nonblocking mode.*/
233
    TcpCloseProc,                       /* Close proc. */
234
    TcpInputProc,                       /* Input proc. */
235
    TcpOutputProc,                      /* Output proc. */
236
    NULL,                               /* Seek proc. */
237
    NULL,                               /* Set option proc. */
238
    TcpGetOptionProc,                   /* Get option proc. */
239
    TcpWatchProc,                       /* Initialize notifier. */
240
    TcpGetHandleProc,                   /* Get OS handles out of channel. */
241
};
242
 
243
 
244
/*
245
 *----------------------------------------------------------------------
246
 *
247
 * FileBlockModeProc --
248
 *
249
 *      Helper procedure to set blocking and nonblocking modes on a
250
 *      file based channel. Invoked by generic IO level code.
251
 *
252
 * Results:
253
 *      0 if successful, errno when failed.
254
 *
255
 * Side effects:
256
 *      Sets the device into blocking or non-blocking mode.
257
 *
258
 *----------------------------------------------------------------------
259
 */
260
 
261
        /* ARGSUSED */
262
static int
263
FileBlockModeProc(instanceData, mode)
264
    ClientData instanceData;            /* File state. */
265
    int mode;                           /* The mode to set. Can be one of
266
                                         * TCL_MODE_BLOCKING or
267
                                         * TCL_MODE_NONBLOCKING. */
268
{
269
    FileState *fsPtr = (FileState *) instanceData;
270
    int curStatus;
271
 
272
#ifndef USE_FIONBIO
273
    curStatus = fcntl(fsPtr->fd, F_GETFL);
274
    if (mode == TCL_MODE_BLOCKING) {
275
        curStatus &= (~(O_NONBLOCK));
276
    } else {
277
        curStatus |= O_NONBLOCK;
278
    }
279
    if (fcntl(fsPtr->fd, F_SETFL, curStatus) < 0) {
280
        return errno;
281
    }
282
    curStatus = fcntl(fsPtr->fd, F_GETFL);
283
#else
284
    if (mode == TCL_MODE_BLOCKING) {
285
        curStatus = 0;
286
    } else {
287
        curStatus = 1;
288
    }
289
    if (ioctl(fsPtr->fd, (int) FIONBIO, &curStatus) < 0) {
290
        return errno;
291
    }
292
#endif
293
    return 0;
294
}
295
 
296
/*
297
 *----------------------------------------------------------------------
298
 *
299
 * FileInputProc --
300
 *
301
 *      This procedure is invoked from the generic IO level to read
302
 *      input from a file based channel.
303
 *
304
 * Results:
305
 *      The number of bytes read is returned or -1 on error. An output
306
 *      argument contains a POSIX error code if an error occurs, or zero.
307
 *
308
 * Side effects:
309
 *      Reads input from the input device of the channel.
310
 *
311
 *----------------------------------------------------------------------
312
 */
313
 
314
static int
315
FileInputProc(instanceData, buf, toRead, errorCodePtr)
316
    ClientData instanceData;            /* File state. */
317
    char *buf;                          /* Where to store data read. */
318
    int toRead;                         /* How much space is available
319
                                         * in the buffer? */
320
    int *errorCodePtr;                  /* Where to store error code. */
321
{
322
    FileState *fsPtr = (FileState *) instanceData;
323
    int bytesRead;                      /* How many bytes were actually
324
                                         * read from the input device? */
325
 
326
    *errorCodePtr = 0;
327
 
328
    /*
329
     * Assume there is always enough input available. This will block
330
     * appropriately, and read will unblock as soon as a short read is
331
     * possible, if the channel is in blocking mode. If the channel is
332
     * nonblocking, the read will never block.
333
     */
334
 
335
    bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
336
    if (bytesRead > -1) {
337
        return bytesRead;
338
    }
339
    *errorCodePtr = errno;
340
    return -1;
341
}
342
 
343
/*
344
 *----------------------------------------------------------------------
345
 *
346
 * FileOutputProc--
347
 *
348
 *      This procedure is invoked from the generic IO level to write
349
 *      output to a file channel.
350
 *
351
 * Results:
352
 *      The number of bytes written is returned or -1 on error. An
353
 *      output argument contains a POSIX error code if an error occurred,
354
 *      or zero.
355
 *
356
 * Side effects:
357
 *      Writes output on the output device of the channel.
358
 *
359
 *----------------------------------------------------------------------
360
 */
361
 
362
static int
363
FileOutputProc(instanceData, buf, toWrite, errorCodePtr)
364
    ClientData instanceData;            /* File state. */
365
    char *buf;                          /* The data buffer. */
366
    int toWrite;                        /* How many bytes to write? */
367
    int *errorCodePtr;                  /* Where to store error code. */
368
{
369
    FileState *fsPtr = (FileState *) instanceData;
370
    int written;
371
 
372
    *errorCodePtr = 0;
373
    written = write(fsPtr->fd, buf, (size_t) toWrite);
374
    if (written > -1) {
375
        return written;
376
    }
377
    *errorCodePtr = errno;
378
    return -1;
379
}
380
 
381
/*
382
 *----------------------------------------------------------------------
383
 *
384
 * FileCloseProc --
385
 *
386
 *      This procedure is called from the generic IO level to perform
387
 *      channel-type-specific cleanup when a file based channel is closed.
388
 *
389
 * Results:
390
 *      0 if successful, errno if failed.
391
 *
392
 * Side effects:
393
 *      Closes the device of the channel.
394
 *
395
 *----------------------------------------------------------------------
396
 */
397
 
398
static int
399
FileCloseProc(instanceData, interp)
400
    ClientData instanceData;    /* File state. */
401
    Tcl_Interp *interp;         /* For error reporting - unused. */
402
{
403
    FileState *fsPtr = (FileState *) instanceData;
404
    FileState **nextPtrPtr;
405
    int errorCode = 0;
406
 
407
    Tcl_DeleteFileHandler(fsPtr->fd);
408
    if (!TclInExit()
409
            || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) {
410
        if (close(fsPtr->fd) < 0) {
411
            errorCode = errno;
412
        }
413
    }
414
    for (nextPtrPtr = &firstFilePtr; (*nextPtrPtr) != NULL;
415
         nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
416
        if ((*nextPtrPtr) == fsPtr) {
417
            (*nextPtrPtr) = fsPtr->nextPtr;
418
            break;
419
        }
420
    }
421
    ckfree((char *) fsPtr);
422
    return errorCode;
423
}
424
 
425
/*
426
 *----------------------------------------------------------------------
427
 *
428
 * FileSeekProc --
429
 *
430
 *      This procedure is called by the generic IO level to move the
431
 *      access point in a file based channel.
432
 *
433
 * Results:
434
 *      -1 if failed, the new position if successful. An output
435
 *      argument contains the POSIX error code if an error occurred,
436
 *      or zero.
437
 *
438
 * Side effects:
439
 *      Moves the location at which the channel will be accessed in
440
 *      future operations.
441
 *
442
 *----------------------------------------------------------------------
443
 */
444
 
445
static int
446
FileSeekProc(instanceData, offset, mode, errorCodePtr)
447
    ClientData instanceData;                    /* File state. */
448
    long offset;                                /* Offset to seek to. */
449
    int mode;                                   /* Relative to where
450
                                                 * should we seek? Can be
451
                                                 * one of SEEK_START,
452
                                                 * SEEK_SET or SEEK_END. */
453
    int *errorCodePtr;                          /* To store error code. */
454
{
455
    FileState *fsPtr = (FileState *) instanceData;
456
    int newLoc;
457
 
458
    newLoc = lseek(fsPtr->fd, offset, mode);
459
 
460
    *errorCodePtr = (newLoc == -1) ? errno : 0;
461
    return newLoc;
462
}
463
 
464
/*
465
 *----------------------------------------------------------------------
466
 *
467
 * FileWatchProc --
468
 *
469
 *      Initialize the notifier to watch the fd from this channel.
470
 *
471
 * Results:
472
 *      None.
473
 *
474
 * Side effects:
475
 *      Sets up the notifier so that a future event on the channel will
476
 *      be seen by Tcl.
477
 *
478
 *----------------------------------------------------------------------
479
 */
480
 
481
static void
482
FileWatchProc(instanceData, mask)
483
    ClientData instanceData;            /* The file state. */
484
    int mask;                           /* Events of interest; an OR-ed
485
                                         * combination of TCL_READABLE,
486
                                         * TCL_WRITABLE and TCL_EXCEPTION. */
487
{
488
    FileState *fsPtr = (FileState *) instanceData;
489
 
490
    /*
491
     * Make sure we only register for events that are valid on this file.
492
     * Note that we are passing Tcl_NotifyChannel directly to
493
     * Tcl_CreateFileHandler with the channel pointer as the client data.
494
     */
495
 
496
    mask &= fsPtr->validMask;
497
    if (mask) {
498
        Tcl_CreateFileHandler(fsPtr->fd, mask,
499
                (Tcl_FileProc *) Tcl_NotifyChannel,
500
                (ClientData) fsPtr->channel);
501
    } else {
502
        Tcl_DeleteFileHandler(fsPtr->fd);
503
    }
504
}
505
 
506
/*
507
 *----------------------------------------------------------------------
508
 *
509
 * FileGetHandleProc --
510
 *
511
 *      Called from Tcl_GetChannelFile to retrieve OS handles from
512
 *      a file based channel.
513
 *
514
 * Results:
515
 *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
516
 *      there is no handle for the specified direction.
517
 *
518
 * Side effects:
519
 *      None.
520
 *
521
 *----------------------------------------------------------------------
522
 */
523
 
524
static int
525
FileGetHandleProc(instanceData, direction, handlePtr)
526
    ClientData instanceData;    /* The file state. */
527
    int direction;              /* TCL_READABLE or TCL_WRITABLE */
528
    ClientData *handlePtr;      /* Where to store the handle.  */
529
{
530
    FileState *fsPtr = (FileState *) instanceData;
531
 
532
    if (direction & fsPtr->validMask) {
533
        *handlePtr = (ClientData) fsPtr->fd;
534
        return TCL_OK;
535
    } else {
536
        return TCL_ERROR;
537
    }
538
}
539
 
540
/*
541
 *----------------------------------------------------------------------
542
 *
543
 * TtySetOptionProc --
544
 *
545
 *      Sets an option on a channel.
546
 *
547
 * Results:
548
 *      A standard Tcl result. Also sets interp->result on error if
549
 *      interp is not NULL.
550
 *
551
 * Side effects:
552
 *      May modify an option on a device.
553
 *      Sets Error message if needed (by calling Tcl_BadChannelOption).
554
 *
555
 *----------------------------------------------------------------------
556
 */
557
 
558
static int
559
TtySetOptionProc(instanceData, interp, optionName, value)
560
    ClientData instanceData;    /* File state. */
561
    Tcl_Interp *interp;         /* For error reporting - can be NULL. */
562
    char *optionName;           /* Which option to set? */
563
    char *value;                /* New value for option. */
564
{
565
    FileState *fsPtr = (FileState *) instanceData;
566
    unsigned int len;
567
    TtyAttrs tty;
568
 
569
    len = strlen(optionName);
570
    if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {
571
        if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
572
                &tty.stop) != TCL_OK) {
573
            return TCL_ERROR;
574
        }
575
        /*
576
         * system calls results should be checked there. -- dl
577
         */
578
 
579
        TtySetAttributes(fsPtr->fd, &tty);
580
        return TCL_OK;
581
    } else {
582
        return Tcl_BadChannelOption(interp, optionName, "mode");
583
    }
584
}
585
 
586
/*
587
 *----------------------------------------------------------------------
588
 *
589
 * TtyGetOptionProc --
590
 *
591
 *      Gets a mode associated with an IO channel. If the optionName arg
592
 *      is non NULL, retrieves the value of that option. If the optionName
593
 *      arg is NULL, retrieves a list of alternating option names and
594
 *      values for the given channel.
595
 *
596
 * Results:
597
 *      A standard Tcl result. Also sets the supplied DString to the
598
 *      string value of the option(s) returned.
599
 *
600
 * Side effects:
601
 *      The string returned by this function is in static storage and
602
 *      may be reused at any time subsequent to the call.
603
 *      Sets Error message if needed (by calling Tcl_BadChannelOption).
604
 *
605
 *----------------------------------------------------------------------
606
 */
607
 
608
static int
609
TtyGetOptionProc(instanceData, interp, optionName, dsPtr)
610
    ClientData instanceData;    /* File state. */
611
    Tcl_Interp *interp;         /* For error reporting - can be NULL. */
612
    char *optionName;           /* Option to get. */
613
    Tcl_DString *dsPtr;         /* Where to store value(s). */
614
{
615
    FileState *fsPtr = (FileState *) instanceData;
616
    unsigned int len;
617
    char buf[32];
618
    TtyAttrs tty;
619
 
620
    if (optionName == NULL) {
621
        Tcl_DStringAppendElement(dsPtr, "-mode");
622
        len = 0;
623
    } else {
624
        len = strlen(optionName);
625
    }
626
    if ((len == 0) ||
627
            ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {
628
        TtyGetAttributes(fsPtr->fd, &tty);
629
        sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
630
        Tcl_DStringAppendElement(dsPtr, buf);
631
        return TCL_OK;
632
    } else {
633
        return Tcl_BadChannelOption(interp, optionName, "mode");
634
    }
635
}
636
 
637
#undef DIRECT_BAUD
638
#ifdef B4800
639
#   if (B4800 == 4800)
640
#       define DIRECT_BAUD
641
#   endif
642
#endif
643
 
644
#ifdef DIRECT_BAUD
645
#   define TtyGetSpeed(baud)   ((unsigned) (baud))
646
#   define TtyGetBaud(speed)   ((int) (speed))
647
#else
648
 
649
static struct {int baud; unsigned long speed;} speeds[] = {
650
#ifdef B0
651
    {0, B0},
652
#endif
653
#ifdef B50
654
    {50, B50},
655
#endif
656
#ifdef B75
657
    {75, B75},
658
#endif
659
#ifdef B110
660
    {110, B110},
661
#endif
662
#ifdef B134
663
    {134, B134},
664
#endif
665
#ifdef B150
666
    {150, B150},
667
#endif
668
#ifdef B200
669
    {200, B200},
670
#endif
671
#ifdef B300
672
    {300, B300},
673
#endif
674
#ifdef B600
675
    {600, B600},
676
#endif
677
#ifdef B1200
678
    {1200, B1200},
679
#endif
680
#ifdef B1800
681
    {1800, B1800},
682
#endif
683
#ifdef B2400
684
    {2400, B2400},
685
#endif
686
#ifdef B4800
687
    {4800, B4800},
688
#endif
689
#ifdef B9600
690
    {9600, B9600},
691
#endif
692
#ifdef B14400
693
    {14400, B14400},
694
#endif
695
#ifdef B19200
696
    {19200, B19200},
697
#endif
698
#ifdef EXTA
699
    {19200, EXTA},
700
#endif
701
#ifdef B28800
702
    {28800, B28800},
703
#endif
704
#ifdef B38400
705
    {38400, B38400},
706
#endif
707
#ifdef EXTB
708
    {38400, EXTB},
709
#endif
710
#ifdef B57600
711
    {57600, B57600},
712
#endif
713
#ifdef _B57600
714
    {57600, _B57600},
715
#endif
716
#ifdef B76800
717
    {76800, B76800},
718
#endif
719
#ifdef B115200
720
    {115200, B115200},
721
#endif
722
#ifdef _B115200
723
    {115200, _B115200},
724
#endif
725
#ifdef B153600
726
    {153600, B153600},
727
#endif
728
#ifdef B230400
729
    {230400, B230400},
730
#endif
731
#ifdef B307200
732
    {307200, B307200},
733
#endif
734
#ifdef B460800
735
    {460800, B460800},
736
#endif
737
    {-1, 0}
738
};
739
 
740
/*
741
 *---------------------------------------------------------------------------
742
 *
743
 * TtyGetSpeed --
744
 *
745
 *      Given a baud rate, get the mask value that should be stored in
746
 *      the termios, termio, or sgttyb structure in order to select that
747
 *      baud rate.
748
 *
749
 * Results:
750
 *      As above.
751
 *
752
 * Side effects:
753
 *      None.
754
 *
755
 *---------------------------------------------------------------------------
756
 */
757
 
758
static unsigned long
759
TtyGetSpeed(baud)
760
    int baud;                   /* The baud rate to look up. */
761
{
762
    int bestIdx, bestDiff, i, diff;
763
 
764
    bestIdx = 0;
765
    bestDiff = 1000000;
766
 
767
    /*
768
     * If the baud rate does not correspond to one of the known mask values,
769
     * choose the mask value whose baud rate is closest to the specified
770
     * baud rate.
771
     */
772
 
773
    for (i = 0; speeds[i].baud >= 0; i++) {
774
        diff = speeds[i].baud - baud;
775
        if (diff < 0) {
776
            diff = -diff;
777
        }
778
        if (diff < bestDiff) {
779
            bestIdx = i;
780
            bestDiff = diff;
781
        }
782
    }
783
    return speeds[bestIdx].speed;
784
}
785
 
786
/*
787
 *---------------------------------------------------------------------------
788
 *
789
 * TtyGetBaud --
790
 *
791
 *      Given a speed mask value from a termios, termio, or sgttyb
792
 *      structure, get the baus rate that corresponds to that mask value.
793
 *
794
 * Results:
795
 *      As above.  If the mask value was not recognized, 0 is returned.
796
 *
797
 * Side effects:
798
 *      None.
799
 *
800
 *---------------------------------------------------------------------------
801
 */
802
 
803
static int
804
TtyGetBaud(speed)
805
    unsigned long speed;        /* Speed mask value to look up. */
806
{
807
    int i;
808
 
809
    for (i = 0; speeds[i].baud >= 0; i++) {
810
        if (speeds[i].speed == speed) {
811
            return speeds[i].baud;
812
        }
813
    }
814
    return 0;
815
}
816
 
817
#endif  /* !DIRECT_BAUD */
818
 
819
 
820
/*
821
 *---------------------------------------------------------------------------
822
 *
823
 * TtyInit --
824
 *
825
 *      Given file descriptor that refers to a serial port,
826
 *      initialize the serial port to a set of sane values so that
827
 *      Tcl can talk to a device located on the serial port.
828
 *
829
 * Results:
830
 *      None.
831
 *
832
 * Side effects:
833
 *      Serial device initialized.
834
 *
835
 *---------------------------------------------------------------------------
836
 */
837
 
838
static void
839
TtyInit(fd)
840
    int fd;                     /* Open file descriptor for serial port to
841
                                 * be initialized. */
842
{
843
#ifdef USE_TERMIOS
844
    struct termios termios;
845
 
846
    tcgetattr(fd, &termios);
847
    termios.c_iflag = IGNBRK;
848
    termios.c_oflag = 0;
849
    termios.c_lflag = 0;
850
    termios.c_cflag |= CREAD;
851
    termios.c_cc[VMIN] = 60;
852
    termios.c_cc[VTIME] = 2;
853
    tcsetattr(fd, TCSANOW, &termios);
854
#else   /* !USE_TERMIOS */
855
#ifdef USE_TERMIO
856
    struct termio termio;
857
 
858
    ioctl(fd, TCGETA, &termio);
859
    termio.c_iflag = IGNBRK;
860
    termio.c_oflag = 0;
861
    termio.c_lflag = 0;
862
    termio.c_cflag |= CREAD;
863
    termio.c_cc[VMIN] = 60;
864
    termio.c_cc[VTIME] = 2;
865
    ioctl(fd, TCSETAW, &termio);
866
#else   /* !USE_TERMIO */
867
#ifdef USE_SGTTY
868
    struct sgttyb sgttyb;
869
 
870
    ioctl(fd, TIOCGETP, &sgttyb);
871
    sgttyb.sg_flags &= (EVENP | ODDP);
872
    sgttyb.sg_flags |= RAW;
873
    ioctl(fd, TIOCSETP, &sgttyb);
874
#endif  /* USE_SGTTY */
875
#endif  /* !USE_TERMIO */
876
#endif  /* !USE_TERMIOS */
877
}
878
 
879
/*
880
 *---------------------------------------------------------------------------
881
 *
882
 * TtyGetAttributes --
883
 *
884
 *      Get the current attributes of the specified serial device.
885
 *
886
 * Results:
887
 *      None.
888
 *
889
 * Side effects:
890
 *      None.
891
 *
892
 *---------------------------------------------------------------------------
893
 */
894
 
895
static void
896
TtyGetAttributes(fd, ttyPtr)
897
    int fd;                     /* Open file descriptor for serial port to
898
                                 * be queried. */
899
    TtyAttrs *ttyPtr;           /* Buffer filled with serial port
900
                                 * attributes. */
901
{
902
#ifdef USE_TERMIOS
903
    int parity, data;
904
    struct termios termios;
905
 
906
    tcgetattr(fd, &termios);
907
    ttyPtr->baud = TtyGetBaud(cfgetospeed(&termios));
908
 
909
    parity = 'n';
910
#ifdef PAREXT
911
    switch ((int) (termios.c_cflag & (PARENB | PARODD | PAREXT))) {
912
        case PARENB                   : parity = 'e'; break;
913
        case PARENB | PARODD          : parity = 'o'; break;
914
        case PARENB |          PAREXT : parity = 's'; break;
915
        case PARENB | PARODD | PAREXT : parity = 'm'; break;
916
    }
917
#else   /* !PAREXT */
918
    switch ((int) (termios.c_cflag & (PARENB | PARODD))) {
919
        case PARENB                   : parity = 'e'; break;
920
        case PARENB | PARODD          : parity = 'o'; break;
921
    }
922
#endif  /* !PAREXT */
923
    ttyPtr->parity = parity;
924
 
925
    data = termios.c_cflag & CSIZE;
926
    ttyPtr->data = (data == CS5) ? 5 : (data == CS6) ? 6 :
927
            (data == CS7) ? 7 : 8;
928
 
929
    ttyPtr->stop = (termios.c_cflag & CSTOPB) ? 2 : 1;
930
#else   /* !USE_TERMIOS */
931
#ifdef USE_TERMIO
932
    int parity, data;
933
    struct termio termio;
934
 
935
    ioctl(fd, TCGETA, &termio);
936
    ttyPtr->baud = TtyGetBaud(termio.c_cflag & CBAUD);
937
    parity = 'n';
938
    switch (termio.c_cflag & (PARENB | PARODD | PAREXT)) {
939
        case PARENB                   : parity = 'e'; break;
940
        case PARENB | PARODD          : parity = 'o'; break;
941
        case PARENB |          PAREXT : parity = 's'; break;
942
        case PARENB | PARODD | PAREXT : parity = 'm'; break;
943
    }
944
    ttyPtr->parity = parity;
945
 
946
    data = termio.c_cflag & CSIZE;
947
    ttyPtr->data = (data == CS5) ? 5 : (data == CS6) ? 6 :
948
            (data == CS7) ? 7 : 8;
949
 
950
    ttyPtr->stop = (termio.c_cflag & CSTOPB) ? 2 : 1;
951
#else   /* !USE_TERMIO */
952
#ifdef USE_SGTTY
953
    int parity;
954
    struct sgttyb sgttyb;
955
 
956
    ioctl(fd, TIOCGETP, &sgttyb);
957
    ttyPtr->baud = TtyGetBaud(sgttyb.sg_ospeed);
958
    parity = 'n';
959
    if (sgttyb.sg_flags & EVENP) {
960
        parity = 'e';
961
    } else if (sgttyb.sg_flags & ODDP) {
962
        parity = 'o';
963
    }
964
    ttyPtr->parity = parity;
965
    ttyPtr->data = (sgttyb.sg_flags & (EVENP | ODDP)) ? 7 : 8;
966
    ttyPtr->stop = 1;
967
#else   /* !USE_SGTTY */
968
    ttyPtr->baud = 0;
969
    ttyPtr->parity = 'n';
970
    ttyPtr->data = 0;
971
    ttyPtr->stop = 0;
972
#endif  /* !USE_SGTTY */
973
#endif  /* !USE_TERMIO */
974
#endif  /* !USE_TERMIOS */
975
}
976
 
977
/*
978
 *---------------------------------------------------------------------------
979
 *
980
 * TtySetAttributes --
981
 *
982
 *      Set the current attributes of the specified serial device.
983
 *
984
 * Results:
985
 *      None.
986
 *
987
 * Side effects:
988
 *      None.
989
 *
990
 *---------------------------------------------------------------------------
991
 */
992
 
993
static void
994
TtySetAttributes(fd, ttyPtr)
995
    int fd;                     /* Open file descriptor for serial port to
996
                                 * be modified. */
997
    TtyAttrs *ttyPtr;           /* Buffer containing new attributes for
998
                                 * serial port. */
999
{
1000
#ifdef USE_TERMIOS
1001
    int parity, data, flag;
1002
    struct termios termios;
1003
 
1004
    tcgetattr(fd, &termios);
1005
    cfsetospeed(&termios, TtyGetSpeed(ttyPtr->baud));
1006
    cfsetispeed(&termios, TtyGetSpeed(ttyPtr->baud));
1007
 
1008
    flag = 0;
1009
    parity = ttyPtr->parity;
1010
    if (parity != 'n') {
1011
        flag |= PARENB;
1012
#ifdef PAREXT
1013
        termios.c_cflag &= ~PAREXT;
1014
        if ((parity == 'm') || (parity == 's')) {
1015
            flag |= PAREXT;
1016
        }
1017
#endif
1018
        if ((parity == 'm') || (parity == 'o')) {
1019
            flag |= PARODD;
1020
        }
1021
    }
1022
    data = ttyPtr->data;
1023
    flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
1024
    if (ttyPtr->stop == 2) {
1025
        flag |= CSTOPB;
1026
    }
1027
 
1028
    termios.c_cflag &= ~(PARENB | PARODD | CSIZE | CSTOPB);
1029
    termios.c_cflag |= flag;
1030
    tcsetattr(fd, TCSANOW, &termios);
1031
 
1032
#else   /* !USE_TERMIOS */
1033
#ifdef USE_TERMIO
1034
    int parity, data, flag;
1035
    struct termio termio;
1036
 
1037
    ioctl(fd, TCGETA, &termio);
1038
    termio.c_cflag &= ~CBAUD;
1039
    termio.c_cflag |= TtyGetSpeed(ttyPtr->baud);
1040
 
1041
    flag = 0;
1042
    parity = ttyPtr->parity;
1043
    if (parity != 'n') {
1044
        flag |= PARENB;
1045
        if ((parity == 'm') || (parity == 's')) {
1046
            flag |= PAREXT;
1047
        }
1048
        if ((parity == 'm') || (parity == 'o')) {
1049
            flag |= PARODD;
1050
        }
1051
    }
1052
    data = ttyPtr->data;
1053
    flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
1054
    if (ttyPtr->stop == 2) {
1055
        flag |= CSTOPB;
1056
    }
1057
 
1058
    termio.c_cflag &= ~(PARENB | PARODD | PAREXT | CSIZE | CSTOPB);
1059
    termio.c_cflag |= flag;
1060
    ioctl(fd, TCSETAW, &termio);
1061
 
1062
#else   /* !USE_TERMIO */
1063
#ifdef USE_SGTTY
1064
    int parity;
1065
    struct sgttyb sgttyb;
1066
 
1067
    ioctl(fd, TIOCGETP, &sgttyb);
1068
    sgttyb.sg_ospeed = TtyGetSpeed(ttyPtr->baud);
1069
    sgttyb.sg_ispeed = TtyGetSpeed(ttyPtr->baud);
1070
 
1071
    parity = ttyPtr->parity;
1072
    if (parity == 'e') {
1073
        sgttyb.sg_flags &= ~ODDP;
1074
        sgttyb.sg_flags |= EVENP;
1075
    } else if (parity == 'o') {
1076
        sgttyb.sg_flags &= ~EVENP;
1077
        sgttyb.sg_flags |= ODDP;
1078
    }
1079
    ioctl(fd, TIOCSETP, &sgttyb);
1080
#endif  /* USE_SGTTY */
1081
#endif  /* !USE_TERMIO */
1082
#endif  /* !USE_TERMIOS */
1083
}
1084
 
1085
/*
1086
 *---------------------------------------------------------------------------
1087
 *
1088
 * TtyParseMode --
1089
 *
1090
 *      Parse the "-mode" argument to the fconfigure command.  The argument
1091
 *      is of the form baud,parity,data,stop.
1092
 *
1093
 * Results:
1094
 *      The return value is TCL_OK if the argument was successfully
1095
 *      parsed, TCL_ERROR otherwise.  If TCL_ERROR is returned, an
1096
 *      error message is left in interp->result (if interp is non-NULL).
1097
 *
1098
 * Side effects:
1099
 *      None.
1100
 *
1101
 *---------------------------------------------------------------------------
1102
 */
1103
 
1104
static int
1105
TtyParseMode(interp, mode, speedPtr, parityPtr, dataPtr, stopPtr)
1106
    Tcl_Interp *interp;         /* If non-NULL, interp for error return. */
1107
    CONST char *mode;           /* Mode string to be parsed. */
1108
    int *speedPtr;              /* Filled with baud rate from mode string. */
1109
    int *parityPtr;             /* Filled with parity from mode string. */
1110
    int *dataPtr;               /* Filled with data bits from mode string. */
1111
    int *stopPtr;               /* Filled with stop bits from mode string. */
1112
{
1113
    int i, end;
1114
    char parity;
1115
    static char *bad = "bad value for -mode";
1116
 
1117
    i = sscanf(mode, "%d,%c,%d,%d%n", speedPtr, &parity, dataPtr,
1118
            stopPtr, &end);
1119
    if ((i != 4) || (mode[end] != '\0')) {
1120
        if (interp != NULL) {
1121
            Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop",
1122
                    NULL);
1123
        }
1124
        return TCL_ERROR;
1125
    }
1126
    if (strchr("noems", parity) == NULL) {
1127
        if (interp != NULL) {
1128
            Tcl_AppendResult(interp, bad,
1129
                    " parity: should be n, o, e, m, or s", NULL);
1130
        }
1131
        return TCL_ERROR;
1132
    }
1133
    *parityPtr = parity;
1134
    if ((*dataPtr < 5) || (*dataPtr > 8)) {
1135
        if (interp != NULL) {
1136
            Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8",
1137
                    NULL);
1138
        }
1139
        return TCL_ERROR;
1140
    }
1141
    if ((*stopPtr < 0) || (*stopPtr > 2)) {
1142
        if (interp != NULL) {
1143
            Tcl_AppendResult(interp, bad, " stop: should be 1 or 2", NULL);
1144
        }
1145
        return TCL_ERROR;
1146
    }
1147
    return TCL_OK;
1148
}
1149
 
1150
/*
1151
 *----------------------------------------------------------------------
1152
 *
1153
 * TclpOpenFileChannel --
1154
 *
1155
 *      Open an file based channel on Unix systems.
1156
 *
1157
 * Results:
1158
 *      The new channel or NULL. If NULL, the output argument
1159
 *      errorCodePtr is set to a POSIX error and an error message is
1160
 *      left in interp->result if interp is not NULL.
1161
 *
1162
 * Side effects:
1163
 *      May open the channel and may cause creation of a file on the
1164
 *      file system.
1165
 *
1166
 *----------------------------------------------------------------------
1167
 */
1168
 
1169
Tcl_Channel
1170
TclpOpenFileChannel(interp, fileName, modeString, permissions)
1171
    Tcl_Interp *interp;                 /* Interpreter for error reporting;
1172
                                         * can be NULL. */
1173
    char *fileName;                     /* Name of file to open. */
1174
    char *modeString;                   /* A list of POSIX open modes or
1175
                                         * a string such as "rw". */
1176
    int permissions;                    /* If the open involves creating a
1177
                                         * file, with what modes to create
1178
                                         * it? */
1179
{
1180
    int fd, seekFlag, mode, channelPermissions;
1181
    FileState *fsPtr;
1182
    char *nativeName, channelName[20];
1183
    Tcl_DString buffer;
1184
    Tcl_ChannelType *channelTypePtr;
1185
 
1186
    mode = TclGetOpenMode(interp, modeString, &seekFlag);
1187
    if (mode == -1) {
1188
        return NULL;
1189
    }
1190
    switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1191
        case O_RDONLY:
1192
            channelPermissions = TCL_READABLE;
1193
            break;
1194
        case O_WRONLY:
1195
            channelPermissions = TCL_WRITABLE;
1196
            break;
1197
        case O_RDWR:
1198
            channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1199
            break;
1200
        default:
1201
            /*
1202
             * This may occurr if modeString was "", for example.
1203
             */
1204
            panic("TclpOpenFileChannel: invalid mode value");
1205
            return NULL;
1206
    }
1207
 
1208
    nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
1209
    if (nativeName == NULL) {
1210
        return NULL;
1211
    }
1212
    fd = open(nativeName, mode, permissions);
1213
 
1214
    /*
1215
     * If nativeName is not NULL, the buffer is valid and we must free
1216
     * the storage.
1217
     */
1218
 
1219
    Tcl_DStringFree(&buffer);
1220
 
1221
    if (fd < 0) {
1222
        if (interp != (Tcl_Interp *) NULL) {
1223
            Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
1224
                    Tcl_PosixError(interp), (char *) NULL);
1225
        }
1226
        return NULL;
1227
    }
1228
 
1229
    /*
1230
     * Set close-on-exec flag on the fd so that child processes will not
1231
     * inherit this fd.
1232
     */
1233
 
1234
    fcntl(fd, F_SETFD, FD_CLOEXEC);
1235
 
1236
    sprintf(channelName, "file%d", fd);
1237
 
1238
    fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1239
    fsPtr->nextPtr = firstFilePtr;
1240
    firstFilePtr = fsPtr;
1241
    fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
1242
    fsPtr->fd = fd;
1243
 
1244
    if (isatty(fd)) {
1245
        /*
1246
         * Initialize the serial port to a set of sane parameters.
1247
         * Especially important if the remote device is set to echo and
1248
         * the serial port driver was also set to echo -- as soon as a char
1249
         * were sent to the serial port, the remote device would echo it,
1250
         * then the serial driver would echo it back to the device, etc.
1251
         */
1252
 
1253
        TtyInit(fd);
1254
        channelTypePtr = &ttyChannelType;
1255
    } else {
1256
        channelTypePtr = &fileChannelType;
1257
    }
1258
 
1259
    fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1260
            (ClientData) fsPtr, channelPermissions);
1261
 
1262
    if (seekFlag) {
1263
        if (Tcl_Seek(fsPtr->channel, 0, SEEK_END) < 0) {
1264
            if (interp != (Tcl_Interp *) NULL) {
1265
                Tcl_AppendResult(interp, "couldn't seek to end of file on \"",
1266
                        channelName, "\": ", Tcl_PosixError(interp), NULL);
1267
            }
1268
            Tcl_Close(NULL, fsPtr->channel);
1269
            return NULL;
1270
        }
1271
    }
1272
 
1273
    if (channelTypePtr == &ttyChannelType) {
1274
        /*
1275
         * Gotcha.  Most modems need a "\r" at the end of the command
1276
         * sequence.  If you just send "at\n", the modem will not respond
1277
         * with "OK" because it never got a "\r" to actually invoke the
1278
         * command.  So, by default, newlines are translated to "\r\n" on
1279
         * output to avoid "bug" reports that the serial port isn't working.
1280
         */
1281
 
1282
        if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
1283
                "auto crlf") != TCL_OK) {
1284
            Tcl_Close(NULL, fsPtr->channel);
1285
            return NULL;
1286
        }
1287
    }
1288
 
1289
    return fsPtr->channel;
1290
}
1291
 
1292
/*
1293
 *----------------------------------------------------------------------
1294
 *
1295
 * Tcl_MakeFileChannel --
1296
 *
1297
 *      Makes a Tcl_Channel from an existing OS level file handle.
1298
 *
1299
 * Results:
1300
 *      The Tcl_Channel created around the preexisting OS level file handle.
1301
 *
1302
 * Side effects:
1303
 *      None.
1304
 *
1305
 *----------------------------------------------------------------------
1306
 */
1307
 
1308
Tcl_Channel
1309
Tcl_MakeFileChannel(handle, mode)
1310
    ClientData handle;          /* OS level handle. */
1311
    int mode;                   /* ORed combination of TCL_READABLE and
1312
                                 * TCL_WRITABLE to indicate file mode. */
1313
{
1314
    FileState *fsPtr;
1315
    char channelName[20];
1316
    int fd = (int) handle;
1317
 
1318
    if (mode == 0) {
1319
        return NULL;
1320
    }
1321
 
1322
    sprintf(channelName, "file%d", fd);
1323
 
1324
    /*
1325
     * Look to see if a channel with this fd and the same mode already exists.
1326
     * If the fd is used, but the mode doesn't match, return NULL.
1327
     */
1328
 
1329
    for (fsPtr = firstFilePtr; fsPtr != NULL; fsPtr = fsPtr->nextPtr) {
1330
        if (fsPtr->fd == fd) {
1331
            return (mode == fsPtr->validMask) ? fsPtr->channel : NULL;
1332
        }
1333
    }
1334
 
1335
    fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
1336
    fsPtr->nextPtr = firstFilePtr;
1337
    firstFilePtr = fsPtr;
1338
    fsPtr->fd = fd;
1339
    fsPtr->validMask = mode | TCL_EXCEPTION;
1340
    fsPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
1341
            (ClientData) fsPtr, mode);
1342
 
1343
    return fsPtr->channel;
1344
}
1345
 
1346
/*
1347
 *----------------------------------------------------------------------
1348
 *
1349
 * TcpBlockModeProc --
1350
 *
1351
 *      This procedure is invoked by the generic IO level to set blocking
1352
 *      and nonblocking mode on a TCP socket based channel.
1353
 *
1354
 * Results:
1355
 *      0 if successful, errno when failed.
1356
 *
1357
 * Side effects:
1358
 *      Sets the device into blocking or nonblocking mode.
1359
 *
1360
 *----------------------------------------------------------------------
1361
 */
1362
 
1363
        /* ARGSUSED */
1364
static int
1365
TcpBlockModeProc(instanceData, mode)
1366
    ClientData instanceData;            /* Socket state. */
1367
    int mode;                           /* The mode to set. Can be one of
1368
                                         * TCL_MODE_BLOCKING or
1369
                                         * TCL_MODE_NONBLOCKING. */
1370
{
1371
    TcpState *statePtr = (TcpState *) instanceData;
1372
    int setting;
1373
 
1374
#ifndef USE_FIONBIO
1375
    setting = fcntl(statePtr->fd, F_GETFL);
1376
    if (mode == TCL_MODE_BLOCKING) {
1377
        statePtr->flags &= (~(TCP_ASYNC_SOCKET));
1378
        setting &= (~(O_NONBLOCK));
1379
    } else {
1380
        statePtr->flags |= TCP_ASYNC_SOCKET;
1381
        setting |= O_NONBLOCK;
1382
    }
1383
    if (fcntl(statePtr->fd, F_SETFL, setting) < 0) {
1384
        return errno;
1385
    }
1386
#endif
1387
 
1388
#ifdef  USE_FIONBIO
1389
    if (mode == TCL_MODE_BLOCKING) {
1390
        statePtr->flags &= (~(TCP_ASYNC_SOCKET));
1391
        setting = 0;
1392
        if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
1393
            return errno;
1394
        }
1395
    } else {
1396
        statePtr->flags |= TCP_ASYNC_SOCKET;
1397
        setting = 1;
1398
        if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
1399
            return errno;
1400
        }
1401
    }
1402
#endif
1403
 
1404
    return 0;
1405
}
1406
 
1407
/*
1408
 *----------------------------------------------------------------------
1409
 *
1410
 * WaitForConnect --
1411
 *
1412
 *      Waits for a connection on an asynchronously opened socket to
1413
 *      be completed.
1414
 *
1415
 * Results:
1416
 *      None.
1417
 *
1418
 * Side effects:
1419
 *      The socket is connected after this function returns.
1420
 *
1421
 *----------------------------------------------------------------------
1422
 */
1423
 
1424
static int
1425
WaitForConnect(statePtr, errorCodePtr)
1426
    TcpState *statePtr;         /* State of the socket. */
1427
    int *errorCodePtr;          /* Where to store errors? */
1428
{
1429
    int timeOut;                /* How long to wait. */
1430
    int state;                  /* Of calling TclWaitForFile. */
1431
    int flags;                  /* fcntl flags for the socket. */
1432
 
1433
    /*
1434
     * If an asynchronous connect is in progress, attempt to wait for it
1435
     * to complete before reading.
1436
     */
1437
 
1438
    if (statePtr->flags & TCP_ASYNC_CONNECT) {
1439
        if (statePtr->flags & TCP_ASYNC_SOCKET) {
1440
            timeOut = 0;
1441
        } else {
1442
            timeOut = -1;
1443
        }
1444
        errno = 0;
1445
        state = TclUnixWaitForFile(statePtr->fd,
1446
                TCL_WRITABLE | TCL_EXCEPTION, timeOut);
1447
        if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
1448
#ifndef USE_FIONBIO
1449
            flags = fcntl(statePtr->fd, F_GETFL);
1450
            flags &= (~(O_NONBLOCK));
1451
            (void) fcntl(statePtr->fd, F_SETFL, flags);
1452
#endif
1453
 
1454
#ifdef  USE_FIONBIO
1455
            flags = 0;
1456
            (void) ioctl(statePtr->fd, FIONBIO, &flags);
1457
#endif
1458
        }
1459
        if (state & TCL_EXCEPTION) {
1460
            return -1;
1461
        }
1462
        if (state & TCL_WRITABLE) {
1463
            statePtr->flags &= (~(TCP_ASYNC_CONNECT));
1464
        } else if (timeOut == 0) {
1465
            *errorCodePtr = errno = EWOULDBLOCK;
1466
            return -1;
1467
        }
1468
    }
1469
    return 0;
1470
}
1471
 
1472
/*
1473
 *----------------------------------------------------------------------
1474
 *
1475
 * TcpInputProc --
1476
 *
1477
 *      This procedure is invoked by the generic IO level to read input
1478
 *      from a TCP socket based channel.
1479
 *
1480
 *      NOTE: We cannot share code with FilePipeInputProc because here
1481
 *      we must use recv to obtain the input from the channel, not read.
1482
 *
1483
 * Results:
1484
 *      The number of bytes read is returned or -1 on error. An output
1485
 *      argument contains the POSIX error code on error, or zero if no
1486
 *      error occurred.
1487
 *
1488
 * Side effects:
1489
 *      Reads input from the input device of the channel.
1490
 *
1491
 *----------------------------------------------------------------------
1492
 */
1493
 
1494
        /* ARGSUSED */
1495
static int
1496
TcpInputProc(instanceData, buf, bufSize, errorCodePtr)
1497
    ClientData instanceData;            /* Socket state. */
1498
    char *buf;                          /* Where to store data read. */
1499
    int bufSize;                        /* How much space is available
1500
                                         * in the buffer? */
1501
    int *errorCodePtr;                  /* Where to store error code. */
1502
{
1503
    TcpState *statePtr = (TcpState *) instanceData;
1504
    int bytesRead, state;
1505
 
1506
    *errorCodePtr = 0;
1507
    state = WaitForConnect(statePtr, errorCodePtr);
1508
    if (state != 0) {
1509
        return -1;
1510
    }
1511
    bytesRead = recv(statePtr->fd, buf, bufSize, 0);
1512
    if (bytesRead > -1) {
1513
        return bytesRead;
1514
    }
1515
    if (errno == ECONNRESET) {
1516
 
1517
        /*
1518
         * Turn ECONNRESET into a soft EOF condition.
1519
         */
1520
 
1521
        return 0;
1522
    }
1523
    *errorCodePtr = errno;
1524
    return -1;
1525
}
1526
 
1527
/*
1528
 *----------------------------------------------------------------------
1529
 *
1530
 * TcpOutputProc --
1531
 *
1532
 *      This procedure is invoked by the generic IO level to write output
1533
 *      to a TCP socket based channel.
1534
 *
1535
 *      NOTE: We cannot share code with FilePipeOutputProc because here
1536
 *      we must use send, not write, to get reliable error reporting.
1537
 *
1538
 * Results:
1539
 *      The number of bytes written is returned. An output argument is
1540
 *      set to a POSIX error code if an error occurred, or zero.
1541
 *
1542
 * Side effects:
1543
 *      Writes output on the output device of the channel.
1544
 *
1545
 *----------------------------------------------------------------------
1546
 */
1547
 
1548
static int
1549
TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
1550
    ClientData instanceData;            /* Socket state. */
1551
    char *buf;                          /* The data buffer. */
1552
    int toWrite;                        /* How many bytes to write? */
1553
    int *errorCodePtr;                  /* Where to store error code. */
1554
{
1555
    TcpState *statePtr = (TcpState *) instanceData;
1556
    int written;
1557
    int state;                          /* Of waiting for connection. */
1558
 
1559
    *errorCodePtr = 0;
1560
    state = WaitForConnect(statePtr, errorCodePtr);
1561
    if (state != 0) {
1562
        return -1;
1563
    }
1564
    written = send(statePtr->fd, buf, toWrite, 0);
1565
    if (written > -1) {
1566
        return written;
1567
    }
1568
    *errorCodePtr = errno;
1569
    return -1;
1570
}
1571
 
1572
/*
1573
 *----------------------------------------------------------------------
1574
 *
1575
 * TcpCloseProc --
1576
 *
1577
 *      This procedure is invoked by the generic IO level to perform
1578
 *      channel-type-specific cleanup when a TCP socket based channel
1579
 *      is closed.
1580
 *
1581
 * Results:
1582
 *      0 if successful, the value of errno if failed.
1583
 *
1584
 * Side effects:
1585
 *      Closes the socket of the channel.
1586
 *
1587
 *----------------------------------------------------------------------
1588
 */
1589
 
1590
        /* ARGSUSED */
1591
static int
1592
TcpCloseProc(instanceData, interp)
1593
    ClientData instanceData;    /* The socket to close. */
1594
    Tcl_Interp *interp;         /* For error reporting - unused. */
1595
{
1596
    TcpState *statePtr = (TcpState *) instanceData;
1597
    int errorCode = 0;
1598
 
1599
    /*
1600
     * Delete a file handler that may be active for this socket if this
1601
     * is a server socket - the file handler was created automatically
1602
     * by Tcl as part of the mechanism to accept new client connections.
1603
     * Channel handlers are already deleted in the generic IO channel
1604
     * closing code that called this function, so we do not have to
1605
     * delete them here.
1606
     */
1607
 
1608
    Tcl_DeleteFileHandler(statePtr->fd);
1609
 
1610
    if (close(statePtr->fd) < 0) {
1611
        errorCode = errno;
1612
    }
1613
    ckfree((char *) statePtr);
1614
 
1615
    return errorCode;
1616
}
1617
 
1618
/*
1619
 *----------------------------------------------------------------------
1620
 *
1621
 * TcpGetOptionProc --
1622
 *
1623
 *      Computes an option value for a TCP socket based channel, or a
1624
 *      list of all options and their values.
1625
 *
1626
 *      Note: This code is based on code contributed by John Haxby.
1627
 *
1628
 * Results:
1629
 *      A standard Tcl result. The value of the specified option or a
1630
 *      list of all options and their values is returned in the
1631
 *      supplied DString. Sets Error message if needed.
1632
 *
1633
 * Side effects:
1634
 *      None.
1635
 *
1636
 *----------------------------------------------------------------------
1637
 */
1638
 
1639
static int
1640
TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
1641
    ClientData instanceData;     /* Socket state. */
1642
    Tcl_Interp *interp;          /* For error reporting - can be NULL. */
1643
    char *optionName;            /* Name of the option to
1644
                                  * retrieve the value for, or
1645
                                  * NULL to get all options and
1646
                                  * their values. */
1647
    Tcl_DString *dsPtr;          /* Where to store the computed
1648
                                  * value; initialized by caller. */
1649
{
1650
    TcpState *statePtr = (TcpState *) instanceData;
1651
    struct sockaddr_in sockname;
1652
    struct sockaddr_in peername;
1653
    struct hostent *hostEntPtr;
1654
    int size = sizeof(struct sockaddr_in);
1655
    size_t len = 0;
1656
    char buf[128];
1657
 
1658
    if (optionName != (char *) NULL) {
1659
        len = strlen(optionName);
1660
    }
1661
 
1662
    if ((len == 0) ||
1663
            ((len > 1) && (optionName[1] == 'p') &&
1664
                    (strncmp(optionName, "-peername", len) == 0))) {
1665
        if (getpeername(statePtr->fd, (struct sockaddr *) &peername, &size)
1666
                >= 0) {
1667
            if (len == 0) {
1668
                Tcl_DStringAppendElement(dsPtr, "-peername");
1669
                Tcl_DStringStartSublist(dsPtr);
1670
            }
1671
            Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1672
            hostEntPtr = gethostbyaddr((char *) &(peername.sin_addr),
1673
                    sizeof(peername.sin_addr), AF_INET);
1674
            if (hostEntPtr != (struct hostent *) NULL) {
1675
                Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1676
            } else {
1677
                Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
1678
            }
1679
            sprintf(buf, "%d", ntohs(peername.sin_port));
1680
            Tcl_DStringAppendElement(dsPtr, buf);
1681
            if (len == 0) {
1682
                Tcl_DStringEndSublist(dsPtr);
1683
            } else {
1684
                return TCL_OK;
1685
            }
1686
        } else {
1687
            /*
1688
             * getpeername failed - but if we were asked for all the options
1689
             * (len==0), don't flag an error at that point because it could
1690
             * be an fconfigure request on a server socket. (which have
1691
             * no peer). same must be done on win&mac.
1692
             */
1693
 
1694
            if (len) {
1695
                if (interp) {
1696
                    Tcl_AppendResult(interp, "can't get peername: ",
1697
                                     Tcl_PosixError(interp),
1698
                                     (char *) NULL);
1699
                }
1700
                return TCL_ERROR;
1701
            }
1702
        }
1703
    }
1704
 
1705
    if ((len == 0) ||
1706
            ((len > 1) && (optionName[1] == 's') &&
1707
                    (strncmp(optionName, "-sockname", len) == 0))) {
1708
        if (getsockname(statePtr->fd, (struct sockaddr *) &sockname, &size)
1709
                >= 0) {
1710
            if (len == 0) {
1711
                Tcl_DStringAppendElement(dsPtr, "-sockname");
1712
                Tcl_DStringStartSublist(dsPtr);
1713
            }
1714
            Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1715
            hostEntPtr = gethostbyaddr((char *) &(sockname.sin_addr),
1716
                    sizeof(sockname.sin_addr), AF_INET);
1717
            if (hostEntPtr != (struct hostent *) NULL) {
1718
                Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
1719
            } else {
1720
                Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
1721
            }
1722
            sprintf(buf, "%d", ntohs(sockname.sin_port));
1723
            Tcl_DStringAppendElement(dsPtr, buf);
1724
            if (len == 0) {
1725
                Tcl_DStringEndSublist(dsPtr);
1726
            } else {
1727
                return TCL_OK;
1728
            }
1729
        } else {
1730
            if (interp) {
1731
                Tcl_AppendResult(interp, "can't get sockname: ",
1732
                                 Tcl_PosixError(interp),
1733
                                 (char *) NULL);
1734
            }
1735
            return TCL_ERROR;
1736
        }
1737
    }
1738
 
1739
    if (len > 0) {
1740
        return Tcl_BadChannelOption(interp, optionName, "peername sockname");
1741
    }
1742
 
1743
    return TCL_OK;
1744
}
1745
 
1746
/*
1747
 *----------------------------------------------------------------------
1748
 *
1749
 * TcpWatchProc --
1750
 *
1751
 *      Initialize the notifier to watch the fd from this channel.
1752
 *
1753
 * Results:
1754
 *      None.
1755
 *
1756
 * Side effects:
1757
 *      Sets up the notifier so that a future event on the channel will
1758
 *      be seen by Tcl.
1759
 *
1760
 *----------------------------------------------------------------------
1761
 */
1762
 
1763
static void
1764
TcpWatchProc(instanceData, mask)
1765
    ClientData instanceData;            /* The socket state. */
1766
    int mask;                           /* Events of interest; an OR-ed
1767
                                         * combination of TCL_READABLE,
1768
                                         * TCL_WRITABLE and TCL_EXCEPTION. */
1769
{
1770
    TcpState *statePtr = (TcpState *) instanceData;
1771
 
1772
    /*
1773
     * Make sure we don't mess with server sockets since they will never
1774
     * be readable or writable at the Tcl level.  This keeps Tcl scripts
1775
     * from interfering with the -accept behavior.
1776
     */
1777
 
1778
    if (!statePtr->acceptProc) {
1779
        if (mask) {
1780
            Tcl_CreateFileHandler(statePtr->fd, mask,
1781
                    (Tcl_FileProc *) Tcl_NotifyChannel,
1782
                    (ClientData) statePtr->channel);
1783
        } else {
1784
            Tcl_DeleteFileHandler(statePtr->fd);
1785
        }
1786
    }
1787
}
1788
 
1789
/*
1790
 *----------------------------------------------------------------------
1791
 *
1792
 * TcpGetHandleProc --
1793
 *
1794
 *      Called from Tcl_GetChannelFile to retrieve OS handles from inside
1795
 *      a TCP socket based channel.
1796
 *
1797
 * Results:
1798
 *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1799
 *      there is no handle for the specified direction.
1800
 *
1801
 * Side effects:
1802
 *      None.
1803
 *
1804
 *----------------------------------------------------------------------
1805
 */
1806
 
1807
        /* ARGSUSED */
1808
static int
1809
TcpGetHandleProc(instanceData, direction, handlePtr)
1810
    ClientData instanceData;    /* The socket state. */
1811
    int direction;              /* Not used. */
1812
    ClientData *handlePtr;      /* Where to store the handle.  */
1813
{
1814
    TcpState *statePtr = (TcpState *) instanceData;
1815
 
1816
    *handlePtr = (ClientData)statePtr->fd;
1817
    return TCL_OK;
1818
}
1819
 
1820
/*
1821
 *----------------------------------------------------------------------
1822
 *
1823
 * CreateSocket --
1824
 *
1825
 *      This function opens a new socket in client or server mode
1826
 *      and initializes the TcpState structure.
1827
 *
1828
 * Results:
1829
 *      Returns a new TcpState, or NULL with an error in interp->result,
1830
 *      if interp is not NULL.
1831
 *
1832
 * Side effects:
1833
 *      Opens a socket.
1834
 *
1835
 *----------------------------------------------------------------------
1836
 */
1837
 
1838
static TcpState *
1839
CreateSocket(interp, port, host, server, myaddr, myport, async)
1840
    Tcl_Interp *interp;         /* For error reporting; can be NULL. */
1841
    int port;                   /* Port number to open. */
1842
    char *host;                 /* Name of host on which to open port.
1843
                                 * NULL implies INADDR_ANY */
1844
    int server;                 /* 1 if socket should be a server socket,
1845
                                 * else 0 for a client socket. */
1846
    char *myaddr;               /* Optional client-side address */
1847
    int myport;                 /* Optional client-side port */
1848
    int async;                  /* If nonzero and creating a client socket,
1849
                                 * attempt to do an async connect. Otherwise
1850
                                 * do a synchronous connect or bind. */
1851
{
1852
    int status, sock, asyncConnect, curState, origState;
1853
    struct sockaddr_in sockaddr;        /* socket address */
1854
    struct sockaddr_in mysockaddr;      /* Socket address for client */
1855
    TcpState *statePtr;
1856
 
1857
    sock = -1;
1858
    origState = 0;
1859
    if (! CreateSocketAddress(&sockaddr, host, port)) {
1860
        goto addressError;
1861
    }
1862
    if ((myaddr != NULL || myport != 0) &&
1863
            ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
1864
        goto addressError;
1865
    }
1866
 
1867
    sock = socket(AF_INET, SOCK_STREAM, 0);
1868
    if (sock < 0) {
1869
        goto addressError;
1870
    }
1871
 
1872
    /*
1873
     * Set the close-on-exec flag so that the socket will not get
1874
     * inherited by child processes.
1875
     */
1876
 
1877
    fcntl(sock, F_SETFD, FD_CLOEXEC);
1878
 
1879
    /*
1880
     * Set kernel space buffering
1881
     */
1882
 
1883
    TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
1884
 
1885
    asyncConnect = 0;
1886
    status = 0;
1887
    if (server) {
1888
 
1889
        /*
1890
         * Set up to reuse server addresses automatically and bind to the
1891
         * specified port.
1892
         */
1893
 
1894
        status = 1;
1895
        (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
1896
                sizeof(status));
1897
        status = bind(sock, (struct sockaddr *) &sockaddr,
1898
                sizeof(struct sockaddr));
1899
        if (status != -1) {
1900
            status = listen(sock, SOMAXCONN);
1901
        }
1902
    } else {
1903
        if (myaddr != NULL || myport != 0) {
1904
            curState = 1;
1905
            (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
1906
                    (char *) &curState, sizeof(curState));
1907
            status = bind(sock, (struct sockaddr *) &mysockaddr,
1908
                    sizeof(struct sockaddr));
1909
            if (status < 0) {
1910
                goto bindError;
1911
            }
1912
        }
1913
 
1914
        /*
1915
         * Attempt to connect. The connect may fail at present with an
1916
         * EINPROGRESS but at a later time it will complete. The caller
1917
         * will set up a file handler on the socket if she is interested in
1918
         * being informed when the connect completes.
1919
         */
1920
 
1921
        if (async) {
1922
#ifndef USE_FIONBIO
1923
            origState = fcntl(sock, F_GETFL);
1924
            curState = origState | O_NONBLOCK;
1925
            status = fcntl(sock, F_SETFL, curState);
1926
#endif
1927
 
1928
#ifdef  USE_FIONBIO
1929
            curState = 1;
1930
            status = ioctl(sock, FIONBIO, &curState);
1931
#endif            
1932
        } else {
1933
            status = 0;
1934
        }
1935
        if (status > -1) {
1936
            status = connect(sock, (struct sockaddr *) &sockaddr,
1937
                    sizeof(sockaddr));
1938
            if (status < 0) {
1939
                if (errno == EINPROGRESS) {
1940
                    asyncConnect = 1;
1941
                    status = 0;
1942
                }
1943
            }
1944
        }
1945
    }
1946
 
1947
bindError:
1948
    if (status < 0) {
1949
        if (interp != NULL) {
1950
            Tcl_AppendResult(interp, "couldn't open socket: ",
1951
                    Tcl_PosixError(interp), (char *) NULL);
1952
        }
1953
        if (sock != -1) {
1954
            close(sock);
1955
        }
1956
        return NULL;
1957
    }
1958
 
1959
    /*
1960
     * Allocate a new TcpState for this socket.
1961
     */
1962
 
1963
    statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
1964
    statePtr->flags = 0;
1965
    if (asyncConnect) {
1966
        statePtr->flags = TCP_ASYNC_CONNECT;
1967
    }
1968
    statePtr->fd = sock;
1969
 
1970
    return statePtr;
1971
 
1972
addressError:
1973
    if (sock != -1) {
1974
        close(sock);
1975
    }
1976
    if (interp != NULL) {
1977
        Tcl_AppendResult(interp, "couldn't open socket: ",
1978
                Tcl_PosixError(interp), (char *) NULL);
1979
    }
1980
    return NULL;
1981
}
1982
 
1983
/*
1984
 *----------------------------------------------------------------------
1985
 *
1986
 * CreateSocketAddress --
1987
 *
1988
 *      This function initializes a sockaddr structure for a host and port.
1989
 *
1990
 * Results:
1991
 *      1 if the host was valid, 0 if the host could not be converted to
1992
 *      an IP address.
1993
 *
1994
 * Side effects:
1995
 *      Fills in the *sockaddrPtr structure.
1996
 *
1997
 *----------------------------------------------------------------------
1998
 */
1999
 
2000
static int
2001
CreateSocketAddress(sockaddrPtr, host, port)
2002
    struct sockaddr_in *sockaddrPtr;    /* Socket address */
2003
    char *host;                         /* Host.  NULL implies INADDR_ANY */
2004
    int port;                           /* Port number */
2005
{
2006
    struct hostent *hostent;            /* Host database entry */
2007
    struct in_addr addr;                /* For 64/32 bit madness */
2008
 
2009
    (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
2010
    sockaddrPtr->sin_family = AF_INET;
2011
    sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
2012
    if (host == NULL) {
2013
        addr.s_addr = INADDR_ANY;
2014
    } else {
2015
        addr.s_addr = inet_addr(host);
2016
        if (addr.s_addr == -1) {
2017
            hostent = gethostbyname(host);
2018
            if (hostent != NULL) {
2019
                memcpy((VOID *) &addr,
2020
                        (VOID *) hostent->h_addr_list[0],
2021
                        (size_t) hostent->h_length);
2022
            } else {
2023
#ifdef  EHOSTUNREACH
2024
                errno = EHOSTUNREACH;
2025
#else
2026
#ifdef ENXIO
2027
                errno = ENXIO;
2028
#endif
2029
#endif
2030
                return 0;        /* error */
2031
            }
2032
        }
2033
    }
2034
 
2035
    /*
2036
     * NOTE: On 64 bit machines the assignment below is rumored to not
2037
     * do the right thing. Please report errors related to this if you
2038
     * observe incorrect behavior on 64 bit machines such as DEC Alphas.
2039
     * Should we modify this code to do an explicit memcpy?
2040
     */
2041
 
2042
    sockaddrPtr->sin_addr.s_addr = addr.s_addr;
2043
    return 1;   /* Success. */
2044
}
2045
 
2046
/*
2047
 *----------------------------------------------------------------------
2048
 *
2049
 * Tcl_OpenTcpClient --
2050
 *
2051
 *      Opens a TCP client socket and creates a channel around it.
2052
 *
2053
 * Results:
2054
 *      The channel or NULL if failed.  An error message is returned
2055
 *      in the interpreter on failure.
2056
 *
2057
 * Side effects:
2058
 *      Opens a client socket and creates a new channel.
2059
 *
2060
 *----------------------------------------------------------------------
2061
 */
2062
 
2063
Tcl_Channel
2064
Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
2065
    Tcl_Interp *interp;                 /* For error reporting; can be NULL. */
2066
    int port;                           /* Port number to open. */
2067
    char *host;                         /* Host on which to open port. */
2068
    char *myaddr;                       /* Client-side address */
2069
    int myport;                         /* Client-side port */
2070
    int async;                          /* If nonzero, attempt to do an
2071
                                         * asynchronous connect. Otherwise
2072
                                         * we do a blocking connect. */
2073
{
2074
    TcpState *statePtr;
2075
    char channelName[20];
2076
 
2077
    /*
2078
     * Create a new client socket and wrap it in a channel.
2079
     */
2080
 
2081
    statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
2082
    if (statePtr == NULL) {
2083
        return NULL;
2084
    }
2085
 
2086
    statePtr->acceptProc = NULL;
2087
    statePtr->acceptProcData = (ClientData) NULL;
2088
 
2089
    sprintf(channelName, "sock%d", statePtr->fd);
2090
 
2091
    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2092
            (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
2093
    if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
2094
            "auto crlf") == TCL_ERROR) {
2095
        Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
2096
        return NULL;
2097
    }
2098
    return statePtr->channel;
2099
}
2100
 
2101
/*
2102
 *----------------------------------------------------------------------
2103
 *
2104
 * Tcl_MakeTcpClientChannel --
2105
 *
2106
 *      Creates a Tcl_Channel from an existing client TCP socket.
2107
 *
2108
 * Results:
2109
 *      The Tcl_Channel wrapped around the preexisting TCP socket.
2110
 *
2111
 * Side effects:
2112
 *      None.
2113
 *
2114
 *----------------------------------------------------------------------
2115
 */
2116
 
2117
Tcl_Channel
2118
Tcl_MakeTcpClientChannel(sock)
2119
    ClientData sock;            /* The socket to wrap up into a channel. */
2120
{
2121
    TcpState *statePtr;
2122
    char channelName[20];
2123
 
2124
    statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2125
    statePtr->fd = (int) sock;
2126
    statePtr->acceptProc = NULL;
2127
    statePtr->acceptProcData = (ClientData) NULL;
2128
 
2129
    sprintf(channelName, "sock%d", statePtr->fd);
2130
 
2131
    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2132
            (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
2133
    if (Tcl_SetChannelOption((Tcl_Interp *) NULL, statePtr->channel,
2134
            "-translation", "auto crlf") == TCL_ERROR) {
2135
        Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
2136
        return NULL;
2137
    }
2138
    return statePtr->channel;
2139
}
2140
 
2141
/*
2142
 *----------------------------------------------------------------------
2143
 *
2144
 * Tcl_OpenTcpServer --
2145
 *
2146
 *      Opens a TCP server socket and creates a channel around it.
2147
 *
2148
 * Results:
2149
 *      The channel or NULL if failed. If an error occurred, an
2150
 *      error message is left in interp->result if interp is
2151
 *      not NULL.
2152
 *
2153
 * Side effects:
2154
 *      Opens a server socket and creates a new channel.
2155
 *
2156
 *----------------------------------------------------------------------
2157
 */
2158
 
2159
Tcl_Channel
2160
Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
2161
    Tcl_Interp *interp;                 /* For error reporting - may be
2162
                                         * NULL. */
2163
    int port;                           /* Port number to open. */
2164
    char *myHost;                       /* Name of local host. */
2165
    Tcl_TcpAcceptProc *acceptProc;      /* Callback for accepting connections
2166
                                         * from new clients. */
2167
    ClientData acceptProcData;          /* Data for the callback. */
2168
{
2169
    TcpState *statePtr;
2170
    char channelName[20];
2171
 
2172
    /*
2173
     * Create a new client socket and wrap it in a channel.
2174
     */
2175
 
2176
    statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
2177
    if (statePtr == NULL) {
2178
        return NULL;
2179
    }
2180
 
2181
    statePtr->acceptProc = acceptProc;
2182
    statePtr->acceptProcData = acceptProcData;
2183
 
2184
    /*
2185
     * Set up the callback mechanism for accepting connections
2186
     * from new clients.
2187
     */
2188
 
2189
    Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
2190
            (ClientData) statePtr);
2191
    sprintf(channelName, "sock%d", statePtr->fd);
2192
    statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2193
            (ClientData) statePtr, 0);
2194
    return statePtr->channel;
2195
}
2196
 
2197
/*
2198
 *----------------------------------------------------------------------
2199
 *
2200
 * TcpAccept --
2201
 *      Accept a TCP socket connection.  This is called by the event loop.
2202
 *
2203
 * Results:
2204
 *      None.
2205
 *
2206
 * Side effects:
2207
 *      Creates a new connection socket. Calls the registered callback
2208
 *      for the connection acceptance mechanism.
2209
 *
2210
 *----------------------------------------------------------------------
2211
 */
2212
 
2213
        /* ARGSUSED */
2214
static void
2215
TcpAccept(data, mask)
2216
    ClientData data;                    /* Callback token. */
2217
    int mask;                           /* Not used. */
2218
{
2219
    TcpState *sockState;                /* Client data of server socket. */
2220
    int newsock;                        /* The new client socket */
2221
    TcpState *newSockState;             /* State for new socket. */
2222
    struct sockaddr_in addr;            /* The remote address */
2223
    int len;                            /* For accept interface */
2224
    char channelName[20];
2225
 
2226
    sockState = (TcpState *) data;
2227
 
2228
    len = sizeof(struct sockaddr_in);
2229
    newsock = accept(sockState->fd, (struct sockaddr *)&addr, &len);
2230
    if (newsock < 0) {
2231
        return;
2232
    }
2233
 
2234
    /*
2235
     * Set close-on-exec flag to prevent the newly accepted socket from
2236
     * being inherited by child processes.
2237
     */
2238
 
2239
    (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
2240
 
2241
    newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
2242
 
2243
    newSockState->flags = 0;
2244
    newSockState->fd = newsock;
2245
    newSockState->acceptProc = (Tcl_TcpAcceptProc *) NULL;
2246
    newSockState->acceptProcData = (ClientData) NULL;
2247
 
2248
    sprintf(channelName, "sock%d", newsock);
2249
    newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
2250
            (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
2251
 
2252
    Tcl_SetChannelOption((Tcl_Interp *) NULL, newSockState->channel,
2253
            "-translation", "auto crlf");
2254
 
2255
    if (sockState->acceptProc != (Tcl_TcpAcceptProc *) NULL) {
2256
        (sockState->acceptProc) (sockState->acceptProcData,
2257
                newSockState->channel, inet_ntoa(addr.sin_addr),
2258
                ntohs(addr.sin_port));
2259
    }
2260
}
2261
 
2262
/*
2263
 *----------------------------------------------------------------------
2264
 *
2265
 * TclGetDefaultStdChannel --
2266
 *
2267
 *      Creates channels for standard input, standard output or standard
2268
 *      error output if they do not already exist.
2269
 *
2270
 * Results:
2271
 *      Returns the specified default standard channel, or NULL.
2272
 *
2273
 * Side effects:
2274
 *      May cause the creation of a standard channel and the underlying
2275
 *      file.
2276
 *
2277
 *----------------------------------------------------------------------
2278
 */
2279
 
2280
Tcl_Channel
2281
TclGetDefaultStdChannel(type)
2282
    int type;                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
2283
{
2284
    Tcl_Channel channel = NULL;
2285
    int fd = 0;                  /* Initializations needed to prevent */
2286
    int mode = 0;                /* compiler warning (used before set). */
2287
    char *bufMode = NULL;
2288
 
2289
    switch (type) {
2290
        case TCL_STDIN:
2291
            if ((lseek(0, (off_t) 0, SEEK_CUR) == -1) &&
2292
                    (errno == EBADF)) {
2293
                return (Tcl_Channel) NULL;
2294
            }
2295
            fd = 0;
2296
            mode = TCL_READABLE;
2297
            bufMode = "line";
2298
            break;
2299
        case TCL_STDOUT:
2300
            if ((lseek(1, (off_t) 0, SEEK_CUR) == -1) &&
2301
                    (errno == EBADF)) {
2302
                return (Tcl_Channel) NULL;
2303
            }
2304
            fd = 1;
2305
            mode = TCL_WRITABLE;
2306
            bufMode = "line";
2307
            break;
2308
        case TCL_STDERR:
2309
            if ((lseek(2, (off_t) 0, SEEK_CUR) == -1) &&
2310
                    (errno == EBADF)) {
2311
                return (Tcl_Channel) NULL;
2312
            }
2313
            fd = 2;
2314
            mode = TCL_WRITABLE;
2315
            bufMode = "none";
2316
            break;
2317
        default:
2318
            panic("TclGetDefaultStdChannel: Unexpected channel type");
2319
            break;
2320
    }
2321
 
2322
    channel = Tcl_MakeFileChannel((ClientData) fd, mode);
2323
 
2324
    /*
2325
     * Set up the normal channel options for stdio handles.
2326
     */
2327
 
2328
    Tcl_SetChannelOption(NULL, channel, "-translation", "auto");
2329
    Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
2330
    return channel;
2331
}
2332
 
2333
/*
2334
 *----------------------------------------------------------------------
2335
 *
2336
 * Tcl_GetOpenFile --
2337
 *
2338
 *      Given a name of a channel registered in the given interpreter,
2339
 *      returns a FILE * for it.
2340
 *
2341
 * Results:
2342
 *      A standard Tcl result. If the channel is registered in the given
2343
 *      interpreter and it is managed by the "file" channel driver, and
2344
 *      it is open for the requested mode, then the output parameter
2345
 *      filePtr is set to a FILE * for the underlying file. On error, the
2346
 *      filePtr is not set, TCL_ERROR is returned and an error message is
2347
 *      left in interp->result.
2348
 *
2349
 * Side effects:
2350
 *      May invoke fdopen to create the FILE * for the requested file.
2351
 *
2352
 *----------------------------------------------------------------------
2353
 */
2354
 
2355
int
2356
Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr)
2357
    Tcl_Interp *interp;         /* Interpreter in which to find file. */
2358
    char *string;               /* String that identifies file. */
2359
    int forWriting;             /* 1 means the file is going to be used
2360
                                 * for writing, 0 means for reading. */
2361
    int checkUsage;             /* 1 means verify that the file was opened
2362
                                 * in a mode that allows the access specified
2363
                                 * by "forWriting". Ignored, we always
2364
                                 * check that the channel is open for the
2365
                                 * requested mode. */
2366
    ClientData *filePtr;        /* Store pointer to FILE structure here. */
2367
{
2368
    Tcl_Channel chan;
2369
    int chanMode;
2370
    Tcl_ChannelType *chanTypePtr;
2371
    ClientData data;
2372
    int fd;
2373
    FILE *f;
2374
 
2375
    chan = Tcl_GetChannel(interp, string, &chanMode);
2376
    if (chan == (Tcl_Channel) NULL) {
2377
        return TCL_ERROR;
2378
    }
2379
    if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
2380
        Tcl_AppendResult(interp,
2381
                "\"", string, "\" wasn't opened for writing", (char *) NULL);
2382
        return TCL_ERROR;
2383
    } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) {
2384
        Tcl_AppendResult(interp,
2385
                "\"", string, "\" wasn't opened for reading", (char *) NULL);
2386
        return TCL_ERROR;
2387
    }
2388
 
2389
    /*
2390
     * We allow creating a FILE * out of file based, pipe based and socket
2391
     * based channels. We currently do not allow any other channel types,
2392
     * because it is likely that stdio will not know what to do with them.
2393
     */
2394
 
2395
    chanTypePtr = Tcl_GetChannelType(chan);
2396
    if ((chanTypePtr == &fileChannelType) || (chanTypePtr == &tcpChannelType)
2397
            || (strcmp(chanTypePtr->typeName, "pipe") == 0)) {
2398
        if (Tcl_GetChannelHandle(chan,
2399
                (forWriting ? TCL_WRITABLE : TCL_READABLE),
2400
                (ClientData*) &data) == TCL_OK) {
2401
            fd = (int) data;
2402
 
2403
            /*
2404
             * The call to fdopen below is probably dangerous, since it will
2405
             * truncate an existing file if the file is being opened
2406
             * for writing....
2407
             */
2408
 
2409
            f = fdopen(fd, (forWriting ? "w" : "r"));
2410
            if (f == NULL) {
2411
                Tcl_AppendResult(interp, "cannot get a FILE * for \"", string,
2412
                        "\"", (char *) NULL);
2413
                return TCL_ERROR;
2414
            }
2415
            *filePtr = (ClientData) f;
2416
            return TCL_OK;
2417
        }
2418
    }
2419
 
2420
    Tcl_AppendResult(interp, "\"", string,
2421
            "\" cannot be used to get a FILE *", (char *) NULL);
2422
    return TCL_ERROR;
2423
}
2424
 
2425
/*
2426
 *----------------------------------------------------------------------
2427
 *
2428
 * TclUnixWaitForFile --
2429
 *
2430
 *      This procedure waits synchronously for a file to become readable
2431
 *      or writable, with an optional timeout.
2432
 *
2433
 * Results:
2434
 *      The return value is an OR'ed combination of TCL_READABLE,
2435
 *      TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
2436
 *      that are present on file at the time of the return.  This
2437
 *      procedure will not return until either "timeout" milliseconds
2438
 *      have elapsed or at least one of the conditions given by mask
2439
 *      has occurred for file (a return value of 0 means that a timeout
2440
 *      occurred).  No normal events will be serviced during the
2441
 *      execution of this procedure.
2442
 *
2443
 * Side effects:
2444
 *      Time passes.
2445
 *
2446
 *----------------------------------------------------------------------
2447
 */
2448
 
2449
int
2450
TclUnixWaitForFile(fd, mask, timeout)
2451
    int fd;                     /* Handle for file on which to wait. */
2452
    int mask;                   /* What to wait for: OR'ed combination of
2453
                                 * TCL_READABLE, TCL_WRITABLE, and
2454
                                 * TCL_EXCEPTION. */
2455
    int timeout;                /* Maximum amount of time to wait for one
2456
                                 * of the conditions in mask to occur, in
2457
                                 * milliseconds.  A value of 0 means don't
2458
                                 * wait at all, and a value of -1 means
2459
                                 * wait forever. */
2460
{
2461
    Tcl_Time abortTime, now;
2462
    struct timeval blockTime, *timeoutPtr;
2463
    int index, bit, numFound, result = 0;
2464
    static fd_mask readyMasks[3*MASK_SIZE];
2465
                                /* This array reflects the readable/writable
2466
                                 * conditions that were found to exist by the
2467
                                 * last call to select. */
2468
 
2469
    /*
2470
     * If there is a non-zero finite timeout, compute the time when
2471
     * we give up.
2472
     */
2473
 
2474
    if (timeout > 0) {
2475
        TclpGetTime(&now);
2476
        abortTime.sec = now.sec + timeout/1000;
2477
        abortTime.usec = now.usec + (timeout%1000)*1000;
2478
        if (abortTime.usec >= 1000000) {
2479
            abortTime.usec -= 1000000;
2480
            abortTime.sec += 1;
2481
        }
2482
        timeoutPtr = &blockTime;
2483
    } else if (timeout == 0) {
2484
        timeoutPtr = &blockTime;
2485
        blockTime.tv_sec = 0;
2486
        blockTime.tv_usec = 0;
2487
    } else {
2488
        timeoutPtr = NULL;
2489
    }
2490
 
2491
    /*
2492
     * Initialize the ready masks and compute the mask offsets.
2493
     */
2494
 
2495
    if (fd >= FD_SETSIZE) {
2496
        panic("TclWaitForFile can't handle file id %d", fd);
2497
    }
2498
    memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
2499
    index = fd/(NBBY*sizeof(fd_mask));
2500
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
2501
 
2502
    /*
2503
     * Loop in a mini-event loop of our own, waiting for either the
2504
     * file to become ready or a timeout to occur.
2505
     */
2506
 
2507
    while (1) {
2508
        if (timeout > 0) {
2509
            blockTime.tv_sec = abortTime.sec - now.sec;
2510
            blockTime.tv_usec = abortTime.usec - now.usec;
2511
            if (blockTime.tv_usec < 0) {
2512
                blockTime.tv_sec -= 1;
2513
                blockTime.tv_usec += 1000000;
2514
            }
2515
            if (blockTime.tv_sec < 0) {
2516
                blockTime.tv_sec = 0;
2517
                blockTime.tv_usec = 0;
2518
            }
2519
        }
2520
 
2521
        /*
2522
         * Set the appropriate bit in the ready masks for the fd.
2523
         */
2524
 
2525
        if (mask & TCL_READABLE) {
2526
            readyMasks[index] |= bit;
2527
        }
2528
        if (mask & TCL_WRITABLE) {
2529
            (readyMasks+MASK_SIZE)[index] |= bit;
2530
        }
2531
        if (mask & TCL_EXCEPTION) {
2532
            (readyMasks+2*(MASK_SIZE))[index] |= bit;
2533
        }
2534
 
2535
        /*
2536
         * Wait for the event or a timeout.
2537
         */
2538
 
2539
        numFound = select(fd+1, (SELECT_MASK *) &readyMasks[0],
2540
                (SELECT_MASK *) &readyMasks[MASK_SIZE],
2541
                (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
2542
        if (numFound == 1) {
2543
            if (readyMasks[index] & bit) {
2544
                result |= TCL_READABLE;
2545
            }
2546
            if ((readyMasks+MASK_SIZE)[index] & bit) {
2547
                result |= TCL_WRITABLE;
2548
            }
2549
            if ((readyMasks+2*(MASK_SIZE))[index] & bit) {
2550
                result |= TCL_EXCEPTION;
2551
            }
2552
            result &= mask;
2553
            if (result) {
2554
                break;
2555
            }
2556
        }
2557
        if (timeout == 0) {
2558
            break;
2559
        }
2560
 
2561
        /*
2562
         * The select returned early, so we need to recompute the timeout.
2563
         */
2564
 
2565
        TclpGetTime(&now);
2566
        if ((abortTime.sec < now.sec)
2567
                || ((abortTime.sec == now.sec)
2568
                && (abortTime.usec <= now.usec))) {
2569
            break;
2570
        }
2571
    }
2572
    return result;
2573
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.