ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/px/pcomb.c
Revision: 2.57
Committed: Sat Dec 9 23:46:51 2023 UTC (5 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.56: +112 -30 lines
Log Message:
feat(pcomb): Added support for 1- and 3-component float matrix i/o

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: pcomb.c,v 2.56 2023/11/20 21:44:29 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 format if known */
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 fputc('\n', stdout); /* end header */
251 if (!outfloat)
252 fprtresolu(xres, yres, stdout);
253
254 combine(); /* combine pictures */
255 quit(0);
256 usage:
257 eputs("Usage: ");
258 eputs(argv[0]);
259 eputs(
260 " [-w][-h][-ff][-x xr][-y yr][-e expr][-f file] [ [-o][-s f][-c r g b] hdr ..]\n");
261 quit(1);
262 return 1; /* pro forma return */
263 }
264
265
266 static int
267 headline( /* check header line & echo if requested */
268 char *s,
269 void *p
270 )
271 {
272 int orig = *(int *)p;
273 char fmt[MAXFMTLEN];
274 double d;
275 int bigend;
276 COLOR ctmp;
277
278 if (isheadid(s)) /* header id */
279 return(0); /* don't echo */
280 if (formatval(fmt, s)) { /* check format */
281 if (globmatch(ourfmt, fmt)) {
282 wrongformat = 0;
283 strcpy(ourfmt, fmt);
284 } else if (!strcmp("float", fmt))
285 input[nfiles].infloat *= -1;
286 else
287 wrongformat = globmatch(PICFMT, fmt) ? 1 : -1;
288 return(0); /* don't echo */
289 }
290 if ((bigend = isbigendian(s)) >= 0) {
291 if (bigend != nativebigendian()) {
292 eputs(input[nfiles].name);
293 eputs(": unsupported input byte ordering\n");
294 quit(1);
295 }
296 return(0); /* don't echo */
297 }
298 if (!strncmp("NROWS=", s, 6)) { /* X-resolution */
299 input[nfiles].rs.yr = atoi(s+6);
300 return(0); /* don't echo */
301 }
302 if (!strncmp("NCOLS=", s, 6)) { /* Y-resolution */
303 input[nfiles].rs.xr = atoi(s+6);
304 return(0); /* don't echo */
305 }
306 if (isncomp(s)) /* # components */
307 input[nfiles].infloat *= ncompval(s);
308
309 if (orig) { /* undo exposure? */
310 if (isexpos(s)) {
311 d = 1./exposval(s);
312 scalecolor(input[nfiles].coef, d);
313 return(0); /* don't echo */
314 }
315 if (iscolcor(s)) {
316 int i;
317 colcorval(ctmp, s);
318 for (i = 3; i--; )
319 colval(input[nfiles].coef,i) /= colval(ctmp,i);
320 return(0); /* don't echo */
321 }
322 } else if (isexpos(s)) { /* record exposure? */
323 d = exposval(s);
324 scalecolor(input[nfiles].expos, d);
325 } else if (iscolcor(s)) {
326 colcorval(ctmp, s);
327 multcolor(input[nfiles].expos, ctmp);
328 }
329 if (isaspect(s))
330 input[nfiles].pa *= aspectval(s);
331 else if (isview(s) && sscanview(&input[nfiles].vw, s) > 0)
332 gotview++;
333
334 if (echoheader) { /* echo line */
335 putchar('\t');
336 return(fputs(s, stdout));
337 }
338 return(0);
339 }
340
341
342 static void
343 checkfile(int orig) /* ready a file */
344 {
345 int i;
346 /* process header */
347 gotview = 0;
348 input[nfiles].infloat = -1;
349 input[nfiles].rs.rt = PIXSTANDARD;
350 input[nfiles].rs.xr = input[nfiles].rs.yr = 0;
351 if (echoheader) {
352 fputs(input[nfiles].name, stdout);
353 fputs(":\n", stdout);
354 }
355 getheader(input[nfiles].fp, headline, &orig);
356
357 if (input[nfiles].infloat <= 0)
358 input[nfiles].infloat = 0;
359 else if ((input[nfiles].infloat != 3) &
360 (input[nfiles].infloat != 1)) {
361 eputs(input[nfiles].name);
362 eputs(": unsupported number of components\n");
363 quit(1);
364 }
365 if (wrongformat < 0) {
366 eputs(input[nfiles].name);
367 eputs(": not a Radiance picture or float matrix\n");
368 quit(1);
369 }
370 if (wrongformat > 0) {
371 wputs(input[nfiles].name);
372 wputs(": warning -- incompatible picture format\n");
373 }
374 if (!gotview || setview(&input[nfiles].vw) != NULL)
375 input[nfiles].vw.type = 0;
376 else if (commvp == NULL)
377 commvp = &input[nfiles].vw;
378 if ((input[nfiles].rs.xr <= 0) | (input[nfiles].rs.yr <= 0) &&
379 !fgetsresolu(&input[nfiles].rs, input[nfiles].fp)) {
380 eputs(input[nfiles].name);
381 eputs(": bad picture size\n");
382 quit(1);
383 }
384 if (xmax == 0 && ymax == 0) {
385 xmax = scanlen(&input[nfiles].rs);
386 ymax = numscans(&input[nfiles].rs);
387 } else if (scanlen(&input[nfiles].rs) != xmax ||
388 numscans(&input[nfiles].rs) != ymax) {
389 eputs(input[nfiles].name);
390 eputs(": resolution mismatch\n");
391 quit(1);
392 }
393 /* allocate scanlines */
394 for (i = 0; i < WINSIZ; i++)
395 input[nfiles].scan[i] = (COLOR *)emalloc(xmax*sizeof(COLOR));
396 }
397
398
399 static double
400 rgb_bright(
401 COLOR clr
402 )
403 {
404 return(bright(clr));
405 }
406
407
408 static double
409 xyz_bright(
410 COLOR clr
411 )
412 {
413 return(clr[CIEY]);
414 }
415
416
417 double (*ourbright)() = rgb_bright;
418
419
420 static void
421 init(void) /* perform final setup */
422 {
423 int i;
424 /* define constants */
425 varset("PI", ':', PI);
426 varset(vnfiles, ':', (double)nfiles);
427 varset(vxmax, ':', (double)xmax);
428 varset(vymax, ':', (double)ymax);
429 /* set functions */
430 for (i = 0; i < 3; i++) {
431 funset(vcolexp[i], 1, ':', l_expos);
432 funset(vcolin[i], 1, '=', l_colin);
433 }
434 funset(vbrtexp, 1, ':', l_expos);
435 funset(vbrtin, 1, '=', l_colin);
436 funset(vpixaspect, 1, ':', l_pixaspect);
437 for (i = 0; i < 7; i++)
438 funset(vray[i], 1, '=', l_ray);
439 funset(vpsize, 1, '=', l_psize);
440 /* set brightness function */
441 if (!strcmp(ourfmt, CIEFMT)) {
442 varset(vwhteff, ':', 1.0);
443 ourbright = xyz_bright;
444 } else
445 varset(vwhteff, ':', WHTEFFICACY);
446 /* these may be overridden */
447 varset(vxres, ':', (double)xmax);
448 varset(vyres, ':', (double)ymax);
449 }
450
451
452 static void
453 combine(void) /* combine pictures */
454 {
455 EPNODE *coldef[3], *brtdef;
456 COLOR *scanout;
457 double d;
458 int i, j;
459 /* check defined variables */
460 for (j = 0; j < 3; j++) {
461 if (vardefined(vcolout[j]))
462 coldef[j] = eparse(vcolout[j]);
463 else
464 coldef[j] = NULL;
465 }
466 if (vardefined(vbrtout))
467 brtdef = eparse(vbrtout);
468 else
469 brtdef = NULL;
470 /* allocate scanline */
471 scanout = (COLOR *)emalloc(xres*sizeof(COLOR));
472 /* set input position */
473 yscan = ymax+MIDSCN;
474 /* combine files */
475 for (ypos = yres-1; ypos >= 0; ypos--) {
476 advance();
477 varset(vypos, '=', (double)ypos);
478 for (xpos = 0; xpos < xres; xpos++) {
479 xscan = (xpos+.5)*xmax/xres;
480 varset(vxpos, '=', (double)xpos);
481 eclock++;
482 if (brtdef != NULL) {
483 d = evalue(brtdef);
484 d *= (outfloat > 0) | (d >= 0);
485 setcolor(scanout[xpos], d, d, d);
486 } else {
487 for (j = 0; j < 3; j++) {
488 if (coldef[j] != NULL) {
489 d = evalue(coldef[j]);
490 } else {
491 d = 0.0;
492 for (i = 0; i < nfiles; i++)
493 d += colval(input[i].scan[MIDSCN][xscan],j);
494 }
495 d *= (outfloat > 0) | (d >= 0);
496 colval(scanout[xpos],j) = d;
497 }
498 }
499 }
500 switch (outfloat) {
501 case 3: /* writing out float triplets */
502 if (fwrite(scanout, sizeof(float)*3, xres, stdout) != xres)
503 goto writerr;
504 break;
505 case 1: /* writing out gray float values */
506 for (xpos = 0; xpos < xres; xpos++)
507 if (putbinary(&scanout[xpos][CIEY],
508 sizeof(float), 1, stdout) != 1)
509 goto writerr;
510 break;
511 default: /* writing out Radiance picture */
512 if (fwritescan(scanout, xres, stdout) < 0)
513 goto writerr;
514 break;
515 }
516 }
517 efree((char *)scanout);
518 return;
519 writerr:
520 perror("write error");
521 quit(1);
522 }
523
524
525 static void
526 advance(void) /* read in data for next scanline */
527 {
528 int ytarget;
529 COLOR *st;
530 int i, j;
531
532 for (ytarget = (ypos+.5)*ymax/yres; yscan > ytarget; yscan--)
533 for (i = 0; i < nfiles; i++) {
534 st = input[i].scan[WINSIZ-1];
535 for (j = WINSIZ-1; j > 0; j--) /* rotate window */
536 input[i].scan[j] = input[i].scan[j-1];
537 input[i].scan[0] = st;
538 if (yscan <= MIDSCN) /* hit bottom? */
539 continue;
540 /* read scanline */
541 if (input[i].infloat) {
542 if (fread(st, sizeof(float)*input[i].infloat,
543 xmax, input[i].fp) != xmax)
544 goto readerr;
545 if (input[i].infloat == 1) {
546 const COLORV *f = st[0] + xmax;
547 int x = xmax;
548 while (x-- > 0)
549 st[x][BLU] = st[x][GRN] =
550 st[x][RED] = *--f;
551 }
552 } else if (freadscan(st, xmax, input[i].fp) < 0)
553 goto readerr;
554 for (j = 0; j < xmax; j++) /* adjust color */
555 multcolor(st[j], input[i].coef);
556 }
557 return;
558 readerr:
559 eputs(input[i].name);
560 eputs(": read error\n");
561 quit(1);
562 }
563
564
565 static double
566 l_expos( /* return picture exposure */
567 char *nam
568 )
569 {
570 int fn, n;
571 double d;
572
573 d = argument(1);
574 if (d <= -0.5 || d >= nfiles+0.5) {
575 errno = EDOM;
576 return(0.0);
577 }
578 if (d < 0.5)
579 return((double)nfiles);
580 fn = d - 0.5;
581 if (nam == vbrtexp)
582 return((*ourbright)(input[fn].expos));
583 n = 3;
584 while (n--)
585 if (nam == vcolexp[n])
586 return(colval(input[fn].expos,n));
587 eputs("Bad call to l_expos()!\n");
588 quit(1);
589 return 1; /* pro forma return */
590 }
591
592
593 static double
594 l_pixaspect(char *nm) /* return pixel aspect ratio */
595 {
596 int fn;
597 double d;
598
599 d = argument(1);
600 if (d <= -0.5 || d >= nfiles+0.5) {
601 errno = EDOM;
602 return(0.0);
603 }
604 if (d < 0.5)
605 return((double)nfiles);
606 fn = d - 0.5;
607 return(input[fn].pa);
608 }
609
610
611 static double
612 l_colin( /* return color value for picture */
613 char *nam
614 )
615 {
616 int fn;
617 int n, xoff, yoff;
618 double d;
619
620 d = argument(1);
621 if (d <= -0.5 || d >= nfiles+0.5) {
622 errno = EDOM;
623 return(0.0);
624 }
625 if (d < 0.5)
626 return((double)nfiles);
627 fn = d - 0.5;
628 xoff = yoff = 0;
629 n = nargum();
630 if (n >= 2) {
631 d = argument(2);
632 if (d < 0.0) {
633 xoff = d-.5;
634 if (xscan+xoff < 0)
635 xoff = -xscan;
636 } else {
637 xoff = d+.5;
638 if (xscan+xoff >= xmax)
639 xoff = xmax-1-xscan;
640 }
641 }
642 if (n >= 3) {
643 d = argument(3);
644 if (d < 0.0) {
645 yoff = d-.5;
646 if (yoff+MIDSCN < 0)
647 yoff = -MIDSCN;
648 if (yscan+yoff < 0)
649 yoff = -yscan;
650 } else {
651 yoff = d+.5;
652 if (yoff+MIDSCN >= WINSIZ)
653 yoff = WINSIZ-1-MIDSCN;
654 if (yscan+yoff >= ymax)
655 yoff = ymax-1-yscan;
656 }
657 }
658 if (nam == vbrtin)
659 return((*ourbright)(input[fn].scan[MIDSCN+yoff][xscan+xoff]));
660 n = 3;
661 while (n--)
662 if (nam == vcolin[n])
663 return(colval(input[fn].scan[MIDSCN+yoff][xscan+xoff],n));
664 eputs("Bad call to l_colin()!\n");
665 quit(1);
666 return 1; /* pro forma return */
667 }
668
669
670 static double
671 l_ray( /* return ray origin or direction */
672 char *nam
673 )
674 {
675 static unsigned long ltick[MAXINP];
676 static FVECT lorg[MAXINP], ldir[MAXINP];
677 static double ldist[MAXINP];
678 RREAL loc[2];
679 double d;
680 int fn;
681 int i;
682
683 d = argument(1);
684 if (d <= -0.5 || d >= nfiles+0.5) {
685 errno = EDOM;
686 return(0.0);
687 }
688 if (d < 0.5)
689 return((double)nfiles);
690 fn = d - 0.5;
691 if (ltick[fn] != eclock) { /* need to compute? */
692 if (input[fn].vw.type == 0) {
693 lorg[fn][0] = lorg[fn][1] = lorg[fn][2] = 0.0;
694 ldir[fn][0] = ldir[fn][1] = ldir[fn][2] = 0.0;
695 ldist[fn] = -1.0;
696 errno = EDOM;
697 } else {
698 pix2loc(loc, &input[fn].rs, xscan, ymax-1-yscan);
699 ldist[fn] = viewray(lorg[fn], ldir[fn],
700 &input[fn].vw, loc[0], loc[1]);
701 if (ldist[fn] < -FTINY)
702 errno = EDOM;
703 }
704 ltick[fn] = eclock;
705 }
706 if (nam == vray[i=6])
707 return(ldist[fn]);
708 while (i--)
709 if (nam == vray[i])
710 return(i < 3 ? lorg[fn][i] : ldir[fn][i-3]);
711 eputs("Bad call to l_ray()!\n");
712 quit(1);
713 return 1; /* pro forma return */
714 }
715
716
717 static double
718 l_psize(char *nm) /* compute pixel size in steradians */
719 {
720 static unsigned long ltick[MAXINP];
721 static double psize[MAXINP];
722 FVECT dir0, org, dirx, diry;
723 RREAL locx[2], locy[2];
724 double d;
725 int fn;
726 int i;
727
728 d = argument(1);
729 if (d <= -0.5 || d >= nfiles+0.5) {
730 errno = EDOM;
731 return(0.0);
732 }
733 if (d < 0.5)
734 return((double)nfiles);
735 fn = d - 0.5;
736 if (ltick[fn] != eclock) { /* need to compute? */
737 psize[fn] = 0.0;
738 if (input[fn].vw.type == 0)
739 errno = EDOM;
740 else if (input[fn].vw.type != VT_PAR &&
741 funvalue(vray[6], 1, &d) >= -FTINY) {
742 for (i = 0; i < 3; i++)
743 dir0[i] = funvalue(vray[3+i], 1, &d);
744 pix2loc(locx, &input[fn].rs, xscan+1, ymax-1-yscan);
745 pix2loc(locy, &input[fn].rs, xscan, ymax-yscan);
746 if (viewray(org, dirx, &input[fn].vw,
747 locx[0], locx[1]) >= -FTINY &&
748 viewray(org, diry, &input[fn].vw,
749 locy[0], locy[1]) >= -FTINY) {
750 /* approximate solid angle */
751 for (i = 0; i < 3; i++) {
752 dirx[i] -= dir0[i];
753 diry[i] -= dir0[i];
754 }
755 fcross(dir0, dirx, diry);
756 psize[fn] = VLEN(dir0);
757 }
758 }
759 ltick[fn] = eclock;
760 }
761 return(psize[fn]);
762 }
763
764
765 extern void
766 wputs(const char *msg)
767 {
768 if (!nowarn)
769 eputs(msg);
770 }
771
772
773 extern void
774 eputs(const char *msg)
775 {
776 fputs(msg, stderr);
777 }
778
779
780 extern void
781 quit(int code) /* exit gracefully */
782 {
783 int i;
784 /* close input files */
785 for (i = 0; i < nfiles; i++)
786 if (input[i].name == Command)
787 pclose(input[i].fp);
788 else
789 fclose(input[i].fp);
790 exit(code);
791 }