ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.69
Committed: Tue Apr 8 23:46:05 2014 UTC (10 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.68: +1 -1 lines
State: FILE REMOVED
Log Message:
Formally retired rtcontrib

File Contents

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