/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * System includes
 */

#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <locale.h>
#include <libintl.h>
#include <assert.h>

/*
 * local pkg command library includes
 */

#include "libinst.h"
#include "messages.h"

/*
 * forward declarations
 */

static int
collectError(int *r_numZones, char **r_zoneNames, char *a_packageName,
	depckl_t *a_dck, int a_depIndex, depckErrorRecord_t *a_eir,
	int a_errIndex);

/*
 * *****************************************************************************
 * global external (public) functions
 * *****************************************************************************
 */

int
depchkReportErrors(depckl_t *a_dck)
{
	char	*packageName;
	char	*zonenames;
	char	msgbuf[4096];
	int	err;
	int	i;
	int	numzones = 0;

	/* entry assertions */

	assert(a_dck != (depckl_t *)NULL);

	/* entry debugging info */

	echoDebug(DBG_DEPCHK_ENTRY);

	zonenames = (char *)NULL;

	/* go through dependency table, collect, collapse, report errors */

	for (i = 0; a_dck[i].name != (char *)NULL; i++) {
		int	j;
		depckError_t	*erc;

		if (zonenames != (char *)NULL) {
			free(zonenames);
			zonenames = (char *)NULL;
		}

		erc = a_dck[i].record;
		if (erc->er_numEntries == 0) {
			continue;
		}

		for (j = 0; j < erc->er_numEntries; j++) {
			int	k;
			depckErrorRecord_t *eir;

			if (zonenames != (char *)NULL) {
				free(zonenames);
				zonenames = (char *)NULL;
			}

			eir = &erc->er_theEntries[j];
			packageName = eir->ier_packageName;
			for (k = 0; k < eir->ier_numZones; k++) {
				int err;

				err = collectError(&numzones, &zonenames,
					packageName, a_dck, i, eir, k);
				if (err != 0) {
					if (zonenames != (char *)NULL) {
						free(zonenames);
						zonenames = (char *)NULL;
					}
					return (err);
				}
			}

			if (a_dck[i].ignore_values == (char *)NULL) {
				continue;
			}

			if (a_dck[i].err_msg == (char **)NULL) {
				(void) snprintf(msgbuf, sizeof (msgbuf),
					ERR_DEPENDENCY_IGNORED, a_dck[i].name,
					packageName,
					numzones == 1 ? "zone" : "zones",
					zonenames ? zonenames : "?");
			} else {
				/* LINTED variable format specifier to ... */
				(void) snprintf(msgbuf, sizeof (msgbuf),
					*a_dck[i].err_msg, "package",
					packageName,
					numzones == 1 ? "zone" : "zones",
					zonenames ? zonenames : "??");
			}

			if (a_dck[i].depcklFunc != NULL) {
				/* call check function */
				err = (a_dck[i].depcklFunc)(msgbuf,
					packageName);
				echoDebug(DBG_DEPCHK_REPORT_ERROR,
					a_dck[i].ignore_values, err,
					packageName, msgbuf);
				if (err != 0) {
					if (zonenames != (char *)NULL) {
						free(zonenames);
						zonenames = (char *)NULL;
					}
					return (err);
				}
			} else {
				/* no check function - just report message */
				echoDebug(DBG_DEPCHK_IGNORE_ERROR,
					a_dck[i].ignore_values, packageName,
					msgbuf);
				ptext(stderr, "\\n%s", msgbuf);
			}
		}
	}

	if (zonenames != (char *)NULL) {
		free(zonenames);
		zonenames = (char *)NULL;
	}

	return (0);
}

void
depchkRecordError(depckError_t *a_erc, char *a_pkginst,
	char *a_zoneName, char *a_value)
{
	depckErrorRecord_t *erc;
	int		i;

	/*
	 * create new error record and entry if first entry
	 * record will look like this:
	 * err->er_#entry=1
	 * err->entry[0]->record->ier_numZones=1
	 * err->entry[0]->record->ier_packageName=a_pkginst
	 * err->entry[0]->record->ier_zones[0]=a_zoneName
	 * err->entry[0]->record->ier_values[0]=a_value
	 */

	if (a_erc->er_numEntries == 0) {
		depckErrorRecord_t	*eir;

		eir = (depckErrorRecord_t *)calloc(1,
					sizeof (depckErrorRecord_t));
		eir->ier_packageName = strdup(a_pkginst);
		eir->ier_numZones = 1;
		eir->ier_zones = (char **)calloc(1, sizeof (char **));
		(eir->ier_zones)[eir->ier_numZones-1] = strdup(a_zoneName);
		eir->ier_values = (char **)calloc(1, sizeof (char *));
		(eir->ier_values)[eir->ier_numZones-1] = strdup(a_value);

		a_erc->er_numEntries = 1;
		a_erc->er_theEntries = eir;

		echoDebug(DBG_DEPCHK_RECORD_ERROR, (long)a_erc, a_pkginst,
					a_zoneName, a_value);

		return;
	}

	/* see if this package already has an entry if so add zone to list */

	for (i = 0; i < a_erc->er_numEntries; i++) {
		erc = &a_erc->er_theEntries[i];

		if (strcmp(erc->ier_packageName, a_pkginst) != 0) {
			continue;
		}

		echoDebug(DBG_DEPCHK_RECORD_ZERROR, (long)a_erc, a_zoneName,
			a_value, erc->ier_packageName, erc->ier_numZones,
			erc->ier_zones[0]);

		/*
		 * this package already has an entry - add zone to
		 * existing package entry the modified records will
		 * look like this:
		 * err->er_#entry++;
		 * err->entry[0]->...
		 * err->entry[i]->
		 * -------------->record->
		 * ---------------------->ier_numZones++;
		 * ---------------------->ier_packageName=a_pkginst
		 * ---------------------->ier_zones[0]=...
		 * ---------------------->ier_zones[...]=...
		 * ---------------------->ier_zones[ier_numZones-1]=a_zoneName
		 * ---------------------->ier_values[0]=...
		 * ---------------------->ier_values[...]=...
		 * ---------------------->ier_values[ier_numZones-1]=a_value
		 * err->entry[i+1]->...
		 */
		erc->ier_numZones++;
		erc->ier_zones = (char **)realloc(erc->ier_zones,
					sizeof (char **)*erc->ier_numZones);
		(erc->ier_zones)[erc->ier_numZones-1] = strdup(a_zoneName);
		erc->ier_values = (char **)realloc(erc->ier_values,
					sizeof (char **)*erc->ier_numZones);
		(erc->ier_values)[erc->ier_numZones-1] = strdup(a_value);
		return;
	}

	/*
	 * this packages does not have an entry - add new package
	 * entry for this zone the modified records will look like this:
	 * err->er_#entry++;
	 * err->entry[0]->record->ier_numZones=...
	 * err->entry[0]->record->ier_packageName=...
	 * err->entry[0]->record->ier_zones[0]=...
	 * err->entry[0]->record->ier_values[0]=...
	 * err->entry[er_#entry-1]->record->ier_numZones=1
	 * err->entry[er_#entry-1]->record->ier_packageName=a_pkginst
	 * err->entry[er_#entry-1]->record->ier_zones[0]=a_zoneName
	 * err->entry[er_#entry-1]->record->ier_values[0]=a_value
	 */

	echoDebug(DBG_DEPCHK_RECORD_PERROR, (long)a_erc,
			a_erc->er_numEntries, a_pkginst, a_zoneName, a_value);

	a_erc->er_numEntries++;

	a_erc->er_theEntries = realloc(a_erc->er_theEntries,
			sizeof (depckErrorRecord_t)*a_erc->er_numEntries);

	erc = &a_erc->er_theEntries[a_erc->er_numEntries-1];

	erc->ier_packageName = strdup(a_pkginst);
	erc->ier_numZones = 1;
	erc->ier_zones = (char **)calloc(1, sizeof (char *));
	(erc->ier_zones)[erc->ier_numZones-1] = strdup(a_zoneName);
	erc->ier_values = (char **)calloc(1, sizeof (char *));
	(erc->ier_values)[erc->ier_numZones-1] = strdup(a_value);
}

/*
 * *****************************************************************************
 * static internal (private) functions
 * *****************************************************************************
 */

static int
collectError(int *r_numZones, char **r_zoneNames, char *a_packageName,
	depckl_t *a_dck, int a_depIndex, depckErrorRecord_t *a_eir,
	int a_errIndex)
{
	char	msgbuf[4096];
	char	*zn = *r_zoneNames;

	if (a_dck[a_depIndex].ignore_values == (char *)NULL) {
		if (a_dck[a_depIndex].err_msg == (char **)NULL) {
			(void) snprintf(msgbuf, sizeof (msgbuf),
			ERR_DEPENDENCY_REPORT, a_eir->ier_values[a_errIndex],
			"package", a_packageName,
			"zone", a_eir->ier_zones[a_errIndex]);
		} else {
			/* LINTED variable format specifier to snprintf(); */
			(void) snprintf(msgbuf, sizeof (msgbuf),
			*a_dck[a_depIndex].err_msg,
			a_eir->ier_values[a_errIndex],
			"package", a_packageName,
			"zone", a_eir->ier_zones[a_errIndex]);
		}
		if (a_dck[a_depIndex].depcklFunc != NULL) {
			int	err;

			err = (a_dck[a_depIndex].depcklFunc)(msgbuf,
							a_packageName);
			echoDebug(DBG_DEPCHK_COLLECT_ERROR, err, a_packageName,
					msgbuf);
			if (err != 0) {
				return (err);
			}
		} else {
			echoDebug(DBG_DEPCHK_COLLECT_IGNORE, a_packageName,
					msgbuf);
			ptext(stderr, "\\n%s", msgbuf);
		}
		return (0);
	}

	*r_numZones = (*r_numZones)+1;
	if (zn == (char *)NULL) {
		zn = strdup(a_eir->ier_zones[a_errIndex]);
	} else {
		char *p;
		int len = strlen(zn)+strlen(a_eir->ier_zones[a_errIndex])+3;
		p = calloc(1, len);
		(void) snprintf(p, len, "%s, %s", zn,
			a_eir->ier_zones[a_errIndex]);
		free(zn);
		zn = p;

	}
	*r_zoneNames = zn;
	return (0);
}