ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.65
Committed: Wed Oct 5 17:20:55 2011 UTC (12 years, 6 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R1
Changes since 1.64: +5 -1 lines
Log Message:
Windows compatibility changes from Bill Hoffman

File Contents

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