ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/ambient.c
Revision: 2.46
Committed: Sun Jan 10 12:45:19 1999 UTC (25 years, 3 months ago) by gwlarson
Content type: text/plain
Branch: MAIN
Changes since 2.45: +19 -7 lines
Log Message:
added ability to open and load read-only file in setambient()

File Contents

# Content
1 /* Copyright (c) 1996 Regents of the University of California */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ LBL";
5 #endif
6
7 /*
8 * ambient.c - routines dealing with ambient (inter-reflected) component.
9 */
10
11 #include "ray.h"
12
13 #include "octree.h"
14
15 #include "otypes.h"
16
17 #include "ambient.h"
18
19 #include "random.h"
20
21 #ifndef OCTSCALE
22 #define OCTSCALE 1.0 /* ceil((valid rad.)/(cube size)) */
23 #endif
24
25 typedef struct ambtree {
26 AMBVAL *alist; /* ambient value list */
27 struct ambtree *kid; /* 8 child nodes */
28 } AMBTREE; /* ambient octree */
29
30 extern CUBE thescene; /* contains space boundaries */
31
32 extern char *shm_boundary; /* memory sharing boundary */
33
34 #define MAXASET 511 /* maximum number of elements in ambient set */
35 OBJECT ambset[MAXASET+1]={0}; /* ambient include/exclude set */
36
37 double maxarad; /* maximum ambient radius */
38 double minarad; /* minimum ambient radius */
39
40 static AMBTREE atrunk; /* our ambient trunk node */
41
42 static FILE *ambfp = NULL; /* ambient file pointer */
43 static int nunflshed = 0; /* number of unflushed ambient values */
44
45 #ifndef SORT_THRESH
46 #ifdef BIGMEM
47 #define SORT_THRESH ((9L<<20)/sizeof(AMBVAL))
48 #else
49 #define SORT_THRESH ((3L<<20)/sizeof(AMBVAL))
50 #endif
51 #endif
52 #ifndef SORT_INTVL
53 #define SORT_INTVL (SORT_THRESH<<1)
54 #endif
55 #ifndef MAX_SORT_INTVL
56 #define MAX_SORT_INTVL (SORT_INTVL<<6)
57 #endif
58
59 static double avsum = 0.; /* computed ambient value sum (log) */
60 static unsigned int navsum = 0; /* number of values in avsum */
61 static unsigned int nambvals = 0; /* total number of indirect values */
62 static unsigned int nambshare = 0; /* number of values from file */
63 static unsigned long ambclock = 0; /* ambient access clock */
64 static unsigned long lastsort = 0; /* time of last value sort */
65 static long sortintvl = SORT_INTVL; /* time until next sort */
66
67 #define MAXACLOCK (1L<<30) /* clock turnover value */
68 /*
69 * Track access times unless we are sharing ambient values
70 * through memory on a multiprocessor, when we want to avoid
71 * claiming our own memory (copy on write). Go ahead anyway
72 * if more than two thirds of our values are unshared.
73 * Compile with -Dtracktime=0 to turn this code off.
74 */
75 #ifndef tracktime
76 #define tracktime (shm_boundary == NULL || nambvals > 3*nambshare)
77 #endif
78
79 #define AMBFLUSH (BUFSIZ/AMBVALSIZ)
80
81 #define newambval() (AMBVAL *)bmalloc(sizeof(AMBVAL))
82
83 static int initambfile(), avsave(), avinsert(), sortambvals(), avlmemi();
84 static AMBVAL *avstore();
85 #ifdef F_SETLKW
86 static aflock();
87 #endif
88
89
90 setambres(ar) /* set ambient resolution */
91 int ar;
92 {
93 ambres = ar < 0 ? 0 : ar; /* may be done already */
94 /* set min & max radii */
95 if (ar <= 0) {
96 minarad = 0;
97 maxarad = thescene.cusize / 2.0;
98 } else {
99 minarad = thescene.cusize / ar;
100 maxarad = 64 * minarad; /* heuristic */
101 if (maxarad > thescene.cusize / 2.0)
102 maxarad = thescene.cusize / 2.0;
103 }
104 if (minarad <= FTINY)
105 minarad = 10*FTINY;
106 if (maxarad <= minarad)
107 maxarad = 64 * minarad;
108 }
109
110
111 setambacc(newa) /* set ambient accuracy */
112 double newa;
113 {
114 double ambdiff;
115
116 if (newa < 0.0)
117 newa = 0.0;
118 ambdiff = fabs(newa - ambacc);
119 if (ambdiff >= .01 && (ambacc = newa) > FTINY && nambvals > 0)
120 sortambvals(1); /* rebuild tree */
121 }
122
123
124 setambient(afile) /* initialize calculation */
125 char *afile;
126 {
127 int readonly = 0;
128 long pos, flen;
129 AMBVAL amb;
130 /* init ambient limits */
131 setambres(ambres);
132 setambacc(ambacc);
133 if (afile == NULL)
134 return;
135 if (ambacc <= FTINY) {
136 sprintf(errmsg, "zero ambient accuracy so \"%s\" not opened",
137 afile);
138 error(WARNING, errmsg);
139 return;
140 }
141 /* open ambient file */
142 if ((ambfp = fopen(afile, "r+")) == NULL)
143 readonly = (ambfp = fopen(afile, "r")) != NULL;
144 if (ambfp != NULL) {
145 initambfile(0); /* file exists */
146 pos = ftell(ambfp);
147 while (readambval(&amb, ambfp))
148 avinsert(avstore(&amb));
149 nambshare = nambvals; /* share loaded values */
150 if (readonly) {
151 sprintf(errmsg,
152 "loaded %u values from read-only ambient file",
153 nambvals);
154 error(WARNING, errmsg);
155 fclose(ambfp); /* close file so no writes */
156 ambfp = NULL;
157 return; /* avoid ambsync() */
158 }
159 /* align file pointer */
160 pos += (long)nambvals*AMBVALSIZ;
161 flen = lseek(fileno(ambfp), 0L, 2);
162 if (flen != pos) {
163 sprintf(errmsg,
164 "ignoring last %ld values in ambient file (corrupted)",
165 (flen - pos)/AMBVALSIZ);
166 error(WARNING, errmsg);
167 fseek(ambfp, pos, 0);
168 ftruncate(fileno(ambfp), pos);
169 }
170 } else if ((ambfp = fopen(afile, "w+")) != NULL) {
171 initambfile(1); /* else create new file */
172 } else {
173 sprintf(errmsg, "cannot open ambient file \"%s\"", afile);
174 error(SYSTEM, errmsg);
175 }
176 nunflshed++; /* lie */
177 ambsync();
178 }
179
180
181 ambnotify(obj) /* record new modifier */
182 OBJECT obj;
183 {
184 static int hitlimit = 0;
185 register OBJREC *o = objptr(obj);
186 register char **amblp;
187
188 if (hitlimit || !ismodifier(o->otype))
189 return;
190 for (amblp = amblist; *amblp != NULL; amblp++)
191 if (!strcmp(o->oname, *amblp)) {
192 if (ambset[0] >= MAXASET) {
193 error(WARNING, "too many modifiers in ambient list");
194 hitlimit++;
195 return; /* should this be fatal? */
196 }
197 insertelem(ambset, obj);
198 return;
199 }
200 }
201
202
203 ambient(acol, r, nrm) /* compute ambient component for ray */
204 COLOR acol;
205 register RAY *r;
206 FVECT nrm;
207 {
208 static int rdepth = 0; /* ambient recursion */
209 double d, l;
210
211 if (ambdiv <= 0) /* no ambient calculation */
212 goto dumbamb;
213 /* check number of bounces */
214 if (rdepth >= ambounce)
215 goto dumbamb;
216 /* check ambient list */
217 if (ambincl != -1 && r->ro != NULL &&
218 ambincl != inset(ambset, r->ro->omod))
219 goto dumbamb;
220
221 if (ambacc <= FTINY) { /* no ambient storage */
222 rdepth++;
223 d = doambient(acol, r, r->rweight, NULL, NULL);
224 rdepth--;
225 if (d <= FTINY)
226 goto dumbamb;
227 return;
228 }
229
230 if (tracktime) /* sort to minimize thrashing */
231 sortambvals(0);
232 /* get ambient value */
233 setcolor(acol, 0.0, 0.0, 0.0);
234 d = sumambient(acol, r, nrm, rdepth,
235 &atrunk, thescene.cuorg, thescene.cusize);
236 if (d > FTINY) {
237 scalecolor(acol, 1.0/d);
238 return;
239 }
240 rdepth++; /* need to cache new value */
241 d = makeambient(acol, r, nrm, rdepth-1);
242 rdepth--;
243 if (d > FTINY)
244 return;
245 dumbamb: /* return global value */
246 copycolor(acol, ambval);
247 if (ambvwt <= 0 | navsum == 0)
248 return;
249 l = bright(ambval); /* average in computations */
250 if (l > FTINY) {
251 d = (log(l)*(double)ambvwt + avsum) /
252 (double)(ambvwt + navsum);
253 d = exp(d) / l;
254 scalecolor(acol, d); /* apply color of ambval */
255 } else {
256 d = exp( avsum / (double)navsum );
257 setcolor(acol, d, d, d); /* neutral color */
258 }
259 }
260
261
262 double
263 sumambient(acol, r, rn, al, at, c0, s) /* get interpolated ambient value */
264 COLOR acol;
265 register RAY *r;
266 FVECT rn;
267 int al;
268 AMBTREE *at;
269 FVECT c0;
270 double s;
271 {
272 double d, e1, e2, wt, wsum;
273 COLOR ct;
274 FVECT ck0;
275 int i;
276 register int j;
277 register AMBVAL *av;
278
279 wsum = 0.0;
280 /* do this node */
281 for (av = at->alist; av != NULL; av = av->next) {
282 if (tracktime)
283 av->latick = ambclock;
284 /*
285 * Ambient level test.
286 */
287 if (av->lvl > al) /* list sorted, so this works */
288 break;
289 if (av->weight < r->rweight-FTINY)
290 continue;
291 /*
292 * Ambient radius test.
293 */
294 d = av->pos[0] - r->rop[0];
295 e1 = d * d;
296 d = av->pos[1] - r->rop[1];
297 e1 += d * d;
298 d = av->pos[2] - r->rop[2];
299 e1 += d * d;
300 e1 /= av->rad * av->rad;
301 if (e1 > ambacc*ambacc*1.21)
302 continue;
303 /*
304 * Normal direction test.
305 */
306 e2 = (1.0 - DOT(av->dir, r->ron)) * r->rweight;
307 if (e2 < 0.0) e2 = 0.0;
308 if (e1 + e2 > ambacc*ambacc*1.21)
309 continue;
310 /*
311 * Ray behind test.
312 */
313 d = 0.0;
314 for (j = 0; j < 3; j++)
315 d += (r->rop[j] - av->pos[j]) *
316 (av->dir[j] + r->ron[j]);
317 if (d*0.5 < -minarad*ambacc-.001)
318 continue;
319 /*
320 * Jittering final test reduces image artifacts.
321 */
322 wt = sqrt(e1) + sqrt(e2);
323 if (wt > ambacc*(.9+.2*urand(9015+samplendx)))
324 continue;
325 if (wt <= 1e-3)
326 wt = 1e3;
327 else
328 wt = 1.0 / wt;
329 wsum += wt;
330 extambient(ct, av, r->rop, rn);
331 scalecolor(ct, wt);
332 addcolor(acol, ct);
333 }
334 if (at->kid == NULL)
335 return(wsum);
336 /* do children */
337 s *= 0.5;
338 for (i = 0; i < 8; i++) {
339 for (j = 0; j < 3; j++) {
340 ck0[j] = c0[j];
341 if (1<<j & i)
342 ck0[j] += s;
343 if (r->rop[j] < ck0[j] - OCTSCALE*s)
344 break;
345 if (r->rop[j] > ck0[j] + (1.0+OCTSCALE)*s)
346 break;
347 }
348 if (j == 3)
349 wsum += sumambient(acol, r, rn, al, at->kid+i, ck0, s);
350 }
351 return(wsum);
352 }
353
354
355 double
356 makeambient(acol, r, rn, al) /* make a new ambient value */
357 COLOR acol;
358 register RAY *r;
359 FVECT rn;
360 int al;
361 {
362 AMBVAL amb;
363 FVECT gp, gd;
364 /* compute weight */
365 amb.weight = pow(AVGREFL, (double)al);
366 if (r->rweight < 0.1*amb.weight) /* heuristic */
367 amb.weight = r->rweight;
368 /* compute ambient */
369 amb.rad = doambient(acol, r, amb.weight, gp, gd);
370 if (amb.rad <= FTINY)
371 return(0.0);
372 /* store it */
373 VCOPY(amb.pos, r->rop);
374 VCOPY(amb.dir, r->ron);
375 amb.lvl = al;
376 copycolor(amb.val, acol);
377 VCOPY(amb.gpos, gp);
378 VCOPY(amb.gdir, gd);
379 /* insert into tree */
380 avsave(&amb); /* and save to file */
381 if (rn != r->ron)
382 extambient(acol, &amb, r->rop, rn); /* texture */
383 return(amb.rad);
384 }
385
386
387 extambient(cr, ap, pv, nv) /* extrapolate value at pv, nv */
388 COLOR cr;
389 register AMBVAL *ap;
390 FVECT pv, nv;
391 {
392 FVECT v1;
393 register int i;
394 double d;
395
396 d = 1.0; /* zeroeth order */
397 /* gradient due to translation */
398 for (i = 0; i < 3; i++)
399 d += ap->gpos[i]*(pv[i]-ap->pos[i]);
400 /* gradient due to rotation */
401 VCROSS(v1, ap->dir, nv);
402 d += DOT(ap->gdir, v1);
403 if (d <= 0.0) {
404 setcolor(cr, 0.0, 0.0, 0.0);
405 return;
406 }
407 copycolor(cr, ap->val);
408 scalecolor(cr, d);
409 }
410
411
412 static
413 initambfile(creat) /* initialize ambient file */
414 int creat;
415 {
416 extern char *progname, *octname, VersionID[];
417
418 #ifdef F_SETLKW
419 aflock(creat ? F_WRLCK : F_RDLCK);
420 #endif
421 #ifdef MSDOS
422 setmode(fileno(ambfp), O_BINARY);
423 #endif
424 setbuf(ambfp, bmalloc(BUFSIZ+8));
425 if (creat) { /* new file */
426 newheader("RADIANCE", ambfp);
427 fprintf(ambfp, "%s -av %g %g %g -aw %d -ab %d -aa %g ",
428 progname, colval(ambval,RED),
429 colval(ambval,GRN), colval(ambval,BLU),
430 ambvwt, ambounce, ambacc);
431 fprintf(ambfp, "-ad %d -as %d -ar %d %s\n",
432 ambdiv, ambssamp, ambres,
433 octname==NULL ? "" : octname);
434 fprintf(ambfp, "SOFTWARE= %s\n", VersionID);
435 fputformat(AMBFMT, ambfp);
436 putc('\n', ambfp);
437 putambmagic(ambfp);
438 } else if (checkheader(ambfp, AMBFMT, NULL) < 0 || !hasambmagic(ambfp))
439 error(USER, "bad ambient file");
440 }
441
442
443 static
444 avsave(av) /* insert and save an ambient value */
445 AMBVAL *av;
446 {
447 avinsert(avstore(av));
448 if (ambfp == NULL)
449 return;
450 if (writambval(av, ambfp) < 0)
451 goto writerr;
452 if (++nunflshed >= AMBFLUSH)
453 if (ambsync() == EOF)
454 goto writerr;
455 return;
456 writerr:
457 error(SYSTEM, "error writing ambient file");
458 }
459
460
461 static AMBVAL *
462 avstore(aval) /* allocate memory and store aval */
463 register AMBVAL *aval;
464 {
465 register AMBVAL *av;
466 double d;
467
468 if ((av = newambval()) == NULL)
469 error(SYSTEM, "out of memory in avstore");
470 copystruct(av, aval);
471 av->latick = ambclock;
472 av->next = NULL;
473 nambvals++;
474 d = bright(av->val);
475 if (d > FTINY) { /* add to log sum for averaging */
476 avsum += log(d);
477 navsum++;
478 }
479 return(av);
480 }
481
482
483 #define ATALLOCSZ 512 /* #/8 trees to allocate at once */
484
485 static AMBTREE *atfreelist = NULL; /* free ambient tree structures */
486
487
488 static
489 AMBTREE *
490 newambtree() /* allocate 8 ambient tree structs */
491 {
492 register AMBTREE *atp, *upperlim;
493
494 if (atfreelist == NULL) { /* get more nodes */
495 atfreelist = (AMBTREE *)bmalloc(ATALLOCSZ*8*sizeof(AMBTREE));
496 if (atfreelist == NULL)
497 return(NULL);
498 /* link new free list */
499 upperlim = atfreelist + 8*(ATALLOCSZ-1);
500 for (atp = atfreelist; atp < upperlim; atp += 8)
501 atp->kid = atp + 8;
502 atp->kid = NULL;
503 }
504 atp = atfreelist;
505 atfreelist = atp->kid;
506 bzero((char *)atp, 8*sizeof(AMBTREE));
507 return(atp);
508 }
509
510
511 static
512 freeambtree(atp) /* free 8 ambient tree structs */
513 AMBTREE *atp;
514 {
515 atp->kid = atfreelist;
516 atfreelist = atp;
517 }
518
519
520 static
521 avinsert(av) /* insert ambient value in our tree */
522 register AMBVAL *av;
523 {
524 register AMBTREE *at;
525 register AMBVAL *ap;
526 AMBVAL avh;
527 FVECT ck0;
528 double s;
529 int branch;
530 register int i;
531
532 if (av->rad <= FTINY)
533 error(CONSISTENCY, "zero ambient radius in avinsert");
534 at = &atrunk;
535 VCOPY(ck0, thescene.cuorg);
536 s = thescene.cusize;
537 while (s*(OCTSCALE/2) > av->rad*ambacc) {
538 if (at->kid == NULL)
539 if ((at->kid = newambtree()) == NULL)
540 error(SYSTEM, "out of memory in avinsert");
541 s *= 0.5;
542 branch = 0;
543 for (i = 0; i < 3; i++)
544 if (av->pos[i] > ck0[i] + s) {
545 ck0[i] += s;
546 branch |= 1 << i;
547 }
548 at = at->kid + branch;
549 }
550 avh.next = at->alist; /* order by increasing level */
551 for (ap = &avh; ap->next != NULL; ap = ap->next)
552 if (ap->next->lvl >= av->lvl)
553 break;
554 av->next = ap->next;
555 ap->next = av;
556 at->alist = avh.next;
557 }
558
559
560 static
561 unloadatree(at, f) /* unload an ambient value tree */
562 register AMBTREE *at;
563 int (*f)();
564 {
565 register AMBVAL *av;
566 register int i;
567 /* transfer values at this node */
568 for (av = at->alist; av != NULL; av = at->alist) {
569 at->alist = av->next;
570 (*f)(av);
571 }
572 if (at->kid == NULL)
573 return;
574 for (i = 0; i < 8; i++) /* transfer and free children */
575 unloadatree(at->kid+i, f);
576 freeambtree(at->kid);
577 at->kid = NULL;
578 }
579
580
581 static struct avl {
582 AMBVAL *p;
583 unsigned long t;
584 } *avlist1; /* ambient value list with ticks */
585 static AMBVAL **avlist2; /* memory positions for sorting */
586 static int i_avlist; /* index for lists */
587
588
589 static
590 av2list(av)
591 register AMBVAL *av;
592 {
593 #ifdef DEBUG
594 if (i_avlist >= nambvals)
595 error(CONSISTENCY, "too many ambient values in av2list1");
596 #endif
597 avlist1[i_avlist].p = avlist2[i_avlist] = av;
598 avlist1[i_avlist++].t = av->latick;
599 }
600
601
602 static int
603 alatcmp(av1, av2) /* compare ambient values for MRA */
604 struct avl *av1, *av2;
605 {
606 register long lc = av2->t - av1->t;
607 return(lc<0 ? -1 : lc>0 ? 1 : 0);
608 }
609
610
611 static int
612 aposcmp(avp1, avp2) /* compare ambient value positions */
613 AMBVAL **avp1, **avp2;
614 {
615 return(*avp1 - *avp2);
616 }
617
618
619 #if 1
620 static int
621 avlmemi(avaddr) /* find list position from address */
622 AMBVAL *avaddr;
623 {
624 register AMBVAL **avlpp;
625
626 avlpp = (AMBVAL **)bsearch((char *)&avaddr, (char *)avlist2,
627 nambvals, sizeof(AMBVAL *), aposcmp);
628 if (avlpp == NULL)
629 error(CONSISTENCY, "address not found in avlmemi");
630 return(avlpp - avlist2);
631 }
632 #else
633 #define avlmemi(avaddr) ((AMBVAL **)bsearch((char *)&avaddr,(char *)avlist2, \
634 nambvals,sizeof(AMBVAL *),aposcmp) - avlist2)
635 #endif
636
637
638 static
639 sortambvals(always) /* resort ambient values */
640 int always;
641 {
642 AMBTREE oldatrunk;
643 AMBVAL tav, *tap, *pnext;
644 register int i, j;
645 /* see if it's time yet */
646 if (!always && (ambclock++ < lastsort+sortintvl ||
647 nambvals < SORT_THRESH))
648 return;
649 /*
650 * The idea here is to minimize memory thrashing
651 * in VM systems by improving reference locality.
652 * We do this by periodically sorting our stored ambient
653 * values in memory in order of most recently to least
654 * recently accessed. This ordering was chosen so that new
655 * ambient values (which tend to be less important) go into
656 * higher memory with the infrequently accessed values.
657 * Since we expect our values to need sorting less
658 * frequently as the process continues, we double our
659 * waiting interval after each call.
660 * This routine is also called by setambacc() with
661 * the "always" parameter set to 1 so that the ambient
662 * tree will be rebuilt with the new accuracy parameter.
663 */
664 if (tracktime) { /* allocate pointer arrays to sort */
665 avlist2 = (AMBVAL **)malloc(nambvals*sizeof(AMBVAL *));
666 avlist1 = (struct avl *)malloc(nambvals*sizeof(struct avl));
667 } else {
668 avlist2 = NULL;
669 avlist1 = NULL;
670 }
671 if (avlist1 == NULL) { /* no time tracking -- rebuild tree? */
672 if (avlist2 != NULL)
673 free((char *)avlist2);
674 if (always) { /* rebuild without sorting */
675 copystruct(&oldatrunk, &atrunk);
676 atrunk.alist = NULL;
677 atrunk.kid = NULL;
678 unloadatree(&oldatrunk, avinsert);
679 }
680 } else { /* sort memory by last access time */
681 /*
682 * Sorting memory is tricky because it isn't contiguous.
683 * We have to sort an array of pointers by MRA and also
684 * by memory position. We then copy values in "loops"
685 * to minimize memory hits. Nevertheless, we will visit
686 * everyone at least twice, and this is an expensive process
687 * when we're thrashing, which is when we need to do it.
688 */
689 #ifdef DEBUG
690 sprintf(errmsg, "sorting %u ambient values at ambclock=%lu...",
691 nambvals, ambclock);
692 eputs(errmsg);
693 #endif
694 i_avlist = 0;
695 unloadatree(&atrunk, av2list); /* empty current tree */
696 #ifdef DEBUG
697 if (i_avlist < nambvals)
698 error(CONSISTENCY, "missing ambient values in sortambvals");
699 #endif
700 qsort((char *)avlist1, nambvals, sizeof(struct avl), alatcmp);
701 qsort((char *)avlist2, nambvals, sizeof(AMBVAL *), aposcmp);
702 for (i = 0; i < nambvals; i++) {
703 if (avlist1[i].p == NULL)
704 continue;
705 tap = avlist2[i];
706 copystruct(&tav, tap);
707 for (j = i; (pnext = avlist1[j].p) != tap;
708 j = avlmemi(pnext)) {
709 copystruct(avlist2[j], pnext);
710 avinsert(avlist2[j]);
711 avlist1[j].p = NULL;
712 }
713 copystruct(avlist2[j], &tav);
714 avinsert(avlist2[j]);
715 avlist1[j].p = NULL;
716 }
717 free((char *)avlist1);
718 free((char *)avlist2);
719 /* compute new sort interval */
720 sortintvl = ambclock - lastsort;
721 if (sortintvl >= MAX_SORT_INTVL/2)
722 sortintvl = MAX_SORT_INTVL;
723 else
724 sortintvl <<= 1; /* wait twice as long next */
725 #ifdef DEBUG
726 eputs("done\n");
727 #endif
728 }
729 if (ambclock >= MAXACLOCK)
730 ambclock = MAXACLOCK/2;
731 lastsort = ambclock;
732 }
733
734
735 #ifdef F_SETLKW
736
737 static
738 aflock(typ) /* lock/unlock ambient file */
739 int typ;
740 {
741 static struct flock fls; /* static so initialized to zeroes */
742
743 fls.l_type = typ;
744 if (fcntl(fileno(ambfp), F_SETLKW, &fls) < 0)
745 error(SYSTEM, "cannot (un)lock ambient file");
746 }
747
748
749 int
750 ambsync() /* synchronize ambient file */
751 {
752 static FILE *ambinp = NULL;
753 static long lastpos = -1;
754 long flen;
755 AMBVAL avs;
756 register int n;
757
758 if (nunflshed == 0)
759 return(0);
760 if (lastpos < 0) /* initializing (locked in initambfile) */
761 goto syncend;
762 /* gain exclusive access */
763 aflock(F_WRLCK);
764 /* see if file has grown */
765 if ((flen = lseek(fileno(ambfp), 0L, 2)) < 0)
766 goto seekerr;
767 if (n = flen - lastpos) { /* file has grown */
768 if (ambinp == NULL) { /* use duplicate filedes */
769 ambinp = fdopen(dup(fileno(ambfp)), "r");
770 if (ambinp == NULL)
771 error(SYSTEM, "fdopen failed in ambsync");
772 }
773 if (fseek(ambinp, lastpos, 0) < 0)
774 goto seekerr;
775 while (n >= AMBVALSIZ) { /* load contributed values */
776 if (!readambval(&avs, ambinp)) {
777 sprintf(errmsg,
778 "ambient file corrupted near character %ld",
779 flen - n);
780 error(WARNING, errmsg);
781 break;
782 }
783 avinsert(avstore(&avs));
784 n -= AMBVALSIZ;
785 }
786 /*** seek always as safety measure
787 if (n) ***/ /* alignment */
788 if (lseek(fileno(ambfp), flen-n, 0) < 0)
789 goto seekerr;
790 }
791 #ifdef DEBUG
792 if (ambfp->_ptr - ambfp->_base != nunflshed*AMBVALSIZ) {
793 sprintf(errmsg, "ambient file buffer at %d rather than %d",
794 ambfp->_ptr - ambfp->_base,
795 nunflshed*AMBVALSIZ);
796 error(CONSISTENCY, errmsg);
797 }
798 #endif
799 syncend:
800 n = fflush(ambfp); /* calls write() at last */
801 if ((lastpos = lseek(fileno(ambfp), 0L, 1)) < 0)
802 goto seekerr;
803 aflock(F_UNLCK); /* release file */
804 nunflshed = 0;
805 return(n);
806 seekerr:
807 error(SYSTEM, "seek failed in ambsync");
808 }
809
810 #else
811
812 int
813 ambsync() /* flush ambient file */
814 {
815 if (nunflshed == 0)
816 return(0);
817 nunflshed = 0;
818 return(fflush(ambfp));
819 }
820
821 #endif