| 1 | /* Copyright (c) 1995 Regents of the University of California */ | 
| 2 |  | 
| 3 | #ifndef lint | 
| 4 | static char SCCSid[] = "$SunId$ LBL"; | 
| 5 | #endif | 
| 6 |  | 
| 7 | /* | 
| 8 | * Radiance animation control program | 
| 9 | */ | 
| 10 |  | 
| 11 | #include "standard.h" | 
| 12 | #include <ctype.h> | 
| 13 | #include <sys/types.h> | 
| 14 | #include <sys/stat.h> | 
| 15 | #include "view.h" | 
| 16 | #include "vars.h" | 
| 17 | #include "netproc.h" | 
| 18 | /* input variables */ | 
| 19 | #define HOST            0               /* rendering host machine */ | 
| 20 | #define RENDER          1               /* rendering options */ | 
| 21 | #define PFILT           2               /* pfilt options */ | 
| 22 | #define PINTERP         3               /* pinterp options */ | 
| 23 | #define OCTREE          4               /* octree file name */ | 
| 24 | #define DIRECTORY       5               /* working (sub)directory */ | 
| 25 | #define BASENAME        6               /* output image base name */ | 
| 26 | #define VIEWFILE        7               /* animation frame views */ | 
| 27 | #define START           8               /* starting frame number */ | 
| 28 | #define END             9               /* ending frame number */ | 
| 29 | #define RIF             10              /* rad input file */ | 
| 30 | #define NEXTANIM        11              /* next animation file */ | 
| 31 | #define ANIMATE         12              /* animation command */ | 
| 32 | #define TRANSFER        13              /* frame transfer command */ | 
| 33 | #define ARCHIVE         14              /* archiving command */ | 
| 34 | #define INTERP          15              /* # frames to interpolate */ | 
| 35 | #define OVERSAMP        16              /* # times to oversample image */ | 
| 36 | #define MBLUR           17              /* samples for motion blur */ | 
| 37 | #define RTRACE          18              /* use rtrace with pinterp? */ | 
| 38 | #define DISKSPACE       19              /* how much disk space to use */ | 
| 39 | #define RESOLUTION      20              /* desired final resolution */ | 
| 40 | #define EXPOSURE        21              /* how to compute exposure */ | 
| 41 |  | 
| 42 | int     NVARS = 22;             /* total number of variables */ | 
| 43 |  | 
| 44 | VARIABLE        vv[] = {                /* variable-value pairs */ | 
| 45 | {"host",        4,      0,      NULL,   NULL}, | 
| 46 | {"render",      3,      0,      NULL,   catvalues}, | 
| 47 | {"pfilt",       2,      0,      NULL,   catvalues}, | 
| 48 | {"pinterp",     2,      0,      NULL,   catvalues}, | 
| 49 | {"OCTREE",      3,      0,      NULL,   onevalue}, | 
| 50 | {"DIRECTORY",   3,      0,      NULL,   onevalue}, | 
| 51 | {"BASENAME",    3,      0,      NULL,   onevalue}, | 
| 52 | {"VIEWFILE",    2,      0,      NULL,   onevalue}, | 
| 53 | {"START",       2,      0,      NULL,   intvalue}, | 
| 54 | {"END",         2,      0,      NULL,   intvalue}, | 
| 55 | {"RIF",         3,      0,      NULL,   onevalue}, | 
| 56 | {"NEXTANIM",    3,      0,      NULL,   onevalue}, | 
| 57 | {"ANIMATE",     2,      0,      NULL,   onevalue}, | 
| 58 | {"TRANSFER",    2,      0,      NULL,   onevalue}, | 
| 59 | {"ARCHIVE",     2,      0,      NULL,   onevalue}, | 
| 60 | {"INTERPOLATE", 3,      0,      NULL,   intvalue}, | 
| 61 | {"OVERSAMPLE",  2,      0,      NULL,   fltvalue}, | 
| 62 | {"MBLUR",       2,      0,      NULL,   onevalue}, | 
| 63 | {"RTRACE",      2,      0,      NULL,   boolvalue}, | 
| 64 | {"DISKSPACE",   3,      0,      NULL,   fltvalue}, | 
| 65 | {"RESOLUTION",  3,      0,      NULL,   onevalue}, | 
| 66 | {"EXPOSURE",    3,      0,      NULL,   onevalue}, | 
| 67 | }; | 
| 68 |  | 
| 69 | #define SFNAME  "STATUS"                /* status file name */ | 
| 70 |  | 
| 71 | struct { | 
| 72 | char    host[64];               /* control host name */ | 
| 73 | int     pid;                    /* control process id */ | 
| 74 | char    cfname[128];            /* control file name */ | 
| 75 | int     rnext;                  /* next frame to render */ | 
| 76 | int     fnext;                  /* next frame to filter */ | 
| 77 | int     tnext;                  /* next frame to transfer */ | 
| 78 | }       astat;                  /* animation status */ | 
| 79 |  | 
| 80 | char    *progname;              /* our program name */ | 
| 81 | char    *cfname;                /* our control file name */ | 
| 82 |  | 
| 83 | int     nowarn = 0;             /* turn warnings off? */ | 
| 84 | int     silent = 0;             /* silent mode? */ | 
| 85 | int     noaction = 0;           /* take no action? */ | 
| 86 |  | 
| 87 | char    rendopt[2048] = "";     /* rendering options */ | 
| 88 | char    rresopt[32];            /* rendering resolution options */ | 
| 89 | char    fresopt[32];            /* filter resolution options */ | 
| 90 | int     pfiltalways;            /* always use pfilt? */ | 
| 91 |  | 
| 92 | char    arcargs[10240];         /* files to archive */ | 
| 93 | char    *arcfirst, *arcnext;    /* pointers to first and next argument */ | 
| 94 |  | 
| 95 | struct pslot { | 
| 96 | int     pid;                    /* process ID (0 if empty) */ | 
| 97 | int     fout;                   /* output frame number */ | 
| 98 | int     (*rcvf)();              /* recover function */ | 
| 99 | }       *pslot;                 /* process slots */ | 
| 100 | int     npslots;                /* number of process slots */ | 
| 101 |  | 
| 102 | int     lastpid;                /* ID of last completed background process */ | 
| 103 | PSERVER *lastpserver;           /* last process server used */ | 
| 104 |  | 
| 105 | #define phostname(ps)   ((ps)->hostname[0] ? (ps)->hostname : astat.host) | 
| 106 |  | 
| 107 | struct pslot    *findpslot(); | 
| 108 |  | 
| 109 | VIEW    *getview(); | 
| 110 | char    *getexp(); | 
| 111 |  | 
| 112 |  | 
| 113 | main(argc, argv) | 
| 114 | int     argc; | 
| 115 | char    *argv[]; | 
| 116 | { | 
| 117 | int     explicate = 0; | 
| 118 | int     i; | 
| 119 |  | 
| 120 | progname = argv[0];                     /* get arguments */ | 
| 121 | for (i = 1; i < argc && argv[i][0] == '-'; i++) | 
| 122 | switch (argv[i][1]) { | 
| 123 | case 'e':                       /* print variables */ | 
| 124 | explicate++; | 
| 125 | break; | 
| 126 | case 'w':                       /* turn off warnings */ | 
| 127 | nowarn++; | 
| 128 | break; | 
| 129 | case 's':                       /* silent mode */ | 
| 130 | silent++; | 
| 131 | break; | 
| 132 | case 'n':                       /* take no action */ | 
| 133 | noaction++; | 
| 134 | break; | 
| 135 | default: | 
| 136 | goto userr; | 
| 137 | } | 
| 138 | if (i != argc-1) | 
| 139 | goto userr; | 
| 140 | cfname = argv[i]; | 
| 141 | /* load variables */ | 
| 142 | loadvars(cfname); | 
| 143 | /* did we get DIRECTORY? */ | 
| 144 | checkdir(); | 
| 145 | /* check status */ | 
| 146 | if (getastat() < 0) { | 
| 147 | fprintf(stderr, "%s: exiting\n", progname); | 
| 148 | quit(1); | 
| 149 | } | 
| 150 | /* pfilt always if options given */ | 
| 151 | pfiltalways = vdef(PFILT); | 
| 152 | /* load RIF if any */ | 
| 153 | if (vdef(RIF)) | 
| 154 | getradfile(vval(RIF)); | 
| 155 | /* set defaults */ | 
| 156 | setdefaults(); | 
| 157 | /* print variables */ | 
| 158 | if (explicate) | 
| 159 | printvars(stdout); | 
| 160 | /* set up process servers */ | 
| 161 | sethosts(); | 
| 162 | /* run animation */ | 
| 163 | animate(); | 
| 164 | /* all done */ | 
| 165 | if (vdef(NEXTANIM)) { | 
| 166 | argv[i] = vval(NEXTANIM);       /* just change input file */ | 
| 167 | if (!silent) | 
| 168 | printargs(argc, argv, stdout); | 
| 169 | execvp(progname, argv);         /* pass to next */ | 
| 170 | quit(1);                        /* shouldn't return */ | 
| 171 | } | 
| 172 | quit(0); | 
| 173 | userr: | 
| 174 | fprintf(stderr, "Usage: %s [-s][-n][-w][-e] anim_file\n", progname); | 
| 175 | quit(1); | 
| 176 | } | 
| 177 |  | 
| 178 |  | 
| 179 | getastat()                      /* check/set animation status */ | 
| 180 | { | 
| 181 | char    buf[256]; | 
| 182 | FILE    *fp; | 
| 183 |  | 
| 184 | sprintf(buf, "%s/%s", vval(DIRECTORY), SFNAME); | 
| 185 | if ((fp = fopen(buf, "r")) == NULL) { | 
| 186 | if (errno != ENOENT) { | 
| 187 | perror(buf); | 
| 188 | return(-1); | 
| 189 | } | 
| 190 | astat.rnext = astat.fnext = astat.tnext = 0; | 
| 191 | goto setours; | 
| 192 | } | 
| 193 | if (fscanf(fp, "Control host: %s\n", astat.host) != 1) | 
| 194 | goto fmterr; | 
| 195 | if (fscanf(fp, "Control PID: %d\n", &astat.pid) != 1) | 
| 196 | goto fmterr; | 
| 197 | if (fscanf(fp, "Control file: %s\n", astat.cfname) != 1) | 
| 198 | goto fmterr; | 
| 199 | if (fscanf(fp, "Next render: %d\n", &astat.rnext) != 1) | 
| 200 | goto fmterr; | 
| 201 | if (fscanf(fp, "Next filter: %d\n", &astat.fnext) != 1) | 
| 202 | goto fmterr; | 
| 203 | if (fscanf(fp, "Next transfer: %d\n", &astat.tnext) != 1) | 
| 204 | goto fmterr; | 
| 205 | fclose(fp); | 
| 206 | if (astat.pid != 0) {                   /* thinks it's still running */ | 
| 207 | if (strcmp(myhostname(), astat.host)) { | 
| 208 | fprintf(stderr, | 
| 209 | "%s: process %d may still be running on host %s\n", | 
| 210 | progname, astat.pid, astat.host); | 
| 211 | return(-1); | 
| 212 | } | 
| 213 | if (kill(astat.pid, 0) != -1 || errno != ESRCH) { | 
| 214 | fprintf(stderr, "%s: process %d is still running\n", | 
| 215 | progname, astat.pid); | 
| 216 | return(-1); | 
| 217 | } | 
| 218 | /* assume it is dead */ | 
| 219 | } | 
| 220 | if (strcmp(cfname, astat.cfname) && astat.tnext != 0) { /* other's */ | 
| 221 | fprintf(stderr, "%s: unfinished job \"%s\"\n", | 
| 222 | progname, astat.cfname); | 
| 223 | return(-1); | 
| 224 | } | 
| 225 | setours:                                        /* set our values */ | 
| 226 | strcpy(astat.host, myhostname()); | 
| 227 | astat.pid = getpid(); | 
| 228 | strcpy(astat.cfname, cfname); | 
| 229 | return(0); | 
| 230 | fmterr: | 
| 231 | fprintf(stderr, "%s: format error in status file \"%s\"\n", | 
| 232 | progname, buf); | 
| 233 | fclose(fp); | 
| 234 | return(-1); | 
| 235 | } | 
| 236 |  | 
| 237 |  | 
| 238 | putastat()                      /* put out current status */ | 
| 239 | { | 
| 240 | char    buf[256]; | 
| 241 | FILE    *fp; | 
| 242 |  | 
| 243 | if (noaction) | 
| 244 | return; | 
| 245 | sprintf(buf, "%s/%s", vval(DIRECTORY), SFNAME); | 
| 246 | if ((fp = fopen(buf, "w")) == NULL) { | 
| 247 | perror(buf); | 
| 248 | quit(1); | 
| 249 | } | 
| 250 | fprintf(fp, "Control host: %s\n", astat.host); | 
| 251 | fprintf(fp, "Control PID: %d\n", astat.pid); | 
| 252 | fprintf(fp, "Control file: %s\n", astat.cfname); | 
| 253 | fprintf(fp, "Next render: %d\n", astat.rnext); | 
| 254 | fprintf(fp, "Next filter: %d\n", astat.fnext); | 
| 255 | fprintf(fp, "Next transfer: %d\n", astat.tnext); | 
| 256 | fclose(fp); | 
| 257 | } | 
| 258 |  | 
| 259 |  | 
| 260 | checkdir()                      /* make sure we have our directory */ | 
| 261 | { | 
| 262 | struct stat     stb; | 
| 263 |  | 
| 264 | if (!vdef(DIRECTORY)) { | 
| 265 | fprintf(stderr, "%s: %s undefined\n", | 
| 266 | progname, vnam(DIRECTORY)); | 
| 267 | quit(1); | 
| 268 | } | 
| 269 | if (stat(vval(DIRECTORY), &stb) == -1) { | 
| 270 | if (errno == ENOENT && mkdir(vval(DIRECTORY), 0777) == 0) | 
| 271 | return; | 
| 272 | perror(vval(DIRECTORY)); | 
| 273 | quit(1); | 
| 274 | } | 
| 275 | if (!(stb.st_mode & S_IFDIR)) { | 
| 276 | fprintf(stderr, "%s: not a directory\n", vval(DIRECTORY)); | 
| 277 | quit(1); | 
| 278 | } | 
| 279 | } | 
| 280 |  | 
| 281 |  | 
| 282 | setdefaults()                   /* set default values */ | 
| 283 | { | 
| 284 | char    buf[256]; | 
| 285 |  | 
| 286 | if (vdef(ANIMATE)) { | 
| 287 | vval(OCTREE) = NULL; | 
| 288 | vdef(OCTREE) = 0; | 
| 289 | } else if (!vdef(OCTREE)) { | 
| 290 | fprintf(stderr, "%s: either %s or %s must be defined\n", | 
| 291 | progname, vnam(OCTREE), vnam(ANIMATE)); | 
| 292 | quit(1); | 
| 293 | } | 
| 294 | if (!vdef(VIEWFILE)) { | 
| 295 | fprintf(stderr, "%s: %s undefined\n", progname, vnam(VIEWFILE)); | 
| 296 | quit(1); | 
| 297 | } | 
| 298 | if (!vdef(HOST)) { | 
| 299 | vval(HOST) = LHOSTNAME; | 
| 300 | vdef(HOST)++; | 
| 301 | } | 
| 302 | if (!vdef(START)) { | 
| 303 | vval(START) = "1"; | 
| 304 | vdef(START)++; | 
| 305 | } | 
| 306 | if (!vdef(END)) { | 
| 307 | sprintf(buf, "%d", countviews()+vint(START)-1); | 
| 308 | vval(END) = savqstr(buf); | 
| 309 | vdef(END)++; | 
| 310 | } | 
| 311 | if (vint(END) < vint(START)) { | 
| 312 | fprintf(stderr, "%s: ending frame less than starting frame\n", | 
| 313 | progname); | 
| 314 | quit(1); | 
| 315 | } | 
| 316 | if (!vdef(BASENAME)) { | 
| 317 | sprintf(buf, "%s/frame%%03d", vval(DIRECTORY)); | 
| 318 | vval(BASENAME) = savqstr(buf); | 
| 319 | vdef(BASENAME)++; | 
| 320 | } | 
| 321 | if (!vdef(RESOLUTION)) { | 
| 322 | vval(RESOLUTION) = "640"; | 
| 323 | vdef(RESOLUTION)++; | 
| 324 | } | 
| 325 | if (!vdef(OVERSAMP)) { | 
| 326 | vval(OVERSAMP) = "2"; | 
| 327 | vdef(OVERSAMP)++; | 
| 328 | } | 
| 329 | if (!vdef(INTERP)) { | 
| 330 | vval(INTERP) = "0"; | 
| 331 | vdef(INTERP)++; | 
| 332 | } | 
| 333 | if (!vdef(MBLUR)) { | 
| 334 | vval(MBLUR) = "0"; | 
| 335 | vdef(MBLUR)++; | 
| 336 | } | 
| 337 | if (!vdef(RTRACE)) { | 
| 338 | vval(RTRACE) = "F"; | 
| 339 | vdef(RTRACE)++; | 
| 340 | } | 
| 341 | if (!vdef(DISKSPACE)) { | 
| 342 | if (!nowarn) | 
| 343 | fprintf(stderr, | 
| 344 | "%s: warning - no %s setting, assuming 100 Mbytes available\n", | 
| 345 | progname, vnam(DISKSPACE)); | 
| 346 | vval(DISKSPACE) = "100"; | 
| 347 | vdef(DISKSPACE)++; | 
| 348 | } | 
| 349 | /* append rendering options */ | 
| 350 | if (vdef(RENDER)) | 
| 351 | sprintf(rendopt+strlen(rendopt), " %s", vval(RENDER)); | 
| 352 | } | 
| 353 |  | 
| 354 |  | 
| 355 | sethosts()                      /* set up process servers */ | 
| 356 | { | 
| 357 | extern char     *iskip(); | 
| 358 | char    buf[256], *dir, *uname; | 
| 359 | int     np; | 
| 360 | register char   *cp; | 
| 361 | int     i; | 
| 362 |  | 
| 363 | npslots = 0; | 
| 364 | if (noaction) | 
| 365 | return; | 
| 366 | for (i = 0; i < vdef(HOST); i++) {      /* add each host */ | 
| 367 | dir = uname = NULL; | 
| 368 | np = 1; | 
| 369 | strcpy(cp=buf, nvalue(HOST, i));        /* copy to buffer */ | 
| 370 | cp = sskip(cp);                         /* skip host name */ | 
| 371 | while (isspace(*cp)) | 
| 372 | *cp++ = '\0'; | 
| 373 | if (*cp) {                              /* has # processes? */ | 
| 374 | np = atoi(cp); | 
| 375 | if ((cp = iskip(cp)) == NULL || (*cp && !isspace(*cp))) | 
| 376 | badvalue(HOST); | 
| 377 | while (isspace(*cp)) | 
| 378 | cp++; | 
| 379 | if (*cp) {                      /* has directory? */ | 
| 380 | dir = cp; | 
| 381 | cp = sskip(cp);                 /* skip dir. */ | 
| 382 | while (isspace(*cp)) | 
| 383 | *cp++ = '\0'; | 
| 384 | if (*cp) {                      /* has user? */ | 
| 385 | uname = cp; | 
| 386 | if (*sskip(cp)) | 
| 387 | badvalue(HOST); | 
| 388 | } | 
| 389 | } | 
| 390 | } | 
| 391 | if (addpserver(buf, dir, uname, np) == NULL) { | 
| 392 | if (!nowarn) | 
| 393 | fprintf(stderr, | 
| 394 | "%s: cannot execute on host \"%s\"\n", | 
| 395 | progname, buf); | 
| 396 | } else | 
| 397 | npslots += np; | 
| 398 | } | 
| 399 | if (npslots == 0) { | 
| 400 | fprintf(stderr, "%s: no working process servers\n", progname); | 
| 401 | quit(1); | 
| 402 | } | 
| 403 | pslot = (struct pslot *)calloc(npslots, sizeof(struct pslot)); | 
| 404 | if (pslot == NULL) { | 
| 405 | perror("malloc"); | 
| 406 | quit(1); | 
| 407 | } | 
| 408 | } | 
| 409 |  | 
| 410 |  | 
| 411 | getradfile(rfargs)              /* run rad and get needed variables */ | 
| 412 | char    *rfargs; | 
| 413 | { | 
| 414 | static short    mvar[] = {OCTREE,PFILT,RESOLUTION,EXPOSURE,-1}; | 
| 415 | char    combuf[256]; | 
| 416 | register int    i; | 
| 417 | register char   *cp; | 
| 418 | /* create rad command */ | 
| 419 | sprintf(rendopt, " @%s/render.opt", vval(DIRECTORY)); | 
| 420 | sprintf(combuf, | 
| 421 | "rad -v 0 -s -e -w %s OPTFILE=%s | egrep '^[ \t]*(NOMATCH", | 
| 422 | rfargs, rendopt+2); | 
| 423 | cp = combuf; | 
| 424 | while (*cp) cp++;               /* match unset variables */ | 
| 425 | for (i = 0; mvar[i] >= 0; i++) | 
| 426 | if (!vdef(mvar[i])) { | 
| 427 | *cp++ = '|'; | 
| 428 | strcpy(cp, vnam(mvar[i])); | 
| 429 | while (*cp) cp++; | 
| 430 | } | 
| 431 | sprintf(cp, ")[ \t]*=' > %s/radset.var", vval(DIRECTORY)); | 
| 432 | cp += 11;                       /* point to file name */ | 
| 433 | if (system(combuf)) { | 
| 434 | fprintf(stderr, "%s: error executing rad command:\n\t%s\n", | 
| 435 | progname, combuf); | 
| 436 | quit(1); | 
| 437 | } | 
| 438 | loadvars(cp);                   /* load variables and remove file */ | 
| 439 | unlink(cp); | 
| 440 | } | 
| 441 |  | 
| 442 |  | 
| 443 | animate()                       /* run animation */ | 
| 444 | { | 
| 445 | int     xres, yres; | 
| 446 | float   pa, mult; | 
| 447 | int     frames_batch; | 
| 448 | register int    i; | 
| 449 | double  d1, d2; | 
| 450 | /* compute rpict resolution */ | 
| 451 | i = sscanf(vval(RESOLUTION), "%d %d %f", &xres, &yres, &pa); | 
| 452 | mult = vflt(OVERSAMP); | 
| 453 | if (i == 3) { | 
| 454 | sprintf(rresopt, "-x %d -y %d -pa %f", (int)(mult*xres), | 
| 455 | (int)(mult*yres), pa); | 
| 456 | sprintf(fresopt, "-x %d -y %d -pa %f", xres, yres, pa); | 
| 457 | } else if (i) { | 
| 458 | if (i == 1) yres = xres; | 
| 459 | sprintf(rresopt, "-x %d -y %d", (int)(mult*xres), | 
| 460 | (int)(mult*yres)); | 
| 461 | sprintf(fresopt, "-x %d -y %d -pa 1", xres, yres); | 
| 462 | } else | 
| 463 | badvalue(RESOLUTION); | 
| 464 | /* consistency checks */ | 
| 465 | if (vdef(ANIMATE)) { | 
| 466 | if (vint(INTERP)) { | 
| 467 | if (!nowarn) | 
| 468 | fprintf(stderr, | 
| 469 | "%s: resetting %s=0 for animation\n", | 
| 470 | progname, vnam(INTERP)); | 
| 471 | vval(INTERP) = "0"; | 
| 472 | } | 
| 473 | if (atoi(vval(MBLUR))) {        /* can't handle this yet */ | 
| 474 | if (!nowarn) | 
| 475 | fprintf(stderr, | 
| 476 | "%s: resetting %s=0 for animation\n", | 
| 477 | progname, vnam(MBLUR)); | 
| 478 | vval(MBLUR) = "0"; | 
| 479 | } | 
| 480 | } | 
| 481 | /* figure # frames per batch */ | 
| 482 | d1 = mult*xres*mult*yres*4;             /* space for orig. picture */ | 
| 483 | if ((i=vint(INTERP)) || atoi(vval(MBLUR))) | 
| 484 | d1 += mult*xres*mult*yres*4;    /* space for z-buffer */ | 
| 485 | d2 = xres*yres*4;                       /* space for final picture */ | 
| 486 | frames_batch = (i+1)*(vflt(DISKSPACE)*1048576.-d1)/(d1+i*d2); | 
| 487 | if (frames_batch < i+2) { | 
| 488 | fprintf(stderr, "%s: insufficient disk space allocated\n", | 
| 489 | progname); | 
| 490 | quit(1); | 
| 491 | } | 
| 492 | /* initialize archive argument list */ | 
| 493 | i = 16; | 
| 494 | if (vdef(ARCHIVE) && strlen(vval(ARCHIVE)) > i) | 
| 495 | i = strlen(vval(ARCHIVE)); | 
| 496 | arcnext = arcfirst = arcargs + i; | 
| 497 | /* initialize status file */ | 
| 498 | if (astat.rnext == 0) | 
| 499 | astat.rnext = astat.fnext = astat.tnext = vint(START); | 
| 500 | putastat(); | 
| 501 | /* render in batches */ | 
| 502 | while (astat.tnext <= vint(END)) { | 
| 503 | renderframes(frames_batch); | 
| 504 | filterframes(); | 
| 505 | transferframes(); | 
| 506 | } | 
| 507 | /* mark status as finished */ | 
| 508 | astat.pid = 0; | 
| 509 | putastat(); | 
| 510 | /* close open files */ | 
| 511 | getview(0); | 
| 512 | getexp(0); | 
| 513 | } | 
| 514 |  | 
| 515 |  | 
| 516 | renderframes(nframes)           /* render next nframes frames */ | 
| 517 | int     nframes; | 
| 518 | { | 
| 519 | static char     vendbuf[16]; | 
| 520 | VIEW    *vp; | 
| 521 | FILE    *fp = NULL; | 
| 522 | char    vfname[128]; | 
| 523 | int     lastframe; | 
| 524 | register int    i; | 
| 525 |  | 
| 526 | if (astat.tnext < astat.rnext)  /* other work to do first */ | 
| 527 | return; | 
| 528 | /* create batch view file */ | 
| 529 | if (!vdef(ANIMATE)) { | 
| 530 | sprintf(vfname, "%s/anim.vf", vval(DIRECTORY)); | 
| 531 | if ((fp = fopen(vfname, "w")) == NULL) { | 
| 532 | perror(vfname); | 
| 533 | quit(1); | 
| 534 | } | 
| 535 | } | 
| 536 | /* bound batch properly */ | 
| 537 | lastframe = astat.rnext + nframes - 1; | 
| 538 | if ((lastframe-1) % (vint(INTERP)+1))   /* need even interval */ | 
| 539 | lastframe += vint(INTERP)+1 - ((lastframe-1)%(vint(INTERP)+1)); | 
| 540 | if (lastframe > vint(END))              /* check for end */ | 
| 541 | lastframe = vint(END); | 
| 542 | /* render each view */ | 
| 543 | for (i = astat.rnext; i <= lastframe; i++) { | 
| 544 | if ((vp = getview(i)) == NULL) { | 
| 545 | if (!nowarn) | 
| 546 | fprintf(stderr, | 
| 547 | "%s: ran out of views before last frame\n", | 
| 548 | progname); | 
| 549 | sprintf(vval(END)=vendbuf, "%d", i-1); | 
| 550 | lastframe = i - 1; | 
| 551 | break; | 
| 552 | } | 
| 553 | if (vdef(ANIMATE))              /* animate frame */ | 
| 554 | animrend(i, vp); | 
| 555 | else {                          /* else record it */ | 
| 556 | fputs(VIEWSTR, fp); | 
| 557 | fprintview(vp, fp); | 
| 558 | putc('\n', fp); | 
| 559 | } | 
| 560 | } | 
| 561 | if (vdef(ANIMATE))              /* wait for renderings to finish */ | 
| 562 | bwait(0); | 
| 563 | else {                          /* else if walk-through */ | 
| 564 | fclose(fp);             /* close view file */ | 
| 565 | walkwait(astat.rnext, lastframe, vfname);       /* walk it */ | 
| 566 | unlink(vfname);         /* remove view file */ | 
| 567 | } | 
| 568 | astat.rnext = i;                /* update status */ | 
| 569 | putastat(); | 
| 570 | } | 
| 571 |  | 
| 572 |  | 
| 573 | filterframes()                          /* catch up with filtering */ | 
| 574 | { | 
| 575 | VIEW    *vp; | 
| 576 | register int    i; | 
| 577 |  | 
| 578 | if (astat.tnext < astat.fnext)  /* other work to do first */ | 
| 579 | return; | 
| 580 | /* filter each view */ | 
| 581 | for (i = astat.fnext; i < astat.rnext; i++) { | 
| 582 | if ((vp = getview(i)) == NULL) {        /* get view i */ | 
| 583 | fprintf(stderr, | 
| 584 | "%s: unexpected error reading view for frame %d\n", | 
| 585 | progname, i); | 
| 586 | quit(1); | 
| 587 | } | 
| 588 | dofilt(i, vp, getexp(i), 0);            /* filter frame */ | 
| 589 | } | 
| 590 | bwait(0);                       /* wait for filter processes */ | 
| 591 | archive();                      /* archive originals */ | 
| 592 | astat.fnext = i;                /* update status */ | 
| 593 | putastat(); | 
| 594 | } | 
| 595 |  | 
| 596 |  | 
| 597 | transferframes()                        /* catch up with picture transfers */ | 
| 598 | { | 
| 599 | char    combuf[10240]; | 
| 600 | register char   *cp; | 
| 601 | register int    i; | 
| 602 |  | 
| 603 | if (astat.tnext >= astat.fnext) /* nothing to do, yet */ | 
| 604 | return; | 
| 605 | if (!vdef(TRANSFER)) {          /* no transfer function -- leave 'em */ | 
| 606 | astat.tnext = astat.fnext; | 
| 607 | putastat();             /* update status */ | 
| 608 | return; | 
| 609 | } | 
| 610 | strcpy(combuf, vval(TRANSFER)); /* start transfer command */ | 
| 611 | cp = combuf + strlen(combuf); | 
| 612 | /* make argument list */ | 
| 613 | for (i = astat.tnext; i < astat.fnext; i++) { | 
| 614 | *cp++ = ' '; | 
| 615 | sprintf(cp, vval(BASENAME), i); | 
| 616 | while (*cp) cp++; | 
| 617 | strcpy(cp, ".pic"); | 
| 618 | cp += 4; | 
| 619 | } | 
| 620 | if (runcom(combuf)) {           /* transfer frames */ | 
| 621 | fprintf(stderr, "%s: error running transfer command\n", | 
| 622 | progname); | 
| 623 | quit(1); | 
| 624 | } | 
| 625 | astat.tnext = i;                /* update status */ | 
| 626 | putastat(); | 
| 627 | } | 
| 628 |  | 
| 629 |  | 
| 630 | animrend(frame, vp)                     /* start animation frame */ | 
| 631 | int     frame; | 
| 632 | VIEW    *vp; | 
| 633 | { | 
| 634 | extern int      recover(); | 
| 635 | char    combuf[2048]; | 
| 636 | char    fname[128]; | 
| 637 |  | 
| 638 | sprintf(fname, vval(BASENAME), frame); | 
| 639 | strcat(fname, ".unf"); | 
| 640 | if (access(fname, F_OK) == 0) | 
| 641 | return; | 
| 642 | sprintf(combuf, "%s %d | rpict%s%s -w0 %s > %s", vval(ANIMATE), frame, | 
| 643 | rendopt, viewopt(vp), rresopt, fname); | 
| 644 | bruncom(combuf, frame, recover);        /* run in background */ | 
| 645 | } | 
| 646 |  | 
| 647 |  | 
| 648 | walkwait(first, last, vfn)              /* walk-through frames */ | 
| 649 | int     first, last; | 
| 650 | char    *vfn; | 
| 651 | { | 
| 652 | char    combuf[2048]; | 
| 653 | char    *inspoint; | 
| 654 | register int    i; | 
| 655 |  | 
| 656 | if (!noaction && vint(INTERP))          /* create dummy frames */ | 
| 657 | for (i = first; i <= last; i++) | 
| 658 | if (i < vint(END) && (i-1) % (vint(INTERP)+1)) { | 
| 659 | sprintf(combuf, vval(BASENAME), i); | 
| 660 | strcat(combuf, ".unf"); | 
| 661 | close(open(combuf, O_RDONLY|O_CREAT, 0666)); | 
| 662 | } | 
| 663 | /* create command */ | 
| 664 | sprintf(combuf, "rpict%s -w0", rendopt); | 
| 665 | if (vint(INTERP) || atoi(vval(MBLUR))) | 
| 666 | sprintf(combuf+strlen(combuf), " -z %s.zbf", vval(BASENAME)); | 
| 667 | sprintf(combuf+strlen(combuf), " -o %s.unf %s -S %d", | 
| 668 | vval(BASENAME), rresopt, first); | 
| 669 | inspoint = combuf + strlen(combuf); | 
| 670 | sprintf(inspoint, " %s < %s", vval(OCTREE), vfn); | 
| 671 | /* run in parallel */ | 
| 672 | i = (last-first+1)/(vint(INTERP)+1); | 
| 673 | if (i < 1) i = 1; | 
| 674 | if (pruncom(combuf, inspoint, i)) { | 
| 675 | fprintf(stderr, "%s: error rendering frames %d through %d\n", | 
| 676 | progname, first, last); | 
| 677 | quit(1); | 
| 678 | } | 
| 679 | if (!noaction && vint(INTERP))          /* remove dummy frames */ | 
| 680 | for (i = first; i <= last; i++) | 
| 681 | if (i < vint(END) && (i-1) % (vint(INTERP)+1)) { | 
| 682 | sprintf(combuf, vval(BASENAME), i); | 
| 683 | strcat(combuf, ".unf"); | 
| 684 | unlink(combuf); | 
| 685 | } | 
| 686 | } | 
| 687 |  | 
| 688 |  | 
| 689 | int | 
| 690 | recover(frame)                          /* recover the specified frame */ | 
| 691 | int     frame; | 
| 692 | { | 
| 693 | static int      *rfrm;          /* list of recovered frames */ | 
| 694 | static int      nrfrms = 0; | 
| 695 | char    combuf[2048]; | 
| 696 | char    fname[128]; | 
| 697 | register char   *cp; | 
| 698 | register int    i; | 
| 699 | /* check to see if recovered already */ | 
| 700 | for (i = nrfrms; i--; ) | 
| 701 | if (rfrm[i] == frame) | 
| 702 | return(0); | 
| 703 | /* build command */ | 
| 704 | sprintf(fname, vval(BASENAME), frame); | 
| 705 | if (vdef(ANIMATE)) | 
| 706 | sprintf(combuf, "%s %d | rpict%s -w0", | 
| 707 | vval(ANIMATE), frame, rendopt); | 
| 708 | else | 
| 709 | sprintf(combuf, "rpict%s -w0", rendopt); | 
| 710 | cp = combuf + strlen(combuf); | 
| 711 | if (vint(INTERP) || atoi(vval(MBLUR))) { | 
| 712 | sprintf(cp, " -z %s.zbf", fname); | 
| 713 | while (*cp) cp++; | 
| 714 | } | 
| 715 | sprintf(cp, " -ro %s.unf", fname); | 
| 716 | while (*cp) cp++; | 
| 717 | if (!vdef(ANIMATE)) { | 
| 718 | *cp++ = ' '; | 
| 719 | strcpy(cp, vval(OCTREE)); | 
| 720 | } | 
| 721 | if (runcom(combuf))             /* run command */ | 
| 722 | return(1); | 
| 723 | /* add frame to recovered list */ | 
| 724 | if (nrfrms) | 
| 725 | rfrm = (int *)realloc((char *)rfrm, (nrfrms+1)*sizeof(int)); | 
| 726 | else | 
| 727 | rfrm = (int *)malloc(sizeof(int)); | 
| 728 | if (rfrm == NULL) { | 
| 729 | perror("malloc"); | 
| 730 | quit(1); | 
| 731 | } | 
| 732 | rfrm[nrfrms++] = frame; | 
| 733 | return(0); | 
| 734 | } | 
| 735 |  | 
| 736 |  | 
| 737 | int | 
| 738 | frecover(frame)                         /* recover filtered frame */ | 
| 739 | int     frame; | 
| 740 | { | 
| 741 | VIEW    *vp; | 
| 742 | char    *ex; | 
| 743 |  | 
| 744 | vp = getview(frame); | 
| 745 | ex = getexp(frame); | 
| 746 | if (dofilt(frame, vp, ex, 2) && dofilt(frame, vp, ex, 1)) | 
| 747 | return(1); | 
| 748 | return(0); | 
| 749 | } | 
| 750 |  | 
| 751 |  | 
| 752 | archive()                       /* archive and remove renderings */ | 
| 753 | { | 
| 754 | #define RMCOML  (sizeof(rmcom)-1) | 
| 755 | static char     rmcom[] = "rm -f"; | 
| 756 | register int    i; | 
| 757 |  | 
| 758 | if (arcnext == arcfirst) | 
| 759 | return;                         /* nothing to do */ | 
| 760 | if (vdef(ARCHIVE)) {                    /* run archive command */ | 
| 761 | i = strlen(vval(ARCHIVE)); | 
| 762 | strncpy(arcfirst-i, vval(ARCHIVE), i); | 
| 763 | if (runcom(arcfirst-i)) { | 
| 764 | fprintf(stderr, "%s: error running archive command\n", | 
| 765 | progname); | 
| 766 | quit(1); | 
| 767 | } | 
| 768 | } | 
| 769 | /* run remove command */ | 
| 770 | strncpy(arcfirst-RMCOML, rmcom, RMCOML); | 
| 771 | runcom(arcfirst-RMCOML); | 
| 772 | arcnext = arcfirst;                     /* reset argument list */ | 
| 773 | #undef RMCOML | 
| 774 | } | 
| 775 |  | 
| 776 |  | 
| 777 | int | 
| 778 | dofilt(frame, vp, ep, rvr)                      /* filter frame */ | 
| 779 | int     frame; | 
| 780 | VIEW    *vp; | 
| 781 | char    *ep; | 
| 782 | int     rvr; | 
| 783 | { | 
| 784 | extern int      frecover(); | 
| 785 | static int      iter = 0; | 
| 786 | char    fnbefore[128], fnafter[128]; | 
| 787 | char    combuf[1024], fname0[128], fname1[128]; | 
| 788 | int     usepinterp, usepfilt, nora_rgbe; | 
| 789 | int     frseq[2]; | 
| 790 | /* check what is needed */ | 
| 791 | usepinterp = atoi(vval(MBLUR)); | 
| 792 | usepfilt = pfiltalways | ep==NULL; | 
| 793 | if (ep != NULL && !strcmp(ep, "1")) | 
| 794 | ep = "+0"; | 
| 795 | nora_rgbe = strcmp(vval(OVERSAMP),"1") || ep==NULL || | 
| 796 | *ep != '+' || *ep != '-' || !isint(ep); | 
| 797 | /* compute rendered views */ | 
| 798 | frseq[0] = frame - ((frame-1) % (vint(INTERP)+1)); | 
| 799 | frseq[1] = frseq[0] + vint(INTERP) + 1; | 
| 800 | if (frseq[1] > vint(END)) | 
| 801 | frseq[1] = vint(END); | 
| 802 | if (frseq[1] == frame) {                        /* pfilt only */ | 
| 803 | frseq[0] = frseq[1]; | 
| 804 | usepinterp = 0;                 /* update what's needed */ | 
| 805 | usepfilt |= nora_rgbe; | 
| 806 | } else if (frseq[0] == frame) {         /* no interpolation needed */ | 
| 807 | if (!rvr && frame > 1+vint(INTERP)) {   /* archive previous */ | 
| 808 | *arcnext++ = ' '; | 
| 809 | sprintf(arcnext, vval(BASENAME), frame-vint(INTERP)-1); | 
| 810 | while (*arcnext) arcnext++; | 
| 811 | strcpy(arcnext, ".unf"); | 
| 812 | arcnext += 4; | 
| 813 | if (usepinterp || vint(INTERP)) {       /* and z-buf */ | 
| 814 | *arcnext++ = ' '; | 
| 815 | sprintf(arcnext, vval(BASENAME), | 
| 816 | frame-vint(INTERP)-1); | 
| 817 | while (*arcnext) arcnext++; | 
| 818 | strcpy(arcnext, ".zbf"); | 
| 819 | arcnext += 4; | 
| 820 | } | 
| 821 | } | 
| 822 | if (!usepinterp)                /* update what's needed */ | 
| 823 | usepfilt |= nora_rgbe; | 
| 824 | } else                                  /* interpolation needed */ | 
| 825 | usepinterp++; | 
| 826 | if (frseq[1] >= astat.rnext)            /* next batch unavailable */ | 
| 827 | frseq[1] = frseq[0]; | 
| 828 | sprintf(fnbefore, vval(BASENAME), frseq[0]); | 
| 829 | sprintf(fnafter, vval(BASENAME), frseq[1]); | 
| 830 | if (rvr == 1 && recover(frseq[0]))      /* recover before frame? */ | 
| 831 | return(1); | 
| 832 | /* generate command */ | 
| 833 | if (usepinterp) {                       /* using pinterp */ | 
| 834 | if (rvr == 2 && recover(frseq[1]))      /* recover after? */ | 
| 835 | return(1); | 
| 836 | if (atoi(vval(MBLUR))) { | 
| 837 | FILE    *fp;            /* motion blurring */ | 
| 838 | sprintf(fname0, "%s/vw0%c", vval(DIRECTORY), | 
| 839 | 'a'+(iter%26)); | 
| 840 | if ((fp = fopen(fname0, "w")) == NULL) { | 
| 841 | perror(fname0); quit(1); | 
| 842 | } | 
| 843 | fputs(VIEWSTR, fp); | 
| 844 | fprintview(vp, fp); | 
| 845 | putc('\n', fp); fclose(fp); | 
| 846 | if ((vp = getview(frame+1)) == NULL) { | 
| 847 | fprintf(stderr, | 
| 848 | "%s: unexpected error reading view for frame %d\n", | 
| 849 | progname, frame+1); | 
| 850 | quit(1); | 
| 851 | } | 
| 852 | sprintf(fname1, "%s/vw1%c", vval(DIRECTORY), | 
| 853 | 'a'+(iter%26)); | 
| 854 | if ((fp = fopen(fname1, "w")) == NULL) { | 
| 855 | perror(fname1); quit(1); | 
| 856 | } | 
| 857 | fputs(VIEWSTR, fp); | 
| 858 | fprintview(vp, fp); | 
| 859 | putc('\n', fp); fclose(fp); | 
| 860 | sprintf(combuf, | 
| 861 | "(pmblur %s %d %s %s; rm -f %s %s) | pinterp -B", | 
| 862 | *sskip(vval(MBLUR)) ? sskip2(vval(MBLUR),1) : "1", | 
| 863 | atoi(vval(MBLUR)), | 
| 864 | fname0, fname1, fname0, fname1); | 
| 865 | iter++; | 
| 866 | } else                          /* no blurring */ | 
| 867 | strcpy(combuf, "pinterp"); | 
| 868 | strcat(combuf, viewopt(vp)); | 
| 869 | if (vbool(RTRACE)) | 
| 870 | sprintf(combuf+strlen(combuf), " -ff -fr '%s -w0 %s'", | 
| 871 | rendopt, vval(OCTREE)); | 
| 872 | if (vdef(PINTERP)) | 
| 873 | sprintf(combuf+strlen(combuf), " %s", vval(PINTERP)); | 
| 874 | if (usepfilt) | 
| 875 | sprintf(combuf+strlen(combuf), " %s", rresopt); | 
| 876 | else | 
| 877 | sprintf(combuf+strlen(combuf), " %s -e %s", | 
| 878 | fresopt, ep); | 
| 879 | sprintf(combuf+strlen(combuf), " %s.unf %s.zbf", | 
| 880 | fnbefore, fnbefore); | 
| 881 | if (frseq[1] != frseq[0]) | 
| 882 | sprintf(combuf+strlen(combuf), " %s.unf %s.zbf", | 
| 883 | fnafter, fnafter); | 
| 884 | if (usepfilt) {                 /* also pfilt */ | 
| 885 | if (vdef(PFILT)) | 
| 886 | sprintf(combuf+strlen(combuf), " | pfilt %s", | 
| 887 | vval(PFILT)); | 
| 888 | else | 
| 889 | strcat(combuf, " | pfilt"); | 
| 890 | if (ep != NULL) | 
| 891 | sprintf(combuf+strlen(combuf), " -1 -e %s %s", | 
| 892 | ep, fresopt); | 
| 893 | else | 
| 894 | sprintf(combuf+strlen(combuf), " %s", fresopt); | 
| 895 | } | 
| 896 | } else if (usepfilt) {                  /* pfilt only */ | 
| 897 | if (rvr == 2) | 
| 898 | return(1); | 
| 899 | if (vdef(PFILT)) | 
| 900 | sprintf(combuf, "pfilt %s", vval(PFILT)); | 
| 901 | else | 
| 902 | strcpy(combuf, "pfilt"); | 
| 903 | if (ep != NULL) | 
| 904 | sprintf(combuf+strlen(combuf), " -1 -e %s %s %s.unf", | 
| 905 | ep, fresopt, fnbefore); | 
| 906 | else | 
| 907 | sprintf(combuf+strlen(combuf), " %s %s.unf", | 
| 908 | fresopt, fnbefore); | 
| 909 | } else {                                /* else just check it */ | 
| 910 | if (rvr == 2) | 
| 911 | return(1); | 
| 912 | sprintf(combuf, "ra_rgbe -e %s -r %s.unf", ep, fnbefore); | 
| 913 | } | 
| 914 | /* output file name */ | 
| 915 | sprintf(fname0, vval(BASENAME), frame); | 
| 916 | sprintf(combuf+strlen(combuf), " > %s.pic", fname0); | 
| 917 | if (rvr)                                /* in recovery */ | 
| 918 | return(runcom(combuf)); | 
| 919 | bruncom(combuf, frame, frecover);       /* else run in background */ | 
| 920 | return(0); | 
| 921 | } | 
| 922 |  | 
| 923 |  | 
| 924 | VIEW * | 
| 925 | getview(n)                      /* get view number n */ | 
| 926 | int     n; | 
| 927 | { | 
| 928 | static FILE     *viewfp = NULL;         /* view file pointer */ | 
| 929 | static int      viewnum = 0;            /* current view number */ | 
| 930 | static VIEW     curview = STDVIEW;      /* current view */ | 
| 931 | char    linebuf[256]; | 
| 932 |  | 
| 933 | if (n == 0) {                   /* signal to close file and clean up */ | 
| 934 | if (viewfp != NULL) { | 
| 935 | fclose(viewfp); | 
| 936 | viewfp = NULL; | 
| 937 | viewnum = 0; | 
| 938 | copystruct(&curview, &stdview); | 
| 939 | } | 
| 940 | return(NULL); | 
| 941 | } | 
| 942 | if (viewfp == NULL) {           /* open file */ | 
| 943 | if ((viewfp = fopen(vval(VIEWFILE), "r")) == NULL) { | 
| 944 | perror(vval(VIEWFILE)); | 
| 945 | quit(1); | 
| 946 | } | 
| 947 | } else if (n < viewnum) {       /* rewind file */ | 
| 948 | if (viewnum == 1 && feof(viewfp)) | 
| 949 | return(&curview);               /* just one view */ | 
| 950 | if (fseek(viewfp, 0L, 0) == EOF) { | 
| 951 | perror(vval(VIEWFILE)); | 
| 952 | quit(1); | 
| 953 | } | 
| 954 | copystruct(&curview, &stdview); | 
| 955 | viewnum = 0; | 
| 956 | } | 
| 957 | while (n > viewnum) {           /* scan to desired view */ | 
| 958 | if (fgets(linebuf, sizeof(linebuf), viewfp) == NULL) | 
| 959 | return(viewnum==1 ? &curview : NULL); | 
| 960 | if (isview(linebuf) && sscanview(&curview, linebuf) > 0) | 
| 961 | viewnum++; | 
| 962 | } | 
| 963 | return(&curview);               /* return it */ | 
| 964 | } | 
| 965 |  | 
| 966 |  | 
| 967 | int | 
| 968 | countviews()                    /* count views in view file */ | 
| 969 | { | 
| 970 | register int    n = 0; | 
| 971 |  | 
| 972 | while (getview(n+1) != NULL) | 
| 973 | n++; | 
| 974 | return(n); | 
| 975 | } | 
| 976 |  | 
| 977 |  | 
| 978 | char * | 
| 979 | getexp(n)                       /* get exposure for nth frame */ | 
| 980 | int     n; | 
| 981 | { | 
| 982 | extern char     *fskip(); | 
| 983 | static char     expval[32]; | 
| 984 | static FILE     *expfp = NULL; | 
| 985 | static long     *exppos; | 
| 986 | static int      curfrm; | 
| 987 | register char   *cp; | 
| 988 |  | 
| 989 | if (n == 0) {                           /* signal to close file */ | 
| 990 | if (expfp != NULL) { | 
| 991 | fclose(expfp); | 
| 992 | expfp = NULL; | 
| 993 | } | 
| 994 | return(NULL); | 
| 995 | } | 
| 996 | if (!vdef(EXPOSURE))                    /* no setting (auto) */ | 
| 997 | return(NULL); | 
| 998 | if (isflt(vval(EXPOSURE)))              /* always the same */ | 
| 999 | return(vval(EXPOSURE)); | 
| 1000 | if (expfp == NULL) {                    /* open exposure file */ | 
| 1001 | if ((expfp = fopen(vval(EXPOSURE), "r")) == NULL) { | 
| 1002 | fprintf(stderr, | 
| 1003 | "%s: cannot open exposure file \"%s\"\n", | 
| 1004 | progname, vval(EXPOSURE)); | 
| 1005 | quit(1); | 
| 1006 | } | 
| 1007 | curfrm = vint(END) + 1;         /* init lookup tab. */ | 
| 1008 | exppos = (long *)malloc(curfrm*sizeof(long *)); | 
| 1009 | if (exppos == NULL) { | 
| 1010 | perror(progname); | 
| 1011 | quit(1); | 
| 1012 | } | 
| 1013 | while (curfrm--) | 
| 1014 | exppos[curfrm] = -1L; | 
| 1015 | curfrm = 0; | 
| 1016 | } | 
| 1017 | /* find position in file */ | 
| 1018 | if (n-1 != curfrm && n != curfrm && exppos[n-1] >= 0 && | 
| 1019 | fseek(expfp, exppos[curfrm=n-1], 0) == EOF) { | 
| 1020 | fprintf(stderr, "%s: seek error on exposure file\n", progname); | 
| 1021 | quit(1); | 
| 1022 | } | 
| 1023 | while (n > curfrm) {                    /* read exposure */ | 
| 1024 | if (exppos[curfrm] < 0) | 
| 1025 | exppos[curfrm] = ftell(expfp); | 
| 1026 | if (fgets(expval, sizeof(expval), expfp) == NULL) { | 
| 1027 | fprintf(stderr, "%s: too few exposures\n", | 
| 1028 | vval(EXPOSURE)); | 
| 1029 | quit(1); | 
| 1030 | } | 
| 1031 | curfrm++; | 
| 1032 | cp = fskip(expval);                     /* check format */ | 
| 1033 | if (cp == NULL || *cp != '\n') { | 
| 1034 | fprintf(stderr, | 
| 1035 | "%s: exposure format error on line %d\n", | 
| 1036 | vval(EXPOSURE), curfrm); | 
| 1037 | quit(1); | 
| 1038 | } | 
| 1039 | *cp = '\0'; | 
| 1040 | } | 
| 1041 | return(expval);                         /* return value */ | 
| 1042 | } | 
| 1043 |  | 
| 1044 |  | 
| 1045 | struct pslot * | 
| 1046 | findpslot(pid)                  /* find or allocate a process slot */ | 
| 1047 | int     pid; | 
| 1048 | { | 
| 1049 | register struct pslot   *psempty = NULL; | 
| 1050 | register int    i; | 
| 1051 |  | 
| 1052 | for (i = 0; i < npslots; i++) {         /* look for match */ | 
| 1053 | if (pslot[i].pid == pid) | 
| 1054 | return(pslot+i); | 
| 1055 | if (psempty == NULL && pslot[i].pid == 0) | 
| 1056 | psempty = pslot+i; | 
| 1057 | } | 
| 1058 | return(psempty);                /* return emtpy slot (error if NULL) */ | 
| 1059 | } | 
| 1060 |  | 
| 1061 |  | 
| 1062 | int | 
| 1063 | donecom(ps, pn, status)         /* clean up after finished process */ | 
| 1064 | PSERVER *ps; | 
| 1065 | int     pn; | 
| 1066 | int     status; | 
| 1067 | { | 
| 1068 | register PROC   *pp; | 
| 1069 |  | 
| 1070 | pp = ps->proc + pn; | 
| 1071 | if (pp->elen) {                 /* pass errors */ | 
| 1072 | if (ps->hostname[0]) | 
| 1073 | fprintf(stderr, "%s: ", ps->hostname); | 
| 1074 | fprintf(stderr, "Error output from: %s\n", pp->com); | 
| 1075 | fputs(pp->errs, stderr); | 
| 1076 | fflush(stderr); | 
| 1077 | if (ps->hostname[0]) | 
| 1078 | status = 1;     /* because rsh doesn't return status */ | 
| 1079 | } | 
| 1080 | freestr(pp->com);               /* free command string */ | 
| 1081 | lastpid = pp->pid;              /* record PID for bwait() */ | 
| 1082 | lastpserver = ps;               /* record server for serverdown() */ | 
| 1083 | return(status); | 
| 1084 | } | 
| 1085 |  | 
| 1086 |  | 
| 1087 | int | 
| 1088 | serverdown()                    /* check status of last process server */ | 
| 1089 | { | 
| 1090 | if (pserverOK(lastpserver))     /* server still up? */ | 
| 1091 | return(0); | 
| 1092 | delpserver(lastpserver);        /* else delete it */ | 
| 1093 | if (pslist == NULL) { | 
| 1094 | fprintf(stderr, "%s: all process servers are down\n", | 
| 1095 | progname); | 
| 1096 | quit(1); | 
| 1097 | } | 
| 1098 | return(1); | 
| 1099 | } | 
| 1100 |  | 
| 1101 |  | 
| 1102 | int | 
| 1103 | bruncom(com, fout, rf)          /* run a command in the background */ | 
| 1104 | char    *com; | 
| 1105 | int     fout; | 
| 1106 | int     (*rf)(); | 
| 1107 | { | 
| 1108 | int     pid; | 
| 1109 | register struct pslot   *psl; | 
| 1110 |  | 
| 1111 | if (noaction) { | 
| 1112 | if (!silent) | 
| 1113 | printf("\t%s\n", com);  /* echo command */ | 
| 1114 | return(0); | 
| 1115 | } | 
| 1116 | /* else start it when we can */ | 
| 1117 | while ((pid = startjob(NULL, savestr(com), donecom)) == -1) | 
| 1118 | bwait(1); | 
| 1119 | if (!silent) {                          /* echo command */ | 
| 1120 | PSERVER *ps; | 
| 1121 | int     psn = pid; | 
| 1122 | ps = findjob(&psn); | 
| 1123 | printf("\t%s\n", com); | 
| 1124 | printf("\tProcess started on %s\n", phostname(ps)); | 
| 1125 | fflush(stdout); | 
| 1126 | } | 
| 1127 | psl = findpslot(pid);           /* record info. in appropriate slot */ | 
| 1128 | psl->pid = pid; | 
| 1129 | psl->fout = fout; | 
| 1130 | psl->rcvf = rf; | 
| 1131 | return(pid); | 
| 1132 | } | 
| 1133 |  | 
| 1134 |  | 
| 1135 | bwait(ncoms)                            /* wait for batch job(s) to finish */ | 
| 1136 | int     ncoms; | 
| 1137 | { | 
| 1138 | int     status; | 
| 1139 | register struct pslot   *psl; | 
| 1140 |  | 
| 1141 | if (noaction) | 
| 1142 | return; | 
| 1143 | while ((status = wait4job(NULL, -1)) != -1) { | 
| 1144 | psl = findpslot(lastpid); | 
| 1145 | if (status) {           /* attempt recovery */ | 
| 1146 | serverdown();   /* check server */ | 
| 1147 | if (psl->rcvf == NULL || (*psl->rcvf)(psl->fout)) { | 
| 1148 | fprintf(stderr, | 
| 1149 | "%s: error rendering frame %d\n", | 
| 1150 | progname, psl->fout); | 
| 1151 | quit(1); | 
| 1152 | } | 
| 1153 | } | 
| 1154 | psl->pid = 0;           /* free process slot */ | 
| 1155 | if (!--ncoms) | 
| 1156 | return;         /* done enough */ | 
| 1157 | } | 
| 1158 | } | 
| 1159 |  | 
| 1160 |  | 
| 1161 | int | 
| 1162 | pruncom(com, ppins, maxcopies)  /* run a command in parallel over network */ | 
| 1163 | char    *com, *ppins; | 
| 1164 | int     maxcopies; | 
| 1165 | { | 
| 1166 | int     retstatus = 0; | 
| 1167 | int     hostcopies; | 
| 1168 | char    com1buf[10240], *com1, *endcom1; | 
| 1169 | int     status; | 
| 1170 | register PSERVER        *ps; | 
| 1171 |  | 
| 1172 | if (!silent) | 
| 1173 | printf("\t%s\n", com);  /* echo command */ | 
| 1174 | if (noaction) | 
| 1175 | return(0); | 
| 1176 | fflush(stdout); | 
| 1177 | /* start jobs on each server */ | 
| 1178 | for (ps = pslist; ps != NULL; ps = ps->next) { | 
| 1179 | hostcopies = 0; | 
| 1180 | if (maxcopies > 1 && ps->nprocs > 1 && ppins != NULL) { | 
| 1181 | strcpy(com1=com1buf, com);      /* build -PP command */ | 
| 1182 | sprintf(com1+(ppins-com), " -PP %s/%s.persist", | 
| 1183 | vval(DIRECTORY), phostname(ps)); | 
| 1184 | strcat(com1, ppins); | 
| 1185 | endcom1 = com1 + strlen(com1); | 
| 1186 | sprintf(endcom1, "; kill `sed -n '1s/^[^ ]* //p' %s/%s.persist`", | 
| 1187 | vval(DIRECTORY), phostname(ps)); | 
| 1188 | } else { | 
| 1189 | com1 = com; | 
| 1190 | endcom1 = NULL; | 
| 1191 | } | 
| 1192 | while (maxcopies > 0 && | 
| 1193 | startjob(ps, savestr(com1), donecom) != -1) { | 
| 1194 | sleep(10); | 
| 1195 | hostcopies++; | 
| 1196 | maxcopies--; | 
| 1197 | if (endcom1 != NULL) | 
| 1198 | *endcom1 = '\0'; | 
| 1199 | } | 
| 1200 | if (!silent && hostcopies) { | 
| 1201 | if (hostcopies > 1) | 
| 1202 | printf("\t%d duplicate processes", hostcopies); | 
| 1203 | else | 
| 1204 | printf("\tProcess"); | 
| 1205 | printf(" started on %s\n", phostname(ps)); | 
| 1206 | fflush(stdout); | 
| 1207 | } | 
| 1208 | } | 
| 1209 | /* wait for jobs to finish */ | 
| 1210 | while ((status = wait4job(NULL, -1)) != -1) | 
| 1211 | if (status) | 
| 1212 | retstatus += !serverdown();     /* check server */ | 
| 1213 | return(retstatus); | 
| 1214 | } | 
| 1215 |  | 
| 1216 |  | 
| 1217 | runcom(cs)                      /* run a command locally and wait for it */ | 
| 1218 | char    *cs; | 
| 1219 | { | 
| 1220 | if (!silent)            /* echo it */ | 
| 1221 | printf("\t%s\n", cs); | 
| 1222 | if (noaction) | 
| 1223 | return(0); | 
| 1224 | fflush(stdout);         /* flush output and pass to shell */ | 
| 1225 | return(system(cs)); | 
| 1226 | } | 
| 1227 |  | 
| 1228 |  | 
| 1229 | rmfile(fn)                      /* remove a file */ | 
| 1230 | char    *fn; | 
| 1231 | { | 
| 1232 | if (!silent) | 
| 1233 | #ifdef MSDOS | 
| 1234 | printf("\tdel %s\n", fn); | 
| 1235 | #else | 
| 1236 | printf("\trm -f %s\n", fn); | 
| 1237 | #endif | 
| 1238 | if (noaction) | 
| 1239 | return(0); | 
| 1240 | return(unlink(fn)); | 
| 1241 | } | 
| 1242 |  | 
| 1243 |  | 
| 1244 | badvalue(vc)                    /* report bad variable value and exit */ | 
| 1245 | int     vc; | 
| 1246 | { | 
| 1247 | fprintf(stderr, "%s: bad value for variable '%s'\n", | 
| 1248 | progname, vnam(vc)); | 
| 1249 | quit(1); | 
| 1250 | } |