/*  swinstall_lib.c -- the top-level routines of swinstall  */
/*
   Copyright (C) 2004,2005 Jim Lowe
   All Rights Reserved.
  
   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.  */

#define FILENEEDDEBUG 1
#undef FILENEEDDEBUG
#include "swuser_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "strob.h"
#include "cplob.h"
#include "vplob.h"
#include "swlib.h"
#include "usgetopt.h"
#include "ugetopt_help.h"
#include "swcommon0.h"
#include "swcommon.h"
#include "swparse.h"
#include "swfork.h"
#include "swgp.h"
#include "etar.h"
#include "swssh.h"
#include "progressmeter.h"
#include "swevents.h"
#include "to_oct.h"
#include "tarhdr.h"
#include "swinstall.h"
#include "swheader.h"
#include "swheaderline.h"
#include "swicat.h"
#include "strar.h"
#include "swi.h"
#include "atomicio.h"
#include "swutilname.h"

extern struct g_pax_read_command g_pax_read_commands[];

/*
 * These are required by the TEVENTS routine
 */

#define SWBIS_DEFAULT_REVISION "0.0"

#define SCARY_ARRAY_LEN 200

/*  Array of scripts that must be run */
static SCARY * g_script_array[SCARY_ARRAY_LEN+1];

/** -------------------------------------- */

static
void
init_header_root(ETAR * etar)
{
	etar_set_typeflag(etar, REGTYPE);
	etar_set_mode_ul(etar, (unsigned int)(0440));
	etar_set_uid(etar, 0);
	etar_set_gid(etar, 0);
	etar_set_uname(etar, "root");
	etar_set_gname(etar, "root");
}

/*
 This purpose of the g_script_array[i] is
 to store the scripts in a form that can easily
 be indexed and searched by using the tag and tagspec
 as a key.  */

static
void
scary_init_script_array(void)
{
	int i;
	for (i=0; i<SCARY_ARRAY_LEN; i++) {
		g_script_array[i] = (SCARY*)NULL;
	}
}

static
SCARY *
scary_create(char * tag, char * tagspec, SWI_CONTROL_SCRIPT * script)
{
	SCARY * scary;
	scary = (SCARY *)malloc(sizeof(SCARY));
	if (!scary) return scary;
	scary->tagM = strdup(tag);		/* e.g. postinstall or preinstall */
	scary->tagspecM = strdup(tagspec);      /* <product_tag> or <product_tag>.<fileset_tag> */
	scary->scriptM = script;                /* pointer to the script object */
	return scary;
}

static
void
scary_delete(SCARY * scary)
{
	free(scary->tagM);
	free(scary->tagspecM);
	free(scary);
}

static
SCARY *
scary_add(SCARY ** array, SCARY * script)
{
	int i;
	for (i=0; i<SCARY_ARRAY_LEN; i++) {
		if (array[i] == (SCARY*)NULL) {
			array[i] = script;
			return script;
		}
	}
	return NULL;
}

static
void
scary_show_array(SCARY ** array)
{
	int i;
	STROB * tmp;
	tmp = strob_open(18);
	for (i=0; i<SCARY_ARRAY_LEN; i++) {
		if (array[i] == (SCARY*)NULL) {
			break;
		} else {
			swlib_writef(STDERR_FILENO, tmp, "%d tag=[%s] tagspec=[%s] script=[%p]\n",
				i, array[i]->tagM,
				array[i]->tagspecM,
				array[i]->scriptM);
		}
	}
	strob_close(tmp);
	return;
}

static
SWI_CONTROL_SCRIPT *
scary_find_script(SCARY ** array, char * tag, char * tagspec)
{
	int i;
	STROB * tmp;
	SWI_CONTROL_SCRIPT * retval = NULL;

	tmp = strob_open(18);
	for (i=0; i<SCARY_ARRAY_LEN; i++) {
		if (array[i] == (SCARY*)NULL) {
			break;
		} else {
			if (
				strcmp(array[i]->tagM, tag) == 0 &&
				strcmp(array[i]->tagspecM, tagspec) == 0 &&
				1
			) {
				retval = array[i]->scriptM;
				break;
			}
		}
	}
	strob_close(tmp);
	return retval;
}

/** -------------------------------------- */

static
void
audit_execution_scripts(GB * G, SWI * swi, SCARY ** scary_scripts)
{
	SWI_PRODUCT * product;
	SWI_XFILE * fileset;
	SWI_CONTROL_SCRIPT * product_xxinstall_script;
	SWI_CONTROL_SCRIPT * fileset_xxinstall_script;
	STROB * tagspec;
	SCARY * scary_script;
	int fileset_index;
	char * execution_scripts[] = { SW_A_postinstall, SW_A_preinstall, (char*)(NULL) };
	char ** ts;
	char * tag;

	/* SWBIS_DEBUG_PRINT(); */
	tagspec = strob_open(32);
	scary_init_script_array();
	
	/*
	 * FIXME, allow more than one product
	 */

	product = swi_package_get_product(swi->swi_pkgM, 0 /* The first product */);
	ts = execution_scripts;
	for (;tag=(*ts),*ts != (char*)NULL; ts++) {	
		product_xxinstall_script = swi_product_get_control_script_by_tag(product, tag);
		if (product_xxinstall_script) {
			scary_script = scary_create(tag, product->baseM.tagM, product_xxinstall_script);
			scary_add(scary_scripts, scary_script);
		}
	}

	/*
	 * Loop over the filesets
	 */
	/* SWBIS_DEBUG_PRINT(); */

	fileset_index = 0;
	fileset = swi_product_get_fileset(product, fileset_index++);
	while(fileset) {	
		strob_sprintf(tagspec, STROB_NO_APPEND, "%s.%s", product->baseM.tagM, fileset->baseM.tagM);
		ts = execution_scripts;
		for (;tag=(*ts),*ts != (char*)NULL; ts++) {	
			fileset_xxinstall_script = swi_xfile_get_control_script_by_tag(fileset, tag);
			if (fileset_xxinstall_script) {
				scary_script = scary_create(tag, strob_str(tagspec), fileset_xxinstall_script);
				scary_add(scary_scripts, scary_script);
			}
		}
		fileset = swi_product_get_fileset(product, fileset_index++);
	}
	strob_close(tagspec);
}

int
swinstall_lib_write_trailer_blocks(int ofd, int nblocks)
{
	int ret;
	int retval = 0;
	int count = nblocks;
	static char * n = NULL;

	if (!n) {
		n = malloc(512);
		if (!n) {
			SWI_internal_fatal_error();
			return -1;
		}
	}
	memset(n, '\0', 512);

	while (count-- > 0) {
		ret = atomicio((ssize_t (*)(int, void *, size_t))(write),
			ofd, (void*)(n), (size_t)(512));
		if (ret < 0) return -1;
		retval += 512;
	}

	if (retval != (512 * nblocks)) {
		SWI_internal_fatal_error();
		return -1;
	}
	return retval;
}

static
int
write_control_script_code_fragment(GB * G, SWI * swi, SWI_CONTROL_SCRIPT * script, char * swspec_tags, STROB * buf)
{
	STROB * tmp;
	
	tmp = strob_open(100);
	strob_sprintf(tmp, STROB_DO_APPEND,
		"%s %s",
		swspec_tags,
		script->baseM.tagM);

	strob_sprintf(buf, STROB_DO_APPEND,
		"	# Generated by swinstall_lib.c:write_control_script_code_fragment\n"
		"	cd \"%s\"\n"
		"	case $? in\n"
		"		0)\n"
		"			%s\n"
		"			sh control.sh \"%s\" \"%s\"\n"
		"			sw_retval=$?\n"
		"			%s\n"
		"			;;\n"
		"		*)\n"
		"			sw_retval=127\n"
		"			;;\n"
		"	esac\n",
		swi->swi_pkgM->catalog_entryM,
		TEVENT(2, (swi->verboseM), SW_CONTROL_SCRIPT_BEGINS, strob_str(tmp)),
		swspec_tags,
		script->baseM.tagM,
		TEVENT(2, (swi->verboseM), SW_CONTROL_SCRIPT_ENDS, "status=$sw_retval")
		);
	
	strob_close(tmp);
	return 0;
}

static
int
write_case_block(SWI * swi, STROB * buf, char * tag)
{
	STROB * tmp;
	tmp = strob_open(100);

	swicat_write_script_cases(swi, tmp, tag);

	strob_sprintf(buf, STROB_DO_APPEND,
		"# generated by swinstall_lib.c:write_case_block\n"
		"	%s)\n"
		"%s"
		"		;;\n",
		tag, strob_str(tmp));

	strob_close(tmp);
	return 0;
}

static
unsigned long int
get_whole_block_size(unsigned long int size)
{
	unsigned long int ret;
	ret = ((size / TARRECORDSIZE) + 1) * TARRECORDSIZE;
	return ret;
}

static
int
write_session_options_filename(STROB * name, SWI * swi)
{
	strob_strcpy(name, swi->swi_pkgM->catalog_entryM);
	swlib_unix_dircat(name, SW_A_session_options);
	return 0;
}

static
int 
write_session_options_file(STROB * buf, SWI * swi)
{
	strob_strcpy(buf, "");
	swextopt_writeExtendedOptions_strob(buf, (struct extendedOptions *)(swi->optaM), swi->swc_idM, 1);
	return strob_strlen(buf);
}

static
int
write_single_file_tar_archive(SWI * swi, int ofd, char * name, char * data)
{
	ETAR * etar;
	int ret;
	int retval = 0;

	etar = etar_open(swi->xformatM->taruM);
	etar_init_hdr(etar);
	init_header_root(etar);
	etar_set_size(etar, strlen(data));
	if (etar_set_pathname(etar, name)) SWLIB_FATAL("name too long");
	etar_set_chksum(etar);

	/*
	 * Emit the header 
	 */
	ret = etar_emit_header(etar, ofd);
	if (ret < 0) return ret;
	retval += ret;

	/*
	 * Emit the file data
	 */
	ret = etar_emit_data_from_buffer(etar, ofd, data, strlen(data));
	if (ret < 0) return ret;
	retval += ret;

	/*
	 * Emit the trailer blocks
	 */	
	ret = etar_write_trailer_blocks(etar, ofd, 2);
	if (ret < 0) return ret;
	retval += ret;

	etar_close(etar);
	return retval;
}

static
int
load_single_file_tar_archive (GB * G,
	SWI * swi,
	int ofd, 
	char * catalog_path,
	char * pax_read_command,
	int alt_catalog_root,
	int event_fd,
	char * id_str,
	char * name,
	char * data
	)
{
	int ret;
	unsigned long int stdin_file_size;
	STROB * tmp;
	STROB * namebuf;

	tmp = strob_open(10);
	namebuf = strob_open(10);

	/*
	 * Compute the data size that will be sent to the stdin of the 'sh -s'
	 * process on the target host.
	 */

	/* (*f_archive_data)(tmp, swi); */

	stdin_file_size = \
		get_whole_block_size((unsigned long int)strlen(data)) +  /* data plue tar block padding */
		TARRECORDSIZE + /* ustar header */
		TARRECORDSIZE + /* trailer block */
		TARRECORDSIZE + /* trailer block */
		0;
	/*
	 * Here is the task scriptlet to load the installed file.
	 */

	strob_sprintf(tmp, 0,
		"dd 2>/dev/null | %s\n"
		"sw_retval=$?\n"	/* This line is required */
		"dd of=/dev/null 2>/dev/null\n"
		,
		pax_read_command
		);

	/*
	 * Now send the task script to the target host 'sh -s'
	 */

	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 			/* file descriptor */
		stdin_file_size,	/* script stdin file length */
		".",   			/* i.e. stay in the target path */
		strob_str(tmp),		/* The actual task script */
		id_str			/* Id string */
		);
	
	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		return -1;
	}

	/*
	 * Now send the data on the same stdin.
	 * In this case it is a tar archive with one file member
	 */

	/* ret = (*f_write_tar_archive)(swi, ofd, name, data); */
	ret = write_single_file_tar_archive(swi, ofd, name, data);
	if (ret <= 0) {
		SWBIS_ERROR_IMPL();
		return -1;
	}

	/*
	 * Assert what must be true.
	 */

	if (ret != (int)stdin_file_size) {
		fprintf(stderr, "ret=%d  stdin_file_size=%lu\n", ret, stdin_file_size);
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}
	
	/*
	 * Reap the events from the event pipe.
	 */
	ret = swicol_rpsh_task_expect(swi->swicolM,
				event_fd,
				SWICOL_TL_21 /*time limit*/);
	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}

	/*
	 * close and return
	 */
	strob_close(tmp);
	strob_close(namebuf);
	return 0;
}

static
int
write_tar_installed_software_index_file (GB * G, SWI * swi, int ofd,
	char * catalog_path,
	char * pax_read_command,
	int alt_catalog_root,
	int event_fd,
	char * id_str
	)
{
	int ret = 0;
	STROB * name;
	STROB * data;

	name = strob_open(100);
	data = strob_open(100);

	swicat_isf_installed_software(data, swi);

	/*
	 * catalog_entryM = [var/lib/swbis/catalog/swbis/swbis/0.000/0]
	 */

	strob_strcpy(name, swi->swi_pkgM->catalog_entryM);
	swlib_unix_dircat(name, SW_A_INSTALLED);

	ret = load_single_file_tar_archive(G,
		swi,
		ofd, 
		catalog_path,
		pax_read_command,
		alt_catalog_root,
		event_fd,
		id_str,
		strob_str(name),
		strob_str(data));

	strob_close(name);
	strob_close(data);
	return ret;
}

static
int
construct_script(GB * G, STROB * buf, SWI * swi, 
		SWI_CONTROL_SCRIPT * script, 
		char * tagspec,
		int error_event_value,
		int warning_event_value)
{
	int ret;
	struct extendedOptions * opta;
	int script_retcode;
	int swi_retcode;

	opta = swi->optaM;
	SWLIB_ASSERT(script != NULL);

	/*
	 * write main script fragment
	 */

	ret = write_control_script_code_fragment(G, swi, script, tagspec, buf);
	SWLIB_ASSERT(ret == 0);

	/*
	 * write some shell code that monitors
	 * return status of the script
	 */

	if (swextopt_is_option_true(SW_E_enforce_scripts, opta)) {
		script_retcode = SW_ERROR;
		swi_retcode = 1;
	} else {
		script_retcode = SW_WARNING;
		swi_retcode = 0;
	}

	strob_sprintf(buf, STROB_DO_APPEND,
		"	# Generated by swinstall_lib.c:construct_script\n"
		"	case \"$sw_retval\" in\n"
		"		%d)\n"
		"			;;\n"
					/* Do nothing */
		"		%d)\n"
		"			%s\n"
		"			sw_retval=0\n"
		"			;;\n"
	
		"		%d)\n"
		"			script_retval=%d\n"
		"			%s\n"
		"			sw_retval=%d\n"
		"			;;\n"
		"		*)\n"
		"			sw_retval=1\n"
		"			;;\n"
		"	esac\n",
	SW_SUCCESS,
	SW_WARNING,
	TEVENT(2, (swi->verboseM), warning_event_value /*SW_CHECK_SCRIPT_WARNING*/, "status=$sw_retval"),
	SW_ERROR,
	script_retcode,
	TEVENT(2, (swi->verboseM), error_event_value /*SW_CHECK_SCRIPT_ERROR*/, "status=$script_retval"),
	swi_retcode
	);	
	return 0;
}

static
int
construct_prepost_script(GB * G, STROB * buf, SWI * swi, char * script_tag)
{
	STROB * tmp;
	SWI_PRODUCT * product;
	SWI_XFILE * fileset;
	SWI_CONTROL_SCRIPT * product_xxinstall_script;
	SWI_CONTROL_SCRIPT * fileset_xxinstall_script;
	struct extendedOptions * opta;
	int script_retcode;
	int swi_retcode;
	int event_error_value;
	int event_warning_value;

	opta = swi->optaM;
	tmp = strob_open(20);


	/*
	 * script_tag is either preinstall or postinstall
	 */

	if (strcmp(script_tag, SW_A_postinstall) == 0) {
		event_error_value = SW_POST_SCRIPT_ERROR;
		event_warning_value = SW_POST_SCRIPT_WARNING;
	} else {
		event_error_value = SW_PRE_SCRIPT_ERROR;
		event_warning_value = SW_PRE_SCRIPT_WARNING;
	}

	product = swi_package_get_product(swi->swi_pkgM, 0 /* The first product */);
	product_xxinstall_script = swi_product_get_control_script_by_tag(product, script_tag);

	fileset = swi_product_get_fileset(product, 0 /* the first one */);
	fileset_xxinstall_script = swi_xfile_get_control_script_by_tag(fileset, script_tag);

	if (
		!product_xxinstall_script &&
		!fileset_xxinstall_script
	) {
		/*
		 * no script required because there are no 
		 * {pre|post}install scripts for either  the product or fileset
		 */
		strob_sprintf(buf, STROB_DO_APPEND, "sw_retval=0\n");
		return 0;
	}

	/*
	 * FIXME -- need to set the state to "transient"
	 */

	/*
	 * Run the product {pre|post}install script
	 */
	
	if (product_xxinstall_script) {
		/*
		 * construct the product script's execution
		 */
		strob_sprintf(buf, STROB_DO_APPEND, 
			"# generated by swinstall_lib.c:construct_execution_script\n",
			"cd \"%s\"\n",
			swi->swi_pkgM->target_pathM);
		construct_script(G, buf, swi,
			product_xxinstall_script,
			product->baseM.tagM,
			event_error_value,
			event_warning_value);
	}
	
	if (swextopt_is_option_true(SW_E_enforce_scripts, opta)) {
		script_retcode = SW_ERROR;
		swi_retcode = 1;
	} else {
		script_retcode = SW_WARNING;
		swi_retcode = 0;
	}

	if (fileset_xxinstall_script) {
		strob_sprintf(buf, STROB_DO_APPEND,
		"case \"$sw_retval\" in\n"
		"	0)\n"
		);

		strob_sprintf(tmp, 0, "%s.%s", product->baseM.tagM, fileset->baseM.tagM);
		strob_sprintf(buf, STROB_DO_APPEND, "\tcd \"%s\"\n", swi->swi_pkgM->target_pathM);
		construct_script(G, buf, swi,
			fileset_xxinstall_script,
			strob_str(tmp),
			event_error_value,
			event_warning_value);

		strob_sprintf(buf, STROB_DO_APPEND,
		"	;;\n"
		"esac\n"
		);
	}

	strob_close(tmp);
	return 0;
}

static
int
construct_analysis_script(GB * G, STROB * buf, SWI * swi, SWI_CONTROL_SCRIPT ** p_script)
{
	int ret;
	SWI_PRODUCT * prod;
	SWI_CONTROL_SCRIPT * script;
	struct extendedOptions * opta;
	int checkscript_retcode;
	int swi_retcode;

	opta = swi->optaM;

	prod = swi_package_get_product(swi->swi_pkgM, 0 /* The first product */);
	if (swi_product_has_control_file(prod, SW_A_checkinstall)) {
	
		strob_sprintf(buf, STROB_DO_APPEND,
		"	cd \"%s\"\n",
		swi->swi_pkgM->target_pathM);
	
		/*
		 * Write the code for the checkinstall script
		 */
	
		script = swi_product_get_control_script_by_tag(prod, SW_A_checkinstall);
		SWLIB_ASSERT(script != NULL);
		ret = write_control_script_code_fragment(G, swi, script, prod->baseM.tagM, buf);
		SWLIB_ASSERT(ret == 0);

		/*
		 * write some shell code that monitors
		 * return status of the checkinstall script
		 */

		if (swextopt_is_option_true(SW_E_enforce_scripts, opta)) {
			checkscript_retcode = SW_ERROR;
			swi_retcode = 1;
		} else {
			checkscript_retcode = SW_WARNING;
			swi_retcode = 0;
		}

		*p_script = script;
		strob_sprintf(buf, STROB_DO_APPEND,
		"# Generated by swinstall_lib.c:construct_analysis script\n"
		"case \"$sw_retval\" in\n"
		"	%d)\n"
		"		;;\n"
				/* Do nothing */
		"	%d)\n"
		"		%s\n"
		"		sw_retval=0\n"
		"		;;\n"

		"	%d)\n"
		"		script_retval=%d\n"
		"		%s\n"
		"		sw_retval=%d\n"
		"		;;\n"
		"esac\n",
		SW_SUCCESS,
		SW_WARNING,
		TEVENT(2, (swi->verboseM), SW_CHECK_SCRIPT_WARNING, "status=$sw_retval"),
		SW_ERROR,
		checkscript_retcode,
		TEVENT(2, (swi->verboseM), SW_CHECK_SCRIPT_ERROR, "status=$script_retval"),
		swi_retcode
		);	
	} else {
		*p_script = NULL;
		strob_sprintf(buf, STROB_DO_APPEND, "sw_retval=0\n");
	}
	return 0;
}


static
int
construct_controlsh_script(GB * G, STROB * buf, SWI * swi)
{
	char * tag;
	STROB * tmp;
	STROB * taglist;

	taglist = strob_open(100);
	tmp = strob_open(100);
	strob_strcpy(buf, "");
	swicat_write_auto_comment(buf, "control.sh");
	swicat_env(buf, swi, NULL, NULL);

	swicat_construct_controlsh_taglist(swi, "*", taglist);

	strob_sprintf(buf, STROB_DO_APPEND,
		"SWBCS_TAGLIST=\"%s\"\n"
		"case \"$#\" in\n"
		"	2)\n"
		"		;;\n"
		"	*)\n"
		"		echo \"usage: control.sh selection tag\" 1>&2\n"
		"		exit 1\n"
		"		;;\n"
		"esac\n"
		"SWBCS_SWSEL_PAT=\"$1\"\n"
		"SWBCS_SCRIPT_TAG=\"$2\"\n"
		"SWBCS_GOT_MATCH=n\n"
		"SWBCS_MATCH=\"\"\n"
		"for fqc in $SWBCS_TAGLIST\n"
		"do\n"
		"	case \"$fqc\" in\n"
		"		${SWBCS_SWSEL_PAT})\n"
		"		case $SWBCS_GOT_MATCH in y) echo SW_SELECTION_NOT_FOUND_AMBIG 1>&2; exit 22;; esac\n"
		"		SWBCS_MATCH=\"$fqc\"\n"
		"		SWBCS_GOT_MATCH=y\n"
		"		;;\n"
		"	esac\n"
		"done\n"
		"case $SWBCS_GOT_MATCH in\n"
        	"	n)\n"
		"		echo SW_SELECTION_NOT_FOUND 1>&2\n"
		"		exit 1\n"
		"		;;\n"
		"esac\n"
		, strob_str(taglist)
		);


	strob_sprintf(buf, STROB_DO_APPEND,
		"case \"$SWBCS_MATCH\" in\n"
		);

	tag = strob_strtok(tmp, strob_str(taglist), " ");
	while(tag) {
		write_case_block(swi, buf, tag);
		tag = strob_strtok(tmp, NULL, " ");
	}
	
	strob_sprintf(buf, STROB_DO_APPEND, "esac\n");
	

	/*
	 * Now finally the environment is set, so now
	 * run the script.
	 */

	strob_sprintf(buf, STROB_DO_APPEND,
		"\n"
		"# Now run the script\n"
		"\n"
		"sh \"${SW_CONTROL_DIRECTORY}/${SWBCS_SCRIPT_NAME}\"\n"
		"swbcs_ret=$?\n"
		"case \"$swbcs_ret\" in\n"
		"	0) ;;\n"
		"	1) ;;\n"
		"	2) ;;\n"
		"	3) ;;\n"
		"	*) swbcs_ret=1 ;;\n"
		"esac\n"
		"exit $swbcs_ret\n"
		);

	strob_close(taglist);
	strob_close(tmp);
	return 0;
}

static
int
write_tar_controlsh_file(GB * G, SWI * swi, int ofd,
	char * catalog_path,
	char * pax_read_command,
	int alt_catalog_root,
	int event_fd,
	char * id_str
	)
{
	int ret = 0;
	STROB * name;
	STROB * data;

	name = strob_open(100);
	data = strob_open(100);

	strob_strcpy(name, swi->swi_pkgM->catalog_entryM);
	swlib_unix_dircat(name, SW_A_controlsh);
	ret = construct_controlsh_script(G, data, swi);
	SWLIB_ASSERT(ret == 0);

	ret = load_single_file_tar_archive(G,
		swi,
		ofd, 
		catalog_path,
		pax_read_command,
		alt_catalog_root,
		event_fd,
		id_str,
		strob_str(name),
		strob_str(data));

	strob_close(name);
	strob_close(data);
	return ret;
}

static
int
write_tar_session_options_file(GB * G, SWI * swi, int ofd,
	char * catalog_path,
	char * pax_read_command,
	int alt_catalog_root,
	int event_fd,
	char * id_str
	)
{
	int ret = 0;
	STROB * name;
	STROB * data;

	name = strob_open(100);
	data = strob_open(100);

	write_session_options_filename(name, swi);
	write_session_options_file(data, swi);

	ret = load_single_file_tar_archive(G,
		swi,
		ofd, 
		catalog_path,
		pax_read_command,
		alt_catalog_root,
		event_fd,
		id_str,
		strob_str(name),
		strob_str(data));

	strob_close(name);
	strob_close(data);
	return ret;
}

static
int
compare_name(char * name1, char * name2, char * att, char * filename)
{
	int ret;
	if (strlen(name1) == 0 || strlen(name2) == 0) return 0;
	ret = swlib_dir_compare(name1, name2, 1);
	if (ret) {
		if (swlib_compare_8859(name1, name2) != 0) {
			fprintf(stderr, "%s: attribute mismatch: %s: att=%s: storage=[%s] INFO=[%s]\n",
				swlib_utilname_get(), filename, att,
				name1, name2);
		} else {
			ret = 0;
		}
	}
	return ret ? 1 : 0;
}

static
int
sanity_compare(AHS * ahs1, AHS * ahs2, char * filename, int do_be_verbose)
{
	int ret = 0;
	int cpiotype1, cpiotype2;
	struct new_cpio_header * h1 = ahs1->file_hdrM;
	struct new_cpio_header * h2 = ahs2->file_hdrM;

	cpiotype1 = (h1->c_mode & CP_IFMT);
	cpiotype2 = (h2->c_mode & CP_IFMT);

	if (cpiotype1 != cpiotype2) {
		ret ++;
		fprintf(stderr, "%s: attribute mismatch: %s: att=type: storage=[%d] [%d]\n",
			swlib_utilname_get(), filename,
			(int)(cpiotype1),
			(int)(cpiotype2));
	}

       	if  ((h2->usage_maskM) & TARU_UM_MODE) {
		if ((07777 & h1->c_mode) != (07777 & h2->c_mode)) {
			ret ++;
			fprintf(stderr, "%s: attribute mismatch: %s: att=mode: storage=[%o] [%o]\n",
				swlib_utilname_get(), filename,
				(int)(h1->c_mode),
				(int)(h2->c_mode));
		}
	} else {
		if (do_be_verbose)
		fprintf(stderr, "%s: warning: INFO file does not contain a mode attribute: %s\n",
			swlib_utilname_get(), filename);
	}

       	if  ((h2->usage_maskM) & TARU_UM_UID) {
 	      	if (
			(h1->c_uid != ULONG_MAX && h2->c_uid != ULONG_MAX) &&
			h1->c_uid != h2->c_uid
		) {
			ret ++;
			fprintf(stderr, "%s: attribute mismatch: %s: att=uid: storage=[%d] INFO=[%d]\n",
				swlib_utilname_get(), filename,
				(int)(h1->c_uid),
				(int)(h2->c_uid));
		}
	} else {
		if (do_be_verbose)
		fprintf(stderr, "%s: warning: INFO file does not contain a uid attribute: %s\n",
			swlib_utilname_get(), filename);
	}

       	if  ((h2->usage_maskM) & TARU_UM_GID) {
 	      	if (
			(h1->c_gid != ULONG_MAX && h2->c_gid != ULONG_MAX) &&
			h1->c_gid != h2->c_gid
		) {
			ret ++;
			fprintf(stderr, "%s: attribute mismatch: %s: att=gid: storage=[%d] INFO=[%d]\n",
				swlib_utilname_get(), filename,
				(int)(h1->c_gid),
				(int)(h2->c_gid));
		}
	} else {
		if (do_be_verbose)
		fprintf(stderr, "%s: warning: INFO file does not contain a gid attribute: %s\n",
			swlib_utilname_get(), filename);
	}

       	if ( ((h2->usage_maskM) & TARU_UM_MTIME) && h1->c_mtime != h2->c_mtime) {
		/* This is now only a warning. */
		/* ret ++; FIXME:  policy at wrong level */
		fprintf(stderr, "%s: Warning: attribute mismatch: %s: att=mtime: storage=[%lu] INFO=[%lu]\n",
			swlib_utilname_get(), filename,
			(h1->c_mtime),
			(h2->c_mtime));
	}

       	if (h1->c_filesize != h2->c_filesize &&
		(
			(cpiotype1 != CP_IFLNK) &&
			(cpiotype2 != CP_IFLNK) &&
			(cpiotype2 != CP_IFLNK) &&
			(h1->c_is_tar_lnktype != 1) &&
			(h1->c_is_tar_lnktype != 1)
		)
	) {
		/*
		 * Don't do a file size comparison if the type is SYMTYPE or LNKTYPE
		 */
		ret ++;
		fprintf(stderr, "%s: attribute mismatch: %s: att=size: storage=[%d] INFO=[%d]\n",
			swlib_utilname_get(), filename,
			(int)(h1->c_filesize),
			(int)(h2->c_filesize));

	}

       	if ((h1->c_dev_maj || h2->c_dev_maj) && 
		h1->c_dev_maj != h2->c_dev_maj) {
		ret ++;
		fprintf(stderr, "%s: attribute mismatch: %s: att=major: storage=[%d] INFO=[%d]\n",
			swlib_utilname_get(), filename,
			(int)(h1->c_dev_maj),
			(int)(h2->c_dev_maj));
	}

       	if ((h1->c_dev_min || h2->c_dev_min) &&
       		h1->c_dev_min != h2->c_dev_min) {
		ret ++;
		fprintf(stderr, "%s: attribute mismatch: %s: att=minor: storage=[%d] INFO=[%d]\n",
			swlib_utilname_get(), filename,
			(int)(h1->c_dev_min),
			(int)(h2->c_dev_min));
	}

       	if  ((h2->usage_maskM) & TARU_UM_GROUP) {
		ret += compare_name(
			ahsStaticGetTarGroupname(h1),
			ahsStaticGetTarGroupname(h2),
			"group", filename);
	} else {
		if (do_be_verbose)
		fprintf(stderr, "%s: warning: INFO file does not contain a group attribute: %s\n",
			swlib_utilname_get(), filename);
	}


       	if  ((h2->usage_maskM) & TARU_UM_OWNER) {
		ret += compare_name(
			ahsStaticGetTarUsername(h1),
			ahsStaticGetTarUsername(h2),
			"owner", filename);
	} else {
		if (do_be_verbose)
			fprintf(stderr, "%s: warning: INFO file does not contain a owner attribute: %s\n",
			swlib_utilname_get(), filename);
	}	

	if (1 || (h1->c_is_tar_lnktype != 1 && h2->c_is_tar_lnktype != 1)) {
		/*
		 * If either is a hard link, don't do this comparison.
		 * The linkname in a tar header does not match the
		 * link_source attribute in the INFO file since the tar
		 * archive may have control directories in the linkname.
		 * This is done so that the IEEE layout tar archive is
		 * self consistent.
		 */

		/*
		 * Actually, don't do this test at all since
		 * the linkname is rewritten.
		 * FIXME there should be sanity check anyway.
		 */
		/*
		ret += compare_name(
			ahsStaticGetTarLinkname(h1),
			ahsStaticGetTarLinkname(h2),
			SW_A_link_source, filename);
		*/
	}
	
	ret += compare_name(
		filename /* ahsStaticGetTarFilename(h1)*/ ,
		ahsStaticGetTarFilename(h2),
		SW_A_path, filename);

	return ret;
}

static
void
safe_check_pathname(char * s)
{
	if (swlib_is_sh_tainted_string(s)) {
		SWLIB_FATAL("tainted string");
	}
}

static
void
sanitize_pathname(char * s)
{
	swlib_squash_all_leading_slash(s);
	safe_check_pathname(s);
}

static
char * 
get_attribute(SWHEADER * header, char * att, int * len)
{
	char * line;
	char * value;
        line = swheader_get_attribute(header, att);
	value = swheaderline_get_value(line, len);
	return value;
}

static
void
enforce_one_prod_one_fileset(SWI * swi)
{
	if (
		(swi->swi_pkgM->swi_coM[0]) == NULL || 
		(swi->swi_pkgM->swi_coM[0]->swi_coM[0]) == NULL
	) 
	{
		/*
		 * No product or no fileset
		 */
		fprintf(stderr, "%s: currently,  only one (1) products/filesets.\n",
			swlib_utilname_get());
		SWI_internal_fatal_error();
	}
	
	if (
		swi->swi_pkgM->swi_coM[1] ||
		swi->swi_pkgM->swi_coM[0]->swi_coM[1]
	) {
		/*
		 * More than one product or fileset
		 */
		fprintf(stderr, "%s: currently, multiple products/filesets not yet supported\n",
			swlib_utilname_get());
		SWI_internal_fatal_error();
	}	
	return;
}

static
SWHEADER *
get_global_index_header(SWI * swi)
{
	SWHEADER * ret;
	ret = swi->swi_pkgM->baseM.global_headerM;
	return ret;
}

static
SWHEADER *
get_fileset_info_header(SWI * swi)
{
	SWHEADER * ret;
	ret = swi->swi_pkgM->swi_coM[0]->swi_coM[0]->info_headerM;
	return ret;
}

static
int
does_have_prod_postinstall(SWI * swi)
{
	SWI_PRODUCT * prod;
	prod = swi_package_get_product(swi->swi_pkgM, 0 /* The first product */);
	return swi_product_has_control_file(prod, SW_A_postinstall);
}

static
int
get_fileset_file_count(SWHEADER * infoheader)
{
	int count = 0;
	char * next_line;
	swheader_reset(infoheader);
	next_line = swheader_get_next_object(infoheader, (int)UCHAR_MAX, (int)UCHAR_MAX);
	while (next_line){
		count++;
		next_line = swheader_get_next_object(infoheader, (int)UCHAR_MAX, (int)UCHAR_MAX);
	}
	return count++;
}

static
unsigned long int
get_delivery_size(SWI * swi, int * result)
{
	unsigned long int size;
	int count;
	char * obj;
	char * line;
	char * value;
	int atoi_error;
	SWHEADER * indexheader = get_global_index_header(swi);
	SWHEADER * infoheader = get_fileset_info_header(swi);

	/*
	 * Get the size of the tar archive of the fileset
	 * which is the fileset size attribute plus 2048 bytes 
	 * for each file to account for the tar header and
	 * slack block.  This assumes the fileset size attribute
	 * is a sum of the file sizes.
	 */

	/*
	 * Get the size attribute from the fileset.
	 */
	*result = 0;
	swheader_reset(indexheader);
        obj = swheader_get_object_by_tag(indexheader, "fileset", "*" );
        line = swheader_get_attribute(indexheader, "size");
	value = swheaderline_get_value(line, NULL);

	if (!value) {
		SWLIB_FATAL("fileset.size attribute is missing");
	}

	size = swlib_atoul(value, &atoi_error);
	if (atoi_error) {
		*result = -1;
		return 0;  /* return a safe value */	
	}
	count = get_fileset_file_count(infoheader);
	if (count < 0) {
		SWLIB_FATAL("");
	}

	size = 	size + 			/* the fileset size value */
		(count * 2048) +	/* header and last block for every file + 1024 extra pad */
		(10 * 512);		/* trailer blocks, 10 is arbitrary */

	/*
	 * Now convert size to be evenly divisible by 512
	 * size = ((size / 512) + 1) * 512;
	 */
	size = get_whole_block_size(size);
	return size;
}

static
int
write_out_signature_member(SWI * swi, struct tar_header * ptar_hdr,
		struct new_cpio_header * file_hdr,
		int ofd, int startoffset,
		int endoffset, int signum,
		int *package_ret)
{
	int ifd;
	XFORMAT * xformat = swi->xformatM;
	int retval = 0;
	int ret;
	unsigned long filesize;
	unsigned long package_filesize;
	char * sig;
	int siglen;
	STROB * tmp = strob_open(16);
	ETAR * etar;
	
	ifd = xformat_get_ifd(xformat);
	etar = etar_open(swi->xformatM->taruM);
	etar_init_hdr(etar);

	/*
	 * Set the basic tar header fields
	 */
	init_header_root(etar);

	/*
	 * This reads the signature tar header from the package.
	 */
	uxfio_read(ifd, (void*)(ptar_hdr), TARRECORDSIZE);

	/*
	 * Determine the filesize of the signature file
	 * It damn well should be 512 or 1024
	 */
	taru_otoul(ptar_hdr->size, &filesize);
	package_filesize = filesize + TARRECORDSIZE;
	*package_ret = package_filesize;

	/*
	 * Do a sanity check on the size.
	 */
	if (filesize != 512 && filesize != 1024) {
		SWI_internal_fatal_error();
	}

	/*
	 * allocate some temporary memory for this
	 */
	sig = malloc((size_t)filesize);
	if (!sig) SWI_internal_fatal_error();

	/*
	 * This reads the sigfile archive member data
	 */	
	uxfio_read(ifd, (void*)(sig), (size_t)filesize);
	sig[filesize-1] = '\0';
	siglen = strlen(sig);

	/*
	 * The package contains a sigfile padded with NULs.
	 * We will only install the ascii bytes which means the
	 * the sig file in the installed catalog will be shorter in
	 * length than in the package.
	 */

	etar_set_size(etar, siglen);
	etar_set_mode_ul(etar, (unsigned int)(0640));

	strob_strcpy(tmp, SWINSTALL_INCAT_NAME);
	swlib_unix_dircat(tmp, SWINSTALL_CATALOG_TAR);
	swlib_squash_all_leading_slash(strob_str(tmp));  /* FIXME */
	if (signum <= 1) {
		strob_sprintf(tmp, 1, ".sig");
	} else {
		strob_sprintf(tmp, 1, ".sig%d", signum);
	}

	if (etar_set_pathname(etar, strob_str(tmp))) SWLIB_FATAL("name too long");
	etar_set_chksum(etar);

	ret = write(ofd, (void*)(etar_get_hdr(etar)), TARRECORDSIZE);
	if (ret != TARRECORDSIZE) {
		SWI_internal_fatal_error();
	}
	retval += ret;

	if (siglen <= 512)
		filesize = 512;
	else if (siglen <= 1024)
		filesize = 1024;
	else
		SWI_internal_fatal_error();
	
	ret = atomicio((ssize_t (*)(int, void *, size_t))write,
			ofd, (void*)(sig), filesize);
	if (ret != (int)filesize) 
		SWI_internal_fatal_error();
	retval += ret;

	etar_close(etar);
	strob_close(tmp);
	free(sig);
	return retval;
}

static
int
write_out_all_signatures(SWI * swi, struct tar_header * ptar_hdr,
		struct new_cpio_header * file_hdr,
		int ofd, int startoffset,
		int endoffset, unsigned long filesize)
{
	int curpos;
	int ifd;
	int ret = 0;
	int sig_number;
	int current = 0;
	int package_current = 0;
	int package_ret = 0;
	int sig_block_length = endoffset - startoffset;
	XFORMAT * xformat = swi->xformatM;
	
	ifd = xformat_get_ifd(xformat);
	curpos = uxfio_lseek(ifd, 0L, SEEK_CUR);
	if (curpos < 0) {
		SWI_internal_fatal_error();
	}

	if (sig_block_length < 1024) {
		/*
		* FIXME: tar format specific.
		* Sanity check.
		*/
		return -1;
	}

	/*
	 * Seek to the beginning of the signature archive members.
	 */

	if (uxfio_lseek(ifd, startoffset, SEEK_SET) < 0) {
		SWI_internal_fatal_error();
	}

	/*
	 * all the sigfiles are the same length, filesize.
	 * sig_number is the number of signatures present, this
	 * needs to be determined because it is decremented to
	 * '1'.  This means the last signature in the package
	 * will have the name 'catalog.tar.sig', the next to last
	 * will have the name 'catalog.tar.sig2', and so on.
	 */

	sig_number = sig_block_length / (int)(filesize + TARRECORDSIZE);

	/* fprintf(stderr, "eraseme filesize=%d sig_number=%d\n", (int)filesize, sig_number); */

	/*
	 * do a sanity check
	 */

	if (sig_block_length % (int)(filesize + TARRECORDSIZE)) {
		SWI_internal_fatal_error();
	}

	/*
	 * Loop to write out all the signatures.
	 */

	while (package_current < sig_block_length) {
		if (sig_number <= 0) {
			/* Sanity check */
			SWI_internal_fatal_error();
		}
		ret = write_out_signature_member(swi, ptar_hdr,
				file_hdr, ofd,
				startoffset, endoffset,
				sig_number--, &package_ret);
		if (ret < 0) return ret;
		package_current += package_ret;
		current += ret;
	}

	/*
	 * Now restore the old offset.
	 */
	if (uxfio_lseek(ifd, curpos, SEEK_SET) < 0) {
		SWI_internal_fatal_error();
	}
	return current;
}


static
int
write_catalog_data(SWI * swi, int ofd, int sig_block_start, int sig_block_end)
{
	int curpos;
	int ifd;
	int iend;
	int amount;
	XFORMAT * xformat = swi->xformatM;
	SWI_PACKAGE * package = swi->swi_pkgM;
	int retval;
	int ret;

	ifd = xformat_get_ifd(xformat);
	curpos = uxfio_lseek(ifd, 0L, SEEK_CUR);
	if (curpos < 0) {
		SWI_internal_fatal_error();
		return -1;
	}

	if (uxfio_lseek(ifd, package->catalog_start_offsetM, SEEK_SET) < 0) {
		SWI_internal_fatal_error();
		return -1;
	}

	if (sig_block_start > 0) {
		iend = sig_block_start;
	} else {
		iend = package->catalog_end_offsetM;
	}
	amount = iend - package->catalog_start_offsetM;

	if (swlib_pump_amount(ofd, ifd, amount) != amount) {
		SWI_internal_fatal_error();
		return -1;
	}
	retval = amount;
	if (sig_block_start > 0) {
		/* 
		 * skip the signatures and write the remaining.
		 */
		amount = sig_block_end - sig_block_start;
		if (uxfio_lseek(ifd, amount, SEEK_CUR) < 0) {
			SWBIS_ERROR_IMPL();
			SWI_internal_fatal_error();
		}
		amount = package->catalog_end_offsetM - sig_block_end;
		if (swlib_pump_amount(ofd, ifd, amount) != amount) {
			SWBIS_ERROR_IMPL();
			SWI_internal_fatal_error();
			return -1;
		}
		retval += amount;
	} else {
		/*
		 * done
		 */
		;
	}
	if (uxfio_lseek(ifd, curpos, SEEK_SET) < 0) {
		SWBIS_ERROR_IMPL();
		SWI_internal_fatal_error();
	}

	/*
	 * Now write 2 NUL trailer blocks
	 */
	ret = swinstall_lib_write_trailer_blocks(ofd, 2);
	if (ret < 0) return ret;
	retval += ret;
	return retval;
} 

static
int
write_catalog_archive_member(SWI * swi, int ofd,
		struct tar_header *ptar_hdr, int sig_block_start, int sig_block_end)
{
	int ret;
	int retval;
	XFORMAT	* xformat;
	STROB * buf;
	char * vendor_tag;
	int size;
	struct tar_header * catalog_tar_hdr;
	ETAR * etar;

	catalog_tar_hdr = (struct tar_header *)malloc(TARRECORDSIZE);
	xformat = swi->xformatM;
	buf = strob_open(100);
	etar = etar_open(swi->xformatM->taruM);
	etar_init_hdr(etar);

	/*
	 * Set the basic tar header fields
	 */
	init_header_root(etar);

	/*
	 * set the tar header name and file size
	 */
	strob_strcpy(buf, "");
	swlib_unix_dircat(buf, SWINSTALL_INCAT_NAME);
	swlib_unix_dircat(buf, SWINSTALL_CATALOG_TAR);
	size = write_catalog_data(swi, swi->nullfdM, sig_block_start, sig_block_end);
	etar_set_size(etar, size);
	if (etar_set_pathname(etar, strob_str(buf))) SWLIB_FATAL("name too long");
	etar_set_chksum(etar);

	/*
	 * write the tar header
	 */
	ret = atomicio((ssize_t (*)(int, void *, size_t))uxfio_write,
				ofd,
				(void*)(etar_get_hdr(etar)),
				(size_t)(TARRECORDSIZE));

	if (ret != TARRECORDSIZE) {
		SWBIS_ERROR_IMPL();
		SWI_internal_fatal_error();
	}

	/*
	 * write the catalog data.
	 */
	if (write_catalog_data(swi, ofd, sig_block_start, sig_block_end) != size) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		retval = -1;
	} else {
		retval = size + ret;
	}
	
	/*
	 * write the vendor_tag attribute file.
	 */
	vendor_tag = strar_get(swi->distdataM->vendor_tagsM, 0 /*product index*/);
	if (vendor_tag && strlen(vendor_tag)) {
		SWLIB_ASSERT(strlen(vendor_tag) < 64); /* Sanity check and enforce the Posix Limit */
		init_header_root(etar);
		etar_set_size(etar, strlen(vendor_tag) + 1 /* +1 is the newline */);
		if (etar_set_pathname(etar, SW_A_vendor_tag)) SWLIB_FATAL("name too long");
		etar_set_chksum(etar);

		/*
		 * write the tar header for the vendor_tag file.
		 * The file name is "vendor_tag"
		 */
		ret = atomicio((ssize_t (*)(int, void *, size_t))uxfio_write,
				ofd,
				(void*)(etar_get_hdr(etar)),
				(size_t)(TARRECORDSIZE));
		if (ret != TARRECORDSIZE) { SWBIS_ERROR_IMPL(); SWI_internal_fatal_error(); }
		retval += ret;

		/*
		 * now write the data block which the value of the vendor_tag.
		 */
		memset((void*)(swi->tarbufM), '\0', TARRECORDSIZE);
		strncpy((char*)(swi->tarbufM), vendor_tag, TARRECORDSIZE - 2);
		strcat((char*)(swi->tarbufM), "\n" /* FIXME \n must be 1 byte */);
		ret = atomicio((ssize_t (*)(int, void *, size_t))uxfio_write,
				ofd, (void*)(swi->tarbufM), (size_t)(TARRECORDSIZE));
		if (ret != TARRECORDSIZE) { SWBIS_ERROR_IMPL(); SWI_internal_fatal_error(); }
		retval += ret;
	} else {
		/*
		 * Since their is no vendor_tag
		 * write zero'ed tar blocks
		 */
		ret = swinstall_lib_write_trailer_blocks(ofd, 2);
		retval += ret;
	}
	etar_close(etar);
	strob_close(buf);
	free(catalog_tar_hdr);
	return retval;
}

static
int
send_nothing_and_wait(SWI * swi, int ofd, int event_fd, char * msgtag, int tl, int retcode)
{
	int ret = 0;
	STROB * tmp;
	STROB * namebuf;

	tmp = strob_open(10);
	namebuf = strob_open(10);

	strob_strcpy(namebuf, ".");

	/*
	 * Here is the minimal task scriptlet
	 */
	strob_sprintf(tmp, 0,
		"sw_retval=%d\n"
		"dd of=/dev/null 2>/dev/null\n",
		retcode
		);

	/*
	 * Send the script into stdin of the POSIX shell
	 * invoked with the '-s' option.
	 */
	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 
		512,  
		strob_str(namebuf),
		strob_str(tmp), msgtag
		);

	if (ret < 0) {
		return -1;
	}

	/*
	 * Now send the stdin payload which must be
	 * exactly stdin_file_size bytes long.
	 */
	
	/* 
	 * Send the payload
	 */
	ret = swinstall_lib_write_trailer_blocks(ofd, 1);

	/*
	 * Reap the events from the event pipe.
	 */
	ret = swicol_rpsh_task_expect(swi->swicolM,
				event_fd,
				tl /*time limit*/);
	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);
	
	strob_close(tmp);
	strob_close(namebuf);
	return ret;
}

static
int
send_signature_files(SWI * swi, int ofd,
	char * catalog_path, char * pax_read_command,
	int alt_catalog_root, int event_fd,
	struct tar_header * ptar_hdr,
	int sig_block_start, int sig_block_end,
	unsigned long filesize)
{
	int ret = 0;
	int padamount;
	int stdin_file_size;
	STROB * tmp;
	STROB * namebuf;
	int sig_block_length;
	struct new_cpio_header * file_hdr;
	struct tar_header * tar_hdr;

	tar_hdr = (struct tar_header *)malloc(TARRECORDSIZE+1);
	tmp = strob_open(10);
	namebuf = strob_open(10);
	file_hdr = taru_make_header();

	memcpy((void*)tar_hdr, (void*)ptar_hdr, TARRECORDSIZE);
	sig_block_length = sig_block_end - sig_block_start;

	stdin_file_size = sig_block_length +
			TARRECORDSIZE +  /* Trailer block */
			TARRECORDSIZE;   /* Trailer block */

	/*
	 * FIXME    ''catalog_path'' is swi->catalog_entryM 
	 */

	strob_strcpy(namebuf, catalog_path);
	if (alt_catalog_root == 0) {
		swlib_squash_all_leading_slash(strob_str(namebuf));
	} else {
		/*
		 * FIXME alternate catalog root not supported
		 */
		swlib_squash_all_leading_slash(strob_str(namebuf));
	}
	
	/*
	 * Here is the task scriptlet.
	 */
	strob_sprintf(tmp, 0,
		"dd 2>/dev/null | %s\n"
		"sw_retval=$?\n"
		"dd of=/dev/null 2>/dev/null\n"
		,
		pax_read_command
		);

	/*
	 * Send the script into stdin of the POSIX shell
	 * invoked with the '-s' option.
	 */
	/* TS_sig */
	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 
		stdin_file_size,
		strob_str(namebuf),
		strob_str(tmp), "Load signatures"
		);

	if (ret < 0) {
		return -1;
	}

	/*
	 * Now send the stdin payload which must be
	 * exactly stdin_file_size bytes long.
	 */
	
	ret = write_out_all_signatures(swi, tar_hdr, file_hdr,
		ofd, sig_block_start, sig_block_end, filesize);

	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		return -1;
	}

	if (ret > (stdin_file_size - TARRECORDSIZE - TARRECORDSIZE)) {
		/*
		* Sanity check
		*/
		SWBIS_ERROR_IMPL();
		return -1;
	}

	/*
	 * Now pad the remaining output which will be at least
	 * 2 * 512 bytes long.
	 */
	padamount = stdin_file_size - ret;

	ret = swlib_pad_amount(ofd, padamount);
	if (ret < 0 || ret != padamount) {
		SWBIS_ERROR_IMPL();
		return -1;
	}

	/*
	 * Reap the events from the event pipe.
	 */
	ret = swicol_rpsh_task_expect(swi->swicolM,
				event_fd,
				SWICOL_TL_27 /*time limit*/);
	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

	taru_free_header(file_hdr);
	strob_close(tmp);
	strob_close(namebuf);
	free(tar_hdr);
	return 0;
}

static
int
common_catalog_tarfile_operation(SWI * swi, int ofd, 
	char * catalog_path, char * pax_read_command,
	int alt_catalog_root, int event_fd,
	char * script, char * message)
{
	int ret = 0;
	STROB * namebuf;

	namebuf = strob_open(10);

	/*
	 * Here is the directory that is chdir'ed into.
	 */
	strob_strcpy(namebuf, catalog_path);
	if (alt_catalog_root == 0) {
		/*
		 * Unless alt_catalog_root is true,
		 * the catalog path is relative to the
		 * target path.
		 */
		swlib_squash_all_leading_slash(strob_str(namebuf));
	} else {
		/* FIXME */
		/* not supported yet */
		SWLIB_FATAL("alt_catalog_root not supported");
	}

	/*
	 * Now send the script.
	 */
	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 
		512,  /* Some dd's can't read zero bytes */
		strob_str(namebuf),
		script,
		message);

	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		return -1;
	}

	/* 
	 * Now Send the payload 
	 * which in this case is just a gratuitous 512 bytes.
	 * This is needed because some dd(1) on some Un*xes can't
	 * read zero blocks.
	 */
	ret = swinstall_lib_write_trailer_blocks(ofd, 1);

	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}

	/*
	 * Now reap the events from the event pipe.
	 */
	ret = swicol_rpsh_task_expect(swi->swicolM,
				event_fd,
				SWICOL_TL_10 /*time limit*/);

	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

	strob_close(namebuf);
	return ret;
}

static
void
update_fileset_state(SWI * swi, char * swsel, char * state)
{

	SWI_PRODUCT * product;
	SWI_XFILE * fileset;
	int fileset_index = 0;

	/*
	 * FIXME, supprt software selections
	 */
	product = swi_package_get_product(swi->swi_pkgM, 0 /* The first product */);
	fileset = swi_product_get_fileset(product, fileset_index++);
	while(fileset) {	
		swi_xfile_set_state(fileset, state);
		fileset = swi_product_get_fileset(product, fileset_index++);
	}
}

static
int
update_execution_script_results(SWI * swi, SWICOL * swicol, SCARY ** array)
{
	int i;
	STROB * buf;
	STROB * tmp;
	SWI_CONTROL_SCRIPT * script;
	int event_index = -1;
	int event_start_index;
	int status;
	char * tag;
	char * tagspec;
	char * message;
	
	buf = strob_open(100);
	tmp = strob_open(100);

	event_start_index = swicol->event_indexM;

	/*
	swicol_print_events(swicol, buf, -1);	
	fprintf(stderr, "%s", strob_str(buf));
	scary_show_array(array);
	*/

	/*
	 * loop over the events
	 */
	message = swicol_rpsh_get_event_message(swicol, SW_CONTROL_SCRIPT_BEGINS,
				event_start_index, &event_index);
	event_index++;
	while (message) {
		/* fprintf(stderr, "message=[%s]\n", message);
			*/
		tagspec = message;
		tag = strchr(message, (int)(' '));
		SWLIB_ASSERT(tag != NULL);
		*tag='\0';
		tag++;
		/* fprintf(stderr, "tag=[%s] tagspec=[%s]\n", tag, tagspec);
			*/
		/*
		 * Now find the script that belongs to this tag and tagspec
		 */
		script = scary_find_script(array, tag, tagspec);
		SWLIB_ASSERT(script != NULL);
		status = swicol_rpsh_get_event_status(swicol, (char*)(NULL),
                		SW_CONTROL_SCRIPT_ENDS, event_index, &event_index);
		SWLIB_ASSERT(status >= 0);
		script->resultM = status;
		message = swicol_rpsh_get_event_message(swicol,
				SW_CONTROL_SCRIPT_BEGINS,
				event_index+1,
				&event_index);
	}


	strob_close(tmp);
	strob_close(buf);
	return 0;
}


static
int
unpack_catalog_tarfile(SWI * swi, int ofd, 
	char * catalog_path, char * pax_read_command,
	int alt_catalog_root, int event_fd)
{
	int ret = 0;
	STROB * tmp;

	tmp = strob_open(10);

	strob_sprintf(tmp, 0,
		"#set -vx\n"
		"echo unpacking catalog %s 1>&2\n"
		"pwd 1>&2\n"
		"cd  " SWINSTALL_INCAT_NAME  "\n"
		"%s %s <catalog.tar\n"
		"sw_retval=$?\n"
		"dd of=/dev/null 2>/dev/null\n"
		,
		swi->exported_catalog_prefixM,
		pax_read_command,
		swi->exported_catalog_prefixM
		);

	ret = common_catalog_tarfile_operation(swi, ofd, catalog_path,
				pax_read_command, alt_catalog_root,
				event_fd, strob_str(tmp),
				"unpack catalog.tar");
	strob_close(tmp);
	return ret;
}

static
int
remove_catalog_directory(SWI * swi, int ofd, 
	char * catalog_path, char * pax_read_command,
	int alt_catalog_root, int event_fd)
{
	int ret = 0;
	STROB * tmp;
	STROB * path;
	STROB * tmptok;
	STROB * rmdir_command;
	char * token;
	char * start;

	path = strob_open(10);
	tmp = strob_open(10);
	tmptok = strob_open(10);
	rmdir_command = strob_open(10);

	/*
	 * perform some sanity checks on swi->exported_catalog_prefixM
	 * It should have the force of <path>/catalog
	 */

	strob_strcpy(path, swi->exported_catalog_prefixM);
	if (swlib_check_clean_relative_path(strob_str(path))) return 1;
	swlib_squash_trailing_slash(strob_str(path));
	if (strstr(strob_str(path), SW_A_catalog) == NULL) return 2;

	/*
	 * OK , swi->exported_catalog_prefixM is now qualified as sane
	 */

	/*
	 * swi->exported_catalog_prefixM is typically :  <path>/catalog
	 * therefore we must execute:
	 *     rmdir <path>
	 *  If there is more than one leading path component
	 *  then they must be rmdir'ed individually
	 *  
	 *  If the exported catalog path is 
	 *      aaaa/bbbb/cccc/catalog    then
	 *  form a commmand string to remove aaaa/bbbb/cccc
         *  using rmdir since it is safer to user than rm -fr
	 *  The command will look like this:
	 *     rmdir aaaa/bbbb/cccc && rmdir aaaa/bbbb && rmdir aaaa
	 */

	strob_strcpy(rmdir_command, "");
	if (strchr(strob_str(path), '/')) {
		start = strob_str(path);
		token = strrchr(start, '/');	
		while(token) {
			*token = '\0';
			strob_sprintf(rmdir_command, 1, " && rmdir \"%s\"", start); 
			token = strrchr(start, '/');	
		}
	}

	strob_sprintf(tmp, 0,
		"cd " SWINSTALL_INCAT_NAME "\n"
		"/bin/rm -fr \"%s\" %s\n"
		"sw_retval=$?\n"
		"#echo rmdir command is [\"%s\"] 1>&2\n"
		"#echo rm command is [\"%s\"] 1>&2\n"
		"dd of=/dev/null\n"
		,
		swi->exported_catalog_prefixM,
		strob_str(rmdir_command),
		strob_str(rmdir_command),
		swi->exported_catalog_prefixM
		);


	ret = common_catalog_tarfile_operation(swi, ofd, catalog_path,
				pax_read_command, alt_catalog_root,
				event_fd, strob_str(tmp),
				"remove catalog directory");
	strob_close(tmp);
	strob_close(path);
	strob_close(rmdir_command);
	strob_close(tmptok);
	return ret;
}

static
int
send_catalog_tarfile(SWI * swi, int ofd, 
	char * catalog_path, char * pax_read_command,
	int alt_catalog_root, int event_fd,
	struct tar_header * ptar_hdr,
	int sig_block_start, int sig_block_end)
	
{
	int ret = 0;
	int dataret = 0;
	int stdin_file_size;
	STROB * tmp;
	STROB * namebuf;

	tmp = strob_open(10);
	namebuf = strob_open(10);

	/*
	 * Here is the task scriptlet.
	 */
	strob_sprintf(tmp, 0,
		"dd 2>/dev/null | %s\n"
		"sw_retval=$?\n"	/* This line is required */
		"dd of=/dev/null 2>/dev/null\n"
		,
		pax_read_command
		);

	stdin_file_size = write_catalog_data(swi, swi->nullfdM, sig_block_start, sig_block_end) +
			TARRECORDSIZE +  /* header */
			TARRECORDSIZE +  /* vendor_tag header */
			TARRECORDSIZE +  /* vendor_tag data */
			TARRECORDSIZE +  /* trailer block */
			TARRECORDSIZE;  /* trailer block */

	if (stdin_file_size % TARRECORDSIZE) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}

	/*
	 * Here is the directory that is chdir'ed into.
	 */
	strob_strcpy(namebuf, catalog_path);
	if (alt_catalog_root == 0) {
		/*
	 	 * Unless alt_catalog_root is true,
		 * the catalog path is relative to the
		 * target path.
		 */
		swlib_squash_all_leading_slash(strob_str(namebuf));
	}

	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 
		stdin_file_size,
		strob_str(namebuf),
		strob_str(tmp), "Install catalog.tar and vendor_tag"
		);

	if (ret < 0) {
		return -1;
	}

	/*
	 * Now write the actual data to the stdin of the
	 * posix shell, It *must* be exactly stdin_file_size
	 * bytes in length.
	 */
	ret = write_catalog_archive_member(swi, ofd, ptar_hdr,
			sig_block_start, sig_block_end);
	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}
	dataret += ret;

	/*
	 * Now write the trailer blocks
	 */	
	ret = swinstall_lib_write_trailer_blocks(ofd, 2);
	if (ret < 0) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}
	dataret += ret;

	/*
	 * Assert what must be true.
	 */
	if (dataret != stdin_file_size) {
		SWBIS_ERROR_IMPL();
		SWI_internal_error();
		return -1;
	}

	/*
	 * Reap the events from the event pipe.
	 */
	ret = swicol_rpsh_task_expect(swi->swicolM,
				event_fd,
				SWICOL_TL_28 /*time limit*/);
	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

	strob_close(tmp);
	strob_close(namebuf);
	return ret;
}

static
int
run_analysis_script(GB * G, SWI * swi, int ofd, int event_fd)
{
	int ret;
	STROB * buf;
	STROB * idtmp;
	SWI_CONTROL_SCRIPT * script;
	int stdin_file_size;
	int this_index;
	int script_status;
	char * checkinstall_message;

	buf = strob_open(400);
	idtmp = strob_open(100);

	ret = construct_analysis_script(G, buf, swi, &script);
	SWLIB_ASSERT(ret == 0);
	
	strob_sprintf(idtmp, 0, "analysis phase 2");
	stdin_file_size = 512;

	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 
		512,
		".",
		strob_str(buf),
		strob_str(idtmp)
		);

	if (ret < 0) {
		fprintf(stderr, "%s: retval=%d newnamebuf=[%s]\n", swlib_utilname_get(), ret, strob_str(idtmp));
		SWBIS_ERROR_IMPL();
		return -34;
	}

	/* 
	 * Send the payload, in this case, it is a gratuitous block of nuls.
	 */
	ret = swinstall_lib_write_trailer_blocks(ofd, 1);

	/*
	 * wait for the script to finish
	 */
	ret = swicol_rpsh_task_expect(swi->swicolM,	
				event_fd,
				SWICOL_TL_30 /*timelimit*/);
	/*
	 * this shows only the events for this task script
	 */	
	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

	checkinstall_message = swicol_rpsh_get_event_message(swi->swicolM,
                			SW_CONTROL_SCRIPT_BEGINS,
					-1, 
					&this_index);

	if (checkinstall_message) {
		/*
		 * Perform a sanity check on the message
		 */

		SWLIB_ASSERT(strstr(checkinstall_message, SW_A_checkinstall) != NULL);

		/*
		 * record the result
		 */

		script_status = swicol_rpsh_get_event_status(swi->swicolM, NULL, SW_CONTROL_SCRIPT_ENDS, this_index, &this_index);
		script->resultM = script_status;
	} else {
		/*
		 * This is OK
		 * There must not have been a checkinstall script
		 */
		;
	}

	strob_close(idtmp);
	strob_close(buf);
	return ret;
}

int
swinstall_get_utsname_attributes(GB * G, SWI * swi, int ofd, int event_fd)
{
	int ret;
	STROB * tmp = strob_open(10);

	strob_sprintf(tmp, 0,
			"sw_retval=0\n"
			"%s\n"
			"%s\n"
			"%s\n"
			"%s\n"
			"dd of=/dev/null 2>/dev/null\n"
			"exit $sw_retval\n"
			"%s"		/* nil line */
			,
			TEVENT(2, 1/*verbose level*/, SWI_ATTRIBUTE, "machine_type=\"$(uname -m)\""),
			TEVENT(2, 1/*verbose level*/, SWI_ATTRIBUTE, "os_name=\"$(uname -s)\""),
			TEVENT(2, 1/*verbose level*/, SWI_ATTRIBUTE, "os_release=\"$(uname -r)\""),
			TEVENT(2, 1, SWI_ATTRIBUTE, "os_version=\"$(uname -v)\""),
			"");
	E_DEBUG2("get_utsname_script=[%s]", strob_str(tmp));

	/* TS_uts_query */
	ret = swicol_rpsh_task_send_script(
		swi->swicolM,
		ofd, 
		512,
		".",
		strob_str(tmp), "get utsnames"
		);

	if (ret < 0) {
		strob_close(tmp);
		return -1;
	}
	
	ret = swinstall_lib_write_trailer_blocks(ofd, 1);

	ret = swicol_rpsh_task_expect(swi->swicolM,
			event_fd,
			SWICOL_TL_29 /*timelimit*/);
	
	if (swi->debug_eventsM)
		swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

	/*
	 * print the events to memory
	 */
        swicol_print_events(swi->swicolM, tmp, swi->swicolM->event_indexM);

	/*
	 * Parse and Store the uts attributes
	 */
	swuts_read_from_events(swi->version_idM->swutsM, strob_str(tmp));

	E_DEBUG2("utsname=[%s]", strob_str(tmp));

	if (ret != 0) {
		/*
		 * Examine the events in swicol->evemt_listM to determine
		 * what action to take
		 */
		if (ret == 2) {
			swlib_doif_writef(swi->verboseM,  1,
				(struct sw_logspec *)(NULL), STDERR_FILENO,
				"SW_RESOURCE_ERROR on target host %s: %d second time limit expired\n", 
					swi->swi_pkgM->target_hostM, 5);
		}
		ret = -1;
	}
	strob_close(tmp);
	return ret;
}

static
int
construct_execution_phase_script(GB * G, SWI * swi,
			STROB * buf,
			char * pax_read_command
			)
{
	int ret;

	/* scary_show_array(g_script_array); */

	/* 
	 * run the script execution in a subshell to
	 *	protect stdin from accidentally being
	 *	read from 
	 */

	strob_sprintf(buf, STROB_DO_APPEND,
	"(\n"
	);

	/*
	 * construct the preinstall portion of the execution script
	 */
	ret = construct_prepost_script(G, buf, swi, SW_A_preinstall);

	strob_sprintf(buf, STROB_DO_APPEND,
	"exit \"$sw_retval\"\n"
	") 1</dev/null\n"
	"sw_retval=$?\n"
	);

	/*
	 * Now write the actual command to install the fileset
	 */

	strob_sprintf(buf, STROB_DO_APPEND,
		"\n"
		"cd \"%s\"\n"
		"case $? in\n"
		"	0)\n"
		"	dd 2>/dev/null | %s\n"
		"	sw_retval=$?\n"
		"	dd of=/dev/null 2>/dev/null\n"
		"	;;\n"
		"	*)\n"
		"	sw_retval=1\n"
		"	;;\n"
		"esac\n"
		"\n"
		,
		swi->swi_pkgM->target_pathM,
		pax_read_command);

	/*
	 * construct the postinstall portion of the execution script
	 */
	strob_sprintf(buf, STROB_DO_APPEND,
		"case $sw_retval in\n"
		"0)\n"
		);

	strob_sprintf(buf, STROB_DO_APPEND,
	"(\n"
	);
	
	ret = construct_prepost_script(G, buf, swi, SW_A_postinstall);

	strob_sprintf(buf, STROB_DO_APPEND,
	"exit \"$sw_retval\"\n"
	") 1</dev/null\n"
	"sw_retval=$?\n"
	);

	strob_sprintf(buf, STROB_DO_APPEND,
		";;\n"
		"esac\n"
		);

	return 0;
}

int
determine_tar_listing_verbose_level(SWI * swi)
{
	int taru_ls_verbose_level;

	if (swi->swc_idM == SWC_U_I /* swinstall */) {
		if (swi->verboseM >= SWC_VERBOSE_4) {
			taru_ls_verbose_level = 1;
		} else {
			taru_ls_verbose_level = 0;
		}
	} else if (swi->swc_idM == SWC_U_L /* swlist */) {
		if (swi->verboseM >= SWC_VERBOSE_2) {
			taru_ls_verbose_level = 1;
		} else {
			taru_ls_verbose_level = 0;
		}
	} else {
		taru_ls_verbose_level = 0;
	}
	return taru_ls_verbose_level;
}

static
int
arfinstall_tar(GB * G, SWI * swi, int ofd, int efd,
	int * deadman, char * target_path, char * catalog_path, VPLOB * swspecs,
	int section, int altofd, int opt_preview,
	char * pax_read_command_key, int alt_catalog_root, int event_fd,
	struct extendedOptions * opta, struct sw_logspec * logspec,
	int keep_old_files, unsigned long int * pstatbytes)
{
	int format;
	int output_format;
	int vofd;
	int padamount;
	int read_header_ret;
	int retval = 0;
	int cmpret = 0;
	int size_result;
	char * current_path;
	char * current_type;
	char * current_link_source;
	char * name;
	char * pax_read_command;
	int ifd;
	int fileset_fd;
	int ret;
	int parseret;
	int is_catalog;
	int data_amount;
	int files_loading_verbose;
	int filecount;
	int optacc_a;
	int atoi_error;
	STROB * resolved_path;
	STROB * namebuf;
	STROB * newnamebuf;
	STROB * catalogdir;
	STROB * tmp;
	STROB * tmp2;
	STROB * pathtmp;
	STROB * tmp_swpath_ex;
	STROB * execution_phase_script;
	AHS * info_ahs2 = ahs_open();
	char * obj;
	int tarheaderflags;
	struct new_cpio_header * file_hdr;
	XFORMAT * xformat = swi->xformatM;
	SWPATH * swpath = swi->swpathM;
	SWPATH_EX * swpath_ex = NULL;
	SWHEADER * infoheader = (SWHEADER*)NULL;
	int sig_block_start;
	int sig_block_end;
	int package_is_signed = -1;
	struct tar_header *ptar_hdr;
	int did_catalog_tar = 0;
	int did_sigfiles = 0;
	int curpos;
	int did_fileset = 0;
	int fileset_write_ret = 0;
	unsigned long int payload_size;
	unsigned long filesize;
	int eoa;
	int header_ret;
	int current_file_offset;
	char * log_level_str;
	int log_level;
	char * md5buf;
	char * filemd5;
	char * is_volatile;
	SWI_PRODUCT * current_product;
	SWI_XFILE * current_fileset;
	char * newname_suffix;
	int product_count = 0;  /* This is always zero until multiple products are supported */
	SWI_CONTROL_SCRIPT * tmp_script;


	swpath_ex = swpath_shallow_create_export();
	catalogdir = strob_open(100);
	newnamebuf = strob_open(100);
	tmp_swpath_ex = strob_open(100);
	namebuf = strob_open(100);
	tmp = strob_open(10);
	tmp2 = strob_open(10);
	pathtmp = strob_open(10);
	resolved_path = strob_open(10);
	execution_phase_script = strob_open(10);
	tarheaderflags = xformat_get_tarheader_flags(xformat);
	file_hdr = taru_make_header();

	swpath_reset(swpath);
	
	swi->swicolM->verbose_levelM = swi->verboseM;
	swicol_set_verbose_level(swi->swicolM, swi->verboseM);
	output_format = xformat_get_output_format(xformat);
	format = xformat_get_format(xformat);
	ifd = xformat_get_ifd(xformat);
	if (ifd < 0) {
		SWI_internal_error();
		return -32;		
	}
	taru_set_header_recording(xformat->taruM, 1/*ON*/);
	xformat->taruM->linkrecord_disableM = 1;
	
	if (opt_preview && altofd >= 0) {
		fileset_fd = altofd;
	} else if (opt_preview) {
		fileset_fd = swi->nullfdM;
	} else {
		fileset_fd = ofd;
	}
	
	if (opt_preview) {
		vofd = swi->nullfdM;
	} else {
		vofd = ofd;
	}

	xformat_set_ofd(xformat, vofd);

	ptar_hdr = (struct tar_header*)(strob_str(xformat->taruM->headerM));
	
	log_level_str = get_opta(opta, SW_E_loglevel);
	SWLIB_ASSERT(log_level_str != NULL);
	log_level = swlib_atoi(log_level_str, &atoi_error);	

	files_loading_verbose = (
				(keep_old_files ? keep_old_files : (swi->verboseM - SWC_VERBOSE_7)) ||
				(log_level >= 2) ||
				(0)
				);
	
	pax_read_command = swc_get_pax_read_command(g_pax_read_commands, pax_read_command_key, 
					files_loading_verbose, 
					keep_old_files, DEFAULT_PAX_R);

	if (swextopt_is_option_true(SW_E_swbis_enforce_file_md5, opta)) {
		md5buf = (char*)(xformat->taruM->md5bufM->str_);
	} else {
		md5buf = NULL;
	}

	/*
	 * FIXME: Eventually support multiple products and filesets.
	 */
	enforce_one_prod_one_fileset(swi);

	/*
	 * Set the current product and fileset
	 */
	current_product = swi_package_get_product(swi->swi_pkgM, 0 /* the first one */);
	current_fileset = swi_product_get_fileset(current_product, 0 /* the first one */);

	/*
	 * Get the fileset size.
	 */
	payload_size = get_delivery_size(swi, &size_result);
	if (size_result) {
		SWBIS_ERROR_IMPL();
		return -31;
	}
		
	/*
	 * write the script to the target.
	 */
	if (opt_preview == 0 && altofd < 0) {
		/*
		 * This is the remainder of the analysis phase
		 */
		strob_sprintf(tmp, 0,
				"sw_retval=0\n"
				"sleep 0\n"
				"dd of=/dev/null 2>/dev/null\n"
				);

		strob_strcpy(newnamebuf, catalog_path);
		if (alt_catalog_root) {
			swlib_squash_all_leading_slash(strob_str(newnamebuf));
		}

		/* 
		 * TS_analysis_001 
		 */
		ret = swicol_rpsh_task_send_script(
			swi->swicolM,
			vofd, 
			512,
			strob_str(newnamebuf),
			strob_str(tmp), "analysis phase 1"
			);

		if (ret < 0) {
			fprintf(stderr, "%s: retval=%d newnamebuf=[%s]\n", swlib_utilname_get(), ret, strob_str(newnamebuf));
			SWBIS_ERROR_IMPL();
			return -34;
		}

		/* Send the payload */
		ret = swinstall_lib_write_trailer_blocks(ofd, 1);

		ret = swicol_rpsh_task_expect(swi->swicolM,	
					event_fd,
					SWICOL_TL_30 /*timelimit*/);
		if (swi->debug_eventsM)
			swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

		if (ret != 0) {
			;
			/*
			 * Examine the events in swicol->evemt_listM to determine
			 * what action to take
			 */
			if (ret == 2) {
				swlib_doif_writef(swi->verboseM,  1,
					(struct sw_logspec *)(NULL), STDERR_FILENO,
					"SW_RESOURCE_ERROR on target host %s: %d second time limit expired\n", 
						swi->swi_pkgM->target_hostM, 5);
			}
			return -36;
		}
	
		if (swi->verboseM >= 12) swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, 0);
	}

	infoheader = get_fileset_info_header(swi);
	filecount = get_fileset_file_count(infoheader);

	fileset_write_ret = 0;
	is_catalog = -1;
	optacc_a = 0;	
	

	/*
	 * =====================================
	 * Loop over the Catalog section files.
	 * =====================================
	 */

	while ((read_header_ret = xformat_read_header(xformat)) > 0 &&
			(!deadman || (deadman && *deadman == 0))) {
		
		E_DEBUG("HERE");
		if (xformat_is_end_of_archive(xformat)){
			/*
			 * This is an unexpected error
			 */
			fprintf(stderr, "%s: premature end of archive\n",
				swlib_utilname_get());
			return -36;
		}

		optacc_a++;  /* Used for Optimization Hack */

		curpos = uxfio_lseek(ifd, 0L, SEEK_CUR);
		if (curpos < 0) {
			SWBIS_ERROR_IMPL();
		}
		xformat_get_name(xformat, namebuf);
		name = strob_str(namebuf);
		data_amount = xformat_get_filesize(xformat);	
	
		/* fprintf(stderr, "eraseme %d %s %d\n", curpos, name, data_amount);  */

		parseret = swpath_parse_path(swpath, name);
		if (parseret < 0) {
			SWBIS_ERROR_IMPL();
			return -38;
		}

		/*
		 * Show the parsed path components
		 */
		swpath_shallow_fill_export(swpath_ex, swpath); 
		if (swi->verboseM >= SWC_VERBOSE_0) {
			swlib_doif_writef(swi->verboseM, SWC_VERBOSE_8,
				logspec, efd, "%s",
					swpath_ex_print(swpath_ex, tmp_swpath_ex, ""));
		}
		is_catalog = swpath_get_is_catalog(swpath);

		if (is_catalog == SWPATH_CTYPE_DIR) {
			/*
			 * Leading package directories
			 */
			;
		} else if (did_catalog_tar == 0 &&
			is_catalog == SWPATH_CTYPE_CAT &&
			strstr(swpath_get_pathname(swpath), SW_A_INDEX)
		) {
			/*
			 * Write out the catalog.tar file
			 *  <catalog_path>/<export>/catalog.tar
			 */

			/*
			 * Examine the catalog for signature.
			 */
			E_DEBUG("HERE");
			did_catalog_tar = 1;
			swi_examine_signature_blocks(swi->swi_pkgM->dfilesM,
					&sig_block_start, &sig_block_end);
			if (sig_block_start < 0) {
				package_is_signed = 0;
			} else {
				package_is_signed = 1;
			}

			if (opt_preview == 0) {
				/* TS_catalog_load */
				ret = send_catalog_tarfile(swi, vofd, 
					catalog_path, pax_read_command,
					alt_catalog_root, event_fd, ptar_hdr,
					sig_block_start, sig_block_end);
			} else {
				ret = 0;
			}
			if (ret < 0) {
				SWBIS_ERROR_IMPL();
				return -40;
			}
		
			curpos = uxfio_lseek(ifd, curpos, SEEK_SET);
			if (curpos < 0) {
				SWBIS_ERROR_IMPL();
			}

			/*
			 * Exhaust the member data to /dev/null
			 */
			ret = xformat_copy_pass(xformat, swi->nullfdM, ifd);
			if (ret < 0) {
				SWBIS_ERROR_IMPL();
			}
		} else if (
			did_sigfiles == 0 &&
			package_is_signed == 0 &&
			is_catalog == SWPATH_CTYPE_CAT 
		) {
			/*
			 * =====================================
			 * Send empty payload to the task shell if
			 * there are no signatures.
			 * =====================================
			 */
			E_DEBUG("HERE");
			did_sigfiles = 1;
			if (opt_preview == 0) {
				/* TS_sig_load */
				ret = send_nothing_and_wait(swi, vofd, event_fd, "no sigs", SWICOL_TL_17, SW_SUCCESS);
			} else {
				ret = 0;
			}
			if (ret) {
				SWBIS_ERROR_IMPL();
				return -42;
			}
			ret = xformat_copy_pass(xformat, swi->nullfdM, ifd);
		} else if (
			did_sigfiles == 0 &&
			package_is_signed > 0 &&
			is_catalog == SWPATH_CTYPE_CAT &&
			strstr(swpath_get_pathname(swpath), "signature")
		) {
			/*
			 * Write out all the signatures.
			 *	 <catalog_path>/export/catalog.tar.sig
			 *	 <catalog_path>/export/catalog.tar.sigN
			 */
			E_DEBUG("HERE");
			did_sigfiles = 1;
			filesize = xformat_get_filesize(xformat);	

			ret = 0;
			if (opt_preview == 0) {
				/* TS_sig_load */
				ret = send_signature_files(swi, vofd,
					catalog_path, pax_read_command,
					alt_catalog_root, event_fd, ptar_hdr,
					sig_block_start, sig_block_end, filesize);
			}

			if (ret < 0) {
				SWBIS_ERROR_IMPL();
				return -44;
			}
			
			curpos = uxfio_lseek(ifd, curpos, SEEK_SET);
			if (curpos < 0) {
				SWBIS_ERROR_IMPL();
			}

			ret = xformat_copy_pass(xformat, swi->nullfdM, ifd);
			if (ret < 0) {
				SWBIS_ERROR_IMPL();
			}
		} else if (is_catalog == SWPATH_CTYPE_CAT) {
			/*
			 * =====================================
			 * Read past the remainder of the catalog files.
			 * =====================================
			 */
			E_DEBUG("HERE");
			ret = xformat_copy_pass(xformat, swi->nullfdM, ifd);
			if (ret < 0) {
				SWBIS_ERROR_IMPL();
			}
		} else if (did_fileset == 0 && is_catalog == SWPATH_CTYPE_STORE) {
			/*
			 * Now ready to deal with the storage section.
			 */		
			E_DEBUG("HERE");
			break;
		}
	} /* while */

	E_DEBUG("HERE");
	if (read_header_ret < 0) {
		SWBIS_ERROR_IMPL();
		return -50;
	}
	E_DEBUG("HERE");

	/*
	 * Good, made it this far..
	 * Now, the catalog.tar and signature files are loaded
	 * We could check the signature here before unpacking the catalog.tar
	 * file.
	 */

	/*
	 * ==========================================
	 * Send the task script to unpack the catalog
	 * ==========================================
	 */

	if (opt_preview == 0) {
		E_DEBUG("HERE");
		/* TS_catalog_unpack */
		ret = unpack_catalog_tarfile(swi, vofd, catalog_path, pax_read_command, alt_catalog_root, event_fd);
		if (ret) retval ++;
	}

	/*
	 * ==========================================
	 * Send the task script to install the 
	 * session_options file.
	 * ==========================================
	 */

	if (opt_preview == 0) {
		/* TS_session_options */
		E_DEBUG("HERE");
		ret = write_tar_session_options_file(G,
			swi,
			vofd,
			catalog_path,
			pax_read_command,
			alt_catalog_root,
			event_fd,
			"load session options"
			);
		if (ret) retval ++;
	}

	/*
	 * ==========================================
	 * Send the task script to load the the 
	 * control.sh file.
	 * ==========================================
	 */

	if (opt_preview == 0) {
		/* TS_load_controlsh */
		E_DEBUG("HERE");
		ret = write_tar_controlsh_file(G,
			swi,
			vofd,
			catalog_path,
			pax_read_command,
			alt_catalog_root,
			event_fd,
			"load control.sh"
			);
		if (ret) retval ++;
	}


	if (opt_preview == 0) {
		/*
		 * TS_analysis_002
		 */
		E_DEBUG("HERE");
		ret = run_analysis_script(G, swi, vofd, event_fd);
		if (ret) retval ++;
	}


	/*
	 * ===================================================
	 * Send the INSTALLED file to set the state to transient
	 * ===================================================
	 */

	if (opt_preview == 0) {
		/* TS_load_index_file_90 */
		/*
		 * Audit the control_scripts that must be run
		 */
		E_DEBUG("HERE");
		audit_execution_scripts(G, swi, g_script_array);
		
		update_fileset_state(swi, "*", SW_STATE_TRANSIENT);

		ret = write_tar_installed_software_index_file(G, swi,
			vofd,
			catalog_path,
			pax_read_command,
			alt_catalog_root,
			event_fd,
			"Load " SW_A_INSTALLED " file (loc=90)");
		if (ret) retval ++;
	}

	/*
	 * =======================================
	 * Send the task script for the fileset.
	 * =======================================
	 */
	E_DEBUG("HERE");
	did_fileset = 1;	
	strob_strcpy(newnamebuf, target_path);
	strob_sprintf(tmp, 0,
		"dd 2>/dev/null | %s\n"
		"sw_retval=$?\n"
		"dd of=/dev/null 2>/dev/null\n"
		,
		pax_read_command);

	ret = 0;
	if (opt_preview == 0) {
		/* TS_execution_phase */
		E_DEBUG("HERE");
		ret = construct_execution_phase_script(G, swi,
				 execution_phase_script,
				pax_read_command);
		ret = swicol_rpsh_task_send_script(
			swi->swicolM,
			vofd, 
			payload_size,  /* fileset size plus padding */
			".", /* strob_str(newnamebuf), */
			strob_str(execution_phase_script), /*strob_str(tmp), */ "load fileset"
			);
	}

	if (ret < 0) {
		E_DEBUG("HERE");
		return -1;
	}

	if (strlen(swpath_get_pathname(swpath))) {
		/*
		 * Now seek back 512 bytes.
		 * This is not required if the control directoires are
		 * in the package, but since HP-UX packages don't have the
		 * control directories as archive members then this is
		 * required.
		 */
		/*
	 	 * uh-oh we've dived into the storage section
		 * and there are no control directory archive members.
		 * Meaning the first header of an actual storage file
		 * has just been read.  This happens with HP-UX
		 * packages.
		 */
		E_DEBUG("HERE HP style package");
		if (uxfio_lseek(ifd, -512, SEEK_CUR) < 0) {
			SWI_internal_fatal_error();
		}
	}

	/*
	 * =====================================
	 * Loop over Storage section files.
	 * =====================================
	 */

	while (
		xformat_read_header(xformat) > 0 &&
		(!deadman || (deadman && *deadman == 0))
	) {
		E_DEBUG("HERE: Storage");
		if (xformat_is_end_of_archive(xformat)){
			break;
		}
		optacc_a++;  
		xformat_get_name(xformat, namebuf);
		name = strob_str(namebuf);
		parseret = swpath_parse_path(swpath, name);
		if (parseret < 0) {
			SWBIS_ERROR_IMPL();
			return -55;
		}
		is_catalog = swpath_get_is_catalog(swpath);
		
		/*
		 * Show the parsed path components
		 */
		swpath_shallow_fill_export(swpath_ex, swpath); 
		swlib_doif_writef(swi->verboseM, SWC_VERBOSE_8,
				logspec, efd, "%s",
					swpath_ex_print(swpath_ex, tmp_swpath_ex, ""));

		/*
		 * The path is obtained from the archive path
		 */
		current_path = swpath_get_pathname(swpath);

		/*
		 * Sanitize and check the filename
		 */
		sanitize_pathname(current_path);
		swlib_squash_leading_dot_slash(current_path);

		if (optacc_a == 1) {
			/*
			 * Look for the current file at the
			 * current position of the infoheader.
			 * This is an optimization path.
			 */
			E_DEBUG("HERE: Storage");
			obj = swheader_get_object_by_tag(infoheader, SW_OBJ_file, current_path);
		} else {
			/*
			 * this is the failsafe code path
			 */
			E_DEBUG("HERE: Storage");
			obj = (char*)NULL;
		}	

		if (obj == (char*)(NULL)) {
			E_DEBUG("HERE: Storage");
			swheader_reset(infoheader);
		}
	
		if (current_path && strlen(current_path)) {
			E_DEBUG("HERE: Storage");
			if (obj == (char*)NULL) {
				obj = swheader_get_object_by_tag(infoheader, SW_OBJ_file, current_path);
			}

			if (obj == (char*)NULL) {
				/*
				* This is a hard error, a storage file must have metadata.
				*/
				fprintf(stderr, "%s: File object not found for path=%s\n", swlib_utilname_get(), current_path);
				fprintf(stderr, "%s: This may be a implementation error or an invalid package\n", swlib_utilname_get());
				SWI_internal_error();
				return -58;
			}

			current_file_offset = swheader_get_current_offset(infoheader);
			current_type = swheader_get_attribute(infoheader, "type");
			current_type = swheaderline_get_value(current_type, NULL);
			swheader_set_current_offset(infoheader, current_file_offset);

			if (!current_type) { 
				SWI_internal_error();
				return -59;
			}
	
			/*
			 * * Here's how to print the file object from the INFO file.
			 * swheaderline_write_debug(obj, STDERR_FILENO);
			 * while((next_attr=swheader_get_next_attribute(infoheader)))
			 * 	swheaderline_write_debug(next_attr, STDERR_FILENO);
			 *  
			 * * Reading the header advances the index pointer, therefore you have
			 * * to seek back or reset and find the object again.
			 *
			 * swheader_reset(infoheader);
			 * obj = swheader_get_object_by_tag(infoheader, SW_OBJ_file, swpath_get_pathname(swpath));
			 */
			
			/*
			 * The metadata in the INFO file trumps the metadata in the
			 * storage file tar header.  For now, just compare and warn if
			 * any are different.  Eventually, in order to be compliant, the
			 * storage section tar header is re-written with the file stats from
			 * the INFO file.
			 */
					
			/*
			 * translate the INFO file attributes to a file_hdr object.
			 */
			ahs_init_header(info_ahs2);
			ret = swheader_fileobject2filehdr(infoheader, ahs_vfile_hdr(info_ahs2));
			if (ret) {
				/*
				* FIXME, handle this error better.
				*/
				SWI_internal_error();
				return -62;
			}

			/*
			 * Restore the offset of the current file object
			 */
			swheader_set_current_offset(infoheader, current_file_offset);

			/*
			 * Find the "md5sum" attribute.
			 */
			filemd5 = get_attribute(infoheader, "md5sum", (int *)NULL);
			swheader_set_current_offset(infoheader, current_file_offset);

			/*
			 * Find the "is_volatile" attribute.
			 */
			is_volatile = get_attribute(infoheader, "is_volatile", (int *)NULL);	
			swheader_set_current_offset(infoheader, current_file_offset);

			if (swextopt_is_value_true(is_volatile)) {
				/*
				 * The INFO file had is_volatile set to true.
				 * Apply the volatile file policy here.
				 */
				if (swextopt_is_option_true(SW_E_swbis_install_volatile, opta)) {
					/*
					 * Install volatile file.
					 * Determine the suffix from the user options.
					 */

					newname_suffix = get_opta(opta, SW_E_swbis_volatile_newname);
					if (newname_suffix && strlen(newname_suffix)) {
						strob_strcpy(pathtmp, current_path);
						safe_check_pathname(newname_suffix);
						strob_strcat(pathtmp, newname_suffix);
						current_path = strob_str(pathtmp);
					} else {
						/*
						 * no suffix specified for volatile files
						 */
						;
					}
				} else {
					/*
					 * skip this file because its volatile and policy excludes it.
					 * we must move the file pointer, just dump the data to * /dev/null
					 */		
					ret = xformat_copy_pass_md5(xformat,
						swi->nullfdM,
						ifd, md5buf);
					SWLIB_ASSERT(ret >= 0);
					optacc_a = 0;
					continue;
				}
			} else {
				/*
				 * nothing to do if not volatile
				 */
				;
			}

			if (	md5buf &&
				(!filemd5 && *current_type == SW_ITYPE_f)
			) {
				swlib_doif_writef(swi->verboseM, SWC_VERBOSE_1,
					logspec, efd, "md5sum attribute not found: %s\n", current_path);
			}

			/*
			 * Compare the header stats from the storage section archive
			 * member to the stats from the INFO file.
			 */
			if (sanity_compare(xformat_ahs_object(xformat), 
					info_ahs2,
					swpath_get_pathname(swpath), swi->verboseM >= SWC_VERBOSE_5)) {
				cmpret++;
			}

			/*
			 * Impose the stats from the INFO file.
			 */				
			ahs_copy(xformat_ahs_object(xformat), info_ahs2);

			/*
			 * Set the sanitized name.
			 */
			ahsStaticSetTarFilename(
				ahs_vfile_hdr(xformat_ahs_object(xformat)),
				current_path);

			/*
			 * Set the link name.
			 */
			if (*current_type == SW_ITYPE_h || *current_type == SW_ITYPE_s) {
				current_link_source = swheader_get_attribute(infoheader, "link_source");
				current_link_source = swheaderline_get_value(current_link_source, NULL);
				swheader_set_current_offset(infoheader, current_file_offset);
				if (!current_link_source) { SWI_internal_error(); return -68; }

				if (strlen(current_link_source) > TARLINKNAMESIZE) {
					fprintf(stderr, "tar link name too long\n");
					SWI_internal_error();
					return -74;
				}
				if (strcmp(target_path, "/") == 0) {
					safe_check_pathname(current_link_source);
				} else {
					sanitize_pathname(current_link_source);
				}
				taru_set_new_linkname(xformat->taruM, (struct tar_header *)NULL, current_link_source);
				ahsStaticSetTarLinkname(
					ahs_vfile_hdr(xformat_ahs_object(xformat)),
					 current_link_source);
			} else {
				taru_set_new_linkname(xformat->taruM, (struct tar_header *)NULL, "");
				ahsStaticSetTarLinkname(ahs_vfile_hdr(xformat_ahs_object(xformat)), "");
			}

			if (pstatbytes) {
				off_t meter_len;
				*pstatbytes = 0;
				if (*current_type == SW_ITYPE_f) {
					meter_len = (off_t)(info_ahs2->file_hdrM->c_filesize);
					if (meter_len && meter_len < 512) meter_len = 512;
				} else {
					meter_len = (off_t)0;
				}	
				start_progress_meter(STDOUT_FILENO, current_path, meter_len, pstatbytes);
				*swlib_pump_get_ppstatbytes() = pstatbytes;
			}

			/*
			 * Write the tar header to the target for real.
			 */
			if (fileset_fd != vofd) {
				/*
				 * This code path is used in preview mode.
				 */
				xformat_set_ofd(xformat, fileset_fd);
				header_ret = xformat_write_header(xformat);
				xformat_set_ofd(xformat, vofd);
			} else {
				/*
				 * Normal fast path.
				 */
				header_ret = xformat_write_header(xformat);
			}	
	
			if (header_ret < 0) {
				SWI_internal_error();
				return -76;
			}	
			fileset_write_ret += header_ret;
			
			if (*current_type == SW_ITYPE_f) {
				ret = xformat_copy_pass_md5(xformat,
						fileset_fd,
						ifd, md5buf);
			} else {
				ret = 0;
			}

			if (ret < 0) {
				fprintf(stderr, "error sending file data for file %s\n", current_path);
				return -77;
			} else {
				if (pstatbytes) {
					*pstatbytes = ret;
				}
				if (*current_type == SW_ITYPE_f) {

					/*
					 * Sanity check
					 */

					if (ret < (int)(info_ahs2->file_hdrM->c_filesize)) {
						SWBIS_ERROR_IMPL();
					}
				}
				fileset_write_ret += ret;
			}

			if (pstatbytes) {
				update_progress_meter(SIGALRM);
				stop_progress_meter();
			}

			if (
				opt_preview ||
				(swi->verboseM >= SWC_VERBOSE_3 && header_ret > 0) ||
				(log_level >= 2)
			) {
				int do_write_at_level;
				int taru_ls_verbose_level;

				/*
				 * write a long ls listing.
				 */

				/*
				 * Read and decode the tar header that was just written
				 * to the target.
				 */

				taru_read_in_tar_header2(xformat->taruM,
					ahs_vfile_hdr(xformat_ahs_object(xformat)),
					-1,
					strob_str(xformat->taruM->headerM),
					&eoa,
					xformat_get_tarheader_flags(xformat));
		
				/*
				 * Print and write the line.
				 */

				do_write_at_level = opt_preview ? 1 : SWC_VERBOSE_3;
				if (log_level >= 2 && logspec->logfdM >= 0) {
					do_write_at_level = 1;
				}
			
				taru_ls_verbose_level = determine_tar_listing_verbose_level(swi);	

				taru_print_tar_ls_list(tmp,
					ahs_vfile_hdr(xformat_ahs_object(xformat)),
					taru_ls_verbose_level);
	
				if (swi->swc_idM == SWC_U_L) {
					/* swlist utility */
					uxfio_write(efd, strob_str(tmp), strob_strlen(tmp));
				} else {
					swlib_doif_writef(swi->verboseM, do_write_at_level,
						logspec, efd, "%s", strob_str(tmp));
				}
			}

	
			if (md5buf && *current_type == SW_ITYPE_f) {
				if (filemd5 == NULL) {
					swlib_doif_writef(swi->verboseM, SWC_VERBOSE_1,
						logspec, efd,
							"Warning: md5sum attribute not found in INFO file for %s\n",
							current_path);

				}
				if (strlen(md5buf) && filemd5) {
					if (strcmp(md5buf, filemd5)) {
						swlib_doif_writef(swi->verboseM, SWC_VERBOSE_1,
							logspec, efd, "md5sum mismatch archive=%s INFO=%s: %s\n",
								md5buf, filemd5,
								current_path);
						return -78;
					} else {
						swlib_doif_writef(swi->verboseM, SWC_VERBOSE_7,
							logspec, efd, "%s %s\n", filemd5, current_path);
					}
				}
			}
			if (fileset_write_ret % 512) {
				SWBIS_ERROR_IMPL();
				return -79;
			}
		} else {
			/*
			 * This code path occurs for storage section control directories.
			 */	
			E_DEBUG("HERE: Storage");
			ret = 0;
		}
		optacc_a = 0;
		if (ret < 0) {
			SWBIS_ERROR_IMPL();
		}
		E_DEBUG("HERE: Storage");
	} /* while */

	if (cmpret) {
		fprintf(stderr,
"swinstall: ==Warning== A mismatch between the INFO file and storage section\n"
"swinstall: ==Warning== file attributes was detected.  Currently, swinstall\n"
"swinstall: ==Warning== *does* override all storage section attributes with\n"
"swinstall: ==Warning== the INFO file attributes. This warning will be removed\n"
"swinstall: ==Warning== in a future release.\n");
	}

	if (opt_preview == 0) {
		if (fileset_write_ret > payload_size - 1024) {
			/*
			 * This is a bad error.
			 * No room left to send the tar trailer blocks.
			 */
			SWBIS_ERROR_IMPL();
			return -81;	
		}
	}

	/*
	 * Now send the tar trailer blocks and padding that
	 * the remote dd(1) process is exepecting.
	 */

	if (swi->verboseM > SWC_VERBOSE_10)
	{
		fprintf(stderr, "payload size = %lu fileset_write_size =%d \n",  payload_size, fileset_write_ret);
		fprintf(stderr, "pad amount = %lu\n",  payload_size - fileset_write_ret);
	}

	padamount = (int)(payload_size - fileset_write_ret);
	ret = swlib_pad_amount(vofd, padamount);
	if (ret != padamount) {
		SWBIS_ERROR_IMPL();
		fprintf(stderr, "%s: swlib_pad_amount returned [%d]\n", swlib_utilname_get(), padamount);
		return -83;
	}

	/*
	 * This waits on the fileset loading task.
	 */
	if (opt_preview == 0 && retval == 0) {
		/*
		 * TS_execution_phase
		 */

		ret = swicol_rpsh_task_expect(swi->swicolM,
				event_fd,
				SWICOL_TL_3600 /*time limit*/);
		if (ret != 0) {
			swlib_doif_writef(swi->verboseM, SWC_VERBOSE_1, logspec, efd, "error loading fileset\n");
			return -85;
		}

		if (swi->debug_eventsM)
			swicol_show_events_to_fd(swi->swicolM, STDERR_FILENO, -1);

		/*
		 * Now we must read the events stack and update the script
		 * retsults.
		 */
	
		update_execution_script_results(swi, swi->swicolM, g_script_array);

	} else {
		if (retval == 0 &&
		    swi->swc_idM == SWC_U_I && /* swinstall utility */
		    1
		) {
			/*
		 	 * This satisfies the preview task shell
			 * which serves to make the script wait until
			 * it receives this script.
			 */
			/* TS_preview */
			ret = send_nothing_and_wait(swi, ofd, event_fd, "preview task", SWICOL_TL_18, SW_SUCCESS);
			if (ret) retval ++;
		}
	}
		
	/*
	 * ====================================================
	 * Send the task script to remove the unpacked catalog
	 * ====================================================
	 */

	if (opt_preview == 0 && retval == 0) {
		/* TS_catalog_dir_remove */
		ret = remove_catalog_directory(swi, vofd, catalog_path, pax_read_command, alt_catalog_root, event_fd);
		if (ret) retval ++;
	}

	/*
	 * ===================================================
	 * Send the INSTALLED file
	 * ===================================================
	 */

	if (opt_preview == 0 && retval == 0) {
		/* TS_load_index_file_91 */
		update_fileset_state(swi, "*", SW_STATE_INSTALLED);
		ret = write_tar_installed_software_index_file(G, swi,
			vofd,
			catalog_path,
			pax_read_command,
			alt_catalog_root,
			event_fd,
			"Load " SW_A_INSTALLED " file (loc=91)");
		if (ret) retval ++;
	}

	/*
	 * ===================================================
	 * Done
	 * ===================================================
	 */

	strob_strcpy(tmp, "");
	construct_execution_phase_script(G, swi, tmp,  pax_read_command);

	strob_close(catalogdir);
	strob_close(newnamebuf);
	strob_close(namebuf);
	strob_close(tmp);
	strob_close(tmp2);
	strob_close(tmp_swpath_ex);
	strob_close(resolved_path);
	strob_close(execution_phase_script);
	taru_free_header(file_hdr);
	ahs_close(info_ahs2);
	return retval;
}

static
int
write_target_install_script(GB * G,
		int ofd, 
		char * fp_targetpath, 
		STROB * control_message, 
		char * sourcepath,
		int delaytime,
		int keep_old_files,
		int nhops,
		int vlv,
		char * pax_read_command_key,
		char * hostname,
		char * blocksize,
		SWI_DISTDATA * distdata,
		char * installed_software_catalog,
		int opt_preview,
		char * sh_dash_s,
		int alt_catalog_root,
		struct sw_logspec * logspec
		)
{
	int retval = 0;
	int ret;
	int is_target_trailing_slash;
	int cmd_verbose = 0;
	int idx = 0;
	char * prodtag;
	char * prodrev;
	char * prodvendortag;
	char * pax_read_command;
	char * opt_char_preview;
	STRAR * catalogpathlist;
	STROB * catbuffer;
	STROB * buffer;
	STROB * tmp;
	STROB * tmpexit;
	STROB * to_devnull;
	STROB * set_vx;
	STROB * subsh, * subsh2;
	STROB * mkdircatpath;
	char * target_dirname, *source_basename, *target_basename;
	char * targetpath;
	char * x_targetpath;
	char umaskbuf[10];

	SWLIB_ASSERT(fp_targetpath != NULL);
	targetpath = strdup(fp_targetpath);

	if (swlib_check_safe_path(targetpath)) { return 2; }
	if (swlib_check_safe_path(sourcepath)) { return 3; }
	
	catalogpathlist = strar_open();
	buffer = strob_open(1);
	catbuffer = strob_open(1);
	tmp = strob_open(10);
	tmpexit = strob_open(10);
	to_devnull = strob_open(10);
	set_vx = strob_open(10);
	subsh = strob_open(10);
	subsh2 = strob_open(10);
	mkdircatpath = strob_open(10);

	/*
	 * Run sanity checks on the distribution tag
	 * and product tags and revisions.
	 */

	if (swlib_is_sh_tainted_string(installed_software_catalog)) { return 4; }
	if (swlib_is_sh_tainted_string(distdata->dist_tagM)) { return 5; }
	if (swlib_is_sh_tainted_string(distdata->catalog_bundle_dir1M)) { return 6; }
	idx = 0;

	/*
	 * Always treat the installed_software catalog path
	 * as being relative to the target_path
	 */
	swlib_squash_all_leading_slash(installed_software_catalog);

	strob_strcpy(catbuffer, installed_software_catalog);
	swlib_unix_dircat(catbuffer, distdata->catalog_bundle_dir1M);

	/*
	 * catbuffer contents:
	 * <installed_software_catalog>/<bundle_tag>
	 */

	prodtag = strar_get(distdata->product_tagsM, idx);
	prodrev = strar_get(distdata->product_revisionsM, idx);
	prodvendortag = strar_get(distdata->vendor_tagsM, idx);

	while(prodtag) {
		strob_strcpy(tmp, strob_str(catbuffer));
		if (swlib_is_sh_tainted_string(prodtag)) { 
			return 7;
		}
		if (swlib_is_sh_tainted_string(prodrev)) { 
			return 8;
		}
		swlib_unix_dircat(tmp, prodtag);
		strob_strcat(tmp, "/");
		if (prodrev) {
			strob_strcat(tmp, prodrev);
		} else {
			/*
			 * Default revision policy is currently
			 * return with error.
			 */
			return 9;
			strob_strcat(tmp, SWBIS_DEFAULT_REVISION);
		}
		strar_add(catalogpathlist, strob_str(tmp));
		idx++;
		prodtag = strar_get(distdata->product_tagsM, idx);
		prodrev = strar_get(distdata->product_revisionsM, idx);
		prodvendortag = strar_get(distdata->vendor_tagsM, idx);
	}

	/*
	 * catalogpathlist[0] contents:
	 * <installed_software_catalog>/<bundle_tag>/<prod_tag>/<prod_rev>	
	 */
	
	if (idx == 0) {
		fprintf(stderr, "no products in distribution\n");
		return 1;
	}

	if (idx > 1) {
		fprintf(stderr, "multiple products not supported\n");
		return 1;
	}
	
	if (opt_preview == 0) {
		opt_char_preview = "";
		strob_strcpy(mkdircatpath, "mkdir -p \"$catpath\"");
	} else {
		opt_char_preview = "true";
		strob_strcpy(mkdircatpath, "");
	}

	if (vlv <= SWC_VERBOSE_4 && keep_old_files == 0) {
		strob_strcpy(to_devnull, "2>/dev/null");
	}

	if (vlv >= SWC_VERBOSE_7 && keep_old_files == 0) {
		cmd_verbose = SWC_VERBOSE_1;
	}
	
	if (vlv >= SWC_VERBOSE_SWIDB) {
		strob_strcpy(set_vx, "set -vx\n");
	}

	pax_read_command = swc_get_pax_read_command(g_pax_read_commands, pax_read_command_key, 
					cmd_verbose, 
					keep_old_files, DEFAULT_PAX_R);

	swc_print_umask(umaskbuf, sizeof(umaskbuf));

	is_target_trailing_slash = (strlen(targetpath) && 
				targetpath[strlen(targetpath) - 1] == '/');

	swlib_doif_writef(vlv, SWC_VERBOSE_IDB, 
		logspec, swc_get_stderr_fd(G), 
			"swc_write_target_copy_script : source_type [%s]\n",
						 strob_str(control_message));
	/*
	 * Squash the trailing slash.
	 */

	swlib_squash_double_slash(targetpath);
	if (strlen(targetpath) > 1) {
		if (targetpath[strlen(targetpath) - 1] == '/' ) {
			targetpath[strlen(targetpath) - 1] = '\0';
		}
	}

	strob_strcpy(tmp, "");	
	swlib_dirname(tmp, targetpath);
	target_dirname = strdup(strob_str(tmp));
	swlib_basename(tmp, sourcepath);
	source_basename = strdup(strob_str(tmp));
	swlib_basename(tmp, targetpath);
	target_basename = strdup(strob_str(tmp));

	/*
	 * Unpack the archive using pax
	 * The source control string was 
	 * SWBIS_SWINSTALL_SOURCE_CTL_DIRECTORY
	 */

	x_targetpath = targetpath;
		
	ret = swlib_writef(ofd, buffer, 
	"echo " SWBIS_TARGET_CTL_MSG_125 ": " KILL_PID "\n"
	"false_() {\n"
	"	return 1\n"
	"}\n"
	"	%s\n" /* Session Begins */
	"	%s"
	"	sw_retval=0\n"
	"	%s"
	"	opt_preview=\"%s\"\n"
	"	umask %s\n"
	"	blocksize=\"%s\"\n"
	"	xtarget=\"%s\"\n"
	"	wcwd=`pwd`\n"
	"	if test -f \"$xtarget\" -o -p \"$xtarget\" -o -b \"$xtarget\" -o -c \"$xtarget\"; then\n"
			/*
			* error
			* The target must be a directory or be non-existent.
			*/
	"		sw_retval=1\n"
	"		if test \"$sw_retval\" != \"0\"; then\n"
	"			 dd count=1 of=/dev/null 2>/dev/null\n"
	"		fi\n"
	"		%s\n" 				/* SW_SESSION_ENDS: status=1 */
	"		exit $sw_retval\n"
	"	fi\n"
	"	cd \"$xtarget\" 2>/dev/null\n"
	"	sw_retval=$?\n"
	"	if test \"$xtarget\" = '.'; then\n"
	"		sw_retval=1\n"
	"	else\n"
	"		if test $sw_retval !=  \"0\"; then\n"
	"			if test \"$opt_preview\" != \"true\"; then\n"
	"				mkdir -p \"$xtarget\"\n"
	"				sw_retval=$?\n"
	"			else\n"
	"				xtarget=/\n"
	"				sw_retval=1\n"
	"			fi\n"
	"		else\n"
	"			cd \"$wcwd\"; sw_retval=1;\n"
	"		fi\n"
	"	fi\n"
	"	if test \"$sw_retval\" = \"0\"; then\n"
	"	%s\n"  /* SOC_CREATED */
	"	fi\n"
	"	cd \"$xtarget\" 2>/dev/null\n"
	"	sw_retval=$?\n"

	/* ---------------- */

	"	swexec_status=0\n"
	"	export swexec_status\n"
	"	case \"$sw_retval\" in\n"
	"		0)\n"
	"			swexec_status=0\n"
	"			catalogbasepath=\"%s\"\n"
	"			(\n"
	"			umask 0077\n"
	"			d=0\n"
	"			catpath=\"$catalogbasepath\"/$d\n"
	"			while test -d \"$catpath\"\n"
	"			do\n"
	"				d=$(($d+1))\n"
	"				catpath=\"$catalogbasepath\"/$d\n"
	"			done\n"
	"			d=0\n"
	"			catpath=\"$catalogbasepath\"/$d\n"
	"			%s\n" 				/* mkdir -p \"$catpath\"\n" */
	"			echo " SWBIS_TARGET_CTL_MSG_128 ": $catpath\n"
	"			)\n"
	"			;;\n"
	"		*)\n"
	"			swexec_status=1\n"
	"			echo " SWBIS_TARGET_CTL_MSG_508 "\n"
	"			;;\n"
	"	esac\n"
	"	case $swexec_status in	0) %s ;; esac\n" 	/* TS_uts_query Task shell for uts query task */
	"	sw_retval=$?\n"
	"	if [ \"$opt_preview\" ]; then\n"
	"		%s\n"					/* TS_preview Task shell for the preview mode */
	"		swexec_status=1\n"
	"		# exit 0\n"
	"	fi\n"
	"	case $sw_retval in 0) ;; *) swexec_status=1; sw_retval=1; ;; esac\n"
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* SW_ANALYSIS_BEGINS */
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_analysis_001 Task shell: analysis phase */
	"	sw_retval=$?\n"						/* Return status */
	"	case $sw_retval in 0) ;; *) swexec_status=1; sw_retval=1; ;; esac\n"
	"	case \"$sw_retval\" in\n"
	"		0)\n"
	"			;;\n"
	"		*)\n"
	"			swexec_status=1\n" 			/* error! Remove the catalog directory */
	"			sw_retval=1\n"
	"			rmdir \"$catpath\" 2>/dev/null\n"
	"			;;\n"
	"	esac\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_catalog_load Task shell for catalog load */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_sig_load Task shell for signature load */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"

	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_catalog_unpack */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_session_options Task shell */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_load_controlsh */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_analysis_002 */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	if [ \"$opt_preview\" = \"\" ]; then\n"
	"	%s\n"							/* SW_ANALYSIS_ENDS */
	"	fi\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* echo SW_EXECUTION_BEGINS */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_load_index_file_90 Task shell */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_execution_phase Task shell for Execution */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_catalog_dir_remove Task shell */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* TS_load_index_file_91 Task shell */
	"	sw_retval=$?\n"
	"	swexec_status=$sw_retval\n"
	
	"	case $swexec_status in	0) %s ;; *) false_ ;; esac\n" 	/* SW_EXECUTION_ENDS */
	"	case $sw_retval in 0) ;; *) swexec_status=1; sw_retval=1; ;; esac\n"
	"	if [ \"$opt_preview\" ]; then\n"
	"		swexec_status=0\n"
	"		sw_retval=0\n"
	"	fi\n"
	"	sleep %d\n"
	"	%s\n" /* Session Ends */
	"%s\n"
	,
	TEVENT(2, vlv, SW_SESSION_BEGINS, ""),
	swicol_subshell_marks(subsh, "target", 'L', nhops, vlv),
	strob_str(set_vx),
	opt_char_preview,
	umaskbuf,
	blocksize,
	x_targetpath, 
	TEVENT(2, vlv, SW_SESSION_ENDS, "status=$sw_retval"),
	TEVENT(2, vlv, SW_SOC_CREATED, x_targetpath),

	/* ----------------- */

	strar_get(catalogpathlist, 0), 	/* <installed_software_catalog>/<bundle_tag>/<prod_tag>/<prod_rev> */
	strob_str(mkdircatpath), 	/*  mkdir -p <catalogpathlist[0]> */
	sh_dash_s, 			/* TS_uts_query  uts attibute task shell */
	sh_dash_s, 			/* TS_preview    Preview mode task shell */
	TEVENT(2, vlv, SW_ANALYSIS_BEGINS, ""),
	sh_dash_s, 			/* TS_analysis_001   *Not Used* */
	sh_dash_s, 			/* TS_catalog_load  Catalog load task shell */
	sh_dash_s, 			/* TS_sig_load  Signature load task shell */
	sh_dash_s, 			/* TS_catalog_unpack Catalog unpack task shell */
	sh_dash_s,			/* TS_session_options */
	sh_dash_s, 			/* TS_load_controlsh */
	sh_dash_s, 			/* TS_analysis_002   analysis phase task shell */
	TEVENT(2, 1/*verbose level*/, SW_ANALYSIS_ENDS, "status=$sw_retval"),
	TEVENT(2, vlv, SW_EXECUTION_BEGINS, ""),
	sh_dash_s,			/* TS_load_index_file_90 */
	sh_dash_s,			/* TS_execution_phase  fileset load task shell */
	sh_dash_s,			/* TS_catalog_dir_remove */
	sh_dash_s,			/* TS_load_index_file_91 */
	TEVENT(2, vlv, SW_EXECUTION_ENDS, "status=$sw_retval"),
	delaytime,
	TEVENT(2, vlv, SW_SESSION_ENDS, "status=$sw_retval"),
	swicol_subshell_marks(subsh2, "install_target", 'R', nhops, vlv)
	);

	free(targetpath);
	free(source_basename);
	free(target_dirname);
	free(target_basename);
        if (G->g_target_script_name) {
                swc_tee_to_file(G->g_target_script_name, strob_str(buffer));
        }
	strob_close(buffer);
	strob_close(catbuffer);
	strob_close(tmp);
	strob_close(tmpexit);
	strob_close(set_vx);
	strob_close(to_devnull);
	strob_close(subsh);
	strob_close(subsh2);
	strob_close(mkdircatpath);
	
	if (ret <= 0) retval = 1;
	swlib_doif_writef(vlv, SWC_VERBOSE_IDB, 
		logspec, swc_get_stderr_fd(G), 
			"swc_write_target_copy_script : retval = [%d]\n",
				retval);
	/*
	 *  retval:
	 *	0 : Ok
	 *	1 : Error
	 */
	return retval;
}

/* ---------------------------------------------------------------- */
/* ------------------ PUBLIC ---- PUBLIC -------------------------- */
/* ----- Routines below are likely called from swinstall.c -------- */
/* ---------------------------------------------------------------- */

int
swinstall_write_target_install_script(GB * G,
		int ofd,
		char * fp_targetpath, 
		STROB * control_message, 
		char * sourcepath,
		int delaytime,
		int keep_old_files,
		int nhops,
		int vlv,
		char * pax_read_command_key,
		char * hostname,
		char * blocksize,
		SWI_DISTDATA * distdata,
		char * installed_software_catalog,
		int opt_preview,
		char * sh_dash_s,
		int alt_catalog_root,
		struct sw_logspec * logspec
		)
{
	return
	write_target_install_script(
		G,
		ofd,
		fp_targetpath, 
		control_message, 
		sourcepath,
		delaytime,
		keep_old_files,
		nhops,
		vlv,
		pax_read_command_key,
		hostname,
		blocksize,
		distdata,
		installed_software_catalog,
		opt_preview,
		sh_dash_s,
		alt_catalog_root,
		logspec);
}

int
swinstall_arfinstall(GB * G, SWI * swi, int ofd, int efd, int * deadman,
	char * target_path, char * catalog_prefix, VPLOB * swspecs,
	int section, int altofd, int opt_preview, char * pax_read_command_key,
	int alt_catalog_root, int event_fd, struct extendedOptions * opta,
	struct sw_logspec * logspec, int keep_old_files, unsigned long int * pstatbytes)
{
	int ret;
	int format;
	XFORMAT * xformat = swi->xformatM;

	if (swi->swi_pkgM->target_pathM) {
		free(swi->swi_pkgM->target_pathM);
		swi->swi_pkgM->target_pathM = NULL;
	}

	if (swi->swi_pkgM->catalog_entryM) {
		free(swi->swi_pkgM->catalog_entryM);
		swi->swi_pkgM->catalog_entryM = NULL;
	}
	swi->swi_pkgM->target_pathM = strdup(target_path);
	swi->swi_pkgM->catalog_entryM = strdup(catalog_prefix);

	/*
	 * catalog_prefix is nominally:
	 *     var/lib/swbis/catalog/<name>/<version>/<seq_no>
	 * 
	 * target path is the absolute path
	 */
	
	E_DEBUG("HERE");
	if (swi->swc_idM == SWC_U_L) {
		;
	} else {
		if (
			swlib_check_clean_absolute_path(swi->swi_pkgM->target_pathM) ||
			0
		) {
			E_DEBUG2("HERE path is [%s]", swi->swi_pkgM->target_pathM);
			return 10;
		}
	}
	
	E_DEBUG("HERE");
	if (strlen(swi->swi_pkgM->catalog_entryM)) {
		/* Disable the check for zero length strings since this is what
		   happens when swlist uses this routine in list mode */
		if (
			swlib_check_clean_path(swi->swi_pkgM->catalog_entryM) ||
			0
		) {
			return 3;
		}
	}

	E_DEBUG("HERE");
	format = xformat_get_format(xformat);

	swicol_set_targetpath(swi->swicolM, target_path);

	{ /* -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ 
		/*
		* Scratch test area
		*/
		/*
		STROB * tmp = strob_open(10);
		swextopt_write_session_options(tmp, opta, SWC_U_I);
		fprintf(stderr, "%s", strob_str(tmp));
		strob_close(tmp);
		*/
	} /* -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ 

	E_DEBUG("HERE");
	if (format == arf_tar || format == arf_ustar) {
		/*
		 * All tar formats
		 */
		ret = arfinstall_tar(G, swi, ofd, efd, deadman,
				target_path, catalog_prefix, swspecs,
				section, altofd, opt_preview, pax_read_command_key,
				alt_catalog_root, event_fd, opta, logspec, keep_old_files, pstatbytes);
	} else {
		/*
		 * all other formats
		 */
		fprintf(stderr, "%s: cpio formats currently not supported\n",
			swlib_utilname_get());
		ret = 1;
	}
	return ret;
}
