Back to index

radiance  4R0+20100331
rtcontrib.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char RCSid[] = "$Id: rtcontrib.c,v 1.55 2009/12/09 21:43:35 greg Exp $";
00003 #endif
00004 /*
00005  * Gather rtrace output to compute contributions from particular sources
00006  */
00007 
00008 #include  "standard.h"
00009 #include  <ctype.h>
00010 #include  <signal.h>
00011 #include  "platform.h"
00012 #include  "rtprocess.h"
00013 #include  "selcall.h"
00014 #include  "color.h"
00015 #include  "resolu.h"
00016 #include  "lookup.h"
00017 #include  "calcomp.h"
00018 
00019 #ifndef       MAXMODLIST
00020 #define       MAXMODLIST    1024          /* maximum modifiers we'll track */
00021 #endif
00022 
00023 int    treebufsiz = BUFSIZ;        /* current tree buffer size */
00024 
00025 typedef double       DCOLOR[3];           /* double-precision color */
00026 
00027 /*
00028  * The MODCONT structure is used to accumulate ray contributions
00029  * for a particular modifier, which may be subdivided into bins
00030  * if binv evaluates > 0.  If outspec contains a %s in it, this will
00031  * be replaced with the modifier name.  If outspec contains a %d in it,
00032  * this will be used to create one output file per bin, otherwise all bins
00033  * will be written to the same file, in order.  If the global outfmt
00034  * is 'c', then a 4-byte RGBE pixel will be output for each bin value
00035  * and the file will conform to a RADIANCE image if xres & yres are set.
00036  */
00037 typedef struct {
00038        const char    *outspec;     /* output file specification */
00039        const char    *modname;     /* modifier name */
00040        EPNODE        *binv;        /* bin value expression */
00041        int           nbins;        /* number of accumulation bins */
00042        DCOLOR        cbin[1];      /* contribution bins (extends struct) */
00043 } MODCONT;                  /* modifier contribution */
00044 
00045 static void mcfree(void *p) { epfree((*(MODCONT *)p).binv); free(p); }
00046 
00047 LUTAB  modconttab = LU_SINIT(NULL,mcfree);       /* modifier lookup table */
00048 
00049 /*
00050  * The STREAMOUT structure holds an open FILE pointer and a count of
00051  * the number of RGB triplets per record, or 0 if unknown.
00052  */
00053 typedef struct {
00054        FILE          *ofp;         /* output file pointer */
00055        int           outpipe;      /* output is to a pipe */
00056        int           reclen;              /* triplets/record */
00057        int           xr, yr;              /* output resolution for picture */
00058 } STREAMOUT;
00059 
00060 /* close output stream and free record */
00061 static void
00062 closestream(void *p)
00063 {
00064        STREAMOUT     *sop = (STREAMOUT *)p;
00065        int           status;
00066        if (sop->outpipe)
00067               status = pclose(sop->ofp);
00068        else
00069               status = fclose(sop->ofp);
00070        if (status)
00071               error(SYSTEM, "error closing output stream");
00072        free(p);
00073 }
00074 
00075 LUTAB  ofiletab = LU_SINIT(free,closestream);    /* output file table */
00076 
00077 #define OF_MODIFIER  01
00078 #define OF_BIN              02
00079 
00080 STREAMOUT *getostream(const char *ospec, const char *mname, int bn, int noopen);
00081 int ofname(char *oname, const char *ospec, const char *mname, int bn);
00082 void printheader(FILE *fout, const char *info);
00083 void printresolu(FILE *fout, int xr, int yr);
00084 
00085 /*
00086  * The rcont structure is used to manage i/o with a particular
00087  * rtrace child process.  Input is passed unchanged from stdin,
00088  * and output is processed in input order and accumulated according
00089  * to the corresponding modifier and bin number.
00090  */
00091 struct rtproc {
00092        struct rtproc *next;        /* next in list of processes */
00093        SUBPROC              pd;           /* rtrace pipe descriptors */
00094        unsigned long raynum;              /* ray number for this tree */
00095        int           bsiz;         /* ray tree buffer length */
00096        char          *buf;         /* ray tree buffer */
00097        int           nbr;          /* number of bytes from rtrace */
00098 };                          /* rtrace process buffer */
00099 
00100                                    /* rtrace command and defaults */
00101 char          *rtargv[256+2*MAXMODLIST] = { "rtrace",
00102                             "-dj", ".9", "-dr", "3",
00103                             "-ab", "1", "-ad", "350", };
00104 
00105 int  rtargc = 9;
00106                                    /* overriding rtrace options */
00107 char          *myrtopts[] = { "-h-", "-x", "1", "-y", "0",
00108                             "-dt", "0", "-as", "0", "-aa", "0", NULL };
00109 
00110 #define       RTCOEFF              "-o~~TmWdp"   /* compute coefficients only */
00111 #define RTCONTRIB    "-o~~TmVdp"   /* compute ray contributions */
00112 
00113 struct rtproc rt0;                 /* head of rtrace process list */
00114 
00115 struct rtproc *rt_unproc = NULL;   /* unprocessed ray trees */
00116 
00117 #define PERSIST_NONE 0             /* no persist file */
00118 #define PERSIST_SINGLE      1             /* user set -P persist */
00119 #define PERSIST_PARALL      2             /* user set -PP persist */
00120 #define PERSIST_OURS 3             /* -PP persist belongs to us */
00121 int    persist_state = PERSIST_NONE;      /* persist file state */
00122 char   persistfn[] = "pfXXXXXX";   /* our persist file name, if set */
00123 
00124 int           gargc;               /* global argc */
00125 char          **gargv;             /* global argv */
00126 #define  progname    gargv[0]
00127 
00128 char          *octree;             /* global octree argument */
00129 
00130 int           inpfmt = 'a';        /* input format */
00131 int           outfmt = 'a';        /* output format */
00132 
00133 int           header = 1;          /* output header? */
00134 int           force_open = 0;             /* truncate existing output? */
00135 int           recover = 0;         /* recover previous output? */
00136 int           accumulate = 1;             /* input rays per output record */
00137 int           xres = 0;            /* horiz. output resolution */
00138 int           yres = 0;            /* vert. output resolution */
00139 
00140 int           account;             /* current accumulation count */
00141 unsigned long raysleft;            /* number of rays left to trace */
00142 long          waitflush;           /* how long until next flush */
00143 
00144 unsigned long lastray = 0;         /* last ray number sent */
00145 unsigned long lastdone = 0;        /* last ray processed */
00146 
00147 int           using_stdout = 0;    /* are we using stdout? */
00148 
00149 const char    *modname[MAXMODLIST];       /* ordered modifier name list */
00150 int           nmods = 0;           /* number of modifiers */
00151 
00152 #define queue_length()             (lastray - lastdone)
00153 
00154 MODCONT *growmodifier(MODCONT *mp, int nb);
00155 MODCONT *addmodifier(char *modn, char *outf, char *binv, int bincnt);
00156 void addmodfile(char *fname, char *outf, char *binv, int bincnt);
00157 
00158 void init(int np);
00159 int done_rprocs(struct rtproc *rtp);
00160 void reload_output(void);
00161 void recover_output(FILE *fin);
00162 void trace_contribs(FILE *fin);
00163 struct rtproc *wait_rproc(void);
00164 struct rtproc *get_rproc(void);
00165 void queue_raytree(struct rtproc *rtp);
00166 void process_queue(void);
00167 
00168 void put_contrib(const DCOLOR cnt, FILE *fout);
00169 void add_contrib(const char *modn);
00170 void done_contrib(int navg);
00171 
00172 /* return number of open rtrace processes */
00173 static int
00174 nrtprocs(void)
00175 {
00176        int    nrtp = 0;
00177        struct rtproc *rtp;
00178 
00179        for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
00180               nrtp += rtp->pd.running;
00181        return(nrtp);
00182 }
00183 
00184 /* set input/output format */
00185 static void
00186 setformat(const char *fmt)
00187 {
00188        switch (fmt[0]) {
00189        case 'f':
00190        case 'd':
00191               SET_FILE_BINARY(stdin);
00192               /* fall through */
00193        case 'a':
00194               inpfmt = fmt[0];
00195               break;
00196        default:
00197               goto fmterr;
00198        }
00199        switch (fmt[1]) {
00200        case '\0':
00201               outfmt = inpfmt;
00202               return;
00203        case 'a':
00204        case 'f':
00205        case 'd':
00206        case 'c':
00207               outfmt = fmt[1];
00208               break;
00209        default:
00210               goto fmterr;
00211        }
00212        if (!fmt[2])
00213               return;
00214 fmterr:
00215        sprintf(errmsg, "Illegal i/o format: -f%s", fmt);
00216        error(USER, errmsg);
00217 }
00218 
00219 /* gather rays from rtrace and output contributions */
00220 int
00221 main(int argc, char *argv[])
00222 {
00223        int    contrib = 0;
00224        int    nprocs = 1;
00225        char   *curout = NULL;
00226        char   *binval = NULL;
00227        int    bincnt = 0;
00228        char   fmt[8];
00229        int    i, j;
00230                             /* need at least one argument */
00231        if (argc < 2) {
00232               fprintf(stderr,
00233 "Usage: %s [-n nprocs][-V][-r][-e expr][-f source][-o ospec][-b binv] {-m mod | -M file} [rtrace options] octree\n",
00234                      argv[0]);
00235               exit(1);
00236        }
00237                             /* global program name */
00238        gargv = argv;
00239                             /* initialize calcomp routines */
00240        esupport |= E_VARIABLE|E_FUNCTION|E_INCHAN|E_RCONST|E_REDEFW;
00241        esupport &= ~(E_OUTCHAN);
00242        varset("PI", ':', PI);
00243                             /* get our options */
00244        for (i = 1; i < argc-1; i++) {
00245                                           /* expand arguments */
00246               while ((j = expandarg(&argc, &argv, i)) > 0)
00247                      ;
00248               if (j < 0) {
00249                      fprintf(stderr, "%s: cannot expand '%s'\n",
00250                                    argv[0], argv[i]);
00251                      exit(1);
00252               }
00253               if (argv[i][0] == '-')
00254                      switch (argv[i][1]) {
00255                      case 'n':            /* number of processes */
00256                             if (argv[i][2] || i >= argc-2) break;
00257                             nprocs = atoi(argv[++i]);
00258                             if (nprocs <= 0)
00259                                    error(USER, "illegal number of processes");
00260                             continue;
00261                      case 'V':            /* output contributions */
00262                             switch (argv[i][2]) {
00263                             case '\0':
00264                                    contrib = !contrib;
00265                                    continue;
00266                             case '+': case '1':
00267                             case 'T': case 't':
00268                             case 'Y': case 'y':
00269                                    contrib = 1;
00270                                    continue;
00271                             case '-': case '0':
00272                             case 'F': case 'f':
00273                             case 'N': case 'n':
00274                                    contrib = 0;
00275                                    continue;
00276                             }
00277                             break;
00278                      case 'c':            /* input rays per output */
00279                             if (argv[i][2] || i >= argc-2) break;
00280                             accumulate = atoi(argv[++i]);
00281                             continue;
00282                      case 'r':            /* recover output */
00283                             if (argv[i][2]) break;
00284                             recover = 1;
00285                             continue;
00286                      case 'h':            /* output header? */
00287                             switch (argv[i][2]) {
00288                             case '\0':
00289                                    header = !header;
00290                                    continue;
00291                             case '+': case '1':
00292                             case 'T': case 't':
00293                             case 'Y': case 'y':
00294                                    header = 1;
00295                                    continue;
00296                             case '-': case '0':
00297                             case 'F': case 'f':
00298                             case 'N': case 'n':
00299                                    header = 0;
00300                                    continue;
00301                             }
00302                             break;
00303                      case 'f':            /* file or force or format */
00304                             if (!argv[i][2]) {
00305                                    char   *fpath;
00306                                    if (i >= argc-2) break;
00307                                    fpath = getpath(argv[++i],
00308                                                  getrlibpath(), R_OK);
00309                                    if (fpath == NULL) {
00310                                           sprintf(errmsg,
00311                                                  "cannot find file '%s'",
00312                                                         argv[i]);
00313                                           error(USER, errmsg);
00314                                    }
00315                                    fcompile(fpath);
00316                                    continue;
00317                             }
00318                             if (argv[i][2] == 'o') {
00319                                    force_open = 1;
00320                                    continue;
00321                             }
00322                             setformat(argv[i]+2);
00323                             continue;
00324                      case 'e':            /* expression */
00325                             if (argv[i][2] || i >= argc-2) break;
00326                             scompile(argv[++i], NULL, 0);
00327                             continue;
00328                      case 'o':            /* output file spec. */
00329                             if (argv[i][2] || i >= argc-2) break;
00330                             curout = argv[++i];
00331                             continue;
00332                      case 'x':            /* horiz. output resolution */
00333                             if (argv[i][2] || i >= argc-2) break;
00334                             xres = atoi(argv[++i]);
00335                             continue;
00336                      case 'y':            /* vert. output resolution */
00337                             if (argv[i][2] || i >= argc-2) break;
00338                             yres = atoi(argv[++i]);
00339                             continue;
00340                      case 'b':            /* bin expression/count */
00341                             if (i >= argc-2) break;
00342                             if (argv[i][2] == 'n') {
00343                                    bincnt = (int)(eval(argv[++i]) + .5);
00344                                    continue;
00345                             }
00346                             if (argv[i][2]) break;
00347                             binval = argv[++i];
00348                             continue;
00349                      case 'm':            /* modifier name */
00350                             if (argv[i][2] || i >= argc-2) break;
00351                             rtargv[rtargc++] = "-ti";
00352                             rtargv[rtargc++] = argv[++i];
00353                             addmodifier(argv[i], curout, binval, bincnt);
00354                             continue;
00355                      case 'M':            /* modifier file */
00356                             if (argv[i][2] || i >= argc-2) break;
00357                             rtargv[rtargc++] = "-tI";
00358                             rtargv[rtargc++] = argv[++i];
00359                             addmodfile(argv[i], curout, binval, bincnt);
00360                             continue;
00361                      case 'P':            /* persist file */
00362                             if (i >= argc-2) break;
00363                             persist_state = (argv[i][2] == 'P') ?
00364                                           PERSIST_PARALL : PERSIST_SINGLE;
00365                             rtargv[rtargc++] = argv[i];
00366                             rtargv[rtargc++] = argv[++i];
00367                             continue;
00368                      }
00369               rtargv[rtargc++] = argv[i]; /* assume rtrace option */
00370        }
00371        if (accumulate <= 0) /* no output flushing for single record */
00372               xres = yres = 0;
00373                             /* set global argument list */
00374        gargc = argc; gargv = argv;
00375                             /* add "mandatory" rtrace settings */
00376        for (j = 0; myrtopts[j] != NULL; j++)
00377               rtargv[rtargc++] = myrtopts[j];
00378        rtargv[rtargc++] = contrib ? RTCONTRIB : RTCOEFF;
00379                             /* just asking for defaults? */
00380        if (!strcmp(argv[i], "-defaults")) {
00381               char   sxres[16], syres[16];
00382               char   *rtpath;
00383               printf("-n %-2d\t\t\t\t# number of processes\n", nprocs);
00384               printf("-c %-5d\t\t\t# accumulated rays per record\n",
00385                             accumulate);
00386               printf("-V%c\t\t\t\t# output %s\n", contrib ? '+' : '-',
00387                             contrib ? "contributions" : "coefficients");
00388               fflush(stdout);                    /* report OUR options */
00389               rtargv[rtargc++] = header ? "-h+" : "-h-";
00390               sprintf(fmt, "-f%c%c", inpfmt, outfmt);
00391               rtargv[rtargc++] = fmt;
00392               rtargv[rtargc++] = "-x";
00393               sprintf(sxres, "%d", xres);
00394               rtargv[rtargc++] = sxres;
00395               rtargv[rtargc++] = "-y";
00396               sprintf(syres, "%d", yres);
00397               rtargv[rtargc++] = syres;
00398               rtargv[rtargc++] = "-defaults";
00399               rtargv[rtargc] = NULL;
00400               rtpath = getpath(rtargv[0], getenv("PATH"), X_OK);
00401               if (rtpath == NULL) {
00402                      eputs(rtargv[0]);
00403                      eputs(": command not found\n");
00404                      exit(1);
00405               }
00406               execv(rtpath, rtargv);
00407               perror(rtpath);      /* execv() should not return */
00408               exit(1);
00409        }
00410        if (nprocs > 1) {    /* add persist file if parallel */
00411               if (persist_state == PERSIST_SINGLE)
00412                      error(USER, "use -PP option for multiple processes");
00413               if (persist_state == PERSIST_NONE) {
00414                      rtargv[rtargc++] = "-PP";
00415                      rtargv[rtargc++] = mktemp(persistfn);
00416                      persist_state = PERSIST_OURS;
00417               }
00418        } 
00419                             /* add format string */
00420        sprintf(fmt, "-f%cf", inpfmt);
00421        rtargv[rtargc++] = fmt;
00422                             /* octree argument is last */
00423        if (i <= 0 || i != argc-1 || argv[i][0] == '-')
00424               error(USER, "missing octree argument");
00425        rtargv[rtargc++] = octree = argv[i];
00426        rtargv[rtargc] = NULL;
00427                             /* start rtrace & recover if requested */
00428        init(nprocs);
00429                             /* compute contributions */
00430        trace_contribs(stdin);
00431                             /* clean up */
00432        quit(0);
00433 }
00434 
00435 #ifndef SIGALRM
00436 #define SIGALRM SIGTERM
00437 #endif
00438 /* kill persistent rtrace process */
00439 static void
00440 killpersist(void)
00441 {
00442        FILE   *fp = fopen(persistfn, "r");
00443        RT_PID pid;
00444 
00445        if (fp == NULL)
00446               return;
00447        if (fscanf(fp, "%*s %d", &pid) != 1 || kill(pid, SIGALRM) < 0)
00448               unlink(persistfn);
00449        fclose(fp);
00450 }
00451 
00452 /* close rtrace processes and clean up */
00453 int
00454 done_rprocs(struct rtproc *rtp)
00455 {
00456        int    st0, st1 = 0;
00457 
00458        if (rtp->next != NULL) {    /* close last opened first! */
00459               st1 = done_rprocs(rtp->next);
00460               free((void *)rtp->next);
00461               rtp->next = NULL;
00462        }
00463        st0 = close_process(&rtp->pd);
00464        if (st0 < 0)
00465               error(WARNING, "unknown return status from rtrace process");
00466        else if (st0 > 0)
00467               return(st0);
00468        return(st1);
00469 }
00470 
00471 /* exit with status */
00472 void
00473 quit(int status)
00474 {
00475        int    rtstat;
00476 
00477        if (persist_state == PERSIST_OURS)  /* terminate waiting rtrace */
00478               killpersist();
00479                                    /* clean up rtrace process(es) */
00480        rtstat = done_rprocs(&rt0);
00481        if (status == 0)
00482               status = rtstat;
00483        exit(status);               /* flushes all output streams */
00484 }
00485 
00486 /* start rtrace processes and initialize */
00487 void
00488 init(int np)
00489 {
00490        struct rtproc *rtp;
00491        int    i;
00492        int    maxbytes;
00493                                    /* make sure we have something to do */
00494        if (!nmods)
00495               error(USER, "No modifiers specified");
00496                                    /* assign ray variables */
00497        scompile("Dx=$1;Dy=$2;Dz=$3;", NULL, 0);
00498        scompile("Px=$4;Py=$5;Pz=$6;", NULL, 0);
00499                                    /* set up signal handling */
00500        signal(SIGINT, quit);
00501 #ifdef SIGHUP
00502        signal(SIGHUP, quit);
00503 #endif
00504 #ifdef SIGTERM
00505        signal(SIGTERM, quit);
00506 #endif
00507 #ifdef SIGPIPE
00508        signal(SIGPIPE, quit);
00509 #endif
00510        rtp = &rt0;                 /* start rtrace process(es) */
00511        for (i = 0; i++ < np; ) {
00512               errno = 0;
00513               maxbytes = open_process(&rtp->pd, rtargv);
00514               if (maxbytes == 0) {
00515                      eputs(rtargv[0]);
00516                      eputs(": command not found\n");
00517                      exit(1);
00518               }
00519               if (maxbytes < 0)
00520                      error(SYSTEM, "cannot start rtrace process");
00521               if (maxbytes > treebufsiz)
00522                      treebufsiz = maxbytes;
00523               rtp->raynum = 0;
00524               rtp->bsiz = 0;
00525               rtp->buf = NULL;
00526               rtp->nbr = 0;
00527               if (i == np)         /* last process? */
00528                      break;
00529               if (i == 1)
00530                      sleep(2);     /* wait for persist file */
00531               rtp->next = (struct rtproc *)malloc(sizeof(struct rtproc));
00532               if (rtp->next == NULL)
00533                      error(SYSTEM, "out of memory in init");
00534               rtp = rtp->next;
00535        }
00536        rtp->next = NULL;           /* terminate list */
00537        if (yres > 0) {
00538               if (xres > 0)
00539                      raysleft = (unsigned long)xres*yres;
00540               else
00541                      raysleft = yres;
00542        } else
00543               raysleft = 0;
00544        if ((account = accumulate) > 0)
00545               raysleft *= accumulate;
00546        waitflush = xres;
00547        if (!recover)
00548               return;
00549                                    /* recover previous values */
00550        if (accumulate <= 0)
00551               reload_output();
00552        else
00553               recover_output(stdin);
00554 }
00555 
00556 /* grow modifier to accommodate more bins */
00557 MODCONT *
00558 growmodifier(MODCONT *mp, int nb)
00559 {
00560        if (nb <= mp->nbins)
00561               return mp;
00562        mp = (MODCONT *)realloc(mp, sizeof(MODCONT) + sizeof(DCOLOR)*(nb-1));
00563        if (mp == NULL)
00564               error(SYSTEM, "out of memory in growmodifier");
00565        memset(mp->cbin+mp->nbins, 0, sizeof(DCOLOR)*(nb-mp->nbins));
00566        mp->nbins = nb;
00567        return mp;
00568 }
00569 
00570 /* add modifier to our list to track */
00571 MODCONT *
00572 addmodifier(char *modn, char *outf, char *binv, int bincnt)
00573 {
00574        LUENT  *lep = lu_find(&modconttab, modn);
00575        MODCONT       *mp;
00576        int    i;
00577        
00578        if (lep->data != NULL) {
00579               sprintf(errmsg, "duplicate modifier '%s'", modn);
00580               error(USER, errmsg);
00581        }
00582        if (nmods >= MAXMODLIST)
00583               error(INTERNAL, "too many modifiers");
00584        modname[nmods++] = modn;    /* XXX assumes static string */
00585        lep->key = modn;            /* XXX assumes static string */
00586        mp = (MODCONT *)malloc(sizeof(MODCONT));
00587        if (mp == NULL)
00588               error(SYSTEM, "out of memory in addmodifier");
00589        mp->outspec = outf;         /* XXX assumes static string */
00590        mp->modname = modn;         /* XXX assumes static string */
00591        if (binv == NULL)
00592               binv = "0";          /* use single bin if unspecified */
00593        mp->binv = eparse(binv);
00594        if (mp->binv->type == NUM) {       /* check value if constant */
00595               bincnt = (int)(evalue(mp->binv) + 1.5);
00596               if (bincnt != 1) {
00597                      sprintf(errmsg, "illegal non-zero constant for bin (%s)",
00598                                    binv);
00599                      error(USER, errmsg);
00600               }
00601        }
00602        mp->nbins = 1;                     /* initialize results holder */
00603        setcolor(mp->cbin[0], 0., 0., 0.);
00604        if (bincnt > 1)
00605               mp = growmodifier(mp, bincnt);
00606        lep->data = (char *)mp;
00607                                    /* allocate output streams */
00608        for (i = bincnt; i-- > 0; )
00609               getostream(mp->outspec, mp->modname, i, 1);
00610        return mp;
00611 }
00612 
00613 /* add modifiers from a file list */
00614 void
00615 addmodfile(char *fname, char *outf, char *binv, int bincnt)
00616 {
00617        char   *mname[MAXMODLIST];
00618        int    i;
00619                                    /* find the file & store strings */
00620        if (wordfile(mname, getpath(fname, getrlibpath(), R_OK)) < 0) {
00621               sprintf(errmsg, "cannot find modifier file '%s'", fname);
00622               error(SYSTEM, errmsg);
00623        }
00624        for (i = 0; mname[i]; i++)  /* add each one */
00625               addmodifier(mname[i], outf, binv, bincnt);
00626 }
00627 
00628 /* put string to stderr */
00629 void
00630 eputs(char  *s)
00631 {
00632        static int  midline = 0;
00633 
00634        if (!*s) return;
00635        if (!midline) {
00636               fputs(progname, stderr);
00637               fputs(": ", stderr);
00638        }
00639        fputs(s, stderr);
00640        midline = s[strlen(s)-1] != '\n';
00641 }
00642 
00643 /* construct output file name and return flags whether modifier/bin present */
00644 int
00645 ofname(char *oname, const char *ospec, const char *mname, int bn)
00646 {
00647        const char    *mnp = NULL;
00648        const char    *bnp = NULL;
00649        const char    *cp;
00650        
00651        if (ospec == NULL)
00652               return -1;
00653        for (cp = ospec; *cp; cp++)        /* check format position(s) */
00654               if (*cp == '%') {
00655                      do
00656                             ++cp;
00657                      while (isdigit(*cp));
00658                      switch (*cp) {
00659                      case '%':
00660                             break;
00661                      case 's':
00662                             if (mnp != NULL)
00663                                    return -1;
00664                             mnp = cp;
00665                             break;
00666                      case 'd':
00667                      case 'i':
00668                      case 'o':
00669                      case 'x':
00670                      case 'X':
00671                             if (bnp != NULL)
00672                                    return -1;
00673                             bnp = cp;
00674                             break;
00675                      default:
00676                             return -1;
00677                      }
00678               }
00679        if (mnp != NULL) {                 /* create file name */
00680               if (bnp != NULL) {
00681                      if (bnp > mnp)
00682                             sprintf(oname, ospec, mname, bn);
00683                      else
00684                             sprintf(oname, ospec, bn, mname);
00685                      return OF_MODIFIER|OF_BIN;
00686               }
00687               sprintf(oname, ospec, mname);
00688               return OF_MODIFIER;
00689        }
00690        if (bnp != NULL) {
00691               sprintf(oname, ospec, bn);
00692               return OF_BIN;
00693        }
00694        strcpy(oname, ospec);
00695        return 0;
00696 }
00697 
00698 /* write header to the given output stream */
00699 void
00700 printheader(FILE *fout, const char *info)
00701 {
00702        extern char   VersionID[];
00703        FILE          *fin = fopen(octree, "r");
00704        
00705        if (fin == NULL)
00706               quit(1);
00707        checkheader(fin, "ignore", fout);  /* copy octree header */
00708        fclose(fin);
00709        printargs(gargc-1, gargv, fout);   /* add our command */
00710        fprintf(fout, "SOFTWARE= %s\n", VersionID);
00711        fputnow(fout);
00712        if (info != NULL)                  /* add extra info if given */
00713               fputs(info, fout);
00714        switch (outfmt) {                  /* add output format */
00715        case 'a':
00716               fputformat("ascii", fout);
00717               break;
00718        case 'f':
00719               fputformat("float", fout);
00720               break;
00721        case 'd':
00722               fputformat("double", fout);
00723               break;
00724        case 'c':
00725               fputformat(COLRFMT, fout);
00726               break;
00727        }
00728        fputc('\n', fout);
00729 }
00730 
00731 /* write resolution string to given output stream */
00732 void
00733 printresolu(FILE *fout, int xr, int yr)
00734 {
00735        if ((xr > 0) & (yr > 0))    /* resolution string */
00736               fprtresolu(xr, yr, fout);
00737        if (xres > 0)               /* global flush flag */
00738               fflush(fout);
00739 }
00740 
00741 /* Get output stream pointer (open and write header if new and noopen==0) */
00742 STREAMOUT *
00743 getostream(const char *ospec, const char *mname, int bn, int noopen)
00744 {
00745        static const DCOLOR  nocontrib = BLKCOLOR;
00746        static STREAMOUT     stdos;
00747        int                  ofl;
00748        char                 oname[1024];
00749        LUENT                *lep;
00750        STREAMOUT            *sop;
00751        
00752        if (ospec == NULL) {               /* use stdout? */
00753               if (!noopen && !using_stdout) {
00754                      if (outfmt != 'a')
00755                             SET_FILE_BINARY(stdout);
00756                      if (header)
00757                             printheader(stdout, NULL);
00758                      printresolu(stdout, xres, yres);
00759                      stdos.xr = xres; stdos.yr = yres;
00760                      using_stdout = 1;
00761               }
00762               stdos.ofp = stdout;
00763               stdos.reclen += noopen;
00764               return &stdos;
00765        }
00766        ofl = ofname(oname, ospec, mname, bn);    /* get output name */
00767        if (ofl < 0) {
00768               sprintf(errmsg, "bad output format '%s'", ospec);
00769               error(USER, errmsg);
00770        }
00771        lep = lu_find(&ofiletab, oname);   /* look it up */
00772        if (lep->key == NULL)                     /* new entry */
00773               lep->key = strcpy((char *)malloc(strlen(oname)+1), oname);
00774        sop = (STREAMOUT *)lep->data;
00775        if (sop == NULL) {                 /* allocate stream */
00776               sop = (STREAMOUT *)malloc(sizeof(STREAMOUT));
00777               if (sop == NULL)
00778                      error(SYSTEM, "out of memory in getostream");
00779               sop->outpipe = oname[0] == '!';
00780               sop->reclen = 0;
00781               sop->ofp = NULL;            /* open iff noopen==0 */
00782               sop->xr = xres; sop->yr = yres;
00783               lep->data = (char *)sop;
00784               if (!sop->outpipe & !force_open & !recover &&
00785                             access(oname, F_OK) == 0) {
00786                      errno = EEXIST;             /* file exists */
00787                      goto openerr;
00788               }
00789        }
00790        if (!noopen && sop->ofp == NULL) { /* open output stream */
00791               long          i;
00792               if (oname[0] == '!')        /* output to command */
00793                      sop->ofp = popen(oname+1, "w");
00794               else                        /* else open file */
00795                      sop->ofp = fopen(oname, "w");
00796               if (sop->ofp == NULL)
00797                      goto openerr;
00798               if (outfmt != 'a')
00799                      SET_FILE_BINARY(sop->ofp);
00800               if (header) {
00801                      char   info[512];
00802                      char   *cp = info;
00803                      if (ofl & OF_MODIFIER || sop->reclen == 1) {
00804                             sprintf(cp, "MODIFIER=%s\n", mname);
00805                             while (*cp) ++cp;
00806                      }
00807                      if (ofl & OF_BIN) {
00808                             sprintf(cp, "BIN=%d\n", bn);
00809                             while (*cp) ++cp;
00810                      }
00811                      *cp = '\0';
00812                      printheader(sop->ofp, info);
00813               }
00814               if (accumulate > 0) {              /* global resolution */
00815                      sop->xr = xres; sop->yr = yres;
00816               }
00817               printresolu(sop->ofp, sop->xr, sop->yr);
00818                                           /* play catch-up */
00819               for (i = accumulate > 0 ? lastdone/accumulate : 0; i--; ) {
00820                      int    j = sop->reclen;
00821                      if (j <= 0) j = 1;
00822                      while (j--)
00823                             put_contrib(nocontrib, sop->ofp);
00824                      if (outfmt == 'a')
00825                             putc('\n', sop->ofp);
00826               }
00827               if (xres > 0)
00828                      fflush(sop->ofp);
00829        }
00830        sop->reclen += noopen;                    /* add to length if noopen */
00831        return sop;                        /* return output stream */
00832 openerr:
00833        sprintf(errmsg, "cannot open '%s' for writing", oname);
00834        error(SYSTEM, errmsg);
00835        return NULL;  /* pro forma return */
00836 }
00837 
00838 /* read input ray into buffer */
00839 int
00840 getinp(char *buf, FILE *fp)
00841 {
00842        double dv[3], *dvp;
00843        float  *fvp;
00844        char   *cp;
00845        int    i;
00846 
00847        switch (inpfmt) {
00848        case 'a':
00849               cp = buf;            /* make sure we get 6 floats */
00850               for (i = 0; i < 6; i++) {
00851                      if (fgetword(cp, buf+127-cp, fp) == NULL)
00852                             return -1;
00853                      if (i >= 3)
00854                             dv[i-3] = atof(cp);
00855                      if ((cp = fskip(cp)) == NULL || *cp)
00856                             return -1;
00857                      *cp++ = ' ';
00858               }
00859               getc(fp);            /* get/put eol */
00860               *cp-- = '\0'; *cp = '\n';
00861               if (DOT(dv,dv) <= FTINY*FTINY)
00862                      return 0;     /* dummy ray */
00863               return strlen(buf);
00864        case 'f':
00865               if (fread(buf, sizeof(float), 6, fp) < 6)
00866                      return -1;
00867               fvp = (float *)buf + 3;
00868               if (DOT(fvp,fvp) <= FTINY*FTINY)
00869                      return 0;     /* dummy ray */
00870               return sizeof(float)*6;
00871        case 'd':
00872               if (fread(buf, sizeof(double), 6, fp) < 6)
00873                      return -1;
00874               dvp = (double *)buf + 3;
00875               if (DOT(dvp,dvp) <= FTINY*FTINY)
00876                      return 0;     /* dummy ray */
00877               return sizeof(double)*6;
00878        }
00879        error(INTERNAL, "botched input format");
00880        return -1;    /* pro forma return */
00881 }
00882 
00883 static float  rparams[9];          /* traced ray parameters */
00884 
00885 /* return channel (ray) value */
00886 double
00887 chanvalue(int n)
00888 {
00889        if (--n < 0 || n >= 6)
00890               error(USER, "illegal channel number ($N)");
00891        return rparams[n+3];
00892 }
00893 
00894 /* add current ray contribution to the appropriate modifier bin */
00895 void
00896 add_contrib(const char *modn)
00897 {
00898        LUENT  *le = lu_find(&modconttab, modn);
00899        MODCONT       *mp = (MODCONT *)le->data;
00900        int    bn;
00901 
00902        if (mp == NULL) {
00903               sprintf(errmsg, "unexpected modifier '%s' from rtrace", modn);
00904               error(USER, errmsg);
00905        }
00906        eclock++;                   /* get bin number */
00907        bn = (int)(evalue(mp->binv) + .5);
00908        if (bn <= 0)
00909               bn = 0;
00910        else if (bn >= mp->nbins)   /* new bin */
00911               le->data = (char *)(mp = growmodifier(mp, bn+1));
00912        addcolor(mp->cbin[bn], rparams);
00913 }
00914 
00915 /* output newline to ASCII file and/or flush as requested */
00916 static int
00917 puteol(const LUENT *e, void *p)
00918 {
00919        STREAMOUT     *sop = (STREAMOUT *)e->data;
00920 
00921        if (outfmt == 'a')
00922               putc('\n', sop->ofp);
00923        if (!waitflush)
00924               fflush(sop->ofp);
00925        if (ferror(sop->ofp)) {
00926               sprintf(errmsg, "write error on file '%s'", e->key);
00927               error(SYSTEM, errmsg);
00928        }
00929        return 0;
00930 }
00931 
00932 /* put out ray contribution to file */
00933 void
00934 put_contrib(const DCOLOR cnt, FILE *fout)
00935 {
00936        float  fv[3];
00937        COLR   cv;
00938 
00939        switch (outfmt) {
00940        case 'a':
00941               fprintf(fout, "%.6e\t%.6e\t%.6e\t", cnt[0], cnt[1], cnt[2]);
00942               break;
00943        case 'f':
00944               copycolor(fv, cnt);
00945               fwrite(fv, sizeof(float), 3, fout);
00946               break;
00947        case 'd':
00948               fwrite(cnt, sizeof(double), 3, fout);
00949               break;
00950        case 'c':
00951               setcolr(cv, cnt[0], cnt[1], cnt[2]);
00952               fwrite(cv, sizeof(cv), 1, fout);
00953               break;
00954        default:
00955               error(INTERNAL, "botched output format");
00956        }
00957 }
00958 
00959 /* output ray tallies and clear for next accumulation */
00960 void
00961 done_contrib(int navg)
00962 {
00963        double        sf = 1.;
00964        int           i, j;
00965        MODCONT              *mp;
00966        STREAMOUT     *sop;
00967                                           /* set average scaling */
00968        if (navg > 1)
00969               sf = 1. / (double)navg;
00970                                           /* output modifiers in order */
00971        for (i = 0; i < nmods; i++) {
00972               mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
00973               if (navg > 1)               /* average scaling */
00974                      for (j = mp->nbins; j--; )
00975                             scalecolor(mp->cbin[j], sf);
00976               sop = getostream(mp->outspec, mp->modname, 0,0);
00977               put_contrib(mp->cbin[0], sop->ofp);
00978               if (mp->nbins > 3 &&        /* minor optimization */
00979                             sop == getostream(mp->outspec, mp->modname, 1,0))
00980                      for (j = 1; j < mp->nbins; j++)
00981                             put_contrib(mp->cbin[j], sop->ofp);
00982               else
00983                      for (j = 1; j < mp->nbins; j++)
00984                             put_contrib(mp->cbin[j],
00985                                 getostream(mp->outspec,mp->modname,j,0)->ofp);
00986                                           /* clear for next time */
00987               memset(mp->cbin, 0, sizeof(DCOLOR)*mp->nbins);
00988        }
00989        --waitflush;                       /* terminate records */
00990        lu_doall(&ofiletab, puteol, NULL);
00991        if (using_stdout & (outfmt == 'a'))
00992               putc('\n', stdout);
00993        if (!waitflush) {
00994               waitflush = xres;
00995               if (using_stdout)
00996                      fflush(stdout);
00997        }
00998 }
00999 
01000 /* queue completed ray tree produced by rtrace process */
01001 void
01002 queue_raytree(struct rtproc *rtp)
01003 {
01004        struct rtproc *rtu, *rtl = NULL;
01005                                    /* insert following ray order */
01006        for (rtu = rt_unproc; rtu != NULL; rtu = (rtl=rtu)->next)
01007               if (rtp->raynum < rtu->raynum)
01008                      break;
01009        rtu = (struct rtproc *)malloc(sizeof(struct rtproc));
01010        if (rtu == NULL)
01011               error(SYSTEM, "out of memory in queue_raytree");
01012        *rtu = *rtp;
01013        if (rtl == NULL) {
01014               rtu->next = rt_unproc;
01015               rt_unproc = rtu;
01016        } else {
01017               rtu->next = rtl->next;
01018               rtl->next = rtu;
01019        }
01020        rtp->raynum = 0;            /* clear path for next ray tree */
01021        rtp->bsiz = 0;
01022        rtp->buf = NULL;
01023        rtp->nbr = 0;
01024 }
01025 
01026 /* process completed ray trees from our queue */
01027 void
01028 process_queue(void)
01029 {
01030        char   modname[128];
01031                                    /* ray-ordered queue */
01032        while (rt_unproc != NULL && rt_unproc->raynum == lastdone+1) {
01033               struct rtproc *rtp = rt_unproc;
01034               int           n = rtp->nbr;
01035               const char    *cp = rtp->buf;
01036               while (n > 0) {             /* process rays */
01037                      register char *mnp = modname;
01038                                    /* skip leading tabs */
01039                      while (n > 0 && *cp == '\t') {
01040                             cp++; n--;
01041                      }
01042                      if (!n || !(isalpha(*cp) | (*cp == '_')))
01043                             error(USER, "bad modifier name from rtrace");
01044                                    /* get modifier name */
01045                      while (n > 1 && *cp != '\t') {
01046                             if (mnp - modname >= sizeof(modname)-2)
01047                                    error(INTERNAL, "modifier name too long");
01048                             *mnp++ = *cp++; n--;
01049                      }
01050                      *mnp = '\0';
01051                      cp++; n--;    /* eat following tab */
01052                      if (n < (int)(sizeof(float)*9))
01053                             error(USER, "incomplete ray value from rtrace");
01054                                    /* add ray contribution */
01055                      memcpy(rparams, cp, sizeof(float)*9);
01056                      cp += sizeof(float)*9; n -= sizeof(float)*9;
01057                      add_contrib(modname);
01058               }
01059                                    /* time to produce record? */
01060               if (account > 0 && !--account)
01061                      done_contrib(account = accumulate);
01062               lastdone = rtp->raynum;
01063               if (rtp->buf != NULL)       /* free up buffer space */
01064                      free(rtp->buf);
01065               rt_unproc = rtp->next;
01066               free(rtp);           /* done with this ray tree */
01067        }
01068 }
01069 
01070 /* wait for rtrace process to finish with ray tree */
01071 struct rtproc *
01072 wait_rproc(void)
01073 {
01074        struct rtproc *rtfree = NULL;
01075        fd_set        readset, errset;
01076        int           nr;
01077        struct rtproc *rt;
01078        int           n;
01079        
01080        do {
01081               nr = 0;                            /* prepare select call */
01082               FD_ZERO(&readset); FD_ZERO(&errset); n = 0;
01083               for (rt = &rt0; rt != NULL; rt = rt->next) {
01084                      if (rt->raynum) {
01085                             FD_SET(rt->pd.r, &readset);
01086                             ++nr;
01087                      }
01088                      FD_SET(rt->pd.r, &errset);
01089                      if (rt->pd.r >= n)
01090                             n = rt->pd.r + 1;
01091               }
01092               if (!nr)                    /* no rays pending */
01093                      break;
01094               if (nr > 1) {               /* call select for multiple proc's */
01095                      errno = 0;
01096                      if (select(n, &readset, NULL, &errset, NULL) < 0)
01097                             error(SYSTEM, "select call error in wait_rproc()");
01098               } else
01099                      FD_ZERO(&errset);
01100               nr = 0;
01101               for (rt = &rt0; rt != NULL; rt = rt->next) {
01102                      if (!FD_ISSET(rt->pd.r, &readset) &&
01103                                    !FD_ISSET(rt->pd.r, &errset))
01104                             continue;
01105                      if (rt->buf == NULL) {
01106                             rt->bsiz = treebufsiz;
01107                             rt->buf = (char *)malloc(treebufsiz);
01108                      } else if (rt->nbr + BUFSIZ > rt->bsiz) {
01109                             if (rt->bsiz + BUFSIZ <= treebufsiz)
01110                                    rt->bsiz = treebufsiz;
01111                             else
01112                                    treebufsiz = rt->bsiz += BUFSIZ;
01113                             rt->buf = (char *)realloc(rt->buf, rt->bsiz);
01114                      }
01115                      if (rt->buf == NULL)
01116                             error(SYSTEM, "out of memory in wait_rproc");
01117                      nr = read(rt->pd.r, rt->buf+rt->nbr, rt->bsiz-rt->nbr);
01118                      if (nr <= 0)
01119                             error(USER, "rtrace process died");
01120                      rt->nbr += nr;              /* advance & check */
01121                      if (rt->nbr >= 4 && !memcmp(rt->buf+rt->nbr-4,
01122                                                  "~\t~\t", 4)) {
01123                             rt->nbr -= 4; /* elide terminator */
01124                             queue_raytree(rt);
01125                             rtfree = rt;  /* ready for next ray */
01126                      }
01127               }
01128        } while ((rtfree == NULL) & (nr > 0));    /* repeat until ready or out */
01129        return rtfree;
01130 }
01131 
01132 /* return next available rtrace process */
01133 struct rtproc *
01134 get_rproc(void)
01135 {
01136        struct rtproc *rtp;
01137                                           /* check for idle rtrace */
01138        for (rtp = &rt0; rtp != NULL; rtp = rtp->next)
01139               if (!rtp->raynum)
01140                      return rtp;
01141        return wait_rproc();               /* need to wait for one */
01142 }
01143 
01144 /* trace ray contributions (main loop) */
01145 void
01146 trace_contribs(FILE *fin)
01147 {
01148        static int    ignore_warning_given = 0;
01149        char          inpbuf[128];
01150        int           iblen;
01151        struct rtproc *rtp;
01152                                           /* loop over input */
01153        while ((iblen = getinp(inpbuf, fin)) >= 0) {
01154               if (!iblen && accumulate != 1) {
01155                      if (!ignore_warning_given++)
01156                             error(WARNING,
01157                             "dummy ray(s) ignored during accumulation\n");
01158                      continue;
01159               }
01160               if (!iblen ||               /* need flush/reset? */
01161                             queue_length() > 10*nrtprocs() ||
01162                             lastray+1 < lastray) {
01163                      while (wait_rproc() != NULL)
01164                             process_queue();
01165                      if (lastray+1 < lastray)
01166                             lastdone = lastray = 0;
01167               }
01168               rtp = get_rproc();          /* get avail. rtrace process */
01169               rtp->raynum = ++lastray;    /* assign ray */
01170               if (iblen) {                /* trace ray if valid */
01171                      writebuf(rtp->pd.w, inpbuf, iblen);
01172               } else {                    /* else bypass dummy ray */
01173                      queue_raytree(rtp);  /* queue empty ray/record */
01174                      if ((yres <= 0) | (xres <= 0))
01175                             waitflush = 1;       /* flush right after */
01176               }
01177               process_queue();            /* catch up with results */
01178               if (raysleft && !--raysleft)
01179                      break;               /* preemptive EOI */
01180        }
01181        while (wait_rproc() != NULL)              /* process outstanding rays */
01182               process_queue();
01183        if (accumulate <= 0)
01184               done_contrib(0);            /* output tallies */
01185        else if (account < accumulate) {
01186               error(WARNING, "partial accumulation in final record");
01187               done_contrib(accumulate - account);
01188        }
01189        if (raysleft)
01190               error(USER, "unexpected EOF on input");
01191        lu_done(&ofiletab);                /* close output files */
01192 }
01193 
01194 /* get ray contribution from previous file */
01195 static int
01196 get_contrib(DCOLOR cnt, FILE *finp)
01197 {
01198        COLOR  fv;
01199        COLR   cv;
01200 
01201        switch (outfmt) {
01202        case 'a':
01203               return(fscanf(finp,"%lf %lf %lf",&cnt[0],&cnt[1],&cnt[2]) == 3);
01204        case 'f':
01205               if (fread(fv, sizeof(fv[0]), 3, finp) != 3)
01206                      return(0);
01207               copycolor(cnt, fv);
01208               return(1);
01209        case 'd':
01210               return(fread(cnt, sizeof(cnt[0]), 3, finp) == 3);
01211        case 'c':
01212               if (fread(cv, sizeof(cv), 1, finp) != 1)
01213                      return(0);
01214               colr_color(fv, cv);
01215               copycolor(cnt, fv);
01216               return(1);
01217        default:
01218               error(INTERNAL, "botched output format");
01219        }
01220        return(0);    /* pro forma return */
01221 }
01222 
01223 /* close output file opened for input */
01224 static int
01225 myclose(const LUENT *e, void *p)
01226 {
01227        STREAMOUT     *sop = (STREAMOUT *)e->data;
01228        
01229        if (sop->ofp == NULL)
01230               return(0);
01231        fclose(sop->ofp);
01232        sop->ofp = NULL;
01233        return(0);
01234 }
01235 
01236 /* load previously accumulated values */
01237 void
01238 reload_output(void)
01239 {
01240        int           i, j;
01241        MODCONT              *mp;
01242        int           ofl;
01243        char          oname[1024];
01244        char          *fmode = "rb";
01245        char          *outvfmt;
01246        LUENT         *ment, *oent;
01247        int           xr, yr;
01248        STREAMOUT     sout;
01249        DCOLOR        rgbv;
01250 
01251        switch (outfmt) {
01252        case 'a':
01253               outvfmt = "ascii";
01254               fmode = "r";
01255               break;
01256        case 'f':
01257               outvfmt = "float";
01258               break;
01259        case 'd':
01260               outvfmt = "double";
01261               break;
01262        case 'c':
01263               outvfmt = COLRFMT;
01264               break;
01265        default:
01266               error(INTERNAL, "botched output format");
01267               return;
01268        }
01269                                           /* reload modifier values */
01270        for (i = 0; i < nmods; i++) {
01271               ment = lu_find(&modconttab,modname[i]);
01272               mp = (MODCONT *)ment->data;
01273               if (mp->outspec == NULL)
01274                      error(USER, "cannot reload from stdout");
01275               if (mp->outspec[0] == '!')
01276                      error(USER, "cannot reload from command");
01277               for (j = 0; ; j++) {        /* load each modifier bin */
01278                      ofl = ofname(oname, mp->outspec, mp->modname, j);
01279                      if (ofl < 0)
01280                             error(USER, "bad output file specification");
01281                      oent = lu_find(&ofiletab, oname);
01282                      if (oent->data != NULL) {
01283                             sout = *(STREAMOUT *)oent->data;
01284                      } else {
01285                             sout.reclen = 0;
01286                             sout.outpipe = 0;
01287                             sout.xr = xres; sout.yr = yres;
01288                             sout.ofp = NULL;
01289                      }
01290                      if (sout.ofp == NULL) {     /* open output as input */
01291                             sout.ofp = fopen(oname, fmode);
01292                             if (sout.ofp == NULL) {
01293                                    if (j)
01294                                           break; /* assume end of modifier */
01295                                    sprintf(errmsg, "missing reload file '%s'",
01296                                                  oname);
01297                                    error(WARNING, errmsg);
01298                                    break;
01299                             }
01300                             if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
01301                                    sprintf(errmsg, "format mismatch for '%s'",
01302                                                  oname);
01303                                    error(USER, errmsg);
01304                             }
01305                             if ((sout.xr > 0) & (sout.yr > 0) &&
01306                                           (!fscnresolu(&xr, &yr, sout.ofp) ||
01307                                                  (xr != sout.xr) |
01308                                                  (yr != sout.yr))) {
01309                                    sprintf(errmsg, "resolution mismatch for '%s'",
01310                                                  oname);
01311                                    error(USER, errmsg);
01312                             }
01313                      }
01314                                                  /* read in RGB value */
01315                      if (!get_contrib(rgbv, sout.ofp)) {
01316                             if (!j) {
01317                                    fclose(sout.ofp);
01318                                    break;        /* ignore empty file */
01319                             }
01320                             if (j < mp->nbins) {
01321                                    sprintf(errmsg, "missing data in '%s'",
01322                                                  oname);
01323                                    error(USER, errmsg);
01324                             }
01325                             break;
01326                      }
01327                      if (j >= mp->nbins)         /* grow modifier size */
01328                             ment->data = (char *)(mp = growmodifier(mp, j+1));
01329                      copycolor(mp->cbin[j], rgbv);
01330                      if (oent->key == NULL)             /* new file entry */
01331                             oent->key = strcpy((char *)
01332                                           malloc(strlen(oname)+1), oname);
01333                      if (oent->data == NULL)
01334                             oent->data = (char *)malloc(sizeof(STREAMOUT));
01335                      *(STREAMOUT *)oent->data = sout;
01336               }
01337        }
01338        lu_doall(&ofiletab, myclose, NULL);       /* close all files */
01339 }
01340 
01341 /* seek on the given output file */
01342 static int
01343 myseeko(const LUENT *e, void *p)
01344 {
01345        STREAMOUT     *sop = (STREAMOUT *)e->data;
01346        off_t         nbytes = *(off_t *)p;
01347        
01348        if (sop->reclen > 1)
01349               nbytes = nbytes * sop->reclen;
01350        if (fseeko(sop->ofp, nbytes, SEEK_CUR) < 0) {
01351               sprintf(errmsg, "seek error on file '%s'", e->key);
01352               error(SYSTEM, errmsg);
01353        }
01354        return 0;
01355 }
01356 
01357 /* recover output if possible */
01358 void
01359 recover_output(FILE *fin)
01360 {
01361        off_t         lastout = -1;
01362        int           outvsiz, recsiz;
01363        char          *outvfmt;
01364        int           i, j;
01365        MODCONT              *mp;
01366        int           ofl;
01367        char          oname[1024];
01368        LUENT         *ment, *oent;
01369        STREAMOUT     sout;
01370        off_t         nvals;
01371        int           xr, yr;
01372 
01373        switch (outfmt) {
01374        case 'a':
01375               error(USER, "cannot recover ASCII output");
01376               return;
01377        case 'f':
01378               outvsiz = sizeof(float)*3;
01379               outvfmt = "float";
01380               break;
01381        case 'd':
01382               outvsiz = sizeof(double)*3;
01383               outvfmt = "double";
01384               break;
01385        case 'c':
01386               outvsiz = sizeof(COLR);
01387               outvfmt = COLRFMT;
01388               break;
01389        default:
01390               error(INTERNAL, "botched output format");
01391               return;
01392        }
01393                                           /* check modifier outputs */
01394        for (i = 0; i < nmods; i++) {
01395               ment = lu_find(&modconttab,modname[i]);
01396               mp = (MODCONT *)ment->data;
01397               if (mp->outspec == NULL)
01398                      error(USER, "cannot recover from stdout");
01399               if (mp->outspec[0] == '!')
01400                      error(USER, "cannot recover from command");
01401               for (j = 0; ; j++) {        /* check each bin's file */
01402                      ofl = ofname(oname, mp->outspec, mp->modname, j);
01403                      if (ofl < 0)
01404                             error(USER, "bad output file specification");
01405                      oent = lu_find(&ofiletab, oname);
01406                      if (oent->data != NULL) {
01407                             sout = *(STREAMOUT *)oent->data;
01408                      } else {
01409                             sout.reclen = 0;
01410                             sout.outpipe = 0;
01411                             sout.ofp = NULL;
01412                      }
01413                      if (sout.ofp != NULL) {     /* already open? */
01414                             if (ofl & OF_BIN)
01415                                    continue;
01416                             break;
01417                      }
01418                                           /* open output */
01419                      sout.ofp = fopen(oname, "rb+");
01420                      if (sout.ofp == NULL) {
01421                             if (j)
01422                                    break; /* assume end of modifier */
01423                             sprintf(errmsg, "missing recover file '%s'",
01424                                           oname);
01425                             error(WARNING, errmsg);
01426                             break;
01427                      }
01428                      nvals = lseek(fileno(sout.ofp), 0, SEEK_END);
01429                      if (nvals <= 0) {
01430                             lastout = 0;  /* empty output, quit here */
01431                             fclose(sout.ofp);
01432                             break;
01433                      }
01434                      if (!sout.reclen) {
01435                             if (!(ofl & OF_BIN)) {
01436                                    sprintf(errmsg,
01437                                           "need -bn to recover file '%s'",
01438                                                  oname);
01439                                    error(USER, errmsg);
01440                             }
01441                             recsiz = outvsiz;
01442                      } else
01443                             recsiz = outvsiz * sout.reclen;
01444 
01445                      lseek(fileno(sout.ofp), 0, SEEK_SET);
01446                      if (header && checkheader(sout.ofp, outvfmt, NULL) != 1) {
01447                             sprintf(errmsg, "format mismatch for '%s'",
01448                                           oname);
01449                             error(USER, errmsg);
01450                      }
01451                      sout.xr = xres; sout.yr = yres;
01452                      if ((sout.xr > 0) & (sout.yr > 0) &&
01453                                    (!fscnresolu(&xr, &yr, sout.ofp) ||
01454                                           (xr != sout.xr) |
01455                                           (yr != sout.yr))) {
01456                             sprintf(errmsg, "resolution mismatch for '%s'",
01457                                           oname);
01458                             error(USER, errmsg);
01459                      }
01460                      nvals = (nvals - (off_t)ftell(sout.ofp)) / recsiz;
01461                      if ((lastout < 0) | (nvals < lastout))
01462                             lastout = nvals;
01463                      if (oent->key == NULL)      /* new entry */
01464                             oent->key = strcpy((char *)
01465                                           malloc(strlen(oname)+1), oname);
01466                      if (oent->data == NULL)
01467                             oent->data = (char *)malloc(sizeof(STREAMOUT));
01468                      *(STREAMOUT *)oent->data = sout;
01469                      if (!(ofl & OF_BIN))
01470                             break;        /* no bin separation */
01471               }
01472               if (!lastout) {                    /* empty output */
01473                      error(WARNING, "no previous data to recover");
01474                      lu_done(&ofiletab);  /* reclose all outputs */
01475                      return;
01476               }
01477               if (j > mp->nbins)          /* reallocate modifier bins */
01478                      ment->data = (char *)(mp = growmodifier(mp, j));
01479        }
01480        if (lastout < 0) {
01481               error(WARNING, "no output files to recover");
01482               return;
01483        }
01484        if (raysleft && lastout >= raysleft/accumulate) {
01485               error(WARNING, "output appears to be complete");
01486               /* XXX should read & discard input? */
01487               quit(0);
01488        }
01489                                           /* seek on all files */
01490        nvals = lastout * outvsiz;
01491        lu_doall(&ofiletab, myseeko, &nvals);
01492                                           /* skip repeated input */
01493        for (nvals = 0; nvals < lastout; nvals++)
01494               if (getinp(oname, fin) < 0)
01495                      error(USER, "unexpected EOF on input");
01496        lastray = lastdone = (unsigned long)lastout * accumulate;
01497        if (raysleft)
01498               raysleft -= lastray;
01499 }