ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cal/rcalc.c
Revision: 1.19
Committed: Tue Jun 14 01:25:02 2005 UTC (18 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad3R7P2, rad3R7P1, rad3R8
Changes since 1.18: +5 -2 lines
Log Message:
Fixed bug in rcalc with -l option where it sometimes hung

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rcalc.c,v 1.18 2005/06/02 04:47:27 greg Exp $";
3 #endif
4 /*
5 * rcalc.c - record calculator program.
6 *
7 * 9/11/87
8 */
9
10 #include <stdlib.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <math.h>
15 #include <ctype.h>
16
17 #include "platform.h"
18 #include "rterror.h"
19 #include "rtmisc.h"
20 #include "rtio.h"
21 #include "calcomp.h"
22
23 #define isnum(c) (isdigit(c) || (c)=='-' || (c)=='.' \
24 || (c)=='+' || (c)=='e' || (c)=='E')
25
26 #define isblnk(c) (igneol ? isspace(c) : (c)==' '||(c)=='\t')
27
28 #define INBSIZ 16384 /* longest record */
29 #define MAXCOL 32 /* number of columns recorded */
30
31 /* field type specifications */
32 #define F_NUL 0 /* empty */
33 #define F_TYP 0x7000 /* mask for type */
34 #define F_WID 0x0fff /* mask for width */
35 #define T_LIT 0x1000 /* string literal */
36 #define T_STR 0x2000 /* string variable */
37 #define T_NUM 0x3000 /* numeric value */
38
39 struct strvar { /* string variable */
40 char *name;
41 char *val;
42 char *preset;
43 struct strvar *next;
44 };
45
46 struct field { /* record format structure */
47 int type; /* type of field (& width) */
48 union {
49 char *sl; /* string literal */
50 struct strvar *sv; /* string variable */
51 char *nv; /* numeric variable */
52 EPNODE *ne; /* numeric expression */
53 } f; /* field contents */
54 struct field *next; /* next field in record */
55 };
56
57 #define savqstr(s) strcpy(emalloc(strlen(s)+1),s)
58 #define freqstr(s) efree(s)
59
60 static int getinputrec(FILE *fp);
61 static void scaninp(void), advinp(void), resetinp(void);
62 static void putrec(void), putout(void), nbsynch(void);
63 static int getrec(void);
64 static void execute(char *file);
65 static void initinp(FILE *fp);
66 static void svpreset(char *eqn);
67 static void readfmt(char *spec, int output);
68 static int readfield(char **pp);
69 static int getfield(struct field *f);
70 static void chanset(int n, double v);
71 static void bchanset(int n, double v);
72 static struct strvar* getsvar(char *svname);
73 static double l_in(char *);
74
75 struct field *inpfmt = NULL; /* input record format */
76 struct field *outfmt = NULL; /* output record structure */
77 struct strvar *svhead = NULL; /* string variables */
78
79 int blnkeq = 1; /* blanks compare equal? */
80 int igneol = 0; /* ignore end of line? */
81 int passive = 0; /* passive mode (transmit unmatched input) */
82 char sepchar = '\t'; /* input/output separator */
83 int noinput = 0; /* no input records? */
84 int nbicols = 0; /* number of binary input columns */
85 int bocols = 0; /* produce binary output columns */
86 char inpbuf[INBSIZ]; /* input buffer */
87 double colval[MAXCOL]; /* input column values */
88 unsigned long colflg = 0; /* column retrieved flags */
89 int colpos; /* output column position */
90
91 int nowarn = 0; /* non-fatal diagnostic output */
92 int unbuff = 0; /* unbuffered output (flush each record) */
93
94 struct {
95 FILE *fin; /* input file */
96 int chr; /* next character */
97 char *beg; /* home position */
98 char *pos; /* scan position */
99 char *end; /* read position */
100 } ipb; /* circular lookahead buffer */
101
102
103 int
104 main(
105 int argc,
106 char *argv[]
107 )
108 {
109 int i;
110
111 esupport |= E_VARIABLE|E_FUNCTION|E_INCHAN|E_OUTCHAN|E_RCONST;
112 esupport &= ~(E_REDEFW);
113
114 #ifdef BIGGERLIB
115 biggerlib();
116 #endif
117 varset("PI", ':', 3.14159265358979323846);
118 funset("in", 1, '=', &l_in);
119
120 for (i = 1; i < argc && argv[i][0] == '-'; i++)
121 switch (argv[i][1]) {
122 case 'b':
123 blnkeq = !blnkeq;
124 break;
125 case 'l':
126 igneol = !igneol;
127 break;
128 case 'p':
129 passive = !passive;
130 break;
131 case 't':
132 sepchar = argv[i][2];
133 break;
134 case 's':
135 svpreset(argv[++i]);
136 break;
137 case 'f':
138 fcompile(argv[++i]);
139 break;
140 case 'e':
141 scompile(argv[++i], NULL, 0);
142 break;
143 case 'n':
144 noinput = 1;
145 break;
146 case 'i':
147 switch (argv[i][2]) {
148 case '\0':
149 nbicols = 0;
150 readfmt(argv[++i], 0);
151 break;
152 case 'a':
153 nbicols = 0;
154 break;
155 case 'd':
156 if (isdigit(argv[i][3]))
157 nbicols = atoi(argv[i]+3);
158 else
159 nbicols = 1;
160 if (nbicols*sizeof(double) > INBSIZ) {
161 eputs(argv[0]);
162 eputs(": too many input columns\n");
163 quit(1);
164 }
165 break;
166 case 'f':
167 if (isdigit(argv[i][3]))
168 nbicols = -atoi(argv[i]+3);
169 else
170 nbicols = -1;
171 if (-nbicols*sizeof(float) > INBSIZ) {
172 eputs(argv[0]);
173 eputs(": too many input columns\n");
174 quit(1);
175 }
176 break;
177 default:
178 goto userr;
179 }
180 break;
181 case 'o':
182 switch (argv[i][2]) {
183 case '\0':
184 bocols = 0;
185 readfmt(argv[++i], 1);
186 break;
187 case 'a':
188 bocols = 0;
189 break;
190 case 'd':
191 bocols = 1;
192 SET_FILE_BINARY(stdout);
193 break;
194 case 'f':
195 bocols = -1;
196 SET_FILE_BINARY(stdout);
197 break;
198 default:
199 goto userr;
200 }
201 break;
202 case 'w':
203 nowarn = !nowarn;
204 break;
205 case 'u':
206 unbuff = !unbuff;
207 break;
208 default:;
209 userr:
210 eputs("Usage: ");
211 eputs(argv[0]);
212 eputs(" [-b][-l][-n][-p][-w][-u][-tS][-s svar=sval][-e expr][-f source][-i infmt][-o outfmt] [file]\n");
213 quit(1);
214 }
215 if (bocols)
216 SET_FILE_BINARY(stdout);
217 if (noinput) { /* produce a single output record */
218 if (i < argc) {
219 eputs(argv[0]);
220 eputs(": file argument(s) incompatible with -n\n");
221 quit(1);
222 }
223 eclock++;
224 putout();
225 quit(0);
226 }
227 if (nbicols)
228 SET_FILE_BINARY(stdin);
229
230 if (blnkeq) /* for efficiency */
231 nbsynch();
232
233 if (i == argc) /* from stdin */
234 execute(NULL);
235 else /* from one or more files */
236 for ( ; i < argc; i++)
237 execute(argv[i]);
238
239 quit(0);
240 return 0; /* pro forma return */
241 }
242
243
244 static void
245 nbsynch(void) /* non-blank starting synch character */
246 {
247 if (inpfmt == NULL || (inpfmt->type & F_TYP) != T_LIT)
248 return;
249 while (isblnk(*inpfmt->f.sl))
250 inpfmt->f.sl++;
251 if (!*inpfmt->f.sl)
252 inpfmt = inpfmt->next;
253 }
254
255
256 static int
257 getinputrec( /* get next input record */
258 FILE *fp
259 )
260 {
261 if (inpfmt != NULL)
262 return(getrec());
263 if (nbicols > 0)
264 return(fread(inpbuf, sizeof(double),
265 nbicols, fp) == nbicols);
266 if (nbicols < 0)
267 return(fread(inpbuf, sizeof(float),
268 -nbicols, fp) == -nbicols);
269 return(fgets(inpbuf, INBSIZ, fp) != NULL);
270 }
271
272
273 static void
274 execute( /* process a file */
275 char *file
276 )
277 {
278 int conditional = vardefined("cond");
279 long nrecs = 0;
280 long nout = 0;
281 FILE *fp;
282
283 if (file == NULL)
284 fp = stdin;
285 else if ((fp = fopen(file, "r")) == NULL) {
286 eputs(file);
287 eputs(": cannot open\n");
288 quit(1);
289 }
290 if (inpfmt != NULL)
291 initinp(fp);
292
293 while (getinputrec(fp)) {
294 varset("recno", '=', (double)++nrecs);
295 colflg = 0;
296 eclock++;
297 if (!conditional || varvalue("cond") > 0.0) {
298 varset("outno", '=', (double)++nout);
299 putout();
300 }
301 }
302 fclose(fp);
303 }
304
305
306 static void
307 putout(void) /* produce an output record */
308 {
309
310 colpos = 0;
311 if (outfmt != NULL)
312 putrec();
313 else if (bocols)
314 chanout(bchanset);
315 else
316 chanout(chanset);
317 if (colpos && !bocols)
318 putchar('\n');
319 if (unbuff)
320 fflush(stdout);
321 }
322
323
324 static double
325 l_in(char *funame) /* function call for $channel */
326 {
327 int n;
328 register char *cp;
329 /* get argument as integer */
330 n = (int)(argument(1) + .5);
331 if (n != 0) /* return channel value */
332 return(chanvalue(n));
333 /* determine number of channels */
334 if (noinput || inpfmt != NULL)
335 return(0);
336 if (nbicols > 0)
337 return(nbicols);
338 if (nbicols < 0)
339 return(-nbicols);
340 cp = inpbuf; /* need to count */
341 for (n = 0; *cp; )
342 if (blnkeq && isspace(sepchar)) {
343 while (isspace(*cp))
344 cp++;
345 n += *cp != '\0';
346 while (*cp && !isspace(*cp))
347 cp++;
348 } else {
349 n += *cp != '\n';
350 while (*cp && *cp++ != sepchar)
351 ;
352 }
353 return(n);
354 }
355
356 double
357 chanvalue( /* return value for column n */
358 int n
359 )
360 {
361 int i;
362 register char *cp;
363
364 if (noinput || inpfmt != NULL) {
365 eputs("no column input\n");
366 quit(1);
367 }
368 if (n < 1) {
369 eputs("illegal channel number\n");
370 quit(1);
371 }
372 if (nbicols > 0) {
373 if (n > nbicols)
374 return(0.0);
375 cp = inpbuf + (n-1)*sizeof(double);
376 return(*(double *)cp);
377 }
378 if (nbicols < 0) {
379 if (n > -nbicols)
380 return(0.0);
381 cp = inpbuf + (n-1)*sizeof(float);
382 return(*(float *)cp);
383 }
384 if (n <= MAXCOL && colflg & 1L<<(n-1))
385 return(colval[n-1]);
386
387 cp = inpbuf;
388 for (i = 1; i < n; i++)
389 if (blnkeq && isspace(sepchar)) {
390 while (isspace(*cp))
391 cp++;
392 while (*cp && !isspace(*cp))
393 cp++;
394 } else
395 while (*cp && *cp++ != sepchar)
396 ;
397
398 while (isspace(*cp)) /* some atof()'s don't like tabs */
399 cp++;
400
401 if (n <= MAXCOL) {
402 colflg |= 1L<<(n-1);
403 return(colval[n-1] = atof(cp));
404 } else
405 return(atof(cp));
406 }
407
408
409 void
410 chanset( /* output column n */
411 int n,
412 double v
413 )
414 {
415 if (colpos == 0) /* no leading separator */
416 colpos = 1;
417 while (colpos < n) {
418 putchar(sepchar);
419 colpos++;
420 }
421 printf("%.9g", v);
422 }
423
424
425 void
426 bchanset( /* output binary channel n */
427 int n,
428 double v
429 )
430 {
431 static char zerobuf[sizeof(double)];
432
433 while (++colpos < n)
434 fwrite(zerobuf,
435 bocols>0 ? sizeof(double) : sizeof(float),
436 1, stdout);
437 if (bocols > 0)
438 fwrite(&v, sizeof(double), 1, stdout);
439 else {
440 float fval = v;
441 fwrite(&fval, sizeof(float), 1, stdout);
442 }
443 }
444
445
446 static void
447 readfmt( /* read record format */
448 char *spec,
449 int output
450 )
451 {
452 int fd;
453 char *inptr;
454 struct field fmt;
455 int res;
456 register struct field *f;
457 /* check for inline format */
458 for (inptr = spec; *inptr; inptr++)
459 if (*inptr == '$')
460 break;
461 if (*inptr) /* inline */
462 inptr = spec;
463 else { /* from file */
464 if ((fd = open(spec, 0)) == -1) {
465 eputs(spec);
466 eputs(": cannot open\n");
467 quit(1);
468 }
469 res = read(fd, inpbuf+2, INBSIZ-2);
470 if (res <= 0 || res >= INBSIZ-1) {
471 eputs(spec);
472 if (res < 0)
473 eputs(": read error\n");
474 else if (res == 0)
475 eputs(": empty file\n");
476 else if (res >= INBSIZ-1)
477 eputs(": format too long\n");
478 quit(1);
479 }
480 close(fd);
481 (inptr=inpbuf+2)[res] = '\0';
482 }
483 f = &fmt; /* get fields */
484 while ((res = readfield(&inptr)) != F_NUL) {
485 f->next = (struct field *)emalloc(sizeof(struct field));
486 f = f->next;
487 f->type = res;
488 switch (res & F_TYP) {
489 case T_LIT:
490 f->f.sl = savqstr(inpbuf);
491 break;
492 case T_STR:
493 f->f.sv = getsvar(inpbuf);
494 break;
495 case T_NUM:
496 if (output)
497 f->f.ne = eparse(inpbuf);
498 else
499 f->f.nv = savestr(inpbuf);
500 break;
501 }
502 /* add final newline if necessary */
503 if (!igneol && *inptr == '\0' && inptr[-1] != '\n')
504 inptr = "\n";
505 }
506 f->next = NULL;
507 if (output)
508 outfmt = fmt.next;
509 else
510 inpfmt = fmt.next;
511 }
512
513
514 static int
515 readfield( /* get next field in format */
516 register char **pp
517 )
518 {
519 int type = F_NUL;
520 int width = 0;
521 register char *cp;
522
523 cp = inpbuf;
524 while (cp < &inpbuf[INBSIZ-1] && **pp != '\0') {
525 width++;
526 switch (type) {
527 case F_NUL:
528 if (**pp == '$') {
529 (*pp)++;
530 width++;
531 if (**pp == '{') {
532 type = T_NUM;
533 (*pp)++;
534 continue;
535 } else if (**pp == '(') {
536 type = T_STR;
537 (*pp)++;
538 continue;
539 } else if (**pp != '$') {
540 eputs("format error\n");
541 quit(1);
542 }
543 width--;
544 }
545 type = T_LIT;
546 *cp++ = *(*pp)++;
547 continue;
548 case T_LIT:
549 if (**pp == '$') {
550 width--;
551 break;
552 }
553 *cp++ = *(*pp)++;
554 continue;
555 case T_NUM:
556 if (**pp == '}') {
557 (*pp)++;
558 break;
559 }
560 if (!isspace(**pp))
561 *cp++ = **pp;
562 (*pp)++;
563 continue;
564 case T_STR:
565 if (**pp == ')') {
566 (*pp)++;
567 break;
568 }
569 if (!isspace(**pp))
570 *cp++ = **pp;
571 (*pp)++;
572 continue;
573 }
574 break;
575 }
576 *cp = '\0';
577 return(type | width);
578 }
579
580
581 struct strvar *
582 getsvar( /* get string variable */
583 char *svname
584 )
585 {
586 register struct strvar *sv;
587
588 for (sv = svhead; sv != NULL; sv = sv->next)
589 if (!strcmp(sv->name, svname))
590 return(sv);
591 sv = (struct strvar *)emalloc(sizeof(struct strvar));
592 sv->name = savqstr(svname);
593 sv->val = sv->preset = NULL;
594 sv->next = svhead;
595 svhead = sv;
596 return(sv);
597 }
598
599
600 static void
601 svpreset( /* preset a string variable */
602 char *eqn
603 )
604 {
605 register struct strvar *sv;
606 register char *val;
607
608 for (val = eqn; *val != '='; val++)
609 if (!*val)
610 return;
611 *val++ = '\0';
612 sv = getsvar(eqn);
613 if (sv->preset != NULL)
614 freqstr(sv->preset);
615 if (sv->val != NULL)
616 freqstr(sv->val);
617 sv->val = sv->preset = savqstr(val);
618 *--val = '=';
619 }
620
621
622 static void
623 clearrec(void) /* clear input record variables */
624 {
625 register struct field *f;
626
627 for (f = inpfmt; f != NULL; f = f->next)
628 switch (f->type & F_TYP) {
629 case T_NUM:
630 dremove(f->f.nv);
631 break;
632 case T_STR:
633 if (f->f.sv->val != f->f.sv->preset) {
634 freqstr(f->f.sv->val);
635 f->f.sv->val = f->f.sv->preset;
636 }
637 break;
638 }
639 }
640
641
642 static int
643 getrec(void) /* get next record from file */
644 {
645 int eatline;
646 register struct field *f;
647
648 while (ipb.chr != EOF) {
649 if (blnkeq) { /* beware of nbsynch() */
650 while (isblnk(ipb.chr))
651 resetinp();
652 if (ipb.chr == EOF)
653 return(0);
654 }
655 eatline = (!igneol && ipb.chr != '\n');
656 clearrec(); /* start with fresh record */
657 for (f = inpfmt; f != NULL; f = f->next)
658 if (getfield(f) == -1)
659 break;
660 if (f == NULL) {
661 advinp(); /* got one! */
662 return(1);
663 }
664 resetinp(); /* eat false start */
665 if (eatline) { /* eat rest of line */
666 while (ipb.chr != '\n') {
667 if (ipb.chr == EOF)
668 return(0);
669 resetinp();
670 }
671 resetinp();
672 }
673 }
674 return(0);
675 }
676
677
678 static int
679 getfield( /* get next field */
680 register struct field *f
681 )
682 {
683 static char buf[RMAXWORD+1]; /* no recursion! */
684 int delim, inword;
685 double d;
686 char *np;
687 register char *cp;
688
689 switch (f->type & F_TYP) {
690 case T_LIT:
691 cp = f->f.sl;
692 do {
693 if (blnkeq && isblnk(*cp)) {
694 if (!isblnk(ipb.chr))
695 return(-1);
696 do
697 cp++;
698 while (isblnk(*cp));
699 do
700 scaninp();
701 while (isblnk(ipb.chr));
702 } else if (*cp == ipb.chr) {
703 cp++;
704 scaninp();
705 } else
706 return(-1);
707 } while (*cp);
708 return(0);
709 case T_STR:
710 if (f->next == NULL || (f->next->type & F_TYP) != T_LIT)
711 delim = EOF;
712 else
713 delim = f->next->f.sl[0];
714 cp = buf;
715 do {
716 if (ipb.chr == EOF || ipb.chr == '\n')
717 inword = 0;
718 else if (blnkeq && delim != EOF)
719 inword = isblnk(delim) ?
720 !isblnk(ipb.chr)
721 : ipb.chr != delim;
722 else
723 inword = cp-buf < (f->type & F_WID);
724 if (inword) {
725 *cp++ = ipb.chr;
726 scaninp();
727 }
728 } while (inword && cp < &buf[RMAXWORD]);
729 *cp = '\0';
730 if (f->f.sv->val == NULL)
731 f->f.sv->val = savqstr(buf); /* first setting */
732 else if (strcmp(f->f.sv->val, buf))
733 return(-1); /* doesn't match! */
734 return(0);
735 case T_NUM:
736 if (f->next == NULL || (f->next->type & F_TYP) != T_LIT)
737 delim = EOF;
738 else
739 delim = f->next->f.sl[0];
740 np = NULL;
741 cp = buf;
742 do {
743 if (!((np==NULL&&isblnk(ipb.chr)) || isnum(ipb.chr)))
744 inword = 0;
745 else if (blnkeq && delim != EOF)
746 inword = isblnk(delim) ?
747 !isblnk(ipb.chr)
748 : ipb.chr != delim;
749 else
750 inword = cp-buf < (f->type & F_WID);
751 if (inword) {
752 if (np==NULL && !isblnk(ipb.chr))
753 np = cp;
754 *cp++ = ipb.chr;
755 scaninp();
756 }
757 } while (inword && cp < &buf[RMAXWORD]);
758 *cp = '\0';
759 d = np==NULL ? 0. : atof(np);
760 if (!vardefined(f->f.nv))
761 varset(f->f.nv, '=', d); /* first setting */
762 else if ((d = (varvalue(f->f.nv)-d)/(d==0.?1.:d)) > .001
763 || d < -.001)
764 return(-1); /* doesn't match! */
765 return(0);
766 }
767 return -1; /* pro forma return */
768 }
769
770
771 static void
772 putrec(void) /* output a record */
773 {
774 char fmt[32];
775 register int n;
776 register struct field *f;
777 int adlast, adnext;
778
779 adlast = 0;
780 for (f = outfmt; f != NULL; f = f->next) {
781 adnext = blnkeq &&
782 f->next != NULL &&
783 !( (f->next->type&F_TYP) == T_LIT &&
784 f->next->f.sl[0] == ' ' );
785 switch (f->type & F_TYP) {
786 case T_LIT:
787 fputs(f->f.sl, stdout);
788 adlast = f->f.sl[(f->type&F_WID)-1] != ' ';
789 break;
790 case T_STR:
791 if (f->f.sv->val == NULL) {
792 eputs(f->f.sv->name);
793 eputs(": undefined string\n");
794 quit(1);
795 }
796 n = (int)(f->type & F_WID) - strlen(f->f.sv->val);
797 if (adlast)
798 fputs(f->f.sv->val, stdout);
799 if (!(adlast && adnext))
800 while (n-- > 0)
801 putchar(' ');
802 if (!adlast)
803 fputs(f->f.sv->val, stdout);
804 adlast = 1;
805 break;
806 case T_NUM:
807 n = f->type & F_WID;
808 if (adlast && adnext)
809 strcpy(fmt, "%g");
810 else if (adlast)
811 sprintf(fmt, "%%-%dg", n);
812 else
813 sprintf(fmt, "%%%dg", n);
814 printf(fmt, evalue(f->f.ne));
815 adlast = 1;
816 break;
817 }
818 }
819 }
820
821
822 static void
823 initinp(FILE *fp) /* prepare lookahead buffer */
824
825 {
826 ipb.fin = fp;
827 ipb.beg = ipb.end = inpbuf;
828 ipb.pos = inpbuf-1; /* position before beginning */
829 ipb.chr = '\0';
830 scaninp();
831 }
832
833
834 static void
835 scaninp(void) /* scan next character */
836 {
837 if (ipb.chr == EOF)
838 return;
839 if (++ipb.pos >= &inpbuf[INBSIZ])
840 ipb.pos = inpbuf;
841 if (ipb.pos == ipb.end) { /* new character */
842 if ((ipb.chr = getc(ipb.fin)) != EOF) {
843 *ipb.end = ipb.chr;
844 if (++ipb.end >= &inpbuf[INBSIZ])
845 ipb.end = inpbuf;
846 if (ipb.end == ipb.beg)
847 ipb.beg = NULL;
848 }
849 } else
850 ipb.chr = *ipb.pos;
851 }
852
853
854 static void
855 advinp(void) /* move home to current position */
856 {
857 ipb.beg = ipb.pos;
858 }
859
860
861 static void
862 resetinp(void) /* rewind position and advance 1 */
863 {
864 if (ipb.beg == NULL) /* full */
865 ipb.beg = ipb.end;
866 ipb.pos = ipb.beg;
867 ipb.chr = *ipb.pos;
868 if (passive) /* transmit unmatched character? */
869 fputc(ipb.chr, stdout);
870 if (++ipb.beg >= &inpbuf[INBSIZ])
871 ipb.beg = inpbuf;
872 scaninp();
873 }
874
875
876 void
877 eputs(char *msg)
878 {
879 fputs(msg, stderr);
880 }
881
882
883 void
884 wputs(char *msg)
885 {
886 if (!nowarn)
887 eputs(msg);
888 }
889
890
891 void
892 quit(int code)
893 {
894 exit(code);
895 }