some more #include juggling
[catagits/fcgi2.git] / libfcgi / fcgi_stdio.c
1 /*
2  * fcgi_stdio.c --
3  *
4  *      FastCGI-stdio compatibility package
5  *
6  *
7  * Copyright (c) 1996 Open Market, Inc.
8  *
9  * See the file "LICENSE.TERMS" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  */
13
14 #ifndef lint
15 static const char rcsid[] = "$Id: fcgi_stdio.c,v 1.8 1999/07/28 00:24:15 roberts Exp $";
16 #endif /* not lint */
17
18 #include "fcgi_config.h"
19
20 #ifdef _WIN32
21 #define DLLAPI  __declspec(dllexport)
22 #include <windows.h>
23 #endif
24
25 #include <errno.h>  /* for errno */
26 #include <stdarg.h> /* for va_arg */
27 #include <stdlib.h> /* for malloc */
28 #include <string.h> /* for strerror */
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #define NO_FCGI_DEFINES
35 #include "fcgi_stdio.h"
36 #undef NO_FCGI_DEFINES
37
38 #include "fcgiapp.h"
39 #include "fcgios.h"
40
41 #ifndef _WIN32
42
43 extern char **environ;
44
45 /* These definitions should be supplied by stdio.h but for some
46  * reason they get lost on certain platforms. */
47 #ifndef fileno
48 extern int fileno(FILE *stream);
49 #endif
50
51 extern FILE *fdopen(int fildes, const char *type);
52 extern FILE *popen(const char *command, const char *type);
53 extern int pclose(FILE *stream);
54
55 #else /* _WIN32 */
56
57 #define popen _popen
58
59 #endif /* _WIN32 */
60
61 #ifndef FALSE
62 #define FALSE (0)
63 #endif
64
65 #ifndef TRUE
66 #define TRUE  (1)
67 #endif
68
69 FCGI_FILE _fcgi_sF[3];
70
71 \f
72 /*
73  *----------------------------------------------------------------------
74  *
75  * FCGI_Accept --
76  *
77  *      Accepts a new request from the HTTP server and creates
78  *      a conventional execution environment for the request.
79  *
80  *      If the application was invoked as a FastCGI server,
81  *      the first call to FCGI_Accept indicates that the application
82  *      has completed its initialization and is ready to accept
83  *      a request.  Subsequent calls to FCGI_Accept indicate that
84  *      the application has completed its processing of the
85  *      current request and is ready to accept a new request.
86  *
87  *      If the application was invoked as a CGI program, the first
88  *      call to FCGI_Accept is essentially a no-op and the second
89  *      call returns EOF (-1).
90  *
91  * Results:
92  *    0 for successful call, -1 for error (application should exit).
93  *
94  * Side effects:
95  *      If the application was invoked as a FastCGI server,
96  *      and this is not the first call to this procedure,
97  *      FCGI_Accept first performs the equivalent of FCGI_Finish.
98  *
99  *      On every call, FCGI_Accept accepts the new request and
100  *      reads the FCGI_PARAMS stream into an environment array,
101  *      i.e. a NULL-terminated array of strings of the form
102  *      ``name=value''.  It assigns a pointer to this array
103  *      to the global variable environ, used by the standard
104  *      library function getenv.  It creates new FCGI_FILE *s
105  *      representing input from the HTTP server, output to the HTTP
106  *      server, and error output to the HTTP server, and assigns these
107  *      new files to stdin, stdout, and stderr respectively.
108  *
109  *      DO NOT mutate or retain pointers to environ or any values
110  *      contained in it (e.g. to the result of calling getenv(3)),
111  *      since these are freed by the next call to FCGI_Finish or
112  *      FCGI_Accept.  In particular do not use setenv(3) or putenv(3)
113  *      in conjunction with FCGI_Accept.
114  *
115  *----------------------------------------------------------------------
116  */
117 static int acceptCalled = FALSE;
118 static int isCGI = FALSE;
119
120 int FCGI_Accept(void)
121 {
122     if(!acceptCalled) {
123         /*
124          * First call to FCGI_Accept.  Is application running
125          * as FastCGI or as CGI?
126          */
127         isCGI = FCGX_IsCGI();
128         acceptCalled = TRUE;
129         atexit(&FCGI_Finish);
130     } else if(isCGI) {
131         /*
132          * Not first call to FCGI_Accept and running as CGI means
133          * application is done.
134          */
135         return(EOF);
136     }
137     if(isCGI) {
138         FCGI_stdin->stdio_stream = stdin;
139         FCGI_stdin->fcgx_stream = NULL;
140         FCGI_stdout->stdio_stream = stdout;
141         FCGI_stdout->fcgx_stream = NULL;
142         FCGI_stderr->stdio_stream = stderr;
143         FCGI_stderr->fcgx_stream = NULL;
144     } else {
145         FCGX_Stream *in, *out, *error;
146         FCGX_ParamArray envp;
147         int acceptResult = FCGX_Accept(&in, &out, &error, &envp);
148         if(acceptResult < 0) {
149             return acceptResult;
150         }
151         FCGI_stdin->stdio_stream = NULL;
152         FCGI_stdin->fcgx_stream = in;
153         FCGI_stdout->stdio_stream = NULL;
154         FCGI_stdout->fcgx_stream = out;
155         FCGI_stderr->stdio_stream = NULL;
156         FCGI_stderr->fcgx_stream = error;
157         environ = envp;
158     }
159     return 0;
160 }
161 \f
162 /*
163  *----------------------------------------------------------------------
164  *
165  * FCGI_Finish --
166  *
167  *      Finishes the current request from the HTTP server.
168  *
169  * Side effects:
170  *
171  *      Flushes any buffered output to the HTTP server.  Then frees
172  *      all storage allocated by the previous call, including all
173  *      storage reachable from the value of environ set by the previous
174  *      call to FCGI_Accept.
175  *
176  *      DO NOT use stdin, stdout, stderr, or environ between calling
177  *      FCGI_Finish and calling FCGI_Accept.
178  *
179  *      DO NOT mutate or retain pointers to environ or any values
180  *      contained in it (e.g. to the result of calling getenv(3)),
181  *      since these are freed by the next call to FCGI_Finish or
182  *      FCGI_Accept.  In particular do not use setenv(3) or putenv(3)
183  *      in conjunction with FCGI_Accept.
184  *
185  *----------------------------------------------------------------------
186  */
187 void FCGI_Finish(void)
188 {
189     if(!acceptCalled || isCGI) {
190         return;
191     }
192     FCGX_Finish();
193     FCGI_stdin->fcgx_stream = NULL;
194     FCGI_stdout->fcgx_stream = NULL;
195     FCGI_stderr->fcgx_stream = NULL;
196     environ = NULL;
197 }
198 \f
199 /*
200  *----------------------------------------------------------------------
201  *
202  * FCGI_StartFilterData --
203  *
204  *
205  *      The current request is for the filter role, and stdin is
206  *      positioned at EOF of FCGI_STDIN.  The call repositions
207  *      stdin to the start of FCGI_DATA.
208  *      If the preconditions are not met (e.g. FCGI_STDIN has not
209  *      been read to EOF), the call sets the stream error code to
210  *      FCGX_CALL_SEQ_ERROR.
211  *
212  * Results:
213  *      0 for a normal return, < 0 for error
214  *
215  *----------------------------------------------------------------------
216  */
217
218 int FCGI_StartFilterData(void)
219 {
220     if(FCGI_stdin->stdio_stream) {
221         return -1;
222     } else {
223         return FCGX_StartFilterData(FCGI_stdin->fcgx_stream);
224     }
225 }
226 \f
227 /*
228  *----------------------------------------------------------------------
229  *
230  * FCGI_SetExitStatus --
231  *
232  *      Sets the exit status for the current request. The exit status
233  *      is the status code the request would have exited with, had
234  *      the request been run as a CGI program.  You can call
235  *      FCGI_SetExitStatus several times during a request; the last call
236  *      before the request ends (by calling FCGI_Accept) determines the
237  *      value.
238  *
239  *----------------------------------------------------------------------
240  */
241
242 void FCGI_SetExitStatus(int status)
243 {
244     if(FCGI_stdin->fcgx_stream) {
245         FCGX_SetExitStatus(status, FCGI_stdin->fcgx_stream);
246     }
247 }
248 \f
249 /*
250  *----------------------------------------------------------------------
251  *
252  * FCGI_perror --
253  *
254  *       Wrapper for function defined in H&S Section 11.2
255  *
256  *----------------------------------------------------------------------
257  */
258
259 void FCGI_perror(const char *str)
260 {
261     FCGI_fputs(str, FCGI_stderr);
262     FCGI_fputs(": ", FCGI_stderr);
263     FCGI_fputs(strerror(OS_Errno), FCGI_stderr);
264     return;
265 }
266 \f
267 /*
268  *----------------------------------------------------------------------
269  *
270  * FCGI_OpenFromFILE --
271  *
272  *      Constructs a new FCGI_FILE * from the FILE *stream.
273  *
274  * Results:
275  *      NULL if stream == NULL or storage could not be allocated,
276  *      otherwise the new FCGI_FILE *.
277  *
278  *----------------------------------------------------------------------
279  */
280
281 static FCGI_FILE *FCGI_OpenFromFILE(FILE *stream)
282 {
283     FCGI_FILE *fp;
284     if(stream == NULL)
285         return NULL;
286     fp = (FCGI_FILE *)malloc(sizeof(FCGI_FILE));
287     if(fp == NULL)
288         return NULL;
289     fp->stdio_stream = stream;
290     fp->fcgx_stream = NULL;
291     return fp;
292 }
293 \f
294 /*
295  *----------------------------------------------------------------------
296  *
297  * FCGI_fopen, FCGI_fclose, FCGI_fflush, FCGI_freopen --
298  *
299  *       Wrappers for functions defined in H&S Section 15.2
300  *
301  *----------------------------------------------------------------------
302  */
303
304 FCGI_FILE *FCGI_fopen(const char *path, const char *mode)
305 {
306     return FCGI_OpenFromFILE(fopen(path, mode));
307 }
308
309 int FCGI_fclose(FCGI_FILE *fp)
310 {
311     int n = EOF;
312     if(fp->stdio_stream) {
313         n = fclose(fp->stdio_stream);
314         fp->stdio_stream = NULL;
315     } else if(fp->fcgx_stream) {
316         n = FCGX_FClose(fp->fcgx_stream);
317         fp->fcgx_stream = NULL;
318     }
319     if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) {
320         free(fp);
321     }
322     return n;
323 }
324
325 int FCGI_fflush(FCGI_FILE *fp)
326 {
327     if(fp == NULL)
328         return fflush(NULL);
329     if(fp->stdio_stream)
330         return fflush(fp->stdio_stream);
331     else if(fp->fcgx_stream)
332         return FCGX_FFlush(fp->fcgx_stream);
333     return EOF;
334 }
335
336 FCGI_FILE *FCGI_freopen(const char *path, const char *mode,
337                         FCGI_FILE *fp)
338 {
339     if(fp->stdio_stream) {
340         if(freopen(path, mode, fp->stdio_stream) == NULL)
341             return NULL;
342         else
343             return fp;
344     } else if(fp->fcgx_stream) {
345         (void) FCGX_FClose(fp->fcgx_stream);
346         fp->stdio_stream = fopen(path, mode);
347         if(fp->stdio_stream == NULL)
348             return NULL;
349         else {
350             fp->fcgx_stream = NULL;
351             return fp;
352         }
353     }
354     return NULL;
355 }
356 \f
357 /*
358  *----------------------------------------------------------------------
359  *
360  * FCGI_setvbuf, FCGI_setbuf --
361  *
362  *       Wrappers for functions defined in H&S Section 15.3
363  *
364  *----------------------------------------------------------------------
365  */
366 int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size)
367 {
368     if(fp->stdio_stream)
369         return setvbuf(fp->stdio_stream, buf, bufmode, size);
370     else {
371         return -1;
372     }
373 }
374
375 void FCGI_setbuf(FCGI_FILE *fp, char *buf)
376 {
377     if(fp->stdio_stream)
378         setbuf(fp->stdio_stream, buf);
379 }
380 \f
381 /*
382  *----------------------------------------------------------------------
383  *
384  * FCGI_fseek, FCGI_ftell, FCGI_rewind, FCGI_fgetpos, FCGI_fsetpos --
385  *
386  *       Wrappers for functions defined in H&S Section 15.5
387  *
388  *----------------------------------------------------------------------
389  */
390
391 int FCGI_fseek(FCGI_FILE *fp, long offset, int whence)
392 {
393     if(fp->stdio_stream)
394         return fseek(fp->stdio_stream, offset, whence);
395     else {
396         OS_SetErrno(ESPIPE);
397         return -1;
398     }
399 }
400
401 int FCGI_ftell(FCGI_FILE *fp)
402 {
403     if(fp->stdio_stream)
404         return ftell(fp->stdio_stream);
405     else {
406         OS_SetErrno(ESPIPE);
407         return -1;
408     }
409 }
410
411 void FCGI_rewind(FCGI_FILE *fp)
412 {
413     if(fp->stdio_stream)
414         rewind(fp->stdio_stream);
415     else
416         OS_SetErrno(ESPIPE);
417 }
418
419 #ifdef HAVE_FPOS
420 int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos)
421 {
422     if(fp->stdio_stream)
423         return fgetpos(fp->stdio_stream, pos);
424     else {
425         OS_SetErrno(ESPIPE);
426         return -1;
427     }
428 }
429
430 int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos)
431 {
432     if(fp->stdio_stream)
433         return fsetpos(fp->stdio_stream, pos);
434     else {
435         OS_SetErrno(ESPIPE);
436         return -1;
437     }
438 }
439 #endif
440 \f
441 /*
442  *----------------------------------------------------------------------
443  *
444  * FCGI_fgetc, FCGI_getchar, FCGI_ungetc --
445  *
446  *       Wrappers for functions defined in H&S Section 15.6
447  *
448  *       XXX: getc and getchar are generally defined as macros
449  *            for performance reasons
450  *
451  *----------------------------------------------------------------------
452  */
453
454 int FCGI_fgetc(FCGI_FILE *fp)
455 {
456     if(fp->stdio_stream)
457         return fgetc(fp->stdio_stream);
458     else if(fp->fcgx_stream)
459         return FCGX_GetChar(fp->fcgx_stream);
460     return EOF;
461 }
462
463 int FCGI_getchar(void)
464 {
465     return FCGI_fgetc(FCGI_stdin);
466 }
467
468 int FCGI_ungetc(int c, FCGI_FILE *fp)
469 {
470     if(fp->stdio_stream)
471         return ungetc(c, fp->stdio_stream);
472     else if(fp->fcgx_stream)
473         return FCGX_UnGetChar(c, fp->fcgx_stream);
474     return EOF;
475 }
476 \f
477 /*
478  *----------------------------------------------------------------------
479  *
480  * FCGI_fgets, FCGI_gets --
481  *
482  *       Wrappers for functions defined in H&S Section 15.7
483  *
484  *----------------------------------------------------------------------
485  */
486
487 char *FCGI_fgets(char *str, int size, FCGI_FILE *fp)
488 {
489     if(fp->stdio_stream)
490         return fgets(str, size, fp->stdio_stream);
491     else if(fp->fcgx_stream)
492         return FCGX_GetLine(str, size, fp->fcgx_stream);
493     return NULL;
494 }
495
496 /*
497  * There is no standard equivalent of gets that takes an explicit
498  * FILE * argument, so the following "wrapper" implements
499  * gets for both FILE * and FCGX_Stream * in terms of FCGI_getchar.
500  */
501
502 char *FCGI_gets(char *str)
503 {
504     char *s;
505     int c;
506     for (s = str; ((c = FCGI_getchar()) != '\n');) {
507         if(c == EOF) {
508             if(s == str)
509                 return NULL;
510             else
511                 break;
512         } else
513             *s++ = c;
514     }
515     *s = 0;
516     return str;
517 }
518 \f
519 /*
520  *----------------------------------------------------------------------
521  *
522  * --
523  *
524  *       Wrappers for functions defined in H&S Section 15.8
525  *
526  *       XXX: missing: fscanf, scanf
527  *
528  *----------------------------------------------------------------------
529  */
530
531 \f
532 /*
533  *----------------------------------------------------------------------
534  *
535  * FCGI_fputc, FCGI_putchar --
536  *
537  *       Wrappers for functions defined in H&S Section 15.9
538  *
539  *       XXX: putc and putchar are generally defined as macros
540  *            for performance reasons
541  *
542  *----------------------------------------------------------------------
543  */
544
545 int FCGI_fputc(int c, FCGI_FILE *fp)
546 {
547     if(fp->stdio_stream)
548         return fputc(c, fp->stdio_stream);
549     else if(fp->fcgx_stream)
550         return FCGX_PutChar(c, fp->fcgx_stream);
551     else return EOF;
552 }
553
554 int FCGI_putchar(int c)
555 {
556     return FCGI_fputc(c, FCGI_stdout);
557 }
558 \f
559 /*
560  *----------------------------------------------------------------------
561  *
562  * FCGI_fputs, FCGI_puts
563  *
564  *       Wrappers for functions defined in H&S Section 15.10
565  *
566  *----------------------------------------------------------------------
567  */
568
569 int FCGI_fputs(const char *str, FCGI_FILE *fp)
570 {
571     if(fp->stdio_stream)
572         return fputs(str, fp->stdio_stream);
573     else if(fp->fcgx_stream)
574         return FCGX_PutS(str, fp->fcgx_stream);
575     return EOF;
576 }
577
578 int FCGI_puts(const char *str)
579 {
580     int n;
581     if(FCGI_stdout->stdio_stream) {
582         n = fputs(str, FCGI_stdout->stdio_stream);
583         if(n < 0)
584             return n;
585         else
586             return fputc('\n', FCGI_stdout->stdio_stream);
587     } else if(FCGI_stdout->fcgx_stream) {
588         n = FCGX_PutS(str, FCGI_stdout->fcgx_stream);
589         if(n < 0)
590             return n;
591         else
592             return FCGX_PutChar('\n', FCGI_stdout->fcgx_stream);
593     }
594     return EOF;
595 }
596 \f
597 /*
598  *----------------------------------------------------------------------
599  *
600  * FCGI_fprintf, FCGI_printf --
601  *
602  *       Wrappers for functions defined in H&S Section 15.11
603  *
604  *----------------------------------------------------------------------
605  */
606
607 int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...)
608 {
609     va_list ap;
610     int n = 0;
611     va_start(ap, format);
612     if(fp->stdio_stream)
613         n = vfprintf(fp->stdio_stream, format, ap);
614     else if(fp->fcgx_stream)
615         n = FCGX_VFPrintF(fp->fcgx_stream, format, ap);
616     va_end(ap);
617     return n;
618 }
619
620 int FCGI_printf(const char *format, ...)
621 {
622     va_list ap;
623     int n;
624     va_start(ap, format);
625     n = FCGI_vfprintf(FCGI_stdout, format, ap);
626     va_end(ap);
627     return n;
628 }
629 \f
630 /*
631  *----------------------------------------------------------------------
632  *
633  * FCGI_vfprintf, FCGI_vprintf --
634  *
635  *       Wrappers for functions defined in H&S Section 15.12
636  *
637  *----------------------------------------------------------------------
638  */
639
640 int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap)
641 {
642     if(fp->stdio_stream)
643         return vfprintf(fp->stdio_stream, format, ap);
644     else if(fp->fcgx_stream)
645         return FCGX_VFPrintF(fp->fcgx_stream, format, ap);
646     return EOF;
647 }
648
649 int FCGI_vprintf(const char *format, va_list ap)
650 {
651     if(FCGI_stdout->stdio_stream)
652         return vfprintf(FCGI_stdout->stdio_stream, format, ap);
653     else if(FCGI_stdout->fcgx_stream)
654         return FCGX_VFPrintF(FCGI_stdout->fcgx_stream, format, ap);
655     return EOF;
656 }
657 \f
658 /*
659  *----------------------------------------------------------------------
660  *
661  * FCGI_fread, FCGI_fwrite --
662  *
663  *       Wrappers for functions defined in H&S Section 15.13
664  *
665  *----------------------------------------------------------------------
666  */
667
668 size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp)
669 {
670     int n;
671     if(fp->stdio_stream)
672         return fread(ptr, size, nmemb, fp->stdio_stream);
673     else if(fp->fcgx_stream) {
674         if((size * nmemb) == 0) {
675             return 0;
676         }
677         n = FCGX_GetStr((char *) ptr, size * nmemb, fp->fcgx_stream);
678         return (n/size);
679     }
680     return (size_t)EOF;
681 }
682
683 size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp)
684 {
685     int n;
686     if(fp->stdio_stream)
687         return fwrite(ptr, size, nmemb, fp->stdio_stream);
688     else if(fp->fcgx_stream) {
689         if((size * nmemb) == 0) {
690             return 0;
691         }
692         n = FCGX_PutStr((char *) ptr, size * nmemb, fp->fcgx_stream);
693         return (n/size);
694     }
695     return (size_t)EOF;
696 }
697 \f
698 /*
699  *----------------------------------------------------------------------
700  *
701  * FCGI_feof, FCGI_ferror, FCGI_clearerr --
702  *
703  *       Wrappers for functions defined in H&S Section 15.14
704  *
705  *----------------------------------------------------------------------
706  */
707 int FCGI_feof(FCGI_FILE *fp)
708 {
709     if(fp->stdio_stream) {
710         return feof(fp->stdio_stream);
711     } else if (fp->fcgx_stream){
712         return FCGX_HasSeenEOF(fp->fcgx_stream);
713     }
714     return -1;
715
716 }
717
718 int FCGI_ferror(FCGI_FILE *fp)
719 {
720     if(fp->stdio_stream) {
721         return ferror(fp->stdio_stream);
722     } else if(fp->fcgx_stream) {
723         return FCGX_GetError(fp->fcgx_stream);
724     }
725     return -1;
726 }
727
728 void FCGI_clearerr(FCGI_FILE *fp)
729 {
730     if(fp->stdio_stream) {
731         clearerr(fp->stdio_stream);
732     } else if(fp->fcgx_stream) {
733         FCGX_ClearError(fp->fcgx_stream);
734     }
735     return;
736 }
737 \f
738 /*
739  *----------------------------------------------------------------------
740  *
741  * FCGI_tmpfile --
742  *
743  *       Wrappers for function defined in H&S Section 15.16
744  *
745  *----------------------------------------------------------------------
746  */
747 FCGI_FILE *FCGI_tmpfile(void)
748 {
749         return FCGI_OpenFromFILE(tmpfile());
750 }
751
752 \f
753 /*
754  *----------------------------------------------------------------------
755  *
756  * FCGI_fileno, FCGI_fdopen, FCGI_popen, FCGI_pclose --
757  *
758  *       Wrappers for POSIX, X/OPEN functions not in ISO C
759  *
760  *----------------------------------------------------------------------
761  */
762
763 int FCGI_fileno(FCGI_FILE *fp)
764 {
765     if(fp->stdio_stream)
766         return fileno(fp->stdio_stream);
767     else
768         return -1;
769 }
770
771 FCGI_FILE *FCGI_fdopen(int fd, const char *mode)
772 {
773 #ifndef _WIN32
774     return FCGI_OpenFromFILE(fdopen(fd, mode));
775 #else
776     return NULL;
777 #endif
778 }
779
780 FCGI_FILE *FCGI_popen(const char *cmd, const char *type)
781 {
782     return FCGI_OpenFromFILE(popen(cmd, type));
783 }
784
785 int FCGI_pclose(FCGI_FILE *fp)
786 {
787     int n = EOF;
788     if(fp->stdio_stream) {
789 #ifndef _WIN32
790         n = pclose(fp->stdio_stream);
791 #endif
792         fp->stdio_stream = NULL;
793     } else if(fp->fcgx_stream) {
794         /*
795          * The caller is deeply confused; don't free the storage.
796          */
797         return EOF;
798     }
799     if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) {
800         free(fp);
801     }
802     return n;
803 }
804
805 /*
806  *----------------------------------------------------------------------
807  */