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