1 /* Copyright (C) 1992-1998 The Geometry Center
2 * Copyright (C) 1998-2000 Stuart Levy, Tamara Munzner, Mark Phillips
3 * Copyright (C) 2007 Claus-Justus Heine
4 *
5 * This file is part of Geomview.
6 *
7 * Geomview is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * Geomview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Geomview; see the file COPYING. If not, write
19 * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
20 * USA, or visit http://www.gnu.org.
21 */
22
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
28
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <math.h>
34 #include <stdlib.h> /* for decl of 'free'; mbp Tue May 16 19:21:59 2000 */
35 #include "handleP.h" /* Includes stdio.h, etc. too */
36 #include "freelist.h"
37
38 #if defined(unix) || defined(__unix)
39 # include <unistd.h>
40 # include <sys/socket.h>
41 # include <sys/un.h>
42 #elif defined(_WIN32)
43 # include <winsock.h>
44 #endif
45
46 #ifndef S_IFMT
47 # define S_IFMT _S_IFMT
48 # define S_IFIFO _S_IFIFO
49 #endif
50
51 #ifdef _WIN32
52 #undef FD_ISSET
FD_ISSET(SOCKET fd,fd_set * fds)53 int FD_ISSET(SOCKET fd, fd_set *fds) {
54 int i = fds->fd_count;
55 while(--i >= 0) {
56 if (fds->fd_array[i] == fd) return 1;
57 }
58 return 0;
59 }
60
gettimeofday(struct timeval * tv,struct timezone * tz)61 void gettimeofday(struct timeval *tv, struct timezone *tz)
62 {
63 fprintf(stderr, "Call to stub gettimeofday()\n");
64 tv->tv_sec = tv->tv_usec = 0;
65 }
66 #endif /*_WIN32*/
67
68 #include <errno.h>
69
70 static const int o_nonblock =
71 #ifdef O_NONBLOCK
72 O_NONBLOCK |
73 #endif
74 #ifdef O_NDELAY
75 O_NDELAY |
76 #endif
77 #ifdef FNONBLK
78 FNONBLK |
79 #endif
80 #ifdef FNDELAY
81 FNDELAY |
82 #endif
83 0;
84
85 #if !defined(O_NONBLOCK) && !defined(O_NDELAY) \
86 && !defined(FNONBLK) && !defined(FNDELAY)
87 # error Do not know how to achieve non-blocking IO
88 #endif
89
90 static DBLLIST(AllPools);
91 static DEF_FREELIST(Pool);
92
93 static fd_set poolwatchfds;
94 static int poolmaxfd = 0;
95 static fd_set poolreadyfds;
96 static int poolnready = 0;
97
98 #define FOREVER ((((unsigned)1)<<31)-1)
99 static struct timeval nexttowake = { FOREVER, };
100
101 static Pool *newPool(char *name);
102
103 /*
104 * Does the whole job of handling stream references to named objects.
105 * Interprets strings of the forms:
106 * name:file
107 * referring to a specific named object to be read from a
108 * specific file, or
109 * file
110 * referring to the nameless content of that file, or
111 * name:
112 * referring to a specific named object from any source.
113 *
114 * In the first two cases, we open (as a Pool) the specified file and
115 * attempt to read it using the functions in *ops.
116 *
117 * The caller of this function owns the returned handle and should
118 * call HandleDelete() when it doesn't need the return value.
119 */
120 Handle *
HandleReferringTo(int prefixch,char * str,HandleOps * ops,Handle ** hp)121 HandleReferringTo(int prefixch, char *str, HandleOps *ops, Handle **hp)
122 {
123 Pool *p = NULL;
124 Handle *h = NULL, *ph = NULL;
125 Handle *hknown = NULL;
126 char *sep;
127 char *fname;
128 char *name;
129 char nb[128];
130
131 if (str == NULL || ops == NULL)
132 return 0;
133
134 sep = strrchr(str, ':');
135 if (prefixch == ':') { /* : name -- take 'name' from anywhere */
136 name = str;
137 fname = NULL;
138 } else if (sep == NULL) { /* < file -- read from file 'name' */
139 fname = str;
140 name = NULL;
141 } else { /* < file:name */
142 name = sep+1;
143 fname = nb;
144 if ((size_t)(sep-str) >= sizeof(nb)) {
145 sep = &str[sizeof(nb)-1];
146 }
147 memcpy(fname, str, sep-str);
148 fname[sep-str] = '\0';
149 /* The ':' introduces an ambiguity: at least on MS win it can
150 * also mean a drive letter, and on other systems it could
151 * also be a legal part of the name. We only hack around the
152 * drive-letter stuff: if we have a one-letter file-name, then
153 * we assume it is a drive letter.
154 */
155 if (fname[1] == '\0' && findfile(NULL, fname) == NULL) {
156 fname = str;
157 }
158 }
159
160 if (fname != NULL && *fname != '\0') {
161 p = PoolStreamOpen(fname, NULL, 0, ops);
162 hknown = HandleCreate(fname, ops);
163 }
164
165 if (p && ((p->flags & (PF_ANY|PF_REREAD)) != PF_ANY || hknown != NULL)) {
166 ph = PoolIn(p); /* Try reading one item. */
167 }
168
169 if (name) {
170 /* Accessing a handle via ':' makes the handle global */
171 h = HandleCreateGlobal(name, ops);
172 }
173
174 if (ph) {
175 if (h) {
176 /* If we were told to assign to a specific named handle
177 * then do so and get rid of the handle returned by
178 * PoolIn().
179 */
180 HandleSetObject(h, HandleObject(ph));
181 HandleDelete(ph);
182 } else {
183 h = ph; /* otherwise we return the handle returned by PoolIn(). */
184 }
185 HandleDelete(hknown);
186 } else if (p) {
187 /* If we have a pool but not handle, then generate one for
188 * this pool; callers of this functinos treat handle == NULL
189 * as error case.
190 */
191 REFGET(Handle, hknown);
192 HandleSetObject(hknown, NULL);
193 h = hknown;
194 if (h->whence) {
195 if (h->whence != p) {
196 /* steal the pool pointer */
197 DblListDelete(&h->poolnode);
198 h->whence = p;
199 DblListAdd(&p->handles, &h->poolnode);
200 }
201 REFPUT(h); /* no need to call HandleDelete() */
202 } else {
203 h->whence = p;
204 DblListAdd(&p->handles, &h->poolnode);
205 }
206 }
207
208 if (hp) {
209 if (*hp) {
210 if (*hp != h) {
211 HandlePDelete(hp);
212 } else {
213 HandleDelete(*hp);
214 }
215 }
216 *hp = h;
217 }
218
219 return h;
220 }
221
222 char *
PoolName(Pool * p)223 PoolName(Pool *p)
224 {
225 return p ? p->poolname : NULL;
226 }
227
228 Pool *
PoolByName(char * fname,HandleOps * ops)229 PoolByName(char *fname, HandleOps *ops)
230 {
231 Pool *p;
232
233 DblListIterateNoDelete(&AllPools, Pool, node, p) {
234 if ((ops == NULL || p->ops == ops) && strcmp(fname, p->poolname) == 0) {
235 return p;
236 }
237 }
238
239 return NULL;
240 }
241
pool_dump(void)242 void pool_dump(void)
243 {
244 Pool *p;
245 Handle *h;
246
247 OOGLWarn("Active Pools:");
248 DblListIterateNoDelete(&AllPools, Pool, node, p) {
249 OOGLWarn(" %s[%s]%p",
250 p->ops ? p->ops->prefix : "none",
251 p->poolname, (void *)p);
252 OOGLWarn(" Attached Handles:");
253 DblListIterateNoDelete(&p->handles, Handle, poolnode, h) {
254 OOGLWarn(" %s", h->name);
255 }
256 }
257 }
258
259 static
watchfd(int fd)260 void watchfd(int fd)
261 {
262 if (fd < 0 || fd >= FD_SETSIZE || FD_ISSET(fd, &poolwatchfds))
263 return;
264
265 FD_SET(fd, &poolwatchfds);
266 if (poolmaxfd <= fd)
267 poolmaxfd = fd+1;
268 }
269
270 static
unwatchfd(int fd)271 void unwatchfd(int fd)
272 {
273 int i;
274
275 if (fd < 0 || fd >= FD_SETSIZE)
276 return;
277
278 if (FD_ISSET(fd, &poolwatchfds)) {
279 FD_CLR(fd, &poolwatchfds);
280 }
281 if (fd+1 >= poolmaxfd) {
282 for(i = poolmaxfd; --i >= 0 && !FD_ISSET(i, &poolwatchfds); )
283 ;
284 poolmaxfd = i+1;
285 }
286 if (FD_ISSET(fd, &poolreadyfds)) {
287 FD_CLR(fd, &poolreadyfds);
288 poolnready--;
289 }
290 }
291
292 static Pool *
newPool(char * name)293 newPool(char *name)
294 {
295 Pool *p;
296
297 FREELIST_NEW(Pool, p);
298 memset(p, 0, sizeof(Pool));
299 DblListInit(&p->node);
300 DblListInit(&p->handles);
301 p->poolname = strdup(name);
302 return p;
303 }
304
305 Pool *
PoolStreamTemp(char * name,IOBFILE * inf,FILE * outf,int rw,HandleOps * ops)306 PoolStreamTemp(char *name, IOBFILE *inf, FILE *outf, int rw, HandleOps *ops)
307 {
308 Pool *p;
309 char dummy[3+sizeof(unsigned long)*2+1];
310 FILE *f = NULL;
311
312 if (name==NULL) {
313 sprintf(name=dummy, "_p@%lx",
314 (unsigned long)(inf ? (void *)inf : (void *)outf));
315 }
316
317 if (inf == NULL && outf == NULL && name != NULL) {
318 f = fopen(name, rw ? (rw>1 ? "w+b":"wb") : "rb");
319 if (f == NULL) {
320 OOGLError(0, "Can't open %s: %s", name, sperror());
321 return NULL;
322 }
323 }
324
325 if (f == NULL && inf == NULL && outf == NULL) {
326 OOGLError(0, "PoolStreamTemp(%s): file == NULL\n", name);
327 return NULL;
328 }
329
330 if (f) {
331 switch(rw) {
332 case 0:
333 inf = iobfileopen(f);
334 outf = NULL;
335 break;
336 case 1:
337 outf = f;
338 inf = NULL;
339 break;
340 case 2:
341 inf = iobfileopen(f);
342 outf = fdopen(dup(fileno(f)), "wb");
343 break;
344 }
345 } else if (rw != 1 && inf == NULL) {
346 inf = iobfileopen(fdopen(dup(fileno(outf)), "rb"));
347 } else if (rw != 0 && outf == NULL) {
348 outf = fdopen(dup(iobfileno(inf)), "wb");
349 }
350
351 if ((rw != 1 && inf == NULL) || (rw != 0 && outf == NULL)) {
352 OOGLError(0, "PoolStreamTemp(%s): file == NULL\n", name);
353 return NULL;
354 }
355
356 p = newPool(name);
357 p->ops = ops;
358 p->type = P_STREAM;
359
360 p->outf = outf;
361 p->inf = inf;
362 p->infd = p->inf ? iobfileno(p->inf) : -1;
363
364 /*p->handles = NULL;*/
365 p->resyncing = NULL;
366 p->otype = PO_ALL;
367 p->mode = inf && outf ? 2 : (outf ? 1 : 0);
368 p->seekable = (p->inf && lseek(iobfileno(p->inf),0,SEEK_CUR) != -1 &&
369 !isatty(iobfileno(p->inf)));
370 p->softEOF = !p->seekable;
371 p->level = (p->outf && lseek(fileno(p->outf),0,SEEK_CUR) != -1 &&
372 !isatty(fileno(p->outf)))
373 ? 0 : 1;
374 p->flags = PF_TEMP;
375 p->client_data = NULL;
376
377 #if HAVE_FCNTL
378 if (p->inf && p->infd >= 0) {
379 fcntl(p->infd, F_SETFL, fcntl(p->infd, F_GETFL) & ~o_nonblock);
380 }
381 if (p->outf && fileno(p->outf) >= 0) {
382 int fd = fileno(p->outf);
383 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~o_nonblock);
384 }
385 #endif /*unix*/
386
387 return p;
388 }
389
390 Pool *
PoolStreamOpen(char * name,FILE * f,int rw,HandleOps * ops)391 PoolStreamOpen(char *name, FILE *f, int rw, HandleOps *ops)
392 {
393 Pool *p;
394 struct stat st;
395
396 p = PoolByName(name, ops);
397
398 if (p == NULL) {
399 p = newPool(name);
400 p->ops = ops;
401 p->type = P_STREAM;
402 p->inf = NULL;
403 p->infd = -1;
404 p->outf = NULL;
405 p->mode = rw;
406 /*p->handles = NULL;*/
407 p->resyncing = NULL;
408 p->otype = PO_ALL;
409 p->level = 0;
410 p->flags = 0;
411 p->client_data = NULL;
412 } else {
413 if (rw == 0 && p->mode == 0 && p->inf != NULL
414 && p->softEOF == 0 && (p->flags & PF_REREAD) == 0
415 && stat(name, &st) == 0 && st.st_mtime == p->inf_mtime) {
416 iobfrewind(p->inf);
417 return p;
418 }
419
420 /*
421 * Combine modes. Allows e.g. adding write stream to read-only pool.
422 */
423 p->mode = ((p->mode+1) | (rw+1)) - 1;
424 if (p->inf && rw != 1) {
425 if (iobfile(p->inf) == stdin) {
426 iobfileclose(p->inf); /* leaves stdin open */
427 } else {
428 iobfclose(p->inf);
429 }
430 p->inf = NULL;
431 }
432 }
433
434 if (f == NULL || f == (FILE *)-1) {
435 if (rw != 1) {
436 if (strcmp(name, "-") == 0) {
437 f = stdin;
438 } else {
439 /* Try opening read/write first in case it's a Linux named pipe */
440 int fd;
441 /* Linux 2.0 is said to prefer that someone always has
442 * a named pipe open for writing as well as reading.
443 * But if we do that, we seem to lose the ENXIO error given
444 * to a process which opens that pipe for non-blocking write.
445 * Let's not do this now, even on Linux.
446 * Note that we do use PoolSleepFor() to slow down polling
447 * of pipes which claim to have data ready, but actually don't;
448 * I think that's what O_RDWR mode is intended to fix.
449 */
450 /* BTW, this is not Linux, but common Unix
451 * behaviour. Reading from a pipe with no writers will
452 * just return.
453 *
454 * cH: In principle we would want to avoid that
455 * O_NONBLOCK; but then the call to open() may
456 * actually block. Using O_RDONLY, however, seems to
457 * render the select stuff unusable.
458 *
459 * cH: update: at least on Linux, O_RDWR means that we
460 * always have a writer for this pipe (ourselves). It
461 * is probably more poratble to use a second file
462 * descriptor in write mode. However, leave it as is
463 * for now.
464 */
465 fd = open(name, O_RDWR | o_nonblock);
466 if (fd < 0)
467 fd = open(name, O_RDONLY | o_nonblock);
468
469 #if HAVE_UNIX_SOCKETS
470 /* Unix-domain socket? */
471 if (fd < 0 && errno == EOPNOTSUPP) {
472 struct sockaddr_un sa;
473 sa.sun_family = AF_UNIX;
474 strncpy(sa.sun_path, name, sizeof(sa.sun_path));
475 fd = socket(PF_UNIX, SOCK_STREAM, 0);
476 if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
477 close(fd);
478 fd = -1;
479 }
480 }
481 #endif /* HAVE_UNIX_SOCKETS */
482
483 if (fd < 0)
484 OOGLError(0, "Cannot open file \"%s\": %s",
485 name, sperror());
486 else
487 f = fdopen(fd, "rb");
488 }
489 p->inf = iobfileopen(f);
490 }
491 if (rw > 0) {
492 if (strcmp(name, "-") == 0)
493 p->outf = stdout;
494 else if ((p->outf = fopen(name, "wb")) == NULL)
495 OOGLError(0, "Cannot create \"%s\": %s", name, sperror());
496 }
497 } else {
498 if (rw != 1) {
499 p->inf = iobfileopen(f);
500 }
501 if (rw > 0) {
502 p->outf = (rw == 2) ? fdopen(dup(fileno(f)), "wb") : f;
503 }
504 }
505
506 if (p->inf == NULL && p->outf == NULL) {
507 PoolDelete(p);
508 return NULL;
509 }
510
511 /* We're committed now. */
512 if (DblListEmpty(&p->node)) {
513 DblListAddTail(&AllPools, &p->node);
514 }
515 p->seekable = false;
516 p->softEOF = false;
517 if (p->inf != NULL) {
518 p->infd = iobfileno(p->inf);
519 if (p->infd != -1) {
520 if (isatty(p->infd)) {
521 p->softEOF = true;
522 } else if (lseek(p->infd,0,SEEK_CUR) != -1) {
523 p->seekable = true;
524 }
525 if (fstat(p->infd, &st) < 0 || (st.st_mode & S_IFMT) == S_IFIFO) {
526 p->softEOF = true;
527 }
528 p->inf_mtime = st.st_mtime;
529 watchfd(p->infd);
530 #if HAVE_FCNTL
531 fcntl(p->infd, F_SETFL, fcntl(p->infd, F_GETFL) & ~o_nonblock);
532 #endif /* HAVE_FCNTL */
533 }
534 }
535 #if HAVE_FCNTL
536 if (p->outf && fileno(p->outf) >= 0) {
537 int fd = fileno(p->outf);
538 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~o_nonblock);
539 }
540 #endif /* HAVE_FCNTL */
541 if (p->level == 0 && p->outf &&
542 (lseek(fileno(p->outf),0,SEEK_CUR) == -1 || isatty(fileno(p->outf))))
543 p->level = 1;
544
545 return p;
546 }
547
548 /* Return "true" if succesful */
PoolSetMark(Pool * p)549 int PoolSetMark(Pool *p)
550 {
551 return iobfsetmark(PoolInputFile(p)) == 0;
552 }
553
554 /* Return "true" if succesful */
PoolSeekMark(Pool * p)555 int PoolSeekMark(Pool *p)
556 {
557 return iobfseekmark(PoolInputFile(p)) == 0;
558 }
559
560 /* Return "true" if succesful. BUT WHO CARES. Grin. */
PoolClearMark(Pool * p)561 int PoolClearMark(Pool *p)
562 {
563 return iobfclearmark(PoolInputFile(p)) == 0;
564 }
565
566 int
PoolIncLevel(Pool * p,int incr)567 PoolIncLevel(Pool *p, int incr)
568 {
569 if (p) {
570 p->level += incr;
571 if (p->level < 0) {
572 OOGLError(0, "PoolIncLevel(): negative level.\n");
573 }
574 return p->level;
575 } else {
576 return incr;
577 }
578 }
579
580 int
PoolOType(Pool * p,int otype)581 PoolOType(Pool *p, int otype)
582 {
583 (void)otype;
584 return p->otype;
585 }
586
587 void
PoolSetOType(Pool * p,int otype)588 PoolSetOType(Pool *p, int otype)
589 {
590 p->otype = otype;
591 }
592
593 IOBFILE *
PoolInputFile(Pool * p)594 PoolInputFile(Pool *p)
595 {
596 return (p && p->type == P_STREAM) ? p->inf : NULL;
597 }
598
599 FILE *
PoolOutputFile(Pool * p)600 PoolOutputFile(Pool *p)
601 {
602 return (p && p->type == P_STREAM) ? p->outf : NULL;
603 }
604
605 void *
PoolClientData(Pool * p)606 PoolClientData(Pool *p)
607 {
608 return (p ? p->client_data : NULL);
609 }
610
611 void
PoolSetClientData(Pool * p,void * data)612 PoolSetClientData(Pool *p, void *data)
613 {
614 p->client_data = data;
615 }
616
617 void
PoolDoReread(Pool * p)618 PoolDoReread(Pool *p)
619 {
620 p->flags |= PF_REREAD; /* Reread when asked */
621 }
622
PoolClose(Pool * p)623 void PoolClose(Pool *p)
624 {
625 if (p->ops->close && (p->flags & PF_CLOSING) == 0) {
626 p->flags |= PF_CLOSING;
627 if ((*p->ops->close)(p)) {
628 return;
629 }
630 }
631
632 if (p->type == P_STREAM) {
633 if (p->inf != NULL) {
634 unwatchfd(iobfileno(p->inf));
635 if (iobfile(p->inf) == stdin) {
636 iobfileclose(p->inf);
637 } else {
638 iobfclose(p->inf);
639 }
640 p->inf = NULL;
641 p->infd = -1;
642 }
643 if (p->outf != NULL) {
644 if (p->outf != stdout) fclose(p->outf);
645 p->outf = NULL;
646 }
647 /* PoolDelete(p); */ /* Shouldn't we delete the Pool? */
648 }
649 }
650
PoolDelete(Pool * p)651 void PoolDelete(Pool *p)
652 {
653 Handle *h, *hn;
654
655 if (p == NULL || (p->flags & PF_DELETED) != 0) {
656 return;
657 }
658
659 p->flags |= PF_DELETED;
660
661 if ((p->flags & PF_TEMP) == 0) {
662 DblListDelete(&p->node);
663 DblListIterate(&p->handles, Handle, poolnode, h, hn) {
664 h->whence = NULL;
665 DblListDelete(&h->poolnode);
666 HandleDelete(h);
667 }
668 }
669
670 free(p->poolname);
671 FREELIST_FREE(Pool, p);
672 }
673
PoolDetach(Pool * p)674 void PoolDetach(Pool *p)
675 {
676 if ((p->flags & PF_TEMP) == 0) {
677 DblListDelete(&p->node);
678 }
679 }
680
PoolReattach(Pool * p)681 void PoolReattach(Pool *p)
682 {
683 if ((p->flags & PF_TEMP) == 0) {
684 if (DblListEmpty(&p->node)) {
685 DblListAddTail(&AllPools, &p->node);
686 }
687 }
688 }
689
690 /*
691 * Marks a Pool as being awake. Doesn't update nexttowake;
692 * so this should only be called where awaken_until() gets a chance to run.
693 */
694 static void
awaken(Pool * p)695 awaken(Pool *p)
696 {
697 p->flags &= ~PF_ASLEEP;
698 timerclear(&p->awaken);
699 if (p->infd >= 0) {
700 watchfd(p->infd);
701 if (iobfhasdata(p->inf) && !FD_ISSET(p->infd, &poolreadyfds)) {
702 FD_SET(p->infd, &poolreadyfds);
703 poolnready++;
704 }
705 }
706 }
707
708 static void
awaken_until(struct timeval * until)709 awaken_until(struct timeval *until)
710 {
711 Pool *p;
712
713 nexttowake.tv_sec = FOREVER;
714 DblListIterateNoDelete(&AllPools, Pool, node, p) {
715 if (p->flags & PF_ASLEEP) {
716 if (timercmp(&p->awaken, until, <)) {
717 awaken(p);
718 } else if (p->inf != NULL && timercmp(&p->awaken, &nexttowake, <)) {
719 nexttowake = p->awaken;
720 }
721 }
722 }
723 }
724
725 /*
726 * PoolInputFDs(fds, maxfd)
727 * supplies a list of file descriptors for select() to watch for input.
728 * Sets *fds to the set of pool input file descriptors
729 * Sets *maxfd to the max+1 file descriptor in fds.
730 * Returns max time to sleep in seconds; 0 if some Pools have input immediately
731 * available (so select() shouldn't block).
732 */
733 float
PoolInputFDs(fd_set * fds,int * maxfd)734 PoolInputFDs(fd_set *fds, int *maxfd)
735 {
736 float timeleft = FOREVER;
737
738 #if defined(unix) || defined(__unix)
739 if (nexttowake.tv_sec != FOREVER) {
740 struct timeval now;
741 gettimeofday(&now, NULL);
742 if (timercmp(&nexttowake, &now, <))
743 awaken_until(&now);
744 timeleft = (nexttowake.tv_sec - now.tv_sec)
745 + .000001 * (nexttowake.tv_usec - now.tv_usec);
746 }
747 #endif
748
749 *fds = poolwatchfds;
750 *maxfd = poolmaxfd;
751 return poolnready != 0 || timeleft < 0 ? 0 : timeleft;
752 }
753
754 /*
755 * PoolInAll(fd_set *fds, int nfds)
756 * Read from all available pools, given a set of file descriptors
757 * which claim to have some input. We also import from all those which
758 * already have buffered input.
759 * nfds is an advisory upper bound on the number of fd's in *fds;
760 * if the caller doesn't keep the return value from select(), just use
761 * FD_SETSIZE or other huge number.
762 * Each fd used is removed from *fds, and *nfds is decremented.
763 * The return value is 1 if anything was read from any pool, 0 otherwise.
764 */
765
766 int
PoolInAll(fd_set * fds,int * nfds)767 PoolInAll(fd_set *fds, int *nfds)
768 {
769 Pool *p;
770 int got = 0;
771
772 /* We use DblListIterateNoDelete() _NOT_ because PoolIn() could
773 * not delete the current Pool p, because PoolIn() can delete
774 * _ANY_ pool so we need to work harder to make this
775 * PoolInAll()-loop failsafe.
776 */
777
778 DblListIterateNoDelete(&AllPools, Pool, node, p) {
779 if (p->type != P_STREAM || p->inf == NULL || p->infd < 0) {
780 continue;
781 }
782
783 if (FD_ISSET(p->infd, &poolreadyfds)) {
784 FD_CLR(p->infd, &poolreadyfds);
785 poolnready--;
786 if (PoolIn(p)) {
787 got++;
788 }
789 } else if (FD_ISSET(p->infd, fds)) {
790 FD_CLR(p->infd, fds);
791 (*nfds)--;
792 if (PoolIn(p)) {
793 got++;
794 }
795 }
796 /* We need to be very careful here: PoolIn() can have _ANY_
797 * sort of side effect, in particular it might delete _ANY_
798 * pool. Luckily we do not really delete pools; if "p" is
799 * actually in the deleted state, then it has its PF_DELETED
800 * flag set. In this case we simply restart the loop.
801 *
802 * NOTE: Just using &AllPools triggers a strict aliasing
803 * warning with gcc. Of course, I don't know if the funny
804 * "next->prev" stuff simply confuses the compiler such that
805 * it does no longer emit the warning, but still emits wrong
806 * code. In principle the construct should be ok. "next->prev"
807 * should always point back to &AllPools. Of course, the
808 * compiler cannot know this. Maybe this is the difference.
809 */
810 if (p->flags & PF_DELETED) {
811 p = DblListContainer(AllPools.next->prev, Pool, node);
812 }
813 }
814 return got;
815 }
816
817 static void
asleep(Pool * p,struct timeval * base,double offset)818 asleep(Pool *p, struct timeval *base, double offset)
819 {
820 struct timeval until;
821
822 base = timeof(base);
823 if (p->inf != NULL) {
824 p->flags |= PF_ASLEEP;
825 addtime(&until, base, offset);
826 if (timercmp(&until, &nexttowake, <)) {
827 nexttowake = until;
828 }
829 p->awaken = until;
830 if (p->infd >= 0) {
831 unwatchfd(p->infd);
832 if (FD_ISSET(p->infd, &poolreadyfds)) {
833 FD_CLR(p->infd, &poolreadyfds);
834 poolnready--;
835 }
836 }
837 }
838 }
839
840 void
PoolSleepUntil(Pool * p,double until)841 PoolSleepUntil(Pool *p, double until)
842 {
843 asleep(p, &p->timebase, until);
844 }
845
846 void
PoolSleepFor(Pool * p,double naptime)847 PoolSleepFor(Pool *p, double naptime)
848 {
849 asleep(p, NULL, naptime);
850 }
851
852 void
PoolSetTime(Pool * p,struct timeval * base,double time_at_base)853 PoolSetTime(Pool *p, struct timeval *base, double time_at_base)
854 {
855 base = timeof(base);
856 addtime(&p->timebase, base, -time_at_base);
857 }
858
859 double
PoolTimeAt(Pool * p,struct timeval * then)860 PoolTimeAt(Pool *p, struct timeval *then)
861 {
862 if (p->timebase.tv_sec == 0) timeof(&p->timebase);
863 then = timeof(then);
864 return then->tv_sec - p->timebase.tv_sec +
865 .000001*(then->tv_usec - p->timebase.tv_usec);
866 }
867
868 void
PoolAwaken(Pool * p)869 PoolAwaken(Pool *p)
870 {
871 awaken(p);
872 if (timercmp(&p->awaken, &nexttowake, <=))
873 awaken_until(&nexttowake);
874 }
875
876 bool
PoolASleep(Pool * p)877 PoolASleep(Pool *p)
878 {
879 if (p->flags & PF_ASLEEP) {
880 struct timeval now;
881 gettimeofday(&now, NULL);
882 if (timercmp(&p->awaken, &now, >)) {
883 return true;
884 }
885 awaken(p);
886 }
887 return false;
888 }
889
890 #if 0
891 static void
892 poolresync(Pool *p, int (*resync)())
893 { /* XXX implement this */
894 }
895 #endif
896
897 #define CBRA '{'
898 #define CKET '}'
899
900 /* The caller of this functions owns the returned handle and must call
901 * HandleDelete() to get rid of the handle.
902 */
PoolIn(Pool * p)903 Handle *PoolIn(Pool *p)
904 {
905 int c = 0;
906 Handle *h = NULL;
907 Ref *r = NULL;
908
909 if (p->type != P_STREAM) {
910 return NULL; /* Do shared memory here someday XXX */
911 }
912 if (p->inf == NULL || p->ops == NULL || p->ops->strmin == NULL) {
913 return NULL; /* No way to read */
914 }
915
916 if ((p->flags & PF_NOPREFETCH) ||
917 ((c = async_iobfnextc(p->inf, 3)) != NODATA && c != EOF)) {
918 if ((*p->ops->strmin)(p, &h, &r)) {
919 /* Attach nameless objects to a handle named for the Pool.
920 * Putting this code here in PoolIn() ensures we just bind
921 * names to top-level objects, not those nested inside a
922 * hierarchy.
923 */
924 if (h == NULL) {
925 h = HandleCreate(p->poolname, p->ops);
926 if (r != NULL) {
927 HandleSetObject(h, r);
928 /* Decrement reference count since we're handing
929 * ownership of the object to the Handle.
930 */
931 REFPUT(r);
932 if (h->whence == NULL) {
933 /* Increment the reference count lest PoolDelete()
934 * will also consume the attached object.
935 */
936 REFGET(Handle, h);
937 }
938 }
939 } else {
940 /* Increment the count such that the calling function
941 * can safely HandleDelete(retval) without destroying a
942 * handle explicitly declared in the pool (with define
943 * or hdefine).
944 */
945 REFGET(Handle, h);
946 }
947 if (h->whence) {
948 if (h->whence != p) {
949 /* steal the pool pointer */
950 DblListDelete(&h->poolnode);
951 h->whence = p;
952 DblListAdd(&p->handles, &h->poolnode);
953 }
954 REFPUT(h); /* no need to call HandleDelete() */
955 } else {
956 h->whence = p;
957 DblListAdd(&p->handles, &h->poolnode);
958 }
959
960 /* Remember whether we've read (PF_ANY) at least one and
961 * (PF_REREAD) at least two objects from this pool.
962 * There'll be a nontrivial side effect of rereading a file
963 * containing multiple objects, so we actually do reread if asked.
964 */
965 p->flags |= (p->flags & PF_ANY) ? PF_REREAD : PF_ANY;
966
967 /* handle_dump(); */
968
969 } else {
970 if (p->flags & PF_DELETED)
971 return NULL;
972 if (p->ops->resync) {
973 (*p->ops->resync)(p);
974 } else if (p->softEOF) {
975 iobfrewind(p->inf);
976 } else if (p->inf != NULL) { /* Careful lest already PoolClose()d */
977 if (p->infd >= 0) {
978 if (FD_ISSET(p->infd, &poolreadyfds)) {
979 FD_CLR(p->infd, &poolreadyfds);
980 poolnready--;
981 }
982 }
983 PoolClose(p);
984 return NULL;
985 }
986 }
987 if (p->seekable && p->inf != NULL)
988 c = iobfnextc(p->inf, 0); /* Notice EOF if appropriate */
989 }
990 if (c == EOF && iobfeof(p->inf)) {
991 if (p->softEOF) {
992 iobfrewind(p->inf);
993 /* cH: opening O_RDWR should fix this, as this leaves one
994 * active writer attached to the FIFO: ourselves.
995 */
996 /*PoolSleepFor(p, 1.0);*/ /* Give us a rest */
997 /* SVR4 poll() doesn't allow us to
998 * wait quietly when the sender closes a named pipe;
999 * any poll() for reading returns immediately with POLLHUP.
1000 * Then, attempts to read return EOF. We loop at full speed.
1001 * Can't see any better way to throttle this than to
1002 * quit listening for a while, at the cost of being slow to
1003 * respond if another sender connects. We wait 1 second.
1004 */
1005 } else {
1006 PoolClose(p);
1007 REFGET(Handle, h); /* PoolDelete() deletes also the handle. */
1008 PoolDelete(p);
1009 return h;
1010 }
1011 }
1012
1013 if (p->inf && !(p->flags & PF_ASLEEP) && p->infd >= 0) {
1014 /*
1015 * Anything left in stdio buffer? If so,
1016 * remember to try reading next time without waiting for select().
1017 */
1018 if (iobfhasdata(p->inf)) {
1019 if (!FD_ISSET(p->infd, &poolreadyfds)) {
1020 FD_SET(p->infd, &poolreadyfds);
1021 poolnready++;
1022 }
1023 } else {
1024 if (FD_ISSET(p->infd, &poolreadyfds)) {
1025 FD_CLR(p->infd, &poolreadyfds);
1026 poolnready--;
1027 }
1028 }
1029 }
1030 return h;
1031 }
1032
1033 /*
1034 * Support routine for writing {handle, object} pairs to Pools.
1035 * Checks the Pool's output type (PO_HANDLES/PO_DATA/PO_ALL). If
1036 * appropriate, writes something to the Pool representing the given
1037 * Handle. Returns nonzero if the associated object should also be
1038 * written literally.
1039 *
1040 * For global handles: also emit a "define" statement.
1041 */
1042 int
PoolStreamOutHandle(Pool * p,Handle * h,int havedata)1043 PoolStreamOutHandle(Pool *p, Handle *h, int havedata)
1044 {
1045 if (p == NULL || p->outf == NULL) {
1046 return 0;
1047 }
1048
1049 if (h == NULL || (p->otype & PO_DATA)) {
1050 return havedata;
1051 }
1052
1053 if (havedata && !h->obj_saved) {
1054 h->obj_saved = true;
1055 PoolFPrint(p, p->outf, "define \"%s\"\n", h->name);
1056 return true;
1057 }
1058
1059 if (h->whence != NULL && h->whence->seekable) {
1060 PoolFPrint(p, p->outf, " < \"");
1061 if (strcmp(h->name, p->poolname) == 0) {
1062 fprintf(p->outf, "%s\"\n", h->whence->poolname);
1063 } else {
1064 fprintf(p->outf, "%s:%s\"\n", h->whence->poolname, h->name);
1065 }
1066 } else {
1067 PoolFPrint(p, p->outf, ": \"%s\"\n", h->name);
1068 }
1069
1070 return havedata && !h->obj_saved &&
1071 (p->otype & (PO_DATA|PO_HANDLES)) == PO_ALL;
1072 }
1073
PoolFPrint(Pool * p,FILE * f,const char * format,...)1074 void PoolFPrint(Pool *p, FILE *f, const char *format, ...)
1075 {
1076 va_list alist;
1077
1078 if (p) {
1079 fprintf(f, "%*s", p->level*2, "");
1080 }
1081 va_start(alist, format);
1082 vfprintf(f, format, alist);
1083 va_end(alist);
1084 }
1085
PoolPrint(Pool * p,const char * format,...)1086 void PoolPrint(Pool *p, const char *format, ...)
1087 {
1088 va_list alist;
1089
1090 if (p) {
1091 fprintf(PoolOutputFile(p), "%*s", p->level*2, "");
1092 }
1093 va_start(alist, format);
1094 vfprintf(PoolOutputFile(p), format, alist);
1095 va_end(alist);
1096 }
1097
1098 /*
1099 * Local Variables: ***
1100 * c-basic-offset: 4 ***
1101 * End: ***
1102 */
1103