ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.67
Committed: Tue Apr 10 05:16:50 2012 UTC (12 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 1.66: +58 -3 lines
Log Message:
Eliminated file thread locking in parent process

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rtcontrib.c,v 1.66 2012/02/23 06:03:46 greg Exp $";
3 #endif
4 /*
5 * Gather rtrace output to compute contributions from particular sources
6 */
7
8 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
9 Need to refactor code by forking a subprocess for each
10 rtrace call to take output and accumulate it into bins
11 for the parent process. This will avoid our current
12 bottleneck around processing output queues, computing
13 bins and the associated buffer growth, which can be crazy
14 (gigabytes/subprocess). Each child process will return
15 a ray number and a fully computed and ready-to-output
16 record of modifiers and their summed bins. These will
17 be queued and sorted by the parent for ordered output.
18 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX*/
19
20 #include "standard.h"
21 #include <ctype.h>
22 #include <signal.h>
23 #include "platform.h"
24 #include "rtprocess.h"
25 #include "selcall.h"
26 #include "color.h"
27 #include "resolu.h"
28 #include "lookup.h"
29 #include "calcomp.h"
30
31 #ifdef _WIN32
32 typedef int ssize_t;
33 #endif
34
35 #ifndef MAXMODLIST
36 #define MAXMODLIST 1024 /* maximum modifiers we'll track */
37 #endif
38
39 ssize_t treebufsiz = BUFSIZ; /* current tree buffer size */
40
41 typedef double DCOLOR[3]; /* double-precision color */
42
43 /*
44 * The MODCONT structure is used to accumulate ray contributions
45 * for a particular modifier, which may be subdivided into bins
46 * if binv evaluates > 0. If outspec contains a %s in it, this will
47 * be replaced with the modifier name. If outspec contains a %d in it,
48 * this will be used to create one output file per bin, otherwise all bins
49 * will be written to the same file, in order. If the global outfmt
50 * is 'c', then a 4-byte RGBE pixel will be output for each bin value
51 * and the file will conform to a RADIANCE image if xres & yres are set.
52 */
53 typedef struct {
54 const char *outspec; /* output file specification */
55 const char *modname; /* modifier name */
56 EPNODE *binv; /* bin value expression */
57 int nbins; /* number of accumulation bins */
58 DCOLOR cbin[1]; /* contribution bins (extends struct) */
59 } MODCONT; /* modifier contribution */
60
61 static void mcfree(void *p) { epfree((*(MODCONT *)p).binv); free(p); }
62
63 LUTAB modconttab = LU_SINIT(NULL,mcfree); /* modifier lookup table */
64
65 /*
66 * The STREAMOUT structure holds an open FILE pointer and a count of
67 * the number of RGB triplets per record, or 0 if unknown.
68 */
69 typedef struct {
70 FILE *ofp; /* output file pointer */
71 int outpipe; /* output is to a pipe */
72 int reclen; /* triplets/record */
73 int xr, yr; /* output resolution for picture */
74 } STREAMOUT;
75
76 /* close output stream and free record */
77 static void
78 closestream(void *p)
79 {
80 STREAMOUT *sop = (STREAMOUT *)p;
81 int status;
82 if (sop->outpipe)
83 status = pclose(sop->ofp);
84 else
85 status = fclose(sop->ofp);
86 if (status)
87 error(SYSTEM, "error closing output stream");
88 free(p);
89 }
90
91 LUTAB ofiletab = LU_SINIT(free,closestream); /* output file table */
92
93 #define OF_MODIFIER 01
94 #define OF_BIN 02
95
96 STREAMOUT *getostream(const char *ospec, const char *mname, int bn, int noopen);
97 int ofname(char *oname, const char *ospec, const char *mname, int bn);
98 void printheader(FILE *fout, const char *info);
99 void printresolu(FILE *fout, int xr, int yr);
100
101 /*
102 * The rcont structure is used to manage i/o with a particular
103 * rtrace child process. Input is passed unchanged from stdin,
104 * and output is processed in input order and accumulated according
105 * to the corresponding modifier and bin number.
106 */
107 struct rtproc {
108 struct rtproc *next; /* next in list of processes */
109 SUBPROC pd; /* rtrace pipe descriptors */
110 unsigned long raynum; /* ray number for this tree */
111 size_t bsiz; /* ray tree buffer length */
112 char *buf; /* ray tree buffer */
113 size_t nbr; /* number of bytes from rtrace */
114 }; /* rtrace process buffer */
115
116 /* rtrace command and defaults */
117 char *rtargv[256+2*MAXMODLIST] = { "rtrace",
118 "-dj", ".9", "-dr", "3",
119 "-ab", "1", "-ad", "350", };
120
121 int rtargc = 9;
122 /* overriding rtrace options */
123 char *myrtopts[] = { "-h-", "-x", "1", "-y", "0",
124 "-dt", "0", "-as", "0", "-aa", "0", NULL };
125
126 #define RTCOEFF "-o~~~TmWdp" /* compute coefficients only */
127 #define RTCONTRIB "-o~~~TmVdp" /* compute ray contributions */
128
129 struct rtproc rt0; /* head of rtrace process list */
130
131 struct rtproc *rt_unproc = NULL; /* unprocessed ray trees */
132
133 #define PERSIST_NONE 0 /* no persist file */
134 #define PERSIST_SINGLE 1 /* user set -P persist */
135 #define PERSIST_PARALL 2 /* user set -PP persist */
136 #define PERSIST_OURS 3 /* -PP persist belongs to us */
137 int persist_state = PERSIST_NONE; /* persist file state */
138 char persistfn[] = "pfXXXXXX"; /* our persist file name, if set */
139
140 int gargc; /* global argc */
141 char **gargv; /* global argv */
142 #define progname gargv[0]
143
144 char *octree; /* global octree argument */
145
146 int inpfmt = 'a'; /* input format */
147 int outfmt = 'a'; /* output format */
148
149 int header = 1; /* output header? */
150 int force_open = 0; /* truncate existing output? */
151 int recover = 0; /* recover previous output? */
152 int accumulate = 1; /* input rays per output record */
153 int xres = 0; /* horiz. output resolution */
154 int yres = 0; /* vert. output resolution */
155
156 int account; /* current accumulation count */
157 unsigned long raysleft; /* number of rays left to trace */
158 long waitflush; /* how long until next flush */
159
160 unsigned long lastray = 0; /* last ray number sent */
161 unsigned long lastdone = 0; /* last ray processed */
162
163 int using_stdout = 0; /* are we using stdout? */
164
165 const char *modname[MAXMODLIST]; /* ordered modifier name list */
166 int nmods = 0; /* number of modifiers */
167
168 #define queue_length() (lastray - lastdone)
169
170 MODCONT *growmodifier(MODCONT *mp, int nb);
171 MODCONT *addmodifier(char *modn, char *outf, char *binv, int bincnt);
172 void addmodfile(char *fname, char *outf, char *binv, int bincnt);
173
174 void init(int np);
175 int done_rprocs(struct rtproc *rtp);
176 void reload_output(void);
177 void recover_output(FILE *fin);
178 void trace_contribs(FILE *fin);
179 struct rtproc *wait_rproc(void);
180 struct rtproc *get_rproc(void);
181 void queue_raytree(struct rtproc *rtp);
182 void process_queue(void);
183
184 void put_contrib(const DCOLOR cnt, FILE *fout);
185 void add_contrib(const char *modn);
186 void done_contrib(int navg);
187
188 #ifdef getc_unlocked /* avoid nasty overheads */
189 #undef getc
190 #define getc getc_unlocked
191 #undef putc
192 #define putc putc_unlocked
193 #undef ferror
194 #define ferror ferror_unlocked
195 static int
196 fread_unl(void *ptr, int size, int nitems, FILE *fp)
197 {
198 char *p = (char *)ptr;
199 int len = size*nitems;
200 while (len-- > 0) {
201 int c = getc_unlocked(fp);
202 if (c == EOF)
203 return((p - (char *)ptr)/size);
204 *p++ = c;
205 }
206 return(nitems);
207 }
208 #undef fread
209 #define fread fread_unl
210 static int
211 fwrite_unl(const void *ptr, int size, int nitems, FILE *fp)
212 {
213 const char *p = (const char *)ptr;
214 int len = size*nitems;
215 while (len-- > 0)
216 putc_unlocked(*p++, fp);
217 if (ferror_unlocked(fp))
218 return(0);
219 return(nitems);
220 }
221 #undef fwrite
222 #define fwrite fwrite_unl
223 #endif
224
225 /* return number of open rtrace processes */
226 static int
227 nrtprocs(void)
228 {
229 int nrtp = 0;
230 struct rtproc *rtp;
231
232 for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
233 nrtp += rtp->pd.running;
234 return(nrtp);
235 }
236
237 /* set input/output format */
238 static void
239 setformat(const char *fmt)
240 {
241 switch (fmt[0]) {
242 case 'f':
243 case 'd':
244 SET_FILE_BINARY(stdin);
245 /* fall through */
246 case 'a':
247 inpfmt = fmt[0];
248 break;
249 default:
250 goto fmterr;
251 }
252 switch (fmt[1]) {
253 case '\0':
254 outfmt = inpfmt;
255 return;
256 case 'a':
257 case 'f':
258 case 'd':
259 case 'c':
260 outfmt = fmt[1];
261 break;
262 default:
263 goto fmterr;
264 }
265 if (!fmt[2])
266 return;
267 fmterr:
268 sprintf(errmsg, "Illegal i/o format: -f%s", fmt);
269 error(USER, errmsg);
270 }
271
272 /* gather rays from rtrace and output contributions */
273 int
274 main(int argc, char *argv[])
275 {
276 int contrib = 0;
277 int nprocs = 1;
278 char *curout = NULL;
279 char *binval = NULL;
280 int bincnt = 0;
281 char fmt[8];
282 int i, j;
283 /* need at least one argument */
284 if (argc < 2) {
285 fprintf(stderr,
286 "Usage: %s [-n nprocs][-V][-r][-e expr][-f source][-o ospec][-b binv] {-m mod | -M file} [rtrace options] octree\n",
287 argv[0]);
288 exit(1);
289 }
290 /* global program name */
291 gargv = argv;
292 /* initialize calcomp routines */
293 esupport |= E_VARIABLE|E_FUNCTION|E_INCHAN|E_RCONST|E_REDEFW;
294 esupport &= ~(E_OUTCHAN);
295 varset("PI", ':', PI);
296 /* get our options */
297 for (i = 1; i < argc-1; i++) {
298 /* expand arguments */
299 while ((j = expandarg(&argc, &argv, i)) > 0)
300 ;
301 if (j < 0) {
302 fprintf(stderr, "%s: cannot expand '%s'\n",
303 argv[0], argv[i]);
304 exit(1);
305 }
306 if (argv[i][0] == '-')
307 switch (argv[i][1]) {
308 case 'n': /* number of processes */
309 if (argv[i][2] || i >= argc-2) break;
310 nprocs = atoi(argv[++i]);
311 if (nprocs <= 0)
312 error(USER, "illegal number of processes");
313 continue;
314 case 'V': /* output contributions */
315 switch (argv[i][2]) {
316 case '\0':
317 contrib = !contrib;
318 continue;
319 case '+': case '1':
320 case 'T': case 't':
321 case 'Y': case 'y':
322 contrib = 1;
323 continue;
324 case '-': case '0':
325 case 'F': case 'f':
326 case 'N': case 'n':
327 contrib = 0;
328 continue;
329 }
330 break;
331 case 'c': /* input rays per output */
332 if (argv[i][2] || i >= argc-2) break;
333 accumulate = atoi(argv[++i]);
334 continue;
335 case 'r': /* recover output */
336 if (argv[i][2]) break;
337 recover = 1;
338 continue;
339 case 'h': /* output header? */
340 switch (argv[i][2]) {
341 case '\0':
342 header = !header;
343 continue;
344 case '+': case '1':
345 case 'T': case 't':
346 case 'Y': case 'y':
347 header = 1;
348 continue;
349 case '-': case '0':
350 case 'F': case 'f':
351 case 'N': case 'n':
352 header = 0;
353 continue;
354 }
355 break;
356 case 'f': /* file or force or format */
357 if (!argv[i][2]) {
358 char *fpath;
359 if (i >= argc-2) break;
360 fpath = getpath(argv[++i],
361 getrlibpath(), R_OK);
362 if (fpath == NULL) {
363 sprintf(errmsg,
364 "cannot find file '%s'",
365 argv[i]);
366 error(USER, errmsg);
367 }
368 fcompile(fpath);
369 continue;
370 }
371 if (argv[i][2] == 'o') {
372 force_open = 1;
373 continue;
374 }
375 setformat(argv[i]+2);
376 continue;
377 case 'e': /* expression */
378 if (argv[i][2] || i >= argc-2) break;
379 scompile(argv[++i], NULL, 0);
380 continue;
381 case 'o': /* output file spec. */
382 if (argv[i][2] || i >= argc-2) break;
383 curout = argv[++i];
384 continue;
385 case 'x': /* horiz. output resolution */
386 if (argv[i][2] || i >= argc-2) break;
387 xres = atoi(argv[++i]);
388 continue;
389 case 'y': /* vert. output resolution */
390 if (argv[i][2] || i >= argc-2) break;
391 yres = atoi(argv[++i]);
392 continue;
393 case 'b': /* bin expression/count */
394 if (i >= argc-2) break;
395 if (argv[i][2] == 'n') {
396 bincnt = (int)(eval(argv[++i]) + .5);
397 continue;
398 }
399 if (argv[i][2]) break;
400 binval = argv[++i];
401 continue;
402 case 'm': /* modifier name */
403 if (argv[i][2] || i >= argc-2) break;
404 rtargv[rtargc++] = "-ti";
405 rtargv[rtargc++] = argv[++i];
406 addmodifier(argv[i], curout, binval, bincnt);
407 continue;
408 case 'M': /* modifier file */
409 if (argv[i][2] || i >= argc-2) break;
410 rtargv[rtargc++] = "-tI";
411 rtargv[rtargc++] = argv[++i];
412 addmodfile(argv[i], curout, binval, bincnt);
413 continue;
414 case 'P': /* persist file */
415 if (i >= argc-2) break;
416 persist_state = (argv[i][2] == 'P') ?
417 PERSIST_PARALL : PERSIST_SINGLE;
418 rtargv[rtargc++] = argv[i];
419 rtargv[rtargc++] = argv[++i];
420 continue;
421 }
422 rtargv[rtargc++] = argv[i]; /* assume rtrace option */
423 }
424 if (accumulate <= 0) /* no output flushing for single record */
425 xres = yres = 0;
426 /* set global argument list */
427 gargc = argc; gargv = argv;
428 /* add "mandatory" rtrace settings */
429 for (j = 0; myrtopts[j] != NULL; j++)
430 rtargv[rtargc++] = myrtopts[j];
431 rtargv[rtargc++] = contrib ? RTCONTRIB : RTCOEFF;
432 /* just asking for defaults? */
433 if (!strcmp(argv[i], "-defaults")) {
434 char nps[8], sxres[16], syres[16];
435 char *rtpath;
436 printf("-c %-5d\t\t\t# accumulated rays per record\n",
437 accumulate);
438 printf("-V%c\t\t\t\t# output %s\n", contrib ? '+' : '-',
439 contrib ? "contributions" : "coefficients");
440 fflush(stdout); /* report OUR options */
441 rtargv[rtargc++] = "-n";
442 sprintf(nps, "%d", nprocs);
443 rtargv[rtargc++] = nps;
444 rtargv[rtargc++] = header ? "-h+" : "-h-";
445 sprintf(fmt, "-f%c%c", inpfmt, outfmt);
446 rtargv[rtargc++] = fmt;
447 rtargv[rtargc++] = "-x";
448 sprintf(sxres, "%d", xres);
449 rtargv[rtargc++] = sxres;
450 rtargv[rtargc++] = "-y";
451 sprintf(syres, "%d", yres);
452 rtargv[rtargc++] = syres;
453 rtargv[rtargc++] = "-defaults";
454 rtargv[rtargc] = NULL;
455 rtpath = getpath(rtargv[0], getenv("PATH"), X_OK);
456 if (rtpath == NULL) {
457 eputs(rtargv[0]);
458 eputs(": command not found\n");
459 exit(1);
460 }
461 execv(rtpath, rtargv);
462 perror(rtpath); /* execv() should not return */
463 exit(1);
464 }
465 if (nprocs > 1) { /* add persist file if parallel */
466 if (persist_state == PERSIST_SINGLE)
467 error(USER, "use -PP option for multiple processes");
468 if (persist_state == PERSIST_NONE) {
469 rtargv[rtargc++] = "-PP";
470 rtargv[rtargc++] = mktemp(persistfn);
471 persist_state = PERSIST_OURS;
472 }
473 }
474 /* add format string */
475 sprintf(fmt, "-f%cf", inpfmt);
476 rtargv[rtargc++] = fmt;
477 /* octree argument is last */
478 if (i <= 0 || i != argc-1 || argv[i][0] == '-')
479 error(USER, "missing octree argument");
480 rtargv[rtargc++] = octree = argv[i];
481 rtargv[rtargc] = NULL;
482 /* start rtrace & recover if requested */
483 init(nprocs);
484 /* compute contributions */
485 trace_contribs(stdin);
486 /* clean up */
487 quit(0);
488 }
489
490 #ifndef SIGALRM
491 #define SIGALRM SIGTERM
492 #endif
493 /* kill persistent rtrace process */
494 static void
495 killpersist(void)
496 {
497 FILE *fp = fopen(persistfn, "r");
498 RT_PID pid;
499
500 if (fp == NULL)
501 return;
502 if (fscanf(fp, "%*s %d", &pid) != 1 || kill(pid, SIGALRM) < 0)
503 unlink(persistfn);
504 fclose(fp);
505 }
506
507 /* close rtrace processes and clean up */
508 int
509 done_rprocs(struct rtproc *rtp)
510 {
511 int st0, st1 = 0;
512
513 if (rtp->next != NULL) { /* close last opened first! */
514 st1 = done_rprocs(rtp->next);
515 free((void *)rtp->next);
516 rtp->next = NULL;
517 }
518 st0 = close_process(&rtp->pd);
519 if (st0 < 0)
520 error(WARNING, "unknown return status from rtrace process");
521 else if (st0 > 0)
522 return(st0);
523 return(st1);
524 }
525
526 /* exit with status */
527 void
528 quit(int status)
529 {
530 int rtstat;
531
532 if (persist_state == PERSIST_OURS) /* terminate waiting rtrace */
533 killpersist();
534 /* clean up rtrace process(es) */
535 rtstat = done_rprocs(&rt0);
536 if (status == 0)
537 status = rtstat;
538 exit(status); /* flushes all output streams */
539 }
540
541 /* start rtrace processes and initialize */
542 void
543 init(int np)
544 {
545 struct rtproc *rtp;
546 int i;
547 int maxbytes;
548 /* make sure we have something to do */
549 if (!nmods)
550 error(USER, "No modifiers specified");
551 /* assign ray variables */
552 scompile("Dx=$1;Dy=$2;Dz=$3;", NULL, 0);
553 scompile("Px=$4;Py=$5;Pz=$6;", NULL, 0);
554 /* set up signal handling */
555 signal(SIGINT, quit);
556 #ifdef SIGHUP
557 signal(SIGHUP, quit);
558 #endif
559 #ifdef SIGTERM
560 signal(SIGTERM, quit);
561 #endif
562 #ifdef SIGPIPE
563 signal(SIGPIPE, quit);
564 #endif
565 rtp = &rt0; /* start rtrace process(es) */
566 for (i = 0; i++ < np; ) {
567 errno = 0;
568 maxbytes = open_process(&rtp->pd, rtargv);
569 if (maxbytes == 0) {
570 eputs(rtargv[0]);
571 eputs(": command not found\n");
572 exit(1);
573 }
574 if (maxbytes < 0)
575 error(SYSTEM, "cannot start rtrace process");
576 if (maxbytes > treebufsiz)
577 treebufsiz = maxbytes;
578 rtp->raynum = 0;
579 rtp->bsiz = 0;
580 rtp->buf = NULL;
581 rtp->nbr = 0;
582 if (i == np) /* last process? */
583 break;
584 if (i == 1)
585 sleep(2); /* wait for persist file */
586 rtp->next = (struct rtproc *)malloc(sizeof(struct rtproc));
587 if (rtp->next == NULL)
588 error(SYSTEM, "out of memory in init");
589 rtp = rtp->next;
590 }
591 rtp->next = NULL; /* terminate list */
592 if (yres > 0) {
593 if (xres > 0)
594 raysleft = (unsigned long)xres*yres;
595 else
596 raysleft = yres;
597 } else
598 raysleft = 0;
599 if ((account = accumulate) > 0)
600 raysleft *= accumulate;
601 waitflush = (yres > 0) & (xres > 1) ? 0 : xres;
602 if (!recover)
603 return;
604 /* recover previous values */
605 if (accumulate <= 0)
606 reload_output();
607 else
608 recover_output(stdin);
609 }
610
611 /* grow modifier to accommodate more bins */
612 MODCONT *
613 growmodifier(MODCONT *mp, int nb)
614 {
615 if (nb <= mp->nbins)
616 return mp;
617 mp = (MODCONT *)realloc(mp, sizeof(MODCONT) + sizeof(DCOLOR)*(nb-1));
618 if (mp == NULL)
619 error(SYSTEM, "out of memory in growmodifier");
620 memset(mp->cbin+mp->nbins, 0, sizeof(DCOLOR)*(nb-mp->nbins));
621 mp->nbins = nb;
622 return mp;
623 }
624
625 /* add modifier to our list to track */
626 MODCONT *
627 addmodifier(char *modn, char *outf, char *binv, int bincnt)
628 {
629 LUENT *lep = lu_find(&modconttab, modn);
630 MODCONT *mp;
631 int i;
632
633 if (lep->data != NULL) {
634 sprintf(errmsg, "duplicate modifier '%s'", modn);
635 error(USER, errmsg);
636 }
637 if (nmods >= MAXMODLIST)
638 error(INTERNAL, "too many modifiers");
639 modname[nmods++] = modn; /* XXX assumes static string */
640 lep->key = modn; /* XXX assumes static string */
641 mp = (MODCONT *)malloc(sizeof(MODCONT));
642 if (mp == NULL)
643 error(SYSTEM, "out of memory in addmodifier");
644 mp->outspec = outf; /* XXX assumes static string */
645 mp->modname = modn; /* XXX assumes static string */
646 if (binv == NULL)
647 binv = "0"; /* use single bin if unspecified */
648 mp->binv = eparse(binv);
649 if (mp->binv->type == NUM) { /* check value if constant */
650 bincnt = (int)(evalue(mp->binv) + 1.5);
651 if (bincnt != 1) {
652 sprintf(errmsg, "illegal non-zero constant for bin (%s)",
653 binv);
654 error(USER, errmsg);
655 }
656 }
657 mp->nbins = 1; /* initialize results holder */
658 setcolor(mp->cbin[0], 0., 0., 0.);
659 if (bincnt > 1)
660 mp = growmodifier(mp, bincnt);
661 lep->data = (char *)mp;
662 /* allocate output streams */
663 for (i = bincnt; i-- > 0; )
664 getostream(mp->outspec, mp->modname, i, 1);
665 return mp;
666 }
667
668 /* add modifiers from a file list */
669 void
670 addmodfile(char *fname, char *outf, char *binv, int bincnt)
671 {
672 char *mname[MAXMODLIST];
673 int i;
674 /* find the file & store strings */
675 if (wordfile(mname, getpath(fname, getrlibpath(), R_OK)) < 0) {
676 sprintf(errmsg, "cannot find modifier file '%s'", fname);
677 error(SYSTEM, errmsg);
678 }
679 for (i = 0; mname[i]; i++) /* add each one */
680 addmodifier(mname[i], outf, binv, bincnt);
681 }
682
683 /* put string to stderr */
684 void
685 eputs(char *s)
686 {
687 static int midline = 0;
688
689 if (!*s) return;
690 if (!midline) {
691 fputs(progname, stderr);
692 fputs(": ", stderr);
693 }
694 fputs(s, stderr);
695 midline = s[strlen(s)-1] != '\n';
696 }
697
698 /* construct output file name and return flags whether modifier/bin present */
699 int
700 ofname(char *oname, const char *ospec, const char *mname, int bn)
701 {
702 const char *mnp = NULL;
703 const char *bnp = NULL;
704 const char *cp;
705
706 if (ospec == NULL)
707 return -1;
708 for (cp = ospec; *cp; cp++) /* check format position(s) */
709 if (*cp == '%') {
710 do
711 ++cp;
712 while (isdigit(*cp));
713 switch (*cp) {
714 case '%':
715 break;
716 case 's':
717 if (mnp != NULL)
718 return -1;
719 mnp = cp;
720 break;
721 case 'd':
722 case 'i':
723 case 'o':
724 case 'x':
725 case 'X':
726 if (bnp != NULL)
727 return -1;
728 bnp = cp;
729 break;
730 default:
731 return -1;
732 }
733 }
734 if (mnp != NULL) { /* create file name */
735 if (bnp != NULL) {
736 if (bnp > mnp)
737 sprintf(oname, ospec, mname, bn);
738 else
739 sprintf(oname, ospec, bn, mname);
740 return OF_MODIFIER|OF_BIN;
741 }
742 sprintf(oname, ospec, mname);
743 return OF_MODIFIER;
744 }
745 if (bnp != NULL) {
746 sprintf(oname, ospec, bn);
747 return OF_BIN;
748 }
749 strcpy(oname, ospec);
750 return 0;
751 }
752
753 /* write header to the given output stream */
754 void
755 printheader(FILE *fout, const char *info)
756 {
757 extern char VersionID[];
758 /* copy octree header */
759 if (octree[0] == '!') {
760 newheader("RADIANCE", fout);
761 fputs(octree+1, fout);
762 if (octree[strlen(octree)-1] != '\n')
763 fputc('\n', fout);
764 } else {
765 FILE *fin = fopen(octree, "r");
766 if (fin == NULL)
767 quit(1);
768 checkheader(fin, "ignore", fout);
769 fclose(fin);
770 }
771 printargs(gargc-1, gargv, fout); /* add our command */
772 fprintf(fout, "SOFTWARE= %s\n", VersionID);
773 fputnow(fout);
774 if (info != NULL) /* add extra info if given */
775 fputs(info, fout);
776 switch (outfmt) { /* add output format */
777 case 'a':
778 fputformat("ascii", fout);
779 break;
780 case 'f':
781 fputformat("float", fout);
782 break;
783 case 'd':
784 fputformat("double", fout);
785 break;
786 case 'c':
787 fputformat(COLRFMT, fout);
788 break;
789 }
790 fputc('\n', fout);
791 }
792
793 /* write resolution string to given output stream */
794 void
795 printresolu(FILE *fout, int xr, int yr)
796 {
797 if ((xr > 0) & (yr > 0)) /* resolution string */
798 fprtresolu(xr, yr, fout);
799 }
800
801 /* Get output stream pointer (open and write header if new and noopen==0) */
802 STREAMOUT *
803 getostream(const char *ospec, const char *mname, int bn, int noopen)
804 {
805 static const DCOLOR nocontrib = BLKCOLOR;
806 static STREAMOUT stdos;
807 int ofl;
808 char oname[1024];
809 LUENT *lep;
810 STREAMOUT *sop;
811
812 if (ospec == NULL) { /* use stdout? */
813 if (!noopen && !using_stdout) {
814 if (outfmt != 'a')
815 SET_FILE_BINARY(stdout);
816 if (header)
817 printheader(stdout, NULL);
818 printresolu(stdout, xres, yres);
819 if (waitflush > 0)
820 fflush(stdout);
821 stdos.xr = xres; stdos.yr = yres;
822 using_stdout = 1;
823 }
824 stdos.ofp = stdout;
825 stdos.reclen += noopen;
826 return &stdos;
827 }
828 ofl = ofname(oname, ospec, mname, bn); /* get output name */
829 if (ofl < 0) {
830 sprintf(errmsg, "bad output format '%s'", ospec);
831 error(USER, errmsg);
832 }
833 lep = lu_find(&ofiletab, oname); /* look it up */
834 if (lep->key == NULL) /* new entry */
835 lep->key = strcpy((char *)malloc(strlen(oname)+1), oname);
836 sop = (STREAMOUT *)lep->data;
837 if (sop == NULL) { /* allocate stream */
838 sop = (STREAMOUT *)malloc(sizeof(STREAMOUT));
839 if (sop == NULL)
840 error(SYSTEM, "out of memory in getostream");
841 sop->outpipe = oname[0] == '!';
842 sop->reclen = 0;
843 sop->ofp = NULL; /* open iff noopen==0 */
844 sop->xr = xres; sop->yr = yres;
845 lep->data = (char *)sop;
846 if (!sop->outpipe & !force_open & !recover &&
847 access(oname, F_OK) == 0) {
848 errno = EEXIST; /* file exists */
849 goto openerr;
850 }
851 }
852 if (!noopen && sop->ofp == NULL) { /* open output stream */
853 long i;
854 if (oname[0] == '!') /* output to command */
855 sop->ofp = popen(oname+1, "w");
856 else /* else open file */
857 sop->ofp = fopen(oname, "w");
858 if (sop->ofp == NULL)
859 goto openerr;
860 if (outfmt != 'a')
861 SET_FILE_BINARY(sop->ofp);
862 #ifdef getc_unlocked /* avoid lock/unlock overhead */
863 flockfile(sop->ofp);
864 #endif
865 if (header) {
866 char info[512];
867 char *cp = info;
868 if (ofl & OF_MODIFIER || sop->reclen == 1) {
869 sprintf(cp, "MODIFIER=%s\n", mname);
870 while (*cp) ++cp;
871 }
872 if (ofl & OF_BIN) {
873 sprintf(cp, "BIN=%d\n", bn);
874 while (*cp) ++cp;
875 }
876 *cp = '\0';
877 printheader(sop->ofp, info);
878 }
879 if (accumulate > 0) { /* global resolution */
880 sop->xr = xres; sop->yr = yres;
881 }
882 printresolu(sop->ofp, sop->xr, sop->yr);
883 /* play catch-up */
884 for (i = accumulate > 0 ? lastdone/accumulate : 0; i--; ) {
885 int j = sop->reclen;
886 if (j <= 0) j = 1;
887 while (j--)
888 put_contrib(nocontrib, sop->ofp);
889 if (outfmt == 'a')
890 putc('\n', sop->ofp);
891 }
892 if (waitflush > 0)
893 fflush(sop->ofp);
894 }
895 sop->reclen += noopen; /* add to length if noopen */
896 return sop; /* return output stream */
897 openerr:
898 sprintf(errmsg, "cannot open '%s' for writing", oname);
899 error(SYSTEM, errmsg);
900 return NULL; /* pro forma return */
901 }
902
903 /* read input ray into buffer */
904 int
905 getinp(char *buf, FILE *fp)
906 {
907 double dv[3], *dvp;
908 float *fvp;
909 char *cp;
910 int i;
911
912 switch (inpfmt) {
913 case 'a':
914 cp = buf; /* make sure we get 6 floats */
915 for (i = 0; i < 6; i++) {
916 if (fgetword(cp, buf+127-cp, fp) == NULL)
917 return -1;
918 if (i >= 3)
919 dv[i-3] = atof(cp);
920 if ((cp = fskip(cp)) == NULL || *cp)
921 return -1;
922 *cp++ = ' ';
923 }
924 getc(fp); /* get/put eol */
925 *cp-- = '\0'; *cp = '\n';
926 if (DOT(dv,dv) <= FTINY*FTINY)
927 return 0; /* dummy ray */
928 return strlen(buf);
929 case 'f':
930 if (fread(buf, sizeof(float), 6, fp) != 6)
931 return -1;
932 fvp = (float *)buf + 3;
933 if (DOT(fvp,fvp) <= FTINY*FTINY)
934 return 0; /* dummy ray */
935 return sizeof(float)*6;
936 case 'd':
937 if (fread(buf, sizeof(double), 6, fp) != 6)
938 return -1;
939 dvp = (double *)buf + 3;
940 if (DOT(dvp,dvp) <= FTINY*FTINY)
941 return 0; /* dummy ray */
942 return sizeof(double)*6;
943 }
944 error(INTERNAL, "botched input format");
945 return -1; /* pro forma return */
946 }
947
948 static float rparams[9]; /* traced ray parameters */
949
950 /* return channel (ray) value */
951 double
952 chanvalue(int n)
953 {
954 if (--n < 0 || n >= 6)
955 error(USER, "illegal channel number ($N)");
956 return rparams[n+3];
957 }
958
959 /* add current ray contribution to the appropriate modifier bin */
960 void
961 add_contrib(const char *modn)
962 {
963 LUENT *le = lu_find(&modconttab, modn);
964 MODCONT *mp = (MODCONT *)le->data;
965 int bn;
966
967 if (mp == NULL) {
968 sprintf(errmsg, "unexpected modifier '%s' from rtrace", modn);
969 error(USER, errmsg);
970 }
971 eclock++; /* get bin number */
972 bn = (int)(evalue(mp->binv) + .5);
973 if (bn <= 0)
974 bn = 0;
975 else if (bn >= mp->nbins) /* new bin */
976 le->data = (char *)(mp = growmodifier(mp, bn+1));
977 addcolor(mp->cbin[bn], rparams);
978 }
979
980 /* output newline to ASCII file and/or flush as requested */
981 static int
982 puteol(const LUENT *e, void *p)
983 {
984 STREAMOUT *sop = (STREAMOUT *)e->data;
985
986 if (outfmt == 'a')
987 putc('\n', sop->ofp);
988 if (!waitflush)
989 fflush(sop->ofp);
990 if (ferror(sop->ofp)) {
991 sprintf(errmsg, "write error on file '%s'", e->key);
992 error(SYSTEM, errmsg);
993 }
994 return 0;
995 }
996
997 /* put out ray contribution to file */
998 void
999 put_contrib(const DCOLOR cnt, FILE *fout)
1000 {
1001 float fv[3];
1002 COLR cv;
1003
1004 switch (outfmt) {
1005 case 'a':
1006 fprintf(fout, "%.6e\t%.6e\t%.6e\t", cnt[0], cnt[1], cnt[2]);
1007 break;
1008 case 'f':
1009 copycolor(fv, cnt);
1010 fwrite(fv, sizeof(float), 3, fout);
1011 break;
1012 case 'd':
1013 fwrite(cnt, sizeof(double), 3, fout);
1014 break;
1015 case 'c':
1016 setcolr(cv, cnt[0], cnt[1], cnt[2]);
1017 fwrite(cv, sizeof(cv), 1, fout);
1018 break;
1019 default:
1020 error(INTERNAL, "botched output format");
1021 }
1022 }
1023
1024 /* output ray tallies and clear for next accumulation */
1025 void
1026 done_contrib(int navg)
1027 {
1028 double sf = 1.;
1029 int i, j;
1030 MODCONT *mp;
1031 STREAMOUT *sop;
1032 /* set average scaling */
1033 if (navg > 1)
1034 sf = 1. / (double)navg;
1035 /* output modifiers in order */
1036 for (i = 0; i < nmods; i++) {
1037 mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
1038 if (navg > 1) /* average scaling */
1039 for (j = mp->nbins; j--; )
1040 scalecolor(mp->cbin[j], sf);
1041 sop = getostream(mp->outspec, mp->modname, 0,0);
1042 put_contrib(mp->cbin[0], sop->ofp);
1043 if (mp->nbins > 3 && /* minor optimization */
1044 sop == getostream(mp->outspec, mp->modname, 1,0))
1045 for (j = 1; j < mp->nbins; j++)
1046 put_contrib(mp->cbin[j], sop->ofp);
1047 else
1048 for (j = 1; j < mp->nbins; j++)
1049 put_contrib(mp->cbin[j],
1050 getostream(mp->outspec,mp->modname,j,0)->ofp);
1051 /* clear for next time */
1052 memset(mp->cbin, 0, sizeof(DCOLOR)*mp->nbins);
1053 }
1054 --waitflush; /* terminate records */
1055 lu_doall(&ofiletab, puteol, NULL);
1056 if (using_stdout & (outfmt == 'a'))
1057 putc('\n', stdout);
1058 if (!waitflush) {
1059 waitflush = (yres > 0) & (xres > 1) ? 0 : xres;
1060 if (using_stdout)
1061 fflush(stdout);
1062 }
1063 }
1064
1065 /* queue completed ray tree produced by rtrace process */
1066 void
1067 queue_raytree(struct rtproc *rtp)
1068 {
1069 struct rtproc *rtu, *rtl = NULL;
1070 /* insert following ray order */
1071 for (rtu = rt_unproc; rtu != NULL; rtu = (rtl=rtu)->next)
1072 if (rtp->raynum < rtu->raynum)
1073 break;
1074 rtu = (struct rtproc *)malloc(sizeof(struct rtproc));
1075 if (rtu == NULL)
1076 error(SYSTEM, "out of memory in queue_raytree");
1077 *rtu = *rtp;
1078 if (rtl == NULL) {
1079 rtu->next = rt_unproc;
1080 rt_unproc = rtu;
1081 } else {
1082 rtu->next = rtl->next;
1083 rtl->next = rtu;
1084 }
1085 rtp->raynum = 0; /* clear path for next ray tree */
1086 rtp->bsiz = 0;
1087 rtp->buf = NULL;
1088 rtp->nbr = 0;
1089 }
1090
1091 /* process completed ray trees from our queue */
1092 void
1093 process_queue(void)
1094 {
1095 char modname[128];
1096 /* ray-ordered queue */
1097 while (rt_unproc != NULL && rt_unproc->raynum == lastdone+1) {
1098 struct rtproc *rtp = rt_unproc;
1099 int n = rtp->nbr;
1100 const char *cp = rtp->buf;
1101 while (n > 0) { /* process rays */
1102 register char *mnp = modname;
1103 /* skip leading tabs */
1104 while (n > 0 && *cp == '\t') {
1105 cp++; n--;
1106 }
1107 if (!n || !(isalpha(*cp) | (*cp == '_')))
1108 error(USER, "bad modifier name from rtrace");
1109 /* get modifier name */
1110 while (n > 1 && *cp != '\t') {
1111 if (mnp - modname >= sizeof(modname)-2)
1112 error(INTERNAL, "modifier name too long");
1113 *mnp++ = *cp++; n--;
1114 }
1115 *mnp = '\0';
1116 cp++; n--; /* eat following tab */
1117 if (n < (int)(sizeof(float)*9))
1118 error(USER, "incomplete ray value from rtrace");
1119 /* add ray contribution */
1120 memcpy(rparams, cp, sizeof(float)*9);
1121 cp += sizeof(float)*9; n -= sizeof(float)*9;
1122 add_contrib(modname);
1123 }
1124 /* time to produce record? */
1125 if (account > 0 && !--account)
1126 done_contrib(account = accumulate);
1127 lastdone = rtp->raynum;
1128 if (rtp->buf != NULL) /* free up buffer space */
1129 free(rtp->buf);
1130 rt_unproc = rtp->next;
1131 free(rtp); /* done with this ray tree */
1132 }
1133 }
1134
1135 /* wait for rtrace process to finish with ray tree */
1136 struct rtproc *
1137 wait_rproc(void)
1138 {
1139 struct rtproc *rtfree = NULL;
1140 fd_set readset, errset;
1141 ssize_t nr;
1142 struct rtproc *rt;
1143 int n;
1144
1145 do {
1146 nr = 0; /* prepare select call */
1147 FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
1148 for (rt = &rt0; rt != NULL; rt = rt->next) {
1149 if (rt->raynum) {
1150 FD_SET(rt->pd.r, &readset);
1151 ++nr;
1152 }
1153 FD_SET(rt->pd.r, &errset);
1154 if (rt->pd.r >= n)
1155 n = rt->pd.r + 1;
1156 }
1157 if (!nr) /* no rays pending */
1158 break;
1159 if (nr > 1) { /* call select for multiple proc's */
1160 errno = 0;
1161 if (select(n, &readset, NULL, &errset, NULL) < 0)
1162 error(SYSTEM, "select call error in wait_rproc()");
1163 } else
1164 FD_ZERO(&errset);
1165 nr = 0;
1166 for (rt = &rt0; rt != NULL; rt = rt->next) {
1167 if (!FD_ISSET(rt->pd.r, &readset) &&
1168 !FD_ISSET(rt->pd.r, &errset))
1169 continue;
1170 if (rt->buf == NULL) {
1171 rt->bsiz = treebufsiz;
1172 rt->buf = (char *)malloc(rt->bsiz);
1173 } else if (rt->nbr + BUFSIZ > rt->bsiz) {
1174 if (rt->bsiz + BUFSIZ <= treebufsiz)
1175 rt->bsiz = treebufsiz;
1176 else if ((treebufsiz = rt->bsiz += BUFSIZ) < 0)
1177 error(INTERNAL,
1178 "ray buffer does not fit memory");
1179 rt->buf = (char *)realloc(rt->buf, rt->bsiz);
1180 }
1181 if (rt->buf == NULL)
1182 error(SYSTEM, "out of memory in wait_rproc");
1183 nr = rt->bsiz - rt->nbr;
1184 if (nr & ~0x7fffffff) /* avoid 32-bit OS issues */
1185 nr = 0x7fffffff;
1186 nr = read(rt->pd.r, rt->buf+rt->nbr, nr);
1187 if (nr < 0)
1188 error(SYSTEM, "read error from rtrace");
1189 if (!nr)
1190 error(USER, "rtrace process died");
1191 rt->nbr += nr; /* advance & check */
1192 if (rt->nbr >= 6 && !memcmp(rt->buf+rt->nbr-6,
1193 "~\t~\t~\t", 6)) {
1194 rt->nbr -= 6; /* elide terminator */
1195 queue_raytree(rt);
1196 rtfree = rt; /* ready for next ray */
1197 }
1198 }
1199 } while ((rtfree == NULL) & (nr > 0)); /* repeat until ready or out */
1200 return rtfree;
1201 }
1202
1203 /* return next available rtrace process */
1204 struct rtproc *
1205 get_rproc(void)
1206 {
1207 struct rtproc *rtp;
1208 /* check for idle rtrace */
1209 for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
1210 if (!rtp->raynum)
1211 return rtp;
1212 return wait_rproc(); /* need to wait for one */
1213 }
1214
1215 /* trace ray contributions (main loop) */
1216 void
1217 trace_contribs(FILE *fin)
1218 {
1219 static int ignore_warning_given = 0;
1220 char inpbuf[128];
1221 int iblen;
1222 struct rtproc *rtp;
1223 /* loop over input */
1224 while ((iblen = getinp(inpbuf, fin)) >= 0) {
1225 if (!iblen && accumulate != 1) {
1226 if (!ignore_warning_given++)
1227 error(WARNING,
1228 "dummy ray(s) ignored during accumulation\n");
1229 continue;
1230 }
1231 if (!iblen || /* need flush/reset? */
1232 queue_length() > 10*nrtprocs() ||
1233 lastray+1 < lastray) {
1234 while (wait_rproc() != NULL)
1235 process_queue();
1236 lastdone = lastray = 0;
1237 }
1238 rtp = get_rproc(); /* get avail. rtrace process */
1239 rtp->raynum = ++lastray; /* assign ray */
1240 if (iblen) { /* trace ray if valid */
1241 writebuf(rtp->pd.w, inpbuf, iblen);
1242 } else { /* else bypass dummy ray */
1243 queue_raytree(rtp); /* queue empty ray/record */
1244 if ((yres <= 0) | (xres <= 0))
1245 waitflush = 1; /* flush right after */
1246 }
1247 process_queue(); /* catch up with results */
1248 if (raysleft && !--raysleft)
1249 break; /* preemptive EOI */
1250 }
1251 while (wait_rproc() != NULL) /* process outstanding rays */
1252 process_queue();
1253 if (accumulate <= 0)
1254 done_contrib(0); /* output tallies */
1255 else if (account < accumulate) {
1256 error(WARNING, "partial accumulation in final record");
1257 done_contrib(accumulate - account);
1258 }
1259 if (raysleft)
1260 error(USER, "unexpected EOF on input");
1261 lu_done(&ofiletab); /* close output files */
1262 }
1263
1264 /* get ray contribution from previous file */
1265 static int
1266 get_contrib(DCOLOR cnt, FILE *finp)
1267 {
1268 COLOR fv;
1269 COLR cv;
1270
1271 switch (outfmt) {
1272 case 'a':
1273 return(fscanf(finp,"%lf %lf %lf",&cnt[0],&cnt[1],&cnt[2]) == 3);
1274 case 'f':
1275 if (fread(fv, sizeof(fv[0]), 3, finp) != 3)
1276 return(0);
1277 copycolor(cnt, fv);
1278 return(1);
1279 case 'd':
1280 return(fread(cnt, sizeof(cnt[0]), 3, finp) == 3);
1281 case 'c':
1282 if (fread(cv, sizeof(cv), 1, finp) != 1)
1283 return(0);
1284 colr_color(fv, cv);
1285 copycolor(cnt, fv);
1286 return(1);
1287 default:
1288 error(INTERNAL, "botched output format");
1289 }
1290 return(0); /* pro forma return */
1291 }
1292
1293 /* close output file opened for input */
1294 static int
1295 myclose(const LUENT *e, void *p)
1296 {
1297 STREAMOUT *sop = (STREAMOUT *)e->data;
1298
1299 if (sop->ofp == NULL)
1300 return(0);
1301 fclose(sop->ofp);
1302 sop->ofp = NULL;
1303 return(0);
1304 }
1305
1306 /* load previously accumulated values */
1307 void
1308 reload_output(void)
1309 {
1310 int i, j;
1311 MODCONT *mp;
1312 int ofl;
1313 char oname[1024];
1314 char *fmode = "rb";
1315 char *outvfmt;
1316 LUENT *ment, *oent;
1317 int xr, yr;
1318 STREAMOUT sout;
1319 DCOLOR rgbv;
1320
1321 switch (outfmt) {
1322 case 'a':
1323 outvfmt = "ascii";
1324 fmode = "r";
1325 break;
1326 case 'f':
1327 outvfmt = "float";
1328 break;
1329 case 'd':
1330 outvfmt = "double";
1331 break;
1332 case 'c':
1333 outvfmt = COLRFMT;
1334 break;
1335 default:
1336 error(INTERNAL, "botched output format");
1337 return;
1338 }
1339 /* reload modifier values */
1340 for (i = 0; i < nmods; i++) {
1341 ment = lu_find(&modconttab,modname[i]);
1342 mp = (MODCONT *)ment->data;
1343 if (mp->outspec == NULL)
1344 error(USER, "cannot reload from stdout");
1345 if (mp->outspec[0] == '!')
1346 error(USER, "cannot reload from command");
1347 for (j = 0; ; j++) { /* load each modifier bin */
1348 ofl = ofname(oname, mp->outspec, mp->modname, j);
1349 if (ofl < 0)
1350 error(USER, "bad output file specification");
1351 oent = lu_find(&ofiletab, oname);
1352 if (oent->data != NULL) {
1353 sout = *(STREAMOUT *)oent->data;
1354 } else {
1355 sout.reclen = 0;
1356 sout.outpipe = 0;
1357 sout.xr = xres; sout.yr = yres;
1358 sout.ofp = NULL;
1359 }
1360 if (sout.ofp == NULL) { /* open output as input */
1361 sout.ofp = fopen(oname, fmode);
1362 if (sout.ofp == NULL) {
1363 if (j)
1364 break; /* assume end of modifier */
1365 sprintf(errmsg, "missing reload file '%s'",
1366 oname);
1367 error(WARNING, errmsg);
1368 break;
1369 }
1370 #ifdef getc_unlocked /* avoid lock/unlock overhead */
1371 flockfile(sout.ofp);
1372 #endif
1373 if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
1374 sprintf(errmsg, "format mismatch for '%s'",
1375 oname);
1376 error(USER, errmsg);
1377 }
1378 if ((sout.xr > 0) & (sout.yr > 0) &&
1379 (!fscnresolu(&xr, &yr, sout.ofp) ||
1380 (xr != sout.xr) |
1381 (yr != sout.yr))) {
1382 sprintf(errmsg, "resolution mismatch for '%s'",
1383 oname);
1384 error(USER, errmsg);
1385 }
1386 }
1387 /* read in RGB value */
1388 if (!get_contrib(rgbv, sout.ofp)) {
1389 if (!j) {
1390 fclose(sout.ofp);
1391 break; /* ignore empty file */
1392 }
1393 if (j < mp->nbins) {
1394 sprintf(errmsg, "missing data in '%s'",
1395 oname);
1396 error(USER, errmsg);
1397 }
1398 break;
1399 }
1400 if (j >= mp->nbins) /* grow modifier size */
1401 ment->data = (char *)(mp = growmodifier(mp, j+1));
1402 copycolor(mp->cbin[j], rgbv);
1403 if (oent->key == NULL) /* new file entry */
1404 oent->key = strcpy((char *)
1405 malloc(strlen(oname)+1), oname);
1406 if (oent->data == NULL)
1407 oent->data = (char *)malloc(sizeof(STREAMOUT));
1408 *(STREAMOUT *)oent->data = sout;
1409 }
1410 }
1411 lu_doall(&ofiletab, myclose, NULL); /* close all files */
1412 }
1413
1414 /* seek on the given output file */
1415 static int
1416 myseeko(const LUENT *e, void *p)
1417 {
1418 STREAMOUT *sop = (STREAMOUT *)e->data;
1419 off_t nbytes = *(off_t *)p;
1420
1421 if (sop->reclen > 1)
1422 nbytes = nbytes * sop->reclen;
1423 if (fseeko(sop->ofp, nbytes, SEEK_CUR) < 0) {
1424 sprintf(errmsg, "seek error on file '%s'", e->key);
1425 error(SYSTEM, errmsg);
1426 }
1427 return 0;
1428 }
1429
1430 /* recover output if possible */
1431 void
1432 recover_output(FILE *fin)
1433 {
1434 off_t lastout = -1;
1435 int outvsiz, recsiz;
1436 char *outvfmt;
1437 int i, j;
1438 MODCONT *mp;
1439 int ofl;
1440 char oname[1024];
1441 LUENT *ment, *oent;
1442 STREAMOUT sout;
1443 off_t nvals;
1444 int xr, yr;
1445
1446 switch (outfmt) {
1447 case 'a':
1448 error(USER, "cannot recover ASCII output");
1449 return;
1450 case 'f':
1451 outvsiz = sizeof(float)*3;
1452 outvfmt = "float";
1453 break;
1454 case 'd':
1455 outvsiz = sizeof(double)*3;
1456 outvfmt = "double";
1457 break;
1458 case 'c':
1459 outvsiz = sizeof(COLR);
1460 outvfmt = COLRFMT;
1461 break;
1462 default:
1463 error(INTERNAL, "botched output format");
1464 return;
1465 }
1466 /* check modifier outputs */
1467 for (i = 0; i < nmods; i++) {
1468 ment = lu_find(&modconttab,modname[i]);
1469 mp = (MODCONT *)ment->data;
1470 if (mp->outspec == NULL)
1471 error(USER, "cannot recover from stdout");
1472 if (mp->outspec[0] == '!')
1473 error(USER, "cannot recover from command");
1474 for (j = 0; ; j++) { /* check each bin's file */
1475 ofl = ofname(oname, mp->outspec, mp->modname, j);
1476 if (ofl < 0)
1477 error(USER, "bad output file specification");
1478 oent = lu_find(&ofiletab, oname);
1479 if (oent->data != NULL) {
1480 sout = *(STREAMOUT *)oent->data;
1481 } else {
1482 sout.reclen = 0;
1483 sout.outpipe = 0;
1484 sout.ofp = NULL;
1485 }
1486 if (sout.ofp != NULL) { /* already open? */
1487 if (ofl & OF_BIN)
1488 continue;
1489 break;
1490 }
1491 /* open output */
1492 sout.ofp = fopen(oname, "rb+");
1493 if (sout.ofp == NULL) {
1494 if (j)
1495 break; /* assume end of modifier */
1496 sprintf(errmsg, "missing recover file '%s'",
1497 oname);
1498 error(WARNING, errmsg);
1499 break;
1500 }
1501 nvals = lseek(fileno(sout.ofp), 0, SEEK_END);
1502 if (nvals <= 0) {
1503 lastout = 0; /* empty output, quit here */
1504 fclose(sout.ofp);
1505 break;
1506 }
1507 if (!sout.reclen) {
1508 if (!(ofl & OF_BIN)) {
1509 sprintf(errmsg,
1510 "need -bn to recover file '%s'",
1511 oname);
1512 error(USER, errmsg);
1513 }
1514 recsiz = outvsiz;
1515 } else
1516 recsiz = outvsiz * sout.reclen;
1517
1518 lseek(fileno(sout.ofp), 0, SEEK_SET);
1519 if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
1520 sprintf(errmsg, "format mismatch for '%s'",
1521 oname);
1522 error(USER, errmsg);
1523 }
1524 sout.xr = xres; sout.yr = yres;
1525 if ((sout.xr > 0) & (sout.yr > 0) &&
1526 (!fscnresolu(&xr, &yr, sout.ofp) ||
1527 (xr != sout.xr) |
1528 (yr != sout.yr))) {
1529 sprintf(errmsg, "resolution mismatch for '%s'",
1530 oname);
1531 error(USER, errmsg);
1532 }
1533 nvals = (nvals - (off_t)ftell(sout.ofp)) / recsiz;
1534 if ((lastout < 0) | (nvals < lastout))
1535 lastout = nvals;
1536 if (oent->key == NULL) /* new entry */
1537 oent->key = strcpy((char *)
1538 malloc(strlen(oname)+1), oname);
1539 if (oent->data == NULL)
1540 oent->data = (char *)malloc(sizeof(STREAMOUT));
1541 *(STREAMOUT *)oent->data = sout;
1542 if (!(ofl & OF_BIN))
1543 break; /* no bin separation */
1544 }
1545 if (!lastout) { /* empty output */
1546 error(WARNING, "no previous data to recover");
1547 lu_done(&ofiletab); /* reclose all outputs */
1548 return;
1549 }
1550 if (j > mp->nbins) /* reallocate modifier bins */
1551 ment->data = (char *)(mp = growmodifier(mp, j));
1552 }
1553 if (lastout < 0) {
1554 error(WARNING, "no output files to recover");
1555 return;
1556 }
1557 if (raysleft && lastout >= raysleft/accumulate) {
1558 error(WARNING, "output appears to be complete");
1559 /* XXX should read & discard input? */
1560 quit(0);
1561 }
1562 /* seek on all files */
1563 nvals = lastout * outvsiz;
1564 lu_doall(&ofiletab, myseeko, &nvals);
1565 /* skip repeated input */
1566 for (nvals = 0; nvals < lastout; nvals++)
1567 if (getinp(oname, fin) < 0)
1568 error(USER, "unexpected EOF on input");
1569 lastray = lastdone = (unsigned long)lastout * accumulate;
1570 if (raysleft)
1571 raysleft -= lastray;
1572 }