1 /*
2  * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
3  *               2002, 2003, 2004
4  *	Ohio University.
5  *
6  * ---
7  *
8  * Starting with the release of tcptrace version 6 in 2001, tcptrace
9  * is licensed under the GNU General Public License (GPL).  We believe
10  * that, among the available licenses, the GPL will do the best job of
11  * allowing tcptrace to continue to be a valuable, freely-available
12  * and well-maintained tool for the networking community.
13  *
14  * Previous versions of tcptrace were released under a license that
15  * was much less restrictive with respect to how tcptrace could be
16  * used in commercial products.  Because of this, I am willing to
17  * consider alternate license arrangements as allowed in Section 10 of
18  * the GNU GPL.  Before I would consider licensing tcptrace under an
19  * alternate agreement with a particular individual or company,
20  * however, I would have to be convinced that such an alternative
21  * would be to the greater benefit of the networking community.
22  *
23  * ---
24  *
25  * This file is part of Tcptrace.
26  *
27  * Tcptrace was originally written and continues to be maintained by
28  * Shawn Ostermann with the help of a group of devoted students and
29  * users (see the file 'THANKS').  The work on tcptrace has been made
30  * possible over the years through the generous support of NASA GRC,
31  * the National Science Foundation, and Sun Microsystems.
32  *
33  * Tcptrace is free software; you can redistribute it and/or modify it
34  * under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * Tcptrace is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with Tcptrace (in the file 'COPYING'); if not, write to the
45  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46  * MA 02111-1307 USA
47  *
48  * Author:	Shawn Ostermann
49  * 		School of Electrical Engineering and Computer Science
50  * 		Ohio University
51  * 		Athens, OH
52  *		ostermann@cs.ohiou.edu
53  *		http://www.tcptrace.org/
54  */
55 #include "tcptrace.h"
56 static char const GCC_UNUSED copyright[] =
57     "@(#)Copyright (c) 2004 -- Ohio University.\n";
58 static char const GCC_UNUSED rcsid[] =
59     "$Header$";
60 
61 
62 #include "compress.h"
63 #include <sys/wait.h>
64 
65 /*
66  * OK, this stuff is a little complicated.  Here's why:
67  * 1) the routines that examine the file to see if it's of
68  *    a particular type want a real file that they can do
69  *    a "seek" on.  Seeking backwards won't work on a stream
70  * 2) What I do for compressed files is to decompress twice:
71  *    - The first time I just save the first COMP_HDR_SIZE bytes
72  *      into a temporary file and then stop the decompression.
73  *      I then use that file to determine that file type
74  *    - After I know the file type, I restart the decompression
75  *      and reconnect the decompress pipe to stdin
76  * 3) If the "file" input _IS_ standard input, then it's harder,
77  *    because I can't restart it.  In that case, I use a helper process
78  *    that reads the rest of the header file and then starts reading
79  *    the rest of the data from standard input.  It's slightly inefficient
80  *    because of the extra process, but I don't know a way around...
81  */
82 
83 
84 /* local routines */
85 static char *FindBinary(char *binname);
86 static struct comp_formats *WhichFormat(char *filename);
87 static FILE *CompSaveHeader(char *filename, struct comp_formats *pf);
88 static int CompOpenPipe(char *filename, struct comp_formats *pf);
89 static FILE *PipeHelper(void);
90 static void PipeFitting(FILE *f_pipe, FILE *f_header, FILE *f_stdin);
91 
92 
93 /* local globals */
94 static int header_length = -1;
95 static Bool is_compressed = FALSE;
96 static FILE * f_orig_stdin = NULL;
97 static int child_pid = -1;
98 static char *tempfile;
99 int posn;
100 
101 
FindBinary(char * binname)102 static char *FindBinary(
103     char *binname)
104 {
105     char *path;
106     char *pch;
107     char *pch_colon;
108     static char abspath[256];
109 
110     /* quick check for absolute path */
111     if (*binname == '/') {
112 	if (access(binname,X_OK) == 0) {
113 	    if (debug>1)
114 		fprintf(stderr,"FindBinary: abs path '%s' is OK\n", binname);
115 	    return(binname);
116 	} else {
117 	    if (debug>1)
118 		fprintf(stderr,"FindBinary: abs path '%s' not found\n", binname);
119 	    return(NULL);
120 	}
121     }
122 
123     path = getenv("PATH");
124     if (path == NULL) {
125 	if (debug)
126 	    fprintf(stderr,"FindBinary: couldn't get PATH envariable\n");
127 	return(NULL);
128     }
129 
130     path = strdup(path);
131     pch = path;
132 
133     while (pch && *pch) {
134 	pch_colon = strchr(pch,':');
135 	if (pch_colon)
136 	    *pch_colon = '\00';
137 
138 	snprintf(abspath,sizeof(abspath),"%s/%s",pch,binname);
139 
140 	if (debug>1)
141 	    fprintf(stderr,"Checking for binary '%s'\n", abspath);
142 	if (access(abspath,X_OK) == 0) {
143 	    if (debug>1)
144 		fprintf(stderr,"FindBinary: found binary '%s'\n", abspath);
145 	    return(abspath);
146 	}
147 
148 	if (pch_colon)
149 	    pch = pch_colon+1;
150 	else
151 	    pch = NULL;
152     }
153 
154     if (debug)
155 	fprintf(stderr,"FindBinary: couldn't find binary '%s' in PATH\n",
156 		binname);
157 
158     return(NULL);
159 }
160 
161 
162 
163 static struct comp_formats *
WhichFormat(char * filename)164 WhichFormat(
165     char *filename)
166 {
167     static struct comp_formats *pf_cache = NULL;
168     static char *pf_file_cache = NULL;
169     int len;
170     int lens;
171     int i;
172 
173     /* check the "cache" :-) */
174     if (pf_file_cache && (strcmp(filename,pf_file_cache) == 0)) {
175 	return(pf_cache);
176     }
177 
178     len = strlen(filename);
179 
180     for (i=0; i < NUM_COMP_FORMATS; ++i) {
181 	struct comp_formats *pf = &supported_comp_formats[i];
182 
183 	if (debug>1)
184 	    fprintf(stderr,"Checking for suffix match '%s' against '%s' (%s)\n",
185 		    filename,pf->comp_suffix,pf->comp_bin);
186 	/* check for suffix match */
187 	lens = strlen(pf->comp_suffix);
188 	if (strcmp(filename+len-lens, pf->comp_suffix) == 0) {
189 	    if (debug>1)
190 		fprintf(stderr,"Suffix match!   '%s' against '%s'\n",
191 			filename,pf->comp_suffix);
192 	    /* stick it in the cache */
193 	    pf_file_cache = strdup(filename);
194 	    pf_cache = pf;
195 	    is_compressed = TRUE;
196 
197 	    /* and tell the world */
198 	    return(pf);
199 	}
200     }
201 
202     pf_file_cache = strdup(filename);
203     pf_cache = NULL;
204     is_compressed = FALSE;
205 
206     if (debug)
207 	fprintf(stderr,"WhichFormat: failed to find compression format for file '%s'\n",
208 		filename);
209 
210     return(NULL);
211 }
212 
213 
214 
215 static FILE *
CompReopenFile(char * filename)216 CompReopenFile(
217     char *filename)
218 {
219     char buf[COMP_HDR_SIZE];
220     struct comp_formats *pf = WhichFormat(filename);
221     int len;
222     int fd;
223     long pos;
224 
225     if (debug>1)
226 	fprintf(stderr,"CompReopenFile('%s') called\n", filename);
227 
228     /* we need to switch from the header file to a pipe connected */
229     /* to a process.  Find out how far we've read from the file */
230     /* so far... */
231     pos = ftell(stdin);
232     if (debug>1)
233 	fprintf(stderr,"CompReopenFile: current file position is %ld\n", pos);
234 
235     /* open a pipe to the original (compressed) file */
236     fd = CompOpenPipe(filename,pf);
237     if (fd == -1)
238 	return(NULL);
239 
240     /* erase the file buffer and reposition to the front */
241 #ifdef HAVE_FPURGE
242     /* needed for NetBSD and FreeBSD (at least) */
243     fpurge(stdin);		/* discard input buffer */
244 #else /* HAVE_FPURGE */
245     fflush(stdin);		/* discard input buffer */
246 #endif /* HAVE_FPURGE */
247     rewind(stdin);
248 
249     /* yank the FD out from under stdin and point to the pipe */
250     dup2(fd,0);
251 
252     /* skip forward in the stream to the same place that we were in */
253     /* for the header file */
254     len = fread(buf,1,pos,stdin);
255     if ((len == 0) && ferror(stdin)) {
256 	perror("read forward in stdin");
257 	exit(-1);
258     }
259 
260     /* OK, I guess we're all set... */
261     return(stdin);
262 }
263 
264 
265 
266 
267 static FILE *
CompSaveHeader(char * filename,struct comp_formats * pf)268 CompSaveHeader(
269     char *filename,
270     struct comp_formats *pf)
271 {
272     FILE *f_stream;
273     FILE *f_file;
274     char buf[COMP_HDR_SIZE];
275     int len;
276     int fd;
277 
278     fd = CompOpenPipe(filename,pf);
279     if (fd == -1)
280 	return(NULL);
281 
282 #ifdef HAVE_MKSTEMP
283     {
284 	/* From Mallman, supposed to be "safer" */
285 	int fd;
286 	extern int mkstemp(char *template);
287 
288 	/* grab a writable string to keep picky compilers happy */
289 	tempfile = strdup("/tmp/trace_hdrXXXXXXXX");
290 
291 	/* create a temporary file name and open it */
292 	if ((fd = mkstemp(tempfile)) == -1) {
293 	    perror("template");
294 	    exit(-1);
295 	}
296 
297 	/* convert to a stream */
298 	f_file = fdopen(fd,"w");
299     }
300 #else /* HAVE_MKSTEMP */
301     /* get a name for a temporary file to store the header in */
302     tempfile = tempnam("/tmp/","trace_hdr");
303 
304     /* open the file */
305     if ((f_file = fopen(tempfile,"w+")) == NULL) {
306 	perror(tempfile);
307 	exit(-1);
308     }
309 
310 #endif /* HAVE_MKSTEMP */
311 
312 
313     /* connect a stdio stream to the pipe */
314     if ((f_stream = fdopen(fd,"r")) == NULL) {
315 	perror("open pipe stream for header");
316 	exit(-1);
317     }
318 
319     /* just grab the first X bytes and stuff into a temp file */
320     len = fread(buf,1,COMP_HDR_SIZE,f_stream);
321     if ((len == 0) && ferror(f_stream)) {
322 	perror("read pipe stream for header");
323 	exit(-1);
324     }
325 
326     if (len == 0) {
327 	/* EOF, failure */
328 	return(NULL);
329     }
330 
331     header_length = len;
332     if (debug>1)
333 	fprintf(stderr,"Saved %d bytes from stream into temp header file '%s'\n",
334 		len, tempfile);
335 
336     /* save the header into a temp file */
337     len = fwrite(buf,1,len,f_file);
338     if ((len == 0) && ferror(f_file)) {
339 	perror("write file stream for header");
340 	exit(-1);
341     }
342 
343     if (debug>1)
344 	fprintf(stderr,"Saved the file header into temp file '%s'\n",
345 		tempfile);
346 
347 
348     /* OK, we have the header, close the file */
349     fclose(f_file);
350 
351     /* if it's stdin, make a copy for later */
352     if (FileIsStdin(filename)) {
353 	f_orig_stdin = f_stream;  /* remember where it is */
354     } else {
355 	fclose(f_stream);
356     }
357 
358     /* re-open the file as stdin */
359     if ((freopen(tempfile,"r",stdin)) == NULL) {
360 	perror("tempfile");
361 	exit(-1);
362     }
363 
364     return(stdin);
365 }
366 
367 
368 
369 static int
CompOpenPipe(char * filename,struct comp_formats * pf)370 CompOpenPipe(
371     char *filename,
372     struct comp_formats *pf)
373 {
374     int fdpipe[2];
375     char *abspath;
376     int i;
377     char *args[COMP_MAX_ARGS];
378 
379     if (debug>1)
380 	fprintf(stderr,"CompOpenPipe('%s') called\n", filename);
381 
382     /* short hand if it's just reading from standard input */
383     if (FileIsStdin(filename)) {
384 	return(dup(0));  /* 0: standard input */
385     }
386 
387     abspath = FindBinary(pf->comp_bin);
388     if (!abspath) {
389 	fprintf(stderr,
390 		"Compression: failed to find binary for '%s' needed to uncompress file\n",
391 		pf->comp_bin);
392 	fprintf(stderr,
393 		"According to my configuration, I need '%s' to decode files of type\n",
394 		pf->comp_bin);
395 	fprintf(stderr, "%s\n", pf->comp_descr);
396 	exit(-1);
397     }
398 
399     /* save the path for later... */
400     pf->comp_bin = strdup(abspath);
401 
402     /* filter args */
403     for (i=0; i < COMP_MAX_ARGS; ++i) {
404 	args[i] = pf->comp_args[i];
405 	if (!args[i])
406 	    break;
407 	if (strcmp(pf->comp_args[i],"%s") == 0)
408 	    args[i] = filename;
409     }
410 
411 
412     if (Mfpipe(fdpipe) == -1) {
413 	perror("pipe");
414 	exit(-1);
415     }
416 
417 #ifdef __VMS
418     child_pid = vfork();
419 #else
420     child_pid = fork();
421 #endif
422     if (child_pid == -1) {
423 	perror("fork");
424 	exit(-1);
425     }
426     if (child_pid == 0) {
427 	/* child */
428 	dup2(fdpipe[1],1);  /* redirect child's stdout to pipe */
429 
430 	/* close all other FDs - lazy, but close enough for our purposes  :-) */
431 	for (i=3; i < 100; ++i) close(i);
432 
433 	if (debug>1) {
434 	    fprintf(stderr,"Execing %s", abspath);
435 	    for (i=1; args[i]; ++i)
436 		fprintf(stderr," %s", args[i]);
437 	    fprintf(stderr,"\n");
438 	}
439 
440 
441 	execv(abspath,args);
442 	fprintf(stderr,"Exec of '%s' failed\n", abspath);
443 	perror(abspath);
444 	exit(-1);
445     }
446 
447     close(fdpipe[1]);
448     return(fdpipe[0]);
449 }
450 
451 
452 
453 
454 
455 FILE *
CompOpenHeader(char * filename)456 CompOpenHeader(
457     char *filename)
458 {
459     FILE *f;
460     struct comp_formats *pf;
461 
462     /* short hand if it's just reading from standard input */
463     if (FileIsStdin(filename)) {
464 	is_compressed = TRUE;	/* pretend that it's compressed */
465 	return(CompSaveHeader(filename,NULL));
466     }
467 
468     /* see if it's a supported compression file */
469     pf = WhichFormat(filename);
470 
471 #ifdef __WIN32
472     if(pf != NULL) {
473        fprintf(stderr, "\nError: windows version of tcptrace does not support\nreading compressed dump files. Uncompress the file\nmanually and try again. Sorry!\n");
474        return((FILE *)-1);
475     }
476     return(NULL);
477 #endif /* __WIN32 */
478 
479     /* if no compression found, just open the file */
480     if (pf == NULL) {
481 	if (freopen(filename,"r",stdin) == NULL) {
482 	    perror(filename);
483 	    return(NULL);
484 	}
485 	return(stdin);
486     }
487 
488     /* open the file through compression */
489     if (debug>1)
490 	printf("Decompressing file of type '%s' using program '%s'\n",
491 	       pf->comp_descr, pf->comp_bin);
492     else if (debug)
493 	printf("Decompressing file using '%s'\n", pf->comp_bin);
494 
495     f = CompSaveHeader(filename,pf);
496 
497     if (!f) {
498 	fprintf(stderr,"Decompression failed for file '%s'\n", filename);
499 	exit(-1);
500     }
501 
502     return(f);
503 }
504 
505 
506 FILE *
CompOpenFile(char * filename)507 CompOpenFile(
508     char *filename)
509 {
510     if (debug>1)
511 	fprintf(stderr,"CompOpenFile('%s') called\n", filename);
512 
513     /* if it isn't compressed, just leave it at stdin */
514     if (!is_compressed)
515 	return(stdin);
516 
517     /* if the header we already saved is the whole file, it must be */
518     /* short, so just read from the file */
519     if (header_length < COMP_HDR_SIZE) {
520 	if (debug>1)
521 	    fprintf(stderr,"CompOpenFile: still using header file, short file...\n");
522 	return(stdin);
523     }
524 
525     /* if we're just reading from standard input, we'll need some help because */
526     /* part of the input is in a file and the rest is still stuck in a pipe */
527     if (FileIsStdin(filename)) {
528 	 posn=ftell(stdin);
529 	 if (posn < 0) {
530 	      perror("CompOpenFile : ftell failed");
531 	      exit(-1);
532 	 }
533 	return(PipeHelper());
534     }
535 
536     /* otherwise, there's more than we saved, we need to re-open the pipe */
537     /* and re-attach it to stdin */
538     return(CompReopenFile(filename));
539 }
540 
541 
542 /* return a FILE * that fill come from a helper process */
543 FILE *
PipeHelper(void)544 PipeHelper(void)
545 {
546     int fdpipe[2];
547     FILE *f_return;
548 
549     /* On coming in, here's what's in the FDs: */
550     /*   stdin: 	has the header file open */
551     /*   f_stdin_file:	holds the rest of the stream */
552 
553     if (Mfpipe(fdpipe) == -1) {
554 	perror("pipe");
555 	exit(-1);
556     }
557     /* remember: fdpipe[0] is for reading, fdpipe[1] is for writing */
558 
559 #ifdef __VMS
560     child_pid = vfork();
561 #else
562     child_pid = fork();
563 #endif
564     if (child_pid == -1) {
565 	perror("fork");
566 	exit(-1);
567     }
568     if (child_pid == 0) {
569 	/* be the helper process */
570 	FILE *f_pipe;
571 
572 	/* attach a stream to the pipe connection */
573 	f_pipe = fdopen(fdpipe[1],"w");
574 	if (f_pipe == NULL) {
575 	    perror("fdopen on pipe for writing");
576 	    exit(-1);
577 	}
578 
579 	/* connect the header file and stream to the pipe */
580 	PipeFitting(f_pipe, stdin, f_orig_stdin);
581 
582 	/* OK, both empty, we're done */
583 	if (debug>1)
584 	    fprintf(stderr,
585 		    "PipeHelper(%d): all done, exiting\n", (int)getpid());
586 
587 	exit(0);
588     }
589 
590     /* I'm still the parent */
591     if (debug>1)
592 	fprintf(stderr,
593 		"PipeHelper: forked off child %d to deal with stdin\n",
594 		child_pid);
595 
596     /* clean up the fd's */
597     close(fdpipe[1]);
598     // Now, we shall purge our old STDIN stream buffer, and point it to the
599     // read end of the pipe, fdpipe[0]
600 
601 #ifdef HAVE_FPURGE
602      fpurge(stdin); // needed for NetBSD/FreeBSD
603 #else
604      fflush(stdin);
605 #endif
606      clearerr(stdin);
607 
608      if (dup2(fdpipe[0],0)==-1) {
609 	  perror("PipeHelper : dup2 failed in parent");
610 	  exit(-1);
611      }
612 
613     /* make a stream attached to the PIPE and return it */
614     f_return = fdopen(fdpipe[0],"r");
615     if (f_return == NULL) {
616 	perror("PipeHelper : fdopen on pipe for reading");
617 	exit(-1);
618     }
619     return(f_return);
620 }
621 
622 
623 static void
PipeFitting(FILE * f_pipe,FILE * f_header,FILE * f_orig_stdin)624 PipeFitting(
625     FILE *f_pipe,
626     FILE *f_header,
627     FILE *f_orig_stdin)
628 {
629     char buf[COMP_HDR_SIZE];		/* just a big buffer */
630     int len;
631 
632     // Fix the file synchronization problems and undefined behavior exhibited
633     // by fread() in managing its buffers, when stdin is opened by both the
634     // parent and child processes.
635     // In the child process (where we are currently executing), close and
636     // re-open the temporary file currently opened as stdin, in which the
637     // first COMP_HDR_SIZE bytes of data were stored. The current file pointer
638     // position in the file was stored in the global variable posn.
639 
640     if (fclose(f_header)<0)
641 	  perror("PipeFitting : fclose failed");
642 
643     if ((f_header=fopen(tempfile,"r"))==NULL) {
644 	 perror("PipeFitting : fopen of tempfile failed");
645 	 exit(-1);
646     }
647 
648     if (fread(buf,1,posn,f_header)!=posn) {
649 	 perror("PipeFitting : fread failed");
650 	 exit(-1);
651     }
652 
653     /* read from f_header (the file) until empty */
654     while (1) {
655 	/* read some more data */
656 	len = fread(buf,1,sizeof(buf),f_header);
657 	if (len == 0)
658 	    break;
659 	if (len < 0) {
660 	    perror("fread from f_header");
661 	    exit(0);
662 	}
663 
664 	if (debug>1)
665 	    fprintf(stderr,
666 		    "PipeFitting: read %d bytes from header file\n", len);
667 
668 	/* send those bytes to the pipe */
669 	if (fwrite(buf,1,len,f_pipe) != len) {
670 	    perror("fwrite on pipe");
671 	    exit(-1);
672 	}
673     }
674 
675     // We are done with the temporary file. Time to close and unlink it.
676     if (fclose(f_header)<0)
677 	  perror("PipeFitting : fclose failed");
678 
679     if (unlink(tempfile)<0)
680 	  perror("PipeFitting : unlink of tempfile failed");
681 
682     if (debug>1)
683 	fprintf(stderr,
684 		"PipeFitting: header file empty, switching to old stdin\n");
685 
686     /* OK, the file is empty, switch back to the stdin stream */
687     while (1) {
688 	/* read some more data */
689 	len = fread(buf,1,sizeof(buf),f_orig_stdin);
690 	if (len == 0)
691 	    break;
692 	if (len < 0) {
693 	    perror("fread from f_orig_stdin");
694 	    exit(0);
695 	}
696 
697 	if (debug>1)
698 	    fprintf(stderr,
699 		    "PipeFitting: read %d bytes from f_orig_stdin\n", len);
700 
701 	/* send those bytes to the pipe */
702 	if (fwrite(buf,1,len,f_pipe) != len) {
703 	    perror("fwrite on pipe");
704 	    exit(-1);
705 	}
706     }
707 }
708 
709 
710 
711 
712 void
CompCloseFile(char * filename)713 CompCloseFile(
714     char *filename)
715 {
716     /* Hmmm... this was commented out, I wonder why? */
717 /*     fclose(stdin); */
718 
719     /* if we have a child, make sure it's dead */
720     if (child_pid != -1) {
721 	kill(child_pid,SIGTERM);
722 	child_pid = -1;
723     }
724 
725     /* in case we have children child still in the background */
726     while (wait(0) != -1)
727 	; /* nothing */
728 
729     /* zero out some globals */
730     header_length = -1;
731 }
732 
733 
734 int
CompIsCompressed(void)735 CompIsCompressed(void)
736 {
737     return(is_compressed);
738 }
739 
740 
741 void
CompFormats(void)742 CompFormats(void)
743 {
744     int i;
745 
746     fprintf(stderr,"Supported Compression Formats:\n");
747     fprintf(stderr,"\tSuffix  Description           Uncompress Command\n");
748     fprintf(stderr,"\t------  --------------------  --------------------------\n");
749 
750     for (i=0; i < NUM_COMP_FORMATS; ++i) {
751 	int arg;
752 	struct comp_formats *pf = &supported_comp_formats[i];
753 
754 	fprintf(stderr,"\t%6s  %-20s  %s",
755 		pf->comp_suffix,
756 		pf->comp_descr,
757 		pf->comp_bin);
758 	for (arg=1; pf->comp_args[arg]; ++arg)
759 	    fprintf(stderr," %s", pf->comp_args[arg]);
760 	fprintf(stderr,"\n");
761     }
762 }
763 
764 
765 /* does the file name "filename" refer to stdin rather than a real file? */
766 /* (in case I need to extend this definition someday) */
767 Bool
FileIsStdin(char * filename)768 FileIsStdin(
769     char *filename)
770 {
771     if (strcmp(filename,"stdin") == 0)
772 	return(1);
773     if (strcmp(filename,"stdin.gz") == 0)
774 	return(1);
775     return(0);
776 }
777