]> git.zerfleddert.de Git - micropolis/blob - src/tclx/src/tclxsel.c
Import Micropolis from http://www.donhopkins.com/home/micropolis/
[micropolis] / src / tclx / src / tclxsel.c
1 /*
2 * tclXselect.c
3 *
4 * Extended Tcl file I/O commands.
5 *-----------------------------------------------------------------------------
6 * Copyright 1992 Karl Lehenbauer and Mark Diekhans.
7 *
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
13 * implied warranty.
14 *-----------------------------------------------------------------------------
15 * $Id: tclXselect.c,v 2.0 1992/10/16 04:51:10 markd Rel $
16 *-----------------------------------------------------------------------------
17 */
18
19 #include "tclxint.h"
20
21 #ifdef MSDOS
22 typedef struct {
23 long fds_bits[1];
24 } fd_set; /* GRB for Micropolis */
25 #endif
26
27 extern
28 double floor ();
29
30 #ifdef TCL_USE_BZERO_MACRO
31 # define bzero(to,length) memset(to,'\0',length)
32 #endif
33
34 /*
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.
37 */
38
39 #ifdef __SLBF
40 # define READ_DATA_PENDING(fp) (fp->_r > 0)
41 #else
42 # define READ_DATA_PENDING(fp) (fp->_cnt != 0)
43 #endif
44
45 /*
46 * A few systems (A/UX 2.0) have select but no macros, define em in this case.
47 */
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)))
53 #endif
54
55 /*
56 * Prototypes of internal functions.
57 */
58 static int
59 ParseSelectFileList _ANSI_ARGS_((Tcl_Interp *interp,
60 char *handleList,
61 fd_set *fileDescSetPtr,
62 FILE ***fileDescListPtr,
63 int *maxFileIdPtr));
64
65 static int
66 FindPendingData _ANSI_ARGS_((int fileDescCnt,
67 FILE **fileDescList,
68 fd_set *fileDescSetPtr));
69
70 static char *
71 ReturnSelectedFileList _ANSI_ARGS_((fd_set *fileDescSetPtr,
72 fd_set *fileDescSet2Ptr,
73 int fileDescCnt,
74 FILE **fileDescList));
75
76 #ifndef TCL_NO_SELECT
77 \f
78 /*
79 *-----------------------------------------------------------------------------
80 *
81 * ParseSelectFileList --
82 *
83 * Parse a list of file handles for select.
84 *
85 * Parameters:
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
92 * returned.
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.
95 * Returns:
96 * The number of files in the list, or -1 if an error occured.
97 *-----------------------------------------------------------------------------
98 */
99 static int
100 ParseSelectFileList (interp, handleList, fileDescSetPtr, fileDescListPtr,
101 maxFileIdPtr)
102 Tcl_Interp *interp;
103 char *handleList;
104 fd_set *fileDescSetPtr;
105 FILE ***fileDescListPtr;
106 int *maxFileIdPtr;
107 {
108 int handleCnt, idx;
109 char **handleArgv;
110 FILE **fileDescList;
111
112 /*
113 * Optimize empty list handling.
114 */
115 if (handleList [0] == '\0') {
116 *fileDescListPtr = NULL;
117 return 0;
118 }
119
120 if (Tcl_SplitList (interp, handleList, &handleCnt, &handleArgv) != TCL_OK)
121 return -1;
122
123 /*
124 * Handle case of an empty list.
125 */
126 if (handleCnt == 0) {
127 *fileDescListPtr = NULL;
128 ckfree ((char *) handleArgv);
129 return 0;
130 }
131
132 fileDescList = (FILE **) ckalloc (sizeof (FILE *) * handleCnt);
133
134 for (idx = 0; idx < handleCnt; idx++) {
135 OpenFile *filePtr;
136 int fileId;
137
138 if (TclGetOpenFile (interp, handleArgv [idx], &filePtr) != TCL_OK) {
139 ckfree ((char *) handleArgv);
140 ckfree ((char *) fileDescList);
141 return -1;
142 }
143 fileId = fileno (filePtr->f);
144 fileDescList [idx] = filePtr->f;
145
146 FD_SET (fileId, fileDescSetPtr);
147 if (fileId > *maxFileIdPtr)
148 *maxFileIdPtr = fileId;
149 }
150
151 *fileDescListPtr = fileDescList;
152 ckfree ((char *) handleArgv);
153 return handleCnt;
154 }
155 \f
156 /*
157 *-----------------------------------------------------------------------------
158 *
159 * FindPendingData --
160 *
161 * Scan a list of read file descriptors to determine if any of them
162 * have data pending in their stdio buffers.
163 *
164 * Parameters:
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.
170 * Returns:
171 * TRUE if any where found that had pending data, FALSE if none were found.
172 *-----------------------------------------------------------------------------
173 */
174 static int
175 FindPendingData (fileDescCnt, fileDescList, fileDescSetPtr)
176 int fileDescCnt;
177 FILE **fileDescList;
178 fd_set *fileDescSetPtr;
179 {
180 int idx, found = FALSE;
181
182 FD_ZERO (fileDescSetPtr);
183
184 #ifndef IS_LINUX
185 for (idx = 0; idx < fileDescCnt; idx++) {
186 if (READ_DATA_PENDING (fileDescList [idx])) {
187 FD_SET (fileno (fileDescList [idx]), fileDescSetPtr);
188 found = TRUE;
189 }
190 }
191 #endif
192 return found;
193 }
194 \f
195 /*
196 *-----------------------------------------------------------------------------
197 *
198 * ReturnSelectedFileList --
199 *
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.
202 *
203 * Parameters:
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.
210 * Returns:
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 *-----------------------------------------------------------------------------
214 */
215 static char *
216 ReturnSelectedFileList (fileDescSetPtr, fileDescSet2Ptr, fileDescCnt,
217 fileDescList)
218 fd_set *fileDescSetPtr;
219 fd_set *fileDescSet2Ptr;
220 int fileDescCnt;
221 FILE **fileDescList;
222 {
223 int idx, handleCnt, fileNum;
224 char *fileHandleList;
225 char **fileHandleArgv, *nextByte;
226
227 /*
228 * Special case the empty list.
229 */
230 if (fileDescCnt == 0) {
231 fileHandleList = ckalloc (1);
232 fileHandleList [0] = '\0';
233 return fileHandleList;
234 }
235
236 /*
237 * Allocate enough room to hold the argv plus all the `fileNNN' strings
238 */
239 fileHandleArgv = (char **)
240 ckalloc ((fileDescCnt * sizeof (char *)) + (9 * fileDescCnt));
241 nextByte = ((char *) fileHandleArgv) + (fileDescCnt * sizeof (char *));
242
243 handleCnt = 0;
244 for (idx = 0; idx < fileDescCnt; idx++) {
245 fileNum = fileno (fileDescList [idx]);
246
247 if (FD_ISSET (fileNum, fileDescSetPtr) ||
248 (fileDescSet2Ptr != NULL &&
249 FD_ISSET (fileNum, fileDescSet2Ptr))) {
250
251 fileHandleArgv [handleCnt] = nextByte; /* Allocate storage */
252 nextByte += 8;
253 sprintf (fileHandleArgv [handleCnt], "file%d", fileNum);
254 handleCnt++;
255 }
256 }
257
258 fileHandleList = Tcl_Merge (handleCnt, fileHandleArgv);
259 ckfree ((char *) fileHandleArgv);
260
261 return fileHandleList;
262 }
263 \f
264 /*
265 *-----------------------------------------------------------------------------
266 *
267 * Tcl_SelectCmd --
268 * Implements the select TCL command:
269 * select readhandles [writehandles] [excepthandles] [timeout]
270 *
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.
273 *
274 * Results:
275 * A list in the form:
276 * {readhandles writehandles excepthandles}
277 * or {} it the timeout expired.
278 *-----------------------------------------------------------------------------
279 */
280 int
281 Tcl_SelectCmd (clientData, interp, argc, argv)
282 ClientData clientData;
283 Tcl_Interp *interp;
284 int argc;
285 char **argv;
286 {
287
288 fd_set readFdSet, writeFdSet, exceptFdSet;
289 int readDescCnt = 0, writeDescCnt = 0, exceptDescCnt = 0;
290 FILE **readDescList = NULL,**writeDescList = NULL,**exceptDescList = NULL;
291 fd_set readFdSet2;
292 char *retListArgv [3];
293
294 int numSelected, maxFileId = 0, pending;
295 int result = TCL_ERROR;
296 struct timeval timeoutRec;
297 struct timeval *timeoutRecPtr;
298
299
300 if (argc < 2) {
301 Tcl_AppendResult (interp, tclXWrongArgs, argv [0],
302 " readhandles [writehandles] [excepthandles]",
303 " [timeout]", (char *) NULL);
304 return TCL_ERROR;
305 }
306
307 /*
308 * Parse the file handles and set everything up for the select call.
309 */
310 FD_ZERO (&readFdSet);
311 FD_ZERO (&writeFdSet);
312 FD_ZERO (&exceptFdSet);
313 readDescCnt = ParseSelectFileList (interp, argv [1], &readFdSet,
314 &readDescList, &maxFileId);
315 if (readDescCnt < 0)
316 goto exitPoint;
317 if (argc > 2) {
318 writeDescCnt = ParseSelectFileList (interp, argv [2], &writeFdSet,
319 &writeDescList, &maxFileId);
320 if (writeDescCnt < 0)
321 goto exitPoint;
322 }
323 if (argc > 3) {
324 exceptDescCnt = ParseSelectFileList (interp, argv [3], &exceptFdSet,
325 &exceptDescList, &maxFileId);
326 if (exceptDescCnt < 0)
327 goto exitPoint;
328 }
329
330 /*
331 * Get the time out. Zero is different that not specified.
332 */
333 timeoutRecPtr = NULL;
334 if ((argc > 4) && (argv [4][0] != '\0')) {
335 double timeout, seconds, microseconds;
336
337 if (Tcl_GetDouble (interp, argv [4], &timeout) != TCL_OK)
338 goto exitPoint;
339 if (timeout < 0) {
340 Tcl_AppendResult (interp, "timeout must be greater than or equal",
341 " to zero", (char *) NULL);
342 goto exitPoint;
343 }
344 seconds = floor (timeout);
345 microseconds = (timeout - seconds) * 1000000.0;
346 timeoutRec.tv_sec = seconds;
347 timeoutRec.tv_usec = microseconds;
348 timeoutRecPtr = &timeoutRec;
349 }
350
351 /*
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.
354 */
355
356 pending = FindPendingData (readDescCnt, readDescList, &readFdSet2);
357 if (pending) {
358 timeoutRec.tv_sec = 0;
359 timeoutRec.tv_usec = 0;
360 timeoutRecPtr = &timeoutRec;
361 }
362
363 /*
364 * All set, do the select.
365 */
366 numSelected = select (maxFileId + 1, &readFdSet, &writeFdSet, &exceptFdSet,
367 timeoutRecPtr);
368 if (numSelected < 0) {
369 interp->result = Tcl_UnixError (interp);
370 goto exitPoint;
371 }
372
373 /*
374 * Return the result, either a 3 element list, or leave the result
375 * empty if the timeout occured.
376 */
377 if (numSelected > 0) {
378 retListArgv [0] = ReturnSelectedFileList (&readFdSet,
379 &readFdSet2,
380 readDescCnt,
381 readDescList);
382 retListArgv [1] = ReturnSelectedFileList (&writeFdSet,
383 NULL,
384 writeDescCnt,
385 writeDescList);
386 retListArgv [2] = ReturnSelectedFileList (&exceptFdSet,
387 NULL,
388 exceptDescCnt,
389 exceptDescList);
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]);
394 }
395
396 result = TCL_OK;
397
398 exitPoint:
399 if (readDescList != NULL)
400 ckfree ((char *) readDescList);
401 if (writeDescList != NULL)
402 ckfree ((char *) writeDescList);
403 if (exceptDescList != NULL)
404 ckfree ((char *) exceptDescList);
405 return result;
406
407 }
408 #else
409 /*
410 *-----------------------------------------------------------------------------
411 *
412 * Tcl_SelectCmd --
413 * Dummy select command that returns an error for systems that don't
414 * have select.
415 *-----------------------------------------------------------------------------
416 */
417 int
418 Tcl_SelectCmd (clientData, interp, argc, argv)
419 ClientData clientData;
420 Tcl_Interp *interp;
421 int argc;
422 char **argv;
423 {
424 Tcl_AppendResult (interp,
425 "select is not available on this version of Unix",
426 (char *) NULL);
427 return TCL_ERROR;
428 }
429 #endif
Impressum, Datenschutz