/*  swverid.c: POSIX-7.2 Object Identification Routines.
 */

/*
   Copyright (C) 1998,2004,2005,2006  James H. Lowe, Jr.
   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 <string.h>
#include <unistd.h>
#include <ctype.h>
#include <limits.h>
#include "strob.h"
#include "swlib.h"
#include "swutilname.h"
#include "misc-fnmatch.h"
#include "swverid.h"

static char * rel_op_array[] = {
		SWVERID_RELOP_NEQ,
		SWVERID_RELOP_LT ,
		SWVERID_RELOP_LTE,
		SWVERID_RELOP_EQ2,
		SWVERID_RELOP_EQ,
		SWVERID_RELOP_GTE,
		SWVERID_RELOP_GT,
		(char *)NULL
};

static
int
is_fully_qualified(SWVERID * spec)
{
	return 0;
}

static
char
get_ver_id_char_from_this(struct VER_ID * this)
{
	char * verid = this->ver_idM;
	if (strlen(verid) > 1) {
		/*
		 * Step over the object modifier letter.
		 * e.g.   'f' == fileset
		 *        'p' == product
		 *        'b' == bundle
		 *  where for example "pr" refers to the product revision
		 */
		verid++;
	}
	return *verid;
}

static
int
compare_relop(int rel, int req)
{
	if (req == SWVERID_CMP_NOT_USED) {
		return rel;
	}

	if (req == SWVERID_CMP_NEQ) {

		if (rel != SWVERID_CMP_EQ) {
			return 0;
		} else {
			return 1;
		}

	} else if (req == SWVERID_CMP_EQ || req == SWVERID_CMP_EQ2) {
		if (rel != SWVERID_CMP_EQ && rel != SWVERID_CMP_EQ2) {
			return 1;
		} else {
			return 0;
		}
	} else if (req == SWVERID_CMP_LTE) {
		if (	
			rel == SWVERID_CMP_EQ ||
			rel == SWVERID_CMP_LTE ||
			rel == SWVERID_CMP_LT ||
			0
			) {
			return 0;
		} else {
			return 1;
		}
	} else if (req == SWVERID_CMP_LT) {
		if (	
			rel == SWVERID_CMP_LTE ||
			0
			) 
		{
			/*
			 * This is an error
			 */
			/* FIXME: do something? */
			return -1;
		}

		if (	
			rel == SWVERID_CMP_LT ||
			0
			) 
		{
			return 0;
		} else {
			return 1;
		}
	} else if (req == SWVERID_CMP_GT) {
		if (	
			rel == SWVERID_CMP_GTE ||
			0
			) 
		{
			/*
			 * This is an error
			 */
			/* FIXME: do something? */
			return -1;
		}

		if (	
			rel == SWVERID_CMP_GT ||
			0
			) 
		{
			return 0;
		} else {
			return 1;
		}
	} else if (req == SWVERID_CMP_GTE) {
		if (	
			rel == SWVERID_CMP_EQ ||
			rel == SWVERID_CMP_GTE ||
			rel == SWVERID_CMP_GT ||
			0
			) 
		{
			return 0;
		} else {
			return 1;
		}
	} else {
		return -1;
	}
}

static
int
swverid_i_rpmvercmp(CPLOB * target_list, CPLOB * candidate_list)
{
	int ret;
	char * target;
	char * candidate;
	
	target = cplob_val(target_list, 0);
	candidate = cplob_val(candidate_list, 0);
	/*
	 * FIXME, match multiple version ids.
	 */
	if (!target || !candidate) {
		return 0; /* match */
	}
	ret = swlib_rpmvercmp(target, candidate);
	return ret;
}

static
int
swverid_i_fnmatch(CPLOB * candidate_list, CPLOB * target_list, int flags)
{
	int ret;
	char * target;
	char * candidate;

	target = cplob_val(target_list, 0);
	candidate = cplob_val(candidate_list, 0);
	/*
	 * FIXME, match multiple version ids.
	 */
	if (!target || !candidate) {
		return 0; /* match */
	}
	ret = fnmatch (/*pattern*/ candidate, /*string*/ target, flags);
	return ret;
} 

static
int
compare_version_id(struct VER_ID * id_target, struct VER_ID * id_candidate, int req)
{
	int ret = -1;
	int retval = -1;
	int flags;
	char verid_letter;

	if (!id_target || !id_candidate) {
		if(id_candidate)
			id_candidate->rel_op_codeM = SWVERID_CMP_NOT_USED;
		return 0;
	}

	verid_letter = get_ver_id_char_from_this(id_candidate);
	if (verid_letter == SWVERID_VERID_REVISION) {
		/*
		 * Its a revision, compare as dotted string.
		 */

		ret = swverid_i_rpmvercmp(id_target->value_listM, id_candidate->value_listM);
		/* return 1: a is newer than b */
		/*        0: a and b are the same version */
		/*       -1: b is newer than a */

		if (ret == 1) {
			id_candidate->rel_op_codeM = SWVERID_CMP_LT;	
		} else if (ret == -1) {
			id_candidate->rel_op_codeM = SWVERID_CMP_GT;	
			retval = 1;
		} else if (ret == 0) {
			id_candidate->rel_op_codeM = SWVERID_CMP_EQ;	
			retval = 0;
		} else {
			return -1;
		}
	} else {
		/*
		 * Its not a revision, compare as a shell pattern
		 */
		flags =  0;
		
		ret = swverid_i_fnmatch (/*pattern*/ id_candidate->value_listM, /*string*/ id_target->value_listM, flags);
		if (ret == 0) {
			id_candidate->rel_op_codeM = SWVERID_CMP_EQ;	
			retval = 0;
		} else {
			id_candidate->rel_op_codeM = SWVERID_CMP_NEQ;	
			retval = 1;
		}
	}
	retval = compare_relop(id_candidate->rel_op_codeM, req);
	return retval;
}

static
int
compare_taglist(SWVERID * target, SWVERID * candidate)
{
	/*
	 * 'target' is a fully qualified (does not contain any
	 * patterns) software spec.
	 * 'candidtate' may contain patterns
	 */

	return -1;
}

static
int
compare_all_version_ids(SWVERID * target, SWVERID * candidate)
{
	int ret;
	struct VER_ID * id_target; 
	struct VER_ID * id_candidate; 

	/*
	 * target is a fully qualified software spec
	 * candidtate is a possible match to the target
	 */

	if (is_fully_qualified(target) == 0) {
		/*
		 * User error
		 */
		return -1;
	}

	/*
	 * Compare the revision
	 */
	id_target = swverid_get_verid(target, SWVERID_VERIDS_REVISION);
	id_candidate = swverid_get_verid(candidate, SWVERID_VERIDS_REVISION);
	compare_version_id(id_target, id_candidate, SWVERID_CMP_NOT_USED);

	/*
	 * Compare the architecture
	 */
	id_target = swverid_get_verid(target, SWVERID_VERIDS_ARCHITECTURE);
	id_candidate = swverid_get_verid(candidate, SWVERID_VERIDS_ARCHITECTURE);
	compare_version_id(id_target, id_candidate, SWVERID_CMP_NOT_USED);

	/*
	 * Compare the vendor_tag
	 */
	id_target = swverid_get_verid(target, SWVERID_VERIDS_VENDOR_TAG);
	id_candidate = swverid_get_verid(candidate, SWVERID_VERIDS_VENDOR_TAG);
	compare_version_id(id_target, id_candidate, SWVERID_CMP_NOT_USED);

	/*
	 * Compare the location
	 */
	id_target = swverid_get_verid(target, SWVERID_VERIDS_LOCATION);
	id_candidate = swverid_get_verid(candidate, SWVERID_VERIDS_LOCATION);
	compare_version_id(id_target, id_candidate, SWVERID_CMP_NOT_USED);

	/*
	 * Compare the qualifier
	 */
	id_target = swverid_get_verid(target, SWVERID_VERIDS_QUALIFIER);
	id_candidate = swverid_get_verid(candidate, SWVERID_VERIDS_QUALIFIER);
	compare_version_id(id_target, id_candidate, SWVERID_CMP_NOT_USED);


	return 0;
}

static
int
determine_rel_op_code(char * rel_op)
{
	char * s;
	int index;

	index = 0;
	s = rel_op_array[index];
	while(s) {	
		if (strcmp(s, rel_op) == 0) return index;
		index++;
		s = rel_op_array[index];
	}
	return -1;
}

static 
struct VER_ID *
open_version_id(char * verid)
{
	struct VER_ID *ver_id=(struct VER_ID*)malloc(sizeof(struct VER_ID));
	STROB * tmp;
	char *s = verid;
	char *olds;
	int count;
	int ret;

	tmp = strob_open(10);
	olds = s;
	while (isalpha(*s)) s++;  /* Step over the 'r', 'a', etc */

	if ((olds - s) == 0 || (s - olds) > 2) {
		/*
		 * Error
		 */
		swbis_free(ver_id);
		strob_close(tmp);
		return NULL;
	}

	/*
	 * Store the version id
	 */
	strncpy(ver_id->ver_idM, olds, (int)(s - olds));
	ver_id->ver_idM[(int)(s - olds)] = '\0';

	/*
	 * Now read the rel_op
	 */
	count=0;
	olds = s;
	while (	count < 3 && (*s == '=' ||
		*s == '|' ||
		*s == '<' ||
		*s == '>')
	) {
		s++;
		count++;
	}

	/*
	 * sanity check
	 */
	if (count > 2) {
		strob_close(tmp);
		return NULL;
	}

	/*
	 * The Rel_op can only be 1 or 2 bytes long
	 */
	if ((olds - s) == 0 || (s - olds) > 2) {
		swbis_free(ver_id);
		strob_close(tmp);
		return NULL;
	}

	/*
	 * Store the rel_op
	 */
	strncpy(ver_id->rel_opM, olds, (int)(s - olds));
	ver_id->rel_opM[(int)(s - olds)] = '\0';


	/*
	 * Now check if it is a valid rel_op string
	 */
	
	ret = determine_rel_op_code(ver_id->rel_opM);
	if (ret < 0) {
		strob_close(tmp);
		return NULL;
	}
	ver_id->rel_op_codeM = ret;	

	/*
	 * Enforce the POSIX requirement that only the revision can use rel_ops
	 */
	if (*(ver_id->ver_idM) == SWVERID_VERID_REVISION) {
		/*
		 * all rel_ops supported except '='
		fprintf(stderr, "JL [%s] %d\n", ver_id->rel_opM, ver_id->rel_op_codeM);
		 */
		if (ver_id->rel_op_codeM == SWVERID_CMP_EQ2) {
			fprintf(stderr, "%s: warning: use '==' for the revision specs\n",
					swlib_utilname_get());
			/* return NULL; */
		}
	} else {
		/*
		 * the rel_op must be '='
		 */
		if (ver_id->rel_op_codeM != SWVERID_CMP_EQ2)
			return NULL;
	}

	/*
	 * Now store the values, which can be '|' separated.
	 */
	ver_id->value_listM = cplob_open(2);
	olds = s;

	s = strob_strtok(tmp, olds, "|");
	while(s) {
		s = strdup(s);
		SWLIB_ASSERT(s != NULL);
		cplob_add_nta(ver_id->value_listM, s);
		s = strob_strtok(tmp, NULL, "|\n\r");
	}
	return ver_id;
}

static 
int
parse_version_id_string(SWVERID * swverid, char * verid_string)
{
	struct VER_ID *verid;
	STROB * buf = strob_open(10);
	char  *s;

	s = strob_strtok(buf, verid_string, ",");
	while (s) {
		verid = open_version_id(s);	
		if (verid == (struct VER_ID *)(NULL)) return -1;
		swverid_add_verid(swverid, verid);
		s = strob_strtok(buf, NULL, ",");
	}
	strob_close(buf);
	return 0;
}

static
int
parse_swspec_string (SWVERID * swverid)
{
	char * source = swverid->source_copyM;
	char * tag;
	char * tags;
	char * verids;
	char * verid;
	int ret;
	STROB * tmp = strob_open(20);
	STROB * tag_tmp2 = strob_open(20);
	STROB * ver_tmp2 = strob_open(20);

	if (!source || strlen(source)  == 0) return -2;
	cplob_shallow_reset(swverid->taglistM);

	/*
	 * parse a software spec
	 *    bash.bash-doc,r==3.3
	 */


	/*
	 * parse the list of tags
	 * which are dot separated tags
	 */
	tags = strob_strtok(tmp, source, ",");
	tag = strob_strtok(tag_tmp2, tags, ".");
	while (tag) {
		cplob_add_nta(swverid->taglistM, strdup(tag));
		tag = strob_strtok(tag_tmp2, NULL, ".");
	}

	/*
	 * parse the version ids which are colon
	 * separated strings.
	 */

	/* FIXME??  what about escaped ,'s */
	verids = strob_strtok(tmp, NULL, ",");
	if (verids) {
		verid = verids;
		while (verid) {
			ret = parse_version_id_string(swverid, verid);
			if (ret) {
				fprintf(stderr, "%s: error parsing version id [%s]\n",
					swlib_utilname_get(),  verid);
				return -1;
			}
			verid = strob_strtok(tmp, NULL, ",");
		}
	}
	strob_close(tmp);
	strob_close(tag_tmp2);
	strob_close(ver_tmp2);
	return 0;
}

static 
int
classify_namespace (char * object_kw)
{
	if (!object_kw)  return SWVERID_NS_NA;
	if (!strncmp("fileset", object_kw, 7)) {
		return SWVERID_NS_MID;
	} else if (!strncmp("controlfile", object_kw, 11)) {
		return SWVERID_NS_MID;
	} else if (!strncmp("subproduct", object_kw, 10)) {
		return SWVERID_NS_MID;
	} else if (!strncmp("product", object_kw, 7)) {
		return SWVERID_NS_TOP;
	} else if (!strncmp("bundle", object_kw, 6)) {
		return SWVERID_NS_TOP;
	} else if (!strncmp("file", object_kw, 4)) {
		return SWVERID_NS_LOW;
	} else {
		return SWVERID_NS_NA;
	}
}

/* ------------------- Public Functions ---------------------------------*/

SWVERID *
swverid_open(char * object_keyword, char *swversion_string)
{
	int ret;
	SWVERID *swverid=(SWVERID*)malloc(sizeof(SWVERID));
	if (!swverid) return NULL;
	
	swverid->object_nameM=swlib_strdup("");
	swverid->taglistM = cplob_open(3);
	cplob_add_nta(swverid->taglistM, (char*)(NULL));
	swverid->catalogM=swlib_strdup("");
	swverid->comparison_codeM=SWVERID_CMP_EQ;
	swverid_set_namespace(swverid, object_keyword);
	swverid_set_object_name(swverid, object_keyword);
	swverid->ver_id_listM=(struct VER_ID*)(NULL);
	swverid->source_copyM = (char*)(NULL);
	swverid->use_path_compareM = 0;
	swverid->swutsM = swuts_create();

	if (swversion_string != (char*)(NULL)) {
		swverid->source_copyM = swlib_strdup(swversion_string);
		ret = parse_swspec_string(swverid);
		if (ret < 0) {
			return NULL;
		}
	}
	return swverid;
}

void
swverid_set_namespace(SWVERID * swverid, char * object_keyword)
{
	swverid->namespaceM = classify_namespace(object_keyword);
	swverid_set_object_name(swverid, object_keyword);
}

void
swverid_close(SWVERID * swverid)
{
	struct VER_ID *vid, *ver_id=swverid->ver_id_listM;
	while(ver_id){
		vid=ver_id->nextM;
		swbis_free(ver_id);
		ver_id=vid;			
	}
	if (swverid->object_nameM) {
		swbis_free(swverid->object_nameM);
		swverid->object_nameM = NULL;
	}
	if (swverid->source_copyM) {
		swbis_free(swverid->source_copyM);
		swverid->source_copyM = NULL;
	}
	/* FIXME this causes a core dump: cplob_close(swverid->taglistM); */
	swuts_delete(swverid->swutsM);
	swbis_free(swverid);
}

o__inline__
void
swverid_set_object_name(SWVERID * swverid, char *name)
{
	if (swverid->object_nameM) swbis_free(swverid->object_nameM);
	if (name)
		swverid->object_nameM=swlib_strdup(name);
	else
		swverid->object_nameM=swlib_strdup("");
}

o__inline__
char *
swverid_get_object_name(SWVERID * swverid)
{
	return swverid->object_nameM;
}

int
swverid_verid_compare(SWVERID * swverid1_target, SWVERID * swverid2_candidate)
{
	int ret;
	ret = compare_all_version_ids(swverid1_target, swverid2_candidate);
	return ret;
}


/*
 * Legacy (possibly depreicated) function.
 */
int
swverid_vtagOLD_compare(SWVERID * swverid1_target, SWVERID * swverid2_candidate)
{
	char * tag1;	

	if (
		strcmp(swverid1_target->object_nameM,
			swverid2_candidate->object_nameM)
	) {
		return SWVERID_CMP_NEQ;
	}

	if (
		swverid1_target->namespaceM != SWVERID_NS_NA &&
		swverid1_target->namespaceM != swverid2_candidate->namespaceM
	) {
		return SWVERID_CMP_NEQ;
	}

	tag1 = cplob_val(swverid1_target->taglistM, 0);
	if (!tag1 || strlen(tag1) == 0) {
		 return SWVERID_CMP_EQ;
	}
	if (strcmp(tag1, "*") == 0) {
		 return SWVERID_CMP_EQ;
	}

	if (swverid1_target->use_path_compareM || swverid2_candidate->use_path_compareM) {
		if (!swlib_compare_8859(cplob_val(swverid1_target->taglistM,0),
				cplob_val(swverid2_candidate->taglistM,0))
		) {
			return SWVERID_CMP_EQ;
		} else {
			return SWVERID_CMP_NEQ;
		}
	} else { 	
		if (!strcmp(cplob_val(swverid1_target->taglistM,0), cplob_val(swverid2_candidate->taglistM,0))) {
			return SWVERID_CMP_EQ;
		} else {
			return SWVERID_CMP_NEQ;
		}
	}
}

int
swverid_compare(SWVERID * swverid1_target, SWVERID * swverid2_candidate)
{
	int ret;
	char * tag1;	

	/*
	 * Make sure we are not comparing different objects
	 */
	if (
		strcmp(swverid1_target->object_nameM,
			swverid2_candidate->object_nameM)
	) {
		/*
		 * Comparison failed
		 */
		return SWVERID_CMP_NEQ;
	}

	/*
	 * Make sure we are not comparing objects from different namespaces
	 */
	if (
		swverid1_target->namespaceM != SWVERID_NS_NA &&
		swverid1_target->namespaceM != swverid2_candidate->namespaceM
	) {
		/*
		 * Comparison failed
		 */
		return SWVERID_CMP_NEQ;
	}

	tag1 = cplob_val(swverid1_target->taglistM, 0);
	if (!tag1 || strlen(tag1) == 0) {
		/*
		 * FIXME ??
		 *  Null target returns Equal
		 */
		 return SWVERID_CMP_EQ;
	}

	if (swverid1_target->use_path_compareM && swverid2_candidate->use_path_compareM) {
		/*
		 * Here, just compare the path which is stored in taglist
		 * FIXME : this functionality probably does not belong in this
		 * object 
		 */
		if (!swlib_compare_8859(cplob_val(swverid1_target->taglistM,0),
				cplob_val(swverid2_candidate->taglistM,0))
		) {
			return SWVERID_CMP_EQ;
		} else {
			return SWVERID_CMP_NEQ;
		}
	} else if (swverid1_target->use_path_compareM == 0 && swverid2_candidate->use_path_compareM == 0) {
		/*
		if (!strcmp(cplob_val(swverid1_target->taglistM,0), cplob_val(swverid2_candidate->taglistM,0))) {
		*/

		/*
		 * Compare the tags
		 */
		compare_taglist(swverid1_target, swverid2_candidate);

		/*
		 * Compare the version ids
		 */
		ret = compare_all_version_ids(swverid1_target, swverid2_candidate);
		return ret;
	} else {
		/*
		 * Invalid usage
		 */
		return -1;
	}
}

int
swverid_add_attribute(SWVERID * swverid, char * object_keyword, char * keyword, char * value) {
	char ver_id[3];
	struct VER_ID *version_id;
	STROB * verid_string;
	int c;

	if (strcmp(object_keyword, "file") == 0 || strcmp(object_keyword, "control_file") == 0) {
		swverid->use_path_compareM = 1;
	}
	
	c = swverid_get_ver_id_char(object_keyword, keyword);
	if (c < 0) {
		/*
		 * Ok, attribute is not a POSIX versioning attribute.
		 * It might be a UTS attribute or it might not be.
		 */
		swuts_add_attribute(swverid->swutsM, keyword, value);
		return 0;
	}
	ver_id[0] = ver_id[1] = ver_id[2] = '\0';
	if (c == 0) {
		/*
		 * the object is a 'file' object, hence in this
		 * usage the pathnames are compared.
		 * FIXME: maybe a 'swverid' object should not be used for this
		 * purpose.
		 */
		if (swverid->source_copyM) {
			swbis_free(swverid->source_copyM);
			swverid->source_copyM = NULL;
		}
		swverid->source_copyM = swlib_strdup(value);
		cplob_additem(swverid->taglistM, 0, swverid->source_copyM);
		return 1;
	}

	if (0 && object_keyword) {
		/* FIXME:  Disabled as of 2005-12-03 pending further development.
		 * store the form of a version Id that specifies the
		 * object to which it belongs
		 */
		ver_id[0] = *object_keyword;
		ver_id[1] = (char)(c);
	} else {
		ver_id[0] = (char)(c);
	}

	verid_string = strob_open(24);
	strob_strcpy(verid_string, ver_id);
	if (ver_id[0] == 'r' || ver_id[1] == 'r') {
		strob_strcat(verid_string,"==");
	} else {
		strob_strcat(verid_string,"=");
	}
	strob_strcat(verid_string, value);

	/*
	 * Now parse the constructed version id
	 */
	version_id = open_version_id(strob_str(verid_string));
	if (version_id == NULL) {
		return -1;
	}

	/*
	 * Add the version id
	 */
	swverid_add_verid(swverid, version_id);

	strob_close(verid_string);
	return 1;
}

int
swverid_get_comparison_sense(SWVERID * swverid1, SWVERID * swverid2)
{
	return 0;
}

Swverid_Cmp_Code
swverid_get_comparison_code(SWVERID * swverid)
{
	return swverid->comparison_codeM;
}

void
swverid_set_comparison_code(SWVERID * swverid, Swverid_Cmp_Code code)
{
	swverid->comparison_codeM=code;
}

char *
swverid_get_tag(SWVERID * swverid, int n)
{
	return cplob_val(swverid->taglistM, n);
}

void
swverid_set_tag(SWVERID * swverid, char * key, char *value)
{
	if (!strcmp(key, "catalog")) {
		if (swverid->catalogM) { 
			swbis_free(swverid->catalogM);
			swverid->catalogM = NULL;
		}
		swverid->catalogM=swlib_strdup(value);
	} else {
		if (cplob_val(swverid->taglistM,0)) { 
			swbis_free(cplob_val(swverid->taglistM,0));
		}
		cplob_additem(swverid->taglistM, 0, swlib_strdup(value));
	}
}

int 
swverid_get_ver_id_char(char * object, char * attr_name)
{
	if (!strcmp(attr_name, SW_A_revision)) {
		return SWVERID_VERID_REVISION;
	}
	else if (!strcmp(attr_name, SW_A_architecture)) {
		return SWVERID_VERID_ARCHITECTURE;
	}
	else if (!strcmp(attr_name, SW_A_vendor_tag)) {
		return SWVERID_VERID_VENDOR_TAG;
	}
	else if (!strcmp(attr_name, SW_A_location)) {
		return SWVERID_VERID_LOCATION;
	}
	else if (!strcmp(attr_name, SW_A_qualifier)) {
		return SWVERID_VERID_QUALIFIER;
	}
	else if (!strcmp(attr_name, SW_A_tag)) {
		return 0;
	}
	else if (!strcmp(attr_name, SW_A_path)) {
		if (!strcmp(object, "file") || !strcmp(object, SW_A_distribution))
			return 0;
		else 
			return -1;
	}
	else {
		return -1;
	}
}

struct VER_ID *
swverid_get_verid(SWVERID * swverid, char * fp_verid) {
	char * verid;
	struct  VER_ID  *prev;
	struct  VER_ID  *last;

	verid = fp_verid;

	/*
	 * Step over the object modifier letter
	 * e.g. 'br' 'pr' etc which is an suggested
	 * extension 
	 */
	if (strlen(verid) > 1)
		verid++;

	last = swverid->ver_id_listM;
	while (last) {
		if (
			*(last->ver_idM) == *verid ||
			*(last->ver_idM + 1) == *verid ||
			1
		) {
			/*
			 * Found it
			 */
			return last;
		}
		last = last->nextM;
	}
	return NULL;
}

void
swverid_add_verid(SWVERID * swverid, struct VER_ID  * verid) {
	struct  VER_ID  *prev;
	struct  VER_ID  *last = swverid->ver_id_listM;
	verid->nextM = NULL;

	if (!last) {
		swverid->ver_id_listM = verid;
		return;
	}	

	while (last) {
		prev = last;
		last = last->nextM;
	}
	prev->nextM = verid;
}

char *
swverid_print(SWVERID * swverid, STROB * buf)
{
	char * s;
	int i = 0;
	int vi = 0;
	char * ret;
	char * val;
	STROB * version = strob_open(32);
	struct  VER_ID  * next;

	strob_strcpy(version, "");
	while ((s = cplob_val(swverid->taglistM, i++))) {
		if (i > 1) strob_strcat(version, ".");
		strob_strcat(version, s);
	}

	next = swverid->ver_id_listM;
	while (next) {
		strob_strcat(version, ",");
		strob_strcat(version, next->ver_idM);
		strob_strcat(version, next->rel_opM);
		while((val=cplob_val(next->value_listM, vi++))) {
			if (vi>1)
				strob_strcat(version, "|");
			strob_strcat(version, val);
		}
		next = next->nextM; 
	}

	strob_strcpy(buf, strob_str(version));
	ret = strob_str(buf);
	strob_close(version);
	return ret;
}

char *
swverid_show_object_debug(SWVERID * swverid, STROB * buf)
{
	strob_strcpy(buf, "");
	if (! swverid_print(swverid, buf))
		return "";
	strob_strcat(buf, "\n");
	strob_sprintf(buf, 1,
			"os_name=%s\n"
			"os_version=%s\n"
			"os_release=%s\n"
			"machine_type=%s\n",
			swverid->swutsM->sysnameM,
			swverid->swutsM->versionM,
			swverid->swutsM->releaseM,
			swverid->swutsM->machineM);
	return strob_str(buf);
}

CPLOB *
swverid_u_parse_swspec(SWVERID * swverid, char * swspec)
{
	CPLOB * list;
	CPLOB * savecplob;
	char * savesource;

	savecplob = swverid->taglistM;
	list = cplob_open(3);
	swverid->taglistM = list;
	savesource = swverid->source_copyM;
	swverid->source_copyM = strdup(swspec);
		
	parse_swspec_string(swverid);

	free(swverid->source_copyM);		
	swverid->taglistM = savecplob;
	swverid->source_copyM = savesource;
	return list;
}
