1 /*
2 * tclUnixChan.c
3 *
4 * Common channel driver for Unix channels based on files, command pipes
5 * and TCP sockets.
6 *
7 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8 * Copyright (c) 1998-1999 by Scriptics Corporation.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 */
13
14 #include "tclInt.h" /* Internal definitions for Tcl. */
15 #include "tclIO.h" /* To get Channel type declaration. */
16
17 #undef SUPPORTS_TTY
18 #if defined(HAVE_TERMIOS_H)
19 # define SUPPORTS_TTY 1
20 # include <termios.h>
21 # ifdef HAVE_SYS_IOCTL_H
22 # include <sys/ioctl.h>
23 # endif /* HAVE_SYS_IOCTL_H */
24 # ifdef HAVE_SYS_MODEM_H
25 # include <sys/modem.h>
26 # endif /* HAVE_SYS_MODEM_H */
27
28 # ifdef FIONREAD
29 # define GETREADQUEUE(fd, int) ioctl((fd), FIONREAD, &(int))
30 # elif defined(FIORDCHK)
31 # define GETREADQUEUE(fd, int) int = ioctl((fd), FIORDCHK, NULL)
32 # else
33 # define GETREADQUEUE(fd, int) int = 0
34 # endif
35
36 # ifdef TIOCOUTQ
37 # define GETWRITEQUEUE(fd, int) ioctl((fd), TIOCOUTQ, &(int))
38 # else
39 # define GETWRITEQUEUE(fd, int) int = 0
40 # endif
41
42 # if !defined(CRTSCTS) && defined(CNEW_RTSCTS)
43 # define CRTSCTS CNEW_RTSCTS
44 # endif /* !CRTSCTS&CNEW_RTSCTS */
45 # if !defined(PAREXT) && defined(CMSPAR)
46 # define PAREXT CMSPAR
47 # endif /* !PAREXT&&CMSPAR */
48
49 #endif /* HAVE_TERMIOS_H */
50
51 /*
52 * Helper macros to make parts of this file clearer. The macros do exactly
53 * what they say on the tin. :-) They also only ever refer to their arguments
54 * once, and so can be used without regard to side effects.
55 */
56
57 #define SET_BITS(var, bits) ((var) |= (bits))
58 #define CLEAR_BITS(var, bits) ((var) &= ~(bits))
59
60 /*
61 * This structure describes per-instance state of a file based channel.
62 */
63
64 typedef struct FileState {
65 Tcl_Channel channel; /* Channel associated with this file. */
66 int fd; /* File handle. */
67 int validMask; /* OR'ed combination of TCL_READABLE,
68 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
69 * which operations are valid on the file. */
70 } FileState;
71
72 #ifdef SUPPORTS_TTY
73
74 /*
75 * The following structure is used to set or get the serial port attributes in
76 * a platform-independent manner.
77 */
78
79 typedef struct TtyAttrs {
80 int baud;
81 int parity;
82 int data;
83 int stop;
84 } TtyAttrs;
85
86 #endif /* !SUPPORTS_TTY */
87
88 #define UNSUPPORTED_OPTION(detail) \
89 if (interp) { \
90 Tcl_SetObjResult(interp, Tcl_ObjPrintf( \
91 "%s not supported for this platform", (detail))); \
92 Tcl_SetErrorCode(interp, "TCL", "UNSUPPORTED", NULL); \
93 }
94
95 /*
96 * Static routines for this file:
97 */
98
99 static int FileBlockModeProc(ClientData instanceData, int mode);
100 static int FileCloseProc(ClientData instanceData,
101 Tcl_Interp *interp);
102 static int FileClose2Proc(ClientData instanceData,
103 Tcl_Interp *interp, int flags);
104 static int FileGetHandleProc(ClientData instanceData,
105 int direction, ClientData *handlePtr);
106 static int FileInputProc(ClientData instanceData, char *buf,
107 int toRead, int *errorCode);
108 static int FileOutputProc(ClientData instanceData,
109 const char *buf, int toWrite, int *errorCode);
110 static int FileSeekProc(ClientData instanceData, long offset,
111 int mode, int *errorCode);
112 static int FileTruncateProc(ClientData instanceData,
113 Tcl_WideInt length);
114 static Tcl_WideInt FileWideSeekProc(ClientData instanceData,
115 Tcl_WideInt offset, int mode, int *errorCode);
116 static void FileWatchProc(ClientData instanceData, int mask);
117 #ifdef SUPPORTS_TTY
118 static void TtyGetAttributes(int fd, TtyAttrs *ttyPtr);
119 static int TtyGetOptionProc(ClientData instanceData,
120 Tcl_Interp *interp, const char *optionName,
121 Tcl_DString *dsPtr);
122 static int TtyGetBaud(speed_t speed);
123 static speed_t TtyGetSpeed(int baud);
124 static void TtyInit(int fd);
125 static void TtyModemStatusStr(int status, Tcl_DString *dsPtr);
126 static int TtyParseMode(Tcl_Interp *interp, const char *mode,
127 TtyAttrs *ttyPtr);
128 static void TtySetAttributes(int fd, TtyAttrs *ttyPtr);
129 static int TtySetOptionProc(ClientData instanceData,
130 Tcl_Interp *interp, const char *optionName,
131 const char *value);
132 #endif /* SUPPORTS_TTY */
133
134 /*
135 * This structure describes the channel type structure for file based IO:
136 */
137
138 static const Tcl_ChannelType fileChannelType = {
139 "file", /* Type name. */
140 TCL_CHANNEL_VERSION_5, /* v5 channel */
141 FileCloseProc, /* Close proc. */
142 FileInputProc, /* Input proc. */
143 FileOutputProc, /* Output proc. */
144 FileSeekProc, /* Seek proc. */
145 NULL, /* Set option proc. */
146 NULL, /* Get option proc. */
147 FileWatchProc, /* Initialize notifier. */
148 FileGetHandleProc, /* Get OS handles out of channel. */
149 FileClose2Proc, /* close2proc. */
150 FileBlockModeProc, /* Set blocking or non-blocking mode.*/
151 NULL, /* flush proc. */
152 NULL, /* handler proc. */
153 FileWideSeekProc, /* wide seek proc. */
154 NULL,
155 FileTruncateProc /* truncate proc. */
156 };
157
158 #ifdef SUPPORTS_TTY
159 /*
160 * This structure describes the channel type structure for serial IO.
161 * Note that this type is a subclass of the "file" type.
162 */
163
164 static const Tcl_ChannelType ttyChannelType = {
165 "tty", /* Type name. */
166 TCL_CHANNEL_VERSION_5, /* v5 channel */
167 FileCloseProc, /* Close proc. */
168 FileInputProc, /* Input proc. */
169 FileOutputProc, /* Output proc. */
170 NULL, /* Seek proc. */
171 TtySetOptionProc, /* Set option proc. */
172 TtyGetOptionProc, /* Get option proc. */
173 FileWatchProc, /* Initialize notifier. */
174 FileGetHandleProc, /* Get OS handles out of channel. */
175 FileClose2Proc, /* close2proc. */
176 FileBlockModeProc, /* Set blocking or non-blocking mode.*/
177 NULL, /* flush proc. */
178 NULL, /* handler proc. */
179 NULL, /* wide seek proc. */
180 NULL, /* thread action proc. */
181 NULL /* truncate proc. */
182 };
183 #endif /* SUPPORTS_TTY */
184
185 /*
186 *----------------------------------------------------------------------
187 *
188 * FileBlockModeProc --
189 *
190 * Helper function to set blocking and nonblocking modes on a file based
191 * channel. Invoked by generic IO level code.
192 *
193 * Results:
194 * 0 if successful, errno when failed.
195 *
196 * Side effects:
197 * Sets the device into blocking or non-blocking mode.
198 *
199 *----------------------------------------------------------------------
200 */
201
202 static int
FileBlockModeProc(ClientData instanceData,int mode)203 FileBlockModeProc(
204 ClientData instanceData, /* File state. */
205 int mode) /* The mode to set. Can be TCL_MODE_BLOCKING
206 * or TCL_MODE_NONBLOCKING. */
207 {
208 FileState *fsPtr = instanceData;
209
210 if (TclUnixSetBlockingMode(fsPtr->fd, mode) < 0) {
211 return errno;
212 }
213
214 return 0;
215 }
216
217 /*
218 *----------------------------------------------------------------------
219 *
220 * FileInputProc --
221 *
222 * This function is invoked from the generic IO level to read input from
223 * a file based channel.
224 *
225 * Results:
226 * The number of bytes read is returned or -1 on error. An output
227 * argument contains a POSIX error code if an error occurs, or zero.
228 *
229 * Side effects:
230 * Reads input from the input device of the channel.
231 *
232 *----------------------------------------------------------------------
233 */
234
235 static int
FileInputProc(ClientData instanceData,char * buf,int toRead,int * errorCodePtr)236 FileInputProc(
237 ClientData instanceData, /* File state. */
238 char *buf, /* Where to store data read. */
239 int toRead, /* How much space is available in the
240 * buffer? */
241 int *errorCodePtr) /* Where to store error code. */
242 {
243 FileState *fsPtr = instanceData;
244 int bytesRead; /* How many bytes were actually read from the
245 * input device? */
246
247 *errorCodePtr = 0;
248
249 /*
250 * Assume there is always enough input available. This will block
251 * appropriately, and read will unblock as soon as a short read is
252 * possible, if the channel is in blocking mode. If the channel is
253 * nonblocking, the read will never block.
254 */
255
256 do {
257 bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
258 } while ((bytesRead < 0) && (errno == EINTR));
259
260 if (bytesRead < 0) {
261 *errorCodePtr = errno;
262 return -1;
263 }
264 return bytesRead;
265 }
266
267 /*
268 *----------------------------------------------------------------------
269 *
270 * FileOutputProc--
271 *
272 * This function is invoked from the generic IO level to write output to
273 * a file channel.
274 *
275 * Results:
276 * The number of bytes written is returned or -1 on error. An output
277 * argument contains a POSIX error code if an error occurred, or zero.
278 *
279 * Side effects:
280 * Writes output on the output device of the channel.
281 *
282 *----------------------------------------------------------------------
283 */
284
285 static int
FileOutputProc(ClientData instanceData,const char * buf,int toWrite,int * errorCodePtr)286 FileOutputProc(
287 ClientData instanceData, /* File state. */
288 const char *buf, /* The data buffer. */
289 int toWrite, /* How many bytes to write? */
290 int *errorCodePtr) /* Where to store error code. */
291 {
292 FileState *fsPtr = instanceData;
293 int written;
294
295 *errorCodePtr = 0;
296
297 if (toWrite == 0) {
298 /*
299 * SF Tcl Bug 465765. Do not try to write nothing into a file. STREAM
300 * based implementations will considers this as EOF (if there is a
301 * pipe behind the file).
302 */
303
304 return 0;
305 }
306 written = write(fsPtr->fd, buf, (size_t) toWrite);
307 if (written > -1) {
308 return written;
309 }
310 *errorCodePtr = errno;
311 return -1;
312 }
313
314 /*
315 *----------------------------------------------------------------------
316 *
317 * FileCloseProc --
318 *
319 * This function is called from the generic IO level to perform
320 * channel-type-specific cleanup when a file based channel is closed.
321 *
322 * Results:
323 * 0 if successful, errno if failed.
324 *
325 * Side effects:
326 * Closes the device of the channel.
327 *
328 *----------------------------------------------------------------------
329 */
330
331 static int
FileCloseProc(ClientData instanceData,Tcl_Interp * interp)332 FileCloseProc(
333 ClientData instanceData, /* File state. */
334 Tcl_Interp *interp) /* For error reporting - unused. */
335 {
336 FileState *fsPtr = instanceData;
337 int errorCode = 0;
338
339 Tcl_DeleteFileHandler(fsPtr->fd);
340
341 /*
342 * Do not close standard channels while in thread-exit.
343 */
344
345 if (!TclInThreadExit()
346 || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) {
347 if (close(fsPtr->fd) < 0) {
348 errorCode = errno;
349 }
350 }
351 ckfree(fsPtr);
352 return errorCode;
353 }
354 static int
FileClose2Proc(ClientData instanceData,Tcl_Interp * interp,int flags)355 FileClose2Proc(
356 ClientData instanceData, /* File state. */
357 Tcl_Interp *interp, /* For error reporting - unused. */
358 int flags)
359 {
360 if ((flags & (TCL_CLOSE_READ | TCL_CLOSE_WRITE)) == 0) {
361 return FileCloseProc(instanceData, interp);
362 }
363 return EINVAL;
364 }
365
366 /*
367 *----------------------------------------------------------------------
368 *
369 * FileSeekProc --
370 *
371 * This function is called by the generic IO level to move the access
372 * point in a file based channel.
373 *
374 * Results:
375 * -1 if failed, the new position if successful. An output argument
376 * contains the POSIX error code if an error occurred, or zero.
377 *
378 * Side effects:
379 * Moves the location at which the channel will be accessed in future
380 * operations.
381 *
382 *----------------------------------------------------------------------
383 */
384
385 static int
FileSeekProc(ClientData instanceData,long offset,int mode,int * errorCodePtr)386 FileSeekProc(
387 ClientData instanceData, /* File state. */
388 long offset, /* Offset to seek to. */
389 int mode, /* Relative to where should we seek? Can be
390 * one of SEEK_START, SEEK_SET or SEEK_END. */
391 int *errorCodePtr) /* To store error code. */
392 {
393 FileState *fsPtr = instanceData;
394 Tcl_WideInt oldLoc, newLoc;
395
396 /*
397 * Save our current place in case we need to roll-back the seek.
398 */
399
400 oldLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) 0, SEEK_CUR);
401 if (oldLoc == Tcl_LongAsWide(-1)) {
402 /*
403 * Bad things are happening. Error out...
404 */
405
406 *errorCodePtr = errno;
407 return -1;
408 }
409
410 newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
411
412 /*
413 * Check for expressability in our return type, and roll-back otherwise.
414 */
415
416 if (newLoc > Tcl_LongAsWide(INT_MAX)) {
417 *errorCodePtr = EOVERFLOW;
418 TclOSseek(fsPtr->fd, (Tcl_SeekOffset) oldLoc, SEEK_SET);
419 return -1;
420 } else {
421 *errorCodePtr = (newLoc == Tcl_LongAsWide(-1)) ? errno : 0;
422 }
423 return (int) Tcl_WideAsLong(newLoc);
424 }
425
426 /*
427 *----------------------------------------------------------------------
428 *
429 * FileWideSeekProc --
430 *
431 * This function is called by the generic IO level to move the access
432 * point in a file based channel, with offsets expressed as wide
433 * integers.
434 *
435 * Results:
436 * -1 if failed, the new position if successful. An output argument
437 * contains the POSIX error code if an error occurred, or zero.
438 *
439 * Side effects:
440 * Moves the location at which the channel will be accessed in future
441 * operations.
442 *
443 *----------------------------------------------------------------------
444 */
445
446 static Tcl_WideInt
FileWideSeekProc(ClientData instanceData,Tcl_WideInt offset,int mode,int * errorCodePtr)447 FileWideSeekProc(
448 ClientData instanceData, /* File state. */
449 Tcl_WideInt offset, /* Offset to seek to. */
450 int mode, /* Relative to where should we seek? Can be
451 * one of SEEK_START, SEEK_CUR or SEEK_END. */
452 int *errorCodePtr) /* To store error code. */
453 {
454 FileState *fsPtr = instanceData;
455 Tcl_WideInt newLoc;
456
457 newLoc = TclOSseek(fsPtr->fd, (Tcl_SeekOffset) offset, mode);
458
459 *errorCodePtr = (newLoc == -1) ? errno : 0;
460 return newLoc;
461 }
462
463 /*
464 *----------------------------------------------------------------------
465 *
466 * FileWatchProc --
467 *
468 * Initialize the notifier to watch the fd from this channel.
469 *
470 * Results:
471 * None.
472 *
473 * Side effects:
474 * Sets up the notifier so that a future event on the channel will
475 * be seen by Tcl.
476 *
477 *----------------------------------------------------------------------
478 */
479
480 static void
FileWatchProc(ClientData instanceData,int mask)481 FileWatchProc(
482 ClientData instanceData, /* The file state. */
483 int mask) /* Events of interest; an OR-ed combination of
484 * TCL_READABLE, TCL_WRITABLE and
485 * TCL_EXCEPTION. */
486 {
487 FileState *fsPtr = instanceData;
488
489 /*
490 * Make sure we only register for events that are valid on this file. Note
491 * that we are passing Tcl_NotifyChannel directly to Tcl_CreateFileHandler
492 * with the channel pointer as the client data.
493 */
494
495 mask &= fsPtr->validMask;
496 if (mask) {
497 Tcl_CreateFileHandler(fsPtr->fd, mask,
498 (Tcl_FileProc *) Tcl_NotifyChannel, fsPtr->channel);
499 } else {
500 Tcl_DeleteFileHandler(fsPtr->fd);
501 }
502 }
503
504 /*
505 *----------------------------------------------------------------------
506 *
507 * FileGetHandleProc --
508 *
509 * Called from Tcl_GetChannelHandle to retrieve OS handles from a file
510 * based channel.
511 *
512 * Results:
513 * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
514 * handle for the specified direction.
515 *
516 * Side effects:
517 * None.
518 *
519 *----------------------------------------------------------------------
520 */
521
522 static int
FileGetHandleProc(ClientData instanceData,int direction,ClientData * handlePtr)523 FileGetHandleProc(
524 ClientData instanceData, /* The file state. */
525 int direction, /* TCL_READABLE or TCL_WRITABLE */
526 ClientData *handlePtr) /* Where to store the handle. */
527 {
528 FileState *fsPtr = instanceData;
529
530 if (direction & fsPtr->validMask) {
531 *handlePtr = INT2PTR(fsPtr->fd);
532 return TCL_OK;
533 }
534 return TCL_ERROR;
535 }
536
537 #ifdef SUPPORTS_TTY
538 /*
539 *----------------------------------------------------------------------
540 *
541 * TtyModemStatusStr --
542 *
543 * Converts a RS232 modem status list of readable flags
544 *
545 *----------------------------------------------------------------------
546 */
547
548 static void
TtyModemStatusStr(int status,Tcl_DString * dsPtr)549 TtyModemStatusStr(
550 int status, /* RS232 modem status */
551 Tcl_DString *dsPtr) /* Where to store string */
552 {
553 #ifdef TIOCM_CTS
554 Tcl_DStringAppendElement(dsPtr, "CTS");
555 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CTS) ? "1" : "0");
556 #endif /* TIOCM_CTS */
557 #ifdef TIOCM_DSR
558 Tcl_DStringAppendElement(dsPtr, "DSR");
559 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_DSR) ? "1" : "0");
560 #endif /* TIOCM_DSR */
561 #ifdef TIOCM_RNG
562 Tcl_DStringAppendElement(dsPtr, "RING");
563 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_RNG) ? "1" : "0");
564 #endif /* TIOCM_RNG */
565 #ifdef TIOCM_CD
566 Tcl_DStringAppendElement(dsPtr, "DCD");
567 Tcl_DStringAppendElement(dsPtr, (status & TIOCM_CD) ? "1" : "0");
568 #endif /* TIOCM_CD */
569 }
570
571 /*
572 *----------------------------------------------------------------------
573 *
574 * TtySetOptionProc --
575 *
576 * Sets an option on a channel.
577 *
578 * Results:
579 * A standard Tcl result. Also sets the interp's result on error if
580 * interp is not NULL.
581 *
582 * Side effects:
583 * May modify an option on a device. Sets Error message if needed (by
584 * calling Tcl_BadChannelOption).
585 *
586 *----------------------------------------------------------------------
587 */
588
589 static int
TtySetOptionProc(ClientData instanceData,Tcl_Interp * interp,const char * optionName,const char * value)590 TtySetOptionProc(
591 ClientData instanceData, /* File state. */
592 Tcl_Interp *interp, /* For error reporting - can be NULL. */
593 const char *optionName, /* Which option to set? */
594 const char *value) /* New value for option. */
595 {
596 FileState *fsPtr = instanceData;
597 unsigned int len, vlen;
598 TtyAttrs tty;
599 int argc;
600 const char **argv;
601 struct termios iostate;
602
603 len = strlen(optionName);
604 vlen = strlen(value);
605
606 /*
607 * Option -mode baud,parity,databits,stopbits
608 */
609
610 if ((len > 2) && (strncmp(optionName, "-mode", len) == 0)) {
611 if (TtyParseMode(interp, value, &tty) != TCL_OK) {
612 return TCL_ERROR;
613 }
614
615 /*
616 * system calls results should be checked there. - dl
617 */
618
619 TtySetAttributes(fsPtr->fd, &tty);
620 return TCL_OK;
621 }
622
623
624 /*
625 * Option -handshake none|xonxoff|rtscts|dtrdsr
626 */
627
628 if ((len > 1) && (strncmp(optionName, "-handshake", len) == 0)) {
629 /*
630 * Reset all handshake options. DTR and RTS are ON by default.
631 */
632
633 tcgetattr(fsPtr->fd, &iostate);
634 CLEAR_BITS(iostate.c_iflag, IXON | IXOFF | IXANY);
635 #ifdef CRTSCTS
636 CLEAR_BITS(iostate.c_cflag, CRTSCTS);
637 #endif /* CRTSCTS */
638 if (Tcl_UtfNcasecmp(value, "NONE", vlen) == 0) {
639 /*
640 * Leave all handshake options disabled.
641 */
642 } else if (Tcl_UtfNcasecmp(value, "XONXOFF", vlen) == 0) {
643 SET_BITS(iostate.c_iflag, IXON | IXOFF | IXANY);
644 } else if (Tcl_UtfNcasecmp(value, "RTSCTS", vlen) == 0) {
645 #ifdef CRTSCTS
646 SET_BITS(iostate.c_cflag, CRTSCTS);
647 #else /* !CRTSTS */
648 UNSUPPORTED_OPTION("-handshake RTSCTS");
649 return TCL_ERROR;
650 #endif /* CRTSCTS */
651 } else if (Tcl_UtfNcasecmp(value, "DTRDSR", vlen) == 0) {
652 UNSUPPORTED_OPTION("-handshake DTRDSR");
653 return TCL_ERROR;
654 } else {
655 if (interp) {
656 Tcl_SetObjResult(interp, Tcl_NewStringObj(
657 "bad value for -handshake: must be one of"
658 " xonxoff, rtscts, dtrdsr or none", -1));
659 Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE",
660 "VALUE", NULL);
661 }
662 return TCL_ERROR;
663 }
664 tcsetattr(fsPtr->fd, TCSADRAIN, &iostate);
665 return TCL_OK;
666 }
667
668 /*
669 * Option -xchar {\x11 \x13}
670 */
671
672 if ((len > 1) && (strncmp(optionName, "-xchar", len) == 0)) {
673 Tcl_DString ds;
674
675 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
676 return TCL_ERROR;
677 } else if (argc != 2) {
678 if (interp) {
679 Tcl_SetObjResult(interp, Tcl_NewStringObj(
680 "bad value for -xchar: should be a list of"
681 " two elements", -1));
682 Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE",
683 "VALUE", NULL);
684 }
685 ckfree(argv);
686 return TCL_ERROR;
687 }
688
689 tcgetattr(fsPtr->fd, &iostate);
690
691 Tcl_UtfToExternalDString(NULL, argv[0], -1, &ds);
692 iostate.c_cc[VSTART] = *(const cc_t *) Tcl_DStringValue(&ds);
693 TclDStringClear(&ds);
694
695 Tcl_UtfToExternalDString(NULL, argv[1], -1, &ds);
696 iostate.c_cc[VSTOP] = *(const cc_t *) Tcl_DStringValue(&ds);
697 Tcl_DStringFree(&ds);
698 ckfree(argv);
699
700 tcsetattr(fsPtr->fd, TCSADRAIN, &iostate);
701 return TCL_OK;
702 }
703
704 /*
705 * Option -timeout msec
706 */
707
708 if ((len > 2) && (strncmp(optionName, "-timeout", len) == 0)) {
709 int msec;
710
711 tcgetattr(fsPtr->fd, &iostate);
712 if (Tcl_GetInt(interp, value, &msec) != TCL_OK) {
713 return TCL_ERROR;
714 }
715 iostate.c_cc[VMIN] = 0;
716 iostate.c_cc[VTIME] = (msec==0) ? 0 : (msec<100) ? 1 : (msec+50)/100;
717 tcsetattr(fsPtr->fd, TCSADRAIN, &iostate);
718 return TCL_OK;
719 }
720
721 /*
722 * Option -ttycontrol {DTR 1 RTS 0 BREAK 0}
723 */
724 if ((len > 4) && (strncmp(optionName, "-ttycontrol", len) == 0)) {
725 #if defined(TIOCMGET) && defined(TIOCMSET)
726 int i, control, flag;
727
728 if (Tcl_SplitList(interp, value, &argc, &argv) == TCL_ERROR) {
729 return TCL_ERROR;
730 }
731 if ((argc % 2) == 1) {
732 if (interp) {
733 Tcl_SetObjResult(interp, Tcl_NewStringObj(
734 "bad value for -ttycontrol: should be a list of"
735 " signal,value pairs", -1));
736 Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE",
737 "VALUE", NULL);
738 }
739 ckfree(argv);
740 return TCL_ERROR;
741 }
742
743 ioctl(fsPtr->fd, TIOCMGET, &control);
744 for (i = 0; i < argc-1; i += 2) {
745 if (Tcl_GetBoolean(interp, argv[i+1], &flag) == TCL_ERROR) {
746 ckfree(argv);
747 return TCL_ERROR;
748 }
749 if (Tcl_UtfNcasecmp(argv[i], "DTR", strlen(argv[i])) == 0) {
750 if (flag) {
751 SET_BITS(control, TIOCM_DTR);
752 } else {
753 CLEAR_BITS(control, TIOCM_DTR);
754 }
755 } else if (Tcl_UtfNcasecmp(argv[i], "RTS", strlen(argv[i])) == 0) {
756 if (flag) {
757 SET_BITS(control, TIOCM_RTS);
758 } else {
759 CLEAR_BITS(control, TIOCM_RTS);
760 }
761 } else if (Tcl_UtfNcasecmp(argv[i], "BREAK", strlen(argv[i])) == 0) {
762 #if defined(TIOCSBRK) && defined(TIOCCBRK)
763 if (flag) {
764 ioctl(fsPtr->fd, TIOCSBRK, NULL);
765 } else {
766 ioctl(fsPtr->fd, TIOCCBRK, NULL);
767 }
768 #else /* TIOCSBRK & TIOCCBRK */
769 UNSUPPORTED_OPTION("-ttycontrol BREAK");
770 ckfree(argv);
771 return TCL_ERROR;
772 #endif /* TIOCSBRK & TIOCCBRK */
773 } else {
774 if (interp) {
775 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
776 "bad signal \"%s\" for -ttycontrol: must be"
777 " DTR, RTS or BREAK", argv[i]));
778 Tcl_SetErrorCode(interp, "TCL", "OPERATION", "FCONFIGURE",
779 "VALUE", NULL);
780 }
781 ckfree(argv);
782 return TCL_ERROR;
783 }
784 } /* -ttycontrol options loop */
785
786 ioctl(fsPtr->fd, TIOCMSET, &control);
787 ckfree(argv);
788 return TCL_OK;
789 #else /* TIOCMGET&TIOCMSET */
790 UNSUPPORTED_OPTION("-ttycontrol");
791 #endif /* TIOCMGET&TIOCMSET */
792 }
793
794 return Tcl_BadChannelOption(interp, optionName,
795 "mode handshake timeout ttycontrol xchar");
796 }
797
798 /*
799 *----------------------------------------------------------------------
800 *
801 * TtyGetOptionProc --
802 *
803 * Gets a mode associated with an IO channel. If the optionName arg is
804 * non-NULL, retrieves the value of that option. If the optionName arg is
805 * NULL, retrieves a list of alternating option names and values for the
806 * given channel.
807 *
808 * Results:
809 * A standard Tcl result. Also sets the supplied DString to the string
810 * value of the option(s) returned. Sets error message if needed
811 * (by calling Tcl_BadChannelOption).
812 *
813 *----------------------------------------------------------------------
814 */
815
816 static int
TtyGetOptionProc(ClientData instanceData,Tcl_Interp * interp,const char * optionName,Tcl_DString * dsPtr)817 TtyGetOptionProc(
818 ClientData instanceData, /* File state. */
819 Tcl_Interp *interp, /* For error reporting - can be NULL. */
820 const char *optionName, /* Option to get. */
821 Tcl_DString *dsPtr) /* Where to store value(s). */
822 {
823 FileState *fsPtr = instanceData;
824 unsigned int len;
825 char buf[3*TCL_INTEGER_SPACE + 16];
826 int valid = 0; /* Flag if valid option parsed. */
827
828 if (optionName == NULL) {
829 len = 0;
830 } else {
831 len = strlen(optionName);
832 }
833 if (len == 0) {
834 Tcl_DStringAppendElement(dsPtr, "-mode");
835 }
836 if (len==0 || (len>2 && strncmp(optionName, "-mode", len)==0)) {
837 TtyAttrs tty;
838
839 valid = 1;
840 TtyGetAttributes(fsPtr->fd, &tty);
841 sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
842 Tcl_DStringAppendElement(dsPtr, buf);
843 }
844
845 /*
846 * Get option -xchar
847 */
848
849 if (len == 0) {
850 Tcl_DStringAppendElement(dsPtr, "-xchar");
851 Tcl_DStringStartSublist(dsPtr);
852 }
853 if (len==0 || (len>1 && strncmp(optionName, "-xchar", len)==0)) {
854 struct termios iostate;
855 Tcl_DString ds;
856
857 valid = 1;
858 tcgetattr(fsPtr->fd, &iostate);
859 Tcl_DStringInit(&ds);
860
861 Tcl_ExternalToUtfDString(NULL, (char *) &iostate.c_cc[VSTART], 1, &ds);
862 Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
863 TclDStringClear(&ds);
864
865 Tcl_ExternalToUtfDString(NULL, (char *) &iostate.c_cc[VSTOP], 1, &ds);
866 Tcl_DStringAppendElement(dsPtr, Tcl_DStringValue(&ds));
867 Tcl_DStringFree(&ds);
868 }
869 if (len == 0) {
870 Tcl_DStringEndSublist(dsPtr);
871 }
872
873 /*
874 * Get option -queue
875 * Option is readonly and returned by [fconfigure chan -queue] but not
876 * returned by unnamed [fconfigure chan].
877 */
878
879 if ((len > 1) && (strncmp(optionName, "-queue", len) == 0)) {
880 int inQueue=0, outQueue=0, inBuffered, outBuffered;
881
882 valid = 1;
883 GETREADQUEUE(fsPtr->fd, inQueue);
884 GETWRITEQUEUE(fsPtr->fd, outQueue);
885 inBuffered = Tcl_InputBuffered(fsPtr->channel);
886 outBuffered = Tcl_OutputBuffered(fsPtr->channel);
887
888 sprintf(buf, "%d", inBuffered+inQueue);
889 Tcl_DStringAppendElement(dsPtr, buf);
890 sprintf(buf, "%d", outBuffered+outQueue);
891 Tcl_DStringAppendElement(dsPtr, buf);
892 }
893
894 #if defined(TIOCMGET)
895 /*
896 * Get option -ttystatus
897 * Option is readonly and returned by [fconfigure chan -ttystatus] but not
898 * returned by unnamed [fconfigure chan].
899 */
900 if ((len > 4) && (strncmp(optionName, "-ttystatus", len) == 0)) {
901 int status;
902
903 valid = 1;
904 ioctl(fsPtr->fd, TIOCMGET, &status);
905 TtyModemStatusStr(status, dsPtr);
906 }
907 #endif /* TIOCMGET */
908
909 if (valid) {
910 return TCL_OK;
911 }
912 return Tcl_BadChannelOption(interp, optionName, "mode"
913 " queue ttystatus xchar"
914 );
915 }
916
917
918 static const struct {int baud; speed_t speed;} speeds[] = {
919 #ifdef B0
920 {0, B0},
921 #endif
922 #ifdef B50
923 {50, B50},
924 #endif
925 #ifdef B75
926 {75, B75},
927 #endif
928 #ifdef B110
929 {110, B110},
930 #endif
931 #ifdef B134
932 {134, B134},
933 #endif
934 #ifdef B150
935 {150, B150},
936 #endif
937 #ifdef B200
938 {200, B200},
939 #endif
940 #ifdef B300
941 {300, B300},
942 #endif
943 #ifdef B600
944 {600, B600},
945 #endif
946 #ifdef B1200
947 {1200, B1200},
948 #endif
949 #ifdef B1800
950 {1800, B1800},
951 #endif
952 #ifdef B2400
953 {2400, B2400},
954 #endif
955 #ifdef B4800
956 {4800, B4800},
957 #endif
958 #ifdef B9600
959 {9600, B9600},
960 #endif
961 #ifdef B14400
962 {14400, B14400},
963 #endif
964 #ifdef B19200
965 {19200, B19200},
966 #endif
967 #ifdef EXTA
968 {19200, EXTA},
969 #endif
970 #ifdef B28800
971 {28800, B28800},
972 #endif
973 #ifdef B38400
974 {38400, B38400},
975 #endif
976 #ifdef EXTB
977 {38400, EXTB},
978 #endif
979 #ifdef B57600
980 {57600, B57600},
981 #endif
982 #ifdef _B57600
983 {57600, _B57600},
984 #endif
985 #ifdef B76800
986 {76800, B76800},
987 #endif
988 #ifdef B115200
989 {115200, B115200},
990 #endif
991 #ifdef _B115200
992 {115200, _B115200},
993 #endif
994 #ifdef B153600
995 {153600, B153600},
996 #endif
997 #ifdef B230400
998 {230400, B230400},
999 #endif
1000 #ifdef B307200
1001 {307200, B307200},
1002 #endif
1003 #ifdef B460800
1004 {460800, B460800},
1005 #endif
1006 #ifdef B500000
1007 {500000, B500000},
1008 #endif
1009 #ifdef B576000
1010 {576000, B576000},
1011 #endif
1012 #ifdef B921600
1013 {921600, B921600},
1014 #endif
1015 #ifdef B1000000
1016 {1000000, B1000000},
1017 #endif
1018 #ifdef B1152000
1019 {1152000, B1152000},
1020 #endif
1021 #ifdef B1500000
1022 {1500000,B1500000},
1023 #endif
1024 #ifdef B2000000
1025 {2000000, B2000000},
1026 #endif
1027 #ifdef B2500000
1028 {2500000,B2500000},
1029 #endif
1030 #ifdef B3000000
1031 {3000000,B3000000},
1032 #endif
1033 #ifdef B3500000
1034 {3500000,B3500000},
1035 #endif
1036 #ifdef B4000000
1037 {4000000,B4000000},
1038 #endif
1039 {-1, 0}
1040 };
1041
1042 /*
1043 *---------------------------------------------------------------------------
1044 *
1045 * TtyGetSpeed --
1046 *
1047 * Given an integer baud rate, get the speed_t value that should be
1048 * used to select that baud rate.
1049 *
1050 * Results:
1051 * As above.
1052 *
1053 *---------------------------------------------------------------------------
1054 */
1055
1056 static speed_t
TtyGetSpeed(int baud)1057 TtyGetSpeed(
1058 int baud) /* The baud rate to look up. */
1059 {
1060 int bestIdx, bestDiff, i, diff;
1061
1062 bestIdx = 0;
1063 bestDiff = 1000000;
1064
1065 /*
1066 * If the baud rate does not correspond to one of the known mask values,
1067 * choose the mask value whose baud rate is closest to the specified baud
1068 * rate.
1069 */
1070
1071 for (i = 0; speeds[i].baud >= 0; i++) {
1072 diff = speeds[i].baud - baud;
1073 if (diff < 0) {
1074 diff = -diff;
1075 }
1076 if (diff < bestDiff) {
1077 bestIdx = i;
1078 bestDiff = diff;
1079 }
1080 }
1081 return speeds[bestIdx].speed;
1082 }
1083
1084 /*
1085 *---------------------------------------------------------------------------
1086 *
1087 * TtyGetBaud --
1088 *
1089 * Return the integer baud rate corresponding to a given speed_t value.
1090 *
1091 * Results:
1092 * As above. If the mask value was not recognized, 0 is returned.
1093 *
1094 *---------------------------------------------------------------------------
1095 */
1096
1097 static int
TtyGetBaud(speed_t speed)1098 TtyGetBaud(
1099 speed_t speed) /* Speed mask value to look up. */
1100 {
1101 int i;
1102
1103 for (i = 0; speeds[i].baud >= 0; i++) {
1104 if (speeds[i].speed == speed) {
1105 return speeds[i].baud;
1106 }
1107 }
1108 return 0;
1109 }
1110
1111 /*
1112 *---------------------------------------------------------------------------
1113 *
1114 * TtyGetAttributes --
1115 *
1116 * Get the current attributes of the specified serial device.
1117 *
1118 * Results:
1119 * None.
1120 *
1121 * Side effects:
1122 * None.
1123 *
1124 *---------------------------------------------------------------------------
1125 */
1126
1127 static void
TtyGetAttributes(int fd,TtyAttrs * ttyPtr)1128 TtyGetAttributes(
1129 int fd, /* Open file descriptor for serial port to be
1130 * queried. */
1131 TtyAttrs *ttyPtr) /* Buffer filled with serial port
1132 * attributes. */
1133 {
1134 struct termios iostate;
1135 int baud, parity, data, stop;
1136
1137 tcgetattr(fd, &iostate);
1138
1139 baud = TtyGetBaud(cfgetospeed(&iostate));
1140
1141 parity = 'n';
1142 #ifdef PAREXT
1143 switch ((int) (iostate.c_cflag & (PARENB | PARODD | PAREXT))) {
1144 case PARENB : parity = 'e'; break;
1145 case PARENB | PARODD : parity = 'o'; break;
1146 case PARENB | PAREXT : parity = 's'; break;
1147 case PARENB | PARODD | PAREXT : parity = 'm'; break;
1148 }
1149 #else /* !PAREXT */
1150 switch ((int) (iostate.c_cflag & (PARENB | PARODD))) {
1151 case PARENB : parity = 'e'; break;
1152 case PARENB | PARODD : parity = 'o'; break;
1153 }
1154 #endif /* PAREXT */
1155
1156 data = iostate.c_cflag & CSIZE;
1157 data = (data == CS5) ? 5 : (data == CS6) ? 6 : (data == CS7) ? 7 : 8;
1158
1159 stop = (iostate.c_cflag & CSTOPB) ? 2 : 1;
1160
1161 ttyPtr->baud = baud;
1162 ttyPtr->parity = parity;
1163 ttyPtr->data = data;
1164 ttyPtr->stop = stop;
1165 }
1166
1167 /*
1168 *---------------------------------------------------------------------------
1169 *
1170 * TtySetAttributes --
1171 *
1172 * Set the current attributes of the specified serial device.
1173 *
1174 * Results:
1175 * None.
1176 *
1177 * Side effects:
1178 * None.
1179 *
1180 *---------------------------------------------------------------------------
1181 */
1182
1183 static void
TtySetAttributes(int fd,TtyAttrs * ttyPtr)1184 TtySetAttributes(
1185 int fd, /* Open file descriptor for serial port to be
1186 * modified. */
1187 TtyAttrs *ttyPtr) /* Buffer containing new attributes for serial
1188 * port. */
1189 {
1190 struct termios iostate;
1191 int parity, data, flag;
1192
1193 tcgetattr(fd, &iostate);
1194 cfsetospeed(&iostate, TtyGetSpeed(ttyPtr->baud));
1195 cfsetispeed(&iostate, TtyGetSpeed(ttyPtr->baud));
1196
1197 flag = 0;
1198 parity = ttyPtr->parity;
1199 if (parity != 'n') {
1200 SET_BITS(flag, PARENB);
1201 #ifdef PAREXT
1202 CLEAR_BITS(iostate.c_cflag, PAREXT);
1203 if ((parity == 'm') || (parity == 's')) {
1204 SET_BITS(flag, PAREXT);
1205 }
1206 #endif /* PAREXT */
1207 if ((parity == 'm') || (parity == 'o')) {
1208 SET_BITS(flag, PARODD);
1209 }
1210 }
1211 data = ttyPtr->data;
1212 SET_BITS(flag,
1213 (data == 5) ? CS5 :
1214 (data == 6) ? CS6 :
1215 (data == 7) ? CS7 : CS8);
1216 if (ttyPtr->stop == 2) {
1217 SET_BITS(flag, CSTOPB);
1218 }
1219
1220 CLEAR_BITS(iostate.c_cflag, PARENB | PARODD | CSIZE | CSTOPB);
1221 SET_BITS(iostate.c_cflag, flag);
1222
1223 tcsetattr(fd, TCSADRAIN, &iostate);
1224 }
1225
1226 /*
1227 *---------------------------------------------------------------------------
1228 *
1229 * TtyParseMode --
1230 *
1231 * Parse the "-mode" argument to the fconfigure command. The argument is
1232 * of the form baud,parity,data,stop.
1233 *
1234 * Results:
1235 * The return value is TCL_OK if the argument was successfully parsed,
1236 * TCL_ERROR otherwise. If TCL_ERROR is returned, an error message is
1237 * left in the interp's result (if interp is non-NULL).
1238 *
1239 *---------------------------------------------------------------------------
1240 */
1241
1242 static int
TtyParseMode(Tcl_Interp * interp,const char * mode,TtyAttrs * ttyPtr)1243 TtyParseMode(
1244 Tcl_Interp *interp, /* If non-NULL, interp for error return. */
1245 const char *mode, /* Mode string to be parsed. */
1246 TtyAttrs *ttyPtr) /* Filled with data from mode string */
1247 {
1248 int i, end;
1249 char parity;
1250 const char *bad = "bad value for -mode";
1251
1252 i = sscanf(mode, "%d,%c,%d,%d%n",
1253 &ttyPtr->baud,
1254 &parity,
1255 &ttyPtr->data,
1256 &ttyPtr->stop, &end);
1257 if ((i != 4) || (mode[end] != '\0')) {
1258 if (interp != NULL) {
1259 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1260 "%s: should be baud,parity,data,stop", bad));
1261 Tcl_SetErrorCode(interp, "TCL", "VALUE", "SERIALMODE", NULL);
1262 }
1263 return TCL_ERROR;
1264 }
1265
1266 /*
1267 * Only allow setting mark/space parity on platforms that support it Make
1268 * sure to allow for the case where strchr is a macro. [Bug: 5089]
1269 *
1270 * We cannot if/else/endif the strchr arguments, it has to be the whole
1271 * function. On AIX this function is apparently a macro, and macros do
1272 * not allow pre-processor directives in their arguments.
1273 */
1274
1275 if (
1276 #if defined(PAREXT)
1277 strchr("noems", parity)
1278 #else
1279 strchr("noe", parity)
1280 #endif /* PAREXT */
1281 == NULL) {
1282 if (interp != NULL) {
1283 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1284 "%s parity: should be %s", bad,
1285 #if defined(PAREXT)
1286 "n, o, e, m, or s"
1287 #else
1288 "n, o, or e"
1289 #endif /* PAREXT */
1290 ));
1291 Tcl_SetErrorCode(interp, "TCL", "VALUE", "SERIALMODE", NULL);
1292 }
1293 return TCL_ERROR;
1294 }
1295 ttyPtr->parity = parity;
1296 if ((ttyPtr->data < 5) || (ttyPtr->data > 8)) {
1297 if (interp != NULL) {
1298 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1299 "%s data: should be 5, 6, 7, or 8", bad));
1300 Tcl_SetErrorCode(interp, "TCL", "VALUE", "SERIALMODE", NULL);
1301 }
1302 return TCL_ERROR;
1303 }
1304 if ((ttyPtr->stop < 0) || (ttyPtr->stop > 2)) {
1305 if (interp != NULL) {
1306 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1307 "%s stop: should be 1 or 2", bad));
1308 Tcl_SetErrorCode(interp, "TCL", "VALUE", "SERIALMODE", NULL);
1309 }
1310 return TCL_ERROR;
1311 }
1312 return TCL_OK;
1313 }
1314
1315 /*
1316 *---------------------------------------------------------------------------
1317 *
1318 * TtyInit --
1319 *
1320 * Given file descriptor that refers to a serial port, initialize the
1321 * serial port to a set of sane values so that Tcl can talk to a device
1322 * located on the serial port.
1323 *
1324 * Side effects:
1325 * Serial device initialized to non-blocking raw mode, similar to sockets
1326 * All other modes can be simulated on top of this in Tcl.
1327 *
1328 *---------------------------------------------------------------------------
1329 */
1330
1331 static void
TtyInit(int fd)1332 TtyInit(
1333 int fd) /* Open file descriptor for serial port to be initialized. */
1334 {
1335 struct termios iostate;
1336 tcgetattr(fd, &iostate);
1337
1338 if (iostate.c_iflag != IGNBRK
1339 || iostate.c_oflag != 0
1340 || iostate.c_lflag != 0
1341 || iostate.c_cflag & CREAD
1342 || iostate.c_cc[VMIN] != 1
1343 || iostate.c_cc[VTIME] != 0)
1344 {
1345 iostate.c_iflag = IGNBRK;
1346 iostate.c_oflag = 0;
1347 iostate.c_lflag = 0;
1348 iostate.c_cflag |= CREAD;
1349 iostate.c_cc[VMIN] = 1;
1350 iostate.c_cc[VTIME] = 0;
1351
1352 tcsetattr(fd, TCSADRAIN, &iostate);
1353 }
1354 }
1355 #endif /* SUPPORTS_TTY */
1356
1357 /*
1358 *----------------------------------------------------------------------
1359 *
1360 * TclpOpenFileChannel --
1361 *
1362 * Open an file based channel on Unix systems.
1363 *
1364 * Results:
1365 * The new channel or NULL. If NULL, the output argument errorCodePtr is
1366 * set to a POSIX error and an error message is left in the interp's
1367 * result if interp is not NULL.
1368 *
1369 * Side effects:
1370 * May open the channel and may cause creation of a file on the file
1371 * system.
1372 *
1373 *----------------------------------------------------------------------
1374 */
1375
1376 Tcl_Channel
TclpOpenFileChannel(Tcl_Interp * interp,Tcl_Obj * pathPtr,int mode,int permissions)1377 TclpOpenFileChannel(
1378 Tcl_Interp *interp, /* Interpreter for error reporting; can be
1379 * NULL. */
1380 Tcl_Obj *pathPtr, /* Name of file to open. */
1381 int mode, /* POSIX open mode. */
1382 int permissions) /* If the open involves creating a file, with
1383 * what modes to create it? */
1384 {
1385 int fd, channelPermissions;
1386 FileState *fsPtr;
1387 const char *native, *translation;
1388 char channelName[16 + TCL_INTEGER_SPACE];
1389 const Tcl_ChannelType *channelTypePtr;
1390
1391 switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1392 case O_RDONLY:
1393 channelPermissions = TCL_READABLE;
1394 break;
1395 case O_WRONLY:
1396 channelPermissions = TCL_WRITABLE;
1397 break;
1398 case O_RDWR:
1399 channelPermissions = (TCL_READABLE | TCL_WRITABLE);
1400 break;
1401 default:
1402 /*
1403 * This may occurr if modeString was "", for example.
1404 */
1405
1406 Tcl_Panic("TclpOpenFileChannel: invalid mode value");
1407 return NULL;
1408 }
1409
1410 native = Tcl_FSGetNativePath(pathPtr);
1411 if (native == NULL) {
1412 if (interp != (Tcl_Interp *) NULL) {
1413 Tcl_AppendResult(interp, "couldn't open \"",
1414 TclGetString(pathPtr), "\": filename is invalid on this platform",
1415 NULL);
1416 }
1417 return NULL;
1418 }
1419
1420 #ifdef DJGPP
1421 SET_BITS(mode, O_BINARY);
1422 #endif
1423
1424 fd = TclOSopen(native, mode, permissions);
1425
1426 if (fd < 0) {
1427 if (interp != NULL) {
1428 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1429 "couldn't open \"%s\": %s",
1430 TclGetString(pathPtr), Tcl_PosixError(interp)));
1431 }
1432 return NULL;
1433 }
1434
1435 /*
1436 * Set close-on-exec flag on the fd so that child processes will not
1437 * inherit this fd.
1438 */
1439
1440 fcntl(fd, F_SETFD, FD_CLOEXEC);
1441
1442 sprintf(channelName, "file%d", fd);
1443
1444 #ifdef SUPPORTS_TTY
1445 if (strcmp(native, "/dev/tty") != 0 && isatty(fd)) {
1446 /*
1447 * Initialize the serial port to a set of sane parameters. Especially
1448 * important if the remote device is set to echo and the serial port
1449 * driver was also set to echo -- as soon as a char were sent to the
1450 * serial port, the remote device would echo it, then the serial
1451 * driver would echo it back to the device, etc.
1452 *
1453 * Note that we do not do this if we're dealing with /dev/tty itself,
1454 * as that tends to cause Bad Things To Happen when you're working
1455 * interactively. Strictly a better check would be to see if the FD
1456 * being set up is a device and has the same major/minor as the
1457 * initial std FDs (beware reopening!) but that's nearly as messy.
1458 */
1459
1460 translation = "auto crlf";
1461 channelTypePtr = &ttyChannelType;
1462 TtyInit(fd);
1463 } else
1464 #endif /* SUPPORTS_TTY */
1465 {
1466 translation = NULL;
1467 channelTypePtr = &fileChannelType;
1468 }
1469
1470 fsPtr = ckalloc(sizeof(FileState));
1471 fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
1472 fsPtr->fd = fd;
1473
1474 fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1475 fsPtr, channelPermissions);
1476
1477 if (translation != NULL) {
1478 /*
1479 * Gotcha. Most modems need a "\r" at the end of the command sequence.
1480 * If you just send "at\n", the modem will not respond with "OK"
1481 * because it never got a "\r" to actually invoke the command. So, by
1482 * default, newlines are translated to "\r\n" on output to avoid "bug"
1483 * reports that the serial port isn't working.
1484 */
1485
1486 if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
1487 translation) != TCL_OK) {
1488 Tcl_Close(NULL, fsPtr->channel);
1489 return NULL;
1490 }
1491 }
1492
1493 return fsPtr->channel;
1494 }
1495
1496 /*
1497 *----------------------------------------------------------------------
1498 *
1499 * Tcl_MakeFileChannel --
1500 *
1501 * Makes a Tcl_Channel from an existing OS level file handle.
1502 *
1503 * Results:
1504 * The Tcl_Channel created around the preexisting OS level file handle.
1505 *
1506 * Side effects:
1507 * None.
1508 *
1509 *----------------------------------------------------------------------
1510 */
1511
1512 Tcl_Channel
Tcl_MakeFileChannel(ClientData handle,int mode)1513 Tcl_MakeFileChannel(
1514 ClientData handle, /* OS level handle. */
1515 int mode) /* ORed combination of TCL_READABLE and
1516 * TCL_WRITABLE to indicate file mode. */
1517 {
1518 FileState *fsPtr;
1519 char channelName[16 + TCL_INTEGER_SPACE];
1520 int fd = PTR2INT(handle);
1521 const Tcl_ChannelType *channelTypePtr;
1522 struct sockaddr sockaddr;
1523 socklen_t sockaddrLen = sizeof(sockaddr);
1524
1525 if (mode == 0) {
1526 return NULL;
1527 }
1528
1529 sockaddr.sa_family = AF_UNSPEC;
1530
1531 #ifdef SUPPORTS_TTY
1532 if (isatty(fd)) {
1533 channelTypePtr = &ttyChannelType;
1534 sprintf(channelName, "serial%d", fd);
1535 } else
1536 #endif /* SUPPORTS_TTY */
1537 if ((getsockname(fd, (struct sockaddr *)&sockaddr, &sockaddrLen) == 0)
1538 && (sockaddrLen > 0)
1539 && (sockaddr.sa_family == AF_INET || sockaddr.sa_family == AF_INET6)) {
1540 return TclpMakeTcpClientChannelMode(INT2PTR(fd), mode);
1541 } else {
1542 channelTypePtr = &fileChannelType;
1543 sprintf(channelName, "file%d", fd);
1544 }
1545
1546 fsPtr = ckalloc(sizeof(FileState));
1547 fsPtr->fd = fd;
1548 fsPtr->validMask = mode | TCL_EXCEPTION;
1549 fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
1550 fsPtr, mode);
1551
1552 return fsPtr->channel;
1553 }
1554
1555 /*
1556 *----------------------------------------------------------------------
1557 *
1558 * TclpGetDefaultStdChannel --
1559 *
1560 * Creates channels for standard input, standard output or standard error
1561 * output if they do not already exist.
1562 *
1563 * Results:
1564 * Returns the specified default standard channel, or NULL.
1565 *
1566 * Side effects:
1567 * May cause the creation of a standard channel and the underlying file.
1568 *
1569 *----------------------------------------------------------------------
1570 */
1571
1572 Tcl_Channel
TclpGetDefaultStdChannel(int type)1573 TclpGetDefaultStdChannel(
1574 int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
1575 {
1576 Tcl_Channel channel = NULL;
1577 int fd = 0; /* Initializations needed to prevent */
1578 int mode = 0; /* compiler warning (used before set). */
1579 const char *bufMode = NULL;
1580
1581 /*
1582 * Some #def's to make the code a little clearer!
1583 */
1584
1585 #define ZERO_OFFSET ((Tcl_SeekOffset) 0)
1586 #define ERROR_OFFSET ((Tcl_SeekOffset) -1)
1587
1588 switch (type) {
1589 case TCL_STDIN:
1590 if ((TclOSseek(0, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
1591 && (errno == EBADF)) {
1592 return NULL;
1593 }
1594 fd = 0;
1595 mode = TCL_READABLE;
1596 bufMode = "line";
1597 break;
1598 case TCL_STDOUT:
1599 if ((TclOSseek(1, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
1600 && (errno == EBADF)) {
1601 return NULL;
1602 }
1603 fd = 1;
1604 mode = TCL_WRITABLE;
1605 bufMode = "line";
1606 break;
1607 case TCL_STDERR:
1608 if ((TclOSseek(2, ZERO_OFFSET, SEEK_CUR) == ERROR_OFFSET)
1609 && (errno == EBADF)) {
1610 return NULL;
1611 }
1612 fd = 2;
1613 mode = TCL_WRITABLE;
1614 bufMode = "none";
1615 break;
1616 default:
1617 Tcl_Panic("TclGetDefaultStdChannel: Unexpected channel type");
1618 break;
1619 }
1620
1621 #undef ZERO_OFFSET
1622 #undef ERROR_OFFSET
1623
1624 channel = Tcl_MakeFileChannel(INT2PTR(fd), mode);
1625 if (channel == NULL) {
1626 return NULL;
1627 }
1628
1629 /*
1630 * Set up the normal channel options for stdio handles.
1631 */
1632
1633 if (Tcl_GetChannelType(channel) == &fileChannelType) {
1634 Tcl_SetChannelOption(NULL, channel, "-translation", "auto");
1635 } else {
1636 Tcl_SetChannelOption(NULL, channel, "-translation", "auto crlf");
1637 }
1638 Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
1639 return channel;
1640 }
1641
1642 /*
1643 *----------------------------------------------------------------------
1644 *
1645 * Tcl_GetOpenFile --
1646 *
1647 * Given a name of a channel registered in the given interpreter, returns
1648 * a FILE * for it.
1649 *
1650 * Results:
1651 * A standard Tcl result. If the channel is registered in the given
1652 * interpreter and it is managed by the "file" channel driver, and it is
1653 * open for the requested mode, then the output parameter filePtr is set
1654 * to a FILE * for the underlying file. On error, the filePtr is not set,
1655 * TCL_ERROR is returned and an error message is left in the interp's
1656 * result.
1657 *
1658 * Side effects:
1659 * May invoke fdopen to create the FILE * for the requested file.
1660 *
1661 *----------------------------------------------------------------------
1662 */
1663
1664 int
Tcl_GetOpenFile(Tcl_Interp * interp,const char * chanID,int forWriting,int checkUsage,ClientData * filePtr)1665 Tcl_GetOpenFile(
1666 Tcl_Interp *interp, /* Interpreter in which to find file. */
1667 const char *chanID, /* String that identifies file. */
1668 int forWriting, /* 1 means the file is going to be used for
1669 * writing, 0 means for reading. */
1670 int checkUsage, /* 1 means verify that the file was opened in
1671 * a mode that allows the access specified by
1672 * "forWriting". Ignored, we always check that
1673 * the channel is open for the requested
1674 * mode. */
1675 ClientData *filePtr) /* Store pointer to FILE structure here. */
1676 {
1677 Tcl_Channel chan;
1678 int chanMode, fd;
1679 const Tcl_ChannelType *chanTypePtr;
1680 ClientData data;
1681 FILE *f;
1682
1683 chan = Tcl_GetChannel(interp, chanID, &chanMode);
1684 if (chan == NULL) {
1685 return TCL_ERROR;
1686 }
1687 if (forWriting && !(chanMode & TCL_WRITABLE)) {
1688 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1689 "\"%s\" wasn't opened for writing", chanID));
1690 Tcl_SetErrorCode(interp, "TCL", "VALUE", "CHANNEL", "NOT_WRITABLE",
1691 NULL);
1692 return TCL_ERROR;
1693 } else if (!forWriting && !(chanMode & TCL_READABLE)) {
1694 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1695 "\"%s\" wasn't opened for reading", chanID));
1696 Tcl_SetErrorCode(interp, "TCL", "VALUE", "CHANNEL", "NOT_READABLE",
1697 NULL);
1698 return TCL_ERROR;
1699 }
1700
1701 /*
1702 * We allow creating a FILE * out of file based, pipe based and socket
1703 * based channels. We currently do not allow any other channel types,
1704 * because it is likely that stdio will not know what to do with them.
1705 */
1706
1707 chanTypePtr = Tcl_GetChannelType(chan);
1708 if ((chanTypePtr == &fileChannelType)
1709 #ifdef SUPPORTS_TTY
1710 || (chanTypePtr == &ttyChannelType)
1711 #endif /* SUPPORTS_TTY */
1712 || (strcmp(chanTypePtr->typeName, "tcp") == 0)
1713 || (strcmp(chanTypePtr->typeName, "pipe") == 0)) {
1714 if (Tcl_GetChannelHandle(chan,
1715 (forWriting ? TCL_WRITABLE : TCL_READABLE), &data) == TCL_OK) {
1716 fd = PTR2INT(data);
1717
1718 /*
1719 * The call to fdopen below is probably dangerous, since it will
1720 * truncate an existing file if the file is being opened for
1721 * writing....
1722 */
1723
1724 f = fdopen(fd, (forWriting ? "w" : "r"));
1725 if (f == NULL) {
1726 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1727 "cannot get a FILE * for \"%s\"", chanID));
1728 Tcl_SetErrorCode(interp, "TCL", "VALUE", "CHANNEL",
1729 "FILE_FAILURE", NULL);
1730 return TCL_ERROR;
1731 }
1732 *filePtr = f;
1733 return TCL_OK;
1734 }
1735 }
1736
1737 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1738 "\"%s\" cannot be used to get a FILE *", chanID));
1739 Tcl_SetErrorCode(interp, "TCL", "VALUE", "CHANNEL", "NO_DESCRIPTOR",
1740 NULL);
1741 return TCL_ERROR;
1742 }
1743
1744 #ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier is
1745 * in tclMacOSXNotify.c */
1746 /*
1747 *----------------------------------------------------------------------
1748 *
1749 * TclUnixWaitForFile --
1750 *
1751 * This function waits synchronously for a file to become readable or
1752 * writable, with an optional timeout.
1753 *
1754 * Results:
1755 * The return value is an OR'ed combination of TCL_READABLE,
1756 * TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions that are
1757 * present on file at the time of the return. This function will not
1758 * return until either "timeout" milliseconds have elapsed or at least
1759 * one of the conditions given by mask has occurred for file (a return
1760 * value of 0 means that a timeout occurred). No normal events will be
1761 * serviced during the execution of this function.
1762 *
1763 * Side effects:
1764 * Time passes.
1765 *
1766 *----------------------------------------------------------------------
1767 */
1768
1769 int
TclUnixWaitForFile(int fd,int mask,int timeout)1770 TclUnixWaitForFile(
1771 int fd, /* Handle for file on which to wait. */
1772 int mask, /* What to wait for: OR'ed combination of
1773 * TCL_READABLE, TCL_WRITABLE, and
1774 * TCL_EXCEPTION. */
1775 int timeout) /* Maximum amount of time to wait for one of
1776 * the conditions in mask to occur, in
1777 * milliseconds. A value of 0 means don't wait
1778 * at all, and a value of -1 means wait
1779 * forever. */
1780 {
1781 Tcl_Time abortTime = {0, 0}, now; /* silence gcc 4 warning */
1782 struct timeval blockTime, *timeoutPtr;
1783 int numFound, result = 0;
1784 fd_set readableMask;
1785 fd_set writableMask;
1786 fd_set exceptionMask;
1787
1788 #ifndef _DARWIN_C_SOURCE
1789 /*
1790 * Sanity check fd.
1791 */
1792
1793 if (fd >= FD_SETSIZE) {
1794 Tcl_Panic("TclUnixWaitForFile can't handle file id %d", fd);
1795 /* must never get here, or select masks overrun will occur below */
1796 }
1797 #endif
1798
1799 /*
1800 * If there is a non-zero finite timeout, compute the time when we give
1801 * up.
1802 */
1803
1804 if (timeout > 0) {
1805 Tcl_GetTime(&now);
1806 abortTime.sec = now.sec + timeout/1000;
1807 abortTime.usec = now.usec + (timeout%1000)*1000;
1808 if (abortTime.usec >= 1000000) {
1809 abortTime.usec -= 1000000;
1810 abortTime.sec += 1;
1811 }
1812 timeoutPtr = &blockTime;
1813 } else if (timeout == 0) {
1814 timeoutPtr = &blockTime;
1815 blockTime.tv_sec = 0;
1816 blockTime.tv_usec = 0;
1817 } else {
1818 timeoutPtr = NULL;
1819 }
1820
1821 /*
1822 * Initialize the select masks.
1823 */
1824
1825 FD_ZERO(&readableMask);
1826 FD_ZERO(&writableMask);
1827 FD_ZERO(&exceptionMask);
1828
1829 /*
1830 * Loop in a mini-event loop of our own, waiting for either the file to
1831 * become ready or a timeout to occur.
1832 */
1833
1834 while (1) {
1835 if (timeout > 0) {
1836 blockTime.tv_sec = abortTime.sec - now.sec;
1837 blockTime.tv_usec = abortTime.usec - now.usec;
1838 if (blockTime.tv_usec < 0) {
1839 blockTime.tv_sec -= 1;
1840 blockTime.tv_usec += 1000000;
1841 }
1842 if (blockTime.tv_sec < 0) {
1843 blockTime.tv_sec = 0;
1844 blockTime.tv_usec = 0;
1845 }
1846 }
1847
1848 /*
1849 * Setup the select masks for the fd.
1850 */
1851
1852 if (mask & TCL_READABLE) {
1853 FD_SET(fd, &readableMask);
1854 }
1855 if (mask & TCL_WRITABLE) {
1856 FD_SET(fd, &writableMask);
1857 }
1858 if (mask & TCL_EXCEPTION) {
1859 FD_SET(fd, &exceptionMask);
1860 }
1861
1862 /*
1863 * Wait for the event or a timeout.
1864 */
1865
1866 numFound = select(fd + 1, &readableMask, &writableMask,
1867 &exceptionMask, timeoutPtr);
1868 if (numFound == 1) {
1869 if (FD_ISSET(fd, &readableMask)) {
1870 SET_BITS(result, TCL_READABLE);
1871 }
1872 if (FD_ISSET(fd, &writableMask)) {
1873 SET_BITS(result, TCL_WRITABLE);
1874 }
1875 if (FD_ISSET(fd, &exceptionMask)) {
1876 SET_BITS(result, TCL_EXCEPTION);
1877 }
1878 result &= mask;
1879 if (result) {
1880 break;
1881 }
1882 }
1883 if (timeout == 0) {
1884 break;
1885 }
1886 if (timeout < 0) {
1887 continue;
1888 }
1889
1890 /*
1891 * The select returned early, so we need to recompute the timeout.
1892 */
1893
1894 Tcl_GetTime(&now);
1895 if ((abortTime.sec < now.sec)
1896 || (abortTime.sec==now.sec && abortTime.usec<=now.usec)) {
1897 break;
1898 }
1899 }
1900 return result;
1901 }
1902 #endif /* HAVE_COREFOUNDATION */
1903
1904 /*
1905 *----------------------------------------------------------------------
1906 *
1907 * FileTruncateProc --
1908 *
1909 * Truncates a file to a given length.
1910 *
1911 * Results:
1912 * 0 if the operation succeeded, and -1 if it failed (in which case
1913 * *errorCodePtr will be set to errno).
1914 *
1915 * Side effects:
1916 * The underlying file is potentially truncated. This can have a wide
1917 * variety of side effects, including moving file pointers that point at
1918 * places later in the file than the truncate point.
1919 *
1920 *----------------------------------------------------------------------
1921 */
1922
1923 static int
FileTruncateProc(ClientData instanceData,Tcl_WideInt length)1924 FileTruncateProc(
1925 ClientData instanceData,
1926 Tcl_WideInt length)
1927 {
1928 FileState *fsPtr = instanceData;
1929 int result;
1930
1931 #ifdef HAVE_TYPE_OFF64_T
1932 /*
1933 * We assume this goes with the type for now...
1934 */
1935
1936 result = ftruncate64(fsPtr->fd, (off64_t) length);
1937 #else
1938 result = ftruncate(fsPtr->fd, (off_t) length);
1939 #endif
1940 if (result) {
1941 return errno;
1942 }
1943 return 0;
1944 }
1945
1946 /*
1947 * Local Variables:
1948 * mode: c
1949 * c-basic-offset: 4
1950 * fill-column: 78
1951 * End:
1952 */
1953