/*      -*- linux-c -*-
 *
 * (C) Copyright IBM Corp. 2003, 2006
 *
 * 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. This
 * file and program are licensed under a BSD style license. See
 * the Copying file included with the OpenHPI distribution for
 * full licensing terms.
 *
 * Author(s):
 *      Steve Sherman <stevees@us.ibm.com>
 *      Renier Morales <renier@openhpi.org>
 *      Thomas Kanngieser <thomas.kanngieser@fci.com>
 */

#include <glib.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <oh_utils.h>
#include <oh_error.h>

/* Defines to create canonical entity path strings */
#define ELEMENTS_IN_SaHpiEntityT 2
#define EPATHSTRING_START_DELIMITER "{"
#define EPATHSTRING_START_DELIMITER_CHAR '{'
#define EPATHSTRING_END_DELIMITER "}"
#define EPATHSTRING_END_DELIMITER_CHAR '}'
#define EPATHSTRING_VALUE_DELIMITER ","
#define EPATHSTRING_VALUE_DELIMITER_CHAR ','
#define EPATHPATTERN_SPLAT '*'
#define EPATHPATTERN_DOT '.'

/**
 * oh_encode_entitypath:
 * @epstr: Pointer to canonical entity path string.
 * @ep: Location to store HPI's entity path structure.
 *
 * Converts an entity path canonical string (generally generated
 * by oh_decode_entitypath()) into an SaHpiEntityPathT structure.
 * 
 * Returns: 
 * SA_OK - normal operation.
 * SA_ERR_HPI_INVALID_PARAMS - Input pointer(s) NULL.
 * SA_ERR_HPI_INVALID_DATA - Invalid canonical entity path string.
 * SA_ERR_HPI_OUT_OF_SPACE - No memory for internal storage.
 **/
SaErrorT oh_encode_entitypath(const gchar *epstr, SaHpiEntityPathT *ep)
{
 	gchar **epathdefs = NULL, **epathvalues = NULL;
	gchar *gstr = NULL, *endptr = NULL;
        GSList  *epath_list = NULL, *lst = NULL;
        int   i, location, num_entities = 0;
        SaErrorT  err = SA_OK;
	SaHpiEntityT  *entityptr = NULL;
	SaHpiTextBufferT tmpbuffer;
	SaHpiEntityTypeT eptype;

	if (!epstr || !ep) {
		return(SA_ERR_HPI_INVALID_PARAMS);
	}

	/* Check for runaway string */
	if (strlen(epstr) >  OH_MAX_TEXT_BUFFER_LENGTH) {
		return(SA_ERR_HPI_INVALID_DATA);
	}

        /* Split out {xxx,yyy} definition pairs */
       	gstr = g_strstrip(g_strdup(epstr));
	if (gstr == NULL) {
		CRIT("Stripped entity path string is NULL"); 
		err = SA_ERR_HPI_INVALID_DATA;
		goto CLEANUP;
	}
	if (gstr[0] == '\0') {
	        oh_init_ep(ep);
		err = SA_OK;
		goto CLEANUP;
	}

	epathdefs = g_strsplit(gstr, EPATHSTRING_END_DELIMITER, -1);
	if (epathdefs == NULL) {
		CRIT("Cannot split entity path string.");
		err = SA_ERR_HPI_INTERNAL_ERROR;
		goto CLEANUP;
        }

	/* Split out HPI entity type and location strings */
	for (i=0; epathdefs[i] != NULL && epathdefs[i][0] != '\0'; i++) {

		epathdefs[i] = g_strstrip(epathdefs[i]);
		/* Check format - for starting delimiter and a comma */
		if ((epathdefs[i][0] != EPATHSTRING_START_DELIMITER_CHAR) || 
		    (strpbrk(epathdefs[i], EPATHSTRING_VALUE_DELIMITER) == NULL)) {
			CRIT("Invalid entity path format.");
			err = SA_ERR_HPI_INVALID_DATA;
			goto CLEANUP;
		}

		epathvalues = g_strsplit(epathdefs[i],
                                         EPATHSTRING_VALUE_DELIMITER,
                                         ELEMENTS_IN_SaHpiEntityT);
		epathvalues[0] = g_strdelimit(epathvalues[0], EPATHSTRING_START_DELIMITER, ' ');

		/* Find entity type */
		oh_init_textbuffer(&tmpbuffer);
		oh_append_textbuffer(&tmpbuffer, g_strstrip(epathvalues[0]));
		err = oh_encode_entitytype(&tmpbuffer, &eptype);

                /* If not an HPI type - support a numeric type. Needed by IPMI Direct plugin */
		if (err) {
			err = SA_OK;
                        int num = strtol(g_strstrip(epathvalues[0]), &endptr, 0);
                        if (num <= 0 || endptr[0] != '\0') {
                                CRIT("Invalid entity type string");
                                err = SA_ERR_HPI_INVALID_DATA;
                                goto CLEANUP;
                        }
			eptype = num;
		}

		/* Find entity location */
		location = strtol(g_strstrip(epathvalues[1]), &endptr, 10);
		if (endptr[0] != '\0') {
			CRIT("Invalid location character");
			err = SA_ERR_HPI_INVALID_DATA;
			goto CLEANUP;
                }

		/* Save entity path definitions; reverse order */
		if (num_entities < SAHPI_MAX_ENTITY_PATH) {
			entityptr = g_new0(SaHpiEntityT, 1);
			if (entityptr == NULL) {
				err = SA_ERR_HPI_OUT_OF_SPACE;
				goto CLEANUP;
			}

			entityptr->EntityType = eptype;
			entityptr->EntityLocation = location;
			epath_list = g_slist_prepend(epath_list, (gpointer)entityptr);
		}
		num_entities++;
		g_strfreev(epathvalues);
		epathvalues = NULL;
	}
  
	/* Initialize and write HPI entity path structure */
	oh_init_ep(ep);
	for (i = 0; epath_list != NULL; i++) {
                lst = epath_list;
                if (i < SAHPI_MAX_ENTITY_PATH) {
                        ep->Entry[i].EntityType =
                                ((SaHpiEntityT *)(lst->data))->EntityType;
                        ep->Entry[i].EntityLocation =
                                ((SaHpiEntityT *)(lst->data))->EntityLocation;
                }
                epath_list = g_slist_remove_link(epath_list,lst);
                g_free(lst->data);
		g_slist_free(lst);
	}

	if (num_entities > SAHPI_MAX_ENTITY_PATH) {
		CRIT("Too many entity defs");
		err = SA_ERR_HPI_INVALID_DATA;
	}

 CLEANUP:
	g_free(gstr);
	g_strfreev(epathdefs);
	if (epathvalues) g_strfreev(epathvalues);
	lst = epath_list;
        while (lst != NULL) {
               free(lst->data);
               lst = g_slist_next(lst);
        } 
	g_slist_free(epath_list);

	return(err);
}

/**
 * oh_decode_entitypath:
 * @ep: Pointer to HPI's entity path structure.
 * @bigbuf: Location to store canonical entity path string.
 *
 * Converts an entity path structure into its canonical string version. 
 * The canonical string is formed by removing the "SAHPI_ENT_" prefix
 * from the HPI types, and creating tuples for the entity types.
 * Order of significance is inverted to make entity paths look more
 * like Unix directory structure. It is also assumed that {ROOT,0}
 * exists implicitly before all of these entries. For example:
 *
 * {SYSTEM_CHASSIS,2}{PROCESSOR_BOARD,0}
 *
 * SAHPI_ENT_ROOT is used to identify end element of an entity path.
 * Fully populated entity path may not have an SAHPI_ENT_ROOT.
 * Duplicate names in SaHPIEntityTypeT enumeration aren't handled
 * and won't be preserved across conversion calls.
 *
 * Returns: 
 * SA_OK - normal case.
 * SA_ERR_HPI_INVALID_PARAMS - Input pointer(s) NULL.
 * SA_ERR_HPI_INVALID_DATA - Location value too big for OpenHpi.
 * SA_ERR_HPI_OUT_OF_SPACE - No memory for internal storage.
 **/
SaErrorT oh_decode_entitypath(const SaHpiEntityPathT *ep,
			      oh_big_textbuffer *bigbuf)
{
        char  *typestr;
 	gchar  *locstr, *catstr;
        gchar  typestr_buffer[20];
	int    i;
	oh_big_textbuffer tmpbuf;
	SaErrorT  err = SA_OK;

	if (!bigbuf || !ep) {
		return(SA_ERR_HPI_INVALID_PARAMS);
	}

	err = oh_init_bigtext(&tmpbuf);
	if (err) return(err);

	locstr = g_new0(gchar, OH_MAX_LOCATION_DIGITS + 1);
	if (locstr == NULL) {
		err = SA_ERR_HPI_OUT_OF_SPACE;
		goto CLEANUP;
	}
        
        /* Find last element of structure. Disregard ROOT element
         * and count as last in entity path. */
        for (i=0; i<SAHPI_MAX_ENTITY_PATH; i++) {
                if (ep->Entry[i].EntityType == SAHPI_ENT_ROOT) {
                            break;
                }
        }

        /* Parse entity path into a string */
	for (i--; i >= 0; i--) {
                guint num_digits, work_location_num;
                
		/* Validate and convert data */
                work_location_num = ep->Entry[i].EntityLocation;
                for (num_digits=1;
		     (work_location_num = work_location_num/10) > 0;
		     num_digits++);
		
		if (num_digits > OH_MAX_LOCATION_DIGITS) {
                        CRIT("Location value too big");
                        err = SA_ERR_HPI_INVALID_DATA;
			goto CLEANUP;
		}
                memset(locstr, 0, OH_MAX_LOCATION_DIGITS + 1);
                snprintf(locstr, OH_MAX_LOCATION_DIGITS + 1, 
			 "%d", ep->Entry[i].EntityLocation);

                /* Find string for current entity type */
		typestr = oh_lookup_entitytype(ep->Entry[i].EntityType);

		/* Support numeric entity types - need by IPMI Direct plugin */
		if (typestr == NULL) {
			snprintf(typestr_buffer, 20, "%d", ep->Entry[i].EntityType);
			typestr = typestr_buffer;
		}

		catstr = g_strconcat(EPATHSTRING_START_DELIMITER,
				     typestr,
				     EPATHSTRING_VALUE_DELIMITER,
				     locstr,
				     EPATHSTRING_END_DELIMITER,
				     NULL);

		oh_append_bigtext(&tmpbuf, catstr);
		g_free(catstr);
	}

	/* Write string */
	oh_init_bigtext(bigbuf);
	oh_append_bigtext(bigbuf, (char *)tmpbuf.Data);

 CLEANUP:
	g_free(locstr);

	return(err);
}

/**
 * oh_init_ep:
 * @ep: Pointer to SaHpiEntityPathT structure to initialize.
 *
 * Initializes an entity path to all {ROOT,0} elements.
 *
 * Returns:
 * SA_OK - normal operations.
 * SA_ERR_HPI_INVALID_PARAMS - @ep is NULL.
 **/
SaErrorT oh_init_ep(SaHpiEntityPathT *ep)
{
	 int i;

         if (!ep) {
		 return(SA_ERR_HPI_INVALID_PARAMS);
	 }
         
         for (i=0; i<SAHPI_MAX_ENTITY_PATH; i++) {
                 ep->Entry[i].EntityType = SAHPI_ENT_ROOT;
                 ep->Entry[i].EntityLocation = 0;
         }

	 return(SA_OK);
 }

/**
 * oh_ep_len:
 * @ep: Pointer to SaHpiEntityPathT structure to get length.
 *
 * Returns entity path length - number of entries before {ROOT,0} element.
 * Returns 0 in case of NULL ep pointer.
 **/
SaHpiUint8T oh_ep_len( const SaHpiEntityPathT * ep )
{
    if ( !ep ) {
        return 0;
    }

    size_t i;
    for ( i = 0; i < SAHPI_MAX_ENTITY_PATH; ++i ) {
        if ( ep->Entry[i].EntityType == SAHPI_ENT_ROOT ) {
            break;
        }
    }

    return i;
}

/**
 * oh_get_child_ep:
 * @ep: Pointer to entity path.
 * @parent: Pointer to parent entity path.
 * @child: Pointer to location that will be filled with child entity path
 *
 * Checks if the given entity path(@ep) consists of of the parent entity path
 * and some child entity path.
 * If it is so then fills child entity path.
 *
 * Returns:
 * SA_OK - @ep consists of the @parent and some child.
 * SA_ERR_HPI_INVALID_PARAMS - one of @ep, @parent, @child is NULL.
 * SA_ERR_HPI_NOT_PRESENT - @ep doesn't consist of the @parent and some child.
 **/
SaErrorT oh_get_child_ep( const SaHpiEntityPathT * ep,
                          const SaHpiEntityPathT * parent,
                          SaHpiEntityPathT * child )
{
    if ( ( !ep ) || ( !parent ) || ( !child ) ) {
        return SA_ERR_HPI_INVALID_PARAMS;
    }

    oh_init_ep( child );

    size_t l_ep     = oh_ep_len( ep );
    size_t l_parent = oh_ep_len( parent );

    if ( l_ep < l_parent ) {
        return SA_ERR_HPI_NOT_PRESENT;
    } else if ( l_ep == l_parent ) {
        return SA_OK;
    }

    size_t l_child = l_ep - l_parent;

    size_t i;

    for ( i = 0; i < l_parent; ++i ) {
        const SaHpiEntityT * x = &ep->Entry[i + l_child];
        const SaHpiEntityT * y = &parent->Entry[i];
        if ( x->EntityType != y->EntityType ) {
            return SA_ERR_HPI_NOT_PRESENT;
        }
        if ( x->EntityLocation != y->EntityLocation ) {
            return SA_ERR_HPI_NOT_PRESENT;
        }
    }

    for ( i = 0; i < l_child; ++i ) {
        child->Entry[i] = ep->Entry[i];
    }

    return SA_OK;
}

/**
 * oh_concat_ep:
 * @dest: Pointer to entity path. Gets appended with @append.
 * @append: Pointer to entity path to append.
 *
 * Concatenate two entity path structures (SaHpiEntityPathT).
 * @append is appeded to @dest. If @dest doesn't have enough room, @append
 * will be truncated into @dest.
 *
 * Returns:
 * SA_OK - normal operations.
 * SA_ERR_HPI_INVALID_PARAMS - @dest is NULL.
 **/
SaErrorT oh_concat_ep(SaHpiEntityPathT *dest, const SaHpiEntityPathT *append)
{
        int i, j;

        if (!dest) {
		return(SA_ERR_HPI_INVALID_PARAMS);
	}

        if (!append) return(SA_OK);

        for (i=0; i<SAHPI_MAX_ENTITY_PATH; i++) {
                if (dest->Entry[i].EntityType == SAHPI_ENT_ROOT) break;
        }

        for (j=0; i<SAHPI_MAX_ENTITY_PATH; i++) {
                dest->Entry[i].EntityLocation = append->Entry[j].EntityLocation;
                dest->Entry[i].EntityType = append->Entry[j].EntityType;
                if (append->Entry[j].EntityType == SAHPI_ENT_ROOT) break;
                j++;
        }
	
        return(SA_OK);
}

/**
 * oh_valid_ep:
 * @ep: Pointer to an SaHpiEntityPathT structure.
 *
 * Check an entity path to make sure it does not contain
 * any invalid entity types. The entity path is checked up to
 * the first root element or to the end of the array, if there
 * are no root elements in the structure.
 *
 * Returns:
 * SAHPI_TRUE - Valid entity path.
 * SAHPI_FALSE - Invalid entity path.
 **/
SaHpiBoolT oh_valid_ep(const SaHpiEntityPathT *ep)
{
	int i;

        for (i=0; i<SAHPI_MAX_ENTITY_PATH; i++) {
		if (ep->Entry[i].EntityType == SAHPI_ENT_ROOT) break;
/* FIXME:: Add explicit check for types with HPI resolves its conflicts with IPMI types */
/* Right now we're allowing users to specify any numeric type */
#if 0
		char *typestr;

		typestr = oh_lookup_entitytype(ep->Entry[i].EntityType);
		if (typestr == NULL) return(SAHPI_FALSE);
#endif
        }

        return(SAHPI_TRUE);
}

/**
 * oh_set_ep_location:
 * @ep: Pointer to entity path to work on
 * @et: entity type to look for
 * @ei: entity location to set when entity type is found
 *
 * Set an location number in the entity path given at the first
 * position (from least significant to most) the specified entity type is found.
 *
 * Returns:
 * SA_OK - normal operations.
 * SA_ERR_HPI_INVALID_PARAMS - @ep is NULL.
 * SA_ERR_HPI_INVALID_DATA - @ep invalid entity path.
 **/
SaErrorT oh_set_ep_location(SaHpiEntityPathT *ep, SaHpiEntityTypeT et, SaHpiEntityLocationT ei)
{
        int i;

	if (!ep) {
		return(SA_ERR_HPI_INVALID_PARAMS);
	}

	if (!oh_valid_ep(ep)) {
		return(SA_ERR_HPI_INVALID_DATA);
	}

        for (i=0; i<SAHPI_MAX_ENTITY_PATH; i++) {
                if (ep->Entry[i].EntityType == et) {
                        ep->Entry[i].EntityLocation = ei;
                        break;
                } else {
			if (ep->Entry[i].EntityType == SAHPI_ENT_ROOT) break;
                }
        }

        return(SA_OK);
}

/**
 * oh_cmp_ep:
 * @ep1: Pointer to entity path structure.
 * @ep2: Pointer to entity path structure.
 *
 * Compares two entity paths up to their root element.
 * To be equal, they must have the same number of elements and each element
 * (type and location pair) must be equal to the corresponding element
 * in the other entity path.
 *
 * Returns:
 * SAHPI_TRUE - if equal.
 * SAHPI_FALSE - if not equal.
 **/
SaHpiBoolT oh_cmp_ep(const SaHpiEntityPathT *ep1, const SaHpiEntityPathT *ep2)
{
        unsigned int i, j;
        
        if (!ep1 || !ep2) {
                return(SAHPI_FALSE);
        }

        for (i=0; i <SAHPI_MAX_ENTITY_PATH; i++) {
                if (ep1->Entry[i].EntityType == SAHPI_ENT_ROOT) {
                        i++;
                        break;                                
                }
        }

        for (j=0; j<SAHPI_MAX_ENTITY_PATH; j++) {
                if (ep2->Entry[j].EntityType == SAHPI_ENT_ROOT) {
                        j++;
                        break;
                }
        }

        if (i != j) return(SAHPI_FALSE);

        for (i=0; i<j; i++) {
                if (ep1->Entry[i].EntityType != ep2->Entry[i].EntityType ||
                    ep1->Entry[i].EntityLocation != ep2->Entry[i].EntityLocation) {
                        /* DBG("Entity element %d: EP1 {%d,%d} != EP2 {%d,%d}", i, 
                            ep1->Entry[i].EntityType,
                            ep1->Entry[i].EntityLocation,
                            ep2->Entry[i].EntityType,
                            ep2->Entry[i].EntityLocation); */
                        return(SAHPI_FALSE);
                }
        }
        
        return(SAHPI_TRUE);
}

/**
 * oh_fprint_ep:
 * @ep: Pointer to entity path stucture.
 *
 * Prints the string form of an entity path structure.
 * The MACRO oh_print_ep(), uses this function to print to STDOUT.
 *
 * Returns:
 * SA_OK - normal operations.
 * SA_ERR_HPI_INVALID_PARAMS - @ep is NULL.
 **/
SaErrorT oh_fprint_ep(FILE *stream, const SaHpiEntityPathT *ep, int offsets)
{
	oh_big_textbuffer bigbuf1, bigbuf2;
        SaErrorT err;
	
        if (!ep) {
		return(SA_ERR_HPI_INVALID_PARAMS);
        }

	err = oh_init_bigtext(&bigbuf1);
	if (err) return(err);
	err = oh_init_bigtext(&bigbuf2);
	if (err) return(err);

	err = oh_append_offset(&bigbuf1, offsets);
	if (err) return(err);
	err = oh_append_bigtext(&bigbuf1, "Entity Path: ");
	if (err) return(err);
	
	err = oh_decode_entitypath(ep, &bigbuf2);
	if (err) return(err);

	err = oh_append_bigtext(&bigbuf1, (char *)bigbuf2.Data);
	if (err) return(err);
	err = oh_append_bigtext(&bigbuf1, "\n");
	if (err) return(err);

	fprintf(stream, "%s", bigbuf1.Data);

        return(SA_OK);
}

/**********************************************************************
 * oh_derive_string:
 * @ep - Pointer to entity's HPI SaHpiEntityPathT.
 * @offset - Offset to add to Entity Path location.
 * @base - Base for numeric conversion and display.
 * @str - Un-normalized character string.
 *
 * This function "normalizes" a string (such as an SNMP OID) 
 * based on entity path. Starting from the end of @str, this routine 
 * replaces the letter 'x', with the last location number of entity path,
 * the process is repeated until all 'x' are replaced by an location number.
 * For example,
 * 
 * @str = ".1.3.6.1.4.1.2.3.x.2.22.1.5.1.1.5.x"
 * @ep = {SAHPI_ENT_CHASSIS, 51}{SAHPI_ENT_SBC_BLADE, 3}
 *
 * Returns a normalized string of ".1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.5.3".
 *
 * An @offset is supported and is added to all substituted numbers.
 * For example,
 *
 * @offset = 3
 * @str = ".1.3.6.1.4.1.2.3.x.2.22.1.5.1.1.5.x"
 * @ep = {SAHPI_ENT_CHASSIS, 51}{SAHPI_ENT_SBC_BLADE, 3}
 *
 * Returns a normalized string of ".1.3.6.1.4.1.2.3.54.2.22.1.5.1.1.5.6".
 *
 * A @base parameter is also supported to allow hex and decimal connversions.
 * For example,

 * @base = 10
 * @str = "123x"
 * @ep = {SAHPI_ENT_CHASSIS, 51}{SAHPI_ENT_SBC_BLADE, 11}
 *
 * Returns a normalized string of "12311".
 *
 * @base = 16
 * @str = "123x"
 * @ep = {SAHPI_ENT_CHASSIS, 51}{SAHPI_ENT_SBC_BLADE, 11}
 *
 * Returns a normalized string of "123B".
 *
 * If @str does not contain any 'x' characters, this routine still 
 * allocates memory and returns a "normalized" string. In this case,
 * the normalized string is identical to @str.
 *
 * Note!
 * Caller of this routine MUST g_free() the returned normalized string
 * when finished with it.
 *
 * Returns:
 * Pointer to normalize string - Normal case.
 * NULL - Error.
 **********************************************************************/
gchar * oh_derive_string(SaHpiEntityPathT *ep,
                         SaHpiEntityLocationT offset,
                         int base,
                         const gchar *str)
{
        gchar *new_str = NULL, *str_walker = NULL;
        gchar **fragments = NULL, **str_nodes = NULL;
        guint num_epe, num_blanks, str_strlen = 0;
        guint total_num_digits, i, work_location_num, num_digits;

	if (!ep || !str) {
		return(NULL);
	}

	if (!(base == 10 || base == 16)) {
		CRIT("Invalid base.");
		return(NULL);
	}

        for (num_epe = 0;
             ep->Entry[num_epe].EntityType != SAHPI_ENT_ROOT && num_epe < SAHPI_MAX_ENTITY_PATH;
             num_epe++);
        /* DBG("Number of elements in entity path: %d", num_epe); */

        if (num_epe == 0) {
                CRIT("Entity Path is null.");
                return(NULL);
        }
        if ((str_strlen = strlen(str)) == 0) return(NULL); /* Str is zero length */
        if (!strrchr(str, OH_DERIVE_BLANK_CHAR)) return(g_strdup(str)); /* Nothing to replace */

        for (num_blanks=0, i=0; i<str_strlen; i++) {
                if (str[i] == OH_DERIVE_BLANK_CHAR) num_blanks++;
        }
        /* DBG("Number of blanks in str: %d, %s", num_blanks, str); */
        if (num_blanks > num_epe) {
                CRIT("Number of replacements=%d > entity path elements=%d", num_blanks, num_epe);
                return(NULL);
        }

        fragments = g_strsplit(str, OH_DERIVE_BLANK_STR, - 1);
        if (!fragments) { CRIT("Cannot split string"); goto CLEANUP; }
        str_nodes = g_new0(gchar *, num_blanks + 1);
        if (!str_nodes) { CRIT("Out of memory."); goto CLEANUP; }
        total_num_digits = 0;
        for (i=0; i<num_blanks; i++) {
                work_location_num = ep->Entry[num_blanks-1-i].EntityLocation;
		if (offset) { work_location_num = work_location_num + offset; }

                for (num_digits = 1;
                     (work_location_num = work_location_num/base) > 0; num_digits++);
                str_nodes[i] = g_new0(gchar, num_digits+1);
                if (!str_nodes[i]) {CRIT("Out of memory."); goto CLEANUP;}
		if (base == 10) {
			snprintf(str_nodes[i], (num_digits + 1) * sizeof(gchar), "%d", 
				 ep->Entry[num_blanks - 1 - i].EntityLocation + offset);
		}
		else { /* Base 16 */
			snprintf(str_nodes[i], (num_digits + 1) * sizeof(gchar), "%X", 
				 ep->Entry[num_blanks - 1 - i].EntityLocation + offset);
		}

                /* DBG("Location number: %s", str_nodes[i]); */
                total_num_digits = total_num_digits + num_digits;
        }

        new_str = g_new0(gchar, str_strlen-num_blanks + total_num_digits + 1);
        if (!new_str) { CRIT("Out of memory."); goto CLEANUP; }
        str_walker = new_str;
        for (i=0; fragments[i]; i++) {
                str_walker = strcpy(str_walker, fragments[i]);
                str_walker = str_walker + strlen(fragments[i]);
                if (str_nodes[i]) {
                        str_walker = strcpy(str_walker, str_nodes[i]);
                        /* DBG("Location number: %s", str_nodes[i]); */
                        str_walker = str_walker + strlen(str_nodes[i]);
                }
                /* DBG("New str: %s", new_str); */
        }

CLEANUP:
        g_strfreev(fragments);
        g_strfreev(str_nodes);

        return(new_str);
}

/**
 * oh_compile_entitypath_pattern
 * @epp_str: entity path pattern string
 * @epp: place where to put the compiled entity path pattern
 *
 * This will create an entitypath pattern structure out of an entity path
 * pattern string.
 * 
 * Entity paths are in this format:
 * {type0,number0}{type1,number1}{type2,number2}...{typen,numbern}
 * Each {typex,numberx} is refered to as a tuple in the entity path. The number
 * part of the tuple is also refered to as the location. The type part is an
 * entity type based on the SaHpiEntityTypeT types defined in SaHpi.h, with the
 * SAHPI_ENT prefix cut out.
 * 
 * An entity path pattern is defined as:
 * <*|{<type|.>,<number|.>}>
 * A splat (*) can take the place of a tuple. It means 0 or more tuples of any
 * type and any number.
 * A dot (.) can take the place of a type and also the place of a number within
 * the tuple. It means any type or any number depending of where it is used. 
 * 
 * Returns: SA_OK on success.
 **/
SaErrorT oh_compile_entitypath_pattern(const char *epp_str,
                                       oh_entitypath_pattern *epp)
{
        int i, j, len, in_tuple = 0, in_entity = 0, start = -1;
        oh_entitypath_pattern pattern;

        if (!epp_str || !epp) {
                return SA_ERR_HPI_INVALID_PARAMS;
        }
        memset(&pattern, 0, sizeof(oh_entitypath_pattern));

        len = strlen(epp_str);
        for (i = 0, j = 0; i < len; i++) {
                if (j >= OH_MAX_EP_TUPLES) return SA_ERR_HPI_ERROR;
                if (in_tuple) { /* We are scanning inside a tuple */
                        if (in_entity) { /* Scanning inside the entity type */
                                if (epp_str[i] == EPATHSTRING_VALUE_DELIMITER_CHAR) {
                                        if (start == -1 || i-start > SAHPI_MAX_ENTITY_PATH) {
                                                return SA_ERR_HPI_ERROR;
                                        } else {
                                                SaHpiTextBufferT buf;
                                                SaHpiEntityTypeT etype;
                                                oh_init_textbuffer(&buf);
                                                strncpy((char *)buf.Data, epp_str + start, i-start);
                                                buf.DataLength = strlen((char *)buf.Data);
                                                if (oh_encode_entitytype(&buf, &etype))
                                                        return SA_ERR_HPI_ERROR;
                                                pattern.epattern[j].etp.type = etype;
                                                in_entity = 0;
                                                start = -1;
                                        }
                                } else if (epp_str[i] == EPATHSTRING_END_DELIMITER_CHAR ||
                                           epp_str[i] == EPATHSTRING_START_DELIMITER_CHAR) {
                                        return SA_ERR_HPI_ERROR;
                                } else if (epp_str[i] == EPATHPATTERN_DOT) {
                                        pattern.epattern[j].is_splat = SAHPI_FALSE;
                                        pattern.epattern[j].etp.is_dot = SAHPI_TRUE;
                                        in_entity = 0;
                                        if (epp_str[i+1] != EPATHSTRING_VALUE_DELIMITER_CHAR) {
                                                return SA_ERR_HPI_ERROR;
                                        }
                                        i++;
                                } else {
                                        if (start == -1) start = i;
                                }
                        } else { /* Scanning inside the location number */
                                if (epp_str[i] == EPATHSTRING_VALUE_DELIMITER_CHAR) {
                                        return SA_ERR_HPI_ERROR;
                                } else if (epp_str[i] == EPATHSTRING_START_DELIMITER_CHAR) {
                                        return SA_ERR_HPI_ERROR;
                                } else if (epp_str[i] == EPATHSTRING_END_DELIMITER_CHAR) {
                                        if (start == -1 || i-start > OH_MAX_LOCATION_DIGITS) {
                                                return SA_ERR_HPI_ERROR;
                                        } else {
                                                char buf[OH_MAX_LOCATION_DIGITS+1], *endptr = NULL;
                                                SaHpiEntityLocationT loc;
                                                memset(buf, 0, OH_MAX_LOCATION_DIGITS+1);
                                                strncpy(buf, epp_str + start, i-start);
                                                loc = (SaHpiEntityLocationT)strtoul(buf, &endptr, 10);
                                                if (endptr && endptr[0] != '\0') return SA_ERR_HPI_ERROR;
                                                pattern.epattern[j].elp.location = loc;
                                                in_tuple = 0;
                                                start = -1;
                                                j++;
                                        }
                                } else if (epp_str[i] == EPATHPATTERN_DOT) {
                                        pattern.epattern[j].elp.is_dot = SAHPI_TRUE;
                                        j++;
                                        in_tuple = 0;
                                        if (epp_str[i+1] != EPATHSTRING_END_DELIMITER_CHAR) {
                                                return SA_ERR_HPI_ERROR;
                                        }
                                        i++;
                                } else {
                                        if (start == -1) start = i;
                                }
                        }
                } else { /* We are not yet inside a tuple. Could find a splat here. */
                        if (epp_str[i] == EPATHSTRING_START_DELIMITER_CHAR) {
                                in_tuple = 1;
                                in_entity = 1;
                        } else if (epp_str[i] == EPATHPATTERN_SPLAT) {
                                if (epp_str[i+1] == EPATHPATTERN_SPLAT) return SA_ERR_HPI_ERROR;
                                pattern.epattern[j].is_splat = SAHPI_TRUE;
                                j++;
                        } else {
                                return SA_ERR_HPI_ERROR;
                        }
                }
        }

        memset(epp, 0, sizeof(oh_entitypath_pattern));
        for (i = 0, j--; j > -1; j--, i++) {
                memcpy(epp->epattern + i, pattern.epattern + j, sizeof(oh_entity_pattern));
        }
        epp->epattern[i].etp.type = SAHPI_ENT_ROOT;

        return SA_OK;
}

static int ep_ended(SaHpiEntityPathT *ep, int i)
{
        if (!ep || i < 0) return 1;

        if (i >= SAHPI_MAX_ENTITY_PATH) return 1;

        if (ep->Entry[i].EntityType == SAHPI_ENT_ROOT)
                return 1;
        else
                return 0;
}

static int epp_ended(oh_entitypath_pattern *epp, int j)
{
        if (!epp || j < 0) return 1;

        if (j >= OH_MAX_EP_TUPLES) return 1;

        if (!epp->epattern[j].is_splat &&
             epp->epattern[j].etp.type == SAHPI_ENT_ROOT)
                return 1;
        else
                return 0;
}

static int matches(oh_entity_pattern *ep, SaHpiEntityT *e)
{
        if (!ep || !e) return 0;

        if (ep->is_splat) return 1;

        if (!ep->etp.is_dot && ep->etp.type != e->EntityType)
                return 0;
        else if (!ep->elp.is_dot && ep->elp.location != e->EntityLocation)
                return 0;
        else
                return 1;
}

/**
 * oh_match_entitipath_pattern
 * @epp: entity path pattern
 * @ep: entity path
 *
 * This will match an entity path pattern to an entity path.
 *
 * Returns: SAHPI_TRUE if its a match, SAHPI_FALSE if its not.
 **/
SaHpiBoolT oh_match_entitypath_pattern(oh_entitypath_pattern *epp,
                                       SaHpiEntityPathT *ep)
{
        int i = 0, j = 0, splatmode = 0;
        
        if (!epp || !ep) return SAHPI_FALSE;

        if (ep->Entry[0].EntityType == SAHPI_ENT_ROOT) {
                return SAHPI_FALSE;
        } else if (!epp->epattern[0].is_splat &&
                    epp->epattern[0].etp.type == SAHPI_ENT_ROOT) {
                return SAHPI_FALSE;
        }

        while (i < SAHPI_MAX_ENTITY_PATH) {
                if (epp->epattern[j].is_splat) {
                        splatmode = 1;
                        j++; /* next item in pattern */
                        if (epp_ended(epp, j)) break;
                } else {
                        /* If we saw a splat then non-matches are ok.
                         * Otherwise, they are not ok.
                         */
                        if (matches(&epp->epattern[j], &ep->Entry[i])) {
                                if (epp_ended(epp, j+1) && ep_ended(ep, i+1)) {
                                        break;
                                } else if (epp_ended(epp, j+1) && !ep_ended(ep, i+1)) {
                                        if (epp->epattern[j].etp.is_dot &&
                                            epp->epattern[j].elp.is_dot &&
                                            splatmode) {
                                                i++;
                                                continue;
                                        } else {
                                                return SAHPI_FALSE;
                                        }
                                } else if (ep_ended(ep, i+1) && !epp_ended(epp, j+1)) {
                                        if (epp->epattern[j+1].is_splat && epp_ended(epp, j+2))
                                                break;
                                        else
                                                return SAHPI_FALSE;
                                }
                                if (splatmode &&
                                    !(epp->epattern[j].etp.is_dot &&
                                      epp->epattern[j].elp.is_dot)) splatmode = 0;
                                i++; /* next tuple in entitypath */
                                j++; /* next item in pattern */
                        } else {
                                if (splatmode)
                                        i++; /* next tuple in entitypath */

                                if (!splatmode || ep_ended(ep, i))
                                        return SAHPI_FALSE;
                        }
                } 
        }

        return SAHPI_TRUE;
}
