/* uinfile.c: Open a package and fill in a UINFORMAT structure.
 */

/*
 * Copyright (C) 2003 James H. Lowe, Jr.  <jhlowe@acm.org>
 *
 * COPYING TERMS AND CONDITIONS
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
 */

#include "swuser_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include "ahs.h"
#include "uinfile.h"
#include "swlib.h"
#include "swutilname.h"
#include "swgp.h"
#include "swfork.h"


#include "debug_config.h"
#ifdef UINFILENEEDDEBUG
#define UINFILE_E_DEBUG(format) SWBISERROR("UINFILE DEBUG: ", format)
#define UINFILE_E_DEBUG2(format, arg) \
				SWBISERROR2("UINFILE DEBUG: ", format, arg)
#define UINFILE_E_DEBUG3(format, arg, arg1) \
			SWBISERROR3("UINFILE DEBUG: ", format, arg, arg1)
#else
#define UINFILE_E_DEBUG(arg)
#define UINFILE_E_DEBUG2(arg, arg1)
#define UINFILE_E_DEBUG3(arg, arg1, arg2)
#endif /* UINFILENEEDDEBUG */

#define UINFILE_I_INIT_READ_SIZE 124
#define UINFILE_I_TAR_BLOCK_SIZE 512


static 
int
determine_if_has_leading_slash(UINFORMAT * uinformat, char * buffer)
{
	int ret = 0;

	if (uinformat->typeM == USTAR_FILEFORMAT) {
		int eoa;
		struct new_cpio_header * file_hdr = taru_make_header();
		taru_read_in_tar_header2(uinformat->taruM, file_hdr,
							-1, buffer, &eoa, 0);
		if (*ahsStaticGetTarFilename(file_hdr) == '/') ret = 1;
		taru_free_header(file_hdr);
	} else if (uinformat->typeM == CPIO_POSIX_FILEFORMAT) {
		/*
		* cpio odc format.
		*/
		if (*(buffer+70) == '/') ret = 1;
	} else {
		/*
		* newc, crc format.
		*/
		if (*(buffer+104) == '/') ret = 1;
	}
	return ret;
}

static int
uinfile_del_pid(UINFORMAT * uinformat, int pid)
{
	int i = 0;
	UINFILE_E_DEBUG("");
	while (i < (int)(sizeof(uinformat->pidlistM)/sizeof(int))) {
		if ((uinformat->pidlistM)[i] == pid) {
			(uinformat->pidlistM)[i] = 0;
			return 0;
		}
		i++;
	}
	return -1;
}

static int
uinfile_add_pid(UINFORMAT * uinformat, int pid)
{
	int i = 0;
	UINFILE_E_DEBUG("");
	while (i < (int)(sizeof(uinformat->pidlistM)/sizeof(int))) {
		if ((uinformat->pidlistM)[i] == 0) {
			(uinformat->pidlistM)[i] = pid;
			return 0;
		}
		i++;
	}
	return -1;
}

static
char *
uinfile_i_get_name(UINFORMAT * uinformat,
		struct new_cpio_header *file_hdr, int fd,
			int format, int * retval)
{
        *retval = taru_read_header(uinformat->taruM, file_hdr, fd,
						format, NULL, 0);
	if (*retval < 0)
		return NULL;
        return swlib_strdup(ahsStaticGetTarFilename(file_hdr));
}

static
int
uinfile_check_ieee_fd(int fd) {
	int current_buftype; 
	current_buftype = uxfio_fcntl(fd, UXFIO_F_GET_BUFTYPE, 0);
	if (
		current_buftype != UXFIO_BUFTYPE_DYNAMIC_MEM &&
		current_buftype != UXFIO_BUFTYPE_MEM &&
		current_buftype != UXFIO_BUFTYPE_FILE
		) {
		fprintf(stderr,
		"internal error, incorrect usage of uinfile_check_ieee\n");
		return -1;
	}
	if (current_buftype == UXFIO_BUFTYPE_MEM) {
		if (uxfio_fcntl(fd, UXFIO_F_SET_BUFFER_LENGTH, 3072) != 0)
			return -1;
	}
	return fd;
}

static
int
uinfile_detect_ieee(UINFORMAT * uinformat, int oflags)
{
	int ret;
	int nameretval;
	char * name = NULL;
	int mm=0;
	int lead_bytes = 0;
	int allow_generic_tar = oflags & UINFILE_DETECT_OTARALLOW;
	struct new_cpio_header *file_hdr=uinformat->file_hdrM;

	uinformat->swpathM = swpath_open("");

	if (uinfile_check_ieee_fd(uinformat->fdM) < 0) {
		fprintf(stderr, "uinfile: Incorrect uxfio fd settings.\n");
		return -1;
	}

	/* 
	 * Read the package and try to find
	 * the magic "catalog/INDEX" name 
	 */

	UINFILE_E_DEBUG("HERE");
	/* 
	 * read leading directories of package 
	 */
	while (
		mm < UINFILE_IEEE_MAX_LEADING_DIR && 
		((name=uinfile_i_get_name(uinformat, file_hdr,
				uinformat->fdM, uinformat->typeM,
						&nameretval)) != NULL)) {
			if (nameretval <= 0) {
				uinformat->layout_typeM =
					UINFILE_FILELAYOUT_UNKNOWN;
				fprintf (stderr,
					"%s: error: package format read error.\n",
						swlib_utilname_get());
				uxfio_close(uinformat->fdM);
				uinformat->fdM = -1;
				return -1;
			}		
			lead_bytes += nameretval;

			UINFILE_E_DEBUG2("parsing path [%s]", name);
			if (swpath_parse_path(uinformat->swpathM, name) < 0) {
				if (allow_generic_tar == 0) {
					fprintf (stderr,
			"uinfile: swpath_parse_path: error parsing: %s\n",
					name);
					uxfio_close(uinformat->fdM);
					uinformat->fdM = -1;
					return -1;
				} else {
					uinformat->layout_typeM =
						UINFILE_FILELAYOUT_UNKNOWN;
					break;
				}
			}

		if (!fnmatch("*/catalog/INDEX", name, 0) ||
				!fnmatch("catalog/INDEX", name, 0)) {
			UINFILE_E_DEBUG("HERE");
			uinformat->layout_typeM=UINFILE_FILELAYOUT_IEEE;
			break;
		}	
		if ((file_hdr->c_mode & CP_IFMT) != CP_IFDIR) {
			UINFILE_E_DEBUG("HERE");
			if (allow_generic_tar == 0) {
				UINFILE_E_DEBUG("HERE");
				uinformat->layout_typeM = 
						UINFILE_FILELAYOUT_UNKNOWN;
				fprintf (stderr,
			"%s: Package layout_version 1.0 not found\n", swlib_utilname_get());
				uxfio_close(uinformat->fdM);
				uinformat->fdM = -1;
				return -1;
			} else {
				uinformat->layout_typeM = 
						UINFILE_FILELAYOUT_UNKNOWN;
				break;
			}
		}
		mm++;
		swbis_free(name);
	}
	if ((mm >= UINFILE_IEEE_MAX_LEADING_DIR &&
			(allow_generic_tar == 0)) || name == NULL) {
		UINFILE_E_DEBUG("HERE");
		uinformat->layout_typeM = UINFILE_FILELAYOUT_UNKNOWN;
		fprintf (stderr,
		"%s: Package layout_version 1.0 not found.\n", swlib_utilname_get());
		uxfio_close(uinformat->fdM);
		uinformat->fdM = -1;
		return -1;
	}

	/* seek back to the begining of the header. */
	UINFILE_E_DEBUG("HERE");
	if ((ret=uxfio_lseek(uinformat->fdM,
				-lead_bytes, UXFIO_SEEK_VCUR)) != 0) {
		fprintf(stderr,
	"uxfio_lseek error in uinfile_handle_return 0015. off=%d ret=%d\n",
							lead_bytes, ret);
		return -1;
	}
	taruib_unread(lead_bytes);
	UINFILE_E_DEBUG("HERE");
	return uinformat->fdM;
}

static
int
uinfile_handle_return(UINFORMAT * uinformat, int oflags, int uxfio_buffer_type)
{
	struct new_cpio_header *file_hdr=uinformat->file_hdrM;

	UINFILE_E_DEBUG("");
	UINFILE_E_DEBUG2("flags = %d", oflags);
	UINFILE_E_DEBUG2("uxfio_buffer_type = %d", uxfio_buffer_type);

	ahsStaticSetTarFilename(file_hdr, NULL);
	ahsStaticSetTarLinkname(file_hdr, NULL);

	UINFILE_E_DEBUG2("HERE current_pos_=%d", (int)((uinformat)->current_pos_));
	if (uxfio_lseek(uinformat->fdM, (off_t)(0), SEEK_SET) < 0) {
		UINFILE_E_DEBUG("HERE");
		fprintf(stderr,
		"uxfio_lseek error in uinfile_handle_return 0001. fd=%d\n",
			uinformat->fdM);
		return -1;
	}
	taruib_set_datalen(0);
	/*
	 * Generic TAR or cpio archive.
	 */

	UINFILE_E_DEBUG("HERE");
	if (oflags & UINFILE_DETECT_FORCEUNIXFD && 
				uinformat->fdM >= UXFIO_FD_MIN) {
		UINFILE_E_DEBUG("HERE");
		uxfio_fcntl(uinformat->fdM, UXFIO_F_ARM_AUTO_DISABLE, 1);
		uinformat->fdM = swlib_fork_to_make_unixfd(uinformat->fdM,
					&(uinformat->blockmask_),
					&(uinformat->defaultmask_),
					(int*)NULL);
	} else if ( 
		(oflags & UINFILE_DETECT_FORCEUXFIOFD &&
					uinformat->fdM < UXFIO_FD_MIN)
		)
	{
		UINFILE_E_DEBUG("HERE");
		uinformat->fdM = uxfio_opendup(uinformat->fdM,
							uxfio_buffer_type);
		uxfio_fcntl(uinformat->fdM, UXFIO_F_SET_CANSEEK, 0);
		UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(uinformat->fdM));
	} else if ( 
		(oflags & UINFILE_DETECT_FORCE_SEEK &&
					uxfio_espipe(uinformat->fdM) && 
		uinformat->fdM < UXFIO_FD_MIN )
		) 
	{
		UINFILE_E_DEBUG("HERE");
		uinformat->fdM = uxfio_opendup(uinformat->fdM,
							uxfio_buffer_type);
		UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(uinformat->fdM));
	} else if ( 
		(oflags & UINFILE_DETECT_FORCE_SEEK &&
					uxfio_espipe(uinformat->fdM) && 
		uinformat->fdM >= UXFIO_FD_MIN )
		) 
	{
		UINFILE_E_DEBUG("HERE");
		uxfio_fcntl(uinformat->fdM, UXFIO_F_SET_BUFTYPE,
							UXFIO_BUFTYPE_MEM);
		UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(uinformat->fdM));
	} else {
		UINFILE_E_DEBUG("HERE");
		;
	}
	if (uinformat->fdM >= UXFIO_FD_MIN) {
	UINFILE_E_DEBUG2("setting uxfio_buffer_type %d", uxfio_buffer_type);
		uxfio_fcntl(uinformat->fdM, UXFIO_F_SET_BUFTYPE,
							uxfio_buffer_type);
	}
	UINFILE_E_DEBUG2("returning fd=%d", uinformat->fdM);
	return uinformat->fdM;
}

static int
uinfile_i_open(char *filename, int oflag, mode_t mode, UINFORMAT ** uinformat,
				int oflags, int xdupfd, int uxfio_buffer_type)
{
	int ret;
	int i;
	int fd, pipe_fd[2], dupfd;
	int refd;
	int zpipe[2];
	pid_t pid;
	char buffer[512];
	char gzmagic[] = UINFILE_MAGIC_gz;
	char Zmagic[] = UINFILE_MAGIC_Z;
	char rpmmagic[] = UINFILE_MAGIC_rpm;
	char bz2magic[] = UINFILE_MAGIC_bz2;
	char debmagic[] = UINFILE_MAGIC_deb;
	char *zcat_command = (char *) (NULL);
	pid_t a_pid;
	int pump_amount = -1;
	int zret = 0;	
	int forcetar;
	int doieee;
	int doarb;
	int dounrpminstall;
	int douncpio;
	int peeklen;
	int control_gz_size = 0;
	int val1;
	int val2;
	char * s_size;

	UINFILE_E_DEBUG("");
	*uinformat=(UINFORMAT *)malloc(sizeof(UINFORMAT));
	if (!(*uinformat)) {
		return -1;
	}
	(*uinformat)->underlying_fdM = -1;
	(*uinformat)->current_pos_ = 0;
	(*uinformat)->zcat_cmdM = zcat_command;
	(*uinformat)->file_hdrM=ahsStaticCreateFilehdr();
	(*uinformat)->verboseM = 0;
	(*uinformat)->has_leading_slashM = 0;
	(*uinformat)->ztypeM = UINFILE_COMPRESSED_NA;
	(*uinformat)->typeM = UNKNOWN_FILEFORMAT;
	memset((*uinformat)->type_revisionM, '\0', sizeof((*uinformat)->type_revisionM));
	(*uinformat)->swpathM = (SWPATH*)(NULL);
	(*uinformat)->taruM = taru_create();
	sigemptyset(&((*uinformat)->blockmask_));
	sigemptyset(&((*uinformat)->defaultmask_));
	sigaddset(&((*uinformat)->blockmask_), SIGALRM);
	sigaddset(&((*uinformat)->defaultmask_), SIGTERM);
	sigaddset(&((*uinformat)->defaultmask_), SIGINT);
	sigaddset(&((*uinformat)->defaultmask_), SIGPIPE);

	forcetar = oflags & UINFILE_DETECT_OTARFORCE;
	doieee = oflags & UINFILE_DETECT_IEEE;
	doarb = oflags & UINFILE_DETECT_ARBITRARY_DATA;
	dounrpminstall = oflags & UINFILE_DETECT_UNRPM;
	douncpio = oflags & UINFILE_DETECT_UNCPIO;

	for (i=0; i < (int)(sizeof((*uinformat)->pidlistM)/sizeof(int)); i++)
					(*uinformat)->pidlistM[i] = 0;

	/* ------------------------------------------------------------- */
	/*            Open file or pipe                                  */
	/* ------------------------------------------------------------- */
	if (filename == (char *) (NULL) ||
				(!strcmp(filename, "-"))) {	/* is a pipe */
		UINFILE_E_DEBUG("HERE PIPE");

		if (filename == (char *) (NULL)) {
			UINFILE_E_DEBUG("HERE");
			dupfd = xdupfd;
		} else {
			UINFILE_E_DEBUG("HERE");
			dupfd = STDIN_FILENO;
		}
		UINFILE_E_DEBUG2("HERE dupfd = %d", dupfd);
		
		val1 = dupfd < UXFIO_FD_MIN ;
		val2 = lseek(dupfd, 0L, SEEK_CUR);

		if (val1 && val2 == -1) {
			UINFILE_E_DEBUG3("HERE %d %s", errno, strerror(errno));
			if (errno == ESPIPE){
				/*
				 * unseekable pipe
				 */
				UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(fd));
				fd = uxfio_opendup(dupfd, uxfio_buffer_type);
				UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(fd));
				if (fd < 0) {
					fprintf(stderr,"uinfile.c: uxfio_opendup failed\n");
					return -1;
				}
			} else {
				perror("uinfile: fatal error");
				return -1;
			}
		} else {
			UINFILE_E_DEBUG("HERE");
			ret = lseek(dupfd, 0L, SEEK_CUR);
			if (ret >= 0) {
				UINFILE_E_DEBUG2("HERE current_pos_=%d", (int)((*uinformat)->current_pos_));
				(*uinformat)->current_pos_ = ret;
				if ((*uinformat)->current_pos_) {
					fprintf(stderr,"uinfile.c: warning: current_pos_ not equal to zero\n");
				}
			} else {
				/*
				fprintf(stderr,"uinfile.c: warning: lseek failed\n");
				*/
			}
			fd = dupfd;
		}
		(*uinformat)->did_dupeM=1;
	} else {		/* is a reg file */
		UINFILE_E_DEBUG("HERE REG FILE");
		fd = open(filename, O_RDONLY, 0);
		if (fd < 0) {
			UINFILE_E_DEBUG("HERE error.\n");
			return fd;
		}
		(*uinformat)->did_dupeM=0;
	}
	refd=fd;
	(*uinformat)->underlying_fdM = refd;
LABEL_REREAD:
	(*uinformat)->fdM = refd;

	/*
	 * Initial read on the serial access archive.
	 * Fixme:
	 */
	if (doarb == 0) {
		UINFILE_E_DEBUG("HERE");
		peeklen = UINFILE_I_INIT_READ_SIZE;
	} else {
		/*
		 * this is used is DETECT_ABRITRARY_DATA is set
		 */
		UINFILE_E_DEBUG("HERE");
		peeklen = 3;
	}	

	memset(buffer, '\0', sizeof(buffer));

	if ((ret=taru_tape_buffered_read(refd, buffer, peeklen)) != peeklen) {
		if (ret) fprintf(stderr,
		"%s: error in initial read. request=%d return=%d\n",
			swlib_utilname_get(), peeklen,  ret);
		return -1;
	}
	UINFILE_E_DEBUG("HERE");

	/*
	 * Identify the file type.
	 */
	if (doarb == 0) {
		if (0) {
			;
		} else if (!memcmp(buffer, rpmmagic, 4) && !forcetar) {
			if (dounrpminstall == 0 && doieee == 0) {
				/*
				 * This is the old historic response to
				 * an RPM file.
				 */
				(*uinformat)->typeM=RPMRHS_FILEFORMAT;
				ret = uinfile_handle_return(*uinformat, oflags,
							uxfio_buffer_type);
				UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(ret));
				return ret;
			} else if (dounrpminstall) {
				/*
				 * Model the RPM as a compression type since this
				 * fits with the code below, e.g. instead of calling
				 * ''gzip -d'', call ''swpackage --unrpm''
				 */
				(*uinformat)->ztypeM = UINFILE_COMPRESSED_RPM;
			} else if (doieee && dounrpminstall == 0) {
				fprintf(stderr,
				"%s: try using the --allow-rpm or --any-format options\n",
						swlib_utilname_get());
				return -1;
			} else {
				SWLIB_EXCEPTION("");
				return -1;
			}
		} else if (douncpio && ((strncmp(buffer, "0707", 4) == 0) &&
				(1/*strncmp(buffer + 257, TMAGIC, 5)*/))) {
			/*
			 * Model the cpio format as a compression type since this
			 * fits with the code below, e.g. instead of calling
			 * ''gzip -d'', call ''arf2arf -H ustar''
			 */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_CPIO;
		} else if (!strncmp(buffer, "0707", 4) &&
				(1/*strncmp(buffer + 257, TMAGIC, 5)*/) &&
								!forcetar) {
			UINFILE_E_DEBUG("HERE its 0707");
			if (!strncmp(buffer, "070702", 6)) { /* new ascii crc */
				(*uinformat)->typeM = CPIO_CRC_FILEFORMAT;
			} else if (!strncmp(buffer, "070701", 6)) {/*new ascii*/
				(*uinformat)->typeM = CPIO_NEWC_FILEFORMAT;
			} else if (!strncmp(buffer, "070707", 6)) {/* POSIX.1 */
				(*uinformat)->typeM = CPIO_POSIX_FILEFORMAT;
			} else {
				SWLIB_EXCEPTION("");
				return -1;
			}
			(*uinformat)->has_leading_slashM =
				determine_if_has_leading_slash(*uinformat,
									buffer);
			ret =  uinfile_handle_return(*uinformat, oflags,
							uxfio_buffer_type);
			if (ret > 0 && doieee) {
				ret = uinfile_detect_ieee(*uinformat, oflags);
			}
			UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(ret));
			return ret;
		} else if (!memcmp(buffer, (void *) gzmagic, 2)) {
						/* gzipped compression */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_GZ;
		} else if (!memcmp(buffer, (void *) bz2magic, 3)) {
							/* bz2 compression */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_BZ2;
		} else if (!memcmp(buffer, (void *) Zmagic, 2)) {
							/* Unix compress */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_Z;
		} else if (!memcmp(buffer, (void *)debmagic, strlen(UINFILE_MAGIC_deb))) {
							/* deb package */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_DEB;
			strncpy((*uinformat)->type_revisionM,
				((char*)buffer)+68, 4);
			/*
			 * Now read some more bytes to include the gzip magic of
			 * the control.tar.gz file
			 */
			if ((ret=taru_tape_buffered_read(refd, buffer+peeklen,
					UINFILE_DEB_CONTROL_OFFSET - peeklen + 4)) != 
				UINFILE_DEB_CONTROL_OFFSET - peeklen + 4) {
				fprintf(stderr,
				"%s: error in second initial read. request=%d return=%d\n",
					swlib_utilname_get(), peeklen,  ret);
				return -1;
			}
		} else {
			/*
			 * If nothing special is detected, assume its tar.
			 */
			if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_NA) {
				(*uinformat)->ztypeM = UINFILE_COMPRESSED_NOT;
			}
			if (taru_tape_buffered_read(refd,
				buffer + peeklen, 512 - peeklen) != (512 - peeklen)) return -1;
			if (!strncmp(buffer + 257, "ustar" /*TMAGIC*/, 5)) {
				/* uncompressed tar */
				/*
				 * This could be a deb format package
				 */
				if (
					(*uinformat)->ztypeM == UINFILE_COMPRESSED_DEB
				) {
					(*uinformat)->typeM=DEB_FILEFORMAT;
					(*uinformat)->has_leading_slashM = 0; /* NO */
					ret = uinfile_handle_return(*uinformat,
							oflags, uxfio_buffer_type);
				} else {
					(*uinformat)->typeM=USTAR_FILEFORMAT;
					(*uinformat)->has_leading_slashM =
						determine_if_has_leading_slash(
									*uinformat,
									buffer);
					ret = uinfile_handle_return(*uinformat,
							oflags, uxfio_buffer_type);
					if (ret > 0 && doieee) {
						ret = uinfile_detect_ieee(*uinformat,
										oflags);
					}
				}
				UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(ret));
				return ret;
			} else {
				uxfio_close(refd);
				swbis_free(*uinformat);
				fprintf(stderr,
				"%s: error: uinfile: unrecognized format\n",
						swlib_utilname_get());
				return -1;
			}
		}
	} else {
		if (!memcmp(buffer, (void *) gzmagic, 2)) {
						/* gzipped compression */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_GZ;
		} else if (!memcmp(buffer, (void *) bz2magic, 3)) {
							/* bz2 compression */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_BZ2;
		} else if (!memcmp(buffer, (void *) Zmagic, 2)) {
							/* Unix compress */
			(*uinformat)->ztypeM = UINFILE_COMPRESSED_Z;
		} else {
			ret =  uinfile_handle_return(*uinformat,
						oflags, uxfio_buffer_type);
			return ret;
		}
	}
	
	/* ------------------------------------------------------------- */
	/*     If we're here then it must be a compressed file.          */
	/*     -or- its an RPM and UINFILE_DETECT_RPM is ON.		 */
	/* ------------------------------------------------------------- */

	UINFILE_E_DEBUG("HERE its compressed");
	{ /* Compressed file. */

	SHCMD *unzip[2];

	unzip[0] = (SHCMD*)NULL;
	unzip[1] = (SHCMD*)NULL;

	UINFILE_E_DEBUG2("HERE current_pos_=%d", (int)((*uinformat)->current_pos_));
	if (uxfio_lseek(refd, (off_t)((*uinformat)->current_pos_), SEEK_SET) !=
		(off_t)((*uinformat)->current_pos_)
	) {
		fprintf(stderr, "%s: %s: uxfio_lseek error. 001.1 pos=%d\n",
			swlib_utilname_get(), __FILE__, ((*uinformat)->current_pos_)); 
		return -1;
	}
	if (zcat_command && (*uinformat)->ztypeM == UINFILE_COMPRESSED_Z) {
		unzip[0] = shcmd_open();
		shcmd_add_arg(unzip[0], "compress");
		shcmd_add_arg(unzip[0], "-cd");
	} else if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_DEB) {
		/*
		 * Make some tests on the first few bytes of the package
		 */
		UINFILE_E_DEBUG("in deb code");
		if (memcmp(((char*)(buffer)) + UINFILE_DEB_CONTROL_OFFSET,
				gzmagic, 3) == 0) {
			unzip[0] = shcmd_open();
			shcmd_add_arg(unzip[0], "gzip");
			shcmd_add_arg(unzip[0], "-d");
		} else if (memcmp(((char*)(buffer)) + UINFILE_DEB_CONTROL_OFFSET,
				bz2magic, 3) == 0) {
			/*
			 * preemptive support for bzip2'ed debian packages
			 */
			unzip[0] = shcmd_open();
			shcmd_add_arg(unzip[0], "bzip2");
			shcmd_add_arg(unzip[0], "-d");
		} else {
			/*
			 * error, the compressed magic not found at the expected offset
			 */
			return -1;
		} 
		/*
		 * grab the size of the control.tar.gz archive from member
		 */
		s_size = (char*)buffer + 
				( strlen(UINFILE_MAGIC_ar) +
				TARU_AR_HEADER_SIZE +
				4 + /* member payload length i.e. "2.0\n" */
				TARU_AR_SIZE_OFFSET );

		control_gz_size = swlib_atoi(s_size, NULL);

		if (control_gz_size <= 0 || control_gz_size > 100000000) {
			/*
			 * sanity fails
			 */
			return -1;
		}

		if (uxfio_lseek(refd, UINFILE_DEB_CONTROL_OFFSET, SEEK_SET) != UINFILE_DEB_CONTROL_OFFSET) {
			fprintf(stderr, "%s: %s: uxfio_lseek error. 001.3 pos=%d\n",
				swlib_utilname_get(), __FILE__, ((*uinformat)->current_pos_)); 
			return -1;
		} else {
			/*
			 * The file is now positioned at the control.tar.gz file
			 */
			; /* continue */
		}
	} else if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_GZ) {
		UINFILE_E_DEBUG("in gzip code");
		unzip[0] = shcmd_open();
		shcmd_add_arg(unzip[0], "gzip");
		shcmd_add_arg(unzip[0], "-d");
	} else if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_BZ2) {
		UINFILE_E_DEBUG("in bzip code");
		unzip[0] = shcmd_open();
		shcmd_add_arg(unzip[0], "bzip2");
		shcmd_add_arg(unzip[0], "-d");
	} else if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_CPIO) {
		UINFILE_E_DEBUG("in unrpm code");
		unzip[0] = shcmd_open();
		shcmd_add_arg(unzip[0], SWBISLIBEXECDIR "/swbis/arf2arf");
		shcmd_add_arg(unzip[0], "-H");
		shcmd_add_arg(unzip[0], "ustar");
	} else if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_RPM) {
		UINFILE_E_DEBUG("in unrpm code");
		unzip[0] = shcmd_open();
		/* shcmd_add_arg(unzip[0], "swbis"); */
		shcmd_add_arg(unzip[0], SWBISBINDIR "/swpackage");
		shcmd_add_arg(unzip[0], "--unrpm");
		/*
		shcmd_add_arg(unzip[0], "--no-defaults");
		shcmd_add_arg(unzip[0], "-W");
		shcmd_add_arg(unzip[0], "files");
		shcmd_add_arg(unzip[0], "-W");
		shcmd_add_arg(unzip[0], "file-digests");
		shcmd_add_arg(unzip[0], "-W");
		shcmd_add_arg(unzip[0], "archive-digests");
		shcmd_add_arg(unzip[0], "-W");
		shcmd_add_arg(unzip[0], "dummy-sign");
		*/
		shcmd_add_arg(unzip[0], "--catalog-owner=0");
		shcmd_add_arg(unzip[0], "--catalog-group=0");
		shcmd_add_arg(unzip[0], "@-");
	} else {
		fprintf(stderr,
			"commpression format not supported by uinfile_open.\n");
		return -1;
	}

	/* -------------------------------------------------------- */
	/*     Uncompress the file                                  */
	/* -------------------------------------------------------- */

	if (pipe(pipe_fd) < 0) {
		fprintf(stderr, "%s", strerror(errno));
		uxfio_close(refd);
		return -1;
	}
	if ((pid = swndfork(&((*uinformat)->blockmask_),
			&((*uinformat)->defaultmask_))) != 0) {
						/* read compressed file */
		/*
		* Parent
		*/
		if (pid < 0) {
			fprintf(stderr,"fork failed.\n");
			uxfio_close(refd);
			return -1;
		}
		uinfile_add_pid(*uinformat, (int)pid);
		close(pipe_fd[1]);
		refd=uxfio_opendup(pipe_fd[0], uxfio_buffer_type);
		UINFILE_E_DEBUG2("HERE\n%s", uxfio_dump_string(refd));

		/*
		 * Re-read the alledgedly uncompressed file.
		 */
		goto LABEL_REREAD;
	} else {
		/* 
		 * Child 
		 */
		int jlxx;
		int b_closefd = -1;
		char ** argvector;
	
		close(pipe_fd[0]);
		/*
		 * This is the master child. clear all the pids.
		 */
		for (i=0;
			i < (int)(sizeof((*uinformat)->pidlistM)/sizeof(int));
					i++)
						(*uinformat)->pidlistM[i] = 0;

		if (unzip[0] != NULL) {
			/*
			* Use the command to uncompress the stream.
			*/
			UINFILE_E_DEBUG("HERE ");
			shcmd_set_dstfd(unzip[0], pipe_fd[1]);
			zpipe[0]=-1;
			zpipe[1]=-1;

			/*
			 * If its a uxfio descriptor then we must fork.
			 */
			if (
				1 && (
				refd >= UXFIO_FD_MIN ||
				(*uinformat)->ztypeM == UINFILE_COMPRESSED_DEB
				)
			) {
				UINFILE_E_DEBUG("HERE ");
				if ((*uinformat)->ztypeM == UINFILE_COMPRESSED_DEB) {
					/*
					 * pump the exact size of the control.tar.gz file
					 */
					UINFILE_E_DEBUG("HERE");
					pump_amount = control_gz_size;
				} else {
					/*
					 * pump until the end
					 */
					UINFILE_E_DEBUG("HERE");
					pump_amount = -1;
				}

				pipe(zpipe);
				a_pid = swndfork(&((*uinformat)->blockmask_),
						&((*uinformat)->defaultmask_));
				if (a_pid > 0) {	
					UINFILE_E_DEBUG("HERE ");
					uxfio_close(refd);
					close(zpipe[1]);
					uinfile_add_pid(*uinformat,
							(int)a_pid);
				} else if (a_pid == 0) {
					UINFILE_E_DEBUG("HERE ");
					close(zpipe[0]);
					if ((*uinformat)->ztypeM != UINFILE_COMPRESSED_DEB) {
						uxfio_fcntl(refd,
						UXFIO_F_ARM_AUTO_DISABLE, 1);
						UINFILE_E_DEBUG2("HERE current_pos_=%d", (int)((*uinformat)->current_pos_));
						if (uxfio_lseek(refd, (off_t)((*uinformat)->current_pos_), SEEK_SET) < 0)
							fprintf(stderr,
						"uxfio_lseek error in child 002.\n");
					}
					taru_pump_amount2(zpipe[1],
								refd, pump_amount, -1); 
					close(zpipe[1]);
					uxfio_close(refd);
					_exit(0);
				} else {
					UINFILE_E_DEBUG("HERE ");
					fprintf(stderr, "%s\n",
							strerror(errno));	
					exit(29);
				}
				shcmd_set_srcfd(unzip[0], zpipe[0]);
				b_closefd =  zpipe[0];
				UINFILE_E_DEBUG("HERE ");
			} else {	
				UINFILE_E_DEBUG("HERE ");
				shcmd_set_srcfd(unzip[0], refd);
				b_closefd = refd;
			}
			UINFILE_E_DEBUG("HERE ");
			shcmd_set_exec_function(unzip[0], "execvp");
			swgp_signal(SIGPIPE, SIG_DFL);
			shcmd_cmdvec_exec(unzip);
			jlxx = shcmd_cmdvec_wait(unzip);
			UINFILE_E_DEBUG("HERE ");
			zret = shcmd_get_exitval(unzip[0]);
			UINFILE_E_DEBUG("HERE ");
			argvector = shcmd_get_argvector(unzip[0]);
			if (argvector && argvector[0] &&
					 strcmp(argvector[0], "gzip") == 0) {
				if (zret == 2) {
					fprintf(stderr, 
			"%s: Warning: gzip exited with a warning.\n", swlib_utilname_get());
					zret = 63;
				} else if (zret == SHCMD_UNSET_EXITVAL) {
					/*
					* This happens when the whole package
					* is not read. such as when writing
					* out only the catalog section.
					*/
					zret = 0;
				} else if (zret != 0) {
					fprintf(stderr, 
		"%s: Error: gzip exited with an error: pid=%d exit value=%d.\n", swlib_utilname_get(),
						(int)(unzip[0]->pid_), (int)zret);
				}
			} else {	
				if (zret && zret != SHCMD_UNSET_EXITVAL) {
				fprintf(stderr,
		"%s: decompression process exiting with value %d.\n", swlib_utilname_get(),
							zret);
				}
			}
		} else {
			fprintf(stderr, " uinfile: internal error 84.001\n");
		}

		/*
		 * Now wait on all the pids.
		 */
		if (b_closefd >= 0) {
			uxfio_close(b_closefd);
		}
		close(pipe_fd[1]);
		while (uinfile_wait_on_all_pid(*uinformat, WNOHANG) < 0) {
			sleep(1); 
		}
		_exit(zret);
	} /* Child */

	if (unzip[0]) {
		shcmd_close(unzip[0]);
	}
	
	} /* End --  Compressed file. */

	/*
	* The child returns -1, but it never should get here.
	*/
	fprintf(stderr, " uinfile: internal error 85.002\n");
	return -1;
}
/*
 * -------------- Public Functions ------------------
 */

int
uinfile_open(char *filename,  mode_t mode, UINFORMAT ** uinformat, int oflags)
{
	int ret;
	int buftype;
	UINFILE_E_DEBUG("");
	buftype = uinfile_decode_buftype(oflags, UXFIO_BUFTYPE_DYNAMIC_MEM);
	ret = uinfile_i_open(filename, -1, mode, uinformat,
					oflags, -1, buftype);
	return ret;
}

int
uinfile_opendup(int xdupfd, mode_t mode, UINFORMAT ** uinformat, int oflags)
{
	int ret;
	int buftype;
	UINFILE_E_DEBUG("");
	buftype = uinfile_decode_buftype(oflags, UXFIO_BUFTYPE_DYNAMIC_MEM);
	ret = uinfile_i_open((char *)NULL, -1, mode,
				uinformat, oflags, xdupfd, buftype);
	return ret;
}

int
uinfile_wait_on_pid(UINFORMAT * uinformat, int pid, int flag, int * status)
{
	int ret;
	UINFILE_E_DEBUG("ENTERING");
	if (uinformat->verboseM)
			fprintf(stderr,
			"Entering uinfile_wait_on_pid: pid=%d options=%d.\n",
					pid, flag);
	UINFILE_E_DEBUG2("waiting on pid %d", pid);
	ret = waitpid((pid_t)pid, status, flag);
	if (uinformat->verboseM) 
		fprintf(stderr,
	"uinformat: waitpid(pid=%d, options=%d) returned %d, status=%d\n", 
			pid, flag, ret, *status);
	if (ret > 0) {
		if (uinformat->verboseM)
			fprintf(stderr, "uinformat: clearing pid %d\n", pid);
		uinfile_del_pid(uinformat, pid);
	}
	UINFILE_E_DEBUG("LEAVING");
	return ret;
}

int
uinfile_wait_on_all_pid(UINFORMAT * uinformat, int flag)
{
	int retval = 0;
	int ret;
	int i = 0;
	int m;
	int status;
	int gotgzwarn = 0;

	UINFILE_E_DEBUG("");
	if (uinformat->verboseM)
			fprintf(stderr, "Entering uinfile_wait_on_all_pid\n");
	while (i < (int)(sizeof(uinformat->pidlistM)/sizeof(int)))   {
		if ((uinformat->pidlistM)[i] > 0) {
			if (uinformat->verboseM)
				fprintf(stderr, "Processing pid %d.\n",
						(uinformat->pidlistM)[i]);
			m = (uinformat->pidlistM)[i];
			ret = uinfile_wait_on_pid(uinformat,
					(uinformat->pidlistM)[i],
							flag, &status);
			if (uinformat->verboseM)
				fprintf(stderr,
			"uinformat: uinfile_wait_on_pid [%d] returned %d\n",
						m, ret);
			if (ret < 0 && flag == WNOHANG) {
				return -1;
			}
			if (ret > 0) {
				if (WIFEXITED(status)) {
					if (WEXITSTATUS(status)) {
						if (WEXITSTATUS(status) == 63) {
							/*
							* this is gzip warning.
							*/
							gotgzwarn = 63;
						} else {
							retval ++;
						}
					}
				} else {
					retval ++;
				}
			}
		}
		i++;
	}
	if (uinformat->verboseM) fprintf(stderr,
		"uinformat: uinfile_wait_on_all_pid returning 0.\n");
	return gotgzwarn + retval;
}

int
uinfile_decode_buftype(int oflags, int v)
{
	UINFILE_E_DEBUG("");
	if ((oflags & UINFILE_UXFIO_BUFTYPE_DYNAMIC_MEM)) {
		v = UXFIO_BUFTYPE_DYNAMIC_MEM;
	} else if ((oflags & UINFILE_UXFIO_BUFTYPE_FILE)) {
		v = UXFIO_BUFTYPE_FILE;
	} else if ((oflags & UINFILE_UXFIO_BUFTYPE_MEM)) {
		v = UXFIO_BUFTYPE_MEM;
	}
	UINFILE_E_DEBUG2("buffer type is %d", v);
	return v;
}

int 
uinfile_get_layout_type(UINFORMAT * uinformat)
{
	return uinformat->layout_typeM;
}

int 
uinfile_get_has_leading_slash(UINFORMAT * uinformat)
{
	return uinformat->has_leading_slashM;
}

int 
uinfile_get_ztype(UINFORMAT * uinformat)
{
	return uinformat->ztypeM;
}

int 
uinfile_get_type(UINFORMAT * uinformat)
{
	return uinformat->typeM;
}

SWPATH *
uinfile_get_swpath(UINFORMAT * uinformat)
{
	return uinformat->swpathM;
}

void
uinfile_set_type(UINFORMAT * uinformat, int type)
{
	uinformat->typeM=type;
}

int
uinfile_close(UINFORMAT * uinformat)
{
	int ret;
	ahsStaticDeleteFilehdr(uinformat->file_hdrM);
	/* uxfio_close(uinformat->fdM); */
	ret = uinfile_wait_on_all_pid(uinformat, 0);
	if (uinformat->verboseM)	
		fprintf(stderr,
			"Leaving uinfile_close with status %d\n", ret);
	swbis_free(uinformat);
	return ret;
}
