ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/pcomb.c
Revision: 2.60
Committed: Thu Nov 7 20:07:08 2024 UTC (4 months, 2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.59: +4 -2 lines
Log Message:
fix(pcomb): Added default RGB format string if unknown

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pcomb.c,v 2.59 2024/02/23 03:47:57 greg Exp $";
3 #endif
4 /*
5 * Combine picture files according to calcomp functions.
6 *
7 * 1/4/89
8 */
9
10 #include "platform.h"
11 #include "standard.h"
12 #include "paths.h"
13 #include "color.h"
14 #include "calcomp.h"
15 #include "view.h"
16
17 #define MAXINP 1024 /* maximum number of input files */
18 #define WINSIZ 127 /* scanline window size */
19 #define MIDSCN ((WINSIZ-1)/2+1)
20
21 struct {
22 char *name; /* file or command name */
23 FILE *fp; /* stream pointer */
24 VIEW vw; /* view for picture */
25 RESOLU rs; /* image resolution and orientation */
26 int infloat; /* input is floating point (#comp)? */
27 float pa; /* pixel aspect ratio */
28 COLOR *scan[WINSIZ]; /* input scanline window */
29 COLOR coef; /* coefficient */
30 COLOR expos; /* recorded exposure */
31 } input[MAXINP]; /* input pictures */
32
33 int nfiles; /* number of input files */
34
35 VIEW *commvp = NULL; /* common view parameters */
36
37 char ourfmt[MAXFMTLEN] = PICFMT; /* input picture format */
38 int outfloat = 0; /* #component float output? */
39
40 char StandardInput[] = "<stdin>";
41 char Command[] = "<Command>";
42 char vcolin[3][4] = {"ri", "gi", "bi"};
43 char vcolout[3][4] = {"ro", "go", "bo"};
44 char vbrtin[] = "li";
45 char vbrtout[] = "lo";
46 char vcolexp[3][4] = {"re", "ge", "be"};
47 char vbrtexp[] = "le";
48 char vpixaspect[] = "pa";
49
50 char vray[7][4] = {"Ox", "Oy", "Oz", "Dx", "Dy", "Dz", "T"};
51
52 char vpsize[] = "S";
53
54 char vnfiles[] = "nfiles";
55 char vwhteff[] = "WE";
56 char vxmax[] = "xmax";
57 char vymax[] = "ymax";
58 char vxres[] = "xres";
59 char vyres[] = "yres";
60 char vxpos[] = "x";
61 char vypos[] = "y";
62
63 int nowarn = 0; /* no warning messages? */
64
65 int xmax = 0, ymax = 0; /* input resolution */
66
67 int xscan, yscan; /* input position */
68
69 int xres, yres; /* output resolution */
70
71 int xpos, ypos; /* output position */
72
73 char *progname; /* global argv[0] */
74
75 int echoheader = 1;
76 int wrongformat = 0;
77 int gotview;
78
79
80 static gethfunc headline;
81 static void checkfile(int orig);
82 static double rgb_bright(COLOR clr);
83 static double xyz_bright(COLOR clr);
84 static void init(void);
85 static void combine(void);
86 static void advance(void);
87 static double l_expos(char *nam);
88 static double l_pixaspect(char *nm);
89 static double l_colin(char *nam);
90 static double l_ray(char *nam);
91 static double l_psize(char *nm);
92
93
94 int
95 main(
96 int argc,
97 char *argv[]
98 )
99 {
100 int original;
101 double f;
102 int a;
103
104 SET_DEFAULT_BINARY();
105 SET_FILE_BINARY(stdin);
106 SET_FILE_BINARY(stdout);
107 progname = argv[0];
108 esupport |= E_VARIABLE|E_FUNCTION|E_RCONST;
109 esupport &= ~(E_OUTCHAN|E_INCHAN);
110 /* scan options */
111 for (a = 1; a < argc; a++) {
112 if (argv[a][0] == '-')
113 switch (argv[a][1]) {
114 case 'x':
115 case 'y':
116 a++;
117 continue;
118 case 'w':
119 nowarn = !nowarn;
120 continue;
121 case 'h':
122 echoheader = !echoheader;
123 continue;
124 case 'f':
125 if (argv[a][2]) {
126 outfloat = (argv[a][2] == 'f');
127 continue;
128 }
129 /* fall through */
130 case 'e':
131 a++;
132 continue;
133 }
134 break;
135 }
136 newheader("RADIANCE", stdout); /* start header */
137 fputnow(stdout);
138 /* process files */
139 for (nfiles = 0; nfiles < MAXINP; nfiles++) {
140 setcolor(input[nfiles].coef, 1.0, 1.0, 1.0);
141 setcolor(input[nfiles].expos, 1.0, 1.0, 1.0);
142 input[nfiles].vw = stdview;
143 input[nfiles].pa = 1.0;
144 }
145 nfiles = 0;
146 original = 0;
147 for ( ; a < argc; a++) {
148 if (nfiles >= MAXINP) {
149 eputs(argv[0]);
150 eputs(": too many picture files\n");
151 quit(1);
152 }
153 if (argv[a][0] == '-')
154 switch (argv[a][1]) {
155 case '\0':
156 input[nfiles].name = StandardInput;
157 input[nfiles].fp = stdin;
158 break;
159 case 'o':
160 original++;
161 continue;
162 case 's':
163 f = atof(argv[++a]);
164 scalecolor(input[nfiles].coef, f);
165 continue;
166 case 'c':
167 colval(input[nfiles].coef,RED)*=atof(argv[++a]);
168 colval(input[nfiles].coef,GRN)*=atof(argv[++a]);
169 colval(input[nfiles].coef,BLU)*=atof(argv[++a]);
170 continue;
171 default:
172 goto usage;
173 }
174 else {
175 if (argv[a][0] == '!') {
176 input[nfiles].name = Command;
177 input[nfiles].fp = popen(argv[a]+1, "r");
178 } else {
179 input[nfiles].name = argv[a];
180 input[nfiles].fp = fopen(argv[a], "r");
181 }
182 if (input[nfiles].fp == NULL) {
183 perror(argv[a]);
184 quit(1);
185 }
186 }
187 checkfile(original);
188 nfiles++;
189 original = 0;
190 }
191 init(); /* set constants */
192 /* go back and get expressions */
193 for (a = 1; a < argc; a++) {
194 char *fpath;
195 if (argv[a][0] == '-')
196 switch (argv[a][1]) {
197 case 'x':
198 varset(vxres, ':', eval(argv[++a]));
199 continue;
200 case 'y':
201 varset(vyres, ':', eval(argv[++a]));
202 continue;
203 case 'w':
204 continue;
205 case 'h':
206 continue;
207 case 'f':
208 if (argv[a][2])
209 continue;
210 fpath = getpath(argv[++a], getrlibpath(), 0);
211 if (fpath == NULL) {
212 eputs(argv[0]);
213 eputs(": cannot find file '");
214 eputs(argv[a]);
215 eputs("'\n");
216 quit(1);
217 }
218 fcompile(fpath);
219 continue;
220 case 'e':
221 scompile(argv[++a], NULL, 0);
222 continue;
223 }
224 break;
225 }
226 /* get output resolution */
227 xres = varvalue(vxres) + .5;
228 yres = varvalue(vyres) + .5;
229 if (xres <= 0 || yres <= 0) {
230 eputs(argv[0]);
231 eputs(": illegal output resolution\n");
232 quit(1);
233 }
234 if (!vardefined(vbrtout)) /* single or 3-channel? */
235 outfloat *= 3;
236
237 printargs(argc, argv, stdout); /* complete header */
238 if (commvp != NULL) {
239 fputs(VIEWSTR, stdout);
240 fprintview(commvp, stdout);
241 fputc('\n', stdout);
242 }
243 if (outfloat) { /* print output format */
244 printf("NROWS=%d\nNCOLS=%d\n", yres, xres);
245 fputncomp(outfloat, stdout);
246 fputendian(stdout);
247 fputformat("float", stdout);
248 } else if (strcmp(ourfmt, PICFMT))
249 fputformat(ourfmt, stdout);
250 else
251 fputformat(COLRFMT, stdout);
252 fputc('\n', stdout); /* end header */
253 if (!outfloat)
254 fprtresolu(xres, yres, stdout);
255
256 doptimize(1); /* optimize definitions */
257 combine(); /* combine pictures */
258 quit(0);
259 usage:
260 eputs("Usage: ");
261 eputs(argv[0]);
262 eputs(
263 " [-w][-h][-ff][-x xr][-y yr][-e expr][-f file] [ [-o][-s f][-c r g b] hdr ..]\n");
264 quit(1);
265 return 1; /* pro forma return */
266 }
267
268
269 static int
270 headline( /* check header line & echo if requested */
271 char *s,
272 void *p
273 )
274 {
275 int orig = *(int *)p;
276 char fmt[MAXFMTLEN];
277 double d;
278 int bigend;
279 COLOR ctmp;
280
281 if (isheadid(s)) /* header id */
282 return(0); /* don't echo */
283 if (formatval(fmt, s)) { /* check format */
284 if (globmatch(ourfmt, fmt)) {
285 wrongformat = 0;
286 strcpy(ourfmt, fmt);
287 } else if (!strcmp("float", fmt))
288 input[nfiles].infloat *= -1;
289 else
290 wrongformat = globmatch(PICFMT, fmt) ? 1 : -1;
291 return(0); /* don't echo */
292 }
293 if ((bigend = isbigendian(s)) >= 0) {
294 if (bigend != nativebigendian()) {
295 eputs(input[nfiles].name);
296 eputs(": unsupported input byte ordering\n");
297 quit(1);
298 }
299 return(0); /* don't echo */
300 }
301 if (!strncmp("NROWS=", s, 6)) { /* X-resolution */
302 input[nfiles].rs.yr = atoi(s+6);
303 return(0); /* don't echo */
304 }
305 if (!strncmp("NCOLS=", s, 6)) { /* Y-resolution */
306 input[nfiles].rs.xr = atoi(s+6);
307 return(0); /* don't echo */
308 }
309 if (isncomp(s)) /* # components */
310 input[nfiles].infloat *= ncompval(s);
311
312 if (orig) { /* undo exposure? */
313 if (isexpos(s)) {
314 d = 1./exposval(s);
315 scalecolor(input[nfiles].coef, d);
316 return(0); /* don't echo */
317 }
318 if (iscolcor(s)) {
319 int i;
320 colcorval(ctmp, s);
321 for (i = 3; i--; )
322 colval(input[nfiles].coef,i) /= colval(ctmp,i);
323 return(0); /* don't echo */
324 }
325 } else if (isexpos(s)) { /* record exposure? */
326 d = exposval(s);
327 scalecolor(input[nfiles].expos, d);
328 } else if (iscolcor(s)) {
329 colcorval(ctmp, s);
330 multcolor(input[nfiles].expos, ctmp);
331 }
332 if (isaspect(s))
333 input[nfiles].pa *= aspectval(s);
334 else if (isview(s) && sscanview(&input[nfiles].vw, s) > 0)
335 gotview++;
336
337 if (echoheader) { /* echo line */
338 putchar('\t');
339 return(fputs(s, stdout));
340 }
341 return(0);
342 }
343
344
345 static void
346 checkfile(int orig) /* ready a file */
347 {
348 int i;
349 /* process header */
350 gotview = 0;
351 input[nfiles].infloat = -1;
352 input[nfiles].rs.rt = PIXSTANDARD;
353 input[nfiles].rs.xr = input[nfiles].rs.yr = 0;
354 if (echoheader) {
355 fputs(input[nfiles].name, stdout);
356 fputs(":\n", stdout);
357 }
358 getheader(input[nfiles].fp, headline, &orig);
359
360 if (input[nfiles].infloat <= 0)
361 input[nfiles].infloat = 0;
362 else if ((input[nfiles].infloat != 3) &
363 (input[nfiles].infloat != 1)) {
364 eputs(input[nfiles].name);
365 eputs(": unsupported number of components\n");
366 quit(1);
367 }
368 if (wrongformat < 0) {
369 eputs(input[nfiles].name);
370 eputs(": not a Radiance picture or float matrix\n");
371 quit(1);
372 }
373 if (wrongformat > 0) {
374 wputs(input[nfiles].name);
375 wputs(": warning -- incompatible picture format\n");
376 }
377 if (!gotview || setview(&input[nfiles].vw) != NULL)
378 input[nfiles].vw.type = 0;
379 else if (commvp == NULL)
380 commvp = &input[nfiles].vw;
381 if ((input[nfiles].rs.xr <= 0) | (input[nfiles].rs.yr <= 0) &&
382 !fgetsresolu(&input[nfiles].rs, input[nfiles].fp)) {
383 eputs(input[nfiles].name);
384 eputs(": bad picture size\n");
385 quit(1);
386 }
387 if (xmax == 0 && ymax == 0) {
388 xmax = scanlen(&input[nfiles].rs);
389 ymax = numscans(&input[nfiles].rs);
390 } else if (scanlen(&input[nfiles].rs) != xmax ||
391 numscans(&input[nfiles].rs) != ymax) {
392 eputs(input[nfiles].name);
393 eputs(": resolution mismatch\n");
394 quit(1);
395 }
396 /* allocate scanlines */
397 for (i = 0; i < WINSIZ; i++)
398 input[nfiles].scan[i] = (COLOR *)emalloc(xmax*sizeof(COLOR));
399 }
400
401
402 static double
403 rgb_bright(
404 COLOR clr
405 )
406 {
407 return(bright(clr));
408 }
409
410
411 static double
412 xyz_bright(
413 COLOR clr
414 )
415 {
416 return(clr[CIEY]);
417 }
418
419
420 double (*ourbright)() = rgb_bright;
421
422
423 static void
424 init(void) /* perform final setup */
425 {
426 int i;
427 /* define constants */
428 varset("PI", ':', PI);
429 varset(vnfiles, ':', (double)nfiles);
430 varset(vxmax, ':', (double)xmax);
431 varset(vymax, ':', (double)ymax);
432 /* set functions */
433 for (i = 0; i < 3; i++) {
434 funset(vcolexp[i], 1, ':', l_expos);
435 funset(vcolin[i], 1, '=', l_colin);
436 }
437 funset(vbrtexp, 1, ':', l_expos);
438 funset(vbrtin, 1, '=', l_colin);
439 funset(vpixaspect, 1, ':', l_pixaspect);
440 for (i = 0; i < 7; i++)
441 funset(vray[i], 1, '=', l_ray);
442 funset(vpsize, 1, '=', l_psize);
443 /* set brightness function */
444 if (!strcmp(ourfmt, CIEFMT)) {
445 varset(vwhteff, ':', 1.0);
446 ourbright = xyz_bright;
447 } else
448 varset(vwhteff, ':', WHTEFFICACY);
449 /* these may be overridden */
450 varset(vxres, ':', (double)xmax);
451 varset(vyres, ':', (double)ymax);
452 }
453
454
455 static void
456 combine(void) /* combine pictures */
457 {
458 EPNODE *coldef[3], *brtdef;
459 int set_x, set_y;
460 COLOR *scanout;
461 double d;
462 int i, j;
463 /* check defined variables */
464 for (j = 0; j < 3; j++) {
465 if (vardefined(vcolout[j]))
466 coldef[j] = eparse(vcolout[j]);
467 else
468 coldef[j] = NULL;
469 }
470 if (vardefined(vbrtout))
471 brtdef = eparse(vbrtout);
472 else
473 brtdef = NULL;
474 /* what to set */
475 set_x = varlookup(vxpos) != NULL && !vardefined(vxpos);
476 set_y = varlookup(vypos) != NULL && !vardefined(vypos);
477 /* allocate scanline */
478 scanout = (COLOR *)emalloc(xres*sizeof(COLOR));
479 /* set input position */
480 yscan = ymax+MIDSCN;
481 /* combine files */
482 for (ypos = yres-1; ypos >= 0; ypos--) {
483 advance();
484 if (set_y) varset(vypos, '=', (double)ypos);
485 for (xpos = 0; xpos < xres; xpos++) {
486 xscan = (xpos+.5)*xmax/xres;
487 if (set_x) varset(vxpos, '=', (double)xpos);
488 eclock++;
489 if (brtdef != NULL) {
490 d = evalue(brtdef);
491 d *= (outfloat > 0) | (d >= 0);
492 setcolor(scanout[xpos], d, d, d);
493 } else {
494 for (j = 0; j < 3; j++) {
495 if (coldef[j] != NULL) {
496 d = evalue(coldef[j]);
497 } else {
498 d = 0.0;
499 for (i = 0; i < nfiles; i++)
500 d += colval(input[i].scan[MIDSCN][xscan],j);
501 }
502 d *= (outfloat > 0) | (d >= 0);
503 colval(scanout[xpos],j) = d;
504 }
505 }
506 }
507 switch (outfloat) {
508 case 3: /* writing out float triplets */
509 if (fwrite(scanout, sizeof(float)*3, xres, stdout) != xres)
510 goto writerr;
511 break;
512 case 1: /* writing out gray float values */
513 for (xpos = 0; xpos < xres; xpos++)
514 if (putbinary(&scanout[xpos][CIEY],
515 sizeof(float), 1, stdout) != 1)
516 goto writerr;
517 break;
518 default: /* writing out Radiance picture */
519 if (fwritescan(scanout, xres, stdout) < 0)
520 goto writerr;
521 break;
522 }
523 }
524 efree((char *)scanout);
525 return;
526 writerr:
527 perror("write error");
528 quit(1);
529 }
530
531
532 static void
533 advance(void) /* read in data for next scanline */
534 {
535 int ytarget;
536 COLOR *st;
537 int i, j;
538
539 for (ytarget = (ypos+.5)*ymax/yres; yscan > ytarget; yscan--)
540 for (i = 0; i < nfiles; i++) {
541 st = input[i].scan[WINSIZ-1];
542 for (j = WINSIZ-1; j > 0; j--) /* rotate window */
543 input[i].scan[j] = input[i].scan[j-1];
544 input[i].scan[0] = st;
545 if (yscan <= MIDSCN) /* hit bottom? */
546 continue;
547 /* read scanline */
548 if (input[i].infloat) {
549 if (fread(st, sizeof(float)*input[i].infloat,
550 xmax, input[i].fp) != xmax)
551 goto readerr;
552 if (input[i].infloat == 1) {
553 const COLORV *f = st[0] + xmax;
554 int x = xmax;
555 while (x-- > 0)
556 st[x][BLU] = st[x][GRN] =
557 st[x][RED] = *--f;
558 }
559 } else if (freadscan(st, xmax, input[i].fp) < 0)
560 goto readerr;
561 for (j = 0; j < xmax; j++) /* adjust color */
562 multcolor(st[j], input[i].coef);
563 }
564 return;
565 readerr:
566 eputs(input[i].name);
567 eputs(": read error\n");
568 quit(1);
569 }
570
571
572 static double
573 l_expos( /* return picture exposure */
574 char *nam
575 )
576 {
577 int fn, n;
578 double d;
579
580 d = argument(1);
581 if (d <= -0.5 || d >= nfiles+0.5) {
582 errno = EDOM;
583 return(0.0);
584 }
585 if (d < 0.5)
586 return((double)nfiles);
587 fn = d - 0.5;
588 if (nam == vbrtexp)
589 return((*ourbright)(input[fn].expos));
590 n = 3;
591 while (n--)
592 if (nam == vcolexp[n])
593 return(colval(input[fn].expos,n));
594 eputs("Bad call to l_expos()!\n");
595 quit(1);
596 return 1; /* pro forma return */
597 }
598
599
600 static double
601 l_pixaspect(char *nm) /* return pixel aspect ratio */
602 {
603 int fn;
604 double d;
605
606 d = argument(1);
607 if (d <= -0.5 || d >= nfiles+0.5) {
608 errno = EDOM;
609 return(0.0);
610 }
611 if (d < 0.5)
612 return((double)nfiles);
613 fn = d - 0.5;
614 return(input[fn].pa);
615 }
616
617
618 static double
619 l_colin( /* return color value for picture */
620 char *nam
621 )
622 {
623 int fn;
624 int n, xoff, yoff;
625 double d;
626
627 d = argument(1);
628 if (d <= -0.5 || d >= nfiles+0.5) {
629 errno = EDOM;
630 return(0.0);
631 }
632 if (d < 0.5)
633 return((double)nfiles);
634 fn = d - 0.5;
635 xoff = yoff = 0;
636 n = nargum();
637 if (n >= 2) {
638 d = argument(2);
639 if (d < 0.0) {
640 xoff = d-.5;
641 if (xscan+xoff < 0)
642 xoff = -xscan;
643 } else {
644 xoff = d+.5;
645 if (xscan+xoff >= xmax)
646 xoff = xmax-1-xscan;
647 }
648 }
649 if (n >= 3) {
650 d = argument(3);
651 if (d < 0.0) {
652 yoff = d-.5;
653 if (yoff+MIDSCN < 0)
654 yoff = -MIDSCN;
655 if (yscan+yoff < 0)
656 yoff = -yscan;
657 } else {
658 yoff = d+.5;
659 if (yoff+MIDSCN >= WINSIZ)
660 yoff = WINSIZ-1-MIDSCN;
661 if (yscan+yoff >= ymax)
662 yoff = ymax-1-yscan;
663 }
664 }
665 if (nam == vbrtin)
666 return((*ourbright)(input[fn].scan[MIDSCN+yoff][xscan+xoff]));
667 n = 3;
668 while (n--)
669 if (nam == vcolin[n])
670 return(colval(input[fn].scan[MIDSCN+yoff][xscan+xoff],n));
671 eputs("Bad call to l_colin()!\n");
672 quit(1);
673 return 1; /* pro forma return */
674 }
675
676
677 static double
678 l_ray( /* return ray origin or direction */
679 char *nam
680 )
681 {
682 static unsigned long ltick[MAXINP];
683 static FVECT lorg[MAXINP], ldir[MAXINP];
684 static double ldist[MAXINP];
685 RREAL loc[2];
686 double d;
687 int fn;
688 int i;
689
690 d = argument(1);
691 if (d <= -0.5 || d >= nfiles+0.5) {
692 errno = EDOM;
693 return(0.0);
694 }
695 if (d < 0.5)
696 return((double)nfiles);
697 fn = d - 0.5;
698 if (ltick[fn] != eclock) { /* need to compute? */
699 if (input[fn].vw.type == 0) {
700 lorg[fn][0] = lorg[fn][1] = lorg[fn][2] = 0.0;
701 ldir[fn][0] = ldir[fn][1] = ldir[fn][2] = 0.0;
702 ldist[fn] = -1.0;
703 errno = EDOM;
704 } else {
705 pix2loc(loc, &input[fn].rs, xscan, ymax-1-yscan);
706 ldist[fn] = viewray(lorg[fn], ldir[fn],
707 &input[fn].vw, loc[0], loc[1]);
708 if (ldist[fn] < -FTINY)
709 errno = EDOM;
710 }
711 ltick[fn] = eclock;
712 }
713 if (nam == vray[i=6])
714 return(ldist[fn]);
715 while (i--)
716 if (nam == vray[i])
717 return(i < 3 ? lorg[fn][i] : ldir[fn][i-3]);
718 eputs("Bad call to l_ray()!\n");
719 quit(1);
720 return 1; /* pro forma return */
721 }
722
723
724 static double
725 l_psize(char *nm) /* compute pixel size in steradians */
726 {
727 static unsigned long ltick[MAXINP];
728 static double psize[MAXINP];
729 FVECT dir0, org, dirx, diry;
730 RREAL locx[2], locy[2];
731 double d;
732 int fn;
733 int i;
734
735 d = argument(1);
736 if (d <= -0.5 || d >= nfiles+0.5) {
737 errno = EDOM;
738 return(0.0);
739 }
740 if (d < 0.5)
741 return((double)nfiles);
742 fn = d - 0.5;
743 if (ltick[fn] != eclock) { /* need to compute? */
744 psize[fn] = 0.0;
745 if (input[fn].vw.type == 0)
746 errno = EDOM;
747 else if (input[fn].vw.type != VT_PAR &&
748 funvalue(vray[6], 1, &d) >= -FTINY) {
749 for (i = 0; i < 3; i++)
750 dir0[i] = funvalue(vray[3+i], 1, &d);
751 pix2loc(locx, &input[fn].rs, xscan+1, ymax-1-yscan);
752 pix2loc(locy, &input[fn].rs, xscan, ymax-yscan);
753 if (viewray(org, dirx, &input[fn].vw,
754 locx[0], locx[1]) >= -FTINY &&
755 viewray(org, diry, &input[fn].vw,
756 locy[0], locy[1]) >= -FTINY) {
757 /* approximate solid angle */
758 for (i = 0; i < 3; i++) {
759 dirx[i] -= dir0[i];
760 diry[i] -= dir0[i];
761 }
762 fcross(dir0, dirx, diry);
763 psize[fn] = VLEN(dir0);
764 }
765 }
766 ltick[fn] = eclock;
767 }
768 return(psize[fn]);
769 }
770
771
772 extern void
773 wputs(const char *msg)
774 {
775 if (!nowarn)
776 eputs(msg);
777 }
778
779
780 extern void
781 eputs(const char *msg)
782 {
783 fputs(msg, stderr);
784 }
785
786
787 extern void
788 quit(int code) /* exit gracefully */
789 {
790 int i;
791 /* close input files */
792 for (i = 0; i < nfiles; i++)
793 if (input[i].name == Command)
794 pclose(input[i].fp);
795 else
796 fclose(input[i].fp);
797 exit(code);
798 }