/* swicat.c -- installed software catalog files and environment setup.
   
   Copyright (C) 2004 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.  */


#include "swuser_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "swheader.h"
#include "swheaderline.h"
#include "ugetopt_help.h"
#include "to_oct.h"
#include "tarhdr.h"
#include "swi.h"
#include "swi_xfile.h"
#include "swinstall.h"
#include "atomicio.h"
#include "ls_list.h"
#include "swevents.h"
#include "swicat.h"

static
void
add_path_code(STROB * buf)
{
	/* 
	 * aa="${aa:="$bb"}"
	 */
	strob_sprintf(buf, STROB_DO_APPEND,
	"gpath=`getconf PATH`\n"
	"case \"$gpath\" in\n"
	"	\"\")\n"
	"		SWPATH=\"$PATH\"\n"
	"		;;\n"
	"	*)\n"
	"		SWPATH=\"${gpath}:${PATH}\"\n"
	"		;;\n"
	"esac\n"
	);
}

static
void
write_shell_assign1(STROB * buf, char * varname, char * value)
{
	/* 
	 * aa="${aa:="$bb"}"
	 */
	strob_sprintf(buf, STROB_DO_APPEND, "%s=\"${%s:=\"%s\"}\"\n", varname, varname, value);
}

static
void
form_session_options_filename(STROB * tmp, SWI * swi)
{

	strob_strcpy(tmp, swi->swi_pkgM->target_pathM);
	swlib_unix_dircat(tmp, swi->swi_pkgM->catalog_entryM);
	swlib_unix_dircat(tmp, "session_options");

	/*
	 * Now make some sanity checks
	 */
	swlib_squash_trailing_slash(strob_str(tmp));
	swlib_squash_double_slash(strob_str(tmp));
	if (swlib_is_clean_absolute_path(strob_str(tmp)))
		SWLIB_FATAL("internal error");
	return;
}
	
static
void
write_attributes(STROB * buf, SWHEADER * swheader, int index)
{
	char * line;
	int level;
	char * keyword;
	char * value;

	swheader_store_state(swheader);
	swheader_set_current_offset(swheader, index);
	while((line=swheader_get_next_attribute(swheader))) {
		keyword = swheaderline_get_keyword(line);
		value = swheaderline_get_value(line, (int *)NULL);
		level = swheaderline_get_level(line);
		swdef_write_keyword_to_buffer(buf, keyword, level, SWPARSE_MD_TYPE_ATT);
		swdef_write_value_to_buffer(buf, value);
	}
	swheader_restore_state(swheader);
}

static
void
write_swbis_env_vars(STROB * buf, SWI * swi)
{
	return;
}

static
void
form_absolute_control_dir(STROB * tmp, SWI * swi, char * control_script_pkg_dir, char ** script_name)
{
	char * s;

	strob_strcpy(tmp, swi->swi_pkgM->target_pathM);
	swlib_unix_dircat(tmp, swi->swi_pkgM->catalog_entryM);
	swlib_unix_dircat(tmp, SWINSTALL_INCAT_NAME);
	swlib_unix_dircat(tmp, control_script_pkg_dir);

	/*
	 * Now make some sanity checks
	 */
	swlib_squash_trailing_slash(strob_str(tmp));
	swlib_squash_double_slash(strob_str(tmp));
	if (swlib_is_clean_absolute_path(strob_str(tmp)))
		SWLIB_FATAL("internal error");

	/*
	 * Now take the dirname of this.
	 */
	s = strrchr(strob_str(tmp), '/');
	if (s) {
		*s = '\0';
		s++;
		*script_name = s;
	} else {
		SWLIB_FATAL("internal error");
	}
	return;
}

static
void 
write_state(STROB * buf, SWI_XFILE * xx, int do_if_active)
{
	if (do_if_active && !(xx->is_activeM)) return;
	if (strcmp(xx->stateM, SW_STATE_UNSET)) {
		strob_sprintf(buf, STROB_DO_APPEND,  "    " SW_A_state " %s\n", xx->stateM);
	}
	return;
}

static
void 
write_times(STROB * buf, SWI_BASE * xx, int do_if_active)
{
	time_t caltime;
	
	/*if (do_if_active && !(xx->is_activeM)) return;
	*/
	caltime = (time_t)(xx->create_timeM);
	strob_sprintf(buf, STROB_DO_APPEND, "    " SW_A_create_time " %lu # %s", (long unsigned)(xx->create_timeM), ctime(&caltime));
	caltime = (time_t)(xx->mod_timeM);
	strob_sprintf(buf, STROB_DO_APPEND, "    " SW_A_mod_time " %lu # %s", (long unsigned)(xx->mod_timeM), ctime(&caltime));
}

static
void
add_env_entry_localdefault(char * name, char * value, STROB * buf)
{
	char * s;
	if (value) {
		s = value;
	} else {
		s = getenv(name);
	}
	if (!s) s="";
	write_shell_assign1(buf, name, s);
}

static
void
add_env_entry(char  * name, char * value, STROB * buf)
{
	char * s;

	if (value) {
		s = value;
	} else {
		s = getenv(name);
	}
	if (s) {
		strob_sprintf(buf, STROB_DO_APPEND, "%s=\"%s\"\n", name, s); 
		strob_sprintf(buf, STROB_DO_APPEND, "export %s\n", name); 
	}
}

int
swicat_write_installed_software(SWI * swi, int ofd)
{
	/*
	* Write the installed software file
	*
	* Example:

	installed_software
	path /
	catalog var/lib/swbis/catalog

	product
	create_time 1059628779
	mod_time 1059629009
	all_filesets BIN DOC
	filesets BIN DOC
	create_time 1059628779
	mod_time 1059629009
	tag foo

	control_file
	tag postinstall
	result 0

	fileset
	create_time 1059628779
	mod_time 1059629009
	tag BIN
	state installed

	fileset
	tag DOC
	create_time 1059628779
	mod_time 1059629009
	state installed
	*/
	STROB * buf = strob_open(10);
	int ret;
	swicat_isf_installed_software(buf, swi);
	ret = atomicio((ssize_t (*)(int, void *, size_t))write,
				ofd, (void*)(strob_str(buf)), strob_strlen(buf));
	strob_close(buf);
	return ret;
}

int
swicat_isf_all_scripts(STROB * buf, SWI_SCRIPTS * xx, int do_if_active)
{
	int i = 0;
	while(i < SWI_MAX_OBJ && xx->swi_coM[i]) {
		swicat_isf_control_script(buf, xx->swi_coM[i], do_if_active);
		i++;
	}
	return 0;
}

int
swicat_isf_control_script(STROB * buf, SWI_CONTROL_SCRIPT * xx, int do_if_active)
{
	/* if (do_if_active && !(xx->is_activeM)) return 0; */
	strob_sprintf(buf, STROB_DO_APPEND, SW_A_control_file "\n");
	strob_sprintf(buf, STROB_DO_APPEND, "\t" SW_A_tag " %s\n", xx->tagM);
	strob_sprintf(buf, STROB_DO_APPEND, "\t" SW_A_result " %s\n", swi_control_script_posix_result(xx));
	if (xx->resultM >= 0)
		strob_sprintf(buf, STROB_DO_APPEND, "\t" "return_code" " %d\n", xx->resultM);
	return 0;
}

int
swicat_isf_fileset(SWI * swi, STROB * buf, SWI_XFILE * xfile, int do_if_active)
{
	/* if (do_if_active && !(xfile->is_activeM)) return 0; */

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

	write_attributes(buf, SWI_INDEX_HEADER(swi), xfile->INDEX_header_indexM);
	write_state(buf, xfile, do_if_active);
	write_times(buf, (SWI_BASE *)xfile, 1 /* do_if_active */);

	/*
	{
		char * line;
		SWHEADER * swheader;
		xfile->INDEX_header_indexM;
		swheader = SWI_INDEX_HEADER(swi);
		swheader_store_state(swheader);
		swheader_set_current_offset(swheader, xfile->INDEX_header_indexM);
		while((line=swheader_get_next_attribute(swheader)))
			swheaderline_write_debug(line, STDERR_FILENO);
		swheader_restore_state(swheader);
	}
	*/

	swicat_isf_all_scripts(buf, xfile->swi_scM, do_if_active);
	strob_sprintf(buf, STROB_DO_APPEND, "\n");
	return 0;
}

int
swicat_isf_product(SWI * swi, STROB * buf, SWI_PRODUCT * prod, int do_if_active)
{
	int i = 0;
	SWHEADER * swheader;

	swheader = SWI_INDEX_HEADER(swi);
	/* if (do_if_active && !(prod->is_activeM)) return 0; */
	strob_sprintf(buf, STROB_DO_APPEND, SW_A_product "\n");
		
	write_attributes(buf, SWI_INDEX_HEADER(swi), prod->header_indexM);

	write_times(buf, (SWI_BASE *)prod, 1 /* do_if_active */);
	/* strob_sprintf(buf, STROB_DO_APPEND, SW_A_all_filesets "%s\n", "" ); */

	/* ------------------------------------------------------ */
	/* ------------------------------------------------------ */
	/* ------------------------------------------------------ */
	/*
	{
		swheader_store_state(swheader);
		swheader_set_current_offset(swheader, prod->header_indexM);
		while((line=swheader_get_next_attribute(swheader)))
			swheaderline_write_debug(line, STDERR_FILENO);
		swheader_restore_state(swheader);
	}
	*/
	/* ------------------------------------------------------ */
	/* ------------------------------------------------------ */
	/* ------------------------------------------------------ */

	swicat_isf_all_scripts(buf, prod->pfilesM->swi_scM, do_if_active);
	while(i < SWI_MAX_OBJ && prod->swi_coM[i]) {
		swicat_isf_fileset(swi, buf, prod->swi_coM[i], do_if_active);
		i++;
	}
	return 0;
}

int
swicat_isf_installed_software(STROB * buf, SWI * swi)
{
	SWI_PRODUCT * prod = swi->swi_pkgM->swi_coM[0];
	strob_sprintf(buf, STROB_DO_APPEND, SW_A_installed_software "\n");
	strob_sprintf(buf, STROB_DO_APPEND, "\t" SW_A_path " %s\n", swi->swi_pkgM->target_pathM);
	strob_sprintf(buf, STROB_DO_APPEND, "\t" SW_A_catalog " %s\n", swi->swi_pkgM->catalog_entryM);
	swicat_isf_product(swi, buf, prod, 1);
	if (swi->swi_pkgM->swi_coM[1]) {
		fprintf(stderr, "multiple products not supported\n");
		return 1;
	}
	return 0;
}

int
swicat_env(STROB * buf, SWI * swi, char * control_script_pkg_dir, char * tag)
{
	STROB * tmp = strob_open(10);
	char * tmp_s;

	add_env_entry("LC_CTYPE", "", buf);
	add_env_entry("LC_MESSAGES", "", buf);
	add_env_entry("LC_TIME", "", buf);
	add_env_entry("TZ", "", buf);
	add_env_entry("LANG", "C", buf);
	add_env_entry("LC_ALL", "C", buf);

	if (swi->swi_pkgM->target_pathM) {
		add_env_entry("SW_ROOT_DIRECTORY", swi->swi_pkgM->target_pathM, buf);
	} else {
		fprintf(stderr, "warning: swi->swi_pkgM->target_pathM is null, using /\n");
		add_env_entry("SW_ROOT_DIRECTORY", "/", buf);
	}

	/*
	 * SW_CATALOG same as installed_software.catalog and may be
	 * a relative path
	 */
	add_env_entry("SW_CATALOG", swi->swi_pkgM->installed_software_catalogM, buf);

	add_env_entry("SW_PATH", "\"$PATH\"", buf);

	/*
	 * SWBIS_CATALOG_ENTRY is what is after the <installed_software_catalog> path
	 * fragment
	 */

	SWLIB_ASSERT(strlen(swi->swi_pkgM->installed_software_catalogM));
	tmp_s = strstr(swi->swi_pkgM->catalog_entryM, swi->swi_pkgM->installed_software_catalogM);
	SWLIB_ASSERT(tmp_s != NULL);
	tmp_s += strlen(swi->swi_pkgM->installed_software_catalogM);
	while(*tmp_s == '/') tmp_s++;

	add_env_entry("SWBIS_CATALOG_ENTRY", tmp_s, buf);

	add_env_entry("SW_LOCATION", "", buf);
	
	add_path_code(buf);

	add_env_entry("SW_SESSION_OPTIONS",
		"${SW_ROOT_DIRECTORY}/${SW_CATALOG}/${SWBIS_CATALOG_ENTRY}/" SW_A_session_options , buf);

	/* add SW_SOFTWARE_SPEC */
	/* FIXME , add support for sw selections */
	add_env_entry("SW_SOFTWARE_SPEC", "*", buf);

	strob_strcat(buf, ". \"${SW_SESSION_OPTIONS}\"\n");	

	strob_close(tmp);
	return 0;
}

int
swicat_make_options_file(STROB * buf)
{
	strob_strcpy(buf, "");	
	return 0;
}

void
swicat_write_auto_comment(STROB * buf, char * filename)
{
        time_t caltime;
        time(&caltime);
        strob_sprintf(buf, STROB_DO_APPEND,
                "# %s\n"
                "# Automatically generated by %s version %s on %s"
                "#\n",
                filename,
		swlib_utilname_get(), SWBIS_RELEASE,
		ctime(&caltime));
}

void
swicat_construct_controlsh_taglist(SWI * swi, char * sw_selections, STROB * list)
{
	int i;
	int j;
	SWI_PACKAGE * package;
	SWI_PRODUCT * product;
	SWI_XFILE * fileset;
	STROB * tmp;

	tmp = strob_open(100);
	strob_strcpy(list, "");

	package = swi->swi_pkgM;
	for(i=0; i<SWI_MAX_OBJ; i++) {
		product = package->swi_coM[i];
		if (product) {
			SWLIB_ASSERT(product->tagM != NULL);
			strob_strcpy(tmp, product->tagM);
			if (swlib_is_clean_path(product->tagM)) SWLIB_ASSERT(0);
			strob_sprintf(list, STROB_DO_APPEND, "%s", strob_str(tmp));
			for(j=0; j<SWI_MAX_OBJ; j++) {
				fileset = product->swi_coM[j];
				if (fileset) {
					SWLIB_ASSERT(fileset->tagM != NULL);
					if (swlib_is_clean_path(fileset->tagM)) SWLIB_ASSERT(0);
					strob_strcpy(tmp, product->tagM);
					strob_strcat(tmp, ".");
					strob_strcat(tmp, fileset->tagM);
					strob_sprintf(list, STROB_DO_APPEND, " %s", strob_str(tmp));
				} else break;
			}
		} else break;
		strob_sprintf(list, STROB_DO_APPEND, " ");
	}
	strob_close(tmp);
}

int
swicat_write_script_cases(SWI * swi, STROB * buf, char * sw_selection)
{
	int ret;
	char * product_tag;
	char * fileset_tag;
	SWVERID * swverid;
	CPLOB * taglist;
	SWI_PRODUCT * product;
	SWI_XFILE * xfile;
	SWI_SCRIPTS * scripts;

	/*
	 * the first step is too find the object implied by
	 * sw_selection.  sw_selection has the form:
	 *     <prod_tag>.<fileset_tag>
	 */

	swverid = swverid_open(SW_A_product, NULL);
	taglist = swverid_u_parse_swspec(swverid, sw_selection);

	/*
	 * do a sanity check,
	 * only 2 dot delimited fields are allowed.
	 */	

	if (cplob_get_nused(taglist) > 3) {
		/* i.e.
		 *    <bundle>.<product>.<fileset><NUL>
		 */
		SWLIB_ASSERT(0);   /* fatal */
	}		

	product_tag = cplob_val(taglist, 0);
	fileset_tag = cplob_val(taglist, 1);  /* may be NUL */

	product = swi_find_product_by_swsel(swi->swi_pkgM, product_tag);
	SWLIB_ASSERT(product != NULL);   /* fatal */

	if (fileset_tag) {
		xfile = swi_find_fileset_by_swsel(product, fileset_tag);
	} else {
		/*
		 * the scripts reside in the pfiles object
		 */
		xfile = product->pfilesM;
	}
	SWLIB_ASSERT(xfile != NULL);   /* fatal */
	scripts = xfile->swi_scM;
	SWLIB_ASSERT(scripts != NULL);   /* fatal */

	/*
	 * OK, we now have the scripts for the product or fileset
	 * Loop over them and write the shell code fragment for each
	 * POSIX script.
	 */
	strob_sprintf(buf, STROB_DO_APPEND,
		"\t#\n"
		"\t# write script cases here\n"
		"\t#\n");
		
	ret = swi_afile_write_script_cases(scripts,  buf);
	ret = 0;
	SWLIB_ASSERT(ret == 0);   /* fatal */

	swverid_close(swverid);
	cplob_close(taglist);
	return 0;
}
