ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rtcontrib.c
Revision: 1.41
Committed: Wed Mar 29 17:31:59 2006 UTC (18 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 1.40: +2 -2 lines
Log Message:
Minor correction to avoid segfault with illegal option

File Contents

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