]>
git.zerfleddert.de Git - micropolis/blob - src/tclx/src/tclxsel.c
4 * Extended Tcl file I/O commands.
5 *-----------------------------------------------------------------------------
6 * Copyright 1992 Karl Lehenbauer and Mark Diekhans.
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted, provided
10 * that the above copyright notice appear in all copies. Karl Lehenbauer and
11 * Mark Diekhans make no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
14 *-----------------------------------------------------------------------------
15 * $Id: tclXselect.c,v 2.0 1992/10/16 04:51:10 markd Rel $
16 *-----------------------------------------------------------------------------
24 } fd_set
; /* GRB for Micropolis */
30 #ifdef TCL_USE_BZERO_MACRO
31 # define bzero(to,length) memset(to,'\0',length)
35 * Macro to probe the stdio buffer to see if any data is pending in the
36 * buffer. Different versions are provided for System V and BSD stdio.
40 # define READ_DATA_PENDING(fp) (fp->_r > 0)
42 # define READ_DATA_PENDING(fp) (fp->_cnt != 0)
46 * A few systems (A/UX 2.0) have select but no macros, define em in this case.
48 #if !defined(TCL_NO_SELECT) && !defined(FD_SET)
49 # define FD_SET(fd,fdset) (fdset)->fds_bits[0] |= (1<<(fd))
50 # define FD_CLR(fd,fdset) (fdset)->fds_bits[0] &= ~(1<<(fd))
51 # define FD_ZERO(fdset) (fdset)->fds_bits[0] = 0
52 # define FD_ISSET(fd,fdset) (((fdset)->fds_bits[0]) & (1<<(fd)))
56 * Prototypes of internal functions.
59 ParseSelectFileList
_ANSI_ARGS_((Tcl_Interp
*interp
,
61 fd_set
*fileDescSetPtr
,
62 FILE ***fileDescListPtr
,
66 FindPendingData
_ANSI_ARGS_((int fileDescCnt
,
68 fd_set
*fileDescSetPtr
));
71 ReturnSelectedFileList
_ANSI_ARGS_((fd_set
*fileDescSetPtr
,
72 fd_set
*fileDescSet2Ptr
,
74 FILE **fileDescList
));
79 *-----------------------------------------------------------------------------
81 * ParseSelectFileList --
83 * Parse a list of file handles for select.
86 * o interp (O) - Error messages are returned in the result.
87 * o handleList (I) - The list of file handles to parse, may be empty.
88 * o fileDescSetPtr (O) - The select fd_set for the parsed handles is
89 * filled in. Should be cleared before this procedure is called.
90 * o fileDescListPtr (O) - A pointer to a dynamically allocated list of
91 * the FILE ptrs that are in the set. If the list is empty, NULL is
93 * o maxFileIdPtr (I/O) - If a file id greater than the current value is
94 * encountered, it will be set to that file id.
96 * The number of files in the list, or -1 if an error occured.
97 *-----------------------------------------------------------------------------
100 ParseSelectFileList (interp
, handleList
, fileDescSetPtr
, fileDescListPtr
,
104 fd_set
*fileDescSetPtr
;
105 FILE ***fileDescListPtr
;
113 * Optimize empty list handling.
115 if (handleList
[0] == '\0') {
116 *fileDescListPtr
= NULL
;
120 if (Tcl_SplitList (interp
, handleList
, &handleCnt
, &handleArgv
) != TCL_OK
)
124 * Handle case of an empty list.
126 if (handleCnt
== 0) {
127 *fileDescListPtr
= NULL
;
128 ckfree ((char *) handleArgv
);
132 fileDescList
= (FILE **) ckalloc (sizeof (FILE *) * handleCnt
);
134 for (idx
= 0; idx
< handleCnt
; idx
++) {
138 if (TclGetOpenFile (interp
, handleArgv
[idx
], &filePtr
) != TCL_OK
) {
139 ckfree ((char *) handleArgv
);
140 ckfree ((char *) fileDescList
);
143 fileId
= fileno (filePtr
->f
);
144 fileDescList
[idx
] = filePtr
->f
;
146 FD_SET (fileId
, fileDescSetPtr
);
147 if (fileId
> *maxFileIdPtr
)
148 *maxFileIdPtr
= fileId
;
151 *fileDescListPtr
= fileDescList
;
152 ckfree ((char *) handleArgv
);
157 *-----------------------------------------------------------------------------
161 * Scan a list of read file descriptors to determine if any of them
162 * have data pending in their stdio buffers.
165 * o fileDescCnt (I) - Number of descriptors in the list.
166 * o fileDescListPtr (I) - A pointer to a list of the FILE pointers for
167 * files that are in the set.
168 * o fileDescSetPtr (I) - A select fd_set with will have a bit set for
169 * every file that has data pending it its buffer.
171 * TRUE if any where found that had pending data, FALSE if none were found.
172 *-----------------------------------------------------------------------------
175 FindPendingData (fileDescCnt
, fileDescList
, fileDescSetPtr
)
178 fd_set
*fileDescSetPtr
;
180 int idx
, found
= FALSE
;
182 FD_ZERO (fileDescSetPtr
);
185 for (idx
= 0; idx
< fileDescCnt
; idx
++) {
186 if (READ_DATA_PENDING (fileDescList
[idx
])) {
187 FD_SET (fileno (fileDescList
[idx
]), fileDescSetPtr
);
196 *-----------------------------------------------------------------------------
198 * ReturnSelectedFileList --
200 * Take the resulting file descriptor sets from a select, and the
201 * list of file descritpors and build up a list of Tcl file handles.
204 * o fileDescSetPtr (I) - The select fd_set.
205 * o fileDescSet2Ptr (I) - Pointer to a second descriptor to also check
206 * (their may be overlap). NULL if no second set.
207 * o fileDescCnt (I) - Number of descriptors in the list.
208 * o fileDescListPtr (I) - A pointer to a list of the FILE pointers for
209 * files that are in the set. If the list is empty, NULL is returned.
211 * A dynamicly allocated list of file handles. If the handles are empty,
212 * it still returns a NULL list to make clean up easy.
213 *-----------------------------------------------------------------------------
216 ReturnSelectedFileList (fileDescSetPtr
, fileDescSet2Ptr
, fileDescCnt
,
218 fd_set
*fileDescSetPtr
;
219 fd_set
*fileDescSet2Ptr
;
223 int idx
, handleCnt
, fileNum
;
224 char *fileHandleList
;
225 char **fileHandleArgv
, *nextByte
;
228 * Special case the empty list.
230 if (fileDescCnt
== 0) {
231 fileHandleList
= ckalloc (1);
232 fileHandleList
[0] = '\0';
233 return fileHandleList
;
237 * Allocate enough room to hold the argv plus all the `fileNNN' strings
239 fileHandleArgv
= (char **)
240 ckalloc ((fileDescCnt
* sizeof (char *)) + (9 * fileDescCnt
));
241 nextByte
= ((char *) fileHandleArgv
) + (fileDescCnt
* sizeof (char *));
244 for (idx
= 0; idx
< fileDescCnt
; idx
++) {
245 fileNum
= fileno (fileDescList
[idx
]);
247 if (FD_ISSET (fileNum
, fileDescSetPtr
) ||
248 (fileDescSet2Ptr
!= NULL
&&
249 FD_ISSET (fileNum
, fileDescSet2Ptr
))) {
251 fileHandleArgv
[handleCnt
] = nextByte
; /* Allocate storage */
253 sprintf (fileHandleArgv
[handleCnt
], "file%d", fileNum
);
258 fileHandleList
= Tcl_Merge (handleCnt
, fileHandleArgv
);
259 ckfree ((char *) fileHandleArgv
);
261 return fileHandleList
;
265 *-----------------------------------------------------------------------------
268 * Implements the select TCL command:
269 * select readhandles [writehandles] [excepthandles] [timeout]
271 * This command is extra smart in the fact that it checks for read data
272 * pending in the stdio buffer first before doing a select.
275 * A list in the form:
276 * {readhandles writehandles excepthandles}
277 * or {} it the timeout expired.
278 *-----------------------------------------------------------------------------
281 Tcl_SelectCmd (clientData
, interp
, argc
, argv
)
282 ClientData clientData
;
288 fd_set readFdSet
, writeFdSet
, exceptFdSet
;
289 int readDescCnt
= 0, writeDescCnt
= 0, exceptDescCnt
= 0;
290 FILE **readDescList
= NULL
,**writeDescList
= NULL
,**exceptDescList
= NULL
;
292 char *retListArgv
[3];
294 int numSelected
, maxFileId
= 0, pending
;
295 int result
= TCL_ERROR
;
296 struct timeval timeoutRec
;
297 struct timeval
*timeoutRecPtr
;
301 Tcl_AppendResult (interp
, tclXWrongArgs
, argv
[0],
302 " readhandles [writehandles] [excepthandles]",
303 " [timeout]", (char *) NULL
);
308 * Parse the file handles and set everything up for the select call.
310 FD_ZERO (&readFdSet
);
311 FD_ZERO (&writeFdSet
);
312 FD_ZERO (&exceptFdSet
);
313 readDescCnt
= ParseSelectFileList (interp
, argv
[1], &readFdSet
,
314 &readDescList
, &maxFileId
);
318 writeDescCnt
= ParseSelectFileList (interp
, argv
[2], &writeFdSet
,
319 &writeDescList
, &maxFileId
);
320 if (writeDescCnt
< 0)
324 exceptDescCnt
= ParseSelectFileList (interp
, argv
[3], &exceptFdSet
,
325 &exceptDescList
, &maxFileId
);
326 if (exceptDescCnt
< 0)
331 * Get the time out. Zero is different that not specified.
333 timeoutRecPtr
= NULL
;
334 if ((argc
> 4) && (argv
[4][0] != '\0')) {
335 double timeout
, seconds
, microseconds
;
337 if (Tcl_GetDouble (interp
, argv
[4], &timeout
) != TCL_OK
)
340 Tcl_AppendResult (interp
, "timeout must be greater than or equal",
341 " to zero", (char *) NULL
);
344 seconds
= floor (timeout
);
345 microseconds
= (timeout
- seconds
) * 1000000.0;
346 timeoutRec
.tv_sec
= seconds
;
347 timeoutRec
.tv_usec
= microseconds
;
348 timeoutRecPtr
= &timeoutRec
;
352 * Check if any data is pending in the read stdio buffers. If there is,
353 * then do the select, but don't block in it.
356 pending
= FindPendingData (readDescCnt
, readDescList
, &readFdSet2
);
358 timeoutRec
.tv_sec
= 0;
359 timeoutRec
.tv_usec
= 0;
360 timeoutRecPtr
= &timeoutRec
;
364 * All set, do the select.
366 numSelected
= select (maxFileId
+ 1, &readFdSet
, &writeFdSet
, &exceptFdSet
,
368 if (numSelected
< 0) {
369 interp
->result
= Tcl_UnixError (interp
);
374 * Return the result, either a 3 element list, or leave the result
375 * empty if the timeout occured.
377 if (numSelected
> 0) {
378 retListArgv
[0] = ReturnSelectedFileList (&readFdSet
,
382 retListArgv
[1] = ReturnSelectedFileList (&writeFdSet
,
386 retListArgv
[2] = ReturnSelectedFileList (&exceptFdSet
,
390 Tcl_SetResult (interp
, Tcl_Merge (3, retListArgv
), TCL_DYNAMIC
);
391 ckfree ((char *) retListArgv
[0]);
392 ckfree ((char *) retListArgv
[1]);
393 ckfree ((char *) retListArgv
[2]);
399 if (readDescList
!= NULL
)
400 ckfree ((char *) readDescList
);
401 if (writeDescList
!= NULL
)
402 ckfree ((char *) writeDescList
);
403 if (exceptDescList
!= NULL
)
404 ckfree ((char *) exceptDescList
);
410 *-----------------------------------------------------------------------------
413 * Dummy select command that returns an error for systems that don't
415 *-----------------------------------------------------------------------------
418 Tcl_SelectCmd (clientData
, interp
, argc
, argv
)
419 ClientData clientData
;
424 Tcl_AppendResult (interp
,
425 "select is not available on this version of Unix",